Commit forgotten files
This commit is contained in:
313
src/wallet/asyncrpcoperation_sweep.cpp
Normal file
313
src/wallet/asyncrpcoperation_sweep.cpp
Normal file
@@ -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<libzcash::SaplingPaymentAddress> 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<std::string>());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
libzcash::SaplingPaymentAddress sweepAddress;
|
||||
std::map<libzcash::SaplingPaymentAddress, std::vector<SaplingNoteEntry>> mapAddresses;
|
||||
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
pwalletMain->GetFilteredNotes(saplingEntries, "", 11);
|
||||
if (!fromRPC_) {
|
||||
if (fSweepMapUsed) {
|
||||
const vector<string>& v = mapMultiArgs["-zsweepaddress"];
|
||||
for(int i = 0; i < v.size(); i++) {
|
||||
auto zAddress = DecodePaymentAddress(v[i]);
|
||||
if (boost::get<libzcash::SaplingPaymentAddress>(&zAddress) != nullptr) {
|
||||
sweepAddress = boost::get<libzcash::SaplingPaymentAddress>(zAddress);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (boost::get<libzcash::SaplingPaymentAddress>(&rpcSweepAddress) != nullptr) {
|
||||
sweepAddress = boost::get<libzcash::SaplingPaymentAddress>(rpcSweepAddress);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & entry : saplingEntries) {
|
||||
//Map all notes by address
|
||||
if (sweepAddress == entry.address) {
|
||||
continue;
|
||||
} else {
|
||||
std::map<libzcash::SaplingPaymentAddress, std::vector<SaplingNoteEntry>>::iterator it;
|
||||
it = mapAddresses.find(entry.address);
|
||||
if (it != mapAddresses.end()) {
|
||||
it->second.push_back(entry);
|
||||
} else {
|
||||
std::vector<SaplingNoteEntry> entries;
|
||||
entries.push_back(entry);
|
||||
mapAddresses[entry.address] = entries;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int numTxCreated = 0;
|
||||
std::vector<std::string> sweepTxIds;
|
||||
CAmount amountSwept = 0;
|
||||
CCoinsViewCache coinsView(pcoinsTip);
|
||||
bool sweepComplete = true;
|
||||
|
||||
for (std::map<libzcash::SaplingPaymentAddress, std::vector<SaplingNoteEntry>>::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<SaplingNoteEntry> 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<libzcash::SaplingPaymentAddress>(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<libzcash::SaplingPaymentAddress>(saplingEntry.address), ivk);
|
||||
|
||||
//Select Notes from that same address we will be sending to.
|
||||
if (ivk == extsk.expsk.full_viewing_key().in_viewing_key() && 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<SaplingOutPoint> ops;
|
||||
std::vector<libzcash::SaplingNote> notes;
|
||||
for (auto fromNote : fromNotes) {
|
||||
ops.push_back(fromNote.op);
|
||||
notes.push_back(fromNote.note);
|
||||
}
|
||||
|
||||
// Fetch Sapling anchor and witnesses
|
||||
uint256 anchor;
|
||||
std::vector<boost::optional<SaplingWitness>> witnesses;
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
pwalletMain->GetSaplingNoteWitnesses(ops, witnesses, anchor);
|
||||
}
|
||||
|
||||
// Add Sapling spends
|
||||
for (size_t i = 0; i < notes.size(); i++) {
|
||||
if (!witnesses[i]) {
|
||||
LogPrint("zrpcunsafe", "%s: Missing Witnesses. Stopping.\n", getId());
|
||||
break;
|
||||
}
|
||||
builder.AddSaplingSpend(extsk.expsk, notes[i], anchor, witnesses[i].get());
|
||||
}
|
||||
|
||||
builder.SetFee(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<libzcash::SaplingPaymentAddress>(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<std::string>& 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;
|
||||
}
|
||||
42
src/wallet/asyncrpcoperation_sweep.h
Normal file
42
src/wallet/asyncrpcoperation_sweep.h
Normal file
@@ -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<libzcash::SaplingPaymentAddress> 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<std::string>& sweepTxIds);
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user