Derive OVKs from transparent account-level key, not child keys.
This also renames a number of legacy key types to better reflect their intended use.
This commit is contained in:
parent
47fc12704b
commit
6fcdfda69e
|
@ -12,7 +12,9 @@ use zcash_primitives::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use zcash_primitives::{keys::OutgoingViewingKey, legacy::keys as transparent};
|
use zcash_primitives::{
|
||||||
|
keys::OutgoingViewingKey, legacy::keys as transparent, legacy::keys::IncomingViewingKey,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
address::RecipientAddress,
|
address::RecipientAddress,
|
||||||
|
@ -378,7 +380,7 @@ pub fn shield_transparent_funds<E, N, P, D, R>(
|
||||||
wallet_db: &mut D,
|
wallet_db: &mut D,
|
||||||
params: &P,
|
params: &P,
|
||||||
prover: impl TxProver,
|
prover: impl TxProver,
|
||||||
sk: &transparent::ExternalPrivKey,
|
sk: &transparent::AccountPrivKey,
|
||||||
extfvk: &ExtendedFullViewingKey,
|
extfvk: &ExtendedFullViewingKey,
|
||||||
account: AccountId,
|
account: AccountId,
|
||||||
memo: &MemoBytes,
|
memo: &MemoBytes,
|
||||||
|
@ -401,9 +403,8 @@ where
|
||||||
.and_then(|x| x.ok_or_else(|| Error::ScanRequired.into()))?;
|
.and_then(|x| x.ok_or_else(|| Error::ScanRequired.into()))?;
|
||||||
|
|
||||||
// derive the t-address for the extpubkey at child index 0
|
// derive the t-address for the extpubkey at child index 0
|
||||||
let t_ext_pubkey = sk.to_external_pubkey();
|
let account_pubkey = sk.to_account_pubkey();
|
||||||
let taddr = t_ext_pubkey.to_address();
|
let ovk = OutgoingViewingKey(account_pubkey.internal_ovk().as_bytes());
|
||||||
let ovk = OutgoingViewingKey(t_ext_pubkey.internal_ovk().as_bytes());
|
|
||||||
|
|
||||||
// derive own shielded address from the provided extended spending key
|
// derive own shielded address from the provided extended spending key
|
||||||
// TODO: this should become the internal change address derived from
|
// TODO: this should become the internal change address derived from
|
||||||
|
@ -411,6 +412,10 @@ where
|
||||||
let z_address = extfvk.default_address().1;
|
let z_address = extfvk.default_address().1;
|
||||||
|
|
||||||
// get UTXOs from DB
|
// get UTXOs from DB
|
||||||
|
let (taddr, child_index) = account_pubkey
|
||||||
|
.derive_external_ivk()
|
||||||
|
.unwrap()
|
||||||
|
.default_address();
|
||||||
let utxos = wallet_db.get_unspent_transparent_outputs(&taddr, latest_anchor)?;
|
let utxos = wallet_db.get_unspent_transparent_outputs(&taddr, latest_anchor)?;
|
||||||
let total_amount = utxos
|
let total_amount = utxos
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -427,9 +432,10 @@ where
|
||||||
|
|
||||||
let mut builder = Builder::new(params.clone(), latest_scanned_height);
|
let mut builder = Builder::new(params.clone(), latest_scanned_height);
|
||||||
|
|
||||||
|
let secret_key = sk.derive_external_secret_key(child_index).unwrap();
|
||||||
for utxo in &utxos {
|
for utxo in &utxos {
|
||||||
builder
|
builder
|
||||||
.add_transparent_input(*sk.secret_key(), utxo.outpoint.clone(), utxo.txout.clone())
|
.add_transparent_input(secret_key, utxo.outpoint.clone(), utxo.txout.clone())
|
||||||
.map_err(Error::Builder)?;
|
.map_err(Error::Builder)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,7 @@ mod tests {
|
||||||
super::transparent,
|
super::transparent,
|
||||||
crate::encoding::AddressCodec,
|
crate::encoding::AddressCodec,
|
||||||
secp256k1::key::SecretKey,
|
secp256k1::key::SecretKey,
|
||||||
zcash_primitives::{consensus::MAIN_NETWORK, legacy},
|
zcash_primitives::{consensus::MAIN_NETWORK, legacy, legacy::keys::IncomingViewingKey},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
|
@ -251,7 +251,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.derive_external_secret_key(0)
|
.derive_external_secret_key(0)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let wif = transparent::Wif::from_secret_key(&MAIN_NETWORK, &sk.secret_key(), true).0;
|
let wif = transparent::Wif::from_secret_key(&MAIN_NETWORK, &sk, true).0;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
wif,
|
wif,
|
||||||
"L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2".to_string()
|
"L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2".to_string()
|
||||||
|
@ -270,30 +270,17 @@ mod tests {
|
||||||
assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string());
|
assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
|
||||||
#[test]
|
|
||||||
fn pk_from_seed() {
|
|
||||||
let pk = legacy::keys::AccountPrivKey::from_seed(&MAIN_NETWORK, &seed(), AccountId(0))
|
|
||||||
.unwrap()
|
|
||||||
.derive_external_secret_key(0)
|
|
||||||
.unwrap()
|
|
||||||
.to_external_pubkey();
|
|
||||||
let hex_value = hex::encode(&pk.public_key().serialize());
|
|
||||||
assert_eq!(
|
|
||||||
hex_value,
|
|
||||||
"03b1d7fb28d17c125b504d06b1530097e0a3c76ada184237e3bc0925041230a5af".to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
#[test]
|
#[test]
|
||||||
fn pk_to_taddr() {
|
fn pk_to_taddr() {
|
||||||
let pk = legacy::keys::AccountPrivKey::from_seed(&MAIN_NETWORK, &seed(), AccountId(0))
|
let taddr = legacy::keys::AccountPrivKey::from_seed(&MAIN_NETWORK, &seed(), AccountId(0))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.derive_external_secret_key(0)
|
.to_account_pubkey()
|
||||||
|
.derive_external_ivk()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_external_pubkey();
|
.derive_address(0)
|
||||||
let taddr = pk.to_address().encode(&MAIN_NETWORK);
|
.unwrap()
|
||||||
|
.encode(&MAIN_NETWORK);
|
||||||
assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string());
|
assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -707,7 +707,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use zcash_primitives::legacy;
|
use zcash_primitives::{legacy, legacy::keys::IncomingViewingKey};
|
||||||
|
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
block::BlockHash,
|
block::BlockHash,
|
||||||
|
@ -771,7 +771,7 @@ mod tests {
|
||||||
init_accounts_table(db_data, &[ufvk]).unwrap();
|
init_accounts_table(db_data, &[ufvk]).unwrap();
|
||||||
(
|
(
|
||||||
extfvk,
|
extfvk,
|
||||||
tkey.map(|k| k.derive_external_pubkey(0).unwrap().to_address()),
|
tkey.map(|k| k.derive_external_ivk().unwrap().default_address().0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,10 @@ use zcash_client_backend::{
|
||||||
use crate::{address_from_extfvk, error::SqliteClientError, WalletDb};
|
use crate::{address_from_extfvk, error::SqliteClientError, WalletDb};
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use zcash_client_backend::encoding::AddressCodec;
|
use {
|
||||||
|
zcash_client_backend::encoding::AddressCodec,
|
||||||
|
zcash_primitives::legacy::keys::IncomingViewingKey,
|
||||||
|
};
|
||||||
|
|
||||||
/// Sets up the internal structure of the data database.
|
/// Sets up the internal structure of the data database.
|
||||||
///
|
///
|
||||||
|
@ -207,9 +210,9 @@ pub fn init_accounts_table<P: consensus::Parameters>(
|
||||||
.map(|extfvk| address_from_extfvk(&wdb.params, extfvk));
|
.map(|extfvk| address_from_extfvk(&wdb.params, extfvk));
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
let taddress_str: Option<String> = key.transparent().and_then(|k| {
|
let taddress_str: Option<String> = key.transparent().and_then(|k| {
|
||||||
k.derive_external_pubkey(0)
|
k.derive_external_ivk()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|k| k.to_address().encode(&wdb.params))
|
.map(|k| k.default_address().0.encode(&wdb.params))
|
||||||
});
|
});
|
||||||
#[cfg(not(feature = "transparent-inputs"))]
|
#[cfg(not(feature = "transparent-inputs"))]
|
||||||
let taddress_str: Option<String> = None;
|
let taddress_str: Option<String> = None;
|
||||||
|
|
|
@ -7,17 +7,19 @@ use crate::{consensus, keys::prf_expand_vec, zip32::AccountId};
|
||||||
|
|
||||||
use super::TransparentAddress;
|
use super::TransparentAddress;
|
||||||
|
|
||||||
|
const MAX_TRANSPARENT_CHILD_INDEX: u32 = 0x7FFFFFFF;
|
||||||
|
|
||||||
/// A type representing a BIP-44 private key at the account path level
|
/// A type representing a BIP-44 private key at the account path level
|
||||||
/// `m/44'/<coin_type>'/<account>'
|
/// `m/44'/<coin_type>'/<account>'
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AccountPrivKey(ExtendedPrivKey);
|
pub struct AccountPrivKey(ExtendedPrivKey);
|
||||||
|
|
||||||
impl AccountPrivKey {
|
impl AccountPrivKey {
|
||||||
/// Perform derivation of the extended private key for the BIP-44 path:
|
/// Performs derivation of the extended private key for the BIP-44 path:
|
||||||
/// `m/44'/<coin_type>'/<account>'
|
/// `m/44'/<coin_type>'/<account>'`.
|
||||||
///
|
///
|
||||||
/// This produces the extended private key for the external (non-change)
|
/// This produces the root of the derivation tree for transparent
|
||||||
/// address at the specified index for the provided account.
|
/// viewing keys and addresses for the for the provided account.
|
||||||
pub fn from_seed<P: consensus::Parameters>(
|
pub fn from_seed<P: consensus::Parameters>(
|
||||||
params: &P,
|
params: &P,
|
||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
|
@ -42,98 +44,54 @@ impl AccountPrivKey {
|
||||||
AccountPubKey(ExtendedPubKey::from_private_key(&self.0))
|
AccountPubKey(ExtendedPubKey::from_private_key(&self.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive BIP-44 private key at the external child path
|
/// Derives the BIP-44 private spending key for the external (incoming payment) child path
|
||||||
/// `m/44'/<coin_type>'/<account>'/0/<child_index>
|
/// `m/44'/<coin_type>'/<account>'/0/<child_index>`.
|
||||||
pub fn derive_external_secret_key(
|
pub fn derive_external_secret_key(
|
||||||
&self,
|
&self,
|
||||||
child_index: u32,
|
child_index: u32,
|
||||||
) -> Result<ExternalPrivKey, hdwallet::error::Error> {
|
) -> Result<secp256k1::key::SecretKey, hdwallet::error::Error> {
|
||||||
self.0
|
self.0
|
||||||
.derive_private_key(KeyIndex::Normal(0))?
|
.derive_private_key(KeyIndex::Normal(0))?
|
||||||
.derive_private_key(KeyIndex::Normal(child_index))
|
.derive_private_key(KeyIndex::Normal(child_index))
|
||||||
.map(ExternalPrivKey)
|
.map(|k| k.private_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derives the BIP-44 private spending key for the internal (change) child path
|
||||||
|
/// `m/44'/<coin_type>'/<account>'/1/<child_index>`.
|
||||||
|
pub fn derive_internal_secret_key(
|
||||||
|
&self,
|
||||||
|
child_index: u32,
|
||||||
|
) -> Result<secp256k1::key::SecretKey, hdwallet::error::Error> {
|
||||||
|
self.0
|
||||||
|
.derive_private_key(KeyIndex::Normal(1))?
|
||||||
|
.derive_private_key(KeyIndex::Normal(child_index))
|
||||||
|
.map(|k| k.private_key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type representing a BIP-44 public key at the account path level
|
/// A type representing a BIP-44 public key at the account path level
|
||||||
/// `m/44'/<coin_type>'/<account>'
|
/// `m/44'/<coin_type>'/<account>'`.
|
||||||
|
///
|
||||||
|
/// This provides the necessary derivation capability for the for
|
||||||
|
/// the transparent component of a unified full viewing key.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AccountPubKey(ExtendedPubKey);
|
pub struct AccountPubKey(ExtendedPubKey);
|
||||||
|
|
||||||
impl AccountPubKey {
|
impl AccountPubKey {
|
||||||
/// Derive BIP-44 public key at the external child path
|
/// Derives the BIP-44 public key at the external "change level" path
|
||||||
/// `m/44'/<coin_type>'/<account>'/0/<child_index>
|
/// `m/44'/<coin_type>'/<account>'/0`.
|
||||||
pub fn derive_external_pubkey(
|
pub fn derive_external_ivk(&self) -> Result<ExternalIvk, hdwallet::error::Error> {
|
||||||
&self,
|
|
||||||
child_index: u32,
|
|
||||||
) -> Result<ExternalPubKey, hdwallet::error::Error> {
|
|
||||||
self.0
|
self.0
|
||||||
.derive_public_key(KeyIndex::Normal(0))?
|
.derive_public_key(KeyIndex::Normal(0))
|
||||||
.derive_public_key(KeyIndex::Normal(child_index))
|
.map(ExternalIvk)
|
||||||
.map(ExternalPubKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_extended_pubkey(extpubkey: ExtendedPubKey) -> Self {
|
/// Derives the BIP-44 public key at the internal "change level" path
|
||||||
AccountPubKey(extpubkey)
|
/// `m/44'/<coin_type>'/<account>'/1`.
|
||||||
}
|
pub fn derive_internal_ivk(&self) -> Result<InternalIvk, hdwallet::error::Error> {
|
||||||
|
self.0
|
||||||
pub fn extended_pubkey(&self) -> &ExtendedPubKey {
|
.derive_public_key(KeyIndex::Normal(1))
|
||||||
&self.0
|
.map(InternalIvk)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type representing a private key at the BIP-44 external child
|
|
||||||
/// level `m/44'/<coin_type>'/<account>'/0/<child_index>
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ExternalPrivKey(ExtendedPrivKey);
|
|
||||||
|
|
||||||
impl ExternalPrivKey {
|
|
||||||
/// Returns the external public key corresponding to this private key
|
|
||||||
pub fn to_external_pubkey(&self) -> ExternalPubKey {
|
|
||||||
ExternalPubKey(ExtendedPubKey::from_private_key(&self.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts the secp256k1 secret key component
|
|
||||||
pub fn secret_key(&self) -> &secp256k1::key::SecretKey {
|
|
||||||
&self.0.private_key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pubkey_to_address(pubkey: &secp256k1::key::PublicKey) -> TransparentAddress {
|
|
||||||
let mut hash160 = ripemd160::Ripemd160::new();
|
|
||||||
hash160.update(Sha256::digest(&pubkey.serialize()));
|
|
||||||
TransparentAddress::PublicKey(*hash160.finalize().as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type representing a public key at the BIP-44 external child
|
|
||||||
/// level `m/44'/<coin_type>'/<account>'/0/<child_index>
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ExternalPubKey(ExtendedPubKey);
|
|
||||||
|
|
||||||
impl std::convert::TryFrom<&[u8; 65]> for ExternalPubKey {
|
|
||||||
type Error = hdwallet::error::Error;
|
|
||||||
|
|
||||||
fn try_from(data: &[u8; 65]) -> Result<Self, Self::Error> {
|
|
||||||
ExternalPubKey::deserialize(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExternalPubKey {
|
|
||||||
/// Returns the transparent address corresponding to
|
|
||||||
/// this public key.
|
|
||||||
pub fn to_address(&self) -> TransparentAddress {
|
|
||||||
pubkey_to_address(&self.0.public_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the secp256k1::key::PublicKey component of
|
|
||||||
/// this public key.
|
|
||||||
pub fn public_key(&self) -> &secp256k1::key::PublicKey {
|
|
||||||
&self.0.public_key
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the chain code component of this public key.
|
|
||||||
pub fn chain_code(&self) -> &[u8] {
|
|
||||||
&self.0.chain_code
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derives the internal ovk and external ovk corresponding to this
|
/// Derives the internal ovk and external ovk corresponding to this
|
||||||
|
@ -142,8 +100,8 @@ impl ExternalPubKey {
|
||||||
/// [transparent-ovk]: https://zips.z.cash/zip-0316#deriving-internal-keys
|
/// [transparent-ovk]: https://zips.z.cash/zip-0316#deriving-internal-keys
|
||||||
pub fn ovks_for_shielding(&self) -> (InternalOvk, ExternalOvk) {
|
pub fn ovks_for_shielding(&self) -> (InternalOvk, ExternalOvk) {
|
||||||
let i_ovk = prf_expand_vec(
|
let i_ovk = prf_expand_vec(
|
||||||
&self.chain_code(),
|
&self.0.chain_code,
|
||||||
&[&[0xd0], &self.public_key().serialize()],
|
&[&[0xd0], &self.0.public_key.serialize()],
|
||||||
);
|
);
|
||||||
let i_ovk = i_ovk.as_bytes();
|
let i_ovk = i_ovk.as_bytes();
|
||||||
let ovk_internal = InternalOvk(i_ovk[..32].try_into().unwrap());
|
let ovk_internal = InternalOvk(i_ovk[..32].try_into().unwrap());
|
||||||
|
@ -161,23 +119,106 @@ impl ExternalPubKey {
|
||||||
pub fn external_ovk(&self) -> ExternalOvk {
|
pub fn external_ovk(&self) -> ExternalOvk {
|
||||||
self.ovks_for_shielding().1
|
self.ovks_for_shielding().1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn pubkey_to_address(pubkey: &secp256k1::key::PublicKey) -> TransparentAddress {
|
||||||
let mut buf = self.0.chain_code.clone();
|
let mut hash160 = ripemd160::Ripemd160::new();
|
||||||
buf.extend(self.0.public_key.serialize().to_vec());
|
hash160.update(Sha256::digest(&pubkey.serialize()));
|
||||||
|
TransparentAddress::PublicKey(*hash160.finalize().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod private {
|
||||||
|
use hdwallet::ExtendedPubKey;
|
||||||
|
pub trait SealedChangeLevelKey {
|
||||||
|
fn extended_pubkey(&self) -> &ExtendedPubKey;
|
||||||
|
fn from_extended_pubkey(key: ExtendedPubKey) -> Self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IncomingViewingKey: private::SealedChangeLevelKey + std::marker::Sized {
|
||||||
|
/// Derives a transparent address at the provided child index.
|
||||||
|
fn derive_address(
|
||||||
|
&self,
|
||||||
|
child_index: u32,
|
||||||
|
) -> Result<TransparentAddress, hdwallet::error::Error> {
|
||||||
|
let child_key = self
|
||||||
|
.extended_pubkey()
|
||||||
|
.derive_public_key(KeyIndex::Normal(child_index))?;
|
||||||
|
Ok(pubkey_to_address(&child_key.public_key))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Searches the space of child indexes for an index that will
|
||||||
|
/// generate a valid transparent address, and returns the resulting
|
||||||
|
/// address and the index at which it was generated.
|
||||||
|
fn default_address(&self) -> (TransparentAddress, u32) {
|
||||||
|
let mut child_index = 0;
|
||||||
|
while child_index <= MAX_TRANSPARENT_CHILD_INDEX {
|
||||||
|
match self.derive_address(child_index) {
|
||||||
|
Ok(addr) => {
|
||||||
|
return (addr, child_index);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
child_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Exhausted child index space attempting to find a default address.");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let extpubkey = self.extended_pubkey();
|
||||||
|
let mut buf = extpubkey.chain_code.clone();
|
||||||
|
buf.extend(extpubkey.public_key.serialize().to_vec());
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize(data: &[u8; 65]) -> Result<Self, hdwallet::error::Error> {
|
fn deserialize(data: &[u8; 65]) -> Result<Self, hdwallet::error::Error> {
|
||||||
let chain_code = data[..32].to_vec();
|
let chain_code = data[..32].to_vec();
|
||||||
let public_key = PublicKey::from_slice(&data[32..])?;
|
let public_key = PublicKey::from_slice(&data[32..])?;
|
||||||
Ok(ExternalPubKey(ExtendedPubKey {
|
Ok(Self::from_extended_pubkey(ExtendedPubKey {
|
||||||
public_key,
|
public_key,
|
||||||
chain_code,
|
chain_code,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type representing an incoming viewing key at the BIP-44 "external"
|
||||||
|
/// path `m/44'/<coin_type>'/<account>'/0`. This allows derivation
|
||||||
|
/// of child addresses that may be provided to external parties.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ExternalIvk(ExtendedPubKey);
|
||||||
|
|
||||||
|
impl private::SealedChangeLevelKey for ExternalIvk {
|
||||||
|
fn extended_pubkey(&self) -> &ExtendedPubKey {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_extended_pubkey(key: ExtendedPubKey) -> Self {
|
||||||
|
ExternalIvk(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IncomingViewingKey for ExternalIvk {}
|
||||||
|
|
||||||
|
/// A type representing an incoming viewing key at the BIP-44 "internal"
|
||||||
|
/// path `m/44'/<coin_type>'/<account>'/1`. This allows derivation
|
||||||
|
/// of change addresses for use within the wallet, but which should
|
||||||
|
/// not be shared with external parties.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct InternalIvk(ExtendedPubKey);
|
||||||
|
|
||||||
|
impl private::SealedChangeLevelKey for InternalIvk {
|
||||||
|
fn extended_pubkey(&self) -> &ExtendedPubKey {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_extended_pubkey(key: ExtendedPubKey) -> Self {
|
||||||
|
InternalIvk(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IncomingViewingKey for InternalIvk {}
|
||||||
|
|
||||||
/// Internal ovk used for autoshielding.
|
/// Internal ovk used for autoshielding.
|
||||||
pub struct InternalOvk([u8; 32]);
|
pub struct InternalOvk([u8; 32]);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue