mirror of https://github.com/zcash/orchard.git
Merge pull request #305 from zcash/fvk-scope
Add explicit scoping for viewing keys and addresses
This commit is contained in:
commit
420d600f0e
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -7,6 +7,30 @@ and this project adheres to Rust's notion of
|
|||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- `orchard::keys`:
|
||||
- `Scope` enum, for distinguishing external and internal scopes for viewing
|
||||
keys and addresses.
|
||||
- `FullViewingKey::{to_ivk, to_ovk}`, which each take a `Scope` argument.
|
||||
- `FullViewingKey::scope_for_address`
|
||||
|
||||
### Changed
|
||||
- `orchard::builder`:
|
||||
- `Builder::add_spend` now requires that the `FullViewingKey` matches the
|
||||
given `Note`, and handles any scoping itself (instead of requiring the
|
||||
caller to pass the `FullViewingKey` for the correct scope).
|
||||
- `orchard::keys`:
|
||||
- `FullViewingKey::{address, address_at}` now each take a `Scope` argument.
|
||||
|
||||
### Removed
|
||||
- `orchard::keys`:
|
||||
- `FullViewingKey::derive_internal`
|
||||
- `impl From<&FullViewingKey> for IncomingViewingKey` (use
|
||||
`FullViewingKey::to_ivk` instead).
|
||||
- `impl From<&FullViewingKey> for OutgoingViewingKey` (use
|
||||
`FullViewingKey::to_ovk` instead).
|
||||
|
||||
## [0.1.0-beta.2] - 2022-03-22
|
||||
### Added
|
||||
- `orchard::keys`:
|
||||
- `DiversifierIndex::to_bytes`
|
||||
- `FullViewingKey::derive_internal`
|
||||
|
|
|
@ -10,7 +10,7 @@ use orchard::{
|
|||
builder::Builder,
|
||||
bundle::Flags,
|
||||
circuit::{ProvingKey, VerifyingKey},
|
||||
keys::{FullViewingKey, SpendingKey},
|
||||
keys::{FullViewingKey, Scope, SpendingKey},
|
||||
value::NoteValue,
|
||||
Anchor, Bundle,
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||
let rng = OsRng;
|
||||
|
||||
let sk = SpendingKey::from_bytes([7; 32]).unwrap();
|
||||
let recipient = FullViewingKey::from(&sk).address_at(0u32);
|
||||
let recipient = FullViewingKey::from(&sk).address_at(0u32, Scope::External);
|
||||
|
||||
let vk = VerifyingKey::build();
|
||||
let pk = ProvingKey::build();
|
||||
|
|
|
@ -5,7 +5,7 @@ use orchard::{
|
|||
builder::Builder,
|
||||
bundle::Flags,
|
||||
circuit::ProvingKey,
|
||||
keys::{FullViewingKey, IncomingViewingKey, SpendingKey},
|
||||
keys::{FullViewingKey, Scope, SpendingKey},
|
||||
note_encryption::{CompactAction, OrchardDomain},
|
||||
value::NoteValue,
|
||||
Anchor, Bundle,
|
||||
|
@ -21,8 +21,8 @@ fn bench_note_decryption(c: &mut Criterion) {
|
|||
let pk = ProvingKey::build();
|
||||
|
||||
let fvk = FullViewingKey::from(&SpendingKey::from_bytes([7; 32]).unwrap());
|
||||
let valid_ivk = IncomingViewingKey::from(&fvk);
|
||||
let recipient = fvk.address_at(0u32);
|
||||
let valid_ivk = fvk.to_ivk(Scope::External);
|
||||
let recipient = valid_ivk.address_at(0u32);
|
||||
|
||||
// Compact actions don't have the full AEAD ciphertext, so ZIP 307 trial-decryption
|
||||
// relies on an invalid ivk resulting in random noise for which the note commitment
|
||||
|
@ -40,7 +40,8 @@ fn bench_note_decryption(c: &mut Criterion) {
|
|||
.map(|i| {
|
||||
let mut sk = [0; 32];
|
||||
sk[..4].copy_from_slice(&i.to_le_bytes());
|
||||
IncomingViewingKey::from(&FullViewingKey::from(&SpendingKey::from_bytes(sk).unwrap()))
|
||||
let fvk = FullViewingKey::from(&SpendingKey::from_bytes(sk).unwrap());
|
||||
fvk.to_ivk(Scope::External)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use orchard::keys::{FullViewingKey, SpendingKey};
|
||||
use orchard::keys::{FullViewingKey, Scope, SpendingKey};
|
||||
|
||||
fn key_derivation(c: &mut Criterion) {
|
||||
// Meaningless random spending key.
|
||||
|
@ -12,7 +12,9 @@ fn key_derivation(c: &mut Criterion) {
|
|||
let fvk = FullViewingKey::from(&sk);
|
||||
|
||||
c.bench_function("derive_fvk", |b| b.iter(|| FullViewingKey::from(&sk)));
|
||||
c.bench_function("default_address", |b| b.iter(|| fvk.address_at(0u32)));
|
||||
c.bench_function("default_address", |b| {
|
||||
b.iter(|| fvk.address_at(0u32, Scope::External))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, key_derivation);
|
||||
|
|
|
@ -12,10 +12,10 @@ use crate::{
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use orchard::keys::{SpendingKey, FullViewingKey};
|
||||
/// use orchard::keys::{SpendingKey, FullViewingKey, Scope};
|
||||
///
|
||||
/// let sk = SpendingKey::from_bytes([7; 32]).unwrap();
|
||||
/// let address = FullViewingKey::from(&sk).address_at(0u32);
|
||||
/// let address = FullViewingKey::from(&sk).address_at(0u32, Scope::External);
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Address {
|
||||
|
@ -73,7 +73,7 @@ pub mod testing {
|
|||
|
||||
use crate::keys::{
|
||||
testing::{arb_diversifier_index, arb_spending_key},
|
||||
FullViewingKey,
|
||||
FullViewingKey, Scope,
|
||||
};
|
||||
|
||||
use super::Address;
|
||||
|
@ -82,7 +82,7 @@ pub mod testing {
|
|||
/// Generates an arbitrary payment address.
|
||||
pub(crate) fn arb_address()(sk in arb_spending_key(), j in arb_diversifier_index()) -> Address {
|
||||
let fvk = FullViewingKey::from(&sk);
|
||||
fvk.address_at(j)
|
||||
fvk.address_at(j, Scope::External)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@ use crate::{
|
|||
bundle::{Action, Authorization, Authorized, Bundle, Flags},
|
||||
circuit::{Circuit, Instance, Proof, ProvingKey},
|
||||
keys::{
|
||||
FullViewingKey, OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey, SpendingKey,
|
||||
FullViewingKey, OutgoingViewingKey, Scope, SpendAuthorizingKey, SpendValidatingKey,
|
||||
SpendingKey,
|
||||
},
|
||||
note::{Note, TransmittedNoteCiphertext},
|
||||
note_encryption::OrchardNoteEncryption,
|
||||
|
@ -55,6 +56,7 @@ impl From<value::OverflowError> for Error {
|
|||
struct SpendInfo {
|
||||
dummy_sk: Option<SpendingKey>,
|
||||
fvk: FullViewingKey,
|
||||
scope: Scope,
|
||||
note: Note,
|
||||
merkle_path: MerklePath,
|
||||
}
|
||||
|
@ -70,6 +72,9 @@ impl SpendInfo {
|
|||
SpendInfo {
|
||||
dummy_sk: Some(sk),
|
||||
fvk,
|
||||
// We use external scope to avoid unnecessary derivations, because the dummy
|
||||
// note's spending key is random and thus scoping is irrelevant.
|
||||
scope: Scope::External,
|
||||
note,
|
||||
merkle_path,
|
||||
}
|
||||
|
@ -91,7 +96,7 @@ impl RecipientInfo {
|
|||
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
|
||||
fn dummy(rng: &mut impl RngCore) -> Self {
|
||||
let fvk: FullViewingKey = (&SpendingKey::random(rng)).into();
|
||||
let recipient = fvk.address_at(0u32);
|
||||
let recipient = fvk.address_at(0u32, Scope::External);
|
||||
|
||||
RecipientInfo {
|
||||
ovk: None,
|
||||
|
@ -191,7 +196,7 @@ impl ActionInfo {
|
|||
alpha: Some(alpha),
|
||||
ak: Some(ak),
|
||||
nk: Some(*self.spend.fvk.nk()),
|
||||
rivk: Some(*self.spend.fvk.rivk()),
|
||||
rivk: Some(self.spend.fvk.rivk(self.spend.scope)),
|
||||
g_d_new_star: Some((*note.recipient().g_d()).to_bytes()),
|
||||
pk_d_new_star: Some(note.recipient().pk_d().to_bytes()),
|
||||
v_new: Some(note.value()),
|
||||
|
@ -246,9 +251,15 @@ impl Builder {
|
|||
return Err("All anchors must be equal.");
|
||||
}
|
||||
|
||||
// Check if note is internal or external.
|
||||
let scope = fvk
|
||||
.scope_for_address(¬e.recipient())
|
||||
.ok_or("FullViewingKey does not correspond to the given note")?;
|
||||
|
||||
self.spends.push(SpendInfo {
|
||||
dummy_sk: None,
|
||||
fvk,
|
||||
scope,
|
||||
note,
|
||||
merkle_path,
|
||||
});
|
||||
|
@ -591,10 +602,7 @@ pub mod testing {
|
|||
address::testing::arb_address,
|
||||
bundle::{Authorized, Bundle, Flags},
|
||||
circuit::ProvingKey,
|
||||
keys::{
|
||||
testing::arb_spending_key, FullViewingKey, OutgoingViewingKey, SpendAuthorizingKey,
|
||||
SpendingKey,
|
||||
},
|
||||
keys::{testing::arb_spending_key, FullViewingKey, SpendAuthorizingKey, SpendingKey},
|
||||
note::testing::arb_note,
|
||||
tree::{Anchor, MerkleHashOrchard, MerklePath},
|
||||
value::{testing::arb_positive_note_value, NoteValue, MAX_NOTE_VALUE},
|
||||
|
@ -624,7 +632,6 @@ pub mod testing {
|
|||
/// Create a bundle from the set of arbitrary bundle inputs.
|
||||
fn into_bundle<V: TryFrom<i64>>(mut self) -> Bundle<Authorized, V> {
|
||||
let fvk = FullViewingKey::from(&self.sk);
|
||||
let ovk = OutgoingViewingKey::from(&fvk);
|
||||
let flags = Flags::from_parts(true, true);
|
||||
let mut builder = Builder::new(flags, self.anchor);
|
||||
|
||||
|
@ -633,6 +640,9 @@ pub mod testing {
|
|||
}
|
||||
|
||||
for (addr, value) in self.recipient_amounts.into_iter() {
|
||||
let scope = fvk.scope_for_address(&addr).unwrap();
|
||||
let ovk = fvk.to_ovk(scope);
|
||||
|
||||
builder
|
||||
.add_recipient(Some(ovk.clone()), addr, value, None)
|
||||
.unwrap();
|
||||
|
@ -720,7 +730,7 @@ mod tests {
|
|||
bundle::{Authorized, Bundle, Flags},
|
||||
circuit::ProvingKey,
|
||||
constants::MERKLE_DEPTH_ORCHARD,
|
||||
keys::{FullViewingKey, SpendingKey},
|
||||
keys::{FullViewingKey, Scope, SpendingKey},
|
||||
tree::EMPTY_ROOTS,
|
||||
value::NoteValue,
|
||||
};
|
||||
|
@ -732,7 +742,7 @@ mod tests {
|
|||
|
||||
let sk = SpendingKey::random(&mut rng);
|
||||
let fvk = FullViewingKey::from(&sk);
|
||||
let recipient = fvk.address_at(0u32);
|
||||
let recipient = fvk.address_at(0u32, Scope::External);
|
||||
|
||||
let mut builder = Builder::new(
|
||||
Flags::from_parts(true, true),
|
||||
|
|
|
@ -906,7 +906,7 @@ mod tests {
|
|||
|
||||
let sender_address = spent_note.recipient();
|
||||
let nk = *fvk.nk();
|
||||
let rivk = *fvk.rivk();
|
||||
let rivk = fvk.rivk(fvk.scope_for_address(&spent_note.recipient()).unwrap());
|
||||
let nf_old = spent_note.nullifier(&fvk);
|
||||
let ak: SpendValidatingKey = fvk.into();
|
||||
let alpha = pallas::Scalar::random(&mut rng);
|
||||
|
|
149
src/keys.rs
149
src/keys.rs
|
@ -1,5 +1,6 @@
|
|||
//! Key structures for Orchard.
|
||||
|
||||
use std::array;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::mem;
|
||||
|
@ -72,9 +73,14 @@ impl SpendingKey {
|
|||
// whether ask = 0; the adjustment to potentially negate ask is not
|
||||
// needed. Also, `from` would panic on ask = 0.
|
||||
let ask = SpendAuthorizingKey::derive_inner(&sk);
|
||||
// If ivk = ⊥, discard this key.
|
||||
let ivk = KeyAgreementPrivateKey::derive_inner(&(&sk).into());
|
||||
CtOption::new(sk, !(ask.is_zero() | ivk.is_none()))
|
||||
// If ivk is 0 or ⊥, discard this key.
|
||||
let fvk = (&sk).into();
|
||||
let external_ivk = KeyAgreementPrivateKey::derive_inner(&fvk);
|
||||
let internal_ivk = KeyAgreementPrivateKey::derive_inner(&fvk.derive_internal());
|
||||
CtOption::new(
|
||||
sk,
|
||||
!(ask.is_zero() | external_ivk.is_none() | internal_ivk.is_none()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the raw bytes of the spending key.
|
||||
|
@ -277,6 +283,24 @@ impl CommitIvkRandomness {
|
|||
}
|
||||
}
|
||||
|
||||
/// The scope of a viewing key or address.
|
||||
///
|
||||
/// A "scope" narrows the visibility or usage to a level below "full".
|
||||
///
|
||||
/// Consistent usage of `Scope` enables the user to provide consistent views over a wallet
|
||||
/// to other people. For example, a user can give an external [`IncomingViewingKey`] to a
|
||||
/// merchant terminal, enabling it to only detect "real" transactions from customers and
|
||||
/// not internal transactions from the wallet.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Scope {
|
||||
/// A scope used for wallet-external operations, namely deriving addresses to give to
|
||||
/// other users in order to receive funds.
|
||||
External,
|
||||
/// A scope used for wallet-internal operations, such as creating change notes,
|
||||
/// auto-shielding, and note management.
|
||||
Internal,
|
||||
}
|
||||
|
||||
/// A key that provides the capability to view incoming and outgoing transactions.
|
||||
///
|
||||
/// This key is useful anywhere you need to maintain accurate balance, but do not want the
|
||||
|
@ -319,17 +343,19 @@ impl FullViewingKey {
|
|||
&self.nk
|
||||
}
|
||||
|
||||
pub(crate) fn rivk(&self) -> &CommitIvkRandomness {
|
||||
&self.rivk
|
||||
}
|
||||
|
||||
pub(crate) fn rivk_internal(&self) -> CommitIvkRandomness {
|
||||
let k = self.rivk.0.to_repr();
|
||||
let ak = self.ak.to_bytes();
|
||||
let nk = self.nk.to_bytes();
|
||||
CommitIvkRandomness(to_scalar(
|
||||
PrfExpand::OrchardRivkInternal.with_ad_slices(&k, &[&ak, &nk]),
|
||||
))
|
||||
/// Returns either `rivk` or `rivk_internal` based on `scope`.
|
||||
pub(crate) fn rivk(&self, scope: Scope) -> CommitIvkRandomness {
|
||||
match scope {
|
||||
Scope::External => self.rivk,
|
||||
Scope::Internal => {
|
||||
let k = self.rivk.0.to_repr();
|
||||
let ak = self.ak.to_bytes();
|
||||
let nk = self.nk.to_bytes();
|
||||
CommitIvkRandomness(to_scalar(
|
||||
PrfExpand::OrchardRivkInternal.with_ad_slices(&k, &[&ak, &nk]),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
|
@ -346,14 +372,25 @@ impl FullViewingKey {
|
|||
}
|
||||
|
||||
/// Returns the payment address for this key at the given index.
|
||||
pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
|
||||
IncomingViewingKey::from(self).address_at(j)
|
||||
pub fn address_at(&self, j: impl Into<DiversifierIndex>, scope: Scope) -> Address {
|
||||
self.to_ivk(scope).address_at(j)
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key corresponding to the given diversifier.
|
||||
pub fn address(&self, d: Diversifier) -> Address {
|
||||
pub fn address(&self, d: Diversifier, scope: Scope) -> Address {
|
||||
// Shortcut: we don't need to derive DiversifierKey.
|
||||
KeyAgreementPrivateKey::from(self).address(d)
|
||||
match scope {
|
||||
Scope::External => KeyAgreementPrivateKey::from_fvk(self),
|
||||
Scope::Internal => KeyAgreementPrivateKey::from_fvk(&self.derive_internal()),
|
||||
}
|
||||
.address(d)
|
||||
}
|
||||
|
||||
/// Returns the scope of the given address, or `None` if the address is not derived
|
||||
/// from this full viewing key.
|
||||
pub fn scope_for_address(&self, address: &Address) -> Option<Scope> {
|
||||
array::IntoIter::new([Scope::External, Scope::Internal])
|
||||
.find(|scope| self.to_ivk(*scope).diversifier_index(address).is_some())
|
||||
}
|
||||
|
||||
/// Serializes the full viewing key as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
|
||||
|
@ -401,17 +438,41 @@ impl FullViewingKey {
|
|||
let nk = NullifierDerivingKey::from_bytes(&bytes[32..64])?;
|
||||
let rivk = CommitIvkRandomness::from_bytes(&bytes[64..])?;
|
||||
|
||||
Some(FullViewingKey { ak, nk, rivk })
|
||||
let fvk = FullViewingKey { ak, nk, rivk };
|
||||
|
||||
// If either ivk is 0 or ⊥, this FVK is invalid.
|
||||
let _: NonZeroPallasBase = Option::from(KeyAgreementPrivateKey::derive_inner(&fvk))?;
|
||||
let _: NonZeroPallasBase =
|
||||
Option::from(KeyAgreementPrivateKey::derive_inner(&fvk.derive_internal()))?;
|
||||
|
||||
Some(fvk)
|
||||
}
|
||||
|
||||
/// Derives an internal full viewing key from a full viewing key, as specified in [ZIP32][orchardinternalfullviewingkey]
|
||||
/// Derives an internal full viewing key from a full viewing key, as specified in
|
||||
/// [ZIP32][orchardinternalfullviewingkey]. Internal use only.
|
||||
///
|
||||
/// [orchardinternalfullviewingkey]: https://zips.z.cash/zip-0032#orchard-internal-key-derivation
|
||||
pub fn derive_internal(&self) -> Self {
|
||||
fn derive_internal(&self) -> Self {
|
||||
FullViewingKey {
|
||||
ak: self.ak.clone(),
|
||||
nk: self.nk,
|
||||
rivk: self.rivk_internal(),
|
||||
rivk: self.rivk(Scope::Internal),
|
||||
}
|
||||
}
|
||||
|
||||
/// Derives an `IncomingViewingKey` for this full viewing key.
|
||||
pub fn to_ivk(&self, scope: Scope) -> IncomingViewingKey {
|
||||
match scope {
|
||||
Scope::External => IncomingViewingKey::from_fvk(self),
|
||||
Scope::Internal => IncomingViewingKey::from_fvk(&self.derive_internal()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Derives an `OutgoingViewingKey` for this full viewing key.
|
||||
pub fn to_ovk(&self, scope: Scope) -> OutgoingViewingKey {
|
||||
match scope {
|
||||
Scope::External => OutgoingViewingKey::from_fvk(self),
|
||||
Scope::Internal => OutgoingViewingKey::from_fvk(&self.derive_internal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -527,9 +588,14 @@ impl Diversifier {
|
|||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct KeyAgreementPrivateKey(NonZeroPallasScalar);
|
||||
|
||||
impl From<&FullViewingKey> for KeyAgreementPrivateKey {
|
||||
fn from(fvk: &FullViewingKey) -> Self {
|
||||
// KeyAgreementPrivateKey cannot be constructed such that this unwrap would fail.
|
||||
impl KeyAgreementPrivateKey {
|
||||
/// Derives `KeyAgreementPrivateKey` from fvk.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
|
||||
fn from_fvk(fvk: &FullViewingKey) -> Self {
|
||||
// FullViewingKey cannot be constructed such that this unwrap would fail.
|
||||
let ivk = KeyAgreementPrivateKey::derive_inner(fvk).unwrap();
|
||||
KeyAgreementPrivateKey(ivk.into())
|
||||
}
|
||||
|
@ -588,11 +654,12 @@ pub struct IncomingViewingKey {
|
|||
ivk: KeyAgreementPrivateKey,
|
||||
}
|
||||
|
||||
impl From<&FullViewingKey> for IncomingViewingKey {
|
||||
fn from(fvk: &FullViewingKey) -> Self {
|
||||
impl IncomingViewingKey {
|
||||
/// Helper method.
|
||||
fn from_fvk(fvk: &FullViewingKey) -> Self {
|
||||
IncomingViewingKey {
|
||||
dk: fvk.derive_dk_ovk().0,
|
||||
ivk: fvk.into(),
|
||||
ivk: KeyAgreementPrivateKey::from_fvk(fvk),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -653,8 +720,9 @@ impl IncomingViewingKey {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct OutgoingViewingKey([u8; 32]);
|
||||
|
||||
impl From<&FullViewingKey> for OutgoingViewingKey {
|
||||
fn from(fvk: &FullViewingKey) -> Self {
|
||||
impl OutgoingViewingKey {
|
||||
/// Helper method.
|
||||
fn from_fvk(fvk: &FullViewingKey) -> Self {
|
||||
fvk.derive_dk_ovk().1
|
||||
}
|
||||
}
|
||||
|
@ -934,7 +1002,7 @@ mod tests {
|
|||
esk in arb_esk(),
|
||||
j in arb_diversifier_index(),
|
||||
) {
|
||||
let ivk = IncomingViewingKey::from(&(&sk).into());
|
||||
let ivk = IncomingViewingKey::from_fvk(&(&sk).into());
|
||||
let addr = ivk.address_at(j);
|
||||
|
||||
let epk = esk.derive_public(addr.g_d());
|
||||
|
@ -978,12 +1046,12 @@ mod tests {
|
|||
assert_eq!(fvk.nk().0.to_repr(), tv.nk);
|
||||
assert_eq!(fvk.rivk.0.to_repr(), tv.rivk);
|
||||
|
||||
let ivk: KeyAgreementPrivateKey = (&fvk).into();
|
||||
assert_eq!(ivk.0.to_repr(), tv.ivk);
|
||||
let external_ivk = fvk.to_ivk(Scope::External);
|
||||
assert_eq!(external_ivk.ivk.0.to_repr(), tv.ivk);
|
||||
|
||||
let diversifier = Diversifier(tv.default_d);
|
||||
|
||||
let addr = fvk.address(diversifier);
|
||||
let addr = fvk.address(diversifier, Scope::External);
|
||||
assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d);
|
||||
|
||||
let rho = Nullifier::from_bytes(&tv.note_rho).unwrap();
|
||||
|
@ -999,17 +1067,14 @@ mod tests {
|
|||
|
||||
assert_eq!(note.nullifier(&fvk).to_bytes(), tv.note_nf);
|
||||
|
||||
let internal_rivk = fvk.rivk_internal();
|
||||
let internal_rivk = fvk.rivk(Scope::Internal);
|
||||
assert_eq!(internal_rivk.0.to_repr(), tv.internal_rivk);
|
||||
|
||||
let internal_fvk = fvk.derive_internal();
|
||||
assert_eq!(internal_rivk, *internal_fvk.rivk());
|
||||
let internal_ivk = fvk.to_ivk(Scope::Internal);
|
||||
assert_eq!(internal_ivk.ivk.0.to_repr(), tv.internal_ivk);
|
||||
assert_eq!(internal_ivk.dk.0, tv.internal_dk);
|
||||
|
||||
let internal_ivk: KeyAgreementPrivateKey = (&internal_fvk).into();
|
||||
assert_eq!(internal_ivk.0.to_repr(), tv.internal_ivk);
|
||||
|
||||
let (internal_dk, internal_ovk) = internal_fvk.derive_dk_ovk();
|
||||
assert_eq!(internal_dk.0, tv.internal_dk);
|
||||
let internal_ovk = fvk.to_ovk(Scope::Internal);
|
||||
assert_eq!(internal_ovk.0, tv.internal_ovk);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use rand::RngCore;
|
|||
use subtle::CtOption;
|
||||
|
||||
use crate::{
|
||||
keys::{EphemeralSecretKey, FullViewingKey, SpendingKey},
|
||||
keys::{EphemeralSecretKey, FullViewingKey, Scope, SpendingKey},
|
||||
spec::{to_base, to_scalar, NonZeroPallasScalar, PrfExpand},
|
||||
value::NoteValue,
|
||||
Address,
|
||||
|
@ -155,7 +155,7 @@ impl Note {
|
|||
) -> (SpendingKey, FullViewingKey, Self) {
|
||||
let sk = SpendingKey::random(rng);
|
||||
let fvk: FullViewingKey = (&sk).into();
|
||||
let recipient = fvk.address_at(0u32);
|
||||
let recipient = fvk.address_at(0u32, Scope::External);
|
||||
|
||||
let note = Note::new(
|
||||
recipient,
|
||||
|
|
|
@ -5,7 +5,7 @@ use orchard::{
|
|||
builder::Builder,
|
||||
bundle::{Authorized, Flags},
|
||||
circuit::{ProvingKey, VerifyingKey},
|
||||
keys::{FullViewingKey, IncomingViewingKey, SpendAuthorizingKey, SpendingKey},
|
||||
keys::{FullViewingKey, Scope, SpendAuthorizingKey, SpendingKey},
|
||||
note::ExtractedNoteCommitment,
|
||||
note_encryption::OrchardDomain,
|
||||
tree::{MerkleHashOrchard, MerklePath},
|
||||
|
@ -36,7 +36,7 @@ fn bundle_chain() {
|
|||
|
||||
let sk = SpendingKey::from_bytes([0; 32]).unwrap();
|
||||
let fvk = FullViewingKey::from(&sk);
|
||||
let recipient = fvk.address_at(0u32);
|
||||
let recipient = fvk.address_at(0u32, Scope::External);
|
||||
|
||||
// Create a shielding bundle.
|
||||
let shielding_bundle: Bundle<_, i64> = {
|
||||
|
@ -59,7 +59,7 @@ fn bundle_chain() {
|
|||
|
||||
// Create a shielded bundle spending the previous output.
|
||||
let shielded_bundle: Bundle<_, i64> = {
|
||||
let ivk = IncomingViewingKey::from(&fvk);
|
||||
let ivk = fvk.to_ivk(Scope::External);
|
||||
let (note, _, _) = shielding_bundle
|
||||
.actions()
|
||||
.iter()
|
||||
|
|
Loading…
Reference in New Issue