RPC: Use OutgoingViewingKeys to recover non-wallet Sapling outputs

This commit is contained in:
Jack Grigg 2019-09-27 14:54:45 +01:00
parent f77915c587
commit 347de0aae2
3 changed files with 87 additions and 9 deletions

View File

@ -3553,6 +3553,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
" \"jsOutput\" : n, (numeric, sprout) the index of the output within the JSDescription\n"
" \"output\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n"
" \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n"
" \"outgoing\" : true|false (boolean, sapling) True if the output is not for an address in the wallet\n"
" \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n"
" \"valueZat\" : xxxx (numeric) The amount in zatoshis\n"
" \"memo\" : \"hexmemo\", (string) Hexademical string representation of the memo field\n"
@ -3651,6 +3652,14 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
outputs.push_back(entry);
}
// Collect OutgoingViewingKeys for recovering output information
std::set<uint256> ovks;
{
// Generate the common ovk for recovering t->z outputs.
HDSeed seed = pwalletMain->GetHDSeedForRPC();
ovks.insert(ovkForShieldingFromTaddr(seed));
}
// Sapling spends
for (size_t i = 0; i < wtx.vShieldedSpend.size(); ++i) {
auto spend = wtx.vShieldedSpend[i];
@ -3663,10 +3672,15 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
auto op = res->second;
auto wtxPrev = pwalletMain->mapWallet.at(op.hash);
auto decrypted = wtxPrev.DecryptSaplingNote(op);
auto decrypted = wtxPrev.DecryptSaplingNote(op).get();
auto notePt = decrypted.first;
auto pa = decrypted.second;
// Store the OutgoingViewingKey for recovering outputs
libzcash::SaplingFullViewingKey fvk;
assert(pwalletMain->GetSaplingFullViewingKey(wtxPrev.mapSaplingNoteData.at(op).ivk, fvk));
ovks.insert(fvk.ovk);
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("type", ADDR_TYPE_SAPLING));
entry.push_back(Pair("spend", (int)i));
@ -3679,17 +3693,36 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
}
// Sapling outputs
for (auto & pair : wtx.mapSaplingNoteData) {
SaplingOutPoint op = pair.first;
for (uint32_t i = 0; i < wtx.vShieldedOutput.size(); ++i) {
auto op = SaplingOutPoint(hash, i);
SaplingNotePlaintext notePt;
SaplingPaymentAddress pa;
bool isOutgoing;
auto decrypted = wtx.DecryptSaplingNote(op);
auto notePt = decrypted.first;
auto pa = decrypted.second;
if (decrypted) {
notePt = decrypted->first;
pa = decrypted->second;
isOutgoing = false;
} else {
// Try recovering the output
auto recovered = wtx.RecoverSaplingNote(op, ovks);
if (recovered) {
notePt = recovered->first;
pa = recovered->second;
isOutgoing = true;
} else {
// Unreadable
continue;
}
}
auto memo = notePt.memo();
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("type", ADDR_TYPE_SAPLING));
entry.push_back(Pair("output", (int)op.n));
entry.push_back(Pair("outgoing", isOutgoing));
entry.push_back(Pair("address", EncodePaymentAddress(pa)));
entry.push_back(Pair("value", ValueFromAmount(notePt.value())));
entry.push_back(Pair("valueZat", notePt.value()));

View File

@ -2258,9 +2258,15 @@ std::pair<SproutNotePlaintext, SproutPaymentAddress> CWalletTx::DecryptSproutNot
}
}
std::pair<SaplingNotePlaintext, SaplingPaymentAddress> CWalletTx::DecryptSaplingNote(
SaplingOutPoint op) const
boost::optional<std::pair<
SaplingNotePlaintext,
SaplingPaymentAddress>> CWalletTx::DecryptSaplingNote(SaplingOutPoint op) const
{
// Check whether we can decrypt this SaplingOutPoint
if (this->mapSaplingNoteData.count(op) == 0) {
return boost::none;
}
auto output = this->vShieldedOutput[op.n];
auto nd = this->mapSaplingNoteData.at(op);
@ -2279,6 +2285,40 @@ std::pair<SaplingNotePlaintext, SaplingPaymentAddress> CWalletTx::DecryptSapling
return std::make_pair(notePt, pa);
}
boost::optional<std::pair<
SaplingNotePlaintext,
SaplingPaymentAddress>> CWalletTx::RecoverSaplingNote(
SaplingOutPoint op, std::set<uint256>& ovks) const
{
auto output = this->vShieldedOutput[op.n];
for (auto ovk : ovks) {
auto outPt = SaplingOutgoingPlaintext::decrypt(
output.outCiphertext,
ovk,
output.cv,
output.cm,
output.ephemeralKey);
if (!outPt) {
continue;
}
auto maybe_pt = SaplingNotePlaintext::decrypt(
output.encCiphertext,
output.ephemeralKey,
outPt->esk,
outPt->pk_d,
output.cm);
assert(static_cast<bool>(maybe_pt));
auto notePt = maybe_pt.get();
return std::make_pair(notePt, SaplingPaymentAddress(notePt.d, outPt->pk_d));
}
// Couldn't recover with any of the provided OutgoingViewingKeys
return boost::none;
}
int64_t CWalletTx::GetTxTime() const
{
int64_t n = nTimeSmart;

View File

@ -557,8 +557,13 @@ public:
std::pair<libzcash::SproutNotePlaintext, libzcash::SproutPaymentAddress> DecryptSproutNote(
JSOutPoint jsop) const;
std::pair<libzcash::SaplingNotePlaintext, libzcash::SaplingPaymentAddress> DecryptSaplingNote(
SaplingOutPoint op) const;
boost::optional<std::pair<
libzcash::SaplingNotePlaintext,
libzcash::SaplingPaymentAddress>> DecryptSaplingNote(SaplingOutPoint op) const;
boost::optional<std::pair<
libzcash::SaplingNotePlaintext,
libzcash::SaplingPaymentAddress>> RecoverSaplingNote(
SaplingOutPoint op, std::set<uint256>& ovks) const;
//! filter decides which addresses will count towards the debit
CAmount GetDebit(const isminefilter& filter) const;