Merge pull request #615 from nuttycom/wallet/shield_to_ufvk
Shield funds to the internal Sapling key for a specified account.
This commit is contained in:
commit
8c00ca3b88
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use zcash_address::unified::Typecode;
|
||||
use zcash_primitives::{
|
||||
consensus::BlockHeight,
|
||||
sapling::Node,
|
||||
|
@ -23,6 +24,9 @@ pub enum ChainInvalid {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum Error<NoteId> {
|
||||
/// No account with the given identifier was found in the wallet.
|
||||
AccountNotFound(AccountId),
|
||||
|
||||
/// The amount specified exceeds the allowed range.
|
||||
InvalidAmount,
|
||||
|
||||
|
@ -48,6 +52,9 @@ pub enum Error<NoteId> {
|
|||
/// does not correspond to root of the current commitment tree.
|
||||
InvalidWitnessAnchor(NoteId, BlockHeight),
|
||||
|
||||
/// No key of the given type was associated with the specified account.
|
||||
KeyNotFound(AccountId, Typecode),
|
||||
|
||||
/// The wallet must first perform a scan of the blockchain before other
|
||||
/// operations can be performed.
|
||||
ScanRequired,
|
||||
|
@ -79,6 +86,9 @@ impl ChainInvalid {
|
|||
impl<N: fmt::Display> fmt::Display for Error<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self {
|
||||
Error::AccountNotFound(account) => {
|
||||
write!(f, "Wallet does not contain account {}", u32::from(*account))
|
||||
}
|
||||
Error::InvalidAmount => write!(
|
||||
f,
|
||||
"The value lies outside the valid range of Zcash amounts."
|
||||
|
@ -104,6 +114,9 @@ impl<N: fmt::Display> fmt::Display for Error<N> {
|
|||
"Witness for note {} has incorrect anchor after scanning block {}",
|
||||
id_note, last_height
|
||||
),
|
||||
Error::KeyNotFound(account, typecode) => {
|
||||
write!(f, "No {:?} key was available for account {}", typecode, u32::from(*account))
|
||||
}
|
||||
Error::ScanRequired => write!(f, "Must scan blocks first"),
|
||||
Error::Builder(e) => write!(f, "{:?}", e),
|
||||
Error::Protobuf(e) => write!(f, "{}", e),
|
||||
|
|
|
@ -12,8 +12,11 @@ use zcash_primitives::{
|
|||
};
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use zcash_primitives::{
|
||||
keys::OutgoingViewingKey, legacy::keys as transparent, legacy::keys::IncomingViewingKey,
|
||||
use {
|
||||
zcash_address::unified::Typecode,
|
||||
zcash_primitives::{
|
||||
keys::OutgoingViewingKey, legacy::keys as transparent, legacy::keys::IncomingViewingKey,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -418,11 +421,10 @@ where
|
|||
/// * `prover`: The TxProver to use in constructing the shielded transaction.
|
||||
/// * `sk`: The secp256k1 secret key that will be used to detect and spend transparent
|
||||
/// UTXOs.
|
||||
/// * `extfvk`: The extended full viewing key that will be used to produce the
|
||||
/// Sapling address to which funds will be sent.
|
||||
/// * `account`: The ZIP32 account identifier associated with the the extended
|
||||
/// full viewing key. This procedure will return an error if this does not correctly
|
||||
/// correspond to `extfvk`.
|
||||
/// * `account`: The ZIP32 account identifier for the account to which funds will
|
||||
/// be shielded. Funds will be shielded to the internal (change) address associated with the
|
||||
/// most preferred shielded receiver corresponding to this account, or if no shielded
|
||||
/// receiver can be used for this account, this function will return an error.
|
||||
/// * `memo`: A memo to be included in the output to the (internal) recipient.
|
||||
/// This can be used to take notes about auto-shielding operations internal
|
||||
/// to the wallet that the wallet can use to improve how it represents those
|
||||
|
@ -437,7 +439,6 @@ pub fn shield_transparent_funds<E, N, P, D, R, U>(
|
|||
params: &P,
|
||||
prover: impl TxProver,
|
||||
sk: &transparent::AccountPrivKey,
|
||||
extfvk: &ExtendedFullViewingKey,
|
||||
account: AccountId,
|
||||
memo: &MemoBytes,
|
||||
min_confirmations: u32,
|
||||
|
@ -448,11 +449,22 @@ where
|
|||
R: Copy + Debug,
|
||||
D: WalletWrite<Error = E, TxRef = R> + WalletWriteTransparent<UtxoRef = U>,
|
||||
{
|
||||
// Check that the ExtendedSpendingKey we have been given corresponds to the
|
||||
// ExtendedFullViewingKey for the account we are spending from.
|
||||
if !wallet_db.is_valid_account_extfvk(account, extfvk)? {
|
||||
return Err(E::from(Error::InvalidExtSk(account)));
|
||||
}
|
||||
// Obtain the UFVK for the specified account & use its internal change address
|
||||
// as the destination for shielded funds.
|
||||
let shielding_address = wallet_db
|
||||
.get_unified_full_viewing_keys()
|
||||
.and_then(|ufvks| {
|
||||
ufvks
|
||||
.get(&account)
|
||||
.ok_or_else(|| E::from(Error::AccountNotFound(account)))
|
||||
.and_then(|ufvk| {
|
||||
// TODO: select the most preferred shielded receiver once we have the ability to
|
||||
// spend Orchard funds.
|
||||
ufvk.sapling()
|
||||
.map(|dfvk| dfvk.change_address().1)
|
||||
.ok_or_else(|| E::from(Error::KeyNotFound(account, Typecode::Sapling)))
|
||||
})
|
||||
})?;
|
||||
|
||||
let (latest_scanned_height, latest_anchor) = wallet_db
|
||||
.get_target_and_anchor_heights(min_confirmations)
|
||||
|
@ -461,11 +473,6 @@ where
|
|||
let account_pubkey = sk.to_account_pubkey();
|
||||
let ovk = OutgoingViewingKey(account_pubkey.internal_ovk().as_bytes());
|
||||
|
||||
// derive own shielded address from the provided extended full viewing key
|
||||
// TODO: this should become the internal change address derived from
|
||||
// the wallet's UFVK
|
||||
let z_address = extfvk.default_address().1;
|
||||
|
||||
// derive the t-address for the extpubkey at the minimum valid child index
|
||||
let (taddr, child_index) = account_pubkey
|
||||
.derive_external_ivk()
|
||||
|
@ -497,11 +504,16 @@ where
|
|||
}
|
||||
|
||||
// there are no sapling notes so we set the change manually
|
||||
builder.send_change_to(ovk, z_address.clone());
|
||||
builder.send_change_to(ovk, shielding_address.clone());
|
||||
|
||||
// add the sapling output to shield the funds
|
||||
builder
|
||||
.add_sapling_output(Some(ovk), z_address.clone(), amount_to_shield, memo.clone())
|
||||
.add_sapling_output(
|
||||
Some(ovk),
|
||||
shielding_address.clone(),
|
||||
amount_to_shield,
|
||||
memo.clone(),
|
||||
)
|
||||
.map_err(Error::Builder)?;
|
||||
|
||||
let (tx, tx_metadata) = builder.build(&prover).map_err(Error::Builder)?;
|
||||
|
@ -515,7 +527,7 @@ where
|
|||
account,
|
||||
outputs: vec![SentTransactionOutput {
|
||||
output_index,
|
||||
recipient_address: &RecipientAddress::Shielded(z_address),
|
||||
recipient_address: &RecipientAddress::Shielded(shielding_address),
|
||||
value: amount_to_shield,
|
||||
memo: Some(memo.clone()),
|
||||
}],
|
||||
|
|
|
@ -108,6 +108,11 @@ and this library adheres to Rust's notion of
|
|||
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.
|
||||
- `zcash_primitives::sapling::keys::DiversifiableFullViewingKey::change_address`
|
||||
has been added as a convenience method for obtaining the change address
|
||||
at the default diversifier. This address **MUST NOT** be encoded and exposed
|
||||
to users. User interfaces should instead mark these notes as "change notes" or
|
||||
"internal wallet operations".
|
||||
- A new module `zcash_primitives::legacy::keys` has been added under the
|
||||
`transparent-inputs` feature flag to support types related to supporting
|
||||
transparent components of unified addresses and derivation of OVKs for
|
||||
|
|
|
@ -292,6 +292,16 @@ impl DiversifiableFullViewingKey {
|
|||
zip32::sapling_default_address(&self.fvk, &self.dk)
|
||||
}
|
||||
|
||||
/// Returns the internal address corresponding to the smallest valid diversifier index,
|
||||
/// along with that index.
|
||||
///
|
||||
/// This address **MUST NOT** be encoded and exposed to end users. User interfaces
|
||||
/// should instead mark these notes as "change notes" or "internal wallet operations".
|
||||
pub fn change_address(&self) -> (zip32::DiversifierIndex, PaymentAddress) {
|
||||
let internal_dfvk = self.derive_internal();
|
||||
zip32::sapling_default_address(&internal_dfvk.fvk, &internal_dfvk.dk)
|
||||
}
|
||||
|
||||
/// Attempts to decrypt the given address's diversifier with this full viewing key.
|
||||
///
|
||||
/// This method extracts the diversifier from the given address and decrypts it as a
|
||||
|
|
Loading…
Reference in New Issue