Files
dragonx/src/asyncrpcoperation.cpp
T
DanSandClaude Opus 4.8 81f803948c hygiene: Phase 6 (partial) — fix 7 verified bugs + quick-wins + dedup
From the Phase-6 structural scoping: land the verified behavioral bugs and
the safe quick-wins/dedups now; the large refactors (monolith splits, ~180
header globals) and consensus-adjacent items stay deferred. Built clean;
self-mined; verifychain=true.

BUGS (verified by reading the code):
- net.cpp CNode::Ban: a braceless `if (subNet.Match(...))` left
  `pnode->fDisconnect = true;` OUTSIDE the guard, so banning any one subnet
  marked EVERY connected peer for disconnect (dropped the whole peer set).
  Wrapped the two statements in braces. (LIVE, high severity.)
- rpcdump.cpp importwallet: the `!fGood -> throw "Error adding some keys"`
  check was trapped inside the `if (fRescan)` block, so importwallet with
  rescan=false silently reported success when key import failed. Hoisted the
  check before the rescan branch and cleaned the garbled braces/indentation.
- wallet.cpp CommitTransaction: ignored AddToWallet()'s return, so a failed
  disk-persist of a just-signed spend was swallowed while the tx broadcast.
  Now logs a hard error on failure.
- hush_nSPV_fullnode.h: the UTXOS branch declared `uint8_t filter` while the
  twin TXIDS branch uses `uint32_t filter`; dragon_rwnum switches on
  sizeof(filter), so the utxos path parsed only 1 of 4 wire filter bytes.
  Widened to uint32_t.
- asyncrpcoperation_sweep.cpp: LogPrintf("%s ... %s", one-arg) read a missing
  vararg; added the __func__ argument.
- rpcdump.cpp importprivkey: inner `auto secret_key` shadowed the outer
  uint8_t and changed the type into DecodeCustomSecret; dropped the shadow.
- rpcdump.cpp getrescaninfo: char[8] + sprintf("%.4f") overflows when the
  ratio >= 10.0 (transient reorg); widened to char[16] + snprintf.

QUICK WINS: removed the duplicate DRAGON_MAXSCRIPTSIZE #define; pinned the
dead HUSH3-branch NOTARISATION_SCAN_LIMIT_BLOCKS to 1440; fixed init typos
(fRequestShutdown, RPC warmup).

DEDUP: extracted the 19-line try/catch error-mapping block — copy-pasted
identically into all six async operations — into
AsyncRPCOperation::set_error_from_current_exception(), so the mapping is
edited in one place. Behavior-identical (verified all six blocks were byte-
identical first).

Deferred (endorsed by the scoping, better as their own PRs): addrman Select_
dedup, the pow.cpp powLimit helper (consensus file), the miner CreateNewBlock
lock-asymmetry, the wallet monolith splits, and the ~180-global / consensus-
retarget / Komodo-heritage work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-27 15:55:00 -05:00

223 lines
7.3 KiB
C++

// Copyright (c) 2016 The Zcash developers
// Copyright (c) 2016-2024 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. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
#include "asyncrpcoperation.h"
#include <stdexcept>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <string>
#include <ctime>
#include <chrono>
using namespace std;
static boost::uuids::random_generator uuidgen;
static std::map<OperationStatus, std::string> OperationStatusMap = {
{OperationStatus::READY, "queued"},
{OperationStatus::EXECUTING, "executing"},
{OperationStatus::CANCELLED, "cancelled"},
{OperationStatus::FAILED, "failed"},
{OperationStatus::SUCCESS, "success"}
};
/**
* Every operation instance should have a globally unique id
*/
AsyncRPCOperation::AsyncRPCOperation() : error_code_(0), error_message_() {
// Set a unique reference for each operation
boost::uuids::uuid uuid = uuidgen();
id_ = "opid-" + boost::uuids::to_string(uuid);
creation_time_ = (int64_t)time(NULL);
set_state(OperationStatus::READY);
}
AsyncRPCOperation::AsyncRPCOperation(const AsyncRPCOperation& o) :
id_(o.id_), creation_time_(o.creation_time_), state_(o.state_.load()),
start_time_(o.start_time_), end_time_(o.end_time_),
error_code_(o.error_code_), error_message_(o.error_message_),
result_(o.result_)
{
}
// Shared error mapping for every async op's main(): rethrow the in-flight
// exception and translate it to this operation's error code/message. Keeping
// it here means the mapping is edited in one place, not copy-pasted into six.
void AsyncRPCOperation::set_error_from_current_exception()
{
try {
throw;
} catch (const UniValue& objError) {
set_error_code(find_value(objError, "code").get_int());
set_error_message(find_value(objError, "message").get_str());
} 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");
}
}
AsyncRPCOperation& AsyncRPCOperation::operator=( const AsyncRPCOperation& other ) {
this->id_ = other.id_;
this->creation_time_ = other.creation_time_;
this->state_.store(other.state_.load());
this->start_time_ = other.start_time_;
this->end_time_ = other.end_time_;
this->error_code_ = other.error_code_;
this->error_message_ = other.error_message_;
this->result_ = other.result_;
return *this;
}
AsyncRPCOperation::~AsyncRPCOperation() {
}
/**
* Override this cancel() method if you can interrupt main() when executing.
*/
void AsyncRPCOperation::cancel() {
if (isReady()) {
set_state(OperationStatus::CANCELLED);
}
}
/**
* Start timing the execution run of the code you're interested in
*/
void AsyncRPCOperation::start_execution_clock() {
std::lock_guard<std::mutex> guard(lock_);
start_time_ = std::chrono::system_clock::now();
}
/**
* Stop timing the execution run
*/
void AsyncRPCOperation::stop_execution_clock() {
std::lock_guard<std::mutex> guard(lock_);
end_time_ = std::chrono::system_clock::now();
}
/**
* Implement this virtual method in any subclass. This is just an example implementation.
*/
void AsyncRPCOperation::main() {
if (isCancelled()) {
return;
}
set_state(OperationStatus::EXECUTING);
start_execution_clock();
// Do some work here..
stop_execution_clock();
// If there was an error, you might set it like this:
/*
setErrorCode(123);
setErrorMessage("Murphy's law");
setState(OperationStatus::FAILED);
*/
// Otherwise, if the operation was a success:
UniValue v(UniValue::VSTR, "We have a result!");
set_result(v);
set_state(OperationStatus::SUCCESS);
}
/**
* Return the error of the completed operation as a UniValue object.
* If there is no error, return null UniValue.
*/
UniValue AsyncRPCOperation::getError() const {
if (!isFailed()) {
return NullUniValue;
}
std::lock_guard<std::mutex> guard(lock_);
UniValue error(UniValue::VOBJ);
error.push_back(Pair("code", this->error_code_));
error.push_back(Pair("message", this->error_message_));
return error;
}
/**
* Return the result of the completed operation as a UniValue object.
* If the operation did not succeed, return null UniValue.
*/
UniValue AsyncRPCOperation::getResult() const {
if (!isSuccess()) {
return NullUniValue;
}
std::lock_guard<std::mutex> guard(lock_);
return this->result_;
}
/**
* Returns a status UniValue object.
* If the operation has failed, it will include an error object.
* If the operation has succeeded, it will include the result value.
* If the operation was cancelled, there will be no error object or result value.
*/
UniValue AsyncRPCOperation::getStatus() const {
OperationStatus status = this->getState();
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("id", this->id_));
obj.push_back(Pair("status", OperationStatusMap[status]));
obj.push_back(Pair("creation_time", this->creation_time_));
// TODO: Issue #1354: There may be other useful metadata to return to the user.
UniValue err = this->getError();
if (!err.isNull()) {
obj.push_back(Pair("error", err.get_obj()));
}
UniValue result = this->getResult();
if (!result.isNull()) {
obj.push_back(Pair("result", result));
// Include execution time for successful operation
std::chrono::duration<double> elapsed_seconds = end_time_ - start_time_;
obj.push_back(Pair("execution_secs", elapsed_seconds.count()));
}
return obj;
}
/**
* Return the operation state in human readable form.
*/
std::string AsyncRPCOperation::getStateAsString() const {
OperationStatus status = this->getState();
return OperationStatusMap[status];
}