Add CWallet::GetUnifiedForReceiver

This commit is contained in:
Kris Nuttycombe 2021-12-13 15:11:29 -07:00
parent 8337442553
commit 0dcdc28a38
6 changed files with 201 additions and 25 deletions

View File

@ -311,7 +311,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey(
void CBasicKeyStore::AddUnifiedAddress(
const libzcash::UFVKId& keyId,
const libzcash::UnifiedAddress& ua)
const std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>& ua)
{
LOCK(cs_KeyStore);
@ -319,20 +319,21 @@ void CBasicKeyStore::AddUnifiedAddress(
// the UA; all other lookups of the associated UFVK will be
// made via the protocol-specific viewing key that is used
// to trial-decrypt a transaction.
auto addrEntry = std::make_pair(keyId, ua.second);
auto p2pkhReceiver = ua.GetP2PKHReceiver();
auto p2pkhReceiver = ua.first.GetP2PKHReceiver();
if (p2pkhReceiver.has_value()) {
mapP2PKHUnified.insert(std::make_pair(p2pkhReceiver.value(), keyId));
mapP2PKHUnified.insert(std::make_pair(p2pkhReceiver.value(), addrEntry));
}
auto p2shReceiver = ua.GetP2SHReceiver();
auto p2shReceiver = ua.first.GetP2SHReceiver();
if (p2shReceiver.has_value()) {
mapP2SHUnified.insert(std::make_pair(p2shReceiver.value(), keyId));
mapP2SHUnified.insert(std::make_pair(p2shReceiver.value(), addrEntry));
}
}
std::optional<libzcash::ZcashdUnifiedFullViewingKey> CBasicKeyStore::GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId)
const libzcash::UFVKId& keyId) const
{
auto mi = mapUnifiedFullViewingKeys.find(keyId);
if (mi != mapUnifiedFullViewingKeys.end()) {
@ -341,3 +342,43 @@ std::optional<libzcash::ZcashdUnifiedFullViewingKey> CBasicKeyStore::GetUnifiedF
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) const
{
return std::visit(FindUFVKId(*this), receiver);
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
if (keystore.mapSaplingIncomingViewingKeys.count(saplingAddr) > 0) {
const auto& saplingIvk = keystore.mapSaplingIncomingViewingKeys.at(saplingAddr);
if (keystore.mapSaplingKeyUnified.count(saplingIvk) > 0) {
return std::make_pair(keystore.mapSaplingKeyUnified.at(saplingIvk), std::nullopt);
} else {
return std::nullopt;
}
} else {
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const CScriptID& scriptId) const {
if (keystore.mapP2SHUnified.count(scriptId) > 0) {
return keystore.mapP2SHUnified.at(scriptId);
} else {
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const CKeyID& keyId) const {
if (keystore.mapP2PKHUnified.count(keyId) > 0) {
return keystore.mapP2PKHUnified.at(keyId);
} else {
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const libzcash::UnknownReceiver& receiver) const {
return std::nullopt;
}

View File

@ -110,11 +110,17 @@ public:
virtual void AddUnifiedAddress(
const libzcash::UFVKId& keyId,
const libzcash::UnifiedAddress &ua
const std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>& ua
) = 0;
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId) = 0;
const libzcash::UFVKId& keyId
) const = 0;
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver
) const = 0;
};
typedef std::map<CKeyID, CKey> KeyMap;
@ -136,11 +142,7 @@ typedef std::map<
// Only maps from default addresses to ivk, may need to be reworked when adding diversified addresses.
typedef std::map<libzcash::SaplingPaymentAddress, libzcash::SaplingIncomingViewingKey> SaplingIncomingViewingKeyMap;
struct UnifiedAddressMetadata {
libzcash::UFVKId keyId;
libzcash::diversifier_index_t j;
std::vector<libzcash::ReceiverType> receiverTypes;
};
class FindUFVKId;
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
@ -164,10 +166,12 @@ protected:
SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys;
// Unified key support
std::map<CKeyID, libzcash::UFVKId> mapP2PKHUnified;
std::map<CScriptID, libzcash::UFVKId> mapP2SHUnified;
std::map<CKeyID, std::pair<libzcash::UFVKId, libzcash::diversifier_index_t>> mapP2PKHUnified;
std::map<CScriptID, std::pair<libzcash::UFVKId, libzcash::diversifier_index_t>> mapP2SHUnified;
std::map<libzcash::SaplingIncomingViewingKey, libzcash::UFVKId> mapSaplingKeyUnified;
std::map<libzcash::UFVKId, libzcash::ZcashdUnifiedFullViewingKey> mapUnifiedFullViewingKeys;
friend class FindUFVKId;
public:
bool SetMnemonicSeed(const MnemonicSeed& seed);
bool HaveMnemonicSeed() const;
@ -351,10 +355,15 @@ public:
*/
virtual void AddUnifiedAddress(
const libzcash::UFVKId& keyId,
const libzcash::UnifiedAddress &ua);
const std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>& ua);
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId);
const libzcash::UFVKId& keyId) const;
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver
) const;
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
@ -364,4 +373,21 @@ typedef std::map<libzcash::SproutPaymentAddress, std::vector<unsigned char> > Cr
//! Sapling
typedef std::map<libzcash::SaplingExtendedFullViewingKey, std::vector<unsigned char> > CryptedSaplingSpendingKeyMap;
class FindUFVKId {
private:
const CBasicKeyStore& keystore;
public:
FindUFVKId(const CBasicKeyStore& keystore): keystore(keystore) {}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const CScriptID& scriptId) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const CKeyID& keyId) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const libzcash::UnknownReceiver& receiver) const;
};
#endif // BITCOIN_KEYSTORE_H

View File

@ -353,6 +353,25 @@ extern "C" {
unsigned char *addr_ret
);
/**
* Decrypts a Sapling diversifier using the specified diversifier key
* to obtain the diversifier index `j` at which the diversivier was
* derived.
*
* Returns `true` if the diversifier decrypted successfully to an index,
* `false` otherwise.
*
* Arguments:
* - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key
* - addr: [c_uchar; 11] the bytes of the diversifier
* - j_ret: [c_uchar; 11] array that will store the retulgin diversifier index
*/
bool librustzcash_sapling_diversifier_index(
const unsigned char *dk,
const unsigned char *d,
unsigned char *j_ret
);
/// Fills the provided buffer with random bytes. This is intended to
/// be a cryptographically secure RNG; it uses Rust's `OsRng`, which
/// is implemented in terms of the `getrandom` crate. The first call

View File

@ -47,6 +47,7 @@ use zcash_primitives::{
constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
merkle_tree::MerklePath,
sapling::{
self,
keys::FullViewingKey,
note_encryption::sapling_ka_agree,
redjubjub::{self, Signature},
@ -1130,6 +1131,21 @@ pub extern "C" fn librustzcash_zip32_find_sapling_address(
}
}
#[no_mangle]
pub extern "C" fn librustzcash_sapling_diversifier_index(
dk: *const [c_uchar; 32],
d: *const [c_uchar; 11],
j_ret: *mut [c_uchar; 11],
) -> bool {
let dk = zip32::DiversifierKey(unsafe { *dk });
let diversifier = sapling::Diversifier(unsafe { *d });
let j_ret = unsafe { &mut *j_ret };
let j = dk.diversifier_index(&diversifier);
j_ret.copy_from_slice(&j.0);
true
}
#[no_mangle]
pub extern "C" fn librustzcash_getrandom(buf: *mut u8, buf_len: usize) {
let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };

View File

@ -604,11 +604,11 @@ UAGenerationResult CWallet::GenerateUnifiedAddress(
}
// Find a working diversifier and construct the associated address.
auto found = ufvk.FindAddress(j, receiverTypes);
auto diversifierIndex = found.second;
auto foundAddress = ufvk.FindAddress(j, receiverTypes);
auto diversifierIndex = foundAddress.second;
// Persist the newly created address to the keystore
AddUnifiedAddress(ufvkid, found.first);
AddUnifiedAddress(ufvkid, foundAddress);
if (hasTransparent) {
// Regenerate the secret key for the transparent address and add it to
@ -621,12 +621,12 @@ UAGenerationResult CWallet::GenerateUnifiedAddress(
// Save the metadata for the generated address so that we can re-derive
// it in the future.
ZcashdUnifiedAddressMetadata addrmeta(ufvkid, found.second, receiverTypes);
ZcashdUnifiedAddressMetadata addrmeta(ufvkid, foundAddress.second, receiverTypes);
mapUnifiedAddressMetadata[ufvkid].insert({diversifierIndex, receiverTypes});
if (fFileBacked) {
CWalletDB(strWalletFile).WriteUnifiedAddressMetadata(addrmeta);
}
return std::make_pair(found.first, addrmeta);
return std::make_pair(foundAddress.first, addrmeta);
} else {
return AddressGenerationError::NoSuchAccount;
}
@ -641,7 +641,7 @@ bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &k
// keystore
for (const auto &[j, receiverTypes] : mapUnifiedAddressMetadata[ufvkid]) {
auto addr = zufvk.Address(j, receiverTypes).value();
AddUnifiedAddress(ufvkid, addr);
AddUnifiedAddress(ufvkid, std::make_pair(addr, j));
}
}
return CCryptoKeyStore::AddUnifiedFullViewingKey(ufvkid, zufvk);
@ -660,9 +660,10 @@ bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &add
auto ufvk = GetUnifiedFullViewingKey(addrmeta.GetKeyID());
if (ufvk.has_value()) {
// restore unified addresses that have been previously generated
auto addr = ufvk.value().Address(addrmeta.GetDiversifierIndex(), addrmeta.GetReceiverTypes());
auto j = addrmeta.GetDiversifierIndex();
auto addr = ufvk.value().Address(j, addrmeta.GetReceiverTypes());
if (addr.has_value()) {
AddUnifiedAddress(addrmeta.GetKeyID(), addr.value());
AddUnifiedAddress(addrmeta.GetKeyID(), std::make_pair(addr.value(), j));
} else {
// an error has occurred; the ufvk is loaded but cannot reproduce the
// address identified by the address metadata.
@ -5524,6 +5525,10 @@ void CWallet::GetFilteredNotes(
}
std::optional<UnifiedAddress> CWallet::GetUnifiedForReceiver(const Receiver& receiver) {
return std::visit(LookupUnifiedAddress(*this), receiver);
}
//
// Shielded key and address generalizations
//
@ -5794,3 +5799,57 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
}
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
if (ufvkPair.has_value()) {
auto ufvkid = ufvkPair.value().first;
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
if (!(ufvk.has_value() && ufvk.value().GetSaplingKey().has_value())) {
throw std::runtime_error("CWallet::LookupUnifiedAddress(): UFVK has no Sapling key part.");
}
diversifier_index_t j;
if (wallet.mapUnifiedAddressMetadata.count(ufvkid) > 0 &&
librustzcash_sapling_diversifier_index(
ufvk.value().GetSaplingKey().value().dk.begin(),
saplingAddr.d.begin(),
j.begin()) &&
wallet.mapUnifiedAddressMetadata.at(ufvkid).count(j) > 0) {
return ufvk.value().Address(j, wallet.mapUnifiedAddressMetadata.at(ufvkid).at(j));
} else {
return std::nullopt;
}
} else {
return std::nullopt;
}
}
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const CScriptID& scriptId) const {
return std::nullopt;
}
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const CKeyID& keyId) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(keyId);
if (ufvkPair.has_value()) {
auto ufvkid = ufvkPair.value().first;
// transparent address UFVK metadata is always accompanied by the child
// index at which the address was produced
diversifier_index_t j = ufvkPair.value().second.value();
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
if (!(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value())) {
throw std::runtime_error("CWallet::LookupUnifiedAddress(): UFVK has no P2PKH key part.");
}
if (wallet.mapUnifiedAddressMetadata.count(ufvkid) > 0) {
return ufvk.value().Address(j, wallet.mapUnifiedAddressMetadata.at(ufvkid).at(j));
} else {
return std::nullopt;
}
} else {
return std::nullopt;
}
}
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const libzcash::UnknownReceiver& receiver) const {
return std::nullopt;
}

View File

@ -1154,6 +1154,8 @@ public:
const libzcash::diversifier_index_t& j,
const std::set<libzcash::ReceiverType>& receivers);
std::optional<libzcash::UnifiedAddress> GetUnifiedForReceiver(const libzcash::Receiver& receiver);
bool AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk);
bool LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &ufvk);
@ -1570,5 +1572,18 @@ public:
KeyAddResult operator()(const libzcash::InvalidEncoding& no) const;
};
class LookupUnifiedAddress {
private:
const CWallet& wallet;
public:
LookupUnifiedAddress(const CWallet& wallet): wallet(wallet) {}
std::optional<libzcash::UnifiedAddress> operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
std::optional<libzcash::UnifiedAddress> operator()(const CScriptID& scriptId) const;
std::optional<libzcash::UnifiedAddress> operator()(const CKeyID& keyId) const;
std::optional<libzcash::UnifiedAddress> operator()(const libzcash::UnknownReceiver& receiver) const;
};
#endif // BITCOIN_WALLET_WALLET_H