-
Notifications
You must be signed in to change notification settings - Fork 48
Orchard key components #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
f077979
ceac39d
eaa7158
2750170
5772c71
a61be5d
71542f7
9455158
57c6492
cfaa61a
26701c3
307787e
bf5fb7a
cef44f5
2462bb2
f7cad77
e98f324
e0b40cb
46bf89c
3c8befa
8e55b46
e041726
42ea809
861eec1
51fd94d
05e86a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,16 @@ | ||
| use halo2::pasta::pallas; | ||
|
|
||
| use crate::keys::Diversifier; | ||
|
|
||
| /// A shielded payment address. | ||
| #[derive(Debug)] | ||
| pub struct Address { | ||
| d: Diversifier, | ||
| pk_d: (), | ||
| pk_d: pallas::Point, | ||
| } | ||
|
|
||
str4d marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| impl Address { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An idea:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 this is how I like to write typeclass instances in Haskell, and the analogy to traits is apt: write a function that is typed to whatever you're working on, and then use that function to provide for the generalized dispatch via the trait/typeclass. |
||
| pub(crate) fn from_parts(d: Diversifier, pk_d: pallas::Point) -> Self { | ||
| Address { d, pk_d } | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an internal API, for which I prefer to be explicit.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For Zebra usage this may not stay internal
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it does become necessary to expose it for Zebra, we can do so in a subsequent PR. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| //! Constants used in the Orchard protocol. | ||
| /// $\ell^\mathsf{Orchard}_\mathsf{scalar}$ | ||
| pub(crate) const L_ORCHARD_SCALAR: usize = 255; | ||
str4d marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,70 +1,193 @@ | ||||||||||||||||||||||||
| //! Key structures for Orchard. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| use crate::address::Address; | ||||||||||||||||||||||||
| use std::convert::TryInto; | ||||||||||||||||||||||||
| use std::mem; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| use aes::Aes256; | ||||||||||||||||||||||||
| use fpe::ff1::{BinaryNumeralString, FF1}; | ||||||||||||||||||||||||
| use group::GroupEncoding; | ||||||||||||||||||||||||
| use halo2::{arithmetic::FieldExt, pasta::pallas}; | ||||||||||||||||||||||||
| use subtle::CtOption; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| use crate::{ | ||||||||||||||||||||||||
| address::Address, | ||||||||||||||||||||||||
| primitives::redpallas::{self, SpendAuth}, | ||||||||||||||||||||||||
| spec::{ | ||||||||||||||||||||||||
| commit_ivk, diversify_hash, extract_p, ka_orchard, prf_expand, prf_expand_vec, to_base, | ||||||||||||||||||||||||
| to_scalar, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// A spending key, from which all key material is derived. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// TODO: In Sapling we never actually used this, instead deriving everything via ZIP 32, | ||||||||||||||||||||||||
| /// so that we could maintain Bitcoin-like HD keys with properties like non-hardened | ||||||||||||||||||||||||
| /// derivation. If we decide that we don't actually require non-hardened derivation, then | ||||||||||||||||||||||||
| /// we could greatly simplify the HD structure and use this struct directly. | ||||||||||||||||||||||||
| /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][§4.2.3]. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// [§4.2.3]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents | ||||||||||||||||||||||||
str4d marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
| pub struct SpendingKey; | ||||||||||||||||||||||||
| pub struct SpendingKey([u8; 32]); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl SpendingKey { | ||||||||||||||||||||||||
| /// Constructs an Orchard spending key from uniformly-random bytes. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// Returns `None` if the bytes do not correspond to a valid Orchard spending key. | ||||||||||||||||||||||||
| pub fn from_bytes(sk: [u8; 32]) -> CtOption<Self> { | ||||||||||||||||||||||||
| let sk = SpendingKey(sk); | ||||||||||||||||||||||||
| // If ask = 0, discard this key. | ||||||||||||||||||||||||
| let ask = SpendAuthorizingKey::derive_inner(&sk); | ||||||||||||||||||||||||
| CtOption::new(sk, !ask.ct_is_zero()) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// A spending authorizing key, used to create spend authorization signatures. | ||||||||||||||||||||||||
str4d marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][§4.2.3]. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// [§4.2.3]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents | ||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
| pub(crate) struct SpendAuthorizingKey; | ||||||||||||||||||||||||
| pub(crate) struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl SpendAuthorizingKey { | ||||||||||||||||||||||||
| /// Derives ask from sk. Internal use only, does not enforce all constraints. | ||||||||||||||||||||||||
| fn derive_inner(sk: &SpendingKey) -> pallas::Scalar { | ||||||||||||||||||||||||
| to_scalar(prf_expand(&sk.0, &[0x06])) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl From<&SpendingKey> for SpendAuthorizingKey { | ||||||||||||||||||||||||
| fn from(_: &SpendingKey) -> Self { | ||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||
| fn from(sk: &SpendingKey) -> Self { | ||||||||||||||||||||||||
| let ask = Self::derive_inner(sk); | ||||||||||||||||||||||||
| // SpendingKey cannot be constructed such that this assertion would fail. | ||||||||||||||||||||||||
| assert!(!bool::from(ask.ct_is_zero())); | ||||||||||||||||||||||||
| // TODO: Add TryFrom<S::Scalar> for SpendAuthorizingKey. | ||||||||||||||||||||||||
daira marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| let ret = SpendAuthorizingKey(ask.to_bytes().try_into().unwrap()); | ||||||||||||||||||||||||
| // If the last bit of repr_P(ak) is 1, negate ask. | ||||||||||||||||||||||||
| if (<[u8; 32]>::from(AuthorizingKey::from(&ret).0)[31] >> 7) == 1 { | ||||||||||||||||||||||||
| SpendAuthorizingKey((-ask).to_bytes().try_into().unwrap()) | ||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||
| ret | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// TODO: This is its protocol spec name for Sapling, but I'd prefer a different name. | ||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
| pub(crate) struct AuthorizingKey; | ||||||||||||||||||||||||
| pub(crate) struct AuthorizingKey(redpallas::VerificationKey<SpendAuth>); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl From<&SpendAuthorizingKey> for AuthorizingKey { | ||||||||||||||||||||||||
| fn from(_: &SpendAuthorizingKey) -> Self { | ||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||
| fn from(ask: &SpendAuthorizingKey) -> Self { | ||||||||||||||||||||||||
| AuthorizingKey((&ask.0).into()) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// A key used to derive [`Nullifier`]s from [`Note`]s. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][§4.2.3]. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// [`Nullifier`]: crate::note::Nullifier; | ||||||||||||||||||||||||
| /// [`Note`]: crate::note::Note; | ||||||||||||||||||||||||
| /// [§4.2.3]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents | ||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
| pub(crate) struct NullifierDerivingKey; | ||||||||||||||||||||||||
| pub(crate) struct NullifierDerivingKey(pallas::Base); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl From<&SpendingKey> for NullifierDerivingKey { | ||||||||||||||||||||||||
| fn from(_: &SpendingKey) -> Self { | ||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||
| fn from(sk: &SpendingKey) -> Self { | ||||||||||||||||||||||||
| NullifierDerivingKey(to_base(prf_expand(&sk.0, &[0x07]))) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// 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 | ||||||||||||||||||||||||
| /// ability to spend funds (such as a view-only wallet). | ||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Is there a better section in the spec? Also the line wrap might be wonky
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That section of the specification defines the string encoding of I'm not sure yet whether it makes sense to implement string encoding in this crate or the downstream users (
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh very agree. I think there is no better layout/description of the Orchard Full Viewing Key than this section at present, the best other section i can see in version v2021.1.16-10-g928b3d is https://zips.z.cash/protocol/nu5.pdf#addressesandkeys, unless I'm missing something or there are incoming additions. I think this is a holdover from Sapling but.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oof that may mean we can't just impl FromStr and Display on this in zebra-chain, we'd have to wrap it or transform from this key: https://doc.zebra.zfnd.org/src/zebra_chain/sapling/keys.rs.html#684
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents for the spec link.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I started putting together a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (The above crate is focused on addresses rather than keys, but we could maybe generalise it for keys in a similar fashion. I'm a bit on-the-fence about whether I want key encodings in an address-focused crate, but I also don't want to see everything become a mish-mash of tiny crates...) |
||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// TODO: Should we just define the FVK to include extended stuff like the diversifier key? | ||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
| pub struct FullViewingKey { | ||||||||||||||||||||||||
| ak: AuthorizingKey, | ||||||||||||||||||||||||
| nk: NullifierDerivingKey, | ||||||||||||||||||||||||
| rivk: (), | ||||||||||||||||||||||||
| rivk: pallas::Scalar, | ||||||||||||||||||||||||
str4d marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl From<&SpendingKey> for FullViewingKey { | ||||||||||||||||||||||||
| fn from(_: &SpendingKey) -> Self { | ||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||
| fn from(sk: &SpendingKey) -> Self { | ||||||||||||||||||||||||
| FullViewingKey { | ||||||||||||||||||||||||
| ak: (&SpendAuthorizingKey::from(sk)).into(), | ||||||||||||||||||||||||
| nk: sk.into(), | ||||||||||||||||||||||||
| rivk: to_scalar(prf_expand(&sk.0, &[0x08])), | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl FullViewingKey { | ||||||||||||||||||||||||
| /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][§4.2.3]. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// [§4.2.3]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents | ||||||||||||||||||||||||
| fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) { | ||||||||||||||||||||||||
daira marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| let k = self.rivk.to_bytes(); | ||||||||||||||||||||||||
| let b = [(&self.ak.0).into(), self.nk.0.to_bytes()]; | ||||||||||||||||||||||||
| let r = prf_expand_vec(&k, &[&[0x82], &b[0][..], &b[1][..]]); | ||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||
| DiversifierKey(r.as_bytes()[..32].try_into().unwrap()), | ||||||||||||||||||||||||
| OutgoingViewingKey(r.as_bytes()[32..].try_into().unwrap()), | ||||||||||||||||||||||||
daira marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// Returns the default payment address for this key. | ||||||||||||||||||||||||
| pub fn default_address(&self) -> Address { | ||||||||||||||||||||||||
| self.address(DiversifierKey::from(self).default_diversifier()) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// Returns the payment address for this key corresponding to the given diversifier. | ||||||||||||||||||||||||
| pub fn address(&self, d: Diversifier) -> Address { | ||||||||||||||||||||||||
| IncomingViewingKey::from(self).address(d) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// A key that provides the capability to derive a sequence of diversifiers. | ||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
| pub struct DiversifierKey([u8; 32]); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl From<&FullViewingKey> for DiversifierKey { | ||||||||||||||||||||||||
| fn from(fvk: &FullViewingKey) -> Self { | ||||||||||||||||||||||||
| fvk.derive_dk_ovk().0 | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// The index for a particular diversifier. | ||||||||||||||||||||||||
| #[derive(Clone, Copy, Debug)] | ||||||||||||||||||||||||
| pub struct DiversifierIndex([u8; 11]); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| macro_rules! di_from { | ||||||||||||||||||||||||
| ($n:ident) => { | ||||||||||||||||||||||||
| impl From<$n> for DiversifierIndex { | ||||||||||||||||||||||||
| fn from(j: $n) -> Self { | ||||||||||||||||||||||||
| let mut j_bytes = [0; 11]; | ||||||||||||||||||||||||
| j_bytes[..mem::size_of::<$n>()].copy_from_slice(&j.to_le_bytes()); | ||||||||||||||||||||||||
| DiversifierIndex(j_bytes) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| di_from!(u8); | ||||||||||||||||||||||||
| di_from!(u16); | ||||||||||||||||||||||||
str4d marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| di_from!(u32); | ||||||||||||||||||||||||
| di_from!(u64); | ||||||||||||||||||||||||
| di_from!(usize); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl DiversifierKey { | ||||||||||||||||||||||||
| /// Returns the diversifier at index 0. | ||||||||||||||||||||||||
| pub fn default_diversifier(&self) -> Diversifier { | ||||||||||||||||||||||||
| self.get(0u8) | ||||||||||||||||||||||||
str4d marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// Returns the diversifier at the given index. | ||||||||||||||||||||||||
| pub fn get(&self, j: impl Into<DiversifierIndex>) -> Diversifier { | ||||||||||||||||||||||||
| let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix"); | ||||||||||||||||||||||||
| let enc = ff | ||||||||||||||||||||||||
| .encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.into().0[..])) | ||||||||||||||||||||||||
| .unwrap(); | ||||||||||||||||||||||||
daira marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| Diversifier(enc.to_bytes_le().try_into().unwrap()) | ||||||||||||||||||||||||
daira marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// A diversifier that can be used to derive a specific [`Address`] from a | ||||||||||||||||||||||||
| /// [`FullViewingKey`] or [`IncomingViewingKey`]. | ||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
|
|
@@ -79,18 +202,20 @@ pub struct Diversifier([u8; 11]); | |||||||||||||||||||||||
| /// This key is not suitable for use on its own in a wallet, as it cannot maintain | ||||||||||||||||||||||||
| /// accurate balance. You should use a [`FullViewingKey`] instead. | ||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
| pub struct IncomingViewingKey; | ||||||||||||||||||||||||
| pub struct IncomingViewingKey(pallas::Scalar); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl From<&FullViewingKey> for IncomingViewingKey { | ||||||||||||||||||||||||
| fn from(_: &FullViewingKey) -> Self { | ||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||
| fn from(fvk: &FullViewingKey) -> Self { | ||||||||||||||||||||||||
| let ak = pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap(); | ||||||||||||||||||||||||
str4d marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| IncomingViewingKey(commit_ivk(&extract_p(&ak), &fvk.nk.0, &fvk.rivk)) | ||||||||||||||||||||||||
str4d marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl IncomingViewingKey { | ||||||||||||||||||||||||
| /// Returns the payment address for this key corresponding to the given diversifier. | ||||||||||||||||||||||||
| pub fn address(&self, _: Diversifier) -> Address { | ||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||
| pub fn address(&self, d: Diversifier) -> Address { | ||||||||||||||||||||||||
str4d marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| let g_d = diversify_hash(&d.0); | ||||||||||||||||||||||||
| Address::from_parts(d, ka_orchard(&self.0, &g_d)) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
@@ -100,10 +225,10 @@ impl IncomingViewingKey { | |||||||||||||||||||||||
| /// This key is not suitable for use on its own in a wallet, as it cannot maintain | ||||||||||||||||||||||||
| /// accurate balance. You should use a [`FullViewingKey`] instead. | ||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||
| pub struct OutgoingViewingKey; | ||||||||||||||||||||||||
| pub struct OutgoingViewingKey([u8; 32]); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl From<&FullViewingKey> for OutgoingViewingKey { | ||||||||||||||||||||||||
| fn from(_: &FullViewingKey) -> Self { | ||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||
| fn from(fvk: &FullViewingKey) -> Self { | ||||||||||||||||||||||||
| fvk.derive_dk_ovk().1 | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.