RPC: Use OutgoingViewingKeys to recover non-wallet Sapling outputs
This commit is contained in:
parent
f77915c587
commit
347de0aae2
|
@ -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"
|
" \"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"
|
" \"output\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n"
|
||||||
" \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\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"
|
" \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n"
|
||||||
" \"valueZat\" : xxxx (numeric) The amount in zatoshis\n"
|
" \"valueZat\" : xxxx (numeric) The amount in zatoshis\n"
|
||||||
" \"memo\" : \"hexmemo\", (string) Hexademical string representation of the memo field\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);
|
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
|
// Sapling spends
|
||||||
for (size_t i = 0; i < wtx.vShieldedSpend.size(); ++i) {
|
for (size_t i = 0; i < wtx.vShieldedSpend.size(); ++i) {
|
||||||
auto spend = wtx.vShieldedSpend[i];
|
auto spend = wtx.vShieldedSpend[i];
|
||||||
|
@ -3663,10 +3672,15 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
|
||||||
auto op = res->second;
|
auto op = res->second;
|
||||||
auto wtxPrev = pwalletMain->mapWallet.at(op.hash);
|
auto wtxPrev = pwalletMain->mapWallet.at(op.hash);
|
||||||
|
|
||||||
auto decrypted = wtxPrev.DecryptSaplingNote(op);
|
auto decrypted = wtxPrev.DecryptSaplingNote(op).get();
|
||||||
auto notePt = decrypted.first;
|
auto notePt = decrypted.first;
|
||||||
auto pa = decrypted.second;
|
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);
|
UniValue entry(UniValue::VOBJ);
|
||||||
entry.push_back(Pair("type", ADDR_TYPE_SAPLING));
|
entry.push_back(Pair("type", ADDR_TYPE_SAPLING));
|
||||||
entry.push_back(Pair("spend", (int)i));
|
entry.push_back(Pair("spend", (int)i));
|
||||||
|
@ -3679,17 +3693,36 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sapling outputs
|
// Sapling outputs
|
||||||
for (auto & pair : wtx.mapSaplingNoteData) {
|
for (uint32_t i = 0; i < wtx.vShieldedOutput.size(); ++i) {
|
||||||
SaplingOutPoint op = pair.first;
|
auto op = SaplingOutPoint(hash, i);
|
||||||
|
|
||||||
|
SaplingNotePlaintext notePt;
|
||||||
|
SaplingPaymentAddress pa;
|
||||||
|
bool isOutgoing;
|
||||||
|
|
||||||
auto decrypted = wtx.DecryptSaplingNote(op);
|
auto decrypted = wtx.DecryptSaplingNote(op);
|
||||||
auto notePt = decrypted.first;
|
if (decrypted) {
|
||||||
auto pa = decrypted.second;
|
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();
|
auto memo = notePt.memo();
|
||||||
|
|
||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
entry.push_back(Pair("type", ADDR_TYPE_SAPLING));
|
entry.push_back(Pair("type", ADDR_TYPE_SAPLING));
|
||||||
entry.push_back(Pair("output", (int)op.n));
|
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("address", EncodePaymentAddress(pa)));
|
||||||
entry.push_back(Pair("value", ValueFromAmount(notePt.value())));
|
entry.push_back(Pair("value", ValueFromAmount(notePt.value())));
|
||||||
entry.push_back(Pair("valueZat", notePt.value()));
|
entry.push_back(Pair("valueZat", notePt.value()));
|
||||||
|
|
|
@ -2258,9 +2258,15 @@ std::pair<SproutNotePlaintext, SproutPaymentAddress> CWalletTx::DecryptSproutNot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<SaplingNotePlaintext, SaplingPaymentAddress> CWalletTx::DecryptSaplingNote(
|
boost::optional<std::pair<
|
||||||
SaplingOutPoint op) const
|
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 output = this->vShieldedOutput[op.n];
|
||||||
auto nd = this->mapSaplingNoteData.at(op);
|
auto nd = this->mapSaplingNoteData.at(op);
|
||||||
|
|
||||||
|
@ -2279,6 +2285,40 @@ std::pair<SaplingNotePlaintext, SaplingPaymentAddress> CWalletTx::DecryptSapling
|
||||||
return std::make_pair(notePt, pa);
|
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 CWalletTx::GetTxTime() const
|
||||||
{
|
{
|
||||||
int64_t n = nTimeSmart;
|
int64_t n = nTimeSmart;
|
||||||
|
|
|
@ -557,8 +557,13 @@ public:
|
||||||
|
|
||||||
std::pair<libzcash::SproutNotePlaintext, libzcash::SproutPaymentAddress> DecryptSproutNote(
|
std::pair<libzcash::SproutNotePlaintext, libzcash::SproutPaymentAddress> DecryptSproutNote(
|
||||||
JSOutPoint jsop) const;
|
JSOutPoint jsop) const;
|
||||||
std::pair<libzcash::SaplingNotePlaintext, libzcash::SaplingPaymentAddress> DecryptSaplingNote(
|
boost::optional<std::pair<
|
||||||
SaplingOutPoint op) const;
|
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
|
//! filter decides which addresses will count towards the debit
|
||||||
CAmount GetDebit(const isminefilter& filter) const;
|
CAmount GetDebit(const isminefilter& filter) const;
|
||||||
|
|
Loading…
Reference in New Issue