From 02ef7149011d9b221f58585dd00747661b9cdf09 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Thu, 25 Aug 2022 09:26:10 -0400 Subject: [PATCH] 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); + +};