From c6e5b07a5906893bcf996ab4032e96a8882ac6c7 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Wed, 24 Aug 2022 23:38:19 -0400 Subject: [PATCH 01/45] You might be a king or a little street zsweeper, but sooner or later you will dance with the reaper --- src/Makefile.am | 2 + src/base58.cpp | 1 + src/compressor.cpp | 1 + src/consensus/upgrades.h | 7 ++- src/consensus/validation.h | 1 + src/core_read.cpp | 1 + src/core_write.cpp | 1 + src/fs.cpp | 1 + src/importcoin.cpp | 3 + src/init.cpp | 58 +++++++++++++++++++ src/key.cpp | 1 + src/merkleblock.cpp | 1 + src/noui.cpp | 1 + src/scheduler.cpp | 1 + src/transaction_builder.cpp | 5 ++ src/transaction_builder.h | 1 + src/uint256.cpp | 3 +- src/utilstrencodings.cpp | 1 + src/utiltime.cpp | 1 + ...asyncrpcoperation_saplingconsolidation.cpp | 4 +- src/wallet/wallet.cpp | 47 ++++++++++++++- src/wallet/wallet.h | 13 ++++- 22 files changed, 146 insertions(+), 9 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 5d73c049f..76f99f00c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -231,6 +231,7 @@ BITCOIN_CORE_H = \ version.h \ wallet/asyncrpcoperation_mergetoaddress.h \ wallet/asyncrpcoperation_saplingconsolidation.h \ + wallet/asyncrpcoperation_sweep.h \ wallet/asyncrpcoperation_sendmany.h \ wallet/asyncrpcoperation_shieldcoinbase.h \ wallet/crypter.h \ @@ -344,6 +345,7 @@ libbitcoin_wallet_a_SOURCES = \ zcbenchmarks.h \ wallet/asyncrpcoperation_mergetoaddress.cpp \ wallet/asyncrpcoperation_saplingconsolidation.cpp \ + wallet/asyncrpcoperation_sweep.cpp \ wallet/asyncrpcoperation_sendmany.cpp \ wallet/asyncrpcoperation_shieldcoinbase.cpp \ wallet/crypter.cpp \ diff --git a/src/base58.cpp b/src/base58.cpp index d46894ea0..f1ca396ab 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html diff --git a/src/compressor.cpp b/src/compressor.cpp index ae7fe02ba..b55313486 100644 --- a/src/compressor.cpp +++ b/src/compressor.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying diff --git a/src/consensus/upgrades.h b/src/consensus/upgrades.h index 7a72e798b..278c3e462 100644 --- a/src/consensus/upgrades.h +++ b/src/consensus/upgrades.h @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2022 The Hush developers // Copyright (c) 2018 The Zcash developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html @@ -17,8 +18,8 @@ * * ******************************************************************************/ -#ifndef ZCASH_CONSENSUS_UPGRADES_H -#define ZCASH_CONSENSUS_UPGRADES_H +#ifndef HUSH_CONSENSUS_UPGRADES_H +#define HUSH_CONSENSUS_UPGRADES_H #include "consensus/params.h" @@ -114,4 +115,4 @@ boost::optional NextActivationHeight( int nHeight, const Consensus::Params& params); -#endif // ZCASH_CONSENSUS_UPGRADES_H +#endif // HUSH_CONSENSUS_UPGRADES_H diff --git a/src/consensus/validation.h b/src/consensus/validation.h index edf38e294..848018728 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2022 The Hush developers // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying diff --git a/src/core_read.cpp b/src/core_read.cpp index 679d221f4..d092f6624 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html diff --git a/src/core_write.cpp b/src/core_write.cpp index bb996c700..67953e2fa 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html diff --git a/src/fs.cpp b/src/fs.cpp index a5e12f1cf..0c6a60014 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers #include "fs.h" namespace fsbridge { diff --git a/src/importcoin.cpp b/src/importcoin.cpp index 16a8d3c64..f6da631c7 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2021 The Hush developers +// Distributed under the GPLv3 software license, see the accompanying +// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * diff --git a/src/init.cpp b/src/init.cpp index e057282be..dd6666710 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -59,6 +59,7 @@ #include "wallet/wallet.h" #include "wallet/walletdb.h" #include "wallet/asyncrpcoperation_saplingconsolidation.h" +#include "wallet/asyncrpcoperation_sweep.h" #endif #include #include @@ -461,6 +462,16 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-consolidation", _("Enable auto Sapling note consolidation (default: false)")); strUsage += HelpMessageOpt("-consolidatesaplingaddress=", _("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("-zsweep", _("Enable zaddr sweeping, automatically move all shielded funds to a one address once per X blocks")); + strUsage += HelpMessageOpt("-zsweepaddress=", _("Specify the shielded address where swept funds will be sent)")); + strUsage += HelpMessageOpt("-zsweepfee", strprintf(_("Fee amount in puposhis used send sweep transactions. (default %i)"), DEFAULT_SWEEP_FEE)); + strUsage += HelpMessageOpt("-zsweepinterval", strprintf(_("Sweep shielded funds every X blocks (default %i)"), 5)); + strUsage += HelpMessageOpt("-zsweepmaxinputs", strprintf(_("Maximum number of shielded inputs to sweep per transaction (default %i)"), 8)); + // By default we only allow sweeping to the current wallet which must have the spending key of the sweep zaddr + // This hopefully will make it harder for people to accidentally sweep funds to a wrong zaddr and lose funds + strUsage += HelpMessageOpt("-zsweepexternal", _("Enable sweeping to an external wallet (default false)")); + strUsage += HelpMessageOpt("-deletetx", _("Enable Old Transaction Deletion")); strUsage += HelpMessageOpt("-deleteinterval", strprintf(_("Delete transaction every blocks during inital block download (default: %i)"), DEFAULT_TX_DELETE_INTERVAL)); strUsage += HelpMessageOpt("-keeptxnum", strprintf(_("Keep the last transactions (default: %i)"), DEFAULT_TX_RETENTION_LASTTX)); @@ -2054,6 +2065,53 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } } + //Set Sweep + pwalletMain->fSweepEnabled = GetBoolArg("-zsweep", false); + + if (pwalletMain->fSweepEnabled) { + fSweepTxFee = GetArg("-zsweepfee", DEFAULT_SWEEP_FEE); + fSweepMapUsed = !mapMultiArgs["-zsweepaddress"].empty(); + + //Validate Sapling Addresses + vector& vSweep = mapMultiArgs["-zsweepaddress"]; + if (vSweep.size() != 1) { + return InitError("A single zsweep address must be specified."); + } + + for (int i = 0; i < vSweep.size(); i++) { + // LogPrintf("Sweep Address: %s\n", vSweep[i]); + auto zSweep = DecodePaymentAddress(vSweep[i]); + if (!IsValidPaymentAddress(zSweep)) { + return InitError("Invalid zsweep address"); + } + auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zSweep); + auto allowSweepToExternalWallet = GetArg("-zsweepexternal", false); + + if (!hasSpendingKey) { + if (allowSweepToExternalWallet) { + LogPrintf("%s: sweeping funds to a zaddr in an external wallet\n", __func__); + } else { + return InitError("Wallet must have the spending key of zsweep address"); + } + } + } + + if (pwalletMain->fSaplingConsolidationEnabled) { + //Validate 1 Consolidation address only that matches the sweep address + vector& vaddresses = mapMultiArgs["-consolidatesaplingaddress"]; + if (vaddresses.size() == 0) { + fConsolidationMapUsed = true; + mapMultiArgs["-consolidatesaplingaddress"] = vSweep; + } else { + for (int i = 0; i < vaddresses.size(); i++) { + if (vSweep[0] != vaddresses[i]) { + return InitError("Consolidation can only be used on the sweep address when sweep is enabled."); + } + } + } + } + } + //Set Transaction Deletion Options fTxDeleteEnabled = GetBoolArg("-deletetx", false); fTxConflictDeleteEnabled = GetBoolArg("-deleteconflicttx", true); diff --git a/src/key.cpp b/src/key.cpp index 70ade77fb..83f831ada 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2014 The Bitcoin Core developers // Copyright (c) 2017 The Zcash developers // Distributed under the GPLv3 software license, see the accompanying diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index c12552f78..86df55cac 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying diff --git a/src/noui.cpp b/src/noui.cpp index 7a838d0bd..e0bd76e4d 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying diff --git a/src/scheduler.cpp b/src/scheduler.cpp index c243ca308..9c4ebf334 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2015 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index 3ff6269b2..0dfd5a299 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -104,6 +104,11 @@ bool TransactionBuilder::AddOpRetLast() return true; } +void TransactionBuilder::SetExpiryHeight(int nHeight) +{ + this->mtx.nExpiryHeight = nHeight; +} + void TransactionBuilder::AddOpRet(CScript &s) { opReturn.emplace(CScript(s)); diff --git a/src/transaction_builder.h b/src/transaction_builder.h index 89a8d6580..6c4d20302 100644 --- a/src/transaction_builder.h +++ b/src/transaction_builder.h @@ -77,6 +77,7 @@ public: TransactionBuilder(const Consensus::Params& consensusParams, int nHeight, CKeyStore* keyStore = nullptr); void SetFee(CAmount fee); + void SetExpiryHeight(int nHeight); // Returns false if the anchor does not match the anchor used by // previously-added Sapling spends. diff --git a/src/uint256.cpp b/src/uint256.cpp index 4883ac50b..97c0cb007 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying @@ -19,9 +20,7 @@ ******************************************************************************/ #include "uint256.h" - #include "utilstrencodings.h" - #include #include diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 665f9bead..4a2815968 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying diff --git a/src/utiltime.cpp b/src/utiltime.cpp index aba3823ca..bc5e3d69c 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index 2594fb4ce..361d04390 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -159,7 +159,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { amountConsolidated += amountToSend; auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain); - //builder.SetExpiryHeight(targetHeight_ + CONSOLIDATION_EXPIRY_DELTA); + builder.SetExpiryHeight(targetHeight_ + CONSOLIDATION_EXPIRY_DELTA); auto actualAmountToSend = amountToSend < fConsolidationTxFee ? 0 : amountToSend - fConsolidationTxFee; LogPrintf("%s: %s Beginning to create transaction with Sapling output amount=%s\n", __func__, opid, FormatMoney(actualAmountToSend)); @@ -235,7 +235,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { break; } - if(pwalletMain->CommitConsolidationTx(tx)) { + if(pwalletMain->CommitAutomatedTx(tx)) { LogPrint("zrpcunsafe", "%s: Committed consolidation transaction with txid=%s\n",opid, tx.GetHash().ToString()); amountConsolidated += actualAmountToSend; consolidationTxIds.push_back(tx.GetHash().ToString()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bda0140f0..83f95cbb3 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -38,6 +38,7 @@ #include "crypter.h" #include "coins.h" #include "wallet/asyncrpcoperation_saplingconsolidation.h" +#include "wallet/asyncrpcoperation_sweep.h" #include "zcash/zip32.h" #include "cc/CCinclude.h" #include @@ -483,6 +484,9 @@ void CWallet::ChainTip(const CBlockIndex *pindex, if (fSaplingConsolidationEnabled) { RunSaplingConsolidation(pindex->GetHeight()); } + if (fSweepEnabled) { + RunSaplingSweep(pindex->GetHeight()); + } if (fTxDeleteEnabled) { DeleteWalletTransactions(pindex); } @@ -501,6 +505,43 @@ void CWallet::ChainTip(const CBlockIndex *pindex, } } +void CWallet::RunSaplingSweep(int blockHeight) { + // Sapling is always active since height=1 of HUSH+HSCs + // if (!NetworkUpgradeActive(blockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { + // return; + // } + AssertLockHeld(cs_wallet); + if (!fSweepEnabled) { + return; + } + + if (nextSweep > blockHeight) { + return; + } + + //Don't Run if consolidation will run soon. + if (fSaplingConsolidationEnabled && nextConsolidation - 15 <= blockHeight) { + return; + } + + //Don't Run While consolidation is running. + if (fConsolidationRunning) { + return; + } + + fSweepRunning = true; + + std::shared_ptr q = getAsyncRPCQueue(); + std::shared_ptr lastOperation = q->getOperationForId(saplingSweepOperationId); + if (lastOperation != nullptr) { + lastOperation->cancel(); + } + pendingSaplingSweepTxs.clear(); + std::shared_ptr operation(new AsyncRPCOperation_sweep(blockHeight + 5)); + saplingSweepOperationId = operation->getId(); + q->addOperation(operation); +} + void CWallet::RunSaplingConsolidation(int blockHeight) { if (!NetworkUpgradeActive(blockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { return; @@ -512,6 +553,10 @@ void CWallet::RunSaplingConsolidation(int blockHeight) { return; } + if (fSweepRunning) { + return; + } + int consolidateInterval = rand() % 5 + 5; if(fZdebug) @@ -530,7 +575,7 @@ void CWallet::RunSaplingConsolidation(int blockHeight) { } } -bool CWallet::CommitConsolidationTx(const CTransaction& tx) { +bool CWallet::CommitAutomatedTx(const CTransaction& tx) { CWalletTx wtx(this, tx); CReserveKey reservekey(pwalletMain); fprintf(stderr,"%s: %s\n",__func__,tx.ToString().c_str()); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 15be2072d..f633f2564 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -785,6 +785,9 @@ private: std::vector pendingSaplingConsolidationTxs; AsyncRPCOperationId saplingConsolidationOperationId; + std::vector pendingSaplingSweepTxs; + AsyncRPCOperationId saplingSweepOperationId; + void AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid); void AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid); void AddToSpends(const uint256& wtxid); @@ -797,7 +800,13 @@ public: */ int64_t nWitnessCacheSize; bool needsRescan = false; + int nextConsolidation = 0; + bool fSaplingConsolidationEnabled = false; + bool fConsolidationRunning = false; + bool fSweepEnabled = false; + bool fSweepRunning = false; + int nextSweep = 0; void ClearNoteWitnessCache(); @@ -1181,12 +1190,14 @@ public: CAmount GetCredit(const CTransaction& tx, int32_t voutNum, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetChange(const CTransaction& tx) const; + void RunSaplingSweep(int blockHeight); + void ChainTip( const CBlockIndex *pindex, const CBlock *pblock, boost::optional> added); void RunSaplingConsolidation(int blockHeight); - bool CommitConsolidationTx(const CTransaction& tx); + bool CommitAutomatedTx(const CTransaction& tx); /** Saves witness caches and best block locator to disk. */ void SetBestChain(const CBlockLocator& loc); std::set> GetNullifiersForAddresses(const std::set & addresses); From 02ef7149011d9b221f58585dd00747661b9cdf09 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Thu, 25 Aug 2022 09:26:10 -0400 Subject: [PATCH 02/45] Commit forgotten files --- src/wallet/asyncrpcoperation_sweep.cpp | 313 +++++++++++++++++++++++++ src/wallet/asyncrpcoperation_sweep.h | 42 ++++ 2 files changed, 355 insertions(+) create mode 100644 src/wallet/asyncrpcoperation_sweep.cpp create mode 100644 src/wallet/asyncrpcoperation_sweep.h diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp new file mode 100644 index 000000000..140c9902a --- /dev/null +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -0,0 +1,313 @@ +// Copyright (c) 2016-2022 The Hush developers +// Distributed under the GPLv3 software license, see the accompanying +// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html +#include "assert.h" +#include "boost/variant/static_visitor.hpp" +#include "asyncrpcoperation_sweep.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" + +extern string randomSietchZaddr(); + +CAmount fSweepTxFee = DEFAULT_SWEEP_FEE; +bool fSweepMapUsed = false; +const int SWEEP_EXPIRY_DELTA = 15; +boost::optional rpcSweepAddress; + +AsyncRPCOperation_sweep::AsyncRPCOperation_sweep(int targetHeight, bool fromRpc) : targetHeight_(targetHeight), fromRPC_(fromRpc){} + +AsyncRPCOperation_sweep::~AsyncRPCOperation_sweep() {} + +void AsyncRPCOperation_sweep::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 Sweep transaction created. (status=%s", getId(), getStateAsString()); + if (success) { + s += strprintf(", success)\n"); + } else { + s += strprintf(", error=%s)\n", getErrorMessage()); + } + + LogPrintf("%s", s); +} + +bool AsyncRPCOperation_sweep::main_impl() { + LogPrint("zrpcunsafe", "%s: Beginning asyncrpcoperation_sweep.\n", getId()); + auto consensusParams = Params().GetConsensus(); + auto nextActivationHeight = NextActivationHeight(targetHeight_, consensusParams); + if (nextActivationHeight && targetHeight_ + SWEEP_EXPIRY_DELTA >= nextActivationHeight.get()) { + LogPrint("zrpcunsafe", "%s: Sweep txs would be created before a NU activation but may expire after. Skipping this round.\n", getId()); + setSweepResult(0, 0, std::vector()); + return true; + } + + std::vector saplingEntries; + libzcash::SaplingPaymentAddress sweepAddress; + std::map> mapAddresses; + + { + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->GetFilteredNotes(saplingEntries, "", 11); + if (!fromRPC_) { + if (fSweepMapUsed) { + const vector& v = mapMultiArgs["-zsweepaddress"]; + for(int i = 0; i < v.size(); i++) { + auto zAddress = DecodePaymentAddress(v[i]); + if (boost::get(&zAddress) != nullptr) { + sweepAddress = boost::get(zAddress); + } + } + } else { + return false; + } + } else { + if (boost::get(&rpcSweepAddress) != nullptr) { + sweepAddress = boost::get(rpcSweepAddress); + } else { + return false; + } + } + + for (auto & entry : saplingEntries) { + //Map all notes by address + if (sweepAddress == entry.address) { + continue; + } else { + std::map>::iterator it; + it = mapAddresses.find(entry.address); + if (it != mapAddresses.end()) { + it->second.push_back(entry); + } else { + std::vector entries; + entries.push_back(entry); + mapAddresses[entry.address] = entries; + } + } + } + } + + int numTxCreated = 0; + std::vector sweepTxIds; + CAmount amountSwept = 0; + CCoinsViewCache coinsView(pcoinsTip); + bool sweepComplete = true; + + for (std::map>::iterator it = mapAddresses.begin(); it != mapAddresses.end(); it++) { + auto addr = (*it).first; + auto saplingEntries = (*it).second; + + libzcash::SaplingExtendedSpendingKey extsk; + if (pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)) { + + std::vector fromNotes; + CAmount amountToSend = 0; + int maxInputs = GetArg("-zsweepmaxinputs", 8); + if( maxInputs > 100 || maxInputs < 5) { + fprintf(stderr,"%s: Invalid zsweep maxinputs=%d is >100 and <5, setting to default of 8\n", __func__, maxInputs); + maxInputs = 8; + } + + //Count Notes availiable for this address + int targetCount = 0; + int noteCount = 0; + for (const SaplingNoteEntry& saplingEntry : saplingEntries) { + + libzcash::SaplingIncomingViewingKey ivk; + pwalletMain->GetSaplingIncomingViewingKey(boost::get(saplingEntry.address), ivk); + + if (ivk == extsk.expsk.full_viewing_key().in_viewing_key() && saplingEntry.address == addr) { + noteCount++; + } + } + + //Don't sweep if under the threshold + if (noteCount <= targetCount){ + continue; + } + + //if we make it here then we need to sweep and the routine is considered incomplete + sweepComplete = false; + + for (const SaplingNoteEntry& saplingEntry : saplingEntries) { + + libzcash::SaplingIncomingViewingKey ivk; + pwalletMain->GetSaplingIncomingViewingKey(boost::get(saplingEntry.address), ivk); + + //Select Notes from that same address we will be sending to. + if (ivk == extsk.expsk.full_viewing_key().in_viewing_key() && saplingEntry.address == addr) { + amountToSend += CAmount(saplingEntry.note.value()); + fromNotes.push_back(saplingEntry); + } + + if (fromNotes.size() >= maxInputs) + break; + + } + + int minQuantity = 1; + if (fromNotes.size() < minQuantity) + continue; + + CAmount fee = fSweepTxFee; + if (amountToSend <= fSweepTxFee) { + fee = 0; + } + amountSwept += amountToSend; + auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain); + { + LOCK2(cs_main, pwalletMain->cs_wallet); + builder.SetExpiryHeight(chainActive.Tip()->GetHeight()+ SWEEP_EXPIRY_DELTA); + } + LogPrint("zrpcunsafe", "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - fee)); + + // Select Sapling notes + std::vector ops; + std::vector notes; + for (auto fromNote : fromNotes) { + ops.push_back(fromNote.op); + notes.push_back(fromNote.note); + } + + // Fetch Sapling anchor and witnesses + uint256 anchor; + std::vector> 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(fee); + builder.AddSaplingOutput(extsk.expsk.ovk, sweepAddress, amountToSend - fee); + + // Add sietch zouts + int ZOUTS = 7; + for(size_t i = 0; i < ZOUTS; i++) { + // In Privacy Zdust We Trust -- Duke + string zdust = randomSietchZaddr(); + auto zaddr = DecodePaymentAddress(zdust); + if (IsValidPaymentAddress(zaddr)) { + CAmount amount=0; + auto sietchZoutput = boost::get(zaddr); + LogPrint("zrpcunsafe", "%s: Adding Sietch zdust output %d\n", __func__, i); // %d %s amount=%li\n", __func__, i, zaddr, amount); + + builder.AddSaplingOutput(extsk.expsk.ovk, sietchZoutput, amount); + } else { + LogPrint("zrpcunsafe", "%s: Invalid payment address %s! Stopping.\n", __func__, zdust); + // status = false; + break; + } + } + LogPrint("zrpcunsafe", "%s: Done adding %d sietch zouts\n", __func__, ZOUTS); + + auto maybe_tx = builder.Build(); + if (!maybe_tx) { + LogPrint("zrpcunsafe", "%s: Failed to build transaction %s.\n",__func__, getId()); + // status=false; + break; + } + CTransaction tx = maybe_tx.get(); + + if (isCancelled()) { + LogPrint("zrpcunsafe", "%s: Canceled. Stopping.\n", getId()); + break; + } + + pwalletMain->CommitAutomatedTx(tx); + LogPrint("zrpcunsafe", "%s: Committed sweep transaction with txid=%s\n", getId(), tx.GetHash().ToString()); + amountSwept += amountToSend - fee; + sweepTxIds.push_back(tx.GetHash().ToString()); + + } + } + + if (sweepComplete) { + int sweepInterval = GetArg("-zsweepinterval", 5); + if (sweepInterval < 1) { + fprintf(stderr,"%s: Invalid sweep interval of %d, setting to default of 5\n", __func__, sweepInterval); + sweepInterval = 5; + } + pwalletMain->nextSweep = sweepInterval + chainActive.Tip()->GetHeight(); + pwalletMain->fSweepRunning = false; + } + + LogPrint("zrpcunsafe", "%s: Created %d transactions with total Sapling output amount=%s\n", getId(), numTxCreated, FormatMoney(amountSwept)); + setSweepResult(numTxCreated, amountSwept, sweepTxIds); + return true; + +} + +void AsyncRPCOperation_sweep::setSweepResult(int numTxCreated, const CAmount& amountSwept, const std::vector& sweepTxIds) { + UniValue res(UniValue::VOBJ); + res.push_back(Pair("num_tx_created", numTxCreated)); + res.push_back(Pair("amount_swept", FormatMoney(amountSwept))); + UniValue txIds(UniValue::VARR); + for (const std::string& txId : sweepTxIds) { + txIds.push_back(txId); + } + res.push_back(Pair("sweep_txids", txIds)); + set_result(res); +} + +void AsyncRPCOperation_sweep::cancel() { + set_state(OperationStatus::CANCELLED); +} + +UniValue AsyncRPCOperation_sweep::getStatus() const { + UniValue v = AsyncRPCOperation::getStatus(); + UniValue obj = v.get_obj(); + obj.push_back(Pair("method", "sweep")); + obj.push_back(Pair("target_height", targetHeight_)); + return obj; +} diff --git a/src/wallet/asyncrpcoperation_sweep.h b/src/wallet/asyncrpcoperation_sweep.h new file mode 100644 index 000000000..8927bf8a3 --- /dev/null +++ b/src/wallet/asyncrpcoperation_sweep.h @@ -0,0 +1,42 @@ +// Copyright (c) 2016-2022 The Hush developers +// Distributed under the GPLv3 software license, see the accompanying +// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html +#include "amount.h" +#include "asyncrpcoperation.h" +#include "univalue.h" +#include "zcash/Address.hpp" +#include "zcash/zip32.h" + +//Default fee used for sweep transactions +static const CAmount DEFAULT_SWEEP_FEE = 10000; +extern CAmount fSweepTxFee; +extern bool fSweepMapUsed; +extern boost::optional rpcSweepAddress; + +class AsyncRPCOperation_sweep : public AsyncRPCOperation +{ +public: + AsyncRPCOperation_sweep(int targetHeight, bool fromRpc = false); + virtual ~AsyncRPCOperation_sweep(); + + // We don't want to be copied or moved around + AsyncRPCOperation_sweep(AsyncRPCOperation_sweep const&) = delete; // Copy construct + AsyncRPCOperation_sweep(AsyncRPCOperation_sweep&&) = delete; // Move construct + AsyncRPCOperation_sweep& operator=(AsyncRPCOperation_sweep const&) = delete; // Copy assign + AsyncRPCOperation_sweep& operator=(AsyncRPCOperation_sweep&&) = delete; // Move assign + + virtual void main(); + + virtual void cancel(); + + virtual UniValue getStatus() const; + +private: + int targetHeight_; + bool fromRPC_; + + bool main_impl(); + + void setSweepResult(int numTxCreated, const CAmount& amountSwept, const std::vector& sweepTxIds); + +}; From 4aedeb557bcd83c8a3f36c49dfa7144500631f2f Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Sat, 27 Aug 2022 10:35:08 -0700 Subject: [PATCH 03/45] Add z_sweepstatus RPC --- src/init.cpp | 1 + src/rpc/server.h | 1 + src/wallet/asyncrpcoperation_sweep.cpp | 2 +- src/wallet/rpcwallet.cpp | 42 ++++++++++++++++++++++++++ src/wallet/wallet.h | 6 +++- 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index dd6666710..2827a2bb9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2086,6 +2086,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zSweep); auto allowSweepToExternalWallet = GetArg("-zsweepexternal", false); + pwalletMain->sweepAddress = vSweep[i]; if (!hasSpendingKey) { if (allowSweepToExternalWallet) { diff --git a/src/rpc/server.h b/src/rpc/server.h index b6a20b559..b2c361765 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -472,6 +472,7 @@ extern UniValue z_getbalance(const UniValue& params, bool fHelp, const CPubKey& extern UniValue z_gettotalbalance(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +extern UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_shieldcoinbase(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_getoperationresult(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index 140c9902a..a2b6a19c3 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -64,7 +64,7 @@ void AsyncRPCOperation_sweep::main() { set_state(OperationStatus::FAILED); } - std::string s = strprintf("%s: Sapling Sweep transaction created. (status=%s", getId(), getStateAsString()); + std::string s = strprintf("%s: Sweep transaction created. (status=%s", getId(), getStateAsString()); if (success) { s += strprintf(", success)\n"); } else { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 42a145b94..e596ed03f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3315,6 +3315,48 @@ UniValue getalldata(const UniValue& params, bool fHelp,const CPubKey&) return returnObj; } +UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() > 0) + throw runtime_error( + "z_sweepstatus\n" + "\nGive details about zsweep operations since the node was started." + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_sweepstatus", "") + + HelpExampleRpc("z_sweepstatus", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + UniValue ret(UniValue::VOBJ); + auto amount_swept; + ret.push_back(Pair("zsweep", pwalletMain->fSweepEnabled)); + ret.push_back(Pair("running", pwalletMain->fSweepRunning)); + ret.push_back(Pair("amount_swept", amount_swept)); + + if (pwalletMain->fConsolidationRunning) { + ret.push_back(Pair("next_zsweep", pwalletMain->sweepInterval + chainActive.Tip()->GetHeight())); + } else { + if (pwalletMain->nextConsolidation == 0) { + ret.push_back(Pair("next_zsweep", chainActive.Tip()->GetHeight() + 1)); + } else { + ret.push_back(Pair("next_zsweep", pwalletMain->nextSweep)); + } + } + ret.push_back(Pair("zsweepinterval", pwalletMain->sweepInterval)); + ret.push_back(Pair("zsweepaddress", pwalletMain->sweepAddress)); + ret.push_back(Pair("zsweepmaxinputs", pwalletMain->sweepMaxInputs)); + ret.push_back(Pair("zsweepfee", pwalletMain->sweepFee)); + ret.push_back(Pair("zsweepexternal", pwalletMain->fSweepExternalEnabled)); + + return ret; +} + UniValue z_listreceivedaddress(const UniValue& params, bool fHelp,const CPubKey&) { if (!EnsureWalletIsAvailable(fHelp)) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f633f2564..ada169c73 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -805,8 +805,12 @@ public: bool fSaplingConsolidationEnabled = false; bool fConsolidationRunning = false; bool fSweepEnabled = false; + bool fSweepExternalEnabled = false; bool fSweepRunning = false; - int nextSweep = 0; + int nextSweep = 0; + int sweepInterval = 20; + int sweepMaxInputs = 200; + std::string sweepAddress = ""; void ClearNoteWitnessCache(); From 4eec81b1916302a895ed32aef93b9915f742b1cb Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 27 Aug 2022 13:57:00 -0400 Subject: [PATCH 04/45] Fix some issues in z_sweepstatus --- src/wallet/rpcwallet.cpp | 3 +-- src/wallet/wallet.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e596ed03f..c977e2764 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3334,10 +3334,9 @@ UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) LOCK2(cs_main, pwalletMain->cs_wallet); UniValue ret(UniValue::VOBJ); - auto amount_swept; ret.push_back(Pair("zsweep", pwalletMain->fSweepEnabled)); ret.push_back(Pair("running", pwalletMain->fSweepRunning)); - ret.push_back(Pair("amount_swept", amount_swept)); + ret.push_back(Pair("amount_swept", pwalletMain->amountSwept)); if (pwalletMain->fConsolidationRunning) { ret.push_back(Pair("next_zsweep", pwalletMain->sweepInterval + chainActive.Tip()->GetHeight())); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index ada169c73..4ae4716cb 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -808,7 +808,9 @@ public: bool fSweepExternalEnabled = false; bool fSweepRunning = false; int nextSweep = 0; + int amountSwept = 0; int sweepInterval = 20; + int sweepFee = 10000; int sweepMaxInputs = 200; std::string sweepAddress = ""; From 37301759713fce42d434f2e38d3694fa200d9267 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 27 Aug 2022 14:04:53 -0400 Subject: [PATCH 05/45] Add some logging when skipping sweep/consolidation --- src/wallet/wallet.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 83f95cbb3..8c6e82586 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -520,12 +520,14 @@ void CWallet::RunSaplingSweep(int blockHeight) { } //Don't Run if consolidation will run soon. - if (fSaplingConsolidationEnabled && nextConsolidation - 15 <= blockHeight) { + if (fSaplingConsolidationEnabled && nextConsolidation - 5 <= blockHeight) { + LogPrintf("%s: not consolidating since next sweep is within 5 blocks\n", __func__); return; } //Don't Run While consolidation is running. if (fConsolidationRunning) { + LogPrintf("%s: not sweeping since consolidation is currently running\n", __func__); return; } From 0e3889c96ae3fab105acc524b87450477ef7d744 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 27 Aug 2022 15:03:11 -0400 Subject: [PATCH 06/45] List z_sweepstatus in RPC list --- src/wallet/rpcwallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c977e2764..00d120f54 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8536,6 +8536,7 @@ static const CRPCCommand commands[] = { "wallet", "z_anonsetblockdelta", &z_anonsetblockdelta, true }, { "wallet", "z_gettotalbalance", &z_gettotalbalance, false }, { "wallet", "z_mergetoaddress", &z_mergetoaddress, false }, + { "wallet", "z_sweepstatus", &z_sweepstatus, true }, { "wallet", "z_sendmany", &z_sendmany, false }, { "wallet", "z_shieldcoinbase", &z_shieldcoinbase, false }, { "wallet", "z_getoperationstatus", &z_getoperationstatus, true }, From 1748f0f2a30da57129d3e01bcab029498f1744b8 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 27 Aug 2022 22:31:40 -0400 Subject: [PATCH 07/45] Improved logging and error checking in consolidation and sweeping --- ...asyncrpcoperation_saplingconsolidation.cpp | 22 +++++------ src/wallet/asyncrpcoperation_sweep.cpp | 38 +++++++++++-------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index 361d04390..25c1f54ac 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -82,7 +82,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { 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",opid); + LogPrintf("%s: Consolidation txs would be created before a NU activation but may expire after. Skipping this round.\n",opid); setConsolidationResult(0, 0, std::vector()); return status; } @@ -97,7 +97,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { pwalletMain->GetFilteredNotes(saplingEntries, "", 11); if(saplingEntries.size() == 0) { - LogPrint("zrpcunsafe", "%s: Nothing to consolidate, done.\n",opid); + LogPrintf("%s: Nothing to consolidate, done.\n",opid); return true; } @@ -109,7 +109,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { libzcash::SaplingPaymentAddress saplingAddress = boost::get(zAddress); addresses.insert(saplingAddress); } else { - LogPrint("zrpcunsafe", "%s: Invalid zaddr, exiting\n", opid); + LogPrintf("%s: Invalid zaddr, exiting\n", opid); return false; } } @@ -176,14 +176,14 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { std::vector> witnesses; { LOCK2(cs_main, pwalletMain->cs_wallet); - LogPrint("zrpcunsafe", "%s: Fetching note witnesses\n", opid); + // LogPrint("zrpcunsafe", "%s: Fetching note witnesses\n", opid); 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", opid); + LogPrintf("%s: Missing Witnesses! Stopping.\n", opid); status=false; break; } @@ -213,7 +213,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { // actually add our sietch zoutput, the new way builder.AddSaplingOutput(extsk.expsk.ovk, sietchZoutput, amount); } else { - LogPrint("zrpcunsafe", "%s: Invalid payment address %s! Stopping.\n", opid, zdust); + LogPrintf("%s: Invalid payment address %s! Stopping.\n", opid, zdust); status = false; break; } @@ -223,25 +223,25 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { auto maybe_tx = builder.Build(); if (!maybe_tx) { - LogPrint("zrpcunsafe", "%s: Failed to build transaction.\n",opid); + LogPrintf("%s: Failed to build transaction.\n",opid); status=false; break; } CTransaction tx = maybe_tx.get(); if (isCancelled()) { - LogPrint("zrpcunsafe", "%s: Canceled. Stopping.\n", opid); + LogPrintf("%s: Canceled. Stopping.\n", opid); status=false; break; } if(pwalletMain->CommitAutomatedTx(tx)) { - LogPrint("zrpcunsafe", "%s: Committed consolidation transaction with txid=%s\n",opid, tx.GetHash().ToString()); + LogPrintf("%s: Committed consolidation transaction with txid=%s\n",opid, tx.GetHash().ToString()); amountConsolidated += actualAmountToSend; consolidationTxIds.push_back(tx.GetHash().ToString()); numTxCreated++; } else { - LogPrint("zrpcunsafe", "%s: Consolidation transaction FAILED in CommitTransaction, txid=%s\n",opid , tx.GetHash().ToString()); + LogPrintf("%s: Consolidation transaction FAILED in CommitTransaction, txid=%s\n",opid , tx.GetHash().ToString()); setConsolidationResult(numTxCreated, amountConsolidated, consolidationTxIds); status = false; break; @@ -249,7 +249,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { } } - LogPrint("zrpcunsafe", "%s: Created %d transactions with total Sapling output amount=%s,status=%d\n",opid , numTxCreated, FormatMoney(amountConsolidated), (int)status); + LogPrintf("%s: Created %d transactions with total Sapling output amount=%s,status=%d\n",opid , numTxCreated, FormatMoney(amountConsolidated), (int)status); setConsolidationResult(numTxCreated, amountConsolidated, consolidationTxIds); return status; } diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index a2b6a19c3..60132bb38 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -75,11 +75,13 @@ void AsyncRPCOperation_sweep::main() { } bool AsyncRPCOperation_sweep::main_impl() { - LogPrint("zrpcunsafe", "%s: Beginning asyncrpcoperation_sweep.\n", getId()); + bool status=true; + auto opid=getId(); + LogPrintf("%s: Beginning asyncrpcoperation_sweep.\n", getId()); auto consensusParams = Params().GetConsensus(); auto nextActivationHeight = NextActivationHeight(targetHeight_, consensusParams); if (nextActivationHeight && targetHeight_ + SWEEP_EXPIRY_DELTA >= nextActivationHeight.get()) { - LogPrint("zrpcunsafe", "%s: Sweep txs would be created before a NU activation but may expire after. Skipping this round.\n", getId()); + LogPrintf("%s: Sweep txs would be created before a NU activation but may expire after. Skipping this round.\n", getId()); setSweepResult(0, 0, std::vector()); return true; } @@ -201,7 +203,7 @@ bool AsyncRPCOperation_sweep::main_impl() { LOCK2(cs_main, pwalletMain->cs_wallet); builder.SetExpiryHeight(chainActive.Tip()->GetHeight()+ SWEEP_EXPIRY_DELTA); } - LogPrint("zrpcunsafe", "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - fee)); + LogPrintf("%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - fee)); // Select Sapling notes std::vector ops; @@ -222,7 +224,7 @@ bool AsyncRPCOperation_sweep::main_impl() { // Add Sapling spends for (size_t i = 0; i < notes.size(); i++) { if (!witnesses[i]) { - LogPrint("zrpcunsafe", "%s: Missing Witnesses. Stopping.\n", getId()); + LogPrintf("%s: Missing Witnesses! Stopping.\n", getId()); break; } builder.AddSaplingSpend(extsk.expsk, notes[i], anchor, witnesses[i].get()); @@ -244,8 +246,8 @@ bool AsyncRPCOperation_sweep::main_impl() { builder.AddSaplingOutput(extsk.expsk.ovk, sietchZoutput, amount); } else { - LogPrint("zrpcunsafe", "%s: Invalid payment address %s! Stopping.\n", __func__, zdust); - // status = false; + LogPrintf("%s: Invalid payment address %s! Stopping.\n", __func__, zdust); + status = false; break; } } @@ -253,22 +255,28 @@ bool AsyncRPCOperation_sweep::main_impl() { auto maybe_tx = builder.Build(); if (!maybe_tx) { - LogPrint("zrpcunsafe", "%s: Failed to build transaction %s.\n",__func__, getId()); - // status=false; + LogPrintf("%s: Failed to build transaction %s.\n",__func__, getId()); + status=false; break; } CTransaction tx = maybe_tx.get(); if (isCancelled()) { - LogPrint("zrpcunsafe", "%s: Canceled. Stopping.\n", getId()); + LogPrintf("%s: Canceled. Stopping.\n", getId()); break; } - pwalletMain->CommitAutomatedTx(tx); - LogPrint("zrpcunsafe", "%s: Committed sweep transaction with txid=%s\n", getId(), tx.GetHash().ToString()); - amountSwept += amountToSend - fee; - sweepTxIds.push_back(tx.GetHash().ToString()); - + if (pwalletMain->CommitAutomatedTx(tx)) { + LogPrintf("%s: Committed sweep transaction with txid=%s\n", getId(), tx.GetHash().ToString()); + amountSwept += amountToSend - fee; + sweepTxIds.push_back(tx.GetHash().ToString()); + numTxCreated++; + } else { + LogPrintf("%s: Sweep transaction FAILED in CommitTransaction, txid=%s\n",opid , tx.GetHash().ToString()); + setSweepResult(numTxCreated, amountSwept, sweepTxIds); + status = false; + break; + } } } @@ -282,7 +290,7 @@ bool AsyncRPCOperation_sweep::main_impl() { pwalletMain->fSweepRunning = false; } - LogPrint("zrpcunsafe", "%s: Created %d transactions with total Sapling output amount=%s\n", getId(), numTxCreated, FormatMoney(amountSwept)); + LogPrintf("%s: Created %d transactions with total output amount=%s\n", getId(), numTxCreated, FormatMoney(amountSwept)); setSweepResult(numTxCreated, amountSwept, sweepTxIds); return true; From 702c2163cd13137e71778beca613ab4b7f792dda Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 30 Aug 2022 21:05:09 -0400 Subject: [PATCH 08/45] Log if we have no zsweepaddress or an invalid zsweepaddress --- src/wallet/asyncrpcoperation_sweep.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index 60132bb38..b76915e92 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -103,12 +103,14 @@ bool AsyncRPCOperation_sweep::main_impl() { } } } else { + LogPrintf("%s: No zsweepaddress configured, exiting\n", opid); return false; } } else { if (boost::get(&rpcSweepAddress) != nullptr) { sweepAddress = boost::get(rpcSweepAddress); } else { + LogPrintf("%s: Invalid zsweepaddress, exiting\n", opid); return false; } } From b4b6988eb03cfc90332fbc82dec4e7e5d64b48be Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 30 Aug 2022 21:05:27 -0400 Subject: [PATCH 09/45] Log when we use fee=0 for small inputs --- src/wallet/asyncrpcoperation_sweep.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index b76915e92..721309385 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -197,9 +197,10 @@ bool AsyncRPCOperation_sweep::main_impl() { CAmount fee = fSweepTxFee; if (amountToSend <= fSweepTxFee) { - fee = 0; + LogPrintf("%s: Amount to send %s is <= fee, using fee=0", getId(), FormatMoney(amountToSend)); + fee = 0; } - amountSwept += amountToSend; + auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain); { LOCK2(cs_main, pwalletMain->cs_wallet); From b50c51f797c3b146ffb4bc81fa668e97e9d619cb Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 30 Aug 2022 21:05:57 -0400 Subject: [PATCH 10/45] Log value of status and correctly return status value instead of always returning success, derp --- src/wallet/asyncrpcoperation_sweep.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index 721309385..780ddfa29 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -293,10 +293,9 @@ bool AsyncRPCOperation_sweep::main_impl() { pwalletMain->fSweepRunning = false; } - LogPrintf("%s: Created %d transactions with total output amount=%s\n", getId(), numTxCreated, FormatMoney(amountSwept)); + LogPrintf("%s: Created %d transactions with total output amount=%s, status=%d\n", getId(), numTxCreated, FormatMoney(amountSwept), (int)status); setSweepResult(numTxCreated, amountSwept, sweepTxIds); - return true; - + return status; } void AsyncRPCOperation_sweep::setSweepResult(int numTxCreated, const CAmount& amountSwept, const std::vector& sweepTxIds) { From 57baf25b0257c30636484b6ac8e8191b2f2ab320 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 30 Aug 2022 22:05:41 -0400 Subject: [PATCH 11/45] Do not add to amountConsolidated until we successfully create the tx and only do it once --- src/wallet/asyncrpcoperation_saplingconsolidation.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index 25c1f54ac..f9f2ebe1e 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -157,7 +157,6 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { if (fromNotes.size() < minQuantity) continue; - amountConsolidated += amountToSend; auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain); builder.SetExpiryHeight(targetHeight_ + CONSOLIDATION_EXPIRY_DELTA); auto actualAmountToSend = amountToSend < fConsolidationTxFee ? 0 : amountToSend - fConsolidationTxFee; @@ -219,8 +218,6 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { } } LogPrint("zrpcunsafe", "%s: Done adding %d sietch zouts\n", opid, MIN_ZOUTS); - //CTransaction tx = builder.Build(); - auto maybe_tx = builder.Build(); if (!maybe_tx) { LogPrintf("%s: Failed to build transaction.\n",opid); From 88263995c81c5c62f367bf3c2bb3bf7eb8ce2d7f Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 30 Aug 2022 22:27:34 -0400 Subject: [PATCH 12/45] Do not coredump if pnode=NULL in RelayTransaction --- src/net.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/net.cpp b/src/net.cpp index db47d5ec7..7df4f5e90 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2125,6 +2125,10 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss) // Only relay to randomly chosen 50% of peers BOOST_FOREACH(CNode* pnode, vRelayNodes) { + //TODO: correct fix is to correctly LOCK vRelayNodes + if(!pnode) + continue; + if(!pnode->fRelayTxes) continue; LOCK(pnode->cs_filter); From 26fd16cb523abddfc2ee38f7b1766ddef20d9434 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Wed, 31 Aug 2022 11:09:18 -0700 Subject: [PATCH 13/45] Avoid relaying expired transactions which causes other nodes to ban us --- src/wallet/wallet.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 8c6e82586..1db729e2f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3121,6 +3121,13 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) if (wtx.nTimeReceived > nTime) continue; + // Do not relay expired transactions, to avoid other nodes banning us + if (wtx.nExpiryHeight > 0 && wtx.nExpiryHeight < chainActive.LastTip->GetHeight()) { + fprintf(stderr,"%s: ignoring expired tx %s\n", wtx.GetHash().ToString().c_str() ); + // TODO: should we call EraseFromWallet(wtx) right here? + continue; + } + if ( (wtx.nLockTime >= LOCKTIME_THRESHOLD && wtx.nLockTime < now-HUSH_MAXMEMPOOLTIME) ) { if(fDebug) { From 4573a8336f84fb63e5d0641be520561aafe80ff8 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Wed, 31 Aug 2022 14:10:40 -0400 Subject: [PATCH 14/45] Fix compile error --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1db729e2f..d7b818723 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3122,7 +3122,7 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) continue; // Do not relay expired transactions, to avoid other nodes banning us - if (wtx.nExpiryHeight > 0 && wtx.nExpiryHeight < chainActive.LastTip->GetHeight()) { + if (wtx.nExpiryHeight > 0 && wtx.nExpiryHeight < chainActive.LastTip()->GetHeight()) { fprintf(stderr,"%s: ignoring expired tx %s\n", wtx.GetHash().ToString().c_str() ); // TODO: should we call EraseFromWallet(wtx) right here? continue; From 63f9e2230818ee817c595b3ac5254d0904bbd103 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Wed, 31 Aug 2022 14:15:38 -0400 Subject: [PATCH 15/45] log the function name correctly --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d7b818723..229f49275 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3123,7 +3123,7 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) // Do not relay expired transactions, to avoid other nodes banning us if (wtx.nExpiryHeight > 0 && wtx.nExpiryHeight < chainActive.LastTip()->GetHeight()) { - fprintf(stderr,"%s: ignoring expired tx %s\n", wtx.GetHash().ToString().c_str() ); + fprintf(stderr,"%s: ignoring expired tx %s\n", __func__, wtx.GetHash().ToString().c_str() ); // TODO: should we call EraseFromWallet(wtx) right here? continue; } From 3327b25a288d5792a58c4723cfedcbf8578d40ea Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Sat, 3 Sep 2022 16:22:43 -0700 Subject: [PATCH 16/45] fix logging bug --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1db729e2f..453b486d8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3123,7 +3123,7 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) // Do not relay expired transactions, to avoid other nodes banning us if (wtx.nExpiryHeight > 0 && wtx.nExpiryHeight < chainActive.LastTip->GetHeight()) { - fprintf(stderr,"%s: ignoring expired tx %s\n", wtx.GetHash().ToString().c_str() ); + fprintf(stderr,"%s: ignoring expired tx %s\n", __func__, wtx.GetHash().ToString().c_str() ); // TODO: should we call EraseFromWallet(wtx) right here? continue; } From 5c8c7507ec30c6ccab5d3367634e25a74c0a2daa Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Tue, 6 Sep 2022 05:55:03 -0700 Subject: [PATCH 17/45] Add z_consolidationstatus RPC --- src/rpc/server.h | 1 + ...asyncrpcoperation_saplingconsolidation.cpp | 1 + src/wallet/rpcwallet.cpp | 40 +++++++++++++++++++ src/wallet/wallet.h | 1 + 4 files changed, 43 insertions(+) diff --git a/src/rpc/server.h b/src/rpc/server.h index b2c361765..3b3e11c6a 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -473,6 +473,7 @@ extern UniValue z_gettotalbalance(const UniValue& params, bool fHelp, const CPub extern UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp +extern UniValue z_consolidationstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_shieldcoinbase(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp extern UniValue z_getoperationresult(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index f9f2ebe1e..5422e1715 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -108,6 +108,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { if (boost::get(&zAddress) != nullptr) { libzcash::SaplingPaymentAddress saplingAddress = boost::get(zAddress); addresses.insert(saplingAddress); + consolidationAddress = zAddress; } else { LogPrintf("%s: Invalid zaddr, exiting\n", opid); return false; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 00d120f54..749576178 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3315,6 +3315,45 @@ UniValue getalldata(const UniValue& params, bool fHelp,const CPubKey&) return returnObj; } +UniValue z_consolidationstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() > 0) + throw runtime_error( + "z_consolidationstatus\n" + "\nGive details about consolidation operations since the node was started." + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_consolidationstatus", "") + + HelpExampleRpc("z_consolidationstatus", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("consolidation", pwalletMain->fConsolidationEnabled)); + ret.push_back(Pair("running", pwalletMain->fConsolidationRunning)); + ret.push_back(Pair("amount_consolidated", pwalletMain->amountConsolidated)); + + if (pwalletMain->fConsolidationRunning) { + ret.push_back(Pair("next_consolidation", pwalletMain->consolidationInterval + chainActive.Tip()->GetHeight())); + } else { + if (pwalletMain->nextConsolidation == 0) { + ret.push_back(Pair("next_consolidation", chainActive.Tip()->GetHeight() + 1)); + } else { + ret.push_back(Pair("next_consolidation", pwalletMain->nextConsolidation)); + } + } + ret.push_back(Pair("consolidationinterval", pwalletMain->consolidationInterval)); + ret.push_back(Pair("consolidationaddress", pwalletMain->consolidationAddress)); + ret.push_back(Pair("consolidationtxfee",(int)fConsolidationTxFee)); + + return ret; +} + UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) { @@ -8537,6 +8576,7 @@ static const CRPCCommand commands[] = { "wallet", "z_gettotalbalance", &z_gettotalbalance, false }, { "wallet", "z_mergetoaddress", &z_mergetoaddress, false }, { "wallet", "z_sweepstatus", &z_sweepstatus, true }, + { "wallet", "z_consolidationstatus", &z_consolidationstatus, true }, { "wallet", "z_sendmany", &z_sendmany, false }, { "wallet", "z_shieldcoinbase", &z_shieldcoinbase, false }, { "wallet", "z_getoperationstatus", &z_getoperationstatus, true }, diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4ae4716cb..7bf0d446a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -813,6 +813,7 @@ public: int sweepFee = 10000; int sweepMaxInputs = 200; std::string sweepAddress = ""; + std::string consolidationAddress = ""; void ClearNoteWitnessCache(); From 9c5e5c550ccc803ff71b6c841c61c023e1961ef9 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 6 Sep 2022 09:20:53 -0400 Subject: [PATCH 18/45] Fix compile issues in z_consolidationstatus --- src/init.cpp | 1 + src/wallet/asyncrpcoperation_saplingconsolidation.cpp | 1 - src/wallet/rpcwallet.cpp | 7 ++++--- src/wallet/wallet.h | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 2827a2bb9..ce03bd7fb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2100,6 +2100,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (pwalletMain->fSaplingConsolidationEnabled) { //Validate 1 Consolidation address only that matches the sweep address vector& vaddresses = mapMultiArgs["-consolidatesaplingaddress"]; + pwalletMain->consolidationAddress = vaddresses[0]; if (vaddresses.size() == 0) { fConsolidationMapUsed = true; mapMultiArgs["-consolidatesaplingaddress"] = vSweep; diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index 5422e1715..f9f2ebe1e 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -108,7 +108,6 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() { if (boost::get(&zAddress) != nullptr) { libzcash::SaplingPaymentAddress saplingAddress = boost::get(zAddress); addresses.insert(saplingAddress); - consolidationAddress = zAddress; } else { LogPrintf("%s: Invalid zaddr, exiting\n", opid); return false; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 749576178..745dc3ce4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3334,12 +3334,13 @@ UniValue z_consolidationstatus(const UniValue& params, bool fHelp, const CPubKey LOCK2(cs_main, pwalletMain->cs_wallet); UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("consolidation", pwalletMain->fConsolidationEnabled)); + ret.push_back(Pair("consolidation", pwalletMain->fSaplingConsolidationEnabled)); ret.push_back(Pair("running", pwalletMain->fConsolidationRunning)); ret.push_back(Pair("amount_consolidated", pwalletMain->amountConsolidated)); if (pwalletMain->fConsolidationRunning) { - ret.push_back(Pair("next_consolidation", pwalletMain->consolidationInterval + chainActive.Tip()->GetHeight())); + // TODO: set a static consolidationInterval instead of it being non-deterministic + // ret.push_back(Pair("next_consolidation", pwalletMain->consolidationInterval + chainActive.Tip()->GetHeight())); } else { if (pwalletMain->nextConsolidation == 0) { ret.push_back(Pair("next_consolidation", chainActive.Tip()->GetHeight() + 1)); @@ -3347,7 +3348,7 @@ UniValue z_consolidationstatus(const UniValue& params, bool fHelp, const CPubKey ret.push_back(Pair("next_consolidation", pwalletMain->nextConsolidation)); } } - ret.push_back(Pair("consolidationinterval", pwalletMain->consolidationInterval)); + // ret.push_back(Pair("consolidationinterval", pwalletMain->consolidationInterval)); ret.push_back(Pair("consolidationaddress", pwalletMain->consolidationAddress)); ret.push_back(Pair("consolidationtxfee",(int)fConsolidationTxFee)); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7bf0d446a..fdaee018b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -809,6 +809,7 @@ public: bool fSweepRunning = false; int nextSweep = 0; int amountSwept = 0; + int amountConsolidated = 0; int sweepInterval = 20; int sweepFee = 10000; int sweepMaxInputs = 200; From 55703fdb95cf6300325341abb04d669e024797c0 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 6 Sep 2022 10:03:47 -0400 Subject: [PATCH 19/45] Do not ban nodes which relay expired transactions --- src/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b8e64dc27..4e466ba45 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1304,10 +1304,12 @@ bool ContextualCheckTransaction(int32_t slowflag,const CBlock *block, CBlockInde // Check that all transactions are unexpired if (IsExpiredTx(tx, nHeight)) { // Don't increase banscore if the transaction only just expired - int expiredDosLevel = IsExpiredTx(tx, nHeight - 1) ? (dosLevel > 10 ? dosLevel : 10) : 0; + //int expiredDosLevel = IsExpiredTx(tx, nHeight - 1) ? (dosLevel > 10 ? dosLevel : 10) : 0; //string strHex = EncodeHexTx(tx); - //fprintf(stderr, "transaction exipred.%s\n",strHex.c_str()); - return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction %s is expired, expiry block %i vs current block %i\n",tx.GetHash().ToString(),tx.nExpiryHeight,nHeight), REJECT_INVALID, "tx-overwinter-expired"); + //fprintf(stderr, "transaction expired.%s\n",strHex.c_str()); + + // Do not ban nodes which relay expired tx's, it's a bug not an attack + return state.DoS(0, error("ContextualCheckTransaction(): transaction %s is expired, expiry block %i vs current block %i\n",tx.GetHash().ToString(),tx.nExpiryHeight,nHeight), REJECT_INVALID, "tx-overwinter-expired"); } } From 456cf77b652c5afb63a00b079b5b9d46ffd0108b Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 6 Sep 2022 10:08:24 -0400 Subject: [PATCH 20/45] Exit RelayTransaction early if we have no nodes to relay to --- src/net.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/net.cpp b/src/net.cpp index 7df4f5e90..40e5070de 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2109,6 +2109,12 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss) auto vRelayNodes = vNodes; + // If we have no nodes to relay to, there is nothing to do + if(vNodes.size() == 0) { + fprintf(stderr, "%s: No nodes to relay to!\n", __func__ ); + return; + } + // We always round down, except when we have only 1 connection auto newSize = (vNodes.size() / 2) == 0 ? 1 : (vNodes.size() / 2); From 85810121633dd38fd21d5723bd91ad8896028be8 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 6 Sep 2022 11:19:16 -0400 Subject: [PATCH 21/45] Sweep operations might not create any txs, so make logging more precise --- src/wallet/asyncrpcoperation_sweep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index 780ddfa29..fcf4769bb 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -64,7 +64,7 @@ void AsyncRPCOperation_sweep::main() { set_state(OperationStatus::FAILED); } - std::string s = strprintf("%s: Sweep transaction created. (status=%s", getId(), getStateAsString()); + std::string s = strprintf("%s: Sweep operation finished. (status=%s", getId(), getStateAsString()); if (success) { s += strprintf(", success)\n"); } else { From 8d8f988a1928ec6ff7d7dbce1649b6919aeeb499 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 6 Sep 2022 11:19:46 -0400 Subject: [PATCH 22/45] Delete expired txs from the wallet, since they can never be included in a block --- src/wallet/wallet.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 229f49275..e024d826c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1715,9 +1715,9 @@ void CWallet::EraseFromWallet(const uint256 &hash) if (mapWallet.erase(hash)) CWalletDB(strWalletFile).EraseTx(hash); } - if(fDebug) { - LogPrintf("%s: erased txid %s\n", __func__, hash.ToString().c_str() ); - } + + LogPrintf("%s: erased txid %s\n", __func__, hash.ToString().c_str() ); + return; } @@ -3111,6 +3111,8 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) // Sort them in chronological order multimap mapSorted; uint32_t now = (uint32_t)time(NULL); + + // vector of wallet transactions to delete std::vector vwtxh; uint32_t erased = 0, skipped = 0; @@ -3122,9 +3124,11 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) continue; // Do not relay expired transactions, to avoid other nodes banning us + // Current code will not ban nodes relaying expired txs but older nodes will if (wtx.nExpiryHeight > 0 && wtx.nExpiryHeight < chainActive.LastTip()->GetHeight()) { fprintf(stderr,"%s: ignoring expired tx %s\n", __func__, wtx.GetHash().ToString().c_str() ); - // TODO: should we call EraseFromWallet(wtx) right here? + // append to list of txs to delete + vwtxh.push_back(wtx.GetHash()); continue; } @@ -3134,6 +3138,7 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) LogPrintf("%s: skip Relaying wtx %s nLockTime %u vs now.%u\n", __func__, wtx.GetHash().ToString(),(uint32_t)wtx.nLockTime,now); } skipped++; + // TODO: this does not seem to handle rescanning+finding old coinbase txs correctly //vwtxh.push_back(wtx.GetHash()); continue; } @@ -3150,10 +3155,13 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) } } - // TODO: this does not seem to handle rescanning+finding old coinbase txs correctly - // Unless we remove these unconfirmed txs from the wallet, they will + // Unless we remove these unconfirmed and/or expired txs from the wallet, they will // persist there forever. They are too old to be accepted by network // consensus rules, so we erase them. + // Expired txs are always unconfirmed, but unconfirmed tx's could be expired or not, + // i.e. expired txs are a subset of unconfirmed tx's. Expired tx's can never be included + // in a block because they are against consensus rules. Unconfirmed tx's might still be + // included in a future block. for (auto hash : vwtxh) { EraseFromWallet(hash); @@ -3174,6 +3182,7 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime) if (GetTime() < nNextResend || !fBroadcastTransactions) return; bool fFirst = (nNextResend == 0); + // TODO: BTC Core changed this to be every 12 hours instead of every 30 mins nNextResend = GetTime() + GetRand(30 * 60); if (fFirst) return; From 61ecffd07460beac9d8c71fe52fb2afb307289e9 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 6 Sep 2022 14:30:06 -0400 Subject: [PATCH 23/45] Change default sweep interval to 10 and consolidation to be deterministic every 45 blocks --- src/wallet/asyncrpcoperation_sweep.cpp | 8 ++++---- src/wallet/wallet.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index fcf4769bb..6045cf46f 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -284,10 +284,10 @@ bool AsyncRPCOperation_sweep::main_impl() { } if (sweepComplete) { - int sweepInterval = GetArg("-zsweepinterval", 5); - if (sweepInterval < 1) { - fprintf(stderr,"%s: Invalid sweep interval of %d, setting to default of 5\n", __func__, sweepInterval); - sweepInterval = 5; + int sweepInterval = GetArg("-zsweepinterval", 10); + if (sweepInterval < 5) { + fprintf(stderr,"%s: Invalid sweep interval of %d, setting to default of 10\n", __func__, sweepInterval); + sweepInterval = 10; } pwalletMain->nextSweep = sweepInterval + chainActive.Tip()->GetHeight(); pwalletMain->fSweepRunning = false; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e024d826c..8cee74ed2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -559,7 +559,7 @@ void CWallet::RunSaplingConsolidation(int blockHeight) { return; } - int consolidateInterval = rand() % 5 + 5; + int consolidateInterval = 45; if(fZdebug) fprintf(stderr,"%s: height=%d interval=%d\n", __func__, blockHeight, consolidateInterval); From 8770fb7035f098cdd54f9c6eba9fe48b9197b893 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Thu, 8 Sep 2022 18:11:10 -0700 Subject: [PATCH 24/45] Hopefully avoid a coredump when consolidation=1 and consolidatesaplingaddress is not specified --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index ce03bd7fb..31073cf16 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2100,11 +2100,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (pwalletMain->fSaplingConsolidationEnabled) { //Validate 1 Consolidation address only that matches the sweep address vector& vaddresses = mapMultiArgs["-consolidatesaplingaddress"]; - pwalletMain->consolidationAddress = vaddresses[0]; if (vaddresses.size() == 0) { fConsolidationMapUsed = true; mapMultiArgs["-consolidatesaplingaddress"] = vSweep; } else { + pwalletMain->consolidationAddress = vaddresses[0]; for (int i = 0; i < vaddresses.size(); i++) { if (vSweep[0] != vaddresses[i]) { return InitError("Consolidation can only be used on the sweep address when sweep is enabled."); From 4647bc85257033732a19c985f8fbe64f3328b15c Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 9 Sep 2022 06:30:06 -0700 Subject: [PATCH 25/45] Try to generate a stacktrace for runtime errors in consolidation --- src/wallet/asyncrpcoperation_saplingconsolidation.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index f9f2ebe1e..d75100be4 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -16,6 +16,7 @@ #include "util.h" #include "utilmoneystr.h" #include "wallet.h" +#include CAmount fConsolidationTxFee = DEFAULT_CONSOLIDATION_FEE; bool fConsolidationMapUsed = false; @@ -44,6 +45,11 @@ void AsyncRPCOperation_saplingconsolidation::main() { set_error_code(code); set_error_message(message); } catch (const runtime_error& e) { + const boost::stacktrace::stacktrace* st = boost::get_error_info(e); + if (st) { + std::cerr << *st << '\n'; + } + set_error_code(-1); set_error_code(-1); set_error_message("runtime error: " + string(e.what())); } catch (const logic_error& e) { From 8f49549a1d4fc3ae3808b47bfdeeb9e672308a94 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 9 Sep 2022 09:37:16 -0400 Subject: [PATCH 26/45] Fix stacktrace --- src/wallet/asyncrpcoperation_saplingconsolidation.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index d75100be4..e578958bc 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -45,10 +45,7 @@ void AsyncRPCOperation_saplingconsolidation::main() { set_error_code(code); set_error_message(message); } catch (const runtime_error& e) { - const boost::stacktrace::stacktrace* st = boost::get_error_info(e); - if (st) { - std::cerr << *st << '\n'; - } + std::cerr << boost::stacktrace::stacktrace() << '\n'; set_error_code(-1); set_error_code(-1); set_error_message("runtime error: " + string(e.what())); From 85f7ac4c8c22600443e354c488829910abe40bc9 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 9 Sep 2022 15:14:01 -0700 Subject: [PATCH 27/45] Enable function names and line numbers in backtraces --- src/wallet/asyncrpcoperation_saplingconsolidation.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index e578958bc..79a0342a0 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -17,6 +17,11 @@ #include "utilmoneystr.h" #include "wallet.h" #include +#include + +// enable function names and line numbers in backtraces +#define BOOST_STACKTRACE_LINK +#define BOOST_STACKTRACE_USE_ADDR2LINE CAmount fConsolidationTxFee = DEFAULT_CONSOLIDATION_FEE; bool fConsolidationMapUsed = false; From 1259dd512dfe36cd0a8fe1a9bfd5b49a538bd00c Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 10 Sep 2022 19:37:34 -0400 Subject: [PATCH 28/45] Fix 'not enough conversion params' bug --- src/wallet/asyncrpcoperation_saplingconsolidation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index 79a0342a0..0d5fa35aa 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -86,7 +86,7 @@ void AsyncRPCOperation_saplingconsolidation::main() { bool AsyncRPCOperation_saplingconsolidation::main_impl() { bool status=true; auto opid=getId(); - LogPrintf("%s: Beginning AsyncRPCOperation_saplingconsolidation.\n", __func__, opid); + LogPrintf("%s: Beginning AsyncRPCOperation_saplingconsolidation\n", opid); auto consensusParams = Params().GetConsensus(); auto nextActivationHeight = NextActivationHeight(targetHeight_, consensusParams); if (nextActivationHeight && targetHeight_ + CONSOLIDATION_EXPIRY_DELTA >= nextActivationHeight.get()) { From 772160af6d940889608e6494ddf9f77a2d93eda1 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 10 Sep 2022 19:38:08 -0400 Subject: [PATCH 29/45] Add more logging for consolidation + sweeping --- .../asyncrpcoperation_saplingconsolidation.cpp | 4 ++-- src/wallet/wallet.cpp | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp index 0d5fa35aa..6eb20d2e6 100644 --- a/src/wallet/asyncrpcoperation_saplingconsolidation.cpp +++ b/src/wallet/asyncrpcoperation_saplingconsolidation.cpp @@ -50,7 +50,7 @@ void AsyncRPCOperation_saplingconsolidation::main() { set_error_code(code); set_error_message(message); } catch (const runtime_error& e) { - std::cerr << boost::stacktrace::stacktrace() << '\n'; + std::cerr << "Consolidation stacktrace:" << '\n' << boost::stacktrace::stacktrace() << '\n'; set_error_code(-1); set_error_code(-1); set_error_message("runtime error: " + string(e.what())); @@ -73,7 +73,7 @@ void AsyncRPCOperation_saplingconsolidation::main() { set_state(OperationStatus::FAILED); } - std::string s = strprintf("%s: Sapling Consolidation transaction created. (status=%s", getId(), getStateAsString()); + std::string s = strprintf("%s: Sapling Consolidation operation complete. (status=%s", getId(), getStateAsString()); if (success) { s += strprintf(", success)\n"); } else { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 8cee74ed2..e8434afcf 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -515,13 +515,14 @@ void CWallet::RunSaplingSweep(int blockHeight) { return; } + LogPrintf("%s: Sweep enabled at blockHeight=%d nextSweep=%d\n", __func__, blockHeight, nextSweep); if (nextSweep > blockHeight) { return; } //Don't Run if consolidation will run soon. if (fSaplingConsolidationEnabled && nextConsolidation - 5 <= blockHeight) { - LogPrintf("%s: not consolidating since next sweep is within 5 blocks\n", __func__); + LogPrintf("%s: not sweeping since next consolidation is within 5 blocks, nextConsolidation=%d , blockHeight=%d\n", __func__, nextConsolidation, blockHeight); return; } @@ -545,9 +546,10 @@ void CWallet::RunSaplingSweep(int blockHeight) { } void CWallet::RunSaplingConsolidation(int blockHeight) { - if (!NetworkUpgradeActive(blockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { - return; - } + // Sapling is always active on HUSH+HSCs + //if (!NetworkUpgradeActive(blockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { + // return; + //} LOCK(cs_wallet); @@ -555,6 +557,8 @@ void CWallet::RunSaplingConsolidation(int blockHeight) { return; } + LogPrintf("%s: consolidation enabled at blockHeight=%d fSweepRunning=%d\n", __func__, blockHeight, fSweepRunning ); + if (fSweepRunning) { return; } @@ -565,6 +569,7 @@ void CWallet::RunSaplingConsolidation(int blockHeight) { fprintf(stderr,"%s: height=%d interval=%d\n", __func__, blockHeight, consolidateInterval); if (blockHeight % consolidateInterval == 0) { + LogPrintf("%s: creating consolidation operation at blockHeight=%d\n", __func__, blockHeight); std::shared_ptr q = getAsyncRPCQueue(); std::shared_ptr lastOperation = q->getOperationForId(saplingConsolidationOperationId); if (lastOperation != nullptr) { From 2a1cad1e5fbdf8081869a53b4399ef38c057ff87 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sun, 11 Sep 2022 20:10:28 -0400 Subject: [PATCH 30/45] Fix bug in z_sweepstatus --- 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 745dc3ce4..ced9b19b2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3381,7 +3381,7 @@ UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) if (pwalletMain->fConsolidationRunning) { ret.push_back(Pair("next_zsweep", pwalletMain->sweepInterval + chainActive.Tip()->GetHeight())); } else { - if (pwalletMain->nextConsolidation == 0) { + if (pwalletMain->nextSweep == 0) { ret.push_back(Pair("next_zsweep", chainActive.Tip()->GetHeight() + 1)); } else { ret.push_back(Pair("next_zsweep", pwalletMain->nextSweep)); From 9347b1fa12215ce7291222656895d1bdb0bceb5a Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sun, 11 Sep 2022 20:18:01 -0400 Subject: [PATCH 31/45] Sweep Interval should be 10 to match other defaults --- src/wallet/wallet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index fdaee018b..147ed6dbc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -810,7 +810,7 @@ public: int nextSweep = 0; int amountSwept = 0; int amountConsolidated = 0; - int sweepInterval = 20; + int sweepInterval = 10; int sweepFee = 10000; int sweepMaxInputs = 200; std::string sweepAddress = ""; From 5b15d790f9cd09779b7c842ce4eb01cadeafa4a1 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sun, 11 Sep 2022 21:56:21 -0400 Subject: [PATCH 32/45] Parse sweep+consolidation intervals on startup, add consolidationinterval config option, change consolidation interval default to 25, only validate consolidatesaplingaddress if consolidation is enabled --- src/init.cpp | 38 ++++++++++++++++++------ src/wallet/asyncrpcoperation_sweep.cpp | 7 +---- src/wallet/rpcwallet.cpp | 25 ++-------------- src/wallet/wallet.cpp | 40 +++++++++++++------------- src/wallet/wallet.h | 1 + 5 files changed, 54 insertions(+), 57 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 31073cf16..f71423e2a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2052,16 +2052,28 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) //Set Sapling Consolidation pwalletMain->fSaplingConsolidationEnabled = GetBoolArg("-consolidation", false); - fConsolidationTxFee = GetArg("-consolidationtxfee", DEFAULT_CONSOLIDATION_FEE); - fConsolidationMapUsed = !mapMultiArgs["-consolidatesaplingaddress"].empty(); + if(pwalletMain->fSaplingConsolidationEnabled) { + fConsolidationTxFee = GetArg("-consolidationtxfee", DEFAULT_CONSOLIDATION_FEE); + fConsolidationMapUsed = !mapMultiArgs["-consolidatesaplingaddress"].empty(); - //Validate Sapling Addresses - vector& 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"); + int consolidationInterval = GetArg("-consolidationinterval", 25); + if (consolidationInterval < 5) { + fprintf(stderr,"%s: Invalid consolidation interval of %d < 5, setting to default of 25\n", __func__, consolidationInterval); + consolidationInterval = 25; + } + + pwalletMain->consolidationInterval = consolidationInterval; + pwalletMain->nextConsolidation = pwalletMain->consolidationInterval + chainActive.Tip()->GetHeight(); + LogPrintf("%s: set nextConsolidation=%d\n", __func__, pwalletMain->nextConsolidation ); + + //Validate Sapling Addresses + vector& 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"); + } } } @@ -2069,6 +2081,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) pwalletMain->fSweepEnabled = GetBoolArg("-zsweep", false); if (pwalletMain->fSweepEnabled) { + int sweepInterval = GetArg("-zsweepinterval", 10); + if (sweepInterval < 5) { + fprintf(stderr,"%s: Invalid sweep interval of %d, setting to default of 10\n", __func__, sweepInterval); + sweepInterval = 10; + } + pwalletMain->sweepInterval = sweepInterval; + pwalletMain->nextSweep = pwalletMain->sweepInterval + chainActive.Tip()->GetHeight(); + LogPrintf("%s: set nextSweep=%d with sweepInterval=%d\n", __func__, pwalletMain->nextSweep, pwalletMain->sweepInterval ); fSweepTxFee = GetArg("-zsweepfee", DEFAULT_SWEEP_FEE); fSweepMapUsed = !mapMultiArgs["-zsweepaddress"].empty(); diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index 6045cf46f..1e7d324af 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -284,12 +284,7 @@ bool AsyncRPCOperation_sweep::main_impl() { } if (sweepComplete) { - int sweepInterval = GetArg("-zsweepinterval", 10); - if (sweepInterval < 5) { - fprintf(stderr,"%s: Invalid sweep interval of %d, setting to default of 10\n", __func__, sweepInterval); - sweepInterval = 10; - } - pwalletMain->nextSweep = sweepInterval + chainActive.Tip()->GetHeight(); + pwalletMain->nextSweep = pwalletMain->sweepInterval + chainActive.Tip()->GetHeight(); pwalletMain->fSweepRunning = false; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ced9b19b2..eaa9e436a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3337,18 +3337,8 @@ UniValue z_consolidationstatus(const UniValue& params, bool fHelp, const CPubKey ret.push_back(Pair("consolidation", pwalletMain->fSaplingConsolidationEnabled)); ret.push_back(Pair("running", pwalletMain->fConsolidationRunning)); ret.push_back(Pair("amount_consolidated", pwalletMain->amountConsolidated)); - - if (pwalletMain->fConsolidationRunning) { - // TODO: set a static consolidationInterval instead of it being non-deterministic - // ret.push_back(Pair("next_consolidation", pwalletMain->consolidationInterval + chainActive.Tip()->GetHeight())); - } else { - if (pwalletMain->nextConsolidation == 0) { - ret.push_back(Pair("next_consolidation", chainActive.Tip()->GetHeight() + 1)); - } else { - ret.push_back(Pair("next_consolidation", pwalletMain->nextConsolidation)); - } - } - // ret.push_back(Pair("consolidationinterval", pwalletMain->consolidationInterval)); + ret.push_back(Pair("next_consolidation", pwalletMain->nextConsolidation)); + ret.push_back(Pair("consolidationinterval", pwalletMain->consolidationInterval)); ret.push_back(Pair("consolidationaddress", pwalletMain->consolidationAddress)); ret.push_back(Pair("consolidationtxfee",(int)fConsolidationTxFee)); @@ -3377,16 +3367,7 @@ UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) ret.push_back(Pair("zsweep", pwalletMain->fSweepEnabled)); ret.push_back(Pair("running", pwalletMain->fSweepRunning)); ret.push_back(Pair("amount_swept", pwalletMain->amountSwept)); - - if (pwalletMain->fConsolidationRunning) { - ret.push_back(Pair("next_zsweep", pwalletMain->sweepInterval + chainActive.Tip()->GetHeight())); - } else { - if (pwalletMain->nextSweep == 0) { - ret.push_back(Pair("next_zsweep", chainActive.Tip()->GetHeight() + 1)); - } else { - ret.push_back(Pair("next_zsweep", pwalletMain->nextSweep)); - } - } + ret.push_back(Pair("next_zsweep", pwalletMain->nextSweep)); ret.push_back(Pair("zsweepinterval", pwalletMain->sweepInterval)); ret.push_back(Pair("zsweepaddress", pwalletMain->sweepAddress)); ret.push_back(Pair("zsweepmaxinputs", pwalletMain->sweepMaxInputs)); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e8434afcf..85faac38b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -515,10 +515,11 @@ void CWallet::RunSaplingSweep(int blockHeight) { return; } - LogPrintf("%s: Sweep enabled at blockHeight=%d nextSweep=%d\n", __func__, blockHeight, nextSweep); if (nextSweep > blockHeight) { + LogPrintf("%s: Not time to sweep yet at blockHeight=%d nextSweep=%d\n", __func__, blockHeight, nextSweep); return; } + LogPrintf("%s: Sweep enabled at blockHeight=%d nextSweep=%d\n", __func__, blockHeight, nextSweep); //Don't Run if consolidation will run soon. if (fSaplingConsolidationEnabled && nextConsolidation - 5 <= blockHeight) { @@ -528,7 +529,7 @@ void CWallet::RunSaplingSweep(int blockHeight) { //Don't Run While consolidation is running. if (fConsolidationRunning) { - LogPrintf("%s: not sweeping since consolidation is currently running\n", __func__); + LogPrintf("%s: not sweeping since consolidation is currently running at height=%d\n", __func__, blockHeight); return; } @@ -557,29 +558,28 @@ void CWallet::RunSaplingConsolidation(int blockHeight) { return; } - LogPrintf("%s: consolidation enabled at blockHeight=%d fSweepRunning=%d\n", __func__, blockHeight, fSweepRunning ); - - if (fSweepRunning) { + if (nextConsolidation > blockHeight) { + LogPrintf("%s: Not time to consolidate yet at blockHeight=%d nextConsolidation=%d\n", __func__, blockHeight, nextConsolidation); return; } - int consolidateInterval = 45; + LogPrintf("%s: consolidation enabled at blockHeight=%d fSweepRunning=%d\n", __func__, blockHeight, fSweepRunning ); - if(fZdebug) - fprintf(stderr,"%s: height=%d interval=%d\n", __func__, blockHeight, consolidateInterval); - - if (blockHeight % consolidateInterval == 0) { - LogPrintf("%s: creating consolidation operation at blockHeight=%d\n", __func__, blockHeight); - std::shared_ptr q = getAsyncRPCQueue(); - std::shared_ptr lastOperation = q->getOperationForId(saplingConsolidationOperationId); - if (lastOperation != nullptr) { - lastOperation->cancel(); - } - pendingSaplingConsolidationTxs.clear(); - std::shared_ptr operation(new AsyncRPCOperation_saplingconsolidation(blockHeight + 5)); - saplingConsolidationOperationId = operation->getId(); - q->addOperation(operation); + if (fSweepRunning) { + LogPrintf("%s: not consolidating since sweep is currently running at height=%d\n", __func__, blockHeight); + return; } + + LogPrintf("%s: creating consolidation operation at blockHeight=%d\n", __func__, blockHeight); + std::shared_ptr q = getAsyncRPCQueue(); + std::shared_ptr lastOperation = q->getOperationForId(saplingConsolidationOperationId); + if (lastOperation != nullptr) { + lastOperation->cancel(); + } + pendingSaplingConsolidationTxs.clear(); + std::shared_ptr operation(new AsyncRPCOperation_saplingconsolidation(blockHeight + 5)); + saplingConsolidationOperationId = operation->getId(); + q->addOperation(operation); } bool CWallet::CommitAutomatedTx(const CTransaction& tx) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 147ed6dbc..f5e07e9da 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -811,6 +811,7 @@ public: int amountSwept = 0; int amountConsolidated = 0; int sweepInterval = 10; + int consolidationInterval = 25; int sweepFee = 10000; int sweepMaxInputs = 200; std::string sweepAddress = ""; From bd9006623c2ae9e1489111d64c87f5a3e7d29a94 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sun, 11 Sep 2022 22:41:03 -0400 Subject: [PATCH 33/45] Avoid coredumps in ReacceptWalletTransactions and log the situation, which is possibly caused by corrupt wallet tx data --- src/wallet/wallet.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 85faac38b..e9450161f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2847,7 +2847,12 @@ void CWallet::ReacceptWalletTransactions() { const uint256& wtxid = item.first; CWalletTx& wtx = item.second; - assert(wtx.GetHash() == wtxid); + if(wtx.GetHash() != wtxid) { + LogPrintf("%s: Something funky going on, skipping this tx. wtx.GetHash() != wtxid (%s != %s)\n", __func__, wtx.GetHash().ToString().c_str(), wtxid.ToString().c_str() ); + continue; + } + // Crashing the node because of this is lame + // assert(wtx.GetHash() == wtxid); int nDepth = wtx.GetDepthInMainChain(); From 78f5021cc2db4079f0ada8eb97250e81ac438919 Mon Sep 17 00:00:00 2001 From: jahway603 Date: Mon, 12 Sep 2022 22:38:53 -0400 Subject: [PATCH 34/45] starting to support zsweepexclude functionality --- src/init.cpp | 16 ++++++++++++++++ src/wallet/wallet.h | 1 + 2 files changed, 17 insertions(+) diff --git a/src/init.cpp b/src/init.cpp index 98f773b0f..3b0bc6bcd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -471,6 +471,7 @@ std::string HelpMessage(HelpMessageMode mode) // By default we only allow sweeping to the current wallet which must have the spending key of the sweep zaddr // This hopefully will make it harder for people to accidentally sweep funds to a wrong zaddr and lose funds strUsage += HelpMessageOpt("-zsweepexternal", _("Enable sweeping to an external wallet (default false)")); + strUsage += HelpMessageOpt("-zsweepexclude", _("Addresses to exclude from sweeping (default none)")); strUsage += HelpMessageOpt("-deletetx", _("Enable Old Transaction Deletion")); strUsage += HelpMessageOpt("-deleteinterval", strprintf(_("Delete transaction every blocks during inital block download (default: %i)"), DEFAULT_TX_DELETE_INTERVAL)); @@ -2094,6 +2095,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) //Validate Sapling Addresses vector& vSweep = mapMultiArgs["-zsweepaddress"]; + vector& vSweepExclude = mapMultiArgs["-zsweepexclude"]; if (vSweep.size() != 1) { return InitError("A single zsweep address must be specified."); } @@ -2117,6 +2119,20 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } } + for (int i = 0; i < vSweepExclude.size(); i++) { + // LogPrintf("Sweep Excluded Address: %s\n", vSweepExclude[i]); + auto zSweepExcluded = DecodePaymentAddress(vSweepExclude[i]); + if (!IsValidPaymentAddress(zSweepExcluded)) { + return InitError("Invalid zsweep address"); + } + auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zSweepExcluded); + pwalletMain->sweepExcludeAddress = vSweepExclude[i]; + + if (!hasSpendingKey) { + return InitError("Wallet must have the spending key of zsweepexclude address"); + } + } + if (pwalletMain->fSaplingConsolidationEnabled) { //Validate 1 Consolidation address only that matches the sweep address vector& vaddresses = mapMultiArgs["-consolidatesaplingaddress"]; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f5e07e9da..2dfb9e6b1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -815,6 +815,7 @@ public: int sweepFee = 10000; int sweepMaxInputs = 200; std::string sweepAddress = ""; + std::string sweepExcludeAddress = ""; std::string consolidationAddress = ""; void ClearNoteWitnessCache(); From ca3bb90e0c2a5ccd852c75c4511557280aef680a Mon Sep 17 00:00:00 2001 From: jahway603 Date: Mon, 12 Sep 2022 23:30:59 -0400 Subject: [PATCH 35/45] skip excluded address from zsweep --- src/wallet/asyncrpcoperation_sweep.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index 1e7d324af..82dc1a40c 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -88,12 +88,18 @@ bool AsyncRPCOperation_sweep::main_impl() { std::vector saplingEntries; libzcash::SaplingPaymentAddress sweepAddress; + libzcash::SaplingPaymentAddress sweepExcludeAddress; std::map> mapAddresses; { LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->GetFilteredNotes(saplingEntries, "", 11); + if (!fromRPC_) { + auto zAddressExclude = DecodePaymentAddress(pwalletMain->sweepExcludeAddress); + if (boost::get(&zAddressExclude) != nullptr) { + sweepExcludeAddress = boost::get(zAddressExclude); + } if (fSweepMapUsed) { const vector& v = mapMultiArgs["-zsweepaddress"]; for(int i = 0; i < v.size(); i++) { @@ -115,8 +121,11 @@ bool AsyncRPCOperation_sweep::main_impl() { } } + // Map all notes by address for (auto & entry : saplingEntries) { - //Map all notes by address + // do not need to sweep Excluded Address + if (sweepExcludeAddress == entry.address) { continue; } + // do not need to sweep the sweepAddress as that is the destination if (sweepAddress == entry.address) { continue; } else { From 0c22e263585e6b73414776db5135871ec7a7dc38 Mon Sep 17 00:00:00 2001 From: jahway603 Date: Mon, 12 Sep 2022 23:47:49 -0400 Subject: [PATCH 36/45] updated z_sweepstatus --- src/wallet/rpcwallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 2f64df677..cc569baf4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3369,6 +3369,7 @@ UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) ret.push_back(Pair("next_zsweep", pwalletMain->nextSweep)); ret.push_back(Pair("zsweepinterval", pwalletMain->sweepInterval)); ret.push_back(Pair("zsweepaddress", pwalletMain->sweepAddress)); + ret.push_back(Pair("zsweepexcludeaddress", pwalletMain->sweepExcludeAddress)); ret.push_back(Pair("zsweepmaxinputs", pwalletMain->sweepMaxInputs)); ret.push_back(Pair("zsweepfee", pwalletMain->sweepFee)); ret.push_back(Pair("zsweepexternal", pwalletMain->fSweepExternalEnabled)); From 88d3f116647356d367478e76b36ba850c4409170 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 13 Sep 2022 00:18:44 -0400 Subject: [PATCH 37/45] Save excluded address to be used later --- src/init.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/init.cpp b/src/init.cpp index 3b0bc6bcd..025d448e9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2121,6 +2121,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) for (int i = 0; i < vSweepExclude.size(); i++) { // LogPrintf("Sweep Excluded Address: %s\n", vSweepExclude[i]); + pwalletMain->sweepExcludeAddress = vSweepExclude[i]; auto zSweepExcluded = DecodePaymentAddress(vSweepExclude[i]); if (!IsValidPaymentAddress(zSweepExcluded)) { return InitError("Invalid zsweep address"); From b687d5f76ef8a0641431b6c80d49e92d72305dc4 Mon Sep 17 00:00:00 2001 From: jahway603 Date: Tue, 13 Sep 2022 00:22:13 -0400 Subject: [PATCH 38/45] debug --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 025d448e9..727a4e946 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2120,7 +2120,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } for (int i = 0; i < vSweepExclude.size(); i++) { - // LogPrintf("Sweep Excluded Address: %s\n", vSweepExclude[i]); + LogPrintf("Sweep Excluded Address: %s\n", vSweepExclude[i]); pwalletMain->sweepExcludeAddress = vSweepExclude[i]; auto zSweepExcluded = DecodePaymentAddress(vSweepExclude[i]); if (!IsValidPaymentAddress(zSweepExcluded)) { From 908240d07fd433d0f1dd375b38fa53347b96d890 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 13 Sep 2022 01:00:56 -0400 Subject: [PATCH 39/45] Make z_sweepstatus match up with config option name --- 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 cc569baf4..59be0f5bd 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3369,7 +3369,7 @@ UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) ret.push_back(Pair("next_zsweep", pwalletMain->nextSweep)); ret.push_back(Pair("zsweepinterval", pwalletMain->sweepInterval)); ret.push_back(Pair("zsweepaddress", pwalletMain->sweepAddress)); - ret.push_back(Pair("zsweepexcludeaddress", pwalletMain->sweepExcludeAddress)); + ret.push_back(Pair("zsweepexclude", pwalletMain->sweepExcludeAddress)); ret.push_back(Pair("zsweepmaxinputs", pwalletMain->sweepMaxInputs)); ret.push_back(Pair("zsweepfee", pwalletMain->sweepFee)); ret.push_back(Pair("zsweepexternal", pwalletMain->fSweepExternalEnabled)); From f90715192efb5377deb49ef04899cb56ce0b4c10 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Thu, 15 Sep 2022 09:38:48 -0400 Subject: [PATCH 40/45] Do not delete expired txs for now, it doesn't work correctly, and log more info about them --- src/wallet/wallet.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e9450161f..3a84f267e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3136,9 +3136,10 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) // Do not relay expired transactions, to avoid other nodes banning us // Current code will not ban nodes relaying expired txs but older nodes will if (wtx.nExpiryHeight > 0 && wtx.nExpiryHeight < chainActive.LastTip()->GetHeight()) { - fprintf(stderr,"%s: ignoring expired tx %s\n", __func__, wtx.GetHash().ToString().c_str() ); + fprintf(stderr,"%s: ignoring expired tx %s with expiry %d at height %d\n", __func__, wtx.GetHash().ToString().c_str(), wtx.nExpiryHeight, chainActive.LastTip()->GetHeight() ); + // TODO: expired detection doesn't seem to work right // append to list of txs to delete - vwtxh.push_back(wtx.GetHash()); + // vwtxh.push_back(wtx.GetHash()); continue; } From 77d7aaba969a2b642d2f606535626bd356d33e51 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 16 Sep 2022 05:47:35 -0700 Subject: [PATCH 41/45] Return an array of addresses to exclude from zsweep in zsweep_status --- src/wallet/rpcwallet.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 59be0f5bd..246615a5a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3369,7 +3369,12 @@ UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) ret.push_back(Pair("next_zsweep", pwalletMain->nextSweep)); ret.push_back(Pair("zsweepinterval", pwalletMain->sweepInterval)); ret.push_back(Pair("zsweepaddress", pwalletMain->sweepAddress)); - ret.push_back(Pair("zsweepexclude", pwalletMain->sweepExcludeAddress)); + UniValue excludes(UniValue::VARR); + // BOOST_FOREACH(const std::string& exclude, pwalletMain->sweepExcludeAddresses ) { + // excludes.push_back(exclude); + // } + excludes.push_back( pwalletMain->sweepExcludeAddress ); + ret.push_back(Pair("zsweepexclude", excludes)); ret.push_back(Pair("zsweepmaxinputs", pwalletMain->sweepMaxInputs)); ret.push_back(Pair("zsweepfee", pwalletMain->sweepFee)); ret.push_back(Pair("zsweepexternal", pwalletMain->fSweepExternalEnabled)); From f796d5d14eb57b21d6bb30559dd4329facf9aa14 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Fri, 16 Sep 2022 10:08:53 -0700 Subject: [PATCH 42/45] Support multiple zsweepexclude zaddrs --- src/init.cpp | 4 +-- src/wallet/asyncrpcoperation_sweep.cpp | 37 ++++++++++++++++++++------ src/wallet/wallet.h | 2 +- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 727a4e946..cec462cf5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2121,17 +2121,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) for (int i = 0; i < vSweepExclude.size(); i++) { LogPrintf("Sweep Excluded Address: %s\n", vSweepExclude[i]); - pwalletMain->sweepExcludeAddress = vSweepExclude[i]; auto zSweepExcluded = DecodePaymentAddress(vSweepExclude[i]); if (!IsValidPaymentAddress(zSweepExcluded)) { return InitError("Invalid zsweep address"); } auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zSweepExcluded); - pwalletMain->sweepExcludeAddress = vSweepExclude[i]; if (!hasSpendingKey) { return InitError("Wallet must have the spending key of zsweepexclude address"); } + // Add this validated zaddr to the list of excluded sweep zaddrs + pwalletMain->sweepExcludeAddresses.push_back( vSweepExclude[i] ); } if (pwalletMain->fSaplingConsolidationEnabled) { diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index 82dc1a40c..46962564d 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -74,6 +74,26 @@ void AsyncRPCOperation_sweep::main() { LogPrintf("%s", s); } +// Is this zaddr excluded from zsweep ? +bool IsExcludedAddress(libzcash::SaplingPaymentAddress zaddr) { + for( auto & sweepExcludeAddress : pwalletMain->sweepExcludeAddresses ) { + auto zAddressExclude = DecodePaymentAddress(sweepExcludeAddress); + + if (boost::get(&zAddressExclude) != nullptr) { + sweepExcludeAddress = boost::get(zAddressExclude); + } else { + // This is an invalid sapling zaddr + LogPrintf("%s: Invalid zsweepexclude zaddr %s, ignoring\n", opid, sweepExcludeAddress); + } + + if (sweepExcludeAddress == entry.address) { + return true; + } + } + + return false; +} + bool AsyncRPCOperation_sweep::main_impl() { bool status=true; auto opid=getId(); @@ -88,7 +108,6 @@ bool AsyncRPCOperation_sweep::main_impl() { std::vector saplingEntries; libzcash::SaplingPaymentAddress sweepAddress; - libzcash::SaplingPaymentAddress sweepExcludeAddress; std::map> mapAddresses; { @@ -96,16 +115,15 @@ bool AsyncRPCOperation_sweep::main_impl() { pwalletMain->GetFilteredNotes(saplingEntries, "", 11); if (!fromRPC_) { - auto zAddressExclude = DecodePaymentAddress(pwalletMain->sweepExcludeAddress); - if (boost::get(&zAddressExclude) != nullptr) { - sweepExcludeAddress = boost::get(zAddressExclude); - } if (fSweepMapUsed) { const vector& v = mapMultiArgs["-zsweepaddress"]; for(int i = 0; i < v.size(); i++) { auto zAddress = DecodePaymentAddress(v[i]); if (boost::get(&zAddress) != nullptr) { sweepAddress = boost::get(zAddress); + } else { + LogPrintf("%s: Invalid zsweepaddress configured, exiting\n", opid); + return false; } } } else { @@ -121,10 +139,13 @@ bool AsyncRPCOperation_sweep::main_impl() { } } - // Map all notes by address + // Map all notes (zutxos) by address for (auto & entry : saplingEntries) { - // do not need to sweep Excluded Address - if (sweepExcludeAddress == entry.address) { continue; } + // do not need to sweep Excluded Addresses + if(IsExcludedAddress(entry.address) { + continue; + } + // do not need to sweep the sweepAddress as that is the destination if (sweepAddress == entry.address) { continue; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 2dfb9e6b1..3b932c4f5 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -815,7 +815,7 @@ public: int sweepFee = 10000; int sweepMaxInputs = 200; std::string sweepAddress = ""; - std::string sweepExcludeAddress = ""; + std::vector sweepExcludeAddresses = ""; std::string consolidationAddress = ""; void ClearNoteWitnessCache(); From 12fa3013cedfc9ee9a14a7593221934433f4b0f9 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 16 Sep 2022 13:11:32 -0400 Subject: [PATCH 43/45] Fix definition of sweepExcludeAddresses --- src/wallet/wallet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3b932c4f5..ec2937a1d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -815,7 +815,7 @@ public: int sweepFee = 10000; int sweepMaxInputs = 200; std::string sweepAddress = ""; - std::vector sweepExcludeAddresses = ""; + std::vector sweepExcludeAddresses; std::string consolidationAddress = ""; void ClearNoteWitnessCache(); From 35f376c8b5bcbd3d3550cbbc78b40a3d78648e75 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 16 Sep 2022 14:28:51 -0400 Subject: [PATCH 44/45] Fix bugs in excluding multiple zsweep zaddrs and report all excluded zsweep zaddrs in z_sweepstatus --- src/wallet/asyncrpcoperation_sweep.cpp | 13 +++++++------ src/wallet/rpcwallet.cpp | 7 +++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/wallet/asyncrpcoperation_sweep.cpp b/src/wallet/asyncrpcoperation_sweep.cpp index 46962564d..237af4cd3 100644 --- a/src/wallet/asyncrpcoperation_sweep.cpp +++ b/src/wallet/asyncrpcoperation_sweep.cpp @@ -80,15 +80,16 @@ bool IsExcludedAddress(libzcash::SaplingPaymentAddress zaddr) { auto zAddressExclude = DecodePaymentAddress(sweepExcludeAddress); if (boost::get(&zAddressExclude) != nullptr) { - sweepExcludeAddress = boost::get(zAddressExclude); + auto excludeAddress = boost::get(zAddressExclude); + if (excludeAddress == zaddr) { + return true; + } } else { // This is an invalid sapling zaddr - LogPrintf("%s: Invalid zsweepexclude zaddr %s, ignoring\n", opid, sweepExcludeAddress); + LogPrintf("%s: Invalid zsweepexclude zaddr %s, ignoring\n", sweepExcludeAddress); + continue; } - if (sweepExcludeAddress == entry.address) { - return true; - } } return false; @@ -142,7 +143,7 @@ bool AsyncRPCOperation_sweep::main_impl() { // Map all notes (zutxos) by address for (auto & entry : saplingEntries) { // do not need to sweep Excluded Addresses - if(IsExcludedAddress(entry.address) { + if(IsExcludedAddress(entry.address)) { continue; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 246615a5a..a09f33e79 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3370,10 +3370,9 @@ UniValue z_sweepstatus(const UniValue& params, bool fHelp, const CPubKey& mypk) ret.push_back(Pair("zsweepinterval", pwalletMain->sweepInterval)); ret.push_back(Pair("zsweepaddress", pwalletMain->sweepAddress)); UniValue excludes(UniValue::VARR); - // BOOST_FOREACH(const std::string& exclude, pwalletMain->sweepExcludeAddresses ) { - // excludes.push_back(exclude); - // } - excludes.push_back( pwalletMain->sweepExcludeAddress ); + BOOST_FOREACH(const std::string& exclude, pwalletMain->sweepExcludeAddresses ) { + excludes.push_back(exclude); + } ret.push_back(Pair("zsweepexclude", excludes)); ret.push_back(Pair("zsweepmaxinputs", pwalletMain->sweepMaxInputs)); ret.push_back(Pair("zsweepfee", pwalletMain->sweepFee)); From 62c9bce6f7d29ce4ed84547777b79011d1176351 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 16 Sep 2022 14:33:37 -0400 Subject: [PATCH 45/45] Correct error messsage about invalid zsweepexclude address --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index cec462cf5..63a8d9c8e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2123,7 +2123,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Sweep Excluded Address: %s\n", vSweepExclude[i]); auto zSweepExcluded = DecodePaymentAddress(vSweepExclude[i]); if (!IsValidPaymentAddress(zSweepExcluded)) { - return InitError("Invalid zsweep address"); + return InitError("Invalid zsweepexclude address"); } auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zSweepExcluded);