Integrate latest Zcash fixes and update for non-latin user names

This commit is contained in:
miketout
2018-10-27 20:29:03 -07:00
33 changed files with 1422 additions and 306 deletions

View File

@@ -399,7 +399,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
if (!pwalletMain->GetHDSeed(seed)) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"CWallet::GenerateNewSaplingZKey(): HD seed not found");
"AsyncRPCOperation_sendmany::main_impl(): HD seed not found");
}
ovk = ovkForShieldingFromTaddr(seed);
}

View File

@@ -105,7 +105,7 @@ TEST(wallet_zkeys_tests, StoreAndLoadSaplingZkeys) {
/**
* This test covers methods on CWallet
* GenerateNewZKey()
* GenerateNewSproutZKey()
* AddSproutZKey()
* LoadZKey()
* LoadZKeyMetadata()
@@ -121,9 +121,7 @@ TEST(wallet_zkeys_tests, store_and_load_zkeys) {
ASSERT_EQ(0, addrs.size());
// wallet should have one key
auto address = wallet.GenerateNewZKey();
ASSERT_NE(boost::get<libzcash::SproutPaymentAddress>(&address), nullptr);
auto addr = boost::get<libzcash::SproutPaymentAddress>(address);
auto addr = wallet.GenerateNewSproutZKey();
wallet.GetSproutPaymentAddresses(addrs);
ASSERT_EQ(1, addrs.size());
@@ -236,7 +234,7 @@ TEST(wallet_zkeys_tests, write_zkey_direct_to_db) {
ASSERT_EQ(0, addrs.size());
// Add random key to the wallet
auto paymentAddress = wallet.GenerateNewZKey();
auto paymentAddress = wallet.GenerateNewSproutZKey();
// wallet should have one key
wallet.GetSproutPaymentAddresses(addrs);
@@ -353,9 +351,7 @@ TEST(wallet_zkeys_tests, write_cryptedzkey_direct_to_db) {
ASSERT_EQ(0, addrs.size());
// Add random key to the wallet
auto address = wallet.GenerateNewZKey();
ASSERT_NE(boost::get<libzcash::SproutPaymentAddress>(&address), nullptr);
auto paymentAddress = boost::get<libzcash::SproutPaymentAddress>(address);
auto paymentAddress = wallet.GenerateNewSproutZKey();
// wallet should have one key
wallet.GetSproutPaymentAddresses(addrs);
@@ -368,13 +364,11 @@ TEST(wallet_zkeys_tests, write_cryptedzkey_direct_to_db) {
ASSERT_TRUE(wallet.EncryptWallet(strWalletPass));
// adding a new key will fail as the wallet is locked
EXPECT_ANY_THROW(wallet.GenerateNewZKey());
EXPECT_ANY_THROW(wallet.GenerateNewSproutZKey());
// unlock wallet and then add
wallet.Unlock(strWalletPass);
auto address2 = wallet.GenerateNewZKey();
ASSERT_NE(boost::get<libzcash::SproutPaymentAddress>(&address2), nullptr);
auto paymentAddress2 = boost::get<libzcash::SproutPaymentAddress>(address2);
auto paymentAddress2 = wallet.GenerateNewSproutZKey();
// Create a new wallet from the existing wallet path
CWallet wallet2("wallet_crypted.dat");

View File

@@ -1244,7 +1244,7 @@ UniValue sendmany(const UniValue& params, bool fHelp)
if (fHelp || params.size() < 2 || params.size() > 5)
throw runtime_error(
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n"
"\nSend multiple times. Amounts are double-precision floating point numbers."
"\nSend multiple times. Amounts are decimal numbers with at most 8 digits of precision."
+ HelpRequiringPassphrase() + "\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n"
@@ -2909,17 +2909,17 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
UniValue results(UniValue::VARR);
if (zaddrs.size() > 0) {
std::vector<CUnspentSproutNotePlaintextEntry> sproutEntries;
std::vector<UnspentSaplingNoteEntry> saplingEntries;
pwalletMain->GetUnspentFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, !fIncludeWatchonly);
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries;
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false);
std::set<std::pair<PaymentAddress, uint256>> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs);
for (CUnspentSproutNotePlaintextEntry & entry : sproutEntries) {
for (auto & entry : sproutEntries) {
UniValue obj(UniValue::VOBJ);
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", entry.nHeight));
obj.push_back(Pair("confirmations", entry.confirmations));
bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(boost::get<libzcash::SproutPaymentAddress>(entry.address));
obj.push_back(Pair("spendable", hasSproutSpendingKey));
obj.push_back(Pair("address", EncodePaymentAddress(entry.address)));
@@ -2932,11 +2932,11 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
results.push_back(obj);
}
for (UnspentSaplingNoteEntry & entry : saplingEntries) {
for (auto & entry : saplingEntries) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("txid", entry.op.hash.ToString()));
obj.push_back(Pair("outindex", (int)entry.op.n));
obj.push_back(Pair("confirmations", entry.nHeight));
obj.push_back(Pair("confirmations", entry.confirmations));
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
pwalletMain->GetSaplingIncomingViewingKey(boost::get<libzcash::SaplingPaymentAddress>(entry.address), ivk);
@@ -3143,6 +3143,14 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp)
sample_times.push_back(benchmark_loadwallet());
} else if (benchmarktype == "listunspent") {
sample_times.push_back(benchmark_listunspent());
} else if (benchmarktype == "createsaplingspend") {
sample_times.push_back(benchmark_create_sapling_spend());
} else if (benchmarktype == "createsaplingoutput") {
sample_times.push_back(benchmark_create_sapling_output());
} else if (benchmarktype == "verifysaplingspend") {
sample_times.push_back(benchmark_verify_sapling_spend());
} else if (benchmarktype == "verifysaplingoutput") {
sample_times.push_back(benchmark_verify_sapling_output());
} else {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid benchmarktype");
}
@@ -3511,7 +3519,7 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp)
}
if (addrType == ADDR_TYPE_SPROUT) {
return EncodePaymentAddress(pwalletMain->GenerateNewZKey());
return EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey());
} else if (addrType == ADDR_TYPE_SAPLING) {
return EncodePaymentAddress(pwalletMain->GenerateNewSaplingZKey());
} else {
@@ -3969,7 +3977,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
if (fHelp || params.size() < 2 || params.size() > 4)
throw runtime_error(
"z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee )\n"
"\nSend multiple times. Amounts are double-precision floating point numbers."
"\nSend multiple times. Amounts are decimal numbers with at most 8 digits of precision."
"\nChange generated from a taddr flows to a new taddr address, while change generated from a zaddr returns to itself."
"\nWhen sending coinbase UTXOs to a zaddr, change is not allowed. The entire value of the UTXO(s) must be consumed."
+ strprintf("\nBefore Sapling activates, the maximum number of zaddr outputs is %d due to transaction size limits.\n", Z_SENDMANY_MAX_ZADDR_OUTPUTS_BEFORE_SAPLING)

View File

@@ -93,23 +93,23 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
}
// Generate a new spending key and return its public payment address
libzcash::PaymentAddress CWallet::GenerateNewZKey()
libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey()
{
AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata
// TODO: Add Sapling support
auto k = SproutSpendingKey::random();
auto addr = k.address();
// Check for collision, even though it is unlikely to ever occur
if (CCryptoKeyStore::HaveSproutSpendingKey(addr))
throw std::runtime_error("CWallet::GenerateNewZKey(): Collision detected");
throw std::runtime_error("CWallet::GenerateNewSproutZKey(): Collision detected");
// Create new metadata
int64_t nCreationTime = GetTime();
mapSproutZKeyMetadata[addr] = CKeyMetadata(nCreationTime);
if (!AddSproutZKey(k))
throw std::runtime_error("CWallet::GenerateNewZKey(): AddSproutZKey failed");
throw std::runtime_error("CWallet::GenerateNewSproutZKey(): AddSproutZKey failed");
return addr;
}
@@ -4879,7 +4879,7 @@ void CWallet::GetFilteredNotes(
std::string address,
int minDepth,
bool ignoreSpent,
bool ignoreUnspendable)
bool requireSpendingKey)
{
std::set<PaymentAddress> filterAddresses;
@@ -4887,11 +4887,12 @@ void CWallet::GetFilteredNotes(
filterAddresses.insert(DecodePaymentAddress(address));
}
GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, ignoreSpent, ignoreUnspendable);
GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey);
}
/**
* Find notes in the wallet filtered by payment addresses, min depth and ability to spend.
* Find notes in the wallet filtered by payment addresses, min depth, max depth,
* if the note is spent, if a spending key is required, and if the notes are locked.
* These notes are decrypted and added to the output parameter vector, outEntries.
*/
void CWallet::GetFilteredNotes(
@@ -4899,8 +4900,10 @@ void CWallet::GetFilteredNotes(
std::vector<SaplingNoteEntry>& saplingEntries,
std::set<PaymentAddress>& filterAddresses,
int minDepth,
int maxDepth,
bool ignoreSpent,
bool ignoreUnspendable)
bool requireSpendingKey,
bool ignoreLocked)
{
LOCK2(cs_main, cs_wallet);
@@ -4908,7 +4911,10 @@ void CWallet::GetFilteredNotes(
CWalletTx wtx = p.second;
// Filter the transactions before checking for notes
if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < minDepth) {
if (!CheckFinalTx(wtx) ||
wtx.GetBlocksToMaturity() > 0 ||
wtx.GetDepthInMainChain() < minDepth ||
wtx.GetDepthInMainChain() > maxDepth) {
continue;
}
@@ -4928,12 +4934,12 @@ void CWallet::GetFilteredNotes(
}
// skip notes which cannot be spent
if (ignoreUnspendable && !HaveSproutSpendingKey(pa)) {
if (requireSpendingKey && !HaveSproutSpendingKey(pa)) {
continue;
}
// skip locked notes
if (IsLockedNote(jsop)) {
if (ignoreLocked && IsLockedNote(jsop)) {
continue;
}
@@ -4957,7 +4963,7 @@ void CWallet::GetFilteredNotes(
hSig,
(unsigned char) j);
sproutEntries.push_back(CSproutNotePlaintextEntry{jsop, pa, plaintext});
sproutEntries.push_back(CSproutNotePlaintextEntry{jsop, pa, plaintext, wtx.GetDepthInMainChain()});
} catch (const note_decryption_failed &err) {
// Couldn't decrypt with this spending key
@@ -4994,7 +5000,7 @@ void CWallet::GetFilteredNotes(
}
// skip notes which cannot be spent
if (ignoreUnspendable) {
if (requireSpendingKey) {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
if (!(GetSaplingIncomingViewingKey(pa, ivk) &&
@@ -5006,137 +5012,18 @@ void CWallet::GetFilteredNotes(
// skip locked notes
// TODO: Add locking for Sapling notes
// if (IsLockedNote(jsop)) {
// if (ignoreLocked && IsLockedNote(op)) {
// continue;
// }
auto note = notePt.note(nd.ivk).get();
saplingEntries.push_back(SaplingNoteEntry {
op, pa, note, notePt.memo() });
}
}
}
/* Find unspent notes filtered by payment address, min depth and max depth */
void CWallet::GetUnspentFilteredNotes(
std::vector<CUnspentSproutNotePlaintextEntry>& sproutEntries,
std::vector<UnspentSaplingNoteEntry>& saplingEntries,
std::set<PaymentAddress>& filterAddresses,
int minDepth,
int maxDepth,
bool requireSpendingKey)
{
LOCK2(cs_main, cs_wallet);
for (auto & p : mapWallet) {
CWalletTx wtx = p.second;
// Filter the transactions before checking for notes
if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < minDepth || wtx.GetDepthInMainChain() > maxDepth) {
continue;
}
for (auto & pair : wtx.mapSproutNoteData) {
JSOutPoint jsop = pair.first;
SproutNoteData nd = pair.second;
SproutPaymentAddress pa = nd.address;
// skip notes which belong to a different payment address in the wallet
if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
continue;
}
// skip note which has been spent
if (nd.nullifier && IsSproutSpent(*nd.nullifier)) {
continue;
}
// skip notes where the spending key is not available
if (requireSpendingKey && !HaveSproutSpendingKey(pa)) {
continue;
}
// skip locked notes
if (IsLockedNote(jsop)) {
continue;
}
int i = jsop.js; // Index into CTransaction.vjoinsplit
int j = jsop.n; // Index into JSDescription.ciphertexts
// Get cached decryptor
ZCNoteDecryption decryptor;
if (!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)));
}
// determine amount of funds in the note
auto hSig = wtx.vjoinsplit[i].h_sig(*pzcashParams, wtx.joinSplitPubKey);
try {
SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt(
decryptor,
wtx.vjoinsplit[i].ciphertexts[j],
wtx.vjoinsplit[i].ephemeralKey,
hSig,
(unsigned char) j);
sproutEntries.push_back(CUnspentSproutNotePlaintextEntry{jsop, pa, plaintext, wtx.GetDepthInMainChain()});
} 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()));
}
}
for (auto & pair : wtx.mapSaplingNoteData) {
SaplingOutPoint op = pair.first;
SaplingNoteData nd = pair.second;
auto maybe_pt = SaplingNotePlaintext::decrypt(
wtx.vShieldedOutput[op.n].encCiphertext,
nd.ivk,
wtx.vShieldedOutput[op.n].ephemeralKey,
wtx.vShieldedOutput[op.n].cm);
assert(static_cast<bool>(maybe_pt));
auto notePt = maybe_pt.get();
auto maybe_pa = nd.ivk.address(notePt.d);
assert(static_cast<bool>(maybe_pa));
auto pa = maybe_pa.get();
// skip notes which belong to a different payment address in the wallet
if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
continue;
}
// skip note which has been spent
if (nd.nullifier && IsSaplingSpent(*nd.nullifier)) {
continue;
}
// skip notes where the spending key is not available
if (requireSpendingKey) {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
if (!(GetSaplingIncomingViewingKey(pa, ivk) &&
GetSaplingFullViewingKey(ivk, fvk) &&
HaveSaplingSpendingKey(fvk))) {
continue;
}
}
auto note = notePt.note(nd.ivk).get();
saplingEntries.push_back(UnspentSaplingNoteEntry {
op, pa, note, notePt.memo(), wtx.GetDepthInMainChain() });
}
}
}
//
// Shielded key and address generalizations
//

View File

@@ -310,38 +310,23 @@ public:
typedef std::map<JSOutPoint, SproutNoteData> mapSproutNoteData_t;
typedef std::map<SaplingOutPoint, SaplingNoteData> mapSaplingNoteData_t;
/** Decrypted note and its location in a transaction. */
/** Decrypted note, its location in a transaction, and number of confirmations. */
struct CSproutNotePlaintextEntry
{
JSOutPoint jsop;
libzcash::SproutPaymentAddress address;
libzcash::SproutNotePlaintext plaintext;
int confirmations;
};
/** Decrypted note, location in a transaction, and confirmation height. */
struct CUnspentSproutNotePlaintextEntry {
JSOutPoint jsop;
libzcash::SproutPaymentAddress address;
libzcash::SproutNotePlaintext plaintext;
int nHeight;
};
/** Sapling note and its location in a transaction. */
/** Sapling note, its location in a transaction, and number of confirmations. */
struct SaplingNoteEntry
{
SaplingOutPoint op;
libzcash::SaplingPaymentAddress address;
libzcash::SaplingNote note;
std::array<unsigned char, ZC_MEMO_SIZE> memo;
};
/** Sapling note, location in a transaction, and confirmation height. */
struct UnspentSaplingNoteEntry {
SaplingOutPoint op;
libzcash::SaplingPaymentAddress address;
libzcash::SaplingNote note;
std::array<unsigned char, ZC_MEMO_SIZE> memo;
int nHeight;
int confirmations;
};
/** A transaction with a merkle branch linking it to the block chain. */
@@ -1044,10 +1029,10 @@ public:
void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const;
/**
* ZKeys
* Sprout ZKeys
*/
//! Generates a new zaddr
libzcash::PaymentAddress GenerateNewZKey();
//! Generates a new Sprout zaddr
libzcash::SproutPaymentAddress GenerateNewSproutZKey();
//! Adds spending key to the store, and saves it to disk
bool AddSproutZKey(const libzcash::SproutSpendingKey &key);
//! Adds spending key to the store, without saving it to disk (used by LoadWallet)
@@ -1300,27 +1285,22 @@ public:
std::string address,
int minDepth=1,
bool ignoreSpent=true,
bool ignoreUnspendable=true);
bool requireSpendingKey=true);
/* Find notes filtered by payment addresses, min depth, ability to spend */
/* Find notes filtered by payment addresses, min depth, max depth, if they are spent,
if a spending key is required, and if they are locked */
void GetFilteredNotes(std::vector<CSproutNotePlaintextEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::set<libzcash::PaymentAddress>& filterAddresses,
int minDepth=1,
int maxDepth=INT_MAX,
bool ignoreSpent=true,
bool ignoreUnspendable=true);
bool requireSpendingKey=true,
bool ignoreLocked=true);
// staking functions
bool VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult, CTransaction &stakeSource, int32_t &voutNum, int32_t nHeight, uint32_t &bnTarget) const;
int32_t VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey pk) const;
/* Find unspent notes filtered by payment address, min depth and max depth */
void GetUnspentFilteredNotes(std::vector<CUnspentSproutNotePlaintextEntry>& sproutEntries,
std::vector<UnspentSaplingNoteEntry>& saplingEntries,
std::set<libzcash::PaymentAddress>& filterAddresses,
int minDepth=1,
int maxDepth=INT_MAX,
bool requireSpendingKey=true);
};
/** A key allocated from the key pool. */