Implement OVK selection for z_sendmany.

This commit is contained in:
Kris Nuttycombe 2022-01-24 20:40:07 -07:00
parent bdcb12e445
commit 3ddbe1fa06
11 changed files with 143 additions and 172 deletions

View File

@ -103,6 +103,22 @@ UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components(
const unsigned char* t_key,
const unsigned char* sapling_key);
/**
* Derive the internal and external OVKs for the binary encoding
* of a transparent FVK (the concatenated bytes of the serialized
* `(ChainCode, CPubKey)` pair.)
*
* Returns `true` if `t_key` was successfully deserialized,
* in which case `internal_ovk_ret` and `external_ovk_ret` (which
* should both point to 32-byte arrays) will have been updated
* with the appropriate key bytes; otherwise, this procedure
* returns `false` and the return values are unmodified.
*/
bool transparent_key_ovks(
const unsigned char* t_key,
unsigned char* internal_ovk_ret,
unsigned char* external_ovk_ret);
#ifdef __cplusplus
}
#endif

View File

@ -261,8 +261,8 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(txOutputAmounts_.z_outputs_total));
LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(fee_));
auto ovks = this->SelectOVKs();
auto selectorAccountId = pwalletMain->FindAccountForSelector(ztxoSelector_);
auto ovks = this->SelectOVKs(spendable, selectorAccountId);
std::visit(match {
[&](const CKeyID& keyId) {
auto accountId = selectorAccountId.value_or(ZCASH_LEGACY_ACCOUNT);
@ -415,10 +415,64 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
return tx.GetHash();
}
std::pair<uint256, uint256> AsyncRPCOperation_sendmany::SelectOVKs() const {
//TODO
std::pair<uint256, uint256> AsyncRPCOperation_sendmany::SelectOVKs(
const SpendableInputs& spendable, std::optional<AccountId> accountId) const {
uint256 internalOVK;
uint256 externalOVK;
if (!spendable.saplingNoteEntries.empty()) {
std::visit(match {
[&](const libzcash::SaplingPaymentAddress& addr) {
libzcash::SaplingExtendedSpendingKey extsk;
assert(pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk));
auto extfvk = extsk.ToXFVK();
externalOVK = extfvk.fvk.ovk;
internalOVK = extfvk.GetInternalDFVK().fvk.ovk;
},
[&](const AccountZTXOPattern& acct) {
auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId()).value();
auto dfvk = ufvk.GetSaplingKey().value();
externalOVK = dfvk.fvk.ovk;
internalOVK = dfvk.GetInternalDFVK().fvk.ovk;
},
[&](const auto& other) {
throw std::runtime_error("unreachable");
}
}, this->ztxoSelector_.GetPattern());
} else if (!spendable.utxos.empty()) {
std::optional<transparent::AccountPubKey> tfvk;
std::visit(match {
[&](const CKeyID& keyId) {
tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey();
},
[&](const CScriptID& keyId) {
tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey();
},
[&](const AccountZTXOPattern& acct) {
// by the time we're here, we know that the UFVK exists for this account
auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId()).value();
tfvk = ufvk.GetTransparentKey().value();
},
[&](const auto& other) {
//unreachable
}
}, this->ztxoSelector_.GetPattern());
assert(tfvk.has_value());
auto ovks = tfvk.value().GetOVKsForShielding();
internalOVK = ovks.first;
externalOVK = ovks.second;
} else if (!spendable.sproutNoteEntries.empty()) {
// use the legacy transparent account OVKs when sending from Sprout
auto tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey();
auto ovks = tfvk.GetOVKsForShielding();
internalOVK = ovks.first;
externalOVK = ovks.second;
} else {
// This should be unreachable; it is left in place as a guard to ensure
// that when new input types are added to SpendableInputs in the future
// that we do not accidentally return the all-zeros OVK.
throw std::runtime_error("No spendable inputs.");
}
return std::make_pair(internalOVK, externalOVK);
}

View File

@ -83,10 +83,9 @@ private:
/**
* Compute the internal and external OVKs to use in transaction construction, given
* the payment source and the set of types that correspond to outputs selected for
* being spent in the transaction.
* the spendable inputs.
*/
std::pair<uint256, uint256> SelectOVKs() const;
std::pair<uint256, uint256> SelectOVKs(const SpendableInputs& spendable, std::optional<AccountId> accountId) const;
static CAmount DefaultDustThreshold();

View File

@ -260,27 +260,13 @@ CPubKey CWallet::GenerateNewKey()
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
auto seedOpt = GetMnemonicSeed();
if (!seedOpt.has_value()) {
throw std::runtime_error(
"CWallet::GenerateNewKey(): Wallet does not have a mnemonic seed.");
}
auto seed = seedOpt.value();
if (!mnemonicHDChain.has_value()) {
throw std::runtime_error(
"CWallet::GenerateNewKey(): Wallet is missing mnemonic seed metadata.");
}
CHDChain& hdChain = mnemonicHDChain.value();
// All mnemonic seeds are checked at construction to ensure that we can obtain
// a valid spending key for the account ZCASH_LEGACY_ACCOUNT;
// therefore, the `value()` call here is safe.
transparent::AccountKey accountKey = transparent::AccountKey::ForAccount(
seed,
BIP44CoinType(),
ZCASH_LEGACY_ACCOUNT).value();
transparent::AccountKey accountKey = this->GetLegacyAccountKey();
std::optional<CPubKey> pubkey = std::nullopt;
do {
auto index = hdChain.GetLegacyTKeyCounter();
@ -288,7 +274,10 @@ CPubKey CWallet::GenerateNewKey()
hdChain.IncrementLegacyTKeyCounter();
if (key.has_value()) {
auto keyPath = transparent::AccountKey::KeyPath(BIP44CoinType(), ZCASH_LEGACY_ACCOUNT, true, index);
pubkey = AddTransparentSecretKey(seed.Fingerprint(), std::make_pair(key.value(), keyPath));
pubkey = AddTransparentSecretKey(
hdChain.GetSeedFingerprint(),
std::make_pair(key.value(), keyPath)
);
}
// if we did not successfully generate a key, try again.
} while (!pubkey.has_value());
@ -423,6 +412,24 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi
return false;
}
libzcash::transparent::AccountKey CWallet::GetLegacyAccountKey() const {
auto seedOpt = GetMnemonicSeed();
if (!seedOpt.has_value()) {
throw std::runtime_error(
"CWallet::GenerateNewKey(): Wallet does not have a mnemonic seed.");
}
auto seed = seedOpt.value();
// All mnemonic seeds are checked at construction to ensure that we can obtain
// a valid spending key for the account ZCASH_LEGACY_ACCOUNT;
// therefore, the `value()` call here is safe.
return transparent::AccountKey::ForAccount(
seed,
BIP44CoinType(),
ZCASH_LEGACY_ACCOUNT).value();
}
std::pair<ZcashdUnifiedSpendingKey, libzcash::AccountId> CWallet::GenerateNewUnifiedSpendingKey() {
AssertLockHeld(cs_wallet);
@ -6142,81 +6149,6 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
return std::nullopt;
}
// GetSproutKeyForPaymentAddress
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
libzcash::SproutSpendingKey k;
if (m_wallet->GetSproutSpendingKey(zaddr, k)) {
return k;
} else {
return std::nullopt;
}
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
return std::nullopt;
}
// GetSaplingKeyForPaymentAddress
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(zaddr, extsk)) {
return extsk;
} else {
return std::nullopt;
}
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
for (const libzcash::Receiver& receiver: uaddr) {
auto saplingAddr = std::get_if<SaplingPaymentAddress>(&receiver);
if (saplingAddr != nullptr) {
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(*saplingAddr, extsk)) {
return extsk;
}
}
}
return std::nullopt;
}
// AddViewingKeyToWallet
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey &vkey) const {

View File

@ -1399,9 +1399,13 @@ public:
const std::vector<unsigned char> &vchCryptedSecret);
//
// Unified keys & addresses
// Unified keys, addresses, and accounts
//
//! Obtain the account key for the legacy account by deriving it from
//! the wallet's mnemonic seed.
libzcash::transparent::AccountKey GetLegacyAccountKey() const;
//! Generate the unified spending key from the wallet's mnemonic seed
//! for the next unused account identifier.
std::pair<libzcash::ZcashdUnifiedSpendingKey, libzcash::AccountId>
@ -1766,34 +1770,6 @@ public:
std::optional<libzcash::ViewingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
class GetSproutKeyForPaymentAddress
{
private:
CWallet *m_wallet;
public:
GetSproutKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::SproutSpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
class GetSaplingKeyForPaymentAddress
{
private:
CWallet *m_wallet;
public:
GetSaplingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
enum PaymentAddressSource {
Random,
LegacyHDSeed,

View File

@ -2,6 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include <rust/unified_keys.h>
#include "streams.h"
#include "transparent.h"
namespace libzcash {
@ -33,6 +36,23 @@ std::optional<CKeyID> AccountPubKey::GetChangeAddress(const diversifier_index_t&
return changeKey.value().GetID();
}
std::pair<uint256, uint256> AccountPubKey::GetOVKsForShielding() const {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pubkey;
assert(ss.size() == 65);
CSerializeData ss_bytes(ss.begin(), ss.end());
uint256 internalOVK;
uint256 externalOVK;
assert(transparent_key_ovks(
reinterpret_cast<unsigned char*>(ss_bytes.data()),
internalOVK.begin(),
externalOVK.begin()));
return std::make_pair(internalOVK, externalOVK);
}
std::optional<std::pair<CKeyID, diversifier_index_t>> AccountPubKey::FindChangeAddress(diversifier_index_t j) const {
while (true) {
auto childIndex = j.ToTransparentChildIndex();

View File

@ -41,6 +41,12 @@ public:
*/
std::optional<std::pair<CKeyID, diversifier_index_t>> FindChangeAddress(diversifier_index_t j) const;
/**
* Return the internal and external OVKs for shielding from transparent
* addresses derived from this key.
*/
std::pair<uint256, uint256> GetOVKsForShielding() const;
friend bool operator==(const AccountPubKey& a, const AccountPubKey& b)
{
return a.pubkey == b.pubkey;

View File

@ -5,6 +5,8 @@
#include "zcash/Address.hpp"
#include "unified.h"
#include <rust/unified_keys.h>
using namespace libzcash;
//
@ -126,9 +128,11 @@ std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress(co
std::optional<RecipientAddress> addr;
std::visit(match {
[&](const TransparentChangeRequest& req) {
auto changeKey = this->GetTransparentChangeAddress(req.GetIndex());
if (changeKey.has_value()) {
addr = changeKey.value();
if (transparentKey.has_value()) {
auto changeAddr = transparentKey.value().GetChangeAddress(req.GetIndex());
if (changeAddr.has_value()) {
addr = changeAddr.value();
}
}
},
[&](const SaplingChangeRequest& req) {
@ -141,38 +145,3 @@ std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress(co
}, req);
return addr;
}
std::optional<CKeyID> ZcashdUnifiedFullViewingKey::GetTransparentChangeAddress(const diversifier_index_t& j) const {
if (transparentKey.has_value()) {
auto childIndex = j.ToTransparentChildIndex();
if (!childIndex.has_value()) return std::nullopt;
auto changeKey = transparentKey.value().DeriveInternal(childIndex.value());
if (!changeKey.has_value()) return std::nullopt;
return changeKey.value().GetID();
} else {
return std::nullopt;
}
}
//std::optional<std::pair<uint256, uint256>> ZcashdUnifiedFullViewingKey::GetTransparentOVKsForShielding() const {
// if (transparentKey.has_value()) {
// CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
// ss << transparentKey.value().GetPubKey();
// assert(ss.size() == 65);
// CSerializeData ss_bytes(ss.begin(), ss.end());
//
// uint256 internalOVK;
// uint256 externalOVK;
//
// assert(transparent_key_ovks(
// reinterpret_cast<unsigned char*>(ss_bytes.data()),
// internalOVK.begin(),
// externalOVK.begin()));
//
// return std::make_pair(internalOVK, externalOVK);
// } else {
// return std::nullopt;
// }
//}

View File

@ -195,13 +195,6 @@ public:
*/
std::optional<RecipientAddress> GetChangeAddress(const ChangeRequest& req) const;
/**
* Return the transparent change address for this UFVK a the given diversifier
* index, if the UFVK has a transparent component and it is possible to derive
* an address at this index.
*/
std::optional<CKeyID> GetTransparentChangeAddress(const diversifier_index_t& j) const;
friend bool operator==(const ZcashdUnifiedFullViewingKey& a, const ZcashdUnifiedFullViewingKey& b)
{
return a.transparentKey == b.transparentKey && a.saplingKey == b.saplingKey;

View File

@ -126,7 +126,7 @@ libzcash::SaplingPaymentAddress SaplingDiversifiableFullViewingKey::DefaultAddre
}
}
libzcash::SaplingPaymentAddress SaplingDiversifiableFullViewingKey::GetChangeAddress() const {
libzcash::SaplingDiversifiableFullViewingKey SaplingDiversifiableFullViewingKey::GetInternalDFVK() const {
CDataStream ss_fvk(SER_NETWORK, PROTOCOL_VERSION);
ss_fvk << fvk;
CSerializeData fvk_bytes(ss_fvk.begin(), ss_fvk.end());
@ -141,7 +141,11 @@ libzcash::SaplingPaymentAddress SaplingDiversifiableFullViewingKey::GetChangeAdd
CDataStream ss_fvk_ret(fvk_bytes_ret, SER_NETWORK, PROTOCOL_VERSION);
ss_fvk_ret >> internalDFVK.fvk;
return internalDFVK;
}
libzcash::SaplingPaymentAddress SaplingDiversifiableFullViewingKey::GetChangeAddress() const {
auto internalDFVK = this->GetInternalDFVK();
return internalDFVK.DefaultAddress();
}

View File

@ -153,6 +153,8 @@ public:
return std::make_pair(addr.value(), j);
}
SaplingDiversifiableFullViewingKey GetInternalDFVK() const;
libzcash::SaplingPaymentAddress DefaultAddress() const;
libzcash::SaplingPaymentAddress GetChangeAddress() const;