Browse Source

Merge pull request #1 from MyHush/duke

merge upstream
pull/83/head
Denio 4 years ago
committed by GitHub
parent
commit
34318d07ff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Makefile.am
  2. 46
      src/init.cpp
  3. 221
      src/wallet/asyncrpcoperation_saplingconsolidation.cpp
  4. 37
      src/wallet/asyncrpcoperation_saplingconsolidation.h
  5. 51
      src/wallet/db.cpp
  6. 1
      src/wallet/db.h
  7. 44
      src/wallet/gtest/test_wallet.cpp
  8. 962
      src/wallet/wallet.cpp
  9. 59
      src/wallet/wallet.h
  10. 6
      src/wallet/walletdb.cpp
  11. 1
      src/wallet/walletdb.h

2
src/Makefile.am

@ -230,6 +230,7 @@ BITCOIN_CORE_H = \
validationinterface.h \
version.h \
wallet/asyncrpcoperation_mergetoaddress.h \
wallet/asyncrpcoperation_saplingconsolidation.h \
wallet/asyncrpcoperation_sendmany.h \
wallet/asyncrpcoperation_shieldcoinbase.h \
wallet/crypter.h \
@ -350,6 +351,7 @@ libbitcoin_wallet_a_SOURCES = \
zcbenchmarks.cpp \
zcbenchmarks.h \
wallet/asyncrpcoperation_mergetoaddress.cpp \
wallet/asyncrpcoperation_saplingconsolidation.cpp \
wallet/asyncrpcoperation_sendmany.cpp \
wallet/asyncrpcoperation_shieldcoinbase.cpp \
wallet/crypter.cpp \

46
src/init.cpp

@ -57,6 +57,7 @@
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#include "wallet/walletdb.h"
#include "wallet/asyncrpcoperation_saplingconsolidation.h"
#endif
#include <stdint.h>
@ -447,6 +448,13 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("Wallet options:"));
strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"));
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), 100));
strUsage += HelpMessageOpt("-consolidation", _("Enable auto Sapling note consolidation"));
strUsage += HelpMessageOpt("-consolidatesaplingaddress=<zaddr>", _("Specify Sapling Address to Consolidate. (default: all)"));
strUsage += HelpMessageOpt("-consolidationtxfee", strprintf(_("Fee amount in Puposhis used send consolidation transactions. (default %i)"), DEFAULT_CONSOLIDATION_FEE));
strUsage += HelpMessageOpt("-deletetx", _("Enable Old Transaction Deletion"));
strUsage += HelpMessageOpt("-deleteinterval", strprintf(_("Delete transaction every <n> blocks during inital block download (default: %i)"), DEFAULT_TX_DELETE_INTERVAL));
strUsage += HelpMessageOpt("-keeptxnum", strprintf(_("Keep the last <n> transactions (default: %i)"), DEFAULT_TX_RETENTION_LASTTX));
strUsage += HelpMessageOpt("-keeptxfornblocks", strprintf(_("Keep transactions for at least <n> blocks (default: %i)"), DEFAULT_TX_RETENTION_BLOCKS));
if (showDebug)
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(CWallet::minTxFee.GetFeePerK())));
@ -498,7 +506,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0));
strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)");
}
string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, partitioncheck, pow, proxy, prune, "
string debugCategories = "addrman, alert, bench, coindb, db, deletetx, estimatefee, http, libevent, lock, mempool, net, partitioncheck, pow, proxy, prune, "
"rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + debugCategories + ".");
@ -1944,6 +1952,42 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
pwalletMain->GenerateNewSeed();
}
//Set Sapling Consolidation
pwalletMain->fSaplingConsolidationEnabled = GetBoolArg("-consolidation", false);
fConsolidationTxFee = GetArg("-consolidationtxfee", DEFAULT_CONSOLIDATION_FEE);
fConsolidationMapUsed = !mapMultiArgs["-consolidatesaplingaddress"].empty();
//Validate Sapling Addresses
vector<string>& vaddresses = mapMultiArgs["-consolidatesaplingaddress"];
for (int i = 0; i < vaddresses.size(); i++) {
LogPrintf("Consolidating Sapling Address: %s\n", vaddresses[i]);
auto zAddress = DecodePaymentAddress(vaddresses[i]);
if (!IsValidPaymentAddress(zAddress)) {
return InitError("Invalid consolidation address");
}
}
//Set Transaction Deletion Options
fTxDeleteEnabled = GetBoolArg("-deletetx", false);
fTxConflictDeleteEnabled = GetBoolArg("-deleteconflicttx", true);
fDeleteInterval = GetArg("-deleteinterval", DEFAULT_TX_DELETE_INTERVAL);
if (fDeleteInterval < 1)
return InitError("deleteinterval must be greater than 0");
fKeepLastNTransactions = GetArg("-keeptxnum", DEFAULT_TX_RETENTION_LASTTX);
if (fKeepLastNTransactions < 1)
return InitError("keeptxnum must be greater than 0");
fDeleteTransactionsAfterNBlocks = GetArg("-keeptxfornblocks", DEFAULT_TX_RETENTION_BLOCKS);
if (fDeleteTransactionsAfterNBlocks < 1)
return InitError("keeptxfornblocks must be greater than 0");
if (fDeleteTransactionsAfterNBlocks < MAX_REORG_LENGTH + 1 ) {
LogPrintf("keeptxfornblock is less the MAX_REORG_LENGTH, Setting to %i\n", MAX_REORG_LENGTH + 1);
fDeleteTransactionsAfterNBlocks = MAX_REORG_LENGTH + 1;
}
if (fFirstRun)
{
// Create new keyUser and set as default key

221
src/wallet/asyncrpcoperation_saplingconsolidation.cpp

@ -0,0 +1,221 @@
#include "assert.h"
#include "boost/variant/static_visitor.hpp"
#include "asyncrpcoperation_saplingconsolidation.h"
#include "init.h"
#include "key_io.h"
#include "rpc/protocol.h"
#include "random.h"
#include "sync.h"
#include "tinyformat.h"
#include "transaction_builder.h"
#include "util.h"
#include "utilmoneystr.h"
#include "wallet.h"
CAmount fConsolidationTxFee = DEFAULT_CONSOLIDATION_FEE;
bool fConsolidationMapUsed = false;
const int CONSOLIDATION_EXPIRY_DELTA = 15;
AsyncRPCOperation_saplingconsolidation::AsyncRPCOperation_saplingconsolidation(int targetHeight) : targetHeight_(targetHeight) {}
AsyncRPCOperation_saplingconsolidation::~AsyncRPCOperation_saplingconsolidation() {}
void AsyncRPCOperation_saplingconsolidation::main() {
if (isCancelled())
return;
set_state(OperationStatus::EXECUTING);
start_execution_clock();
bool success = false;
try {
success = main_impl();
} catch (const UniValue& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
}
stop_execution_clock();
if (success) {
set_state(OperationStatus::SUCCESS);
} else {
set_state(OperationStatus::FAILED);
}
std::string s = strprintf("%s: Sapling Consolidation transaction created. (status=%s", getId(), getStateAsString());
if (success) {
s += strprintf(", success)\n");
} else {
s += strprintf(", error=%s)\n", getErrorMessage());
}
LogPrintf("%s", s);
}
bool AsyncRPCOperation_saplingconsolidation::main_impl() {
LogPrint("zrpcunsafe", "%s: Beginning AsyncRPCOperation_saplingconsolidation.\n", getId());
auto consensusParams = Params().GetConsensus();
auto nextActivationHeight = NextActivationHeight(targetHeight_, consensusParams);
if (nextActivationHeight && targetHeight_ + CONSOLIDATION_EXPIRY_DELTA >= nextActivationHeight.get()) {
LogPrint("zrpcunsafe", "%s: Consolidation txs would be created before a NU activation but may expire after. Skipping this round.\n", getId());
setConsolidationResult(0, 0, std::vector<std::string>());
return true;
}
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries;
std::set<libzcash::SaplingPaymentAddress> addresses;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
// We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying
// an anchor at height N-10 for each Sprout JoinSplit description
// Consider, should notes be sorted?
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, "", 11);
if (fConsolidationMapUsed) {
const vector<string>& v = mapMultiArgs["-consolidatesaplingaddress"];
for(int i = 0; i < v.size(); i++) {
auto zAddress = DecodePaymentAddress(v[i]);
if (boost::get<libzcash::SaplingPaymentAddress>(&zAddress) != nullptr) {
libzcash::SaplingPaymentAddress saplingAddress = boost::get<libzcash::SaplingPaymentAddress>(zAddress);
addresses.insert(saplingAddress );
}
}
} else {
pwalletMain->GetSaplingPaymentAddresses(addresses);
}
}
int numTxCreated = 0;
std::vector<std::string> consolidationTxIds;
CAmount amountConsolidated = 0;
CCoinsViewCache coinsView(pcoinsTip);
for (auto addr : addresses) {
libzcash::SaplingExtendedSpendingKey extsk;
if (pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)) {
std::vector<SaplingNoteEntry> fromNotes;
CAmount amountToSend = 0;
int maxQuantity = rand() % 35 + 10;
for (const SaplingNoteEntry& saplingEntry : saplingEntries) {
libzcash::SaplingIncomingViewingKey ivk;
pwalletMain->GetSaplingIncomingViewingKey(boost::get<libzcash::SaplingPaymentAddress>(saplingEntry.address), ivk);
//Select Notes from that same address we will be sending to.
if (ivk == extsk.expsk.full_viewing_key().in_viewing_key()) {
amountToSend += CAmount(saplingEntry.note.value());
fromNotes.push_back(saplingEntry);
}
//Only use a randomly determined number of notes between 10 and 45
if (fromNotes.size() >= maxQuantity)
break;
}
//random minimum 2 - 12 required
int minQuantity = rand() % 10 + 2;
if (fromNotes.size() < minQuantity)
continue;
amountConsolidated += amountToSend;
auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain);
//builder.SetExpiryHeight(targetHeight_ + CONSOLIDATION_EXPIRY_DELTA);
LogPrint("zrpcunsafe", "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - fConsolidationTxFee));
// Select Sapling notes
std::vector<SaplingOutPoint> ops;
std::vector<libzcash::SaplingNote> notes;
for (auto fromNote : fromNotes) {
ops.push_back(fromNote.op);
notes.push_back(fromNote.note);
}
// Fetch Sapling anchor and witnesses
uint256 anchor;
std::vector<boost::optional<SaplingWitness>> witnesses;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetSaplingNoteWitnesses(ops, witnesses, anchor);
}
// Add Sapling spends
for (size_t i = 0; i < notes.size(); i++) {
if (!witnesses[i]) {
LogPrint("zrpcunsafe", "%s: Missing Witnesses. Stopping.\n", getId());
break;
}
builder.AddSaplingSpend(extsk.expsk, notes[i], anchor, witnesses[i].get());
}
builder.SetFee(fConsolidationTxFee);
builder.AddSaplingOutput(extsk.expsk.ovk, addr, amountToSend - fConsolidationTxFee);
//CTransaction tx = builder.Build();
auto maybe_tx = builder.Build();
if (!maybe_tx) {
LogPrint("zrpcunsafe", "%s: Failed to build transaction.", getId());
break;
}
CTransaction tx = maybe_tx.get();
if (isCancelled()) {
LogPrint("zrpcunsafe", "%s: Canceled. Stopping.\n", getId());
break;
}
pwalletMain->CommitConsolidationTx(tx);
LogPrint("zrpcunsafe", "%s: Committed consolidation transaction with txid=%s\n", getId(), tx.GetHash().ToString());
amountConsolidated += amountToSend - fConsolidationTxFee;
consolidationTxIds.push_back(tx.GetHash().ToString());
}
}
LogPrint("zrpcunsafe", "%s: Created %d transactions with total Sapling output amount=%s\n", getId(), numTxCreated, FormatMoney(amountConsolidated));
setConsolidationResult(numTxCreated, amountConsolidated, consolidationTxIds);
return true;
}
void AsyncRPCOperation_saplingconsolidation::setConsolidationResult(int numTxCreated, const CAmount& amountConsolidated, const std::vector<std::string>& consolidationTxIds) {
UniValue res(UniValue::VOBJ);
res.push_back(Pair("num_tx_created", numTxCreated));
res.push_back(Pair("amount_consolidated", FormatMoney(amountConsolidated)));
UniValue txIds(UniValue::VARR);
for (const std::string& txId : consolidationTxIds) {
txIds.push_back(txId);
}
res.push_back(Pair("consolidation_txids", txIds));
set_result(res);
}
void AsyncRPCOperation_saplingconsolidation::cancel() {
set_state(OperationStatus::CANCELLED);
}
UniValue AsyncRPCOperation_saplingconsolidation::getStatus() const {
UniValue v = AsyncRPCOperation::getStatus();
UniValue obj = v.get_obj();
obj.push_back(Pair("method", "saplingconsolidation"));
obj.push_back(Pair("target_height", targetHeight_));
return obj;
}

37
src/wallet/asyncrpcoperation_saplingconsolidation.h

@ -0,0 +1,37 @@
#include "amount.h"
#include "asyncrpcoperation.h"
#include "univalue.h"
#include "zcash/Address.hpp"
#include "zcash/zip32.h"
//Default fee used for consolidation transactions
static const CAmount DEFAULT_CONSOLIDATION_FEE = 0;
extern CAmount fConsolidationTxFee;
extern bool fConsolidationMapUsed;
class AsyncRPCOperation_saplingconsolidation : public AsyncRPCOperation
{
public:
AsyncRPCOperation_saplingconsolidation(int targetHeight);
virtual ~AsyncRPCOperation_saplingconsolidation();
// We don't want to be copied or moved around
AsyncRPCOperation_saplingconsolidation(AsyncRPCOperation_saplingconsolidation const&) = delete; // Copy construct
AsyncRPCOperation_saplingconsolidation(AsyncRPCOperation_saplingconsolidation&&) = delete; // Move construct
AsyncRPCOperation_saplingconsolidation& operator=(AsyncRPCOperation_saplingconsolidation const&) = delete; // Copy assign
AsyncRPCOperation_saplingconsolidation& operator=(AsyncRPCOperation_saplingconsolidation&&) = delete; // Move assign
virtual void main();
virtual void cancel();
virtual UniValue getStatus() const;
private:
int targetHeight_;
bool main_impl();
void setConsolidationResult(int numTxCreated, const CAmount& amountConsolidated, const std::vector<std::string>& consolidationTxIds);
};

51
src/wallet/db.cpp

@ -105,7 +105,7 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn)
nEnvFlags |= DB_PRIVATE;
dbenv->set_lg_dir(pathLogDir.string().c_str());
dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
dbenv->set_cachesize(1, 0x100000, 1); // 1 MiB should be enough for just the wallet, Increased by 1 GB
dbenv->set_lg_bsize(0x10000);
dbenv->set_lg_max(1048576);
dbenv->set_lk_max_locks(40000);
@ -181,6 +181,55 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFu
return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
}
bool CDBEnv::Compact(const std::string& strFile)
{
LOCK(cs_db);
DB_COMPACT dbcompact;
dbcompact.compact_fillpercent = 80;
dbcompact.compact_pages = DB_MAX_PAGES;
dbcompact.compact_timeout = 0;
DB_COMPACT *pdbcompact;
pdbcompact = &dbcompact;
int result = 1;
if (mapDb[strFile] != NULL) {
Db* pdb = mapDb[strFile];
result = pdb->compact(NULL, NULL, NULL, pdbcompact, DB_FREE_SPACE, NULL);
delete pdb;
mapDb[strFile] = NULL;
switch (result)
{
case DB_LOCK_DEADLOCK:
LogPrint("db","Deadlock %i\n", result);
break;
case DB_LOCK_NOTGRANTED:
LogPrint("db","Lock Not Granted %i\n", result);
break;
case DB_REP_HANDLE_DEAD:
LogPrint("db","Handle Dead %i\n", result);
break;
case DB_REP_LOCKOUT:
LogPrint("db","Rep Lockout %i\n", result);
break;
case EACCES:
LogPrint("db","Eacces %i\n", result);
break;
case EINVAL:
LogPrint("db","Error Invalid %i\n", result);
break;
case 0:
LogPrint("db","Wallet Compact Sucessful\n");
break;
default:
LogPrint("db","Compact result int %i\n", result);
}
}
return (result == 0);
}
bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<CDBEnv::KeyValPair>& vResult)
{
LOCK(cs_db);

1
src/wallet/db.h

@ -88,6 +88,7 @@ public:
* NOTE: reads the entire database into memory, so cannot be used
* for huge databases.
*/
bool Compact(const std::string& strFile);
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);

44
src/wallet/gtest/test_wallet.cpp

@ -53,12 +53,10 @@ public:
return CCryptoKeyStore::Unlock(vMasterKeyIn);
}
void IncrementNoteWitnesses(const CBlockIndex* pindex,
const CBlock* pblock,
SproutMerkleTree& sproutTree,
SaplingMerkleTree& saplingTree) {
CWallet::IncrementNoteWitnesses(pindex, pblock, sproutTree, saplingTree);
void BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly) {
CWallet::BuildWitnessCache(pindex, witnessOnly);
}
void DecrementNoteWitnesses(const CBlockIndex* pindex) {
CWallet::DecrementNoteWitnesses(pindex);
}
@ -116,7 +114,7 @@ std::pair<JSOutPoint, SaplingOutPoint> CreateValidBlock(TestWallet& wallet,
wallet.AddToWallet(wtx, true, NULL);
block.vtx.push_back(wtx);
wallet.IncrementNoteWitnesses(&index, &block, sproutTree, saplingTree);
wallet.BuildWitnessCache(&index, false);
return std::make_pair(jsoutpt, saplingNotes[0]);
}
@ -724,8 +722,8 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
wallet.AddToWallet(wtx, true, NULL);
// Simulate receiving new block and ChainTip signal
wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
wallet.BuildWitnessCache(&fakeIndex, false);
wallet.UpdateNullifierNoteMapForBlock(&block);
// Retrieve the updated wtx from wallet
uint256 hash = wtx.GetHash();
@ -1008,8 +1006,8 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
}
// Simulate receiving new block and ChainTip signal
wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
wallet.BuildWitnessCache(&fakeIndex, false);
wallet.UpdateNullifierNoteMapForBlock(&block);
// Retrieve the updated wtx from wallet
uint256 hash = wtx.GetHash();
@ -1126,8 +1124,8 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
// Simulate receiving new block and ChainTip signal.
// This triggers calculation of nullifiers for notes belonging to this wallet
// in the output descriptions of wtx.
wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
wallet.BuildWitnessCache(&fakeIndex, false);
wallet.UpdateNullifierNoteMapForBlock(&block);
// Retrieve the updated wtx from wallet
wtx = wallet.mapWallet[wtx.GetHash()];
@ -1263,7 +1261,7 @@ TEST(WalletTests, CachedWitnessesEmptyChain) {
CBlockIndex index(block);
SproutMerkleTree sproutTree;
SaplingMerkleTree saplingTree;
wallet.IncrementNoteWitnesses(&index, &block, sproutTree, saplingTree);
wallet.BuildWitnessCache(&index, false);
::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
@ -1354,7 +1352,7 @@ TEST(WalletTests, CachedWitnessesChainTip) {
EXPECT_NE(anchors1.second, anchors3.second);
// Re-incrementing with the same block should give the same result
wallet.IncrementNoteWitnesses(&index2, &block2, sproutTree, saplingTree);
wallet.BuildWitnessCache(&index2, false);
auto anchors4 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
EXPECT_NE(anchors4.first, anchors4.second);
@ -1364,7 +1362,7 @@ TEST(WalletTests, CachedWitnessesChainTip) {
EXPECT_EQ(anchors2.second, anchors4.second);
// Incrementing with the same block again should not change the cache
wallet.IncrementNoteWitnesses(&index2, &block2, sproutTree, saplingTree);
wallet.BuildWitnessCache(&index2, false);
std::vector<boost::optional<SproutWitness>> sproutWitnesses5;
std::vector<boost::optional<SaplingWitness>> saplingWitnesses5;
@ -1447,7 +1445,7 @@ TEST(WalletTests, CachedWitnessesDecrementFirst) {
EXPECT_NE(anchors2.second, anchors4.second);
// Re-incrementing with the same block should give the same result
wallet.IncrementNoteWitnesses(&index2, &block2, sproutTree, saplingTree);
wallet.BuildWitnessCache(&index2, false);
auto anchors5 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
@ -1504,7 +1502,7 @@ TEST(WalletTests, CachedWitnessesCleanIndex) {
for (size_t i = 0; i < numBlocks; i++) {
SproutMerkleTree sproutRiPrevTree {sproutRiTree};
SaplingMerkleTree saplingRiPrevTree {saplingRiTree};
wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), sproutRiTree, saplingRiTree);
wallet.BuildWitnessCache(&indices[i], false);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
for (size_t j = 0; j < numBlocks; j++) {
@ -1531,7 +1529,7 @@ TEST(WalletTests, CachedWitnessesCleanIndex) {
}
{
wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), sproutRiPrevTree, saplingRiPrevTree);
wallet.BuildWitnessCache(&indices[i], false);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
for (size_t j = 0; j < numBlocks; j++) {
EXPECT_TRUE((bool) sproutWitnesses[j]);
@ -1886,8 +1884,8 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
wallet.AddToWallet(wtx, true, NULL);
// Simulate receiving new block and ChainTip signal
wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
wallet.BuildWitnessCache(&fakeIndex, false);
wallet.UpdateNullifierNoteMapForBlock(&block);
// Retrieve the updated wtx from wallet
uint256 hash = wtx.GetHash();
@ -1915,7 +1913,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
EXPECT_EQ(2, wtx2.mapSaplingNoteData.size());
EXPECT_EQ(1, wtx2.mapSaplingNoteData[sop0].witnesses.size()); // wtx2 has fake witness for payment output
EXPECT_EQ(0, wtx2.mapSaplingNoteData[sop1].witnesses.size()); // wtx2 never had incrementnotewitness called
EXPECT_EQ(0, wtx2.mapSaplingNoteData[sop1].witnesses.size()); // wtx2 never had BuildWitnessCache called
// After updating, they should be the same
EXPECT_TRUE(wallet.UpdatedNoteData(wtx2, wtx));
@ -2040,8 +2038,8 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
wallet.AddToWallet(wtx, true, NULL);
// Simulate receiving new block and ChainTip signal
wallet.IncrementNoteWitnesses(&fakeIndex, &block, sproutTree, saplingTree);
wallet.UpdateSaplingNullifierNoteMapForBlock(&block);
wallet.BuildWitnessCache(&fakeIndex, false);
wallet.UpdateNullifierNoteMapForBlock(&block);
// Retrieve the updated wtx from wallet
uint256 hash = wtx.GetHash();

962
src/wallet/wallet.cpp

File diff suppressed because it is too large

59
src/wallet/wallet.h

@ -23,6 +23,7 @@
#define BITCOIN_WALLET_WALLET_H
#include "amount.h"
#include "asyncrpcoperation.h"
#include "coins.h"
#include "key.h"
#include "keystore.h"
@ -60,6 +61,11 @@ extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool fSendFreeTransactions;
extern bool fPayAtLeastCustomFee;
extern bool fTxDeleteEnabled;
extern bool fTxConflictDeleteEnabled;
extern int fDeleteInterval;
extern unsigned int fDeleteTransactionsAfterNBlocks;
extern unsigned int fKeepLastNTransactions;
//! -paytxfee default
@ -82,6 +88,18 @@ extern unsigned int WITNESS_CACHE_SIZE;
//! Size of HD seed in bytes
static const size_t HD_WALLET_SEED_LENGTH = 32;
//Default Transaction Rentention N-BLOCKS
static const int DEFAULT_TX_DELETE_INTERVAL = 1000;
//Default Transaction Rentention N-BLOCKS
static const unsigned int DEFAULT_TX_RETENTION_BLOCKS = 10000;
//Default Retenion Last N-Transactions
static const unsigned int DEFAULT_TX_RETENTION_LASTTX = 200;
//Amount of transactions to delete per run while syncing
static const int MAX_DELETE_TX_SIZE = 50000;
class CBlockIndex;
class CCoinControl;
class COutput;
@ -250,15 +268,18 @@ public:
* -1 as a placeholder. The next time CWallet::ChainTip is called, we can
* determine what height the witness cache for this note is valid for (even
* if no witnesses were cached), and so can set the correct value in
* CWallet::IncrementNoteWitnesses and CWallet::DecrementNoteWitnesses.
* CWallet::BuildWitnessCache and CWallet::DecrementNoteWitnesses.
*/
int witnessHeight;
SproutNoteData() : address(), nullifier(), witnessHeight {-1} { }
//In Memory Only
bool witnessRootValidated;
SproutNoteData() : address(), nullifier(), witnessHeight {-1}, witnessRootValidated {false} { }
SproutNoteData(libzcash::SproutPaymentAddress a) :
address {a}, nullifier(), witnessHeight {-1} { }
address {a}, nullifier(), witnessHeight {-1}, witnessRootValidated {false} { }
SproutNoteData(libzcash::SproutPaymentAddress a, uint256 n) :
address {a}, nullifier {n}, witnessHeight {-1} { }
address {a}, nullifier {n}, witnessHeight {-1}, witnessRootValidated {false} { }
ADD_SERIALIZE_METHODS;
@ -300,6 +321,9 @@ public:
libzcash::SaplingIncomingViewingKey ivk;
boost::optional<uint256> nullifier;
//In Memory Only
bool witnessRootValidated;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
@ -779,6 +803,9 @@ private:
typedef TxSpendMap<uint256> TxNullifiers;
TxNullifiers mapTxSproutNullifiers;
TxNullifiers mapTxSaplingNullifiers;
std::vector<CTransaction> pendingSaplingConsolidationTxs;
AsyncRPCOperationId saplingConsolidationOperationId;
void AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid);
void AddToSproutSpends(const uint256& nullifier, const uint256& wtxid);
@ -793,6 +820,7 @@ public:
*/
int64_t nWitnessCacheSize;
bool needsRescan = false;
bool fSaplingConsolidationEnabled = false;
void ClearNoteWitnessCache();
@ -800,13 +828,16 @@ public:
std::set<uint256> GetNullifiers();
protected:
int SproutWitnessMinimumHeight(const uint256& nullifier, int nWitnessHeight, int nMinimumHeight);
int SaplingWitnessMinimumHeight(const uint256& nullifier, int nWitnessHeight, int nMinimumHeight);
/**
* pindex is the new tip being connected.
*/
void IncrementNoteWitnesses(const CBlockIndex* pindex,
const CBlock* pblock,
SproutMerkleTree& sproutTree,
SaplingMerkleTree& saplingTree);
int VerifyAndSetInitialWitness(const CBlockIndex* pindex, bool witnessOnly);
void BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly);
/**
* pindex is the old tip being disconnected.
*/
@ -1001,8 +1032,11 @@ public:
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
unsigned int GetSpendDepth(const uint256& hash, unsigned int n) const;
bool IsSproutSpent(const uint256& nullifier) const;
unsigned int GetSproutSpendDepth(const uint256& nullifier) const;
bool IsSaplingSpent(const uint256& nullifier) const;
unsigned int GetSaplingSpendDepth(const uint256& nullifier) const;
bool IsLockedCoin(uint256 hash, unsigned int n) const;
void LockCoin(COutPoint& output);
@ -1137,8 +1171,9 @@ public:
void MarkDirty();
bool UpdateNullifierNoteMap();
void UpdateNullifierNoteMapWithTx(const CWalletTx& wtx);
void UpdateSproutNullifierNoteMapWithTx(CWalletTx& wtx);
void UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx);
void UpdateSaplingNullifierNoteMapForBlock(const CBlock* pblock);
void UpdateNullifierNoteMapForBlock(const CBlock* pblock);
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb);
void EraseFromWallet(const uint256 &hash);
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
@ -1148,6 +1183,10 @@ public:
std::vector<uint256> commitments,
std::vector<boost::optional<SproutWitness>>& witnesses,
uint256 &final_anchor);
void ReorderWalletTransactions(std::map<std::pair<int,int>, CWalletTx> &mapSorted, int64_t &maxOrderPos);
void UpdateWalletTransactionOrder(std::map<std::pair<int,int>, CWalletTx> &mapSorted, bool resetOrder);
void DeleteTransactions(std::vector<uint256> &removeTxs);
void DeleteWalletTransactions(const CBlockIndex* pindex);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime);
@ -1215,6 +1254,8 @@ public:
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const;
void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree, bool added);
void RunSaplingConsolidation(int blockHeight);
void CommitConsolidationTx(const CTransaction& tx);
/** Saves witness caches and best block locator to disk. */
void SetBestChain(const CBlockLocator& loc);
std::set<std::pair<libzcash::PaymentAddress, uint256>> GetNullifiersForAddresses(const std::set<libzcash::PaymentAddress> & addresses);

6
src/wallet/walletdb.cpp

@ -1208,6 +1208,12 @@ bool BackupWallet(const CWallet& wallet, const string& strDest)
return false;
}
bool CWalletDB::Compact(CDBEnv& dbenv, const std::string& strFile)
{
bool fSuccess = dbenv.Compact(strFile);
return fSuccess;
}
//
// Try to (very carefully!) recover wallet.dat if there is a problem.
//

1
src/wallet/walletdb.h

@ -189,6 +189,7 @@ public:
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
DBErrors ZapWalletTx(CWallet* pwallet, std::vector<CWalletTx>& vWtx);
static bool Compact(CDBEnv& dbenv, const std::string& strFile);
static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys);
static bool Recover(CDBEnv& dbenv, const std::string& filename);

Loading…
Cancel
Save