From 7920cbc7e702f63848221a6f5428e47e5c86906a Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 4 Oct 2019 11:38:54 -0700 Subject: [PATCH 1/8] Merge z_viewtransaction from str4d zcash PR4146 --- src/wallet/rpcwallet.cpp | 212 +++++++++++++++++++++++++++++++++++++++ src/wallet/wallet.cpp | 101 +++++++++++++++++++ src/wallet/wallet.h | 10 ++ 3 files changed, 323 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9c8565bf6..15e56564d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -53,6 +53,7 @@ #include #include +#include #include @@ -4121,6 +4122,216 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp) return result; } +UniValue z_viewtransaction(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() != 1) + throw runtime_error( + "z_viewtransaction \"txid\"\n" + "\nGet detailed shielded information about in-wallet transaction \n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "\nResult:\n" + "{\n" + " \"txid\" : \"transactionid\", (string) The transaction id\n" + " \"spends\" : [\n" + " {\n" + " \"type\" : \"sprout|sapling\", (string) The type of address\n" + " \"js\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" + " \"jsSpend\" : n, (numeric, sprout) the index of the spend within the JSDescription\n" + " \"spend\" : n, (numeric, sapling) the index of the spend within vShieldedSpend\n" + " \"txidPrev\" : \"transactionid\", (string) The id for the transaction this note was created in\n" + " \"jsPrev\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" + " \"jsOutputPrev\" : n, (numeric, sprout) the index of the output within the JSDescription\n" + " \"outputPrev\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n" + " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" + " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" + " \"valueZat\" : xxxx (numeric) The amount in zatoshis\n" + " }\n" + " ,...\n" + " ],\n" + " \"outputs\" : [\n" + " {\n" + " \"type\" : \"sprout|sapling\", (string) The type of address\n" + " \"js\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\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" + " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" + " \"recovered\" : 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" + " \"memoStr\" : \"memo\", (string) Only returned if memo contains valid UTF-8 text.\n" + " }\n" + " ,...\n" + " ],\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("z_viewtransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + + HelpExampleCli("z_viewtransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") + + HelpExampleRpc("z_viewtransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + UniValue entry(UniValue::VOBJ); + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + entry.push_back(Pair("txid", hash.GetHex())); + + UniValue spends(UniValue::VARR); + UniValue outputs(UniValue::VARR); + + // Sprout spends + for (size_t i = 0; i < wtx.vJoinSplit.size(); ++i) { + for (size_t j = 0; j < wtx.vJoinSplit[i].nullifiers.size(); ++j) { + auto nullifier = wtx.vJoinSplit[i].nullifiers[j]; + + // Fetch the note that is being spent, if ours + auto res = pwalletMain->mapSproutNullifiersToNotes.find(nullifier); + if (res == pwalletMain->mapSproutNullifiersToNotes.end()) { + continue; + } + auto jsop = res->second; + auto wtxPrev = pwalletMain->mapWallet.at(jsop.hash); + + auto decrypted = wtxPrev.DecryptSproutNote(jsop); + auto notePt = decrypted.first; + auto pa = decrypted.second; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("type", ADDR_TYPE_SPROUT)); + entry.push_back(Pair("js", (int)i)); + entry.push_back(Pair("jsSpend", (int)j)); + entry.push_back(Pair("txidPrev", jsop.hash.GetHex())); + entry.push_back(Pair("jsPrev", (int)jsop.js)); + entry.push_back(Pair("jsOutputPrev", (int)jsop.n)); + entry.push_back(Pair("address", EncodePaymentAddress(pa))); + entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); + entry.push_back(Pair("valueZat", notePt.value())); + outputs.push_back(entry); + } + } + + // Sprout outputs + for (auto & pair : wtx.mapSproutNoteData) { + JSOutPoint jsop = pair.first; + + auto decrypted = wtx.DecryptSproutNote(jsop); + auto notePt = decrypted.first; + auto pa = decrypted.second; + auto memo = notePt.memo(); + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("type", ADDR_TYPE_SPROUT)); + entry.push_back(Pair("js", (int)jsop.js)); + entry.push_back(Pair("jsOutput", (int)jsop.n)); + entry.push_back(Pair("address", EncodePaymentAddress(pa))); + entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); + entry.push_back(Pair("valueZat", notePt.value())); + entry.push_back(Pair("memo", HexStr(memo))); + if (memo[0] <= 0xf4) { + auto end = std::find_if(memo.rbegin(), memo.rend(), [](unsigned char v) { return v != 0; }); + std::string memoStr(memo.begin(), end.base()); + if (utf8::is_valid(memoStr)) { + entry.push_back(Pair("memoStr", memoStr)); + } + } + outputs.push_back(entry); + } + + // Sapling spends + std::set ovks; + for (size_t i = 0; i < wtx.vShieldedSpend.size(); ++i) { + auto spend = wtx.vShieldedSpend[i]; + + // Fetch the note that is being spent + auto res = pwalletMain->mapSaplingNullifiersToNotes.find(spend.nullifier); + if (res == pwalletMain->mapSaplingNullifiersToNotes.end()) { + continue; + } + auto op = res->second; + auto wtxPrev = pwalletMain->mapWallet.at(op.hash); + + 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)); + entry.push_back(Pair("txidPrev", op.hash.GetHex())); + entry.push_back(Pair("outputPrev", (int)op.n)); + entry.push_back(Pair("address", EncodePaymentAddress(pa))); + entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); + entry.push_back(Pair("valueZat", notePt.value())); + spends.push_back(entry); + } + + // Sapling outputs + for (uint32_t i = 0; i < wtx.vShieldedOutput.size(); ++i) { + auto op = SaplingOutPoint(hash, i); + + SaplingNotePlaintext notePt; + SaplingPaymentAddress pa; + bool isRecovered; + + auto decrypted = wtx.DecryptSaplingNote(op); + if (decrypted) { + notePt = decrypted->first; + pa = decrypted->second; + isRecovered = false; + } else { + // Try recovering the output + auto recovered = wtx.RecoverSaplingNote(op, ovks); + if (recovered) { + notePt = recovered->first; + pa = recovered->second; + isRecovered = 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("recovered", isRecovered)); + entry.push_back(Pair("address", EncodePaymentAddress(pa))); + entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); + entry.push_back(Pair("valueZat", notePt.value())); + entry.push_back(Pair("memo", HexStr(memo))); + if (memo[0] <= 0xf4) { + auto end = std::find_if(memo.rbegin(), memo.rend(), [](unsigned char v) { return v != 0; }); + std::string memoStr(memo.begin(), end.base()); + if (utf8::is_valid(memoStr)) { + entry.push_back(Pair("memoStr", memoStr)); + } + } + outputs.push_back(entry); + } + + entry.push_back(Pair("spends", spends)); + entry.push_back(Pair("outputs", outputs)); + + return entry; +} + UniValue z_getoperationresult(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -8287,6 +8498,7 @@ static const CRPCCommand commands[] = { "wallet", "z_importviewingkey", &z_importviewingkey, true }, { "wallet", "z_exportwallet", &z_exportwallet, true }, { "wallet", "z_importwallet", &z_importwallet, true }, + { "wallet", "z_viewtransaction", &z_viewtransaction, false }, // TODO: rearrange into another category { "disclosure", "z_getpaymentdisclosure", &z_getpaymentdisclosure, true }, { "disclosure", "z_validatepaymentdisclosure", &z_validatepaymentdisclosure, true } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4d834ccee..0752612f4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2487,6 +2487,107 @@ void CWalletTx::SetSaplingNoteData(mapSaplingNoteData_t ¬eData) } } +std::pair CWalletTx::DecryptSproutNote( + JSOutPoint jsop) const +{ + LOCK(pwallet->cs_wallet); + + auto nd = this->mapSproutNoteData.at(jsop); + SproutPaymentAddress pa = nd.address; + + // Get cached decryptor + ZCNoteDecryption decryptor; + if (!pwallet->GetNoteDecryptor(pa, decryptor)) { + // Note decryptors are created when the wallet is loaded, so it should always exist + throw std::runtime_error(strprintf( + "Could not find note decryptor for payment address %s", + EncodePaymentAddress(pa))); + } + + auto hSig = this->vJoinSplit[jsop.js].h_sig(*pzcashParams, this->joinSplitPubKey); + try { + SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( + decryptor, + this->vJoinSplit[jsop.js].ciphertexts[jsop.n], + this->vJoinSplit[jsop.js].ephemeralKey, + hSig, + (unsigned char) jsop.n); + + return std::make_pair(plaintext, pa); + } catch (const note_decryption_failed &err) { + // Couldn't decrypt with this spending key + throw std::runtime_error(strprintf( + "Could not decrypt note for payment address %s", + EncodePaymentAddress(pa))); + } catch (const std::exception &exc) { + // Unexpected failure + throw std::runtime_error(strprintf( + "Error while decrypting note for payment address %s: %s", + EncodePaymentAddress(pa), exc.what())); + } +} + +boost::optional> 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); + + auto maybe_pt = SaplingNotePlaintext::decrypt( + output.encCiphertext, + nd.ivk, + output.ephemeralKey, + output.cm); + assert(static_cast(maybe_pt)); + auto notePt = maybe_pt.get(); + + auto maybe_pa = nd.ivk.address(notePt.d); + assert(static_cast(maybe_pa)); + auto pa = maybe_pa.get(); + + return std::make_pair(notePt, pa); +} + +boost::optional> CWalletTx::RecoverSaplingNote( + SaplingOutPoint op, std::set& 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(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; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 296e2fa57..d5e45e95e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -567,6 +567,16 @@ public: void SetSproutNoteData(mapSproutNoteData_t ¬eData); void SetSaplingNoteData(mapSaplingNoteData_t ¬eData); + std::pair DecryptSproutNote( + JSOutPoint jsop) const; + boost::optional> DecryptSaplingNote(SaplingOutPoint op) const; + boost::optional> RecoverSaplingNote( + SaplingOutPoint op, std::set& ovks) const; + //! filter decides which addresses will count towards the debit CAmount GetDebit(const isminefilter& filter) const; CAmount GetCredit(const isminefilter& filter) const; From a9e6edeed9182f8a15cdb38b9f8140523f13ae97 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 4 Oct 2019 12:11:42 -0700 Subject: [PATCH 2/8] more stuff --- contrib/debian/copyright | 4 ++++ depends/packages/packages.mk | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 7b0f72fce..3c93ce7a9 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -68,6 +68,10 @@ Files: src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 Copyright: 2008 Paolo Bonzini License: GNU-All-permissive-License +Files: depends/sources/utfcpp-*.tar.gz +Copyright: 2006 Nemanja Trifunovic +License: Boost-Software-License-1.0 + License: Boost-Software-License-1.0 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index e29c62580..1c1a50668 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,10 +1,10 @@ rust_packages := rust librustzcash ifeq ($(build_os),darwin) - zcash_packages := libsnark libgmp libsodium + zcash_packages := libsnark libgmp libsodium utfcpp else proton_packages := proton - zcash_packages := libgmp libsodium + zcash_packages := libgmp libsodium utfcpp endif rust_crates := \ From 0ebd33efa66c2a5e4b3d9fa20e665e50c3051450 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 4 Oct 2019 12:34:34 -0700 Subject: [PATCH 3/8] fix compile --- src/wallet/rpcwallet.cpp | 6 +++--- src/wallet/wallet.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 15e56564d..fdcd6ea82 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4191,9 +4191,9 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) UniValue outputs(UniValue::VARR); // Sprout spends - for (size_t i = 0; i < wtx.vJoinSplit.size(); ++i) { - for (size_t j = 0; j < wtx.vJoinSplit[i].nullifiers.size(); ++j) { - auto nullifier = wtx.vJoinSplit[i].nullifiers[j]; + for (size_t i = 0; i < wtx.vjoinsplit.size(); ++i) { + for (size_t j = 0; j < wtx.vjoinsplit[i].nullifiers.size(); ++j) { + auto nullifier = wtx.vjoinsplit[i].nullifiers[j]; // Fetch the note that is being spent, if ours auto res = pwalletMain->mapSproutNullifiersToNotes.find(nullifier); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0752612f4..1ae73e190 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2504,12 +2504,12 @@ std::pair CWalletTx::DecryptSproutNot EncodePaymentAddress(pa))); } - auto hSig = this->vJoinSplit[jsop.js].h_sig(*pzcashParams, this->joinSplitPubKey); + auto hSig = this->vjoinsplit[jsop.js].h_sig(*pzcashParams, this->joinSplitPubKey); try { SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( decryptor, - this->vJoinSplit[jsop.js].ciphertexts[jsop.n], - this->vJoinSplit[jsop.js].ephemeralKey, + this->vjoinsplit[jsop.js].ciphertexts[jsop.n], + this->vjoinsplit[jsop.js].ephemeralKey, hSig, (unsigned char) jsop.n); From 04c4435497652177ae216a4c6abaf3baa64f9ae6 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 4 Oct 2019 12:40:15 -0700 Subject: [PATCH 4/8] safe mode is ok --- src/wallet/rpcwallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index fdcd6ea82..92f839f92 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8498,7 +8498,7 @@ static const CRPCCommand commands[] = { "wallet", "z_importviewingkey", &z_importviewingkey, true }, { "wallet", "z_exportwallet", &z_exportwallet, true }, { "wallet", "z_importwallet", &z_importwallet, true }, - { "wallet", "z_viewtransaction", &z_viewtransaction, false }, + { "wallet", "z_viewtransaction", &z_viewtransaction, true }, // TODO: rearrange into another category { "disclosure", "z_getpaymentdisclosure", &z_getpaymentdisclosure, true }, { "disclosure", "z_validatepaymentdisclosure", &z_validatepaymentdisclosure, true } From 5b94d6e4fa7eea47c340cfbf11f08be428fbf1bc Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 4 Oct 2019 18:25:21 -0700 Subject: [PATCH 5/8] still not happy --- src/rpc/client.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 665887b3b..ddc0915d7 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -151,6 +151,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "z_mergetoaddress", 2}, { "z_mergetoaddress", 3}, { "z_mergetoaddress", 4}, + { "z_viewtransaction", 1}, { "z_sendmany", 1}, { "z_sendmany", 2}, { "z_sendmany", 3}, From 812078e2128ae303ecd4cc1bae582e0433646924 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 5 Oct 2019 12:36:27 -0400 Subject: [PATCH 6/8] We don't want no sprout around here --- src/wallet/rpcwallet.cpp | 77 ++++------------------------------------ 1 file changed, 7 insertions(+), 70 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 92f839f92..fe7ac2313 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4138,15 +4138,11 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"txid\" : \"transactionid\", (string) The transaction id\n" " \"spends\" : [\n" " {\n" - " \"type\" : \"sprout|sapling\", (string) The type of address\n" - " \"js\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" - " \"jsSpend\" : n, (numeric, sprout) the index of the spend within the JSDescription\n" - " \"spend\" : n, (numeric, sapling) the index of the spend within vShieldedSpend\n" + " \"type\" : \"sapling\", (string) The type of address\n" + " \"spend\" : n, (numeric) the index of the spend within vShieldedSpend\n" " \"txidPrev\" : \"transactionid\", (string) The id for the transaction this note was created in\n" - " \"jsPrev\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" - " \"jsOutputPrev\" : n, (numeric, sprout) the index of the output within the JSDescription\n" - " \"outputPrev\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n" - " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" + " \"outputPrev\" : n, (numeric) the index of the output within the vShieldedOutput\n" + " \"address\" : \"zcashaddress\", (string) The Hush shielded address involved in the transaction\n" " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"valueZat\" : xxxx (numeric) The amount in zatoshis\n" " }\n" @@ -4155,11 +4151,9 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"outputs\" : [\n" " {\n" " \"type\" : \"sprout|sapling\", (string) The type of address\n" - " \"js\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\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) the index of the output within the vShieldedOutput\n" " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" - " \"recovered\" : true|false (boolean, sapling) True if the output is not for an address in the wallet\n" + " \"recovered\" : true|false (boolean) 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" @@ -4181,6 +4175,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) hash.SetHex(params[0].get_str()); UniValue entry(UniValue::VOBJ); + //TODO: if no txid is given, show details for most recent zutxo reported by z_listunspent if (!pwalletMain->mapWallet.count(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; @@ -4190,64 +4185,6 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) UniValue spends(UniValue::VARR); UniValue outputs(UniValue::VARR); - // Sprout spends - for (size_t i = 0; i < wtx.vjoinsplit.size(); ++i) { - for (size_t j = 0; j < wtx.vjoinsplit[i].nullifiers.size(); ++j) { - auto nullifier = wtx.vjoinsplit[i].nullifiers[j]; - - // Fetch the note that is being spent, if ours - auto res = pwalletMain->mapSproutNullifiersToNotes.find(nullifier); - if (res == pwalletMain->mapSproutNullifiersToNotes.end()) { - continue; - } - auto jsop = res->second; - auto wtxPrev = pwalletMain->mapWallet.at(jsop.hash); - - auto decrypted = wtxPrev.DecryptSproutNote(jsop); - auto notePt = decrypted.first; - auto pa = decrypted.second; - - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("type", ADDR_TYPE_SPROUT)); - entry.push_back(Pair("js", (int)i)); - entry.push_back(Pair("jsSpend", (int)j)); - entry.push_back(Pair("txidPrev", jsop.hash.GetHex())); - entry.push_back(Pair("jsPrev", (int)jsop.js)); - entry.push_back(Pair("jsOutputPrev", (int)jsop.n)); - entry.push_back(Pair("address", EncodePaymentAddress(pa))); - entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); - entry.push_back(Pair("valueZat", notePt.value())); - outputs.push_back(entry); - } - } - - // Sprout outputs - for (auto & pair : wtx.mapSproutNoteData) { - JSOutPoint jsop = pair.first; - - auto decrypted = wtx.DecryptSproutNote(jsop); - auto notePt = decrypted.first; - auto pa = decrypted.second; - auto memo = notePt.memo(); - - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("type", ADDR_TYPE_SPROUT)); - entry.push_back(Pair("js", (int)jsop.js)); - entry.push_back(Pair("jsOutput", (int)jsop.n)); - entry.push_back(Pair("address", EncodePaymentAddress(pa))); - entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); - entry.push_back(Pair("valueZat", notePt.value())); - entry.push_back(Pair("memo", HexStr(memo))); - if (memo[0] <= 0xf4) { - auto end = std::find_if(memo.rbegin(), memo.rend(), [](unsigned char v) { return v != 0; }); - std::string memoStr(memo.begin(), end.base()); - if (utf8::is_valid(memoStr)) { - entry.push_back(Pair("memoStr", memoStr)); - } - } - outputs.push_back(entry); - } - // Sapling spends std::set ovks; for (size_t i = 0; i < wtx.vShieldedSpend.size(); ++i) { From cae920cf69a4b5498b4d7090b35c2dacdbfffd2c Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 5 Oct 2019 13:05:54 -0400 Subject: [PATCH 7/8] More stuff --- src/komodo-tx.cpp | 6 ++--- src/wallet/rpcwallet.cpp | 51 ++++++++++------------------------------ src/wallet/wallet.cpp | 39 ------------------------------ 3 files changed, 15 insertions(+), 81 deletions(-) diff --git a/src/komodo-tx.cpp b/src/komodo-tx.cpp index 084d99c80..7e0477eb7 100644 --- a/src/komodo-tx.cpp +++ b/src/komodo-tx.cpp @@ -80,10 +80,10 @@ static int AppInitRawTx(int argc, char* argv[]) if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help")) { // First part of help message is specific to this utility - std::string strUsage = _("Zcash zcash-tx utility version") + " " + FormatFullVersion() + "\n\n" + + std::string strUsage = _("Hush komodo-tx utility version") + " " + FormatFullVersion() + "\n\n" + _("Usage:") + "\n" + - " zcash-tx [options] [commands] " + _("Update hex-encoded zcash transaction") + "\n" + - " zcash-tx [options] -create [commands] " + _("Create hex-encoded zcash transaction") + "\n" + + " komodo-tx [options] [commands] " + _("Update hex-encoded zcash transaction") + "\n" + + " komodo-tx [options] -create [commands] " + _("Create hex-encoded zcash transaction") + "\n" + "\n"; fprintf(stdout, "%s", strUsage.c_str()); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index fe7ac2313..9f21e3455 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -343,7 +343,7 @@ UniValue setaccount(const UniValue& params, bool fHelp) CTxDestination dest = DecodeDestination(params[0].get_str()); if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Hush address!"); } string strAccount; @@ -390,7 +390,7 @@ UniValue getaccount(const UniValue& params, bool fHelp) CTxDestination dest = DecodeDestination(params[0].get_str()); if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Hush address!"); } std::string strAccount; @@ -450,7 +450,7 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr if (nValue > curBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); - // Parse Zcash address + // Parse Hush address CScript scriptPubKey = GetScriptForDestination(address); // Create and send the transaction @@ -524,7 +524,7 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) CTxDestination dest = DecodeDestination(params[0].get_str()); if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Hush address!"); } // Amount @@ -936,7 +936,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) // Bitcoin address CTxDestination dest = DecodeDestination(params[0].get_str()); if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Hush address!"); } CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwalletMain, scriptPubKey)) { @@ -1390,7 +1390,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp) std::string strAccount = AccountFromValue(params[0]); CTxDestination dest = DecodeDestination(params[1].get_str()); if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Hush address!"); } CAmount nAmount = AmountFromValue(params[2]); if (nAmount <= 0) @@ -1487,7 +1487,7 @@ UniValue sendmany(const UniValue& params, bool fHelp) for (const std::string& name_ : keys) { CTxDestination dest = DecodeDestination(name_); if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Hush address: ") + name_); } CScript scriptPubKey = GetScriptForDestination(dest); @@ -2860,7 +2860,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) " \"txid\" : \"txid\", (string) the transaction id \n" " \"vout\" : n, (numeric) the vout value\n" " \"generated\" : true|false (boolean) true if txout is a coinbase transaction output\n" - " \"address\" : \"address\", (string) the Zcash address\n" + " \"address\" : \"address\", (string) the Hush address\n" " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" @@ -2894,7 +2894,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) const UniValue& input = inputs[idx]; CTxDestination dest = DecodeDestination(input.get_str()); if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + input.get_str()); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Hush address: ") + input.get_str()); } if (!destinations.insert(dest).second) { throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str()); @@ -3018,13 +3018,12 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) "Optionally filter to only include notes sent to specified addresses.\n" "When minconf is 0, unspent notes with zero confirmations are returned, even though they are not immediately spendable.\n" "Results are an array of Objects, each of which has:\n" - "{txid, jsindex, jsoutindex, confirmations, address, amount, memo} (Sprout)\n" "{txid, outindex, confirmations, address, amount, memo} (Sapling)\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" "3. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n" - "4. \"addresses\" (string) A json array of zaddrs (both Sprout and Sapling) to filter on. Duplicate addresses not allowed.\n" + "4. \"addresses\" (string) A json array of zaddrs to filter on. Duplicate addresses not allowed.\n" " [\n" " \"address\" (string) zaddr\n" " ,...\n" @@ -3131,32 +3130,6 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); std::set> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs); - for (auto & entry : sproutEntries) { - UniValue obj(UniValue::VOBJ); - - int nHeight = tx_height(entry.jsop.hash); - int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations); - // Only return notarized results when minconf>1 - if (nMinDepth > 1 && dpowconfs == 1) - continue; - - obj.push_back(Pair("txid", entry.jsop.hash.ToString())); - obj.push_back(Pair("jsindex", (int)entry.jsop.js )); - obj.push_back(Pair("jsoutindex", (int)entry.jsop.n)); - obj.push_back(Pair("confirmations", dpowconfs)); - obj.push_back(Pair("rawconfirmations", entry.confirmations)); - bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(boost::get(entry.address)); - obj.push_back(Pair("spendable", hasSproutSpendingKey)); - obj.push_back(Pair("address", EncodePaymentAddress(entry.address))); - obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value())))); - std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end()); - obj.push_back(Pair("memo", HexStr(data))); - if (hasSproutSpendingKey) { - obj.push_back(Pair("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop))); - } - results.push_back(obj); - } - for (auto & entry : saplingEntries) { UniValue obj(UniValue::VOBJ); @@ -4150,9 +4123,9 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " ],\n" " \"outputs\" : [\n" " {\n" - " \"type\" : \"sprout|sapling\", (string) The type of address\n" + " \"type\" : \"sapling\", (string) The type of address\n" " \"output\" : n, (numeric) the index of the output within the vShieldedOutput\n" - " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" + " \"address\" : \"hushaddress\", (string) The Hush address involved in the transaction\n" " \"recovered\" : true|false (boolean) 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" diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1ae73e190..7006dd0d0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2487,45 +2487,6 @@ void CWalletTx::SetSaplingNoteData(mapSaplingNoteData_t ¬eData) } } -std::pair CWalletTx::DecryptSproutNote( - JSOutPoint jsop) const -{ - LOCK(pwallet->cs_wallet); - - auto nd = this->mapSproutNoteData.at(jsop); - SproutPaymentAddress pa = nd.address; - - // Get cached decryptor - ZCNoteDecryption decryptor; - if (!pwallet->GetNoteDecryptor(pa, decryptor)) { - // Note decryptors are created when the wallet is loaded, so it should always exist - throw std::runtime_error(strprintf( - "Could not find note decryptor for payment address %s", - EncodePaymentAddress(pa))); - } - - auto hSig = this->vjoinsplit[jsop.js].h_sig(*pzcashParams, this->joinSplitPubKey); - try { - SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( - decryptor, - this->vjoinsplit[jsop.js].ciphertexts[jsop.n], - this->vjoinsplit[jsop.js].ephemeralKey, - hSig, - (unsigned char) jsop.n); - - return std::make_pair(plaintext, pa); - } catch (const note_decryption_failed &err) { - // Couldn't decrypt with this spending key - throw std::runtime_error(strprintf( - "Could not decrypt note for payment address %s", - EncodePaymentAddress(pa))); - } catch (const std::exception &exc) { - // Unexpected failure - throw std::runtime_error(strprintf( - "Error while decrypting note for payment address %s: %s", - EncodePaymentAddress(pa), exc.what())); - } -} boost::optional Date: Sat, 5 Oct 2019 15:44:07 -0400 Subject: [PATCH 8/8] Add nullifier, anchor, commitment and rk --- src/wallet/rpcwallet.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9f21e3455..2bc06d2d2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3033,7 +3033,6 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) " {\n" " \"txid\" : \"txid\", (string) the transaction id \n" " \"jsindex\" : n (numeric) the joinsplit index\n" - " \"jsoutindex\" (sprout) : n (numeric) the output index of the joinsplit\n" " \"outindex\" (sapling) : n (numeric) the output index\n" " \"confirmations\" : n (numeric) the number of confirmations\n" " \"spendable\" : true|false (boolean) true if note can be spent by wallet, false if note has zero confirmations, false if address is watchonly\n" @@ -3095,7 +3094,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) string address = o.get_str(); auto zaddr = DecodePaymentAddress(address); if (!IsValidPaymentAddress(zaddr)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address); + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid Hush zaddr: ") + address); } auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); if (!fIncludeWatchonly && !hasSpendingKey) { @@ -3111,14 +3110,9 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) } else { // User did not provide zaddrs, so use default i.e. all addresses - std::set sproutzaddrs = {}; - pwalletMain->GetSproutPaymentAddresses(sproutzaddrs); - - // Sapling support std::set saplingzaddrs = {}; pwalletMain->GetSaplingPaymentAddresses(saplingzaddrs); - zaddrs.insert(sproutzaddrs.begin(), sproutzaddrs.end()); zaddrs.insert(saplingzaddrs.begin(), saplingzaddrs.end()); } @@ -4148,15 +4142,16 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) hash.SetHex(params[0].get_str()); UniValue entry(UniValue::VOBJ); - //TODO: if no txid is given, show details for most recent zutxo reported by z_listunspent + if (!pwalletMain->mapWallet.count(hash)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet Hush transaction id!"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; entry.push_back(Pair("txid", hash.GetHex())); UniValue spends(UniValue::VARR); UniValue outputs(UniValue::VARR); + char str[64]; // Sapling spends std::set ovks; @@ -4166,14 +4161,14 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) // Fetch the note that is being spent auto res = pwalletMain->mapSaplingNullifiersToNotes.find(spend.nullifier); if (res == pwalletMain->mapSaplingNullifiersToNotes.end()) { + fprintf(stderr,"Could not find spending note %s", uint256_str(str, spend.nullifier)); continue; } - auto op = res->second; - auto wtxPrev = pwalletMain->mapWallet.at(op.hash); - + auto op = res->second; + auto wtxPrev = pwalletMain->mapWallet.at(op.hash); auto decrypted = wtxPrev.DecryptSaplingNote(op).get(); - auto notePt = decrypted.first; - auto pa = decrypted.second; + auto notePt = decrypted.first; + auto pa = decrypted.second; // Store the OutgoingViewingKey for recovering outputs libzcash::SaplingFullViewingKey fvk; @@ -4183,6 +4178,10 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) UniValue entry(UniValue::VOBJ); entry.push_back(Pair("type", ADDR_TYPE_SAPLING)); entry.push_back(Pair("spend", (int)i)); + entry.push_back(Pair("nullifier", uint256_str(str,spend.nullifier))); + entry.push_back(Pair("anchor", uint256_str(str,spend.anchor))); + entry.push_back(Pair("commitment", uint256_str(str,spend.cv))); + entry.push_back(Pair("rk", uint256_str(str,spend.rk))); entry.push_back(Pair("txidPrev", op.hash.GetHex())); entry.push_back(Pair("outputPrev", (int)op.n)); entry.push_back(Pair("address", EncodePaymentAddress(pa)));