Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/bench/rpc_mempool.cpp b/src/bench/rpc_mempool.cpp
index aa33ef768..c6da10f82 100644
--- a/src/bench/rpc_mempool.cpp
+++ b/src/bench/rpc_mempool.cpp
@@ -1,51 +1,50 @@
// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <chainparamsbase.h>
#include <consensus/amount.h>
#include <kernel/cs_main.h>
#include <kernel/mempool_entry.h>
#include <rpc/mempool.h>
#include <test/util/setup_common.h>
#include <txmempool.h>
#include <util/chaintype.h>
#include <univalue.h>
static void AddTx(const CTransactionRef &tx, const Amount &fee,
CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs) {
LockPoints lp;
pool.addUnchecked(CTxMemPoolEntryRef::make(tx, fee, /*time=*/0,
/*height=*/1,
/*_sigChecks=*/1, lp));
}
static void RpcMempool(benchmark::Bench &bench) {
const TestingSetup test_setup{
ChainType::MAIN,
/* extra_args */
{
"-nodebuglogfile",
"-nodebug",
},
};
CTxMemPool &pool = *Assert(test_setup.m_node.mempool);
LOCK2(cs_main, pool.cs);
for (int i = 0; i < 1000; ++i) {
CMutableTransaction tx = CMutableTransaction();
tx.vin.resize(1);
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout.resize(1);
tx.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
tx.vout[0].nValue = i * COIN;
const CTransactionRef tx_r{MakeTransactionRef(tx)};
AddTx(tx_r, /* fee */ i * COIN, pool);
}
bench.run([&] { (void)MempoolToJSON(pool, /*verbose*/ true); });
}
BENCHMARK(RpcMempool);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index ae1ed49ac..d2802771b 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -1,902 +1,903 @@
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <chainparams.h>
+#include <chainparamsbase.h>
#include <clientversion.h>
#include <coins.h>
#include <common/args.h>
#include <common/system.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <core_io.h>
#include <currencyunit.h>
#include <key_io.h>
#include <primitives/transaction.h>
#include <rpc/util.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <util/exception.h>
#include <util/fs.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/translation.h>
#include <univalue.h>
#include <cstdio>
#include <functional>
#include <memory>
static bool fCreateBlank;
static std::map<std::string, UniValue> registers;
static const int CONTINUE_EXECUTION = -1;
const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr;
static void SetupBitcoinTxArgs(ArgsManager &argsman) {
SetupHelpOptions(argsman);
SetupCurrencyUnitOptions(argsman);
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
argsman.AddArg("-create", "Create new, empty TX.", ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
argsman.AddArg("-json", "Select JSON output", ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
argsman.AddArg(
"-txid",
"Output only the hex-encoded transaction id of the resultant "
"transaction.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions(argsman);
argsman.AddArg("delin=N", "Delete input N from TX", ArgsManager::ALLOW_ANY,
OptionsCategory::COMMANDS);
argsman.AddArg("delout=N", "Delete output N from TX",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg("locktime=N", "Set TX lock time to N",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg("nversion=N", "Set TX version to N", ArgsManager::ALLOW_ANY,
OptionsCategory::COMMANDS);
argsman.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg("outpubkey=VALUE:PUBKEY[:FLAGS]",
"Add pay-to-pubkey output to TX. "
"Optionally add the \"S\" flag to wrap the output in a "
"pay-to-script-hash.",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg("outscript=VALUE:SCRIPT[:FLAGS]",
"Add raw script output to TX. "
"Optionally add the \"S\" flag to wrap the output in a "
"pay-to-script-hash.",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg(
"outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]",
"Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS. "
"Optionally add the \"S\" flag to wrap the output in a "
"pay-to-script-hash.",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg("sign=SIGHASH-FLAGS",
"Add zero or more signatures to transaction. "
"This command requires JSON registers:"
"prevtxs=JSON object, "
"privatekeys=JSON object. "
"See signrawtransactionwithkey docs for format of sighash "
"flags, JSON objects.",
ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
argsman.AddArg("load=NAME:FILENAME",
"Load JSON file FILENAME into register NAME",
ArgsManager::ALLOW_ANY, OptionsCategory::REGISTER_COMMANDS);
argsman.AddArg("set=NAME:JSON-STRING",
"Set register NAME to given JSON-STRING",
ArgsManager::ALLOW_ANY, OptionsCategory::REGISTER_COMMANDS);
}
//
// This function returns either one of EXIT_ codes when it's expected to stop
// the process or CONTINUE_EXECUTION when it's expected to continue further.
//
static int AppInitRawTx(int argc, char *argv[]) {
//
// Parameters
//
SetupBitcoinTxArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n",
error);
return EXIT_FAILURE;
}
// Check for -chain, -testnet or -regtest parameter (Params() calls are only
// valid after this clause)
try {
SelectParams(gArgs.GetChainType());
} catch (const std::exception &e) {
tfm::format(std::cerr, "Error: %s\n", e.what());
return EXIT_FAILURE;
}
fCreateBlank = gArgs.GetBoolArg("-create", false);
if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
// First part of help message is specific to this utility
std::string strUsage = PACKAGE_NAME " bitcoin-tx utility version " +
FormatFullVersion() + "\n";
if (gArgs.IsArgSet("-version")) {
strUsage += FormatParagraph(LicenseInfo());
} else {
strUsage +=
"\n"
"Usage: bitcoin-tx [options] <hex-tx> [commands] Update "
"hex-encoded bitcoin transaction\n"
"or: bitcoin-tx [options] -create [commands] Create "
"hex-encoded bitcoin transaction\n"
"\n";
strUsage += gArgs.GetHelpMessage();
}
tfm::format(std::cout, "%s", strUsage);
if (argc < 2) {
tfm::format(std::cerr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
return CONTINUE_EXECUTION;
}
static void RegisterSetJson(const std::string &key,
const std::string &rawJson) {
UniValue val;
if (!val.read(rawJson)) {
std::string strErr = "Cannot parse JSON for key " + key;
throw std::runtime_error(strErr);
}
registers[key] = val;
}
static void RegisterSet(const std::string &strInput) {
// separate NAME:VALUE in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) || (pos == 0) ||
(pos == (strInput.size() - 1))) {
throw std::runtime_error("Register input requires NAME:VALUE");
}
std::string key = strInput.substr(0, pos);
std::string valStr = strInput.substr(pos + 1, std::string::npos);
RegisterSetJson(key, valStr);
}
static void RegisterLoad(const std::string &strInput) {
// separate NAME:FILENAME in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) || (pos == 0) ||
(pos == (strInput.size() - 1))) {
throw std::runtime_error("Register load requires NAME:FILENAME");
}
std::string key = strInput.substr(0, pos);
std::string filename = strInput.substr(pos + 1, std::string::npos);
FILE *f = fsbridge::fopen(filename.c_str(), "r");
if (!f) {
std::string strErr = "Cannot open file " + filename;
throw std::runtime_error(strErr);
}
// load file chunks into one big buffer
std::string valStr;
while ((!feof(f)) && (!ferror(f))) {
char buf[4096];
int bread = fread(buf, 1, sizeof(buf), f);
if (bread <= 0) {
break;
}
valStr.insert(valStr.size(), buf, bread);
}
int error = ferror(f);
fclose(f);
if (error) {
std::string strErr = "Error reading file " + filename;
throw std::runtime_error(strErr);
}
// evaluate as JSON buffer register
RegisterSetJson(key, valStr);
}
static Amount ExtractAndValidateValue(const std::string &strValue) {
Amount value;
if (!ParseMoney(strValue, value)) {
throw std::runtime_error("invalid TX output value");
}
return value;
}
static void MutateTxVersion(CMutableTransaction &tx,
const std::string &cmdVal) {
int64_t newVersion;
if (!ParseInt64(cmdVal, &newVersion) ||
newVersion < CTransaction::MIN_VERSION ||
newVersion > CTransaction::MAX_VERSION) {
throw std::runtime_error("Invalid TX version requested: '" + cmdVal +
"'");
}
tx.nVersion = int(newVersion);
}
static void MutateTxLocktime(CMutableTransaction &tx,
const std::string &cmdVal) {
int64_t newLocktime;
if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL ||
newLocktime > 0xffffffffLL) {
throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal +
"'");
}
tx.nLockTime = (unsigned int)newLocktime;
}
static void MutateTxAddInput(CMutableTransaction &tx,
const std::string &strInput) {
std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
// separate TXID:VOUT in string
if (vStrInputParts.size() < 2) {
throw std::runtime_error("TX input missing separator");
}
// extract and validate TXID
uint256 hash;
if (!ParseHashStr(vStrInputParts[0], hash)) {
throw std::runtime_error("invalid TX input txid");
}
TxId txid(hash);
static const unsigned int minTxOutSz = 9;
static const unsigned int maxVout = MAX_TX_SIZE / minTxOutSz;
// extract and validate vout
const std::string &strVout = vStrInputParts[1];
int64_t vout;
if (!ParseInt64(strVout, &vout) || vout < 0 ||
vout > static_cast<int64_t>(maxVout)) {
throw std::runtime_error("invalid TX input vout '" + strVout + "'");
}
// extract the optional sequence number
uint32_t nSequenceIn = std::numeric_limits<unsigned int>::max();
if (vStrInputParts.size() > 2) {
nSequenceIn = std::stoul(vStrInputParts[2]);
}
// append to transaction input list
CTxIn txin(txid, vout, CScript(), nSequenceIn);
tx.vin.push_back(txin);
}
static void MutateTxAddOutAddr(CMutableTransaction &tx,
const std::string &strInput,
const CChainParams &chainParams) {
// Separate into VALUE:ADDRESS
std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
if (vStrInputParts.size() != 2) {
throw std::runtime_error("TX output missing or too many separators");
}
// Extract and validate VALUE
Amount value = ExtractAndValidateValue(vStrInputParts[0]);
// extract and validate ADDRESS
std::string strAddr = vStrInputParts[1];
CTxDestination destination = DecodeDestination(strAddr, chainParams);
if (!IsValidDestination(destination)) {
throw std::runtime_error("invalid TX output address");
}
CScript scriptPubKey = GetScriptForDestination(destination);
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutPubKey(CMutableTransaction &tx,
const std::string &strInput) {
// Separate into VALUE:PUBKEY[:FLAGS]
std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3) {
throw std::runtime_error("TX output missing or too many separators");
}
// Extract and validate VALUE
Amount value = ExtractAndValidateValue(vStrInputParts[0]);
// Extract and validate PUBKEY
CPubKey pubkey(ParseHex(vStrInputParts[1]));
if (!pubkey.IsFullyValid()) {
throw std::runtime_error("invalid TX output pubkey");
}
CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
// Extract and validate FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == 3) {
std::string flags = vStrInputParts[2];
bScriptHash = (flags.find('S') != std::string::npos);
}
if (bScriptHash) {
// Get the ID for the script, and then construct a P2SH destination for
// it.
scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutMultiSig(CMutableTransaction &tx,
const std::string &strInput) {
// Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
// Check that there are enough parameters
if (vStrInputParts.size() < 3) {
throw std::runtime_error("Not enough multisig parameters");
}
// Extract and validate VALUE
Amount value = ExtractAndValidateValue(vStrInputParts[0]);
// Extract REQUIRED
uint32_t required = stoul(vStrInputParts[1]);
// Extract NUMKEYS
uint32_t numkeys = stoul(vStrInputParts[2]);
// Validate there are the correct number of pubkeys
if (vStrInputParts.size() < numkeys + 3) {
throw std::runtime_error("incorrect number of multisig pubkeys");
}
if (required < 1 || required > MAX_PUBKEYS_PER_MULTISIG || numkeys < 1 ||
numkeys > MAX_PUBKEYS_PER_MULTISIG || numkeys < required) {
throw std::runtime_error("multisig parameter mismatch. Required " +
ToString(required) + " of " +
ToString(numkeys) + "signatures.");
}
// extract and validate PUBKEYs
std::vector<CPubKey> pubkeys;
for (int pos = 1; pos <= int(numkeys); pos++) {
CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
if (!pubkey.IsFullyValid()) {
throw std::runtime_error("invalid TX output pubkey");
}
pubkeys.push_back(pubkey);
}
// Extract FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == numkeys + 4) {
std::string flags = vStrInputParts.back();
bScriptHash = (flags.find('S') != std::string::npos);
} else if (vStrInputParts.size() > numkeys + 4) {
// Validate that there were no more parameters passed
throw std::runtime_error("Too many parameters");
}
CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
if (bScriptHash) {
if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
throw std::runtime_error(
strprintf("redeemScript exceeds size limit: %d > %d",
scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
}
// Get the ID for the script, and then construct a P2SH destination for
// it.
scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutData(CMutableTransaction &tx,
const std::string &strInput) {
Amount value = Amount::zero();
// separate [VALUE:]DATA in string
size_t pos = strInput.find(':');
if (pos == 0) {
throw std::runtime_error("TX output value not specified");
}
if (pos == std::string::npos) {
pos = 0;
} else {
// Extract and validate VALUE
value = ExtractAndValidateValue(strInput.substr(0, pos));
++pos;
}
// extract and validate DATA
const std::string strData{strInput.substr(pos, std::string::npos)};
if (!IsHex(strData)) {
throw std::runtime_error("invalid TX output data");
}
std::vector<uint8_t> data = ParseHex(strData);
CTxOut txout(value, CScript() << OP_RETURN << data);
tx.vout.push_back(txout);
}
static void MutateTxAddOutScript(CMutableTransaction &tx,
const std::string &strInput) {
// separate VALUE:SCRIPT[:FLAGS]
std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
if (vStrInputParts.size() < 2) {
throw std::runtime_error("TX output missing separator");
}
// Extract and validate VALUE
Amount value = ExtractAndValidateValue(vStrInputParts[0]);
// extract and validate script
std::string strScript = vStrInputParts[1];
CScript scriptPubKey = ParseScript(strScript);
// Extract FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == 3) {
std::string flags = vStrInputParts.back();
bScriptHash = (flags.find('S') != std::string::npos);
}
if (scriptPubKey.size() > MAX_SCRIPT_SIZE) {
throw std::runtime_error(strprintf("script exceeds size limit: %d > %d",
scriptPubKey.size(),
MAX_SCRIPT_SIZE));
}
if (bScriptHash) {
if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
throw std::runtime_error(
strprintf("redeemScript exceeds size limit: %d > %d",
scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
}
scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxDelInput(CMutableTransaction &tx,
const std::string &strInIdx) {
// parse requested deletion index
int64_t inIdx;
if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 ||
inIdx >= static_cast<int64_t>(tx.vin.size())) {
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
}
// delete input from transaction
tx.vin.erase(tx.vin.begin() + inIdx);
}
static void MutateTxDelOutput(CMutableTransaction &tx,
const std::string &strOutIdx) {
// parse requested deletion index
int64_t outIdx;
if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 ||
outIdx >= static_cast<int64_t>(tx.vout.size())) {
throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'");
}
// delete output from transaction
tx.vout.erase(tx.vout.begin() + outIdx);
}
static const unsigned int N_SIGHASH_OPTS = 12;
static const struct {
const char *flagStr;
int flags;
} sigHashOptions[N_SIGHASH_OPTS] = {
{"ALL", SIGHASH_ALL},
{"NONE", SIGHASH_NONE},
{"SINGLE", SIGHASH_SINGLE},
{"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY},
{"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY},
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY},
{"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID},
{"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID},
{"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID},
{"ALL|FORKID|ANYONECANPAY",
SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
{"NONE|FORKID|ANYONECANPAY",
SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
{"SINGLE|FORKID|ANYONECANPAY",
SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
};
static bool findSigHashFlags(SigHashType &sigHashType,
const std::string &flagStr) {
sigHashType = SigHashType();
for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
if (flagStr == sigHashOptions[i].flagStr) {
sigHashType = SigHashType(sigHashOptions[i].flags);
return true;
}
}
return false;
}
static void MutateTxSign(CMutableTransaction &tx, const std::string &flagStr) {
SigHashType sigHashType = SigHashType().withForkId();
if ((flagStr.size() > 0) && !findSigHashFlags(sigHashType, flagStr)) {
throw std::runtime_error("unknown sighash flag/sign option");
}
// mergedTx will end up with all the signatures; it
// starts as a clone of the raw tx:
CMutableTransaction mergedTx{tx};
const CMutableTransaction txv{tx};
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
if (!registers.count("privatekeys")) {
throw std::runtime_error("privatekeys register variable must be set.");
}
FillableSigningProvider tempKeystore;
UniValue keysObj = registers["privatekeys"];
for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
if (!keysObj[kidx].isStr()) {
throw std::runtime_error("privatekey not a std::string");
}
CKey key = DecodeSecret(keysObj[kidx].getValStr());
if (!key.IsValid()) {
throw std::runtime_error("privatekey not valid");
}
tempKeystore.AddKey(key);
}
// Add previous txouts given in the RPC call:
if (!registers.count("prevtxs")) {
throw std::runtime_error("prevtxs register variable must be set.");
}
UniValue prevtxsObj = registers["prevtxs"];
for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
UniValue prevOut = prevtxsObj[previdx];
if (!prevOut.isObject()) {
throw std::runtime_error("expected prevtxs internal object");
}
std::map<std::string, UniValue::VType> types = {
{"txid", UniValue::VSTR},
{"vout", UniValue::VNUM},
{"scriptPubKey", UniValue::VSTR}};
if (!prevOut.checkObject(types)) {
throw std::runtime_error("prevtxs internal object typecheck fail");
}
uint256 hash;
if (!ParseHashStr(prevOut["txid"].get_str(), hash)) {
throw std::runtime_error("txid must be hexadecimal string (not '" +
prevOut["txid"].get_str() + "')");
}
TxId txid(hash);
const int nOut = prevOut["vout"].getInt<int>();
if (nOut < 0) {
throw std::runtime_error("vout cannot be negative");
}
COutPoint out(txid, nOut);
std::vector<uint8_t> pkData(
ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
const Coin &coin = view.AccessCoin(out);
if (!coin.IsSpent() &&
coin.GetTxOut().scriptPubKey != scriptPubKey) {
std::string err("Previous output scriptPubKey mismatch:\n");
err = err + ScriptToAsmStr(coin.GetTxOut().scriptPubKey) +
"\nvs:\n" + ScriptToAsmStr(scriptPubKey);
throw std::runtime_error(err);
}
CTxOut txout;
txout.scriptPubKey = scriptPubKey;
txout.nValue = Amount::zero();
if (prevOut.exists("amount")) {
txout.nValue = AmountFromValue(prevOut["amount"]);
}
view.AddCoin(out, Coin(txout, 1, false), true);
}
// If redeemScript given and private keys given, add redeemScript to the
// tempKeystore so it can be signed:
if (scriptPubKey.IsPayToScriptHash() &&
prevOut.exists("redeemScript")) {
UniValue v = prevOut["redeemScript"];
std::vector<uint8_t> rsData(ParseHexUV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
const FillableSigningProvider &keystore = tempKeystore;
// Sign what we can:
for (size_t i = 0; i < mergedTx.vin.size(); i++) {
CTxIn &txin = mergedTx.vin[i];
const Coin &coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {
continue;
}
const CScript &prevPubKey = coin.GetTxOut().scriptPubKey;
const Amount amount = coin.GetTxOut().nValue;
SignatureData sigdata =
DataFromTransaction(mergedTx, i, coin.GetTxOut());
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) ||
(i < mergedTx.vout.size())) {
ProduceSignature(keystore,
MutableTransactionSignatureCreator(
&mergedTx, i, amount, sigHashType),
prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
}
tx = mergedTx;
}
class Secp256k1Init {
ECCVerifyHandle globalVerifyHandle;
public:
Secp256k1Init() { ECC_Start(); }
~Secp256k1Init() { ECC_Stop(); }
};
static void MutateTx(CMutableTransaction &tx, const std::string &command,
const std::string &commandVal,
const CChainParams &chainParams) {
std::unique_ptr<Secp256k1Init> ecc;
if (command == "nversion") {
MutateTxVersion(tx, commandVal);
} else if (command == "locktime") {
MutateTxLocktime(tx, commandVal);
} else if (command == "delin") {
MutateTxDelInput(tx, commandVal);
} else if (command == "in") {
MutateTxAddInput(tx, commandVal);
} else if (command == "delout") {
MutateTxDelOutput(tx, commandVal);
} else if (command == "outaddr") {
MutateTxAddOutAddr(tx, commandVal, chainParams);
} else if (command == "outpubkey") {
ecc.reset(new Secp256k1Init());
MutateTxAddOutPubKey(tx, commandVal);
} else if (command == "outmultisig") {
ecc.reset(new Secp256k1Init());
MutateTxAddOutMultiSig(tx, commandVal);
} else if (command == "outscript") {
MutateTxAddOutScript(tx, commandVal);
} else if (command == "outdata") {
MutateTxAddOutData(tx, commandVal);
} else if (command == "sign") {
ecc.reset(new Secp256k1Init());
MutateTxSign(tx, commandVal);
} else if (command == "load") {
RegisterLoad(commandVal);
} else if (command == "set") {
RegisterSet(commandVal);
} else {
throw std::runtime_error("unknown command");
}
}
static void OutputTxJSON(const CTransaction &tx) {
UniValue entry(UniValue::VOBJ);
TxToUniv(tx, BlockHash(), entry);
std::string jsonOutput = entry.write(4);
tfm::format(std::cout, "%s\n", jsonOutput);
}
static void OutputTxHash(const CTransaction &tx) {
// the hex-encoded transaction id.
std::string strHexHash = tx.GetId().GetHex();
tfm::format(std::cout, "%s\n", strHexHash);
}
static void OutputTxHex(const CTransaction &tx) {
std::string strHex = EncodeHexTx(tx);
tfm::format(std::cout, "%s\n", strHex);
}
static void OutputTx(const CTransaction &tx) {
if (gArgs.GetBoolArg("-json", false)) {
OutputTxJSON(tx);
} else if (gArgs.GetBoolArg("-txid", false)) {
OutputTxHash(tx);
} else {
OutputTxHex(tx);
}
}
static std::string readStdin() {
char buf[4096];
std::string ret;
while (!feof(stdin)) {
size_t bread = fread(buf, 1, sizeof(buf), stdin);
ret.append(buf, bread);
if (bread < sizeof(buf)) {
break;
}
}
if (ferror(stdin)) {
throw std::runtime_error("error reading stdin");
}
return TrimString(ret);
}
static int CommandLineRawTx(int argc, char *argv[],
const CChainParams &chainParams) {
std::string strPrint;
int nRet = 0;
try {
// Skip switches; Permit common stdin convention "-"
while (argc > 1 && IsSwitchChar(argv[1][0]) && (argv[1][1] != 0)) {
argc--;
argv++;
}
CMutableTransaction tx;
int startArg;
if (!fCreateBlank) {
// require at least one param
if (argc < 2) {
throw std::runtime_error("too few parameters");
}
// param: hex-encoded bitcoin transaction
std::string strHexTx(argv[1]);
// "-" implies standard input
if (strHexTx == "-") {
strHexTx = readStdin();
}
if (!DecodeHexTx(tx, strHexTx)) {
throw std::runtime_error("invalid transaction encoding");
}
startArg = 2;
} else {
startArg = 1;
}
for (int i = startArg; i < argc; i++) {
std::string arg = argv[i];
std::string key, value;
size_t eqpos = arg.find('=');
if (eqpos == std::string::npos) {
key = arg;
} else {
key = arg.substr(0, eqpos);
value = arg.substr(eqpos + 1);
}
MutateTx(tx, key, value, chainParams);
}
OutputTx(CTransaction(tx));
} catch (const std::exception &e) {
strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
} catch (const UniValue &e) {
strPrint = std::string("error code: ") + e["code"].getValStr() +
" message: " + e["message"].getValStr();
nRet = EXIT_FAILURE;
} catch (...) {
PrintExceptionContinue(nullptr, "CommandLineRawTx()");
throw;
}
if (strPrint != "") {
tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
}
return nRet;
}
int main(int argc, char *argv[]) {
SetupEnvironment();
try {
int ret = AppInitRawTx(argc, argv);
if (ret != CONTINUE_EXECUTION) {
return ret;
}
} catch (const std::exception &e) {
PrintExceptionContinue(&e, "AppInitRawTx()");
return EXIT_FAILURE;
} catch (...) {
PrintExceptionContinue(nullptr, "AppInitRawTx()");
return EXIT_FAILURE;
}
int ret = EXIT_FAILURE;
try {
ret = CommandLineRawTx(argc, argv, Params());
} catch (const std::exception &e) {
PrintExceptionContinue(&e, "CommandLineRawTx()");
} catch (...) {
PrintExceptionContinue(nullptr, "CommandLineRawTx()");
}
return ret;
}
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index f6700e07a..ae57490db 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -1,52 +1,53 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Copyright (c) 2017-2020 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <chainparamsbase.h>
#include <common/args.h>
#include <consensus/merkle.h>
#include <currencyunit.h>
#include <logging.h>
#include <tinyformat.h>
#include <util/chaintype.h>
#include <cassert>
static std::unique_ptr<const CChainParams> globalChainParams;
const CChainParams &Params() {
assert(globalChainParams);
return *globalChainParams;
}
void ReadChainArgs(const ArgsManager &args,
CChainParams::ChainOptions &options) {
options.ecash = args.GetBoolArg("-ecash", DEFAULT_ECASH);
// Only relevant for REGTEST
options.fastprune = args.GetBoolArg("-fastprune", false);
}
std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager &args,
const ChainType chain) {
auto opts = CChainParams::ChainOptions{};
ReadChainArgs(args, opts);
switch (chain) {
case ChainType::MAIN:
return CChainParams::Main(opts);
case ChainType::TESTNET:
return CChainParams::TestNet(opts);
case ChainType::REGTEST: {
return CChainParams::RegTest(opts);
}
}
throw std::invalid_argument(
strprintf("%s: Invalid ChainType value", __func__));
}
void SelectParams(const ChainType chain) {
SelectBaseParams(chain);
globalChainParams = CreateChainParams(gArgs, chain);
}
diff --git a/src/chainparams.h b/src/chainparams.h
index e23598999..90496c682 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -1,43 +1,44 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CHAINPARAMS_H
#define BITCOIN_CHAINPARAMS_H
#include <kernel/chainparams.h>
-#include <chainparamsbase.h>
#include <consensus/params.h>
#include <netaddress.h>
#include <primitives/block.h>
#include <protocol.h>
#include <util/chaintype.h>
#include <util/hash_type.h>
#include <memory>
#include <string>
#include <vector>
+class ArgsManager;
+
/**
* Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
* @returns a CChainParams* of the chosen chain.
* @throws a std::runtime_error if the chain is not supported.
*/
std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager &args,
const ChainType chain);
/**
* Return the currently selected parameters. This won't change after app
* startup, except for unit tests.
*/
const CChainParams &Params();
/**
* Sets the params returned by Params() to those for the given BIP70 chain name.
* @throws std::runtime_error when the chain is not supported.
*/
void SelectParams(const ChainType chain);
#endif // BITCOIN_CHAINPARAMS_H
diff --git a/src/init.cpp b/src/init.cpp
index d4132ed1c..af656c261 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,3138 +1,3139 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <init.h>
#include <kernel/checks.h>
#include <kernel/mempool_persist.h>
#include <kernel/validation_cache_sizes.h>
#include <addrman.h>
#include <avalanche/avalanche.h>
#include <avalanche/processor.h>
#include <avalanche/proof.h> // For AVALANCHE_LEGACY_PROOF_DEFAULT
#include <avalanche/validation.h>
#include <avalanche/voterecord.h> // For AVALANCHE_VOTE_STALE_*
#include <banman.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
+#include <chainparamsbase.h>
#include <common/args.h>
#include <config.h>
#include <consensus/amount.h>
#include <currencyunit.h>
#include <flatfile.h>
#include <hash.h>
#include <httprpc.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
#include <index/txindex.h>
#include <init/common.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <mapport.h>
#include <mempool_args.h>
#include <net.h>
#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
#include <node/blockmanager_args.h>
#include <node/blockstorage.h>
#include <node/caches.h>
#include <node/chainstate.h>
#include <node/chainstatemanager_args.h>
#include <node/context.h>
#include <node/kernel_notifications.h>
#include <node/mempool_persist_args.h>
#include <node/miner.h>
#include <node/peerman_args.h>
#include <node/ui_interface.h>
#include <node/validation_cache_args.h>
#include <policy/block/rtt.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <scheduler.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <script/standard.h>
#include <shutdown.h>
#include <sync.h>
#include <timedata.h>
#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
#include <util/asmap.h>
#include <util/chaintype.h>
#include <util/check.h>
#include <util/fs.h>
#include <util/fs_helpers.h>
#include <util/moneystr.h>
#include <util/string.h>
#include <util/syserror.h>
#include <util/thread.h>
#include <util/threadnames.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <walletinitinterface.h>
#include <boost/signals2/signal.hpp>
#if ENABLE_CHRONIK
#include <chronik-cpp/chronik.h>
#endif
#if ENABLE_ZMQ
#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqnotificationinterface.h>
#include <zmq/zmqrpc.h>
#endif
#ifndef WIN32
#include <cerrno>
#include <csignal>
#include <sys/stat.h>
#endif
#include <algorithm>
#include <condition_variable>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <functional>
#include <set>
#include <string>
#include <thread>
#include <vector>
using kernel::DEFAULT_STOPAFTERBLOCKIMPORT;
using kernel::DumpMempool;
using kernel::ValidationCacheSizes;
using node::ApplyArgsManOptions;
using node::BlockManager;
using node::CacheSizes;
using node::CalculateCacheSizes;
using node::DEFAULT_PERSIST_MEMPOOL;
using node::fReindex;
using node::ImportBlocks;
using node::KernelNotifications;
using node::LoadChainstate;
using node::MempoolPath;
using node::NodeContext;
using node::ShouldPersistMempool;
using node::VerifyLoadedChainstate;
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static constexpr bool DEFAULT_CHRONIK = false;
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for accessing
// block files don't count towards the fd_set size limit anyway.
#define MIN_CORE_FILEDESCRIPTORS 0
#else
#define MIN_CORE_FILEDESCRIPTORS 150
#endif
static const char *DEFAULT_ASMAP_FILENAME = "ip_asn.map";
static const std::string HEADERS_TIME_FILE_NAME{"headerstime.dat"};
/**
* The PID file facilities.
*/
static const char *BITCOIN_PID_FILENAME = "bitcoind.pid";
static fs::path GetPidFile(const ArgsManager &args) {
return AbsPathForConfigVal(args,
args.GetPathArg("-pid", BITCOIN_PID_FILENAME));
}
[[nodiscard]] static bool CreatePidFile(const ArgsManager &args) {
std::ofstream file{GetPidFile(args)};
if (file) {
#ifdef WIN32
tfm::format(file, "%d\n", GetCurrentProcessId());
#else
tfm::format(file, "%d\n", getpid());
#endif
return true;
} else {
return InitError(strprintf(_("Unable to create the PID file '%s': %s"),
fs::PathToString(GetPidFile(args)),
SysErrorString(errno)));
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
//
//
// Thread management and startup/shutdown:
//
// The network-processing threads are all part of a thread group created by
// AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM signal handler sets
// fRequestShutdown, which makes main thread's WaitForShutdown() interrupts the
// thread group.
// And then, WaitForShutdown() makes all other on-going threads in the thread
// group join the main thread.
// Shutdown() is then called to clean up database connections, and stop other
// threads that should only be stopped after the main network-processing threads
// have exited.
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
// ShutdownRequested() getting set, and then does the normal Qt shutdown thing.
//
void Interrupt(NodeContext &node) {
InterruptHTTPServer();
InterruptHTTPRPC();
InterruptRPC();
InterruptREST();
InterruptTorControl();
InterruptMapPort();
if (node.avalanche) {
// Avalanche needs to be stopped before we interrupt the thread group as
// the scheduler will stop working then.
node.avalanche->stopEventLoop();
}
if (node.connman) {
node.connman->Interrupt();
}
if (g_txindex) {
g_txindex->Interrupt();
}
ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Interrupt(); });
if (g_coin_stats_index) {
g_coin_stats_index->Interrupt();
}
}
void Shutdown(NodeContext &node) {
static Mutex g_shutdown_mutex;
TRY_LOCK(g_shutdown_mutex, lock_shutdown);
if (!lock_shutdown) {
return;
}
LogPrintf("%s: In progress...\n", __func__);
Assert(node.args);
/// Note: Shutdown() must be able to handle cases in which initialization
/// failed part of the way, for example if the data directory was found to
/// be locked. Be sure that anything that writes files or flushes caches
/// only does this if the respective module was initialized.
util::ThreadRename("shutoff");
if (node.mempool) {
node.mempool->AddTransactionsUpdated(1);
}
StopHTTPRPC();
StopREST();
StopRPC();
StopHTTPServer();
for (const auto &client : node.chain_clients) {
client->flush();
}
StopMapPort();
// Because avalanche and the network depend on each other, it is important
// to shut them down in this order:
// 1. Stop avalanche event loop.
// 2. Shutdown network processing.
// 3. Destroy avalanche::Processor.
// 4. Destroy CConnman
if (node.avalanche) {
node.avalanche->stopEventLoop();
}
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
if (node.peerman) {
UnregisterValidationInterface(node.peerman.get());
}
if (node.connman) {
node.connman->Stop();
}
StopTorControl();
// After everything has been shut down, but before things get flushed, stop
// the CScheduler/checkqueue, scheduler and load block thread.
if (node.scheduler) {
node.scheduler->stop();
}
if (node.chainman && node.chainman->m_thread_load.joinable()) {
node.chainman->m_thread_load.join();
}
StopScriptCheckWorkerThreads();
// After the threads that potentially access these pointers have been
// stopped, destruct and reset all to nullptr.
node.peerman.reset();
// Destroy various global instances
node.avalanche.reset();
node.connman.reset();
node.banman.reset();
node.addrman.reset();
if (node.mempool && node.mempool->GetLoadTried() &&
ShouldPersistMempool(*node.args)) {
DumpMempool(*node.mempool, MempoolPath(*node.args));
}
// FlushStateToDisk generates a ChainStateFlushed callback, which we should
// avoid missing
if (node.chainman) {
LOCK(cs_main);
for (Chainstate *chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
}
}
}
// After there are no more peers/RPC left to give us new data which may
// generate CValidationInterface callbacks, flush them...
GetMainSignals().FlushBackgroundCallbacks();
#if ENABLE_CHRONIK
if (node.args->GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
chronik::Stop();
}
#endif
// Stop and delete all indexes only after flushing background callbacks.
if (g_txindex) {
g_txindex->Stop();
g_txindex.reset();
}
if (g_coin_stats_index) {
g_coin_stats_index->Stop();
g_coin_stats_index.reset();
}
ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Stop(); });
DestroyAllBlockFilterIndexes();
// Any future callbacks will be dropped. This should absolutely be safe - if
// missing a callback results in an unrecoverable situation, unclean
// shutdown would too. The only reason to do the above flushes is to let the
// wallet catch up with our current chain to avoid any strange pruning edge
// cases and make next startup faster by avoiding rescan.
if (node.chainman) {
LOCK(cs_main);
for (Chainstate *chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews();
}
}
node.chainman->DumpRecentHeadersTime(node.chainman->m_options.datadir /
HEADERS_TIME_FILE_NAME);
}
for (const auto &client : node.chain_clients) {
client->stop();
}
#if ENABLE_ZMQ
if (g_zmq_notification_interface) {
UnregisterValidationInterface(g_zmq_notification_interface.get());
g_zmq_notification_interface.reset();
}
#endif
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
node.kernel.reset();
node.mempool.reset();
node.chainman.reset();
node.scheduler.reset();
try {
if (!fs::remove(GetPidFile(*node.args))) {
LogPrintf("%s: Unable to remove PID file: File does not exist\n",
__func__);
}
} catch (const fs::filesystem_error &e) {
LogPrintf("%s: Unable to remove PID file: %s\n", __func__,
fsbridge::get_filesystem_error_message(e));
}
LogPrintf("%s: done\n", __func__);
}
/**
* Signal handlers are very limited in what they are allowed to do.
* The execution context the handler is invoked in is not guaranteed,
* so we restrict handler operations to just touching variables:
*/
#ifndef WIN32
static void HandleSIGTERM(int) {
StartShutdown();
}
static void HandleSIGHUP(int) {
LogInstance().m_reopen_file = true;
}
#else
static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType) {
StartShutdown();
Sleep(INFINITE);
return true;
}
#endif
#ifndef WIN32
static void registerSignalHandler(int signal, void (*handler)(int)) {
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(signal, &sa, NULL);
}
#endif
static boost::signals2::connection rpc_notify_block_change_connection;
static void OnRPCStarted() {
rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(
std::bind(RPCNotifyBlockChange, std::placeholders::_2));
}
static void OnRPCStopped() {
rpc_notify_block_change_connection.disconnect();
RPCNotifyBlockChange(nullptr);
g_best_block_cv.notify_all();
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
void SetupServerArgs(NodeContext &node) {
assert(!node.args);
node.args = &gArgs;
ArgsManager &argsman = *node.args;
SetupHelpOptions(argsman);
SetupCurrencyUnitOptions(argsman);
// server-only for now
argsman.AddArg("-help-debug",
"Print help message with debugging options and exit", false,
OptionsCategory::DEBUG_TEST);
init::AddLoggingArgs(argsman);
const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET);
const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST);
const auto defaultChainParams = CreateChainParams(argsman, ChainType::MAIN);
const auto testnetChainParams =
CreateChainParams(argsman, ChainType::TESTNET);
const auto regtestChainParams =
CreateChainParams(argsman, ChainType::REGTEST);
// Hidden Options
std::vector<std::string> hidden_args = {
"-dbcrashratio",
"-forcecompactdb",
"-maxaddrtosend",
"-parkdeepreorg",
"-automaticunparking",
"-replayprotectionactivationtime",
"-enableminerfund",
"-chronikallowpause",
"-chronikcors",
// GUI args. These will be overwritten by SetupUIArgs for the GUI
"-allowselfsignedrootcertificates",
"-choosedatadir",
"-lang=<lang>",
"-min",
"-resetguisettings",
"-rootcertificates=<file>",
"-splash",
"-uiplatform",
// TODO remove after the May 2025 upgrade
"-schumpeteractivationtime",
// TODO remove after the Nov 2025 upgrade
"-shibusawaactivationtime",
};
// Set all of the args and their help
// When adding new options to the categories, please keep and ensure
// alphabetical ordering. Do not translate _(...) -help-debug options, Many
// technical terms, and only a very small audience, so is unnecessary stress
// to translators.
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
#if defined(HAVE_SYSTEM)
argsman.AddArg(
"-alertnotify=<cmd>",
"Execute command when a relevant alert is received or we see "
"a really long fork (%s in cmd is replaced by message)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
argsman.AddArg(
"-assumevalid=<hex>",
strprintf(
"If this block is in the chain assume that it and its ancestors "
"are valid and potentially skip their script verification (0 to "
"verify all, default: %s, testnet: %s)",
defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(),
testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksdir=<dir>",
"Specify directory to hold blocks subdirectory for *.dat "
"files (default: <datadir>)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-fastprune",
"Use smaller block files and lower minimum prune height for "
"testing purposes",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
#if defined(HAVE_SYSTEM)
argsman.AddArg("-blocknotify=<cmd>",
"Execute command when the best block changes (%s in cmd is "
"replaced by block hash)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
argsman.AddArg("-blockreconstructionextratxn=<n>",
strprintf("Extra transactions to keep in memory for compact "
"block reconstructions (default: %u)",
DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-blocksonly",
strprintf("Whether to reject transactions from network peers. "
"Disables automatic broadcast and rebroadcast of "
"transactions, unless the source peer has the "
"'forcerelay' permission. RPC transactions are"
" not affected. (default: %u)",
DEFAULT_BLOCKSONLY),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-coinstatsindex",
strprintf("Maintain coinstats index used by the "
"gettxoutsetinfo RPC (default: %u)",
DEFAULT_COINSTATSINDEX),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-conf=<file>",
strprintf("Specify path to read-only configuration file. Relative "
"paths will be prefixed by datadir location. (default: %s)",
BITCOIN_CONF_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-dbbatchsize",
strprintf("Maximum database write batch size in bytes (default: %u)",
DEFAULT_DB_BATCH_SIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::OPTIONS);
argsman.AddArg(
"-dbcache=<n>",
strprintf("Set database cache size in MiB (%d to %d, default: %d)",
MIN_DB_CACHE_MB, MAX_DB_CACHE_MB, DEFAULT_DB_CACHE_MB),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-includeconf=<file>",
"Specify additional configuration file, relative to the -datadir path "
"(only useable from configuration file, not command line)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-loadblock=<file>",
"Imports blocks from external file on startup",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxmempool=<n>",
strprintf("Keep the transaction memory pool below <n> "
"megabytes (default: %u)",
DEFAULT_MAX_MEMPOOL_SIZE_MB),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxorphantx=<n>",
strprintf("Keep at most <n> unconnectable transactions in "
"memory (default: %u)",
DEFAULT_MAX_ORPHAN_TRANSACTIONS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-mempoolexpiry=<n>",
strprintf("Do not keep transactions in the mempool longer "
"than <n> hours (default: %u)",
DEFAULT_MEMPOOL_EXPIRY_HOURS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-minimumchainwork=<hex>",
strprintf(
"Minimum work assumed to exist on a valid chain in hex "
"(default: %s, testnet: %s)",
defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(),
testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::OPTIONS);
argsman.AddArg(
"-par=<n>",
strprintf("Set the number of script verification threads (%u to %d, 0 "
"= auto, <0 = leave that many cores free, default: %d)",
-GetNumCores(), MAX_SCRIPTCHECK_THREADS,
DEFAULT_SCRIPTCHECK_THREADS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool",
strprintf("Whether to save the mempool on shutdown and load "
"on restart (default: %u)",
DEFAULT_PERSIST_MEMPOOL),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-persistrecentheaderstime",
strprintf(
"Whether the node stores the recent headers reception time to a "
"file and load it upon startup. This is intended for mining nodes "
"to overestimate the real time target upon restart (default: %u)",
DEFAULT_STORE_RECENT_HEADERS_TIME),
ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
argsman.AddArg(
"-pid=<file>",
strprintf("Specify pid file. Relative paths will be prefixed "
"by a net-specific datadir location. (default: %s)",
BITCOIN_PID_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-prune=<n>",
strprintf("Reduce storage requirements by enabling pruning (deleting) "
"of old blocks. This allows the pruneblockchain RPC to be "
"called to delete specific blocks and enables automatic "
"pruning of old blocks if a target size in MiB is provided. "
"This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the "
"entire blockchain. (default: 0 = disable pruning blocks, "
"1 = allow manual pruning via RPC, >=%u = automatically "
"prune block files to stay under the specified target size "
"in MiB)",
MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-reindex-chainstate",
"Rebuild chain state from the currently indexed blocks. When "
"in pruning mode or if blocks on disk might be corrupted, use "
"full -reindex instead.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-reindex",
"Rebuild chain state and block index from the blk*.dat files on disk."
" This will also rebuild active optional indexes.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-settings=<file>",
strprintf(
"Specify path to dynamic settings data file. Can be disabled with "
"-nosettings. File is written at runtime and not meant to be "
"edited by users (use %s instead for custom settings). Relative "
"paths will be prefixed by datadir location. (default: %s)",
BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
#ifndef WIN32
argsman.AddArg(
"-sysperms",
"Create new files with system default permissions, instead of umask "
"077 (only effective with disabled wallet functionality)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-sysperms");
#endif
argsman.AddArg("-txindex",
strprintf("Maintain a full transaction index, used by the "
"getrawtransaction rpc call (default: %d)",
DEFAULT_TXINDEX),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if ENABLE_CHRONIK
argsman.AddArg(
"-chronik",
strprintf("Enable the Chronik indexer, which can be read via a "
"dedicated HTTP/Protobuf interface (default: %d)",
DEFAULT_CHRONIK),
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chronikbind=<addr>[:port]",
strprintf(
"Bind the Chronik indexer to the given address to listen for "
"HTTP/Protobuf connections to access the index. Unlike the "
"JSON-RPC, it's ok to have this publicly exposed on the internet. "
"This option can be specified multiple times (default: %s; default "
"port: %u, testnet: %u, regtest: %u)",
Join(chronik::DEFAULT_BINDS, ", "),
defaultBaseParams->ChronikPort(), testnetBaseParams->ChronikPort(),
regtestBaseParams->ChronikPort()),
ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
OptionsCategory::CHRONIK);
argsman.AddArg("-chroniktokenindex",
"Enable token indexing in Chronik (default: 1)",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg("-chroniklokadidindex",
"Enable LOKAD ID indexing in Chronik (default: 1)",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg("-chronikreindex",
"Reindex the Chronik indexer from genesis, but leave the "
"other indexes untouched",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chroniktxnumcachebuckets",
strprintf(
"Tuning param of the TxNumCache, specifies how many buckets "
"to use on the belt. Caution against setting this too high, "
"it may slow down indexing. Set to 0 to disable. (default: %d)",
chronik::DEFAULT_TX_NUM_CACHE_BUCKETS),
ArgsManager::ALLOW_INT, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chroniktxnumcachebucketsize",
strprintf(
"Tuning param of the TxNumCache, specifies the size of each bucket "
"on the belt. Unlike the number of buckets, this may be increased "
"without much danger of slowing the indexer down. The total cache "
"size will be `num_buckets * bucket_size * 40B`, so by default the "
"cache will require %dkB of memory. (default: %d)",
chronik::DEFAULT_TX_NUM_CACHE_BUCKETS *
chronik::DEFAULT_TX_NUM_CACHE_BUCKET_SIZE * 40 / 1000,
chronik::DEFAULT_TX_NUM_CACHE_BUCKET_SIZE),
ArgsManager::ALLOW_INT, OptionsCategory::CHRONIK);
argsman.AddArg("-chronikperfstats",
"Output some performance statistics (e.g. num cache hits, "
"seconds spent) into a <datadir>/perf folder. (default: 0)",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chronikscripthashindex",
"Enable the scripthash index for the Chronik indexer (default: 0) ",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chronikelectrumbind=<addr>[:port][:t|s]",
strprintf(
"Bind the Chronik Electrum interface to the given "
"address:port:protocol. If not set, the Electrum interface will "
"not start. This option can be specified multiple times. The "
"protocol is selected by a single letter, where 't' means TCP and "
"'s' means TLS. If TLS is selected, the certificate chain and "
"private key must both be passed (see -chronikelectrumcert and "
"-chronikelectrumprivkey (default: disabled; default port: %u, "
"testnet: %u, regtest: %u; default protocol: TCP)",
defaultBaseParams->ChronikElectrumPort(),
testnetBaseParams->ChronikElectrumPort(),
regtestBaseParams->ChronikElectrumPort()),
ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
OptionsCategory::HIDDEN);
argsman.AddArg(
"-chronikelectrumcert",
"Path to the certificate file to be used by the Chronik Electrum "
"server when the TLS protocol is selected. The file should contain "
"the whole certificate chain (typically a .pem file). If used the "
"-chronikelectrumprivkey must be set as well.",
ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
OptionsCategory::HIDDEN);
argsman.AddArg(
"-chronikelectrumprivkey",
"Path to the private key file to be used by the Chronik Electrum "
"server when the TLS protocol is selected. If used the "
"-chronikelectrumcert must be set as well.",
ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
OptionsCategory::HIDDEN);
argsman.AddArg(
"-chronikelectrummaxhistory",
strprintf("Largest tx history we are willing to serve. (default: %u)",
chronik::DEFAULT_ELECTRUM_MAX_HISTORY),
ArgsManager::ALLOW_INT, OptionsCategory::HIDDEN);
argsman.AddArg(
"-chronikelectrumdonationaddress",
strprintf(
"The server donation address. No checks are done on the server "
"side to ensure this is a valid eCash address, it is just relayed "
"to clients verbatim as a text string (%u characters maximum).",
chronik::MAX_LENGTH_DONATION_ADDRESS),
ArgsManager::ALLOW_STRING, OptionsCategory::HIDDEN);
#endif
argsman.AddArg(
"-blockfilterindex=<type>",
strprintf("Maintain an index of compact filters by block "
"(default: %s, values: %s).",
DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
" If <type> is not supplied or if <type> = 1, indexes for "
"all known types are enabled.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-usecashaddr",
"Use Cash Address for destination encoding instead of base58 "
"(activate by default on Jan, 14)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-addnode=<ip>",
"Add a node to connect to and attempt to keep the connection "
"open (see the `addnode` RPC command help for more info)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg("-asmap=<file>",
strprintf("Specify asn mapping used for bucketing of the "
"peers (default: %s). Relative paths will be "
"prefixed by the net-specific datadir location.",
DEFAULT_ASMAP_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bantime=<n>",
strprintf("Default duration (in seconds) of manually "
"configured bans (default: %u)",
DEFAULT_MISBEHAVING_BANTIME),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-bind=<addr>[:<port>][=onion]",
strprintf("Bind to given address and always listen on it (default: "
"0.0.0.0). Use [host]:port notation for IPv6. Append =onion "
"to tag any incoming connections to that address and port as "
"incoming Tor connections (default: 127.0.0.1:%u=onion, "
"testnet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)",
defaultBaseParams->OnionServiceTargetPort(),
testnetBaseParams->OnionServiceTargetPort(),
regtestBaseParams->OnionServiceTargetPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg(
"-connect=<ip>",
"Connect only to the specified node(s); -connect=0 disables automatic "
"connections (the rules for this peer are the same as for -addnode)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg(
"-discover",
"Discover own IP addresses (default: 1 when listening and no "
"-externalip or -proxy)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns",
strprintf("Allow DNS lookups for -addnode, -seednode and "
"-connect (default: %d)",
DEFAULT_NAME_LOOKUP),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-dnsseed",
strprintf(
"Query for peer addresses via DNS lookup, if low on addresses "
"(default: %u unless -connect used)",
DEFAULT_DNSSEED),
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-externalip=<ip>", "Specify your own public address",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-fixedseeds",
strprintf(
"Allow fixed seeds if DNS seeds don't provide peers (default: %u)",
DEFAULT_FIXEDSEEDS),
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg(
"-forcednsseed",
strprintf(
"Always query for peer addresses via DNS lookup (default: %d)",
DEFAULT_FORCEDNSSEED),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-overridednsseed",
"If set, only use the specified DNS seed when "
"querying for peer addresses via DNS lookup.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-listen",
"Accept connections from outside (default: 1 if no -proxy or -connect)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-listenonion",
strprintf("Automatically create Tor onion service (default: %d)",
DEFAULT_LISTEN_ONION),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxconnections=<n>",
strprintf("Maintain at most <n> connections to peers. The effective "
"limit depends on system limitations and might be lower than "
"the specified value (default: %u)",
DEFAULT_MAX_PEER_CONNECTIONS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxreceivebuffer=<n>",
strprintf("Maximum per-connection receive buffer, <n>*1000 "
"bytes (default: %u)",
DEFAULT_MAXRECEIVEBUFFER),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxsendbuffer=<n>",
strprintf(
"Maximum per-connection send buffer, <n>*1000 bytes (default: %u)",
DEFAULT_MAXSENDBUFFER),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxtimeadjustment",
strprintf("Maximum allowed median peer time offset adjustment. Local "
"perspective of time may be influenced by peers forward or "
"backward by this amount. (default: %u seconds)",
DEFAULT_MAX_TIME_ADJUSTMENT),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>",
strprintf("Use separate SOCKS5 proxy to reach peers via Tor "
"onion services (default: %s)",
"-proxy"),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2psam=<ip:port>",
"I2P SAM proxy to reach I2P peers and accept I2P "
"connections (default: none)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-i2pacceptincoming",
"If set and -i2psam is also set then incoming I2P connections are "
"accepted via the SAM proxy. If this is not set but -i2psam is set "
"then only outgoing connections will be made to the I2P network. "
"Ignored if -i2psam is not set. Listening for incoming I2P connections "
"is done through the SAM proxy, not by binding to a local address and "
"port (default: 1)",
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg(
"-onlynet=<net>",
"Make outgoing connections only through network <net> (" +
Join(GetNetworkNames(), ", ") +
"). Incoming connections are not affected by this option. This "
"option can be specified multiple times to allow multiple "
"networks. Warning: if it is used with non-onion networks "
"and the -onion or -proxy option is set, then outbound onion "
"connections will still be made; use -noonion or -onion=0 to "
"disable outbound onion connections in this case",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters",
strprintf("Support filtering of blocks and transaction with "
"bloom filters (default: %d)",
DEFAULT_PEERBLOOMFILTERS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-peerblockfilters",
strprintf(
"Serve compact block filters to peers per BIP 157 (default: %u)",
DEFAULT_PEERBLOCKFILTERS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig",
strprintf("Relay non-P2SH multisig (default: %d)",
DEFAULT_PERMIT_BAREMULTISIG),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
// TODO: remove the sentence "Nodes not using ... incoming connections."
// once the changes from https://github.com/bitcoin/bitcoin/pull/23542 have
// become widespread.
argsman.AddArg("-port=<port>",
strprintf("Listen for connections on <port>. Nodes not "
"using the default ports (default: %u, "
"testnet: %u, regtest: %u) are unlikely to get "
"incoming connections. Not relevant for I2P (see "
"doc/i2p.md).",
defaultChainParams->GetDefaultPort(),
testnetChainParams->GetDefaultPort(),
regtestChainParams->GetDefaultPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-proxyrandomize",
strprintf("Randomize credentials for every proxy connection. "
"This enables Tor stream isolation (default: %d)",
DEFAULT_PROXYRANDOMIZE),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-seednode=<ip>",
"Connect to a node to retrieve peer addresses, and disconnect",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-networkactive",
"Enable all P2P network activity (default: 1). Can be changed "
"by the setnetworkactive RPC command",
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-timeout=<n>",
strprintf("Specify connection timeout in milliseconds "
"(minimum: 1, default: %d)",
DEFAULT_CONNECT_TIMEOUT),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-peertimeout=<n>",
strprintf("Specify p2p connection timeout in seconds. This option "
"determines the amount of time a peer may be inactive before "
"the connection to it is dropped. (minimum: 1, default: %d)",
DEFAULT_PEER_CONNECT_TIMEOUT),
true, OptionsCategory::CONNECTION);
argsman.AddArg(
"-torcontrol=<ip>:<port>",
strprintf(
"Tor control port to use if onion listening enabled (default: %s)",
DEFAULT_TOR_CONTROL),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-torpassword=<pass>",
"Tor control port password (default: empty)",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::CONNECTION);
#ifdef USE_UPNP
#if USE_UPNP
argsman.AddArg("-upnp",
"Use UPnP to map the listening port (default: 1 when "
"listening and no -proxy)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#else
argsman.AddArg(
"-upnp",
strprintf("Use UPnP to map the listening port (default: %u)", 0),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#endif
#else
hidden_args.emplace_back("-upnp");
#endif
#ifdef USE_NATPMP
argsman.AddArg(
"-natpmp",
strprintf("Use NAT-PMP to map the listening port (default: %s)",
DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"),
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
#else
hidden_args.emplace_back("-natpmp");
#endif // USE_NATPMP
argsman.AddArg(
"-whitebind=<[permissions@]addr>",
"Bind to the given address and add permission flags to the peers "
"connecting to it."
"Use [host]:port notation for IPv6. Allowed permissions: " +
Join(NET_PERMISSIONS_DOC, ", ") +
". "
"Specify multiple permissions separated by commas (default: "
"download,noban,mempool,relay). Can be specified multiple times.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-whitelist=<[permissions@]IP address or network>",
"Add permission flags to the peers using the given "
"IP address (e.g. 1.2.3.4) or CIDR-notated network "
"(e.g. 1.2.3.0/24). "
"Uses the same permissions as -whitebind. "
"Additional flags \"in\" and \"out\" control whether "
"permissions apply to incoming connections and/or manual "
"(default: incoming only). "
"Can be specified multiple times.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxuploadtarget=<n>",
strprintf("Tries to keep outbound traffic under the given target (in "
"MiB per 24h). Limit does not apply to peers with 'download' "
"permission. 0 = no limit (default: %d)",
DEFAULT_MAX_UPLOAD_TARGET),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
g_wallet_init_interface.AddWalletOptions(argsman);
#if ENABLE_ZMQ
argsman.AddArg("-zmqpubhashblock=<address>",
"Enable publish hash block in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubhashtx=<address>",
"Enable publish hash transaction in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawblock=<address>",
"Enable publish raw block in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawtx=<address>",
"Enable publish raw transaction in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubsequence=<address>",
"Enable publish hash block and tx sequence in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubhashblockhwm=<n>",
strprintf("Set publish hash block outbound message high water "
"mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubhashtxhwm=<n>",
strprintf("Set publish hash transaction outbound message high "
"water mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
false, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubrawblockhwm=<n>",
strprintf("Set publish raw block outbound message high water "
"mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubrawtxhwm=<n>",
strprintf("Set publish raw transaction outbound message high "
"water mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubsequencehwm=<n>",
strprintf("Set publish hash sequence message high water mark"
" (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
#else
hidden_args.emplace_back("-zmqpubhashblock=<address>");
hidden_args.emplace_back("-zmqpubhashtx=<address>");
hidden_args.emplace_back("-zmqpubrawblock=<address>");
hidden_args.emplace_back("-zmqpubrawtx=<address>");
hidden_args.emplace_back("-zmqpubsequence=<n>");
hidden_args.emplace_back("-zmqpubhashblockhwm=<n>");
hidden_args.emplace_back("-zmqpubhashtxhwm=<n>");
hidden_args.emplace_back("-zmqpubrawblockhwm=<n>");
hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
hidden_args.emplace_back("-zmqpubsequencehwm=<n>");
#endif
argsman.AddArg(
"-checkblocks=<n>",
strprintf("How many blocks to check at startup (default: %u, 0 = all)",
DEFAULT_CHECKBLOCKS),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checklevel=<n>",
strprintf("How thorough the block verification of "
"-checkblocks is: %s (0-4, default: %u)",
Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkblockindex",
strprintf("Do a consistency check for the block tree, "
"chainstate, and other validation data structures "
"occasionally. (default: %u, regtest: %u)",
defaultChainParams->DefaultConsistencyChecks(),
regtestChainParams->DefaultConsistencyChecks()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkaddrman=<n>",
strprintf("Run addrman consistency checks every <n> "
"operations. Use 0 to disable. (default: %u)",
DEFAULT_ADDRMAN_CONSISTENCY_CHECKS),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-checkmempool=<n>",
strprintf("Run mempool consistency checks every <n> transactions. Use "
"0 to disable. (default: %u, regtest: %u)",
defaultChainParams->DefaultConsistencyChecks(),
regtestChainParams->DefaultConsistencyChecks()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkpoints",
strprintf("Only accept block chain matching built-in "
"checkpoints (default: %d)",
DEFAULT_CHECKPOINTS_ENABLED),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-deprecatedrpc=<method>",
"Allows deprecated RPC method(s) to be used",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-stopafterblockimport",
strprintf("Stop running after importing blocks from disk (default: %d)",
DEFAULT_STOPAFTERBLOCKIMPORT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-stopatheight",
strprintf("Stop running after reaching the given height in "
"the main chain (default: %u)",
DEFAULT_STOPATHEIGHT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-addrmantest", "Allows to test address relay on localhost",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-capturemessages", "Capture all P2P messages to disk",
ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mocktime=<n>",
"Replace actual time with " + UNIX_EPOCH_TIME +
" (default: 0)",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-maxsigcachesize=<n>",
strprintf("Limit size of signature cache to <n> MiB (default: %u)",
DEFAULT_MAX_SIG_CACHE_BYTES >> 20),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-maxscriptcachesize=<n>",
strprintf("Limit size of script cache to <n> MiB (default: %u)",
DEFAULT_MAX_SCRIPT_CACHE_BYTES >> 20),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxtipage=<n>",
strprintf("Maximum tip age in seconds to consider node in "
"initial block download (default: %u)",
Ticks<std::chrono::seconds>(DEFAULT_MAX_TIP_AGE)),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uacomment=<cmt>",
"Append comment to the user agent string",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uaclientname=<clientname>", "Set user agent client name",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uaclientversion=<clientversion>",
"Set user agent client version", ArgsManager::ALLOW_ANY,
OptionsCategory::DEBUG_TEST);
SetupChainParamsBaseOptions(argsman);
argsman.AddArg(
"-acceptnonstdtxn",
strprintf(
"Relay and mine \"non-standard\" transactions (%sdefault: %u)",
"testnet/regtest only; ", defaultChainParams->RequireStandard()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
argsman.AddArg("-excessiveblocksize=<n>",
strprintf("Do not accept blocks larger than this limit, in "
"bytes (default: %d)",
DEFAULT_MAX_BLOCK_SIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
const auto &ticker = Currency::get().ticker;
argsman.AddArg(
"-dustrelayfee=<amt>",
strprintf("Fee rate (in %s/kB) used to define dust, the value of an "
"output such that it will cost about 1/3 of its value in "
"fees at this fee rate to spend it. (default: %s)",
ticker, FormatMoney(DUST_RELAY_TX_FEE)),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-bytespersigcheck",
strprintf("Equivalent bytes per sigCheck in transactions for relay and "
"mining (default: %u).",
DEFAULT_BYTES_PER_SIGCHECK),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-bytespersigop",
strprintf("DEPRECATED: Equivalent bytes per sigCheck in transactions "
"for relay and mining (default: %u). This has been "
"deprecated since v0.26.8 and will be removed in the future, "
"please use -bytespersigcheck instead.",
DEFAULT_BYTES_PER_SIGCHECK),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-datacarrier",
strprintf("Relay and mine data carrier transactions (default: %d)",
DEFAULT_ACCEPT_DATACARRIER),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-datacarriersize",
strprintf("Maximum size of data in data carrier transactions "
"we relay and mine (default: %u)",
MAX_OP_RETURN_RELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-minrelaytxfee=<amt>",
strprintf("Fees (in %s/kB) smaller than this are rejected for "
"relaying, mining and transaction creation (default: %s)",
ticker, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB)),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-whitelistrelay",
strprintf("Add 'relay' permission to whitelisted peers "
"with default permissions. This will accept relayed "
"transactions even when not relaying transactions "
"(default: %d)",
DEFAULT_WHITELISTRELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-whitelistforcerelay",
strprintf("Add 'forcerelay' permission to whitelisted peers "
"with default permissions. This will relay transactions "
"even if the transactions were already in the mempool "
"(default: %d)",
DEFAULT_WHITELISTFORCERELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-blockmaxsize=<n>",
strprintf("Set maximum block size in bytes (default: %d)",
DEFAULT_MAX_GENERATED_BLOCK_SIZE),
ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
argsman.AddArg(
"-blockmintxfee=<amt>",
strprintf("Set lowest fee rate (in %s/kB) for transactions to "
"be included in block creation. (default: %s)",
ticker, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)),
ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-simplegbt",
"Use a simplified getblocktemplate output (default: 0)",
ArgsManager::ALLOW_BOOL, OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-blockversion=<n>",
"Override block version to test forking scenarios",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-server", "Accept command line and JSON-RPC commands",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rest",
strprintf("Accept public REST requests (default: %d)",
DEFAULT_REST_ENABLE),
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcbind=<addr>[:port]",
"Bind to given address to listen for JSON-RPC connections. Do not "
"expose the RPC server to untrusted networks such as the public "
"internet! This option is ignored unless -rpcallowip is also passed. "
"Port is optional and overrides -rpcport. Use [host]:port notation "
"for IPv6. This option can be specified multiple times (default: "
"127.0.0.1 and ::1 i.e., localhost)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY |
ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg(
"-rpcdoccheck",
strprintf("Throw a non-fatal error at runtime if the documentation for "
"an RPC is incorrect (default: %u)",
DEFAULT_RPC_DOC_CHECK),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg(
"-rpccookiefile=<loc>",
"Location of the auth cookie. Relative paths will be prefixed "
"by a net-specific datadir location. (default: data dir)",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg(
"-rpcwhitelist=<whitelist>",
"Set a whitelist to filter incoming RPC calls for a specific user. The "
"field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc "
"2>,...,<rpc n>. If multiple whitelists are set for a given user, they "
"are set-intersected. See -rpcwhitelistdefault documentation for "
"information on default whitelist behavior.",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcwhitelistdefault",
"Sets default behavior for rpc whitelisting. Unless "
"rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc "
"server acts as if all rpc users are subject to "
"empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault "
"is set to 1 and no -rpcwhitelist is set, rpc server acts as if all "
"rpc users are subject to empty whitelists.",
ArgsManager::ALLOW_BOOL, OptionsCategory::RPC);
argsman.AddArg(
"-rpcauth=<userpw>",
"Username and HMAC-SHA-256 hashed password for JSON-RPC connections. "
"The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A "
"canonical python script is included in share/rpcauth. The client then "
"connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> "
"pair of arguments. This option can be specified multiple times",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcport=<port>",
strprintf("Listen for JSON-RPC connections on <port> "
"(default: %u, testnet: %u, regtest: %u)",
defaultBaseParams->RPCPort(),
testnetBaseParams->RPCPort(),
regtestBaseParams->RPCPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::RPC);
argsman.AddArg(
"-rpcallowip=<ip>",
"Allow JSON-RPC connections from specified source. Valid for "
"<ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. "
"1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). "
"This option can be specified multiple times",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcthreads=<n>",
strprintf(
"Set the number of threads to service RPC calls (default: %d)",
DEFAULT_HTTP_THREADS),
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpccorsdomain=value",
"Domain from which to accept cross origin requests (browser enforced)",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcworkqueue=<n>",
strprintf("Set the depth of the work queue to service RPC "
"calls (default: %d)",
DEFAULT_HTTP_WORKQUEUE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::RPC);
argsman.AddArg("-rpcservertimeout=<n>",
strprintf("Timeout during HTTP requests (default: %d)",
DEFAULT_HTTP_SERVER_TIMEOUT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::RPC);
#if HAVE_DECL_FORK
argsman.AddArg("-daemon",
strprintf("Run in the background as a daemon and accept "
"commands (default: %d)",
DEFAULT_DAEMON),
ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
argsman.AddArg("-daemonwait",
strprintf("Wait for initialization to be finished before "
"exiting. This implies -daemon (default: %d)",
DEFAULT_DAEMONWAIT),
ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-daemon");
hidden_args.emplace_back("-daemonwait");
#endif
// Avalanche options.
argsman.AddArg("-avalanche",
strprintf("Enable the avalanche feature (default: %u)",
AVALANCHE_DEFAULT_ENABLED),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avalanchestakingrewards",
strprintf("Enable the avalanche staking rewards feature (default: %u, "
"testnet: %u, regtest: %u)",
defaultChainParams->GetConsensus().enableStakingRewards,
testnetChainParams->GetConsensus().enableStakingRewards,
regtestChainParams->GetConsensus().enableStakingRewards),
ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
argsman.AddArg("-avalancheconflictingproofcooldown",
strprintf("Mandatory cooldown before a proof conflicting "
"with an already registered one can be considered "
"in seconds (default: %u)",
AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg("-avalanchepeerreplacementcooldown",
strprintf("Mandatory cooldown before a peer can be replaced "
"in seconds (default: %u)",
AVALANCHE_DEFAULT_PEER_REPLACEMENT_COOLDOWN),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avaminquorumstake",
strprintf(
"Minimum amount of known stake for a usable quorum (default: %s)",
FormatMoney(AVALANCHE_DEFAULT_MIN_QUORUM_STAKE)),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avaminquorumconnectedstakeratio",
strprintf("Minimum proportion of known stake we"
" need nodes for to have a usable quorum (default: %s). "
"This parameter is parsed with a maximum precision of "
"0.000001.",
AVALANCHE_DEFAULT_MIN_QUORUM_CONNECTED_STAKE_RATIO),
ArgsManager::ALLOW_STRING, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avaminavaproofsnodecount",
strprintf("Minimum number of node that needs to send us an avaproofs"
" message before we consider we have a usable quorum"
" (default: %s)",
AVALANCHE_DEFAULT_MIN_AVAPROOFS_NODE_COUNT),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avastalevotethreshold",
strprintf("Number of avalanche votes before a voted item goes stale "
"when voting confidence is low (default: %u)",
AVALANCHE_VOTE_STALE_THRESHOLD),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avastalevotefactor",
strprintf(
"Factor affecting the number of avalanche votes before a voted "
"item goes stale when voting confidence is high (default: %u)",
AVALANCHE_VOTE_STALE_FACTOR),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg("-avacooldown",
strprintf("Mandatory cooldown between two avapoll in "
"milliseconds (default: %u)",
AVALANCHE_DEFAULT_COOLDOWN),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avatimeout",
strprintf("Avalanche query timeout in milliseconds (default: %u)",
AVALANCHE_DEFAULT_QUERY_TIMEOUT.count()),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avadelegation",
"Avalanche proof delegation to the master key used by this node "
"(default: none). Should be used in conjunction with -avaproof and "
"-avamasterkey",
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg("-avaproof",
"Avalanche proof to be used by this node (default: none)",
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avaproofstakeutxoconfirmations",
strprintf(
"Minimum number of confirmations before a stake utxo is mature"
" enough to be included into a proof. Utxos in the mempool are not "
"accepted (i.e this value must be greater than 0) (default: %s)",
AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS),
ArgsManager::ALLOW_INT, OptionsCategory::HIDDEN);
argsman.AddArg("-avaproofstakeutxodustthreshold",
strprintf("Minimum value each stake utxo must have to be "
"considered valid (default: %s)",
avalanche::PROOF_DUST_THRESHOLD),
ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
argsman.AddArg("-avamasterkey",
"Master key associated with the proof. If a proof is "
"required, this is mandatory.",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::AVALANCHE);
argsman.AddArg("-avasessionkey", "Avalanche session key (default: random)",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::HIDDEN);
argsman.AddArg("-enablertt",
strprintf("Whether to enforce Real Time Targeting via "
"Avalanche, default (%u)",
DEFAULT_ENABLE_RTT),
ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-maxavalancheoutbound",
strprintf(
"Set the maximum number of avalanche outbound peers to connect to. "
"Note that this option takes precedence over the -maxconnections "
"option (default: %u).",
DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-persistavapeers",
strprintf("Whether to save the avalanche peers upon shutdown and load "
"them upon startup (default: %u).",
DEFAULT_PERSIST_AVAPEERS),
ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
hidden_args.emplace_back("-avalanchepreconsensus");
hidden_args.emplace_back("-avalanchestakingpreconsensus");
// Add the hidden options
argsman.AddHiddenArgs(hidden_args);
}
static bool fHaveGenesis = false;
static GlobalMutex g_genesis_wait_mutex;
static std::condition_variable g_genesis_wait_cv;
static void BlockNotifyGenesisWait(const CBlockIndex *pBlockIndex) {
if (pBlockIndex != nullptr) {
{
LOCK(g_genesis_wait_mutex);
fHaveGenesis = true;
}
g_genesis_wait_cv.notify_all();
}
}
#if HAVE_SYSTEM
static void StartupNotify(const ArgsManager &args) {
std::string cmd = args.GetArg("-startupnotify", "");
if (!cmd.empty()) {
std::thread t(runCommand, cmd);
// thread runs free
t.detach();
}
}
#endif
static bool AppInitServers(Config &config,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
NodeContext &node) {
const ArgsManager &args = *Assert(node.args);
RPCServerSignals::OnStarted(&OnRPCStarted);
RPCServerSignals::OnStopped(&OnRPCStopped);
if (!InitHTTPServer(config)) {
return false;
}
StartRPC();
node.rpc_interruption_point = RpcInterruptionPoint;
if (!StartHTTPRPC(httpRPCRequestProcessor)) {
return false;
}
if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) {
StartREST(&node);
}
StartHTTPServer();
return true;
}
// Parameter interaction based on rules
void InitParameterInteraction(ArgsManager &args) {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified.
if (args.IsArgSet("-bind")) {
if (args.SoftSetBoolArg("-listen", true)) {
LogPrintf(
"%s: parameter interaction: -bind set -> setting -listen=1\n",
__func__);
}
}
if (args.IsArgSet("-whitebind")) {
if (args.SoftSetBoolArg("-listen", true)) {
LogPrintf("%s: parameter interaction: -whitebind set -> setting "
"-listen=1\n",
__func__);
}
}
if (args.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen
// by default.
if (args.SoftSetBoolArg("-dnsseed", false)) {
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-dnsseed=0\n",
__func__);
}
if (args.SoftSetBoolArg("-listen", false)) {
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-listen=0\n",
__func__);
}
}
if (args.IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy
// server is specified.
if (args.SoftSetBoolArg("-listen", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -listen=0\n",
__func__);
}
// to protect privacy, do not map ports when a proxy is set. The user
// may still specify -listen=1 to listen locally, so don't rely on this
// happening through -listen below.
if (args.SoftSetBoolArg("-upnp", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -upnp=0\n",
__func__);
}
if (args.SoftSetBoolArg("-natpmp", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -natpmp=0\n",
__func__);
}
// to protect privacy, do not discover addresses by default
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf("%s: parameter interaction: -proxy set -> setting "
"-discover=0\n",
__func__);
}
}
if (!args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening
// (pointless)
if (args.SoftSetBoolArg("-upnp", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -upnp=0\n",
__func__);
}
if (args.SoftSetBoolArg("-natpmp", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -natpmp=0\n",
__func__);
}
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -discover=0\n",
__func__);
}
if (args.SoftSetBoolArg("-listenonion", false)) {
LogPrintf("%s: parameter interaction: -listen=0 -> setting "
"-listenonion=0\n",
__func__);
}
if (args.SoftSetBoolArg("-i2pacceptincoming", false)) {
LogPrintf("%s: parameter interaction: -listen=0 -> setting "
"-i2pacceptincoming=0\n",
__func__);
}
}
if (args.IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf("%s: parameter interaction: -externalip set -> setting "
"-discover=0\n",
__func__);
}
}
// disable whitelistrelay in blocksonly mode
if (args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (args.SoftSetBoolArg("-whitelistrelay", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting "
"-whitelistrelay=0\n",
__func__);
}
}
// Forcing relay from whitelisted hosts implies we will accept relays from
// them in the first place.
if (args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (args.SoftSetBoolArg("-whitelistrelay", true)) {
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> "
"setting -whitelistrelay=1\n",
__func__);
}
}
// If avalanche is set, soft set all the feature flags accordingly.
if (args.IsArgSet("-avalanche")) {
const bool fAvalanche =
args.GetBoolArg("-avalanche", AVALANCHE_DEFAULT_ENABLED);
args.SoftSetBoolArg("-automaticunparking", !fAvalanche);
}
}
/**
* Initialize global loggers.
*
* Note that this is called very early in the process lifetime, so you should be
* careful about what global state you rely on here.
*/
void InitLogging(const ArgsManager &args) {
init::SetLoggingOptions(args);
init::LogPackageVersion();
}
namespace { // Variables internal to initialization process only
int nMaxConnections;
int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK_LIMITED);
int64_t peer_connect_timeout;
std::set<BlockFilterType> g_enabled_filter_types;
} // namespace
[[noreturn]] static void new_handler_terminate() {
// Rather than throwing std::bad-alloc if allocation fails, terminate
// immediately to (try to) avoid chain corruption. Since LogPrintf may
// itself allocate memory, set the handler directly to terminate first.
std::set_new_handler(std::terminate);
LogPrintf("Error: Out of memory. Terminating.\n");
// The log was successful, terminate now.
std::terminate();
};
bool AppInitBasicSetup(const ArgsManager &args) {
// Step 1: setup
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, 0, 0));
// Disable confusing "helpful" text message on abort, Ctrl-C
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
#ifdef WIN32
// Enable Data Execution Prevention (DEP)
SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
#endif
if (!InitShutdownState()) {
return InitError(
Untranslated("Initializing wait-for-shutdown state failed."));
}
if (!SetupNetworking()) {
return InitError(Untranslated("Initializing networking failed"));
}
#ifndef WIN32
if (!args.GetBoolArg("-sysperms", false)) {
umask(077);
}
// Clean shutdown on SIGTERM
registerSignalHandler(SIGTERM, HandleSIGTERM);
registerSignalHandler(SIGINT, HandleSIGTERM);
// Reopen debug.log on SIGHUP
registerSignalHandler(SIGHUP, HandleSIGHUP);
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client
// closes unexpectedly
signal(SIGPIPE, SIG_IGN);
#else
SetConsoleCtrlHandler(consoleCtrlHandler, true);
#endif
std::set_new_handler(new_handler_terminate);
return true;
}
bool AppInitParameterInteraction(Config &config, const ArgsManager &args) {
const CChainParams &chainparams = config.GetChainParams();
// Step 2: parameter interactions
// also see: InitParameterInteraction()
// Error if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this chain's section of the config file.
ChainType chain = args.GetChainType();
bilingual_str errors;
for (const auto &arg : args.GetUnsuitableSectionOnlyArgs()) {
errors +=
strprintf(_("Config setting for %s only applied on %s "
"network when in [%s] section.") +
Untranslated("\n"),
arg, ChainTypeToString(chain), ChainTypeToString(chain));
}
if (!errors.empty()) {
return InitError(errors);
}
// Warn if unrecognized section name are present in the config file.
bilingual_str warnings;
for (const auto &section : args.GetUnrecognizedSections()) {
warnings += strprintf(Untranslated("%s:%i ") +
_("Section [%s] is not recognized.") +
Untranslated("\n"),
section.m_file, section.m_line, section.m_name);
}
if (!warnings.empty()) {
InitWarning(warnings);
}
if (!fs::is_directory(args.GetBlocksDirPath())) {
return InitError(
strprintf(_("Specified blocks directory \"%s\" does not exist."),
args.GetArg("-blocksdir", "")));
}
// parse and validate enabled filter types
std::string blockfilterindex_value =
args.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
g_enabled_filter_types = AllBlockFilterTypes();
} else if (blockfilterindex_value != "0") {
const std::vector<std::string> names =
args.GetArgs("-blockfilterindex");
for (const auto &name : names) {
BlockFilterType filter_type;
if (!BlockFilterTypeByName(name, filter_type)) {
return InitError(
strprintf(_("Unknown -blockfilterindex value %s."), name));
}
g_enabled_filter_types.insert(filter_type);
}
}
// Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index
// are both enabled.
if (args.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) {
if (g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
return InitError(
_("Cannot set -peerblockfilters without -blockfilterindex."));
}
nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
}
if (args.GetIntArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
return InitError(_("Prune mode is incompatible with -txindex."));
}
if (args.GetBoolArg("-reindex-chainstate", false)) {
return InitError(
_("Prune mode is incompatible with -reindex-chainstate. Use "
"full -reindex instead."));
}
if (args.GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
return InitError(_("Prune mode is incompatible with -chronik."));
}
}
// -bind and -whitebind can't be set when not listening
size_t nUserBind =
args.GetArgs("-bind").size() + args.GetArgs("-whitebind").size();
if (nUserBind != 0 && !args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError(Untranslated(
"Cannot set -bind or -whitebind together with -listen=0"));
}
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
nUserMaxConnections =
args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
// -maxavalancheoutbound takes precedence over -maxconnections
const int maxAvalancheOutbound = args.GetIntArg(
"-maxavalancheoutbound", DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS);
const bool fAvalanche =
args.GetBoolArg("-avalanche", AVALANCHE_DEFAULT_ENABLED);
if (fAvalanche && maxAvalancheOutbound > nMaxConnections) {
nMaxConnections = std::max(maxAvalancheOutbound, nMaxConnections);
// Indicate the value set by the user
LogPrintf("Increasing -maxconnections from %d to %d to comply with "
"-maxavalancheoutbound\n",
nUserMaxConnections, nMaxConnections);
}
// Trim requested connection counts, to fit into system limitations
// <int> in std::min<int>(...) to work around FreeBSD compilation issue
// described in #2695
nFD = RaiseFileDescriptorLimit(
nMaxConnections + nBind + MIN_CORE_FILEDESCRIPTORS +
MAX_ADDNODE_CONNECTIONS + NUM_FDS_MESSAGE_CAPTURE);
#ifdef USE_POLL
int fd_max = nFD;
#else
int fd_max = FD_SETSIZE;
#endif
nMaxConnections = std::max(
std::min<int>(nMaxConnections,
fd_max - nBind - MIN_CORE_FILEDESCRIPTORS -
MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE),
0);
if (nFD < MIN_CORE_FILEDESCRIPTORS) {
return InitError(_("Not enough file descriptors available."));
}
nMaxConnections =
std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS,
nMaxConnections);
if (nMaxConnections < nUserMaxConnections) {
// Not categorizing as "Warning" because this is the normal behavior for
// platforms using the select() interface for which FD_SETSIZE is
// usually 1024.
LogPrintf("Reducing -maxconnections from %d to %d, because of system "
"limitations.\n",
nUserMaxConnections, nMaxConnections);
}
// Step 3: parameter-to-internal-flags
init::SetLoggingCategories(args);
// Configure excessive block size.
const int64_t nProposedExcessiveBlockSize =
args.GetIntArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE);
if (nProposedExcessiveBlockSize <= 0 ||
!config.SetMaxBlockSize(nProposedExcessiveBlockSize)) {
return InitError(
_("Excessive block size must be > 1,000,000 bytes (1MB)"));
}
// Check blockmaxsize does not exceed maximum accepted block size.
const int64_t nProposedMaxGeneratedBlockSize =
args.GetIntArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
if (nProposedMaxGeneratedBlockSize <= 0) {
return InitError(_("Max generated block size must be greater than 0"));
}
if (uint64_t(nProposedMaxGeneratedBlockSize) > config.GetMaxBlockSize()) {
return InitError(_("Max generated block size (blockmaxsize) cannot "
"exceed the excessive block size "
"(excessiveblocksize)"));
}
nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
peer_connect_timeout =
args.GetIntArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
return InitError(Untranslated(
"peertimeout cannot be configured with a negative value."));
}
// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that
// happens.
if (args.IsArgSet("-blockmintxfee")) {
Amount n = Amount::zero();
if (!ParseMoney(args.GetArg("-blockmintxfee", ""), n)) {
return InitError(AmountErrMsg("blockmintxfee",
args.GetArg("-blockmintxfee", "")));
}
}
nBytesPerSigCheck =
args.IsArgSet("-bytespersigcheck")
? args.GetIntArg("-bytespersigcheck", nBytesPerSigCheck)
: args.GetIntArg("-bytespersigop", nBytesPerSigCheck);
if (!g_wallet_init_interface.ParameterInteraction()) {
return false;
}
// Option to startup with mocktime set (used for regression testing):
SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) {
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
}
if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
return InitError(_(
"No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
}
// Avalanche parameters
const int64_t stakeUtxoMinConfirmations =
args.GetIntArg("-avaproofstakeutxoconfirmations",
AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS);
if (!chainparams.IsTestChain() &&
stakeUtxoMinConfirmations !=
AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS) {
return InitError(_("Avalanche stake UTXO minimum confirmations can "
"only be set on test chains."));
}
if (stakeUtxoMinConfirmations <= 0) {
return InitError(_("Avalanche stake UTXO minimum confirmations must be "
"a positive integer."));
}
if (args.IsArgSet("-avaproofstakeutxodustthreshold")) {
Amount amount = Amount::zero();
auto parsed = ParseMoney(
args.GetArg("-avaproofstakeutxodustthreshold", ""), amount);
if (!parsed || Amount::zero() == amount) {
return InitError(AmountErrMsg(
"avaproofstakeutxodustthreshold",
args.GetArg("-avaproofstakeutxodustthreshold", "")));
}
if (!chainparams.IsTestChain() &&
amount != avalanche::PROOF_DUST_THRESHOLD) {
return InitError(_("Avalanche stake UTXO dust threshold can "
"only be set on test chains."));
}
}
// This is a staking node
if (fAvalanche && args.IsArgSet("-avaproof")) {
if (!args.GetBoolArg("-listen", true)) {
return InitError(_("Running a staking node requires accepting "
"inbound connections. Please enable -listen."));
}
if (args.IsArgSet("-proxy")) {
return InitError(_("Running a staking node behind a proxy is not "
"supported. Please disable -proxy."));
}
if (args.IsArgSet("-i2psam")) {
return InitError(_("Running a staking node behind I2P is not "
"supported. Please disable -i2psam."));
}
if (args.IsArgSet("-onlynet")) {
return InitError(
_("Restricting the outbound network is not supported when "
"running a staking node. Please disable -onlynet."));
}
}
// Also report errors from parsing before daemonization
{
KernelNotifications notifications{};
ChainstateManager::Options chainman_opts_dummy{
.config = config,
.datadir = args.GetDataDirNet(),
.notifications = notifications,
};
if (const auto error{ApplyArgsManOptions(args, chainman_opts_dummy)}) {
return InitError(*error);
}
BlockManager::Options blockman_opts_dummy{
.chainparams = chainman_opts_dummy.config.GetChainParams(),
.blocks_dir = args.GetBlocksDirPath(),
};
if (const auto error{ApplyArgsManOptions(args, blockman_opts_dummy)}) {
return InitError(*error);
}
}
return true;
}
static bool LockDataDirectory(bool probeOnly) {
// Make sure only a single Bitcoin process is using the data directory.
fs::path datadir = gArgs.GetDataDirNet();
if (!DirIsWritable(datadir)) {
return InitError(strprintf(
_("Cannot write to data directory '%s'; check permissions."),
fs::PathToString(datadir)));
}
if (!LockDirectory(datadir, ".lock", probeOnly)) {
return InitError(strprintf(_("Cannot obtain a lock on data directory "
"%s. %s is probably already running."),
fs::PathToString(datadir), PACKAGE_NAME));
}
return true;
}
bool AppInitSanityChecks(const kernel::Context &kernel) {
// Step 4: sanity checks
auto result{kernel::SanityChecks(kernel)};
if (!result) {
InitError(util::ErrorString(result));
return InitError(strprintf(
_("Initialization sanity check failed. %s is shutting down."),
PACKAGE_NAME));
}
// Probe the data directory lock to give an early error message, if possible
// We cannot hold the data directory lock here, as the forking for daemon()
// hasn't yet happened, and a fork will cause weird behavior to it.
return LockDataDirectory(true);
}
bool AppInitLockDataDirectory() {
// After daemonization get the data directory lock again and hold on to it
// until exit. This creates a slight window for a race condition to happen,
// however this condition is harmless: it will at most make us exit without
// printing a message to console.
if (!LockDataDirectory(false)) {
// Detailed error printed inside LockDataDirectory
return false;
}
return true;
}
bool AppInitInterfaces(NodeContext &node) {
node.chain = interfaces::MakeChain(node, Params());
// Create client interfaces for wallets that are supposed to be loaded
// according to -wallet and -disablewallet options. This only constructs
// the interfaces, it doesn't load wallet data. Wallets actually get loaded
// when load() and start() interface methods are called below.
g_wallet_init_interface.Construct(node);
return true;
}
bool AppInitMain(Config &config, RPCServer &rpcServer,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
NodeContext &node,
interfaces::BlockAndHeaderTipInfo *tip_info) {
// Step 4a: application initialization
const ArgsManager &args = *Assert(node.args);
const CChainParams &chainparams = config.GetChainParams();
if (!CreatePidFile(args)) {
// Detailed error printed inside CreatePidFile().
return false;
}
if (!init::StartLogging(args)) {
// Detailed error printed inside StartLogging().
return false;
}
LogPrintf("Using at most %i automatic connections (%i file descriptors "
"available)\n",
nMaxConnections, nFD);
// Warn about relative -datadir path.
if (args.IsArgSet("-datadir") &&
!args.GetPathArg("-datadir").is_absolute()) {
LogPrintf("Warning: relative datadir option '%s' specified, which will "
"be interpreted relative to the current working directory "
"'%s'. This is fragile, because if bitcoin is started in the "
"future from a different location, it will be unable to "
"locate the current data files. There could also be data "
"loss if bitcoin is started while in a temporary "
"directory.\n",
args.GetArg("-datadir", ""),
fs::PathToString(fs::current_path()));
}
ValidationCacheSizes validation_cache_sizes{};
ApplyArgsManOptions(args, validation_cache_sizes);
if (!InitSignatureCache(validation_cache_sizes.signature_cache_bytes)) {
return InitError(strprintf(
_("Unable to allocate memory for -maxsigcachesize: '%s' MiB"),
args.GetIntArg("-maxsigcachesize",
DEFAULT_MAX_SIG_CACHE_BYTES >> 20)));
}
if (!InitScriptExecutionCache(
validation_cache_sizes.script_execution_cache_bytes)) {
return InitError(strprintf(
_("Unable to allocate memory for -maxscriptcachesize: '%s' MiB"),
args.GetIntArg("-maxscriptcachesize",
DEFAULT_MAX_SCRIPT_CACHE_BYTES >> 20)));
}
int script_threads = args.GetIntArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (script_threads <= 0) {
// -par=0 means autodetect (number of cores - 1 script threads)
// -par=-n means "leave n cores free" (number of cores - n - 1 script
// threads)
script_threads += GetNumCores();
}
// Subtract 1 because the main thread counts towards the par threads
script_threads = std::max(script_threads - 1, 0);
// Number of script-checking threads <= MAX_SCRIPTCHECK_THREADS
script_threads = std::min(script_threads, MAX_SCRIPTCHECK_THREADS);
LogPrintf("Script verification uses %d additional threads\n",
script_threads);
if (script_threads >= 1) {
StartScriptCheckWorkerThreads(script_threads);
}
assert(!node.scheduler);
node.scheduler = std::make_unique<CScheduler>();
// Start the lightweight task scheduler thread
node.scheduler->m_service_thread =
std::thread(&util::TraceThread, "scheduler",
[&] { node.scheduler->serviceQueue(); });
// Gather some entropy once per minute.
node.scheduler->scheduleEvery(
[] {
RandAddPeriodic();
return true;
},
std::chrono::minutes{1});
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
/**
* Register RPC commands regardless of -server setting so they will be
* available in the GUI RPC console even if external calls are disabled.
*/
RegisterAllRPCCommands(config, rpcServer, tableRPC);
for (const auto &client : node.chain_clients) {
client->registerRpcs();
}
#if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC);
#endif
/**
* Start the RPC server. It will be started in "warmup" mode and not
* process calls yet (but it will verify that the server is there and will
* be ready later). Warmup mode will be completed when initialisation is
* finished.
*/
if (args.GetBoolArg("-server", false)) {
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
if (!AppInitServers(config, httpRPCRequestProcessor, node)) {
return InitError(
_("Unable to start HTTP server. See debug log for details."));
}
}
// Step 5: verify wallet database integrity
for (const auto &client : node.chain_clients) {
if (!client->verify()) {
return false;
}
}
// Step 6: network initialization
// Note that we absolutely cannot open any actual connections
// until the very end ("start node") as the UTXO/block state
// is not yet setup and may end up being set up twice if we
// need to reindex later.
fListen = args.GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = args.GetBoolArg("-discover", true);
{
// Initialize addrman
assert(!node.addrman);
// Read asmap file if configured
std::vector<bool> asmap;
if (args.IsArgSet("-asmap")) {
fs::path asmap_path =
args.GetPathArg("-asmap", DEFAULT_ASMAP_FILENAME);
if (!asmap_path.is_absolute()) {
asmap_path = args.GetDataDirNet() / asmap_path;
}
if (!fs::exists(asmap_path)) {
InitError(strprintf(_("Could not find asmap file %s"),
fs::quoted(fs::PathToString(asmap_path))));
return false;
}
asmap = DecodeAsmap(asmap_path);
if (asmap.size() == 0) {
InitError(strprintf(_("Could not parse asmap file %s"),
fs::quoted(fs::PathToString(asmap_path))));
return false;
}
const uint256 asmap_version = (HashWriter{} << asmap).GetHash();
LogPrintf("Using asmap version %s for IP bucketing\n",
asmap_version.ToString());
} else {
LogPrintf("Using /16 prefix for IP bucketing\n");
}
uiInterface.InitMessage(_("Loading P2P addresses...").translated);
auto addrman{LoadAddrman(chainparams, asmap, args)};
if (!addrman) {
return InitError(util::ErrorString(addrman));
}
node.addrman = std::move(*addrman);
}
assert(!node.banman);
node.banman = std::make_unique<BanMan>(
args.GetDataDirNet() / "banlist.dat", config.GetChainParams(),
&uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = std::make_unique<CConnman>(
config, GetRand<uint64_t>(), GetRand<uint64_t>(), *node.addrman,
args.GetBoolArg("-networkactive", true));
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
for (const std::string &cmt : args.GetArgs("-uacomment")) {
if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) {
return InitError(strprintf(
_("User Agent comment (%s) contains unsafe characters."), cmt));
}
uacomments.push_back(cmt);
}
const std::string client_name = args.GetArg("-uaclientname", CLIENT_NAME);
const std::string client_version =
args.GetArg("-uaclientversion", FormatVersion(CLIENT_VERSION));
if (client_name != SanitizeString(client_name, SAFE_CHARS_UA_COMMENT)) {
return InitError(strprintf(
_("-uaclientname (%s) contains invalid characters."), client_name));
}
if (client_version !=
SanitizeString(client_version, SAFE_CHARS_UA_COMMENT)) {
return InitError(
strprintf(_("-uaclientversion (%s) contains invalid characters."),
client_version));
}
const std::string strSubVersion =
FormatUserAgent(client_name, client_version, uacomments);
if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
return InitError(strprintf(
_("Total length of network version string (%i) exceeds maximum "
"length (%i). Reduce the number or size of uacomments."),
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
if (args.IsArgSet("-onlynet")) {
std::set<enum Network> nets;
for (const std::string &snet : args.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE) {
return InitError(strprintf(
_("Unknown network specified in -onlynet: '%s'"), snet));
}
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (!nets.count(net)) {
SetReachable(net, false);
}
}
}
// Check for host lookup allowed before parsing any network related
// parameters
fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
bool proxyRandomize =
args.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set
// a proxy, this is the default
std::string proxyArg = args.GetArg("-proxy", "");
SetReachable(NET_ONION, false);
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) {
return InitError(strprintf(
_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
if (!addrProxy.IsValid()) {
return InitError(strprintf(
_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_ONION, addrProxy);
SetNameProxy(addrProxy);
// by default, -proxy sets onion as reachable, unless -noonion later
SetReachable(NET_ONION, true);
}
// -onion can be used to set only a proxy for .onion, or override normal
// proxy for .onion addresses.
// -noonion (or -onion=0) disables connecting to .onion entirely. An empty
// string is used to not override the onion proxy (in which case it defaults
// to -proxy set above, or none)
std::string onionArg = args.GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") {
// Handle -noonion/-onion=0
SetReachable(NET_ONION, false);
} else {
CService onionProxy;
if (!Lookup(onionArg, onionProxy, 9050, fNameLookup)) {
return InitError(strprintf(
_("Invalid -onion address or hostname: '%s'"), onionArg));
}
proxyType addrOnion = proxyType(onionProxy, proxyRandomize);
if (!addrOnion.IsValid()) {
return InitError(strprintf(
_("Invalid -onion address or hostname: '%s'"), onionArg));
}
SetProxy(NET_ONION, addrOnion);
SetReachable(NET_ONION, true);
}
}
for (const std::string &strAddr : args.GetArgs("-externalip")) {
CService addrLocal;
if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) &&
addrLocal.IsValid()) {
AddLocal(addrLocal, LOCAL_MANUAL);
} else {
return InitError(ResolveErrMsg("externalip", strAddr));
}
}
#if ENABLE_ZMQ
g_zmq_notification_interface = CZMQNotificationInterface::Create(
[&chainman = node.chainman](CBlock &block, const CBlockIndex &index) {
assert(chainman);
return chainman->m_blockman.ReadBlockFromDisk(block, index);
});
if (g_zmq_notification_interface) {
RegisterValidationInterface(g_zmq_notification_interface.get());
}
#endif
// Step 7: load block chain
node.notifications = std::make_unique<KernelNotifications>();
fReindex = args.GetBoolArg("-reindex", false);
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
ChainstateManager::Options chainman_opts{
.config = config,
.datadir = args.GetDataDirNet(),
.adjusted_time_callback = GetAdjustedTime,
.notifications = *node.notifications,
};
// no error can happen, already checked in AppInitParameterInteraction
Assert(!ApplyArgsManOptions(args, chainman_opts));
if (chainman_opts.checkpoints_enabled) {
LogPrintf("Checkpoints will be verified.\n");
} else {
LogPrintf("Skipping checkpoint verification.\n");
}
BlockManager::Options blockman_opts{
.chainparams = chainman_opts.config.GetChainParams(),
.blocks_dir = args.GetBlocksDirPath(),
};
// no error can happen, already checked in AppInitParameterInteraction
Assert(!ApplyArgsManOptions(args, blockman_opts));
// cache size calculations
CacheSizes cache_sizes =
CalculateCacheSizes(args, g_enabled_filter_types.size());
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1f MiB for block index database\n",
cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LogPrintf("* Using %.1f MiB for transaction index database\n",
cache_sizes.tx_index * (1.0 / 1024 / 1024));
}
for (BlockFilterType filter_type : g_enabled_filter_types) {
LogPrintf("* Using %.1f MiB for %s block filter index database\n",
cache_sizes.filter_index * (1.0 / 1024 / 1024),
BlockFilterTypeName(filter_type));
}
LogPrintf("* Using %.1f MiB for chain state database\n",
cache_sizes.coins_db * (1.0 / 1024 / 1024));
assert(!node.mempool);
assert(!node.chainman);
CTxMemPool::Options mempool_opts{
.check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0,
};
if (const auto err{ApplyArgsManOptions(args, chainparams, mempool_opts)}) {
return InitError(*err);
}
mempool_opts.check_ratio =
std::clamp<int>(mempool_opts.check_ratio, 0, 1'000'000);
// FIXME: this legacy limit comes from the DEFAULT_DESCENDANT_SIZE_LIMIT
// (101) that was enforced before the wellington activation. While it's
// still a good idea to have some minimum mempool size, using this value as
// a threshold is no longer relevant.
int64_t nMempoolSizeMin = 101 * 1000 * 40;
if (mempool_opts.max_size_bytes < 0 ||
(!chainparams.IsTestChain() &&
mempool_opts.max_size_bytes < nMempoolSizeMin)) {
return InitError(strprintf(_("-maxmempool must be at least %d MB"),
std::ceil(nMempoolSizeMin / 1000000.0)));
}
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of "
"unused mempool space)\n",
cache_sizes.coins * (1.0 / 1024 / 1024),
mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
node.mempool = std::make_unique<CTxMemPool>(config, mempool_opts);
node.chainman =
std::make_unique<ChainstateManager>(chainman_opts, blockman_opts);
ChainstateManager &chainman = *node.chainman;
// This is defined and set here instead of inline in validation.h to
// avoid a hard dependency between validation and index/base, since the
// latter is not in libbitcoinkernel.
chainman.snapshot_download_completed = [&node]() {
if (!node.chainman->m_blockman.IsPruneMode()) {
LogPrintf("[snapshot] re-enabling NODE_NETWORK services\n");
node.connman->AddLocalServices(NODE_NETWORK);
}
LogPrintf("[snapshot] restarting indexes\n");
// Drain the validation interface queue to ensure that the old
// indexes don't have any pending work.
SyncWithValidationInterfaceQueue();
for (auto *index : node.indexes) {
index->Interrupt();
index->Stop();
if (!(index->Init() && index->StartBackgroundSync())) {
LogPrintf("[snapshot] WARNING failed to restart index %s "
"on snapshot chain\n",
index->GetName());
}
}
};
node::ChainstateLoadOptions options;
options.mempool = Assert(node.mempool.get());
options.reindex = node::fReindex;
options.reindex_chainstate = fReindexChainState;
options.prune = chainman.m_blockman.IsPruneMode();
options.check_blocks =
args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
options.require_full_verification =
args.IsArgSet("-checkblocks") || args.IsArgSet("-checklevel");
options.check_interrupt = ShutdownRequested;
options.coins_error_cb = [] {
uiInterface.ThreadSafeMessageBox(
_("Error reading from database, shutting down."), "",
CClientUIInterface::MSG_ERROR);
};
uiInterface.InitMessage(_("Loading block index...").translated);
const int64_t load_block_index_start_time = GetTimeMillis();
auto catch_exceptions = [](auto &&f) {
try {
return f();
} catch (const std::exception &e) {
LogPrintf("%s\n", e.what());
return std::make_tuple(node::ChainstateLoadStatus::FAILURE,
_("Error opening block database"));
}
};
auto [status, error] = catch_exceptions(
[&] { return LoadChainstate(chainman, cache_sizes, options); });
if (status == node::ChainstateLoadStatus::SUCCESS) {
uiInterface.InitMessage(_("Verifying blocks...").translated);
if (chainman.m_blockman.m_have_pruned &&
options.check_blocks > MIN_BLOCKS_TO_KEEP) {
LogPrintfCategory(BCLog::PRUNE,
"pruned datadir may not have more than %d "
"blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
std::tie(status, error) = catch_exceptions(
[&] { return VerifyLoadedChainstate(chainman, options); });
if (status == node::ChainstateLoadStatus::SUCCESS) {
WITH_LOCK(cs_main, return node.chainman->LoadRecentHeadersTime(
node.chainman->m_options.datadir /
HEADERS_TIME_FILE_NAME));
fLoaded = true;
LogPrintf(" block index %15dms\n",
GetTimeMillis() - load_block_index_start_time);
}
}
if (status == node::ChainstateLoadStatus::FAILURE_FATAL ||
status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB ||
status ==
node::ChainstateLoadStatus::FAILURE_INSUFFICIENT_DBCACHE) {
return InitError(error);
}
if (!fLoaded && !ShutdownRequested()) {
// first suggest a reindex
if (!options.reindex) {
bool fRet = uiInterface.ThreadSafeQuestion(
error + Untranslated(".\n\n") +
_("Do you want to rebuild the block database now?"),
error.original + ".\nPlease restart with -reindex or "
"-reindex-chainstate to recover.",
"",
CClientUIInterface::MSG_ERROR |
CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
AbortShutdown();
} else {
LogPrintf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
return InitError(error);
}
}
}
// As LoadBlockIndex can take several minutes, it's possible the user
// requested to kill the GUI during the last operation. If so, exit.
// As the program has not fully started yet, Shutdown() is possibly
// overkill.
if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
ChainstateManager &chainman = *Assert(node.chainman);
if (args.GetBoolArg("-avalanche", AVALANCHE_DEFAULT_ENABLED)) {
// Initialize Avalanche.
bilingual_str avalancheError;
node.avalanche = avalanche::Processor::MakeProcessor(
args, *node.chain, node.connman.get(), chainman, node.mempool.get(),
*node.scheduler, avalancheError);
if (!node.avalanche) {
InitError(avalancheError);
return false;
}
if (node.avalanche->isAvalancheServiceAvailable()) {
nLocalServices = ServiceFlags(nLocalServices | NODE_AVALANCHE);
}
}
PeerManager::Options peerman_opts{};
ApplyArgsManOptions(args, peerman_opts);
assert(!node.peerman);
node.peerman = PeerManager::make(*node.connman, *node.addrman,
node.banman.get(), chainman, *node.mempool,
node.avalanche.get(), peerman_opts);
RegisterValidationInterface(node.peerman.get());
// Encoded addresses using cashaddr instead of base58.
// We do this by default to avoid confusion with BTC addresses.
config.SetCashAddrEncoding(args.GetBoolArg("-usecashaddr", true));
// Step 8: load indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
auto result{
WITH_LOCK(cs_main, return CheckLegacyTxindex(*Assert(
chainman.m_blockman.m_block_tree_db)))};
if (!result) {
return InitError(util::ErrorString(result));
}
g_txindex =
std::make_unique<TxIndex>(interfaces::MakeChain(node, Params()),
cache_sizes.tx_index, false, fReindex);
node.indexes.emplace_back(g_txindex.get());
}
for (const auto &filter_type : g_enabled_filter_types) {
InitBlockFilterIndex(
[&] { return interfaces::MakeChain(node, Params()); }, filter_type,
cache_sizes.filter_index, false, fReindex);
node.indexes.emplace_back(GetBlockFilterIndex(filter_type));
}
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
g_coin_stats_index = std::make_unique<CoinStatsIndex>(
interfaces::MakeChain(node, Params()), /* cache size */ 0, false,
fReindex);
node.indexes.emplace_back(g_coin_stats_index.get());
}
// Init indexes
for (auto index : node.indexes) {
if (!index->Init()) {
return false;
}
}
#if ENABLE_CHRONIK
if (args.GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
const bool fReindexChronik =
fReindex || args.GetBoolArg("-chronikreindex", false);
if (!chronik::Start(args, config, node, fReindexChronik)) {
return false;
}
}
#endif
// Step 9: load wallet
for (const auto &client : node.chain_clients) {
if (!client->load()) {
return false;
}
}
// Step 10: data directory maintenance
// if pruning, perform the initial blockstore prune
// after any wallet rescanning has taken place.
if (chainman.m_blockman.IsPruneMode()) {
if (!fReindex) {
LOCK(cs_main);
for (Chainstate *chainstate : chainman.GetAll()) {
uiInterface.InitMessage(_("Pruning blockstore...").translated);
chainstate->PruneAndFlush();
}
}
} else {
// Prior to setting NODE_NETWORK, check if we can provide historical
// blocks.
if (!WITH_LOCK(chainman.GetMutex(),
return chainman.BackgroundSyncInProgress())) {
LogPrintf("Setting NODE_NETWORK on non-prune mode\n");
nLocalServices = ServiceFlags(nLocalServices | NODE_NETWORK);
} else {
LogPrintf("Running node in NODE_NETWORK_LIMITED mode until "
"snapshot background sync completes\n");
}
}
// Step 11: import blocks
if (!CheckDiskSpace(args.GetDataDirNet())) {
InitError(
strprintf(_("Error: Disk space is low for %s"),
fs::quoted(fs::PathToString(args.GetDataDirNet()))));
return false;
}
if (!CheckDiskSpace(args.GetBlocksDirPath())) {
InitError(
strprintf(_("Error: Disk space is low for %s"),
fs::quoted(fs::PathToString(args.GetBlocksDirPath()))));
return false;
}
// Either install a handler to notify us when genesis activates, or set
// fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
if (WITH_LOCK(chainman.GetMutex(),
return chainman.ActiveChain().Tip() == nullptr)) {
block_notify_genesis_wait_connection =
uiInterface.NotifyBlockTip_connect(
std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
}
#if defined(HAVE_SYSTEM)
const std::string block_notify = args.GetArg("-blocknotify", "");
if (!block_notify.empty()) {
uiInterface.NotifyBlockTip_connect([block_notify](
SynchronizationState sync_state,
const CBlockIndex *pBlockIndex) {
if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) {
return;
}
std::string command = block_notify;
ReplaceAll(command, "%s", pBlockIndex->GetBlockHash().GetHex());
std::thread t(runCommand, command);
// thread runs free
t.detach();
});
}
#endif
std::vector<fs::path> vImportFiles;
for (const std::string &strFile : args.GetArgs("-loadblock")) {
vImportFiles.push_back(fs::PathFromString(strFile));
}
avalanche::Processor *const avalanche = node.avalanche.get();
chainman.m_thread_load = std::thread(
&util::TraceThread, "initload", [=, &chainman, &args, &node] {
// Import blocks
ImportBlocks(chainman, avalanche, vImportFiles);
// Start indexes initial sync
if (!StartIndexBackgroundSync(node)) {
bilingual_str err_str =
_("Failed to start indexes, shutting down..");
AbortNode(err_str.original, err_str);
// TODO: replace AbortNode call with following line after
// backporting core#27861
// chainman.GetNotifications().fatalError(err_str.original,
// err_str);
return;
}
// Load mempool from disk
chainman.ActiveChainstate().LoadMempool(
ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{});
});
// Wait for genesis block to be processed
{
WAIT_LOCK(g_genesis_wait_mutex, lock);
// We previously could hang here if StartShutdown() is called prior to
// ImportBlocks getting started, so instead we just wait on a timer to
// check ShutdownRequested() regularly.
while (!fHaveGenesis && !ShutdownRequested()) {
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
}
block_notify_genesis_wait_connection.disconnect();
}
if (ShutdownRequested()) {
return false;
}
// Step 12: start node
int chain_active_height;
//// debug print
{
LOCK(cs_main);
LogPrintf("block tree size = %u\n", chainman.BlockIndex().size());
chain_active_height = chainman.ActiveChain().Height();
if (tip_info) {
tip_info->block_height = chain_active_height;
tip_info->block_time =
chainman.ActiveChain().Tip()
? chainman.ActiveChain().Tip()->GetBlockTime()
: chainman.GetParams().GenesisBlock().GetBlockTime();
tip_info->verification_progress = GuessVerificationProgress(
chainman.GetParams().TxData(), chainman.ActiveChain().Tip());
}
if (tip_info && chainman.m_best_header) {
tip_info->header_height = chainman.m_best_header->nHeight;
tip_info->header_time = chainman.m_best_header->GetBlockTime();
}
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
if (node.peerman) {
node.peerman->SetBestHeight(chain_active_height);
}
// Map ports with UPnP or NAT-PMP.
StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP),
args.GetBoolArg("-natpmp", DEFAULT_NATPMP));
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
connOptions.nMaxConnections = nMaxConnections;
connOptions.m_max_avalanche_outbound =
node.avalanche
? args.GetIntArg("-maxavalancheoutbound",
DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS)
: 0;
connOptions.m_max_outbound_full_relay = std::min(
MAX_OUTBOUND_FULL_RELAY_CONNECTIONS,
connOptions.nMaxConnections - connOptions.m_max_avalanche_outbound);
connOptions.m_max_outbound_block_relay = std::min(
MAX_BLOCK_RELAY_ONLY_CONNECTIONS,
connOptions.nMaxConnections - connOptions.m_max_avalanche_outbound -
connOptions.m_max_outbound_full_relay);
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
connOptions.nMaxFeeler = MAX_FEELER_CONNECTIONS;
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
connOptions.m_msgproc.push_back(node.peerman.get());
if (node.avalanche) {
connOptions.m_msgproc.push_back(node.avalanche.get());
}
connOptions.nSendBufferMaxSize =
1000 * args.GetIntArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize =
1000 * args.GetIntArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = args.GetArgs("-addnode");
connOptions.nMaxOutboundLimit =
1024 * 1024 *
args.GetIntArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
connOptions.m_peer_connect_timeout = peer_connect_timeout;
connOptions.whitelist_forcerelay =
args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY);
connOptions.whitelist_relay =
args.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY);
// Port to bind to if `-bind=addr` is provided without a `:port` suffix.
const uint16_t default_bind_port = static_cast<uint16_t>(
args.GetIntArg("-port", config.GetChainParams().GetDefaultPort()));
const auto BadPortWarning = [](const char *prefix, uint16_t port) {
return strprintf(_("%s request to listen on port %u. This port is "
"considered \"bad\" and "
"thus it is unlikely that any Bitcoin ABC peers "
"connect to it. See "
"doc/p2p-bad-ports.md for details and a full list."),
prefix, port);
};
for (const std::string &bind_arg : args.GetArgs("-bind")) {
CService bind_addr;
const size_t index = bind_arg.rfind('=');
if (index == std::string::npos) {
if (Lookup(bind_arg, bind_addr, default_bind_port,
/*fAllowLookup=*/false)) {
connOptions.vBinds.push_back(bind_addr);
if (IsBadPort(bind_addr.GetPort())) {
InitWarning(BadPortWarning("-bind", bind_addr.GetPort()));
}
continue;
}
} else {
const std::string network_type = bind_arg.substr(index + 1);
if (network_type == "onion") {
const std::string truncated_bind_arg =
bind_arg.substr(0, index);
if (Lookup(truncated_bind_arg, bind_addr,
BaseParams().OnionServiceTargetPort(), false)) {
connOptions.onion_binds.push_back(bind_addr);
continue;
}
}
}
return InitError(ResolveErrMsg("bind", bind_arg));
}
for (const std::string &strBind : args.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
bilingual_str error;
if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) {
return InitError(error);
}
connOptions.vWhiteBinds.push_back(whitebind);
}
// If the user did not specify -bind= or -whitebind= then we bind
// on any address - 0.0.0.0 (IPv4) and :: (IPv6).
connOptions.bind_on_any =
args.GetArgs("-bind").empty() && args.GetArgs("-whitebind").empty();
// Emit a warning if a bad port is given to -port= but only if -bind and
// -whitebind are not given, because if they are, then -port= is ignored.
if (connOptions.bind_on_any && args.IsArgSet("-port")) {
const uint16_t port_arg = args.GetIntArg("-port", 0);
if (IsBadPort(port_arg)) {
InitWarning(BadPortWarning("-port", port_arg));
}
}
CService onion_service_target;
if (!connOptions.onion_binds.empty()) {
onion_service_target = connOptions.onion_binds.front();
} else if (!connOptions.vBinds.empty()) {
onion_service_target = connOptions.vBinds.front();
} else {
onion_service_target = DefaultOnionServiceTarget();
connOptions.onion_binds.push_back(onion_service_target);
}
if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
if (connOptions.onion_binds.size() > 1) {
InitWarning(strprintf(
_("More than one onion bind address is provided. Using %s "
"for the automatically created Tor onion service."),
onion_service_target.ToStringIPPort()));
}
StartTorControl(onion_service_target);
}
if (connOptions.bind_on_any) {
// Only add all IP addresses of the machine if we would be listening on
// any address - 0.0.0.0 (IPv4) and :: (IPv6).
Discover();
}
for (const auto &net : args.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
ConnectionDirection connection_direction;
bilingual_str error;
if (!NetWhitelistPermissions::TryParse(net, subnet,
connection_direction, error)) {
return InitError(error);
}
if (connection_direction & ConnectionDirection::In) {
connOptions.vWhitelistedRangeIncoming.push_back(subnet);
}
if (connection_direction & ConnectionDirection::Out) {
connOptions.vWhitelistedRangeOutgoing.push_back(subnet);
}
}
connOptions.vSeedNodes = args.GetArgs("-seednode");
// Initiate outbound connections unless connect=0
connOptions.m_use_addrman_outgoing = !args.IsArgSet("-connect");
if (!connOptions.m_use_addrman_outgoing) {
const auto connect = args.GetArgs("-connect");
if (connect.size() != 1 || connect[0] != "0") {
connOptions.m_specified_outgoing = connect;
}
}
const std::string &i2psam_arg = args.GetArg("-i2psam", "");
if (!i2psam_arg.empty()) {
CService addr;
if (!Lookup(i2psam_arg, addr, 7656, fNameLookup) || !addr.IsValid()) {
return InitError(strprintf(
_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
}
SetReachable(NET_I2P, true);
SetProxy(NET_I2P, proxyType{addr});
} else {
SetReachable(NET_I2P, false);
}
connOptions.m_i2p_accept_incoming =
args.GetBoolArg("-i2pacceptincoming", true);
if (!node.connman->Start(*node.scheduler, connOptions)) {
return false;
}
// Step 13: finished
// At this point, the RPC is "started", but still in warmup, which means it
// cannot yet be called. Before we make it callable, we need to make sure
// that the RPC's view of the best block is valid and consistent with
// ChainstateManager's active tip.
//
// If we do not do this, RPC's view of the best block will be height=0 and
// hash=0x0. This will lead to erroroneous responses for things like
// waitforblockheight.
RPCNotifyBlockChange(
WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()));
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading").translated);
for (const auto &client : node.chain_clients) {
client->start(*node.scheduler);
}
BanMan *banman = node.banman.get();
node.scheduler->scheduleEvery(
[banman] {
banman->DumpBanlist();
return true;
},
DUMP_BANS_INTERVAL);
// Start Avalanche's event loop.
if (node.avalanche) {
node.avalanche->startEventLoop(*node.scheduler);
}
if (node.peerman) {
node.peerman->StartScheduledTasks(*node.scheduler);
}
#if HAVE_SYSTEM
StartupNotify(args);
#endif
return true;
}
bool StartIndexBackgroundSync(NodeContext &node) {
// Find the oldest block among all indexes.
// This block is used to verify that we have the required blocks' data
// stored on disk, starting from that point up to the current tip.
// indexes_start_block='nullptr' means "start from height 0".
std::optional<const CBlockIndex *> indexes_start_block;
std::string older_index_name;
ChainstateManager &chainman = *Assert(node.chainman);
const Chainstate &chainstate =
WITH_LOCK(::cs_main, return chainman.GetChainstateForIndexing());
const CChain &index_chain = chainstate.m_chain;
for (auto index : node.indexes) {
const IndexSummary &summary = index->GetSummary();
if (summary.synced) {
continue;
}
// Get the last common block between the index best block and the active
// chain
LOCK(::cs_main);
const CBlockIndex *pindex = chainman.m_blockman.LookupBlockIndex(
BlockHash{summary.best_block_hash});
if (!index_chain.Contains(pindex)) {
pindex = index_chain.FindFork(pindex);
}
if (!indexes_start_block || !pindex ||
pindex->nHeight < indexes_start_block.value()->nHeight) {
indexes_start_block = pindex;
older_index_name = summary.name;
if (!pindex) {
// Starting from genesis so no need to look for earlier block.
break;
}
}
};
// Verify all blocks needed to sync to current tip are present.
if (indexes_start_block) {
LOCK(::cs_main);
const CBlockIndex *start_block = *indexes_start_block;
if (!start_block) {
start_block = chainman.ActiveChain().Genesis();
}
if (!chainman.m_blockman.CheckBlockDataAvailability(
*index_chain.Tip(), *Assert(start_block))) {
return InitError(strprintf(
Untranslated("%s best block of the index goes beyond pruned "
"data. Please disable the index or reindex (which "
"will download the whole blockchain again)"),
older_index_name));
}
}
// Start threads
for (auto index : node.indexes) {
if (!index->StartBackgroundSync()) {
return false;
}
}
return true;
}
diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp
index 8ff044bd2..da758424b 100644
--- a/src/kernel/chainparams.cpp
+++ b/src/kernel/chainparams.cpp
@@ -1,524 +1,523 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <kernel/chainparams.h>
-#include <chainparamsbase.h>
#include <chainparamsconstants.h>
#include <chainparamsseeds.h>
#include <consensus/amount.h>
#include <consensus/merkle.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <uint256.h>
#include <util/chaintype.h>
#include <util/strencodings.h>
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
static CBlock CreateGenesisBlock(const char *pszTimestamp,
const CScript &genesisOutputScript,
uint32_t nTime, uint32_t nNonce,
uint32_t nBits, int32_t nVersion,
const Amount genesisReward) {
CMutableTransaction txNew;
txNew.nVersion = 1;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig =
CScript() << 486604799 << CScriptNum(4)
<< std::vector<uint8_t>((const uint8_t *)pszTimestamp,
(const uint8_t *)pszTimestamp +
strlen(pszTimestamp));
txNew.vout[0].nValue = genesisReward;
txNew.vout[0].scriptPubKey = genesisOutputScript;
CBlock genesis;
genesis.nTime = nTime;
genesis.nBits = nBits;
genesis.nNonce = nNonce;
genesis.nVersion = nVersion;
genesis.vtx.push_back(MakeTransactionRef(std::move(txNew)));
genesis.hashPrevBlock.SetNull();
genesis.hashMerkleRoot = BlockMerkleRoot(genesis);
return genesis;
}
/**
* Build the genesis block. Note that the output of its generation transaction
* cannot be spent since it did not originally exist in the database.
*
* CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000,
* hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893,
* vtx=1)
* CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
* CTxIn(COutPoint(000000, -1), coinbase
* 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
* CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
* vMerkleTree: 4a5e1e
*/
static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce,
uint32_t nBits, int32_t nVersion,
const Amount genesisReward) {
const char *pszTimestamp =
"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
const CScript genesisOutputScript =
CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
"a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112"
"de5c384df7ba0b8d578a4c702b6bf11d5f")
<< OP_CHECKSIG;
return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce,
nBits, nVersion, genesisReward);
}
/**
* Main network
*/
class CMainParams : public CChainParams {
public:
explicit CMainParams(const ChainOptions &opts) {
m_chain_type = ChainType::MAIN;
consensus.nSubsidyHalvingInterval = 210000;
// 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 -
// April 1, 2012
consensus.BIP16Height = 173805;
consensus.BIP34Height = 227931;
consensus.BIP34Hash = BlockHash::fromHex(
"000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
// 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
consensus.BIP65Height = 388381;
// 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
consensus.BIP66Height = 363725;
// 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5
consensus.CSVHeight = 419328;
consensus.powLimit = uint256S(
"00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
// two weeks
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60;
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
// two days
consensus.nDAAHalfLife = 2 * 24 * 60 * 60;
// The miner fund is enabled by default on mainnet.
consensus.enableMinerFund = true;
// The staking rewards are enabled by default on mainnet.
consensus.enableStakingRewards = true;
// The best chain should have at least this much work.
consensus.nMinimumChainWork =
ChainParamsConstants::MAINNET_MINIMUM_CHAIN_WORK;
// By default assume that the signatures in ancestors of this block are
// valid.
consensus.defaultAssumeValid =
ChainParamsConstants::MAINNET_DEFAULT_ASSUME_VALID;
// August 1, 2017 hard fork
consensus.uahfHeight = 478558;
// November 13, 2017 hard fork
consensus.daaHeight = 504031;
// November 15, 2018 hard fork
consensus.magneticAnomalyHeight = 556766;
// November 15, 2019 protocol upgrade
consensus.gravitonHeight = 609135;
// May 15, 2020 12:00:00 UTC protocol upgrade
consensus.phononHeight = 635258;
// Nov 15, 2020 12:00:00 UTC protocol upgrade
consensus.axionHeight = 661647;
// May 15, 2023 12:00:00 UTC protocol upgrade
consensus.wellingtonHeight = 792116;
// Nov 15, 2023 12:00:00 UTC protocol upgrade
consensus.cowperthwaiteHeight = 818669;
// May 15, 2025 12:00:00 UTC protocol upgrade
consensus.schumpeterActivationTime = 1747310400;
// Nov. 15, 2025 12:00:00 UTC protocol upgrade
consensus.shibusawaActivationTime = 1763208000;
/**
* The message start string is designed to be unlikely to occur in
* normal data. The characters are rarely used upper ASCII, not valid as
* UTF-8, and produce a large 32-bit integer with any alignment.
*/
diskMagic[0] = 0xf9;
diskMagic[1] = 0xbe;
diskMagic[2] = 0xb4;
diskMagic[3] = 0xd9;
netMagic[0] = 0xe3;
netMagic[1] = 0xe1;
netMagic[2] = 0xf3;
netMagic[3] = 0xe8;
nDefaultPort = 8333;
nPruneAfterHeight = 100000;
m_assumed_blockchain_size =
ChainParamsConstants::MAINNET_ASSUMED_BLOCKCHAIN_SIZE;
m_assumed_chain_state_size =
ChainParamsConstants::MAINNET_ASSUMED_CHAINSTATE_SIZE;
genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1,
50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
assert(consensus.hashGenesisBlock ==
uint256S("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1"
"b60a8ce26f"));
assert(genesis.hashMerkleRoot ==
uint256S("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b"
"7afdeda33b"));
// Note that of those which support the service bits prefix, most only
// support a subset of possible options. This is fine at runtime as
// we'll fall back to using them as an addrfetch if they don't support
// the service bits we want, but we should get them updated to support
// all service bits wanted by any release ASAP to avoid it where
// possible.
// Bitcoin ABC seeder
vSeeds.emplace_back("seed.bitcoinabc.org");
// Fabien
vSeeds.emplace_back("seeder.fabien.cash");
// status.cash
vSeeds.emplace_back("seeder.status.cash");
base58Prefixes[PUBKEY_ADDRESS] = std::vector<uint8_t>(1, 0);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<uint8_t>(1, 5);
base58Prefixes[SECRET_KEY] = std::vector<uint8_t>(1, 128);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
cashaddrPrefix = opts.ecash ? "ecash" : "bitcoincash";
vFixedSeeds = std::vector<SeedSpec6>(std::begin(pnSeed6_main),
std::end(pnSeed6_main));
fDefaultConsistencyChecks = false;
fRequireStandard = true;
m_is_test_chain = false;
m_is_mockable_chain = false;
checkpointData = CheckpointData(ChainType::MAIN);
m_assumeutxo_data = {
// TODO to be specified in a future patch.
};
// Data as of block
// 000000000000000001d2ce557406b017a928be25ee98906397d339c3f68eec5d
// (height 523992).
chainTxData = ChainTxData{
// UNIX timestamp of last known number of transactions.
1522608016,
// Total number of transactions between genesis and that timestamp
// (the tx=... number in the ChainStateFlushed debug.log lines)
248589038,
// Estimated number of transactions per second after that timestamp.
3.2,
};
}
};
/**
* Testnet (v3)
*/
class CTestNetParams : public CChainParams {
public:
explicit CTestNetParams(const ChainOptions &opts) {
m_chain_type = ChainType::TESTNET;
consensus.nSubsidyHalvingInterval = 210000;
// 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65
consensus.BIP16Height = 514;
consensus.BIP34Height = 21111;
consensus.BIP34Hash = BlockHash::fromHex(
"0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
// 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
consensus.BIP65Height = 581885;
// 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
consensus.BIP66Height = 330776;
// 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb
consensus.CSVHeight = 770112;
consensus.powLimit = uint256S(
"00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
// two weeks
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60;
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = true;
consensus.fPowNoRetargeting = false;
// two days
consensus.nDAAHalfLife = 2 * 24 * 60 * 60;
// The miner fund is disabled by default on testnet.
consensus.enableMinerFund = false;
// The staking rewards are disabled by default on testnet.
consensus.enableStakingRewards = false;
// The best chain should have at least this much work.
consensus.nMinimumChainWork =
ChainParamsConstants::TESTNET_MINIMUM_CHAIN_WORK;
// By default assume that the signatures in ancestors of this block are
// valid.
consensus.defaultAssumeValid =
ChainParamsConstants::TESTNET_DEFAULT_ASSUME_VALID;
// August 1, 2017 hard fork
consensus.uahfHeight = 1155875;
// November 13, 2017 hard fork
consensus.daaHeight = 1188697;
// November 15, 2018 hard fork
consensus.magneticAnomalyHeight = 1267996;
// November 15, 2019 protocol upgrade
consensus.gravitonHeight = 1341711;
// May 15, 2020 12:00:00 UTC protocol upgrade
consensus.phononHeight = 1378460;
// Nov 15, 2020 12:00:00 UTC protocol upgrade
consensus.axionHeight = 1421481;
// May 15, 2023 12:00:00 UTC protocol upgrade
consensus.wellingtonHeight = 1556117;
// Nov 15, 2023 12:00:00 UTC protocol upgrade
consensus.cowperthwaiteHeight = 1584485;
// May 15, 2025 12:00:00 UTC protocol upgrade
consensus.schumpeterActivationTime = 1747310400;
// Nov. 15, 2025 12:00:00 UTC protocol upgrade
consensus.shibusawaActivationTime = 1763208000;
diskMagic[0] = 0x0b;
diskMagic[1] = 0x11;
diskMagic[2] = 0x09;
diskMagic[3] = 0x07;
netMagic[0] = 0xf4;
netMagic[1] = 0xe5;
netMagic[2] = 0xf3;
netMagic[3] = 0xf4;
nDefaultPort = 18333;
nPruneAfterHeight = 1000;
m_assumed_blockchain_size =
ChainParamsConstants::TESTNET_ASSUMED_BLOCKCHAIN_SIZE;
m_assumed_chain_state_size =
ChainParamsConstants::TESTNET_ASSUMED_CHAINSTATE_SIZE;
genesis =
CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
assert(consensus.hashGenesisBlock ==
uint256S("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526"
"f8d77f4943"));
assert(genesis.hashMerkleRoot ==
uint256S("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b"
"7afdeda33b"));
vFixedSeeds.clear();
vSeeds.clear();
// nodes with support for servicebits filtering should be at the top
// Bitcoin ABC seeder
vSeeds.emplace_back("testnet-seed.bitcoinabc.org");
// Fabien
vSeeds.emplace_back("testnet-seeder.fabien.cash");
// status.cash
vSeeds.emplace_back("testnet-seeder.status.cash");
base58Prefixes[PUBKEY_ADDRESS] = std::vector<uint8_t>(1, 111);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<uint8_t>(1, 196);
base58Prefixes[SECRET_KEY] = std::vector<uint8_t>(1, 239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
cashaddrPrefix = opts.ecash ? "ectest" : "bchtest";
vFixedSeeds = std::vector<SeedSpec6>(std::begin(pnSeed6_test),
std::end(pnSeed6_test));
fDefaultConsistencyChecks = false;
fRequireStandard = false;
m_is_test_chain = true;
m_is_mockable_chain = false;
checkpointData = CheckpointData(ChainType::TESTNET);
m_assumeutxo_data = {
// TODO to be specified in a future patch.
};
// Data as of block
// 000000000005b07ecf85563034d13efd81c1a29e47e22b20f4fc6919d5b09cd6
// (height 1223263)
chainTxData = ChainTxData{1522608381, 15052068, 0.15};
}
};
/**
* Regression test
*/
class CRegTestParams : public CChainParams {
public:
explicit CRegTestParams(const ChainOptions &opts) {
m_chain_type = ChainType::REGTEST;
consensus.nSubsidyHalvingInterval = 150;
// always enforce P2SH BIP16 on regtest
consensus.BIP16Height = 0;
// BIP34 activated on regtest (Used in functional tests)
consensus.BIP34Height = 500;
consensus.BIP34Hash = BlockHash();
// BIP65 activated on regtest (Used in functional tests)
consensus.BIP65Height = 1351;
// BIP66 activated on regtest (Used in functional tests)
consensus.BIP66Height = 1251;
// CSV activated on regtest (Used in functional tests)
consensus.CSVHeight = 576;
consensus.powLimit = uint256S(
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
// two weeks
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60;
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = true;
consensus.fPowNoRetargeting = true;
// two days
consensus.nDAAHalfLife = 2 * 24 * 60 * 60;
// The miner fund is disabled by default on regtest.
consensus.enableMinerFund = false;
// The staking rewards are disabled by default on regtest.
consensus.enableStakingRewards = false;
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
// By default assume that the signatures in ancestors of this block are
// valid.
consensus.defaultAssumeValid = BlockHash();
// UAHF is always enabled on regtest.
consensus.uahfHeight = 0;
// November 13, 2017 hard fork is always on on regtest.
consensus.daaHeight = 0;
// November 15, 2018 hard fork is always on on regtest.
consensus.magneticAnomalyHeight = 0;
// November 15, 2019 protocol upgrade
consensus.gravitonHeight = 0;
// May 15, 2020 12:00:00 UTC protocol upgrade
consensus.phononHeight = 0;
// Nov 15, 2020 12:00:00 UTC protocol upgrade
consensus.axionHeight = 0;
// May 15, 2023 12:00:00 UTC protocol upgrade
consensus.wellingtonHeight = 0;
// Nov 15, 2023 12:00:00 UTC protocol upgrade
consensus.cowperthwaiteHeight = 0;
// May 15, 2025 12:00:00 UTC protocol upgrade
consensus.schumpeterActivationTime = 1747310400;
// Nov. 15, 2025 12:00:00 UTC protocol upgrade
consensus.shibusawaActivationTime = 1763208000;
diskMagic[0] = 0xfa;
diskMagic[1] = 0xbf;
diskMagic[2] = 0xb5;
diskMagic[3] = 0xda;
netMagic[0] = 0xda;
netMagic[1] = 0xb5;
netMagic[2] = 0xbf;
netMagic[3] = 0xfa;
nDefaultPort = 18444;
nPruneAfterHeight = opts.fastprune ? 100 : 1000;
m_assumed_blockchain_size = 0;
m_assumed_chain_state_size = 0;
genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
assert(consensus.hashGenesisBlock ==
uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b"
"1a11466e2206"));
assert(genesis.hashMerkleRoot ==
uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab212"
"7b7afdeda33b"));
//! Regtest mode doesn't have any fixed seeds.
vFixedSeeds.clear();
//! Regtest mode doesn't have any DNS seeds.
vSeeds.clear();
fDefaultConsistencyChecks = true;
fRequireStandard = true;
m_is_test_chain = true;
m_is_mockable_chain = true;
checkpointData = CheckpointData(ChainType::REGTEST);
m_assumeutxo_data = {
{.height = 110,
.hash_serialized =
AssumeutxoHash{uint256S("0xd754ca97ef24c5132f8d2147c19310b7a6b"
"d136766430304735a73372fe36213")},
.nChainTx = 111,
.blockhash =
BlockHash{uint256S("0x47cfb2b77860d250060e78d3248bb05092876545"
"3cbcbdbc121e3c48b99a376c")}},
{// For use by test/functional/feature_assumeutxo.py
.height = 299,
.hash_serialized =
AssumeutxoHash{uint256S("0xa966794ed5a2f9debaefc7ca48dbc5d5e12"
"a89ff9fe45bd00ec5732d074580a9")},
.nChainTx = 334,
.blockhash =
BlockHash{uint256S("0x118a7d5473bccce9b314789e14ce426fc65fb09d"
"feda0131032bb6d86ed2fd0b")}},
};
chainTxData = ChainTxData{0, 0, 0};
base58Prefixes[PUBKEY_ADDRESS] = std::vector<uint8_t>(1, 111);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<uint8_t>(1, 196);
base58Prefixes[SECRET_KEY] = std::vector<uint8_t>(1, 239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
cashaddrPrefix = opts.ecash ? "ecregtest" : "bchreg";
}
};
std::unique_ptr<const CChainParams>
CChainParams::RegTest(const ChainOptions &options) {
return std::make_unique<const CRegTestParams>(options);
}
std::unique_ptr<const CChainParams>
CChainParams::Main(const ChainOptions &options) {
return std::make_unique<const CMainParams>(options);
}
std::unique_ptr<const CChainParams>
CChainParams::TestNet(const ChainOptions &options) {
return std::make_unique<const CTestNetParams>(options);
}
diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp
index d6cbf39b9..4ce2bacff 100644
--- a/src/qt/networkstyle.cpp
+++ b/src/qt/networkstyle.cpp
@@ -1,92 +1,91 @@
// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/networkstyle.h>
#include <qt/guiconstants.h>
-#include <chainparamsbase.h>
#include <tinyformat.h>
#include <util/chaintype.h>
#include <QApplication>
static const struct {
const ChainType networkId;
const char *appName;
const int iconColorHueShift;
const int iconColorSaturationReduction;
} network_styles[] = {
{ChainType::MAIN, QAPP_APP_NAME_DEFAULT, 0, 0},
{ChainType::TESTNET, QAPP_APP_NAME_TESTNET, 70, 30},
{ChainType::REGTEST, QAPP_APP_NAME_REGTEST, 160, 30},
};
// titleAddText needs to be const char* for tr()
NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift,
const int iconColorSaturationReduction,
const char *_titleAddText)
: appName(_appName),
titleAddText(qApp->translate("SplashScreen", _titleAddText)) {
// load pixmap
QPixmap pixmap(":/icons/bitcoin");
if (iconColorHueShift != 0 && iconColorSaturationReduction != 0) {
// generate QImage from QPixmap
QImage img = pixmap.toImage();
int h, s, l, a;
// traverse though lines
for (int y = 0; y < img.height(); y++) {
QRgb *scL = reinterpret_cast<QRgb *>(img.scanLine(y));
// loop through pixels
for (int x = 0; x < img.width(); x++) {
// preserve alpha because QColor::getHsl doesn't return the
// alpha value
a = qAlpha(scL[x]);
QColor col(scL[x]);
// get hue value
col.getHsl(&h, &s, &l);
// rotate color on RGB color circle
// 70° should end up with the typical "testnet" green
h += iconColorHueShift;
// change saturation value
if (s > iconColorSaturationReduction) {
s -= iconColorSaturationReduction;
}
col.setHsl(h, s, l, a);
// set the pixel
scL[x] = col.rgba();
}
}
// convert back to QPixmap
pixmap.convertFromImage(img);
}
appIcon = QIcon(pixmap);
trayAndWindowIcon = QIcon(pixmap.scaled(QSize(256, 256)));
}
const NetworkStyle *NetworkStyle::instantiate(const ChainType networkId) {
std::string titleAddText =
networkId == ChainType::MAIN
? ""
: strprintf("[%s]", ChainTypeToString(networkId));
for (const auto &network_style : network_styles) {
if (networkId == network_style.networkId) {
return new NetworkStyle(network_style.appName,
network_style.iconColorHueShift,
network_style.iconColorSaturationReduction,
titleAddText.c_str());
}
}
return nullptr;
}
diff --git a/src/seeder/options.cpp b/src/seeder/options.cpp
index 3b327afe1..61bc93b73 100644
--- a/src/seeder/options.cpp
+++ b/src/seeder/options.cpp
@@ -1,171 +1,172 @@
// Copyright (c) 2022 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <seeder/options.h>
#include <chainparams.h>
+#include <chainparamsbase.h>
#include <clientversion.h>
#include <common/args.h>
#include <string>
namespace seeder {
int CDnsSeedOpts::ParseCommandLine(int argc, const char **argv) {
assert(argsManager);
std::string error;
if (!argsManager->ParseParameters(argc, argv, error)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n",
error);
return EXIT_FAILURE;
}
if (HelpRequested(*argsManager) || argsManager->IsArgSet("-version")) {
std::string strUsage =
PACKAGE_NAME " Seeder " + FormatFullVersion() + "\n";
if (HelpRequested(*argsManager)) {
strUsage +=
"\nUsage: bitcoin-seeder -host=<host> -ns=<ns> "
"[-mbox=<mbox>] [-threads=<threads>] [-port=<port>]\n\n" +
argsManager->GetHelpMessage();
}
tfm::format(std::cout, "%s", strUsage);
return EXIT_SUCCESS;
}
dumpInterval = std::chrono::seconds(
argsManager->GetIntArg("-dumpinterval", DEFAULT_DUMP_INTERVAL_SECONDS));
if (dumpInterval.count() <= 0) {
tfm::format(
std::cerr,
"Error: -dumpinterval argument expects only positive integers\n");
return EXIT_FAILURE;
}
nThreads = argsManager->GetIntArg("-threads", DEFAULT_NUM_THREADS);
if (nThreads <= 0) {
tfm::format(
std::cerr,
"Error: -threads argument expects only positive integers\n");
return EXIT_FAILURE;
}
nPort = argsManager->GetIntArg("-port", DEFAULT_PORT);
if (nPort < 0 || nPort > 65535) {
tfm::format(std::cerr, "Error: -port argument expects only positive "
"integers in the range 0 - 65535\n");
return EXIT_FAILURE;
}
nDnsThreads =
argsManager->GetIntArg("-dnsthreads", DEFAULT_NUM_DNS_THREADS);
if (nDnsThreads <= 0) {
tfm::format(
std::cerr,
"Error: -dnsthreads argument expects only positive integers\n");
return EXIT_FAILURE;
}
fWipeBan = argsManager->GetBoolArg("-wipeban", DEFAULT_WIPE_BAN);
fWipeIgnore = argsManager->GetBoolArg("-wipeignore", DEFAULT_WIPE_IGNORE);
mbox = argsManager->GetArg("-mbox", DEFAULT_EMAIL);
ns = argsManager->GetArg("-ns", DEFAULT_NAMESERVER);
host = argsManager->GetArg("-host", DEFAULT_HOST);
tor = argsManager->GetArg("-onion", DEFAULT_TOR_PROXY);
ip_addr = argsManager->GetArg("-address", DEFAULT_LISTEN_ADDRESS);
ipv4_proxy = argsManager->GetArg("-proxyipv4", DEFAULT_IPV4_PROXY);
ipv6_proxy = argsManager->GetArg("-proxyipv6", DEFAULT_IPV6_PROXY);
SelectParams(argsManager->GetChainType());
// Both IPv4 and IPv6 addresses are valid, but the listening address is
// treated as IPv6 internally
if (ip_addr.find(':') == std::string::npos) {
ip_addr.insert(0, "::FFFF:");
}
if (argsManager->IsArgSet("-filter")) {
// Parse whitelist additions
std::string flagString = argsManager->GetArg("-filter", "");
size_t flagstartpos = 0;
while (flagstartpos < flagString.size()) {
size_t flagendpos = flagString.find_first_of(',', flagstartpos);
uint64_t flag = atoi64(
flagString.substr(flagstartpos, (flagendpos - flagstartpos)));
filter_whitelist.insert(flag);
if (flagendpos == std::string::npos) {
break;
}
flagstartpos = flagendpos + 1;
}
}
if (filter_whitelist.empty()) {
filter_whitelist.insert(NODE_NETWORK);
filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM);
filter_whitelist.insert(NODE_NETWORK_LIMITED);
filter_whitelist.insert(NODE_NETWORK_LIMITED | NODE_BLOOM);
}
return CONTINUE_EXECUTION;
}
void CDnsSeedOpts::SetupSeederArgs() {
assert(argsManager);
SetupHelpOptions(*argsManager);
argsManager->AddArg(
"-help-debug", "Show all debugging options (usage: --help -help-debug)",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
SetupChainParamsBaseOptions(*argsManager);
argsManager->AddArg("-version", "Print version and exit",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg("-host=<host>", "Hostname of the DNS seed",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg("-ns=<ns>", "Hostname of the nameserver",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg("-mbox=<mbox>",
"E-Mail address reported in SOA records",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg(
"-dumpinterval=<seconds>",
strprintf("Number of seconds between each database dump (default: %d)",
DEFAULT_DUMP_INTERVAL_SECONDS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg(
"-threads=<threads>",
strprintf("Number of crawlers to run in parallel (default: %d)",
DEFAULT_NUM_THREADS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg("-dnsthreads=<threads>",
strprintf("Number of DNS server threads (default: %d)",
DEFAULT_NUM_DNS_THREADS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg("-address=<address>",
strprintf("Address to listen on (default: '%s')",
DEFAULT_LISTEN_ADDRESS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg(
"-port=<port>",
strprintf("UDP port to listen on (default: %d)", DEFAULT_PORT),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsManager->AddArg("-onion=<ip:port>", "Tor proxy IP/Port",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsManager->AddArg("-overridednsseed",
"If set, only use the specified DNS seed when "
"querying for peer addresses via DNS lookup->",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsManager->AddArg("-proxyipv4=<ip:port>", "IPV4 SOCKS5 proxy IP/Port",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsManager->AddArg("-proxyipv6=<ip:port>", "IPV6 SOCKS5 proxy IP/Port",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsManager->AddArg("-filter=<f1,f2,...>",
"Allow these flag combinations as filters",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsManager->AddArg("-wipeban", "Wipe list of banned nodes",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsManager->AddArg("-wipeignore", "Wipe list of ignored nodes",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
}
} // namespace seeder
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 1cff359f0..b2afd28f0 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -1,292 +1,291 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
-#include <chainparamsbase.h>
#include <coins.h>
#include <consensus/amount.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <key.h>
#include <policy/policy.h>
#include <primitives/blockhash.h>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <validation.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
#include <cstdint>
#include <limits>
#include <optional>
#include <string>
#include <vector>
namespace {
const TestingSetup *g_setup;
const Coin EMPTY_COIN{};
bool operator==(const Coin &a, const Coin &b) {
if (a.IsSpent() && b.IsSpent()) {
return true;
}
return a.IsCoinBase() == b.IsCoinBase() && a.GetHeight() == b.GetHeight() &&
a.GetTxOut() == b.GetTxOut();
}
} // namespace
void initialize_coins_view() {
static const auto testing_setup =
MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(coins_view, initialize_coins_view) {
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CCoinsView backend_coins_view;
CCoinsViewCache coins_view_cache{&backend_coins_view,
/*deterministic=*/true};
COutPoint random_out_point;
Coin random_coin;
CMutableTransaction random_mutable_transaction;
while (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
[&] {
if (random_coin.IsSpent()) {
return;
}
Coin coin = random_coin;
bool expected_code_path = false;
const bool possible_overwrite =
fuzzed_data_provider.ConsumeBool();
try {
coins_view_cache.AddCoin(random_out_point, std::move(coin),
possible_overwrite);
expected_code_path = true;
} catch (const std::logic_error &e) {
if (e.what() ==
std::string{"Attempted to overwrite an unspent coin "
"(when possible_overwrite is false)"}) {
assert(!possible_overwrite);
expected_code_path = true;
}
}
assert(expected_code_path);
},
[&] { (void)coins_view_cache.Flush(); },
[&] { (void)coins_view_cache.Sync(); },
[&] {
coins_view_cache.SetBestBlock(
BlockHash{ConsumeUInt256(fuzzed_data_provider)});
},
[&] {
Coin move_to;
(void)coins_view_cache.SpendCoin(
random_out_point,
fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);
},
[&] { coins_view_cache.Uncache(random_out_point); },
[&] {
if (fuzzed_data_provider.ConsumeBool()) {
backend_coins_view = CCoinsView{};
}
coins_view_cache.SetBackend(backend_coins_view);
},
[&] {
const std::optional<COutPoint> opt_out_point =
ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
if (!opt_out_point) {
return;
}
random_out_point = *opt_out_point;
},
[&] {
const std::optional<Coin> opt_coin =
ConsumeDeserializable<Coin>(fuzzed_data_provider);
if (!opt_coin) {
return;
}
random_coin = *opt_coin;
},
[&] {
const std::optional<CMutableTransaction>
opt_mutable_transaction =
ConsumeDeserializable<CMutableTransaction>(
fuzzed_data_provider);
if (!opt_mutable_transaction) {
return;
}
random_mutable_transaction = *opt_mutable_transaction;
},
[&] {
CCoinsMapMemoryResource resource;
CCoinsMap coins_map{
0, SaltedOutpointHasher{/*deterministic=*/true},
CCoinsMap::key_equal{}, &resource};
while (fuzzed_data_provider.ConsumeBool()) {
CCoinsCacheEntry coins_cache_entry;
coins_cache_entry.flags =
fuzzed_data_provider.ConsumeIntegral<uint8_t>();
if (fuzzed_data_provider.ConsumeBool()) {
coins_cache_entry.coin = random_coin;
} else {
const std::optional<Coin> opt_coin =
ConsumeDeserializable<Coin>(fuzzed_data_provider);
if (!opt_coin) {
return;
}
coins_cache_entry.coin = *opt_coin;
}
coins_map.emplace(random_out_point,
std::move(coins_cache_entry));
}
bool expected_code_path = false;
try {
coins_view_cache.BatchWrite(
coins_map,
fuzzed_data_provider.ConsumeBool()
? BlockHash{ConsumeUInt256(fuzzed_data_provider)}
: coins_view_cache.GetBestBlock());
expected_code_path = true;
} catch (const std::logic_error &e) {
if (e.what() ==
std::string{"FRESH flag misapplied to coin that exists "
"in parent cache"}) {
expected_code_path = true;
}
}
assert(expected_code_path);
});
}
{
const Coin &coin_using_access_coin =
coins_view_cache.AccessCoin(random_out_point);
const bool exists_using_access_coin =
!(coin_using_access_coin == EMPTY_COIN);
const bool exists_using_have_coin =
coins_view_cache.HaveCoin(random_out_point);
const bool exists_using_have_coin_in_cache =
coins_view_cache.HaveCoinInCache(random_out_point);
Coin coin_using_get_coin;
const bool exists_using_get_coin =
coins_view_cache.GetCoin(random_out_point, coin_using_get_coin);
if (exists_using_get_coin) {
assert(coin_using_get_coin == coin_using_access_coin);
}
assert((exists_using_access_coin && exists_using_have_coin_in_cache &&
exists_using_have_coin && exists_using_get_coin) ||
(!exists_using_access_coin && !exists_using_have_coin_in_cache &&
!exists_using_have_coin && !exists_using_get_coin));
const bool exists_using_have_coin_in_backend =
backend_coins_view.HaveCoin(random_out_point);
if (exists_using_have_coin_in_backend) {
assert(exists_using_have_coin);
}
Coin coin_using_backend_get_coin;
if (backend_coins_view.GetCoin(random_out_point,
coin_using_backend_get_coin)) {
assert(exists_using_have_coin_in_backend);
assert(coin_using_get_coin == coin_using_backend_get_coin);
} else {
assert(!exists_using_have_coin_in_backend);
}
}
{
bool expected_code_path = false;
try {
(void)coins_view_cache.Cursor();
} catch (const std::logic_error &) {
expected_code_path = true;
}
assert(expected_code_path);
(void)coins_view_cache.DynamicMemoryUsage();
(void)coins_view_cache.EstimateSize();
(void)coins_view_cache.GetBestBlock();
(void)coins_view_cache.GetCacheSize();
(void)coins_view_cache.GetHeadBlocks();
(void)coins_view_cache.HaveInputs(
CTransaction{random_mutable_transaction});
}
{
const CCoinsViewCursor *coins_view_cursor = backend_coins_view.Cursor();
assert(coins_view_cursor == nullptr);
(void)backend_coins_view.EstimateSize();
(void)backend_coins_view.GetBestBlock();
(void)backend_coins_view.GetHeadBlocks();
}
if (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
[&] {
const CTransaction transaction{random_mutable_transaction};
bool is_spent = false;
for (const CTxOut &tx_out : transaction.vout) {
if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) {
is_spent = true;
}
}
if (is_spent) {
// Avoid:
// coins.cpp:69: void CCoinsViewCache::AddCoin(const
// COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()'
// failed.
return;
}
bool expected_code_path = false;
const int height{
int(fuzzed_data_provider.ConsumeIntegral<uint32_t>() >> 1)};
const bool possible_overwrite =
fuzzed_data_provider.ConsumeBool();
try {
AddCoins(coins_view_cache, transaction, height,
possible_overwrite);
expected_code_path = true;
} catch (const std::logic_error &e) {
if (e.what() ==
std::string{"Attempted to overwrite an unspent coin "
"(when possible_overwrite is false)"}) {
assert(!possible_overwrite);
expected_code_path = true;
}
}
assert(expected_code_path);
},
[&] {
uint32_t flags =
fuzzed_data_provider.ConsumeIntegral<uint32_t>();
(void)AreInputsStandard(
CTransaction{random_mutable_transaction}, coins_view_cache,
flags);
},
[&] {
TxValidationState state;
Amount tx_fee_out;
const CTransaction transaction{random_mutable_transaction};
if (ContainsSpentInput(transaction, coins_view_cache)) {
// Avoid:
// consensus/tx_verify.cpp:171: bool
// Consensus::CheckTxInputs(const CTransaction &,
// TxValidationState &, const CCoinsViewCache &, int,
// CAmount &): Assertion `!coin.IsSpent()' failed.
}
try {
(void)Consensus::CheckTxInputs(
transaction, state, coins_view_cache,
fuzzed_data_provider.ConsumeIntegralInRange<int>(
0, std::numeric_limits<int>::max()),
tx_fee_out);
assert(MoneyRange(tx_fee_out));
} catch (const std::runtime_error &) {
}
});
}
}
diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp
index c87b42b0d..0d64b9021 100644
--- a/src/test/fuzz/key.cpp
+++ b/src/test/fuzz/key.cpp
@@ -1,343 +1,342 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
-#include <chainparamsbase.h>
#include <config.h>
#include <key.h>
#include <key_io.h>
#include <outputtype.h>
#include <policy/policy.h>
#include <pubkey.h>
#include <rpc/util.h>
#include <script/keyorigin.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <streams.h>
#include <util/chaintype.h>
#include <util/strencodings.h>
#include <test/fuzz/fuzz.h>
#include <cassert>
#include <cstdint>
#include <numeric>
#include <string>
#include <vector>
void initialize_key() {
static const ECCVerifyHandle ecc_verify_handle;
ECC_Start();
SelectParams(ChainType::REGTEST);
}
FUZZ_TARGET_INIT(key, initialize_key) {
const CKey key = [&] {
CKey k;
k.Set(buffer.begin(), buffer.end(), true);
return k;
}();
if (!key.IsValid()) {
return;
}
{
assert(key.begin() + key.size() == key.end());
assert(key.IsCompressed());
assert(key.size() == 32);
assert(DecodeSecret(EncodeSecret(key)) == key);
}
{
CKey invalid_key;
assert(!(invalid_key == key));
assert(!invalid_key.IsCompressed());
assert(!invalid_key.IsValid());
assert(invalid_key.size() == 0);
}
{
CKey uncompressed_key;
uncompressed_key.Set(buffer.begin(), buffer.end(), false);
assert(!(uncompressed_key == key));
assert(!uncompressed_key.IsCompressed());
assert(key.size() == 32);
assert(uncompressed_key.begin() + uncompressed_key.size() ==
uncompressed_key.end());
assert(uncompressed_key.IsValid());
}
{
CKey copied_key;
copied_key.Set(key.begin(), key.end(), key.IsCompressed());
assert(copied_key == key);
}
{
CKey negated_key = key;
negated_key.Negate();
assert(negated_key.IsValid());
assert(!(negated_key == key));
negated_key.Negate();
assert(negated_key == key);
}
const uint256 random_uint256 = Hash(buffer);
{
CKey child_key;
ChainCode child_chaincode;
const bool ok =
key.Derive(child_key, child_chaincode, 0, random_uint256);
assert(ok);
assert(child_key.IsValid());
assert(!(child_key == key));
assert(child_chaincode != random_uint256);
}
const CPubKey pubkey = key.GetPubKey();
{
assert(pubkey.size() == 33);
assert(key.VerifyPubKey(pubkey));
assert(pubkey.GetHash() != random_uint256);
assert(pubkey.begin() + pubkey.size() == pubkey.end());
assert(pubkey.data() == pubkey.begin());
assert(pubkey.IsCompressed());
assert(pubkey.IsValid());
assert(pubkey.IsFullyValid());
assert(HexToPubKey(HexStr(pubkey)) == pubkey);
assert(GetAllDestinationsForKey(pubkey).size() == 1);
}
{
CDataStream data_stream{SER_NETWORK, INIT_PROTO_VERSION};
pubkey.Serialize(data_stream);
CPubKey pubkey_deserialized;
pubkey_deserialized.Unserialize(data_stream);
assert(pubkey_deserialized == pubkey);
}
{
const CScript tx_pubkey_script = GetScriptForRawPubKey(pubkey);
assert(!tx_pubkey_script.IsPayToScriptHash());
assert(!tx_pubkey_script.IsPushOnly());
assert(!tx_pubkey_script.IsUnspendable());
assert(tx_pubkey_script.HasValidOps());
assert(tx_pubkey_script.size() == 35);
const CScript tx_multisig_script = GetScriptForMultisig(1, {pubkey});
assert(!tx_multisig_script.IsPayToScriptHash());
assert(!tx_multisig_script.IsPushOnly());
assert(!tx_multisig_script.IsUnspendable());
assert(tx_multisig_script.HasValidOps());
assert(tx_multisig_script.size() == 37);
FillableSigningProvider fillable_signing_provider;
assert(IsSolvable(fillable_signing_provider, tx_pubkey_script));
assert(IsSolvable(fillable_signing_provider, tx_multisig_script));
assert(fillable_signing_provider.GetKeys().size() == 0);
assert(!fillable_signing_provider.HaveKey(pubkey.GetID()));
const bool ok_add_key = fillable_signing_provider.AddKey(key);
assert(ok_add_key);
assert(fillable_signing_provider.HaveKey(pubkey.GetID()));
FillableSigningProvider fillable_signing_provider_pub;
assert(!fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
const bool ok_add_key_pubkey =
fillable_signing_provider_pub.AddKeyPubKey(key, pubkey);
assert(ok_add_key_pubkey);
assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
TxoutType which_type_tx_pubkey;
const bool is_standard_tx_pubkey =
IsStandard(tx_pubkey_script, std::nullopt, which_type_tx_pubkey);
assert(is_standard_tx_pubkey);
assert(which_type_tx_pubkey == TxoutType::PUBKEY);
TxoutType which_type_tx_multisig;
const bool is_standard_tx_multisig = IsStandard(
tx_multisig_script, std::nullopt, which_type_tx_multisig);
assert(is_standard_tx_multisig);
assert(which_type_tx_multisig == TxoutType::MULTISIG);
std::vector<std::vector<uint8_t>> v_solutions_ret_tx_pubkey;
const TxoutType outtype_tx_pubkey =
Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
assert(outtype_tx_pubkey == TxoutType::PUBKEY);
assert(v_solutions_ret_tx_pubkey.size() == 1);
assert(v_solutions_ret_tx_pubkey[0].size() == 33);
std::vector<std::vector<uint8_t>> v_solutions_ret_tx_multisig;
const TxoutType outtype_tx_multisig =
Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
assert(outtype_tx_multisig == TxoutType::MULTISIG);
assert(v_solutions_ret_tx_multisig.size() == 3);
assert(v_solutions_ret_tx_multisig[0].size() == 1);
assert(v_solutions_ret_tx_multisig[1].size() == 33);
assert(v_solutions_ret_tx_multisig[2].size() == 1);
OutputType output_type{};
const CTxDestination tx_destination =
GetDestinationForKey(pubkey, output_type);
assert(output_type == OutputType::LEGACY);
assert(IsValidDestination(tx_destination));
assert(CTxDestination{PKHash{pubkey}} == tx_destination);
const CScript script_for_destination =
GetScriptForDestination(tx_destination);
assert(script_for_destination.size() == 25);
const Config &config = GetConfig();
const CChainParams &params = config.GetChainParams();
const std::string destination_address =
EncodeDestination(tx_destination, config);
assert(DecodeDestination(destination_address, params) ==
tx_destination);
const CPubKey pubkey_from_address_string = AddrToPubKey(
params, fillable_signing_provider, destination_address);
assert(pubkey_from_address_string == pubkey);
CKeyID key_id = pubkey.GetID();
assert(!key_id.IsNull());
assert(key_id == CKeyID{key_id});
assert(key_id ==
GetKeyForDestination(fillable_signing_provider, tx_destination));
CPubKey pubkey_out;
const bool ok_get_pubkey =
fillable_signing_provider.GetPubKey(key_id, pubkey_out);
assert(ok_get_pubkey);
CKey key_out;
const bool ok_get_key =
fillable_signing_provider.GetKey(key_id, key_out);
assert(ok_get_key);
assert(fillable_signing_provider.GetKeys().size() == 1);
assert(fillable_signing_provider.HaveKey(key_id));
KeyOriginInfo key_origin_info;
const bool ok_get_key_origin =
fillable_signing_provider.GetKeyOrigin(key_id, key_origin_info);
assert(!ok_get_key_origin);
}
{
const std::vector<uint8_t> vch_pubkey{pubkey.begin(), pubkey.end()};
assert(CPubKey::ValidSize(vch_pubkey));
assert(!CPubKey::ValidSize(
{pubkey.begin(), pubkey.begin() + pubkey.size() - 1}));
const CPubKey pubkey_ctor_1{vch_pubkey};
assert(pubkey == pubkey_ctor_1);
const CPubKey pubkey_ctor_2{vch_pubkey.begin(), vch_pubkey.end()};
assert(pubkey == pubkey_ctor_2);
CPubKey pubkey_set;
pubkey_set.Set(vch_pubkey.begin(), vch_pubkey.end());
assert(pubkey == pubkey_set);
}
{
const CPubKey invalid_pubkey{};
assert(!invalid_pubkey.IsValid());
assert(!invalid_pubkey.IsFullyValid());
assert(!(pubkey == invalid_pubkey));
assert(pubkey != invalid_pubkey);
assert(pubkey < invalid_pubkey);
}
{
// Cover CPubKey's operator[](unsigned int pos)
unsigned int sum = 0;
for (size_t i = 0; i < pubkey.size(); ++i) {
sum += pubkey[i];
}
assert(std::accumulate(pubkey.begin(), pubkey.end(), 0U) == sum);
}
{
CPubKey decompressed_pubkey = pubkey;
assert(decompressed_pubkey.IsCompressed());
const bool ok = decompressed_pubkey.Decompress();
assert(ok);
assert(!decompressed_pubkey.IsCompressed());
assert(decompressed_pubkey.size() == 65);
}
{
std::vector<uint8_t> vch_sig_ecdsa;
const bool ecdsa_ok =
key.SignECDSA(random_uint256, vch_sig_ecdsa, false);
assert(ecdsa_ok);
assert(pubkey.VerifyECDSA(random_uint256, vch_sig_ecdsa));
assert(CPubKey::CheckLowS(vch_sig_ecdsa));
const std::vector<uint8_t> vch_invalid_sig_ecdsa{
vch_sig_ecdsa.begin(),
vch_sig_ecdsa.begin() + vch_sig_ecdsa.size() - 1};
assert(!pubkey.VerifyECDSA(random_uint256, vch_invalid_sig_ecdsa));
assert(!CPubKey::CheckLowS(vch_invalid_sig_ecdsa));
std::vector<uint8_t> vch_sig_schnorr;
const bool schnorr_ok =
key.SignSchnorr(random_uint256, vch_sig_schnorr);
assert(schnorr_ok);
assert(vch_sig_schnorr.size() == 64);
assert(pubkey.VerifySchnorr(random_uint256, vch_sig_schnorr));
const std::vector<uint8_t> vch_invalid_sig_schnorr{
vch_sig_schnorr.begin(),
vch_sig_schnorr.begin() + vch_sig_schnorr.size() - 1};
assert(!pubkey.VerifySchnorr(random_uint256, vch_invalid_sig_schnorr));
}
{
std::vector<uint8_t> vch_compact_sig;
const bool ok_sign_compact =
key.SignCompact(random_uint256, vch_compact_sig);
assert(ok_sign_compact);
CPubKey recover_pubkey;
const bool ok_recover_compact =
recover_pubkey.RecoverCompact(random_uint256, vch_compact_sig);
assert(ok_recover_compact);
assert(recover_pubkey == pubkey);
}
{
CPubKey child_pubkey;
ChainCode child_chaincode;
const bool ok =
pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
assert(ok);
assert(child_pubkey != pubkey);
assert(child_pubkey.IsCompressed());
assert(child_pubkey.IsFullyValid());
assert(child_pubkey.IsValid());
assert(child_pubkey.size() == 33);
assert(child_chaincode != random_uint256);
}
const CPrivKey priv_key = key.GetPrivKey();
{
for (const bool skip_check : {true, false}) {
CKey loaded_key;
const bool ok = loaded_key.Load(priv_key, pubkey, skip_check);
assert(ok);
assert(key == loaded_key);
}
}
}
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index fee0c6438..860bd9c99 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -1,105 +1,104 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
-#include <chainparamsbase.h>
#include <config.h>
#include <net.h>
#include <net_permissions.h>
#include <netaddress.h>
#include <protocol.h>
#include <random.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <util/asmap.h>
#include <util/chaintype.h>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
void initialize_net() {
static const auto testing_setup = MakeNoLogFileContext<>(ChainType::MAIN);
}
FUZZ_TARGET_INIT(net, initialize_net) {
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const Config &config = GetConfig();
const std::optional<CAddress> address =
ConsumeDeserializable<CAddress>(fuzzed_data_provider);
if (!address) {
return;
}
const std::optional<CAddress> address_bind =
ConsumeDeserializable<CAddress>(fuzzed_data_provider);
if (!address_bind) {
return;
}
const auto sock = std::make_shared<FuzzedSock>(fuzzed_data_provider);
CNode node{
fuzzed_data_provider.ConsumeIntegral<NodeId>(),
sock,
*address,
fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
*address_bind,
fuzzed_data_provider.ConsumeRandomLengthString(32),
fuzzed_data_provider.PickValueInArray(
{ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY,
ConnectionType::MANUAL, ConnectionType::FEELER,
ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH}),
fuzzed_data_provider.ConsumeBool()};
node.SetCommonVersion(fuzzed_data_provider.ConsumeIntegral<int>());
while (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider, [&] { node.CloseSocketDisconnect(); },
[&] {
CNodeStats stats;
node.copyStats(stats);
},
[&] {
const CNode *add_ref_node = node.AddRef();
assert(add_ref_node == &node);
},
[&] {
if (node.GetRefCount() > 0) {
node.Release();
}
},
[&] {
const std::optional<CService> service_opt =
ConsumeDeserializable<CService>(fuzzed_data_provider);
if (!service_opt) {
return;
}
node.SetAddrLocal(*service_opt);
},
[&] {
const std::vector<uint8_t> b =
ConsumeRandomLengthByteVector(fuzzed_data_provider);
bool complete;
node.ReceiveMsgBytes(config, b, complete);
});
}
(void)node.GetAddrLocal();
(void)node.GetId();
(void)node.GetLocalNonce();
const int ref_count = node.GetRefCount();
assert(ref_count >= 0);
(void)node.GetCommonVersion();
const NetPermissionFlags net_permission_flags =
ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
(void)node.HasPermission(net_permission_flags);
(void)node.ConnectedThroughNetwork();
}
diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp
index 6a98cd5c2..48bc842de 100644
--- a/src/test/fuzz/script_sigcache.cpp
+++ b/src/test/fuzz/script_sigcache.cpp
@@ -1,55 +1,54 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
-#include <chainparamsbase.h>
#include <key.h>
#include <pubkey.h>
#include <script/sigcache.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
namespace {
const BasicTestingSetup *g_setup;
} // namespace
void initialize_script_sigcache() {
static const auto testing_setup = MakeNoLogFileContext<>();
g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(script_sigcache, initialize_script_sigcache) {
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const std::optional<CMutableTransaction> mutable_transaction =
ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
const CTransaction tx = mutable_transaction
? CTransaction{*mutable_transaction}
: CTransaction{};
const unsigned int n_in =
fuzzed_data_provider.ConsumeIntegral<unsigned int>();
const Amount amount = ConsumeMoney(fuzzed_data_provider);
const bool store = fuzzed_data_provider.ConsumeBool();
PrecomputedTransactionData tx_data;
CachingTransactionSignatureChecker caching_transaction_signature_checker{
mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
const std::optional<CPubKey> pub_key =
ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
if (pub_key) {
const std::vector<uint8_t> random_bytes =
ConsumeRandomLengthByteVector(fuzzed_data_provider);
if (!random_bytes.empty()) {
(void)caching_transaction_signature_checker.VerifySignature(
random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
}
}
}
diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp
index 10fc2186c..c8c58714b 100644
--- a/src/test/fuzz/script_sign.cpp
+++ b/src/test/fuzz/script_sign.cpp
@@ -1,186 +1,185 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
-#include <chainparamsbase.h>
#include <key.h>
#include <pubkey.h>
#include <script/keyorigin.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <util/chaintype.h>
#include <cassert>
#include <cstdint>
#include <iostream>
#include <map>
#include <optional>
#include <string>
#include <vector>
void initialize_script_sign() {
static const ECCVerifyHandle ecc_verify_handle;
ECC_Start();
SelectParams(ChainType::REGTEST);
}
FUZZ_TARGET_INIT(script_sign, initialize_script_sign) {
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const std::vector<uint8_t> key =
ConsumeRandomLengthByteVector(fuzzed_data_provider, 128);
{
CDataStream random_data_stream =
ConsumeDataStream(fuzzed_data_provider);
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
try {
DeserializeHDKeypaths(random_data_stream, key, hd_keypaths);
} catch (const std::ios_base::failure &) {
}
CDataStream serialized{SER_NETWORK, PROTOCOL_VERSION};
SerializeHDKeypaths(serialized, hd_keypaths,
fuzzed_data_provider.ConsumeIntegral<uint8_t>());
}
{
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
while (fuzzed_data_provider.ConsumeBool()) {
const std::optional<CPubKey> pub_key =
ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
if (!pub_key) {
break;
}
const std::optional<KeyOriginInfo> key_origin_info =
ConsumeDeserializable<KeyOriginInfo>(fuzzed_data_provider);
if (!key_origin_info) {
break;
}
hd_keypaths[*pub_key] = *key_origin_info;
}
CDataStream serialized{SER_NETWORK, PROTOCOL_VERSION};
try {
SerializeHDKeypaths(
serialized, hd_keypaths,
fuzzed_data_provider.ConsumeIntegral<uint8_t>());
} catch (const std::ios_base::failure &) {
}
std::map<CPubKey, KeyOriginInfo> deserialized_hd_keypaths;
try {
DeserializeHDKeypaths(serialized, key, hd_keypaths);
} catch (const std::ios_base::failure &) {
}
assert(hd_keypaths.size() >= deserialized_hd_keypaths.size());
}
{
SignatureData signature_data_1{ConsumeScript(fuzzed_data_provider)};
SignatureData signature_data_2{ConsumeScript(fuzzed_data_provider)};
signature_data_1.MergeSignatureData(signature_data_2);
}
FillableSigningProvider provider;
CKey k;
const std::vector<uint8_t> key_data =
ConsumeRandomLengthByteVector(fuzzed_data_provider);
k.Set(key_data.begin(), key_data.end(), fuzzed_data_provider.ConsumeBool());
if (k.IsValid()) {
provider.AddKey(k);
}
{
const std::optional<CMutableTransaction> mutable_transaction =
ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
const std::optional<CTxOut> tx_out =
ConsumeDeserializable<CTxOut>(fuzzed_data_provider);
const unsigned int n_in =
fuzzed_data_provider.ConsumeIntegral<unsigned int>();
if (mutable_transaction && tx_out &&
mutable_transaction->vin.size() > n_in) {
SignatureData signature_data_1 =
DataFromTransaction(*mutable_transaction, n_in, *tx_out);
CTxIn input;
UpdateInput(input, signature_data_1);
const CScript script = ConsumeScript(fuzzed_data_provider);
SignatureData signature_data_2{script};
signature_data_1.MergeSignatureData(signature_data_2);
}
if (mutable_transaction) {
CTransaction tx_from{*mutable_transaction};
CMutableTransaction tx_to;
const std::optional<CMutableTransaction> opt_tx_to =
ConsumeDeserializable<CMutableTransaction>(
fuzzed_data_provider);
if (opt_tx_to) {
tx_to = *opt_tx_to;
}
CMutableTransaction script_tx_to = tx_to;
CMutableTransaction sign_transaction_tx_to = tx_to;
if (n_in < tx_to.vin.size() &&
tx_to.vin[n_in].prevout.GetN() < tx_from.vout.size()) {
(void)SignSignature(
provider, tx_from, tx_to, n_in,
SigHashType(
fuzzed_data_provider.ConsumeIntegral<uint32_t>()));
}
if (n_in < script_tx_to.vin.size()) {
(void)SignSignature(
provider, ConsumeScript(fuzzed_data_provider), script_tx_to,
n_in, ConsumeMoney(fuzzed_data_provider),
SigHashType(
fuzzed_data_provider.ConsumeIntegral<uint32_t>()));
MutableTransactionSignatureCreator signature_creator{
&tx_to, n_in, ConsumeMoney(fuzzed_data_provider),
SigHashType(
fuzzed_data_provider.ConsumeIntegral<uint32_t>())};
std::vector<uint8_t> vch_sig;
CKeyID address;
if (fuzzed_data_provider.ConsumeBool()) {
if (k.IsValid()) {
address = k.GetPubKey().GetID();
}
} else {
address = CKeyID{ConsumeUInt160(fuzzed_data_provider)};
}
(void)signature_creator.CreateSig(
provider, vch_sig, address,
ConsumeScript(fuzzed_data_provider));
}
std::map<COutPoint, Coin> coins;
while (fuzzed_data_provider.ConsumeBool()) {
const std::optional<COutPoint> outpoint =
ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
if (!outpoint) {
break;
}
const std::optional<Coin> coin =
ConsumeDeserializable<Coin>(fuzzed_data_provider);
if (!coin) {
break;
}
coins[*outpoint] = *coin;
}
std::map<int, std::string> input_errors;
(void)SignTransaction(
sign_transaction_tx_to, &provider, coins,
SigHashType(fuzzed_data_provider.ConsumeIntegral<uint32_t>()),
input_errors);
}
}
{
SignatureData signature_data_1;
(void)ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR,
ConsumeScript(fuzzed_data_provider),
signature_data_1);
SignatureData signature_data_2;
(void)ProduceSignature(provider, DUMMY_MAXIMUM_SIGNATURE_CREATOR,
ConsumeScript(fuzzed_data_provider),
signature_data_2);
}
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index e3fed1486..10569ccfe 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -1,602 +1,601 @@
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_TEST_FUZZ_UTIL_H
#define BITCOIN_TEST_FUZZ_UTIL_H
#include <arith_uint256.h>
-#include <chainparamsbase.h>
#include <coins.h>
#include <consensus/amount.h>
#include <net.h>
#include <netbase.h>
#include <script/script.h>
#include <script/standard.h>
#include <serialize.h>
#include <streams.h>
#include <uint256.h>
#include <util/vector.h>
#include <version.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/net.h>
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <optional>
#include <string>
namespace fuzzer {
// FIXME find a better way to avoid duplicating the MAX_MONEY definition
constexpr int64_t MAX_MONEY_AS_INT = int64_t(21000000) * int64_t(100000000);
} // end namespace fuzzer
using namespace fuzzer;
class FuzzedSock : public Sock {
FuzzedDataProvider &m_fuzzed_data_provider;
public:
explicit FuzzedSock(FuzzedDataProvider &fuzzed_data_provider);
~FuzzedSock() override {}
FuzzedSock &operator=(Sock &&other) override;
SOCKET Get() const override;
SOCKET Release() override;
void Reset() override;
ssize_t Send(const void *data, size_t len, int flags) const override;
ssize_t Recv(void *buf, size_t len, int flags) const override;
std::unique_ptr<Sock> Accept(sockaddr *addr,
socklen_t *addr_len) const override;
bool Wait(std::chrono::milliseconds timeout, Event requested,
Event *occurred = nullptr) const override;
};
template <typename... Callables>
void CallOneOf(FuzzedDataProvider &fuzzed_data_provider,
Callables... callables) {
constexpr size_t call_size{sizeof...(callables)};
static_assert(call_size >= 1);
const size_t call_index{
fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, call_size - 1)};
size_t i{0};
return ((i++ == call_index ? callables() : void()), ...);
}
[[nodiscard]] inline std::vector<uint8_t>
ConsumeRandomLengthByteVector(FuzzedDataProvider &fuzzed_data_provider,
const size_t max_length = 4096) noexcept {
const std::string s =
fuzzed_data_provider.ConsumeRandomLengthString(max_length);
return {s.begin(), s.end()};
}
[[nodiscard]] inline CDataStream
ConsumeDataStream(FuzzedDataProvider &fuzzed_data_provider,
const size_t max_length = 4096) noexcept {
return CDataStream{
ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length),
SER_NETWORK, INIT_PROTO_VERSION};
}
[[nodiscard]] inline std::vector<std::string>
ConsumeRandomLengthStringVector(FuzzedDataProvider &fuzzed_data_provider,
const size_t max_vector_size = 16,
const size_t max_string_length = 16) noexcept {
const size_t n_elements =
fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size);
std::vector<std::string> r;
for (size_t i = 0; i < n_elements; ++i) {
r.push_back(
fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
}
return r;
}
template <typename T>
[[nodiscard]] inline std::vector<T>
ConsumeRandomLengthIntegralVector(FuzzedDataProvider &fuzzed_data_provider,
const size_t max_vector_size = 16) noexcept {
const size_t n_elements =
fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size);
std::vector<T> r;
for (size_t i = 0; i < n_elements; ++i) {
r.push_back(fuzzed_data_provider.ConsumeIntegral<T>());
}
return r;
}
template <typename T>
[[nodiscard]] inline std::optional<T>
ConsumeDeserializable(FuzzedDataProvider &fuzzed_data_provider,
const size_t max_length = 4096) noexcept {
const std::vector<uint8_t> buffer =
ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION};
T obj;
try {
ds >> obj;
} catch (const std::ios_base::failure &) {
return std::nullopt;
}
return obj;
}
template <typename WeakEnumType, size_t size>
[[nodiscard]] WeakEnumType
ConsumeWeakEnum(FuzzedDataProvider &fuzzed_data_provider,
const WeakEnumType (&all_types)[size]) noexcept {
return fuzzed_data_provider.ConsumeBool()
? fuzzed_data_provider.PickValueInArray<WeakEnumType>(all_types)
: WeakEnumType(
fuzzed_data_provider.ConsumeIntegral<
typename std::underlying_type<WeakEnumType>::type>());
}
[[nodiscard]] inline opcodetype
ConsumeOpcodeType(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return static_cast<opcodetype>(
fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE));
}
[[nodiscard]] inline Amount
ConsumeMoney(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(
0, MAX_MONEY_AS_INT) *
SATOSHI;
}
[[nodiscard]] inline int64_t
ConsumeTime(FuzzedDataProvider &fuzzed_data_provider) noexcept {
static const int64_t time_min =
ParseISO8601DateTime("1970-01-01T00:00:00Z");
static const int64_t time_max =
ParseISO8601DateTime("9999-12-31T23:59:59Z");
return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(time_min,
time_max);
}
[[nodiscard]] inline CScript
ConsumeScript(FuzzedDataProvider &fuzzed_data_provider) noexcept {
const std::vector<uint8_t> b =
ConsumeRandomLengthByteVector(fuzzed_data_provider);
return {b.begin(), b.end()};
}
[[nodiscard]] inline CScriptNum
ConsumeScriptNum(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
}
[[nodiscard]] inline uint160
ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept {
const std::vector<uint8_t> v160 =
fuzzed_data_provider.ConsumeBytes<uint8_t>(160 / 8);
if (v160.size() != 160 / 8) {
return {};
}
return uint160{v160};
}
[[nodiscard]] inline uint256
ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept {
const std::vector<uint8_t> v256 =
fuzzed_data_provider.ConsumeBytes<uint8_t>(256 / 8);
if (v256.size() != 256 / 8) {
return {};
}
return uint256{v256};
}
[[nodiscard]] inline arith_uint256
ConsumeArithUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return UintToArith256(ConsumeUInt256(fuzzed_data_provider));
}
[[nodiscard]] inline CTxDestination
ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept {
CTxDestination tx_destination;
CallOneOf(
fuzzed_data_provider, [&] { tx_destination = CNoDestination{}; },
[&] { tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)}; },
[&] {
tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
});
return tx_destination;
}
template <typename T>
[[nodiscard]] bool MultiplicationOverflow(const T i, const T j) noexcept {
static_assert(std::is_integral<T>::value, "Integral required.");
if (std::numeric_limits<T>::is_signed) {
if (i > 0) {
if (j > 0) {
return i > (std::numeric_limits<T>::max() / j);
} else {
return j < (std::numeric_limits<T>::min() / i);
}
} else {
if (j > 0) {
return i < (std::numeric_limits<T>::min() / j);
} else {
return i != 0 && (j < (std::numeric_limits<T>::max() / i));
}
}
} else {
return j != 0 && i > std::numeric_limits<T>::max() / j;
}
}
template <class T>
[[nodiscard]] bool AdditionOverflow(const T i, const T j) noexcept {
static_assert(std::is_integral<T>::value, "Integral required.");
if (std::numeric_limits<T>::is_signed) {
return (i > 0 && j > std::numeric_limits<T>::max() - i) ||
(i < 0 && j < std::numeric_limits<T>::min() - i);
}
return std::numeric_limits<T>::max() - i < j;
}
[[nodiscard]] inline bool
ContainsSpentInput(const CTransaction &tx,
const CCoinsViewCache &inputs) noexcept {
for (const CTxIn &tx_in : tx.vin) {
const Coin &coin = inputs.AccessCoin(tx_in.prevout);
if (coin.IsSpent()) {
return true;
}
}
return false;
}
/**
* Sets errno to a value selected from the given std::array `errnos`.
*/
template <typename T, size_t size>
void SetFuzzedErrNo(FuzzedDataProvider &fuzzed_data_provider,
const std::array<T, size> &errnos) {
errno = fuzzed_data_provider.PickValueInArray(errnos);
}
/**
* Sets a fuzzed errno in the range [0, 133 (EHWPOISON)]. Can be used from
* functions emulating standard library functions that set errno, or in other
* contexts where the value of errno might be relevant for the execution path
* that will be taken.
*/
inline void SetFuzzedErrNo(FuzzedDataProvider &fuzzed_data_provider) noexcept {
errno = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 133);
}
/**
* Returns a byte vector of specified size regardless of the number of remaining
* bytes available from the fuzzer. Pads with zero value bytes if needed to
* achieve the specified size.
*/
[[nodiscard]] inline std::vector<uint8_t>
ConsumeFixedLengthByteVector(FuzzedDataProvider &fuzzed_data_provider,
const size_t length) noexcept {
std::vector<uint8_t> result(length);
const std::vector<uint8_t> random_bytes =
fuzzed_data_provider.ConsumeBytes<uint8_t>(length);
if (!random_bytes.empty()) {
std::memcpy(result.data(), random_bytes.data(), random_bytes.size());
}
return result;
}
inline CNetAddr
ConsumeNetAddr(FuzzedDataProvider &fuzzed_data_provider) noexcept {
const Network network = fuzzed_data_provider.PickValueInArray(
{Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL,
Network::NET_ONION});
CNetAddr net_addr;
if (network == Network::NET_IPV4) {
const in_addr v4_addr = {
.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
net_addr = CNetAddr{v4_addr};
} else if (network == Network::NET_IPV6) {
if (fuzzed_data_provider.remaining_bytes() >= 16) {
in6_addr v6_addr = {};
memcpy(v6_addr.s6_addr,
fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
net_addr = CNetAddr{
v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
}
} else if (network == Network::NET_INTERNAL) {
net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
} else if (network == Network::NET_ONION) {
net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
}
return net_addr;
}
inline CSubNet
ConsumeSubNet(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return {ConsumeNetAddr(fuzzed_data_provider),
fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
}
inline CService
ConsumeService(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return {ConsumeNetAddr(fuzzed_data_provider),
fuzzed_data_provider.ConsumeIntegral<uint16_t>()};
}
inline CAddress
ConsumeAddress(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return {ConsumeService(fuzzed_data_provider),
ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS),
NodeSeconds{std::chrono::seconds{
fuzzed_data_provider.ConsumeIntegral<uint32_t>()}}};
}
template <bool ReturnUniquePtr = false>
auto ConsumeNode(
FuzzedDataProvider &fuzzed_data_provider,
const std::optional<NodeId> &node_id_in = std::nullopt) noexcept {
const NodeId node_id =
node_id_in.value_or(fuzzed_data_provider.ConsumeIntegral<NodeId>());
const auto sock = std::make_shared<FuzzedSock>(fuzzed_data_provider);
const CAddress address = ConsumeAddress(fuzzed_data_provider);
const uint64_t keyed_net_group =
fuzzed_data_provider.ConsumeIntegral<uint64_t>();
const uint64_t local_host_nonce =
fuzzed_data_provider.ConsumeIntegral<uint64_t>();
const uint64_t local_extra_entropy =
fuzzed_data_provider.ConsumeIntegral<uint64_t>();
const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider);
const std::string addr_name =
fuzzed_data_provider.ConsumeRandomLengthString(64);
const ConnectionType conn_type =
fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES);
const bool inbound_onion = fuzzed_data_provider.ConsumeBool();
if constexpr (ReturnUniquePtr) {
return std::make_unique<CNode>(node_id, sock, address, keyed_net_group,
local_host_nonce, local_extra_entropy,
addr_bind, addr_name, conn_type,
inbound_onion);
} else {
return CNode{node_id, sock,
address, keyed_net_group,
local_host_nonce, local_extra_entropy,
addr_bind, addr_name,
conn_type, inbound_onion};
}
}
inline std::unique_ptr<CNode>
ConsumeNodeAsUniquePtr(FuzzedDataProvider &fdp,
const std::optional<NodeId> &node_id_in = std::nullopt) {
return ConsumeNode<true>(fdp, node_id_in);
}
class FuzzedFileProvider {
FuzzedDataProvider &m_fuzzed_data_provider;
int64_t m_offset = 0;
public:
FuzzedFileProvider(FuzzedDataProvider &fuzzed_data_provider)
: m_fuzzed_data_provider{fuzzed_data_provider} {}
FILE *open() {
SetFuzzedErrNo(m_fuzzed_data_provider);
if (m_fuzzed_data_provider.ConsumeBool()) {
return nullptr;
}
std::string mode;
// clang-format off
CallOneOf(
m_fuzzed_data_provider,
[&] { mode = "r"; },
[&] { mode = "r+"; },
[&] { mode = "w"; },
[&] { mode = "w+"; },
[&] { mode = "a"; },
[&] { mode = "a+"; }
);
// clang-format on
#ifdef _GNU_SOURCE
const cookie_io_functions_t io_hooks = {
FuzzedFileProvider::read,
FuzzedFileProvider::write,
FuzzedFileProvider::seek,
FuzzedFileProvider::close,
};
return fopencookie(this, mode.c_str(), io_hooks);
#else
(void)mode;
return nullptr;
#endif
}
static ssize_t read(void *cookie, char *buf, size_t size) {
FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie;
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
if (buf == nullptr || size == 0 ||
fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
}
const std::vector<uint8_t> random_bytes =
fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
if (random_bytes.empty()) {
return 0;
}
std::memcpy(buf, random_bytes.data(), random_bytes.size());
if (AdditionOverflow(fuzzed_file->m_offset,
int64_t(random_bytes.size()))) {
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
}
fuzzed_file->m_offset += random_bytes.size();
return random_bytes.size();
}
static ssize_t write(void *cookie, const char *buf, size_t size) {
FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie;
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
const ssize_t n =
fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(
0, size);
if (AdditionOverflow(fuzzed_file->m_offset, int64_t(n))) {
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
}
fuzzed_file->m_offset += n;
return n;
}
static int seek(void *cookie, int64_t *offset, int whence) {
assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie;
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
int64_t new_offset = 0;
if (whence == SEEK_SET) {
new_offset = *offset;
} else if (whence == SEEK_CUR) {
if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
return -1;
}
new_offset = fuzzed_file->m_offset + *offset;
} else if (whence == SEEK_END) {
const int64_t n = fuzzed_file->m_fuzzed_data_provider
.ConsumeIntegralInRange<int64_t>(0, 4096);
if (AdditionOverflow(n, *offset)) {
return -1;
}
new_offset = n + *offset;
}
if (new_offset < 0) {
return -1;
}
fuzzed_file->m_offset = new_offset;
*offset = new_offset;
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(
-1, 0);
}
static int close(void *cookie) {
FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie;
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(
-1, 0);
}
};
[[nodiscard]] inline FuzzedFileProvider
ConsumeFile(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return {fuzzed_data_provider};
}
class FuzzedAutoFileProvider {
FuzzedFileProvider m_fuzzed_file_provider;
public:
FuzzedAutoFileProvider(FuzzedDataProvider &fuzzed_data_provider)
: m_fuzzed_file_provider{fuzzed_data_provider} {}
AutoFile open() { return AutoFile{m_fuzzed_file_provider.open()}; }
};
[[nodiscard]] inline FuzzedAutoFileProvider
ConsumeAutoFile(FuzzedDataProvider &fuzzed_data_provider) noexcept {
return {fuzzed_data_provider};
}
#define WRITE_TO_STREAM_CASE(type, consume) \
[&] { \
type o = consume; \
stream << o; \
}
template <typename Stream>
void WriteToStream(FuzzedDataProvider &fuzzed_data_provider,
Stream &stream) noexcept {
while (fuzzed_data_provider.ConsumeBool()) {
try {
CallOneOf(
fuzzed_data_provider,
WRITE_TO_STREAM_CASE(bool, fuzzed_data_provider.ConsumeBool()),
WRITE_TO_STREAM_CASE(
int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>()),
WRITE_TO_STREAM_CASE(
uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>()),
WRITE_TO_STREAM_CASE(
int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>()),
WRITE_TO_STREAM_CASE(
uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>()),
WRITE_TO_STREAM_CASE(
int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>()),
WRITE_TO_STREAM_CASE(
uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>()),
WRITE_TO_STREAM_CASE(
int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()),
WRITE_TO_STREAM_CASE(
uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
WRITE_TO_STREAM_CASE(
float, fuzzed_data_provider.ConsumeFloatingPoint<float>()),
WRITE_TO_STREAM_CASE(
double,
fuzzed_data_provider.ConsumeFloatingPoint<double>()),
WRITE_TO_STREAM_CASE(
std::string,
fuzzed_data_provider.ConsumeRandomLengthString(32)),
WRITE_TO_STREAM_CASE(std::vector<uint8_t>,
ConsumeRandomLengthIntegralVector<uint8_t>(
fuzzed_data_provider)));
} catch (const std::ios_base::failure &) {
break;
}
}
}
#define READ_FROM_STREAM_CASE(type) \
[&] { \
type o; \
stream >> o; \
}
template <typename Stream>
void ReadFromStream(FuzzedDataProvider &fuzzed_data_provider,
Stream &stream) noexcept {
while (fuzzed_data_provider.ConsumeBool()) {
try {
// clang-format off
CallOneOf(
fuzzed_data_provider,
READ_FROM_STREAM_CASE(bool),
READ_FROM_STREAM_CASE(int8_t),
READ_FROM_STREAM_CASE(uint8_t),
READ_FROM_STREAM_CASE(int16_t),
READ_FROM_STREAM_CASE(uint16_t),
READ_FROM_STREAM_CASE(int32_t),
READ_FROM_STREAM_CASE(uint32_t),
READ_FROM_STREAM_CASE(int64_t),
READ_FROM_STREAM_CASE(uint64_t),
READ_FROM_STREAM_CASE(float),
READ_FROM_STREAM_CASE(double),
READ_FROM_STREAM_CASE(std::string),
READ_FROM_STREAM_CASE(std::vector<uint8_t>)
);
// clang-format on
} catch (const std::ios_base::failure &) {
break;
}
}
}
[[nodiscard]] inline FuzzedSock
ConsumeSock(FuzzedDataProvider &fuzzed_data_provider) {
return FuzzedSock{fuzzed_data_provider};
}
#endif // BITCOIN_TEST_FUZZ_UTIL_H
diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp
index 6811d5e94..57e88f7c8 100644
--- a/src/test/fuzz/validation_load_mempool.cpp
+++ b/src/test/fuzz/validation_load_mempool.cpp
@@ -1,52 +1,51 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <kernel/mempool_persist.h>
-#include <chainparamsbase.h>
#include <mempool_args.h>
#include <node/mempool_persist_args.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/mempool_utils.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
#include <txmempool.h>
#include <util/time.h>
#include <validation.h>
#include <cstdint>
#include <vector>
using kernel::DumpMempool;
using node::MempoolPath;
namespace {
const TestingSetup *g_setup;
} // namespace
void initialize_validation_load_mempool() {
static const auto testing_setup =
MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool) {
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider);
CTxMemPool pool{GetConfig(), MemPoolOptionsForTest(g_setup->m_node)};
auto &chainstate{static_cast<DummyChainState &>(
g_setup->m_node.chainman->ActiveChainstate())};
chainstate.SetMempool(&pool);
auto fuzzed_fopen = [&](const fs::path &, const char *) {
return fuzzed_file_provider.open();
};
(void)chainstate.LoadMempool(MempoolPath(g_setup->m_args), fuzzed_fopen);
(void)DumpMempool(pool, MempoolPath(g_setup->m_args), fuzzed_fopen, true);
}
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 7fa685c8b..5914b5477 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -1,285 +1,284 @@
// Copyright (c) 2015-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_TEST_UTIL_SETUP_COMMON_H
#define BITCOIN_TEST_UTIL_SETUP_COMMON_H
#include <blockindex.h>
-#include <chainparamsbase.h>
#include <common/args.h>
#include <config.h>
#include <consensus/amount.h>
#include <key.h>
#include <node/caches.h>
#include <node/context.h>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <stdexcept>
#include <txmempool.h>
#include <util/chaintype.h>
#include <util/check.h>
#include <util/fs.h>
#include <util/string.h>
#include <util/vector.h>
#include <type_traits>
#include <vector>
class CFeeRate;
class Config;
class FastRandomContext;
// Enable BOOST_CHECK_EQUAL for enum class types
template <typename T>
std::ostream &operator<<(
typename std::enable_if<std::is_enum<T>::value, std::ostream>::type &stream,
const T &e) {
return stream << static_cast<typename std::underlying_type<T>::type>(e);
}
static constexpr Amount CENT(COIN / 100);
extern std::vector<const char *> fixture_extra_args;
/**
* Basic testing setup.
* This just configures logging, data dir and chain parameters.
*/
struct BasicTestingSetup {
// keep as first member to be destructed last
node::NodeContext m_node;
explicit BasicTestingSetup(
const ChainType chainType = ChainType::MAIN,
const std::vector<const char *> &extra_args = {});
~BasicTestingSetup();
const fs::path m_path_root;
ArgsManager m_args;
};
CTxMemPool::Options MemPoolOptionsForTest(const node::NodeContext &node);
/**
* Testing setup that performs all steps up until right before
* ChainstateManager gets initialized. Meant for testing ChainstateManager
* initialization behaviour.
*/
struct ChainTestingSetup : public BasicTestingSetup {
node::CacheSizes m_cache_sizes{};
explicit ChainTestingSetup(
const ChainType chainType = ChainType::MAIN,
const std::vector<const char *> &extra_args = {});
~ChainTestingSetup();
};
/**
* Testing setup that configures a complete environment.
*/
struct TestingSetup : public ChainTestingSetup {
bool m_coins_db_in_memory{true};
bool m_block_tree_db_in_memory{true};
void LoadVerifyActivateChainstate();
explicit TestingSetup(const ChainType chainType = ChainType::MAIN,
const std::vector<const char *> &extra_args = {},
const bool coins_db_in_memory = true,
const bool block_tree_db_in_memory = true);
};
/** Identical to TestingSetup, but chain set to regtest */
struct RegTestingSetup : public TestingSetup {
RegTestingSetup() : TestingSetup{ChainType::REGTEST} {}
};
class CBlock;
class Chainstate;
class CMutableTransaction;
class CScript;
/**
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
struct TestChain100Setup : public TestingSetup {
TestChain100Setup(const ChainType chain_type = ChainType::REGTEST,
const std::vector<const char *> &extra_args = {},
const bool coins_db_in_memory = true,
const bool block_tree_db_in_memory = true);
/**
* Create a new block with just given transactions, coinbase paying to
* scriptPubKey, and try to add it to the current chain.
* If no chainstate is specified, default to the active.
*/
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction> &txns,
const CScript &scriptPubKey,
Chainstate *chainstate = nullptr);
/**
* Create a new block with just given transactions, coinbase paying to
* scriptPubKey.
*/
CBlock CreateBlock(const std::vector<CMutableTransaction> &txns,
const CScript &scriptPubKey, Chainstate &chainstate);
//! Mine a series of new blocks on the active chain.
void mineBlocks(int num_blocks);
/**
* Create a transaction and submit to the mempool.
*
* @param input_transaction The transaction to spend
* @param input_vout The vout to spend from the input_transaction
* @param input_height The height of the block that included the
* input_transaction
* @param input_signing_key The key to spend the input_transaction
* @param output_destination Where to send the output
* @param output_amount How much to send
* @param submit Whether or not to submit to mempool
*/
CMutableTransaction CreateValidMempoolTransaction(
CTransactionRef input_transaction, int input_vout, int input_height,
CKey input_signing_key, CScript output_destination,
Amount output_amount = COIN, bool submit = true);
~TestChain100Setup();
/**
* Create transactions spending from m_coinbase_txns. These transactions
* will only spend coins that exist in the current chain, but may be
* premature coinbase spends, have missing signatures, or violate some other
* consensus rules. They should only be used for testing mempool
* consistency. All transactions will have some random number of inputs and
* outputs (between 1 and 24). Transactions may or may not be dependent upon
* each other; if dependencies exit, every parent will always be somewhere
* in the list before the child so each transaction can be submitted in the
* same order they appear in the list.
* @param[in] submit When true, submit transactions to the mempool.
* When false, return them but don't submit them.
* @returns A vector of transactions that can be submitted to the mempool.
*/
std::vector<CTransactionRef> PopulateMempool(FastRandomContext &det_rand,
size_t num_transactions,
bool submit);
/**
* Mock the mempool minimum feerate by adding a transaction and calling
* TrimToSize(0), simulating the mempool "reaching capacity" and evicting by
* descendant feerate. Note that this clears the mempool, and the new
* minimum feerate will depend on the maximum feerate of transactions
* removed, so this must be called while the mempool is empty.
*
* @param target_feerate The new mempool minimum feerate after this
* function returns. Must be above
* max(incremental feerate, min relay feerate), or
* 1 sat/B with default settings.
*/
void MockMempoolMinFee(const CFeeRate &target_feerate);
// For convenience, coinbase transactions.
std::vector<CTransactionRef> m_coinbase_txns;
// private/public key needed to spend coinbase transactions.
CKey coinbaseKey;
};
/**
* Make a test setup that has disk access to the debug.log file disabled. Can
* be used in "hot loops", for example fuzzing or benchmarking.
*/
template <class T = const BasicTestingSetup>
std::unique_ptr<T>
MakeNoLogFileContext(const ChainType chain_type = ChainType::REGTEST,
const std::vector<const char *> &extra_args = {}) {
const std::vector<const char *> arguments = Cat(
{
"-nodebuglogfile",
"-nodebug",
},
extra_args);
return std::make_unique<T>(chain_type, arguments);
}
struct TestMemPoolEntryHelper {
// Default values
Amount nFee;
int64_t nTime;
unsigned int nHeight;
unsigned int nSigChecks;
uint64_t entryId = 0;
TestMemPoolEntryHelper() : nFee(), nTime(0), nHeight(1), nSigChecks(1) {}
CTxMemPoolEntryRef FromTx(const CMutableTransaction &tx) const;
CTxMemPoolEntryRef FromTx(const CTransactionRef &tx) const;
// Change the default value
TestMemPoolEntryHelper &Fee(Amount _fee) {
nFee = _fee;
return *this;
}
TestMemPoolEntryHelper &Time(int64_t _time) {
nTime = _time;
return *this;
}
TestMemPoolEntryHelper &Height(unsigned int _height) {
nHeight = _height;
return *this;
}
TestMemPoolEntryHelper &SigChecks(unsigned int _nSigChecks) {
nSigChecks = _nSigChecks;
return *this;
}
TestMemPoolEntryHelper &EntryId(uint64_t _entryId) {
entryId = _entryId;
return *this;
}
};
enum class ScriptError;
// define implicit conversions here so that these types may be used in
// BOOST_*_EQUAL
std::ostream &operator<<(std::ostream &os, const uint256 &num);
std::ostream &operator<<(std::ostream &os, const ScriptError &err);
CBlock getBlock13b8a();
/**
* BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
* Use as
* BOOST_CHECK_EXCEPTION(code that throws, exception type, HasReason("foo"));
*/
class HasReason {
public:
explicit HasReason(const std::string &reason) : m_reason(reason) {}
bool operator()(const std::exception &e) const {
return std::string(e.what()).find(m_reason) != std::string::npos;
};
private:
const std::string m_reason;
};
// Dummy for subclassing in unittests
class DummyConfig : public Config {
public:
DummyConfig();
explicit DummyConfig(std::string net);
bool SetMaxBlockSize(uint64_t maxBlockSize) override { return false; }
uint64_t GetMaxBlockSize() const override { return 32'000'000; }
void SetChainParams(const CChainParams chainParamsIn) override {}
const CChainParams &GetChainParams() const override { return *chainParams; }
void SetCashAddrEncoding(bool) override {}
bool UseCashAddrEncoding() const override { return false; }
private:
std::unique_ptr<const CChainParams> chainParams;
};
#endif // BITCOIN_TEST_UTIL_SETUP_COMMON_H

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 17:04 (39 m, 36 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5865569
Default Alt Text
(271 KB)

Event Timeline