Initial Vexta Core v0.1.0 release

This commit is contained in:
Capri 2026-07-15 14:07:28 +00:00
commit 25c454329d
2481 changed files with 689289 additions and 0 deletions

185
src/util/asmap.cpp Normal file
View file

@ -0,0 +1,185 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <map>
#include <vector>
#include <assert.h>
#include <crypto/common.h>
namespace {
constexpr uint32_t INVALID = 0xFFFFFFFF;
uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos, uint8_t minval, const std::vector<uint8_t> &bit_sizes)
{
uint32_t val = minval;
bool bit;
for (std::vector<uint8_t>::const_iterator bit_sizes_it = bit_sizes.begin();
bit_sizes_it != bit_sizes.end(); ++bit_sizes_it) {
if (bit_sizes_it + 1 != bit_sizes.end()) {
if (bitpos == endpos) break;
bit = *bitpos;
bitpos++;
} else {
bit = 0;
}
if (bit) {
val += (1 << *bit_sizes_it);
} else {
for (int b = 0; b < *bit_sizes_it; b++) {
if (bitpos == endpos) return INVALID; // Reached EOF in mantissa
bit = *bitpos;
bitpos++;
val += bit << (*bit_sizes_it - 1 - b);
}
return val;
}
}
return INVALID; // Reached EOF in exponent
}
enum class Instruction : uint32_t
{
RETURN = 0,
JUMP = 1,
MATCH = 2,
DEFAULT = 3,
};
const std::vector<uint8_t> TYPE_BIT_SIZES{0, 0, 1};
Instruction DecodeType(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
{
return Instruction(DecodeBits(bitpos, endpos, 0, TYPE_BIT_SIZES));
}
const std::vector<uint8_t> ASN_BIT_SIZES{15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
uint32_t DecodeASN(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
{
return DecodeBits(bitpos, endpos, 1, ASN_BIT_SIZES);
}
const std::vector<uint8_t> MATCH_BIT_SIZES{1, 2, 3, 4, 5, 6, 7, 8};
uint32_t DecodeMatch(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
{
return DecodeBits(bitpos, endpos, 2, MATCH_BIT_SIZES);
}
const std::vector<uint8_t> JUMP_BIT_SIZES{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
uint32_t DecodeJump(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
{
return DecodeBits(bitpos, endpos, 17, JUMP_BIT_SIZES);
}
}
uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip)
{
std::vector<bool>::const_iterator pos = asmap.begin();
const std::vector<bool>::const_iterator endpos = asmap.end();
uint8_t bits = ip.size();
uint32_t default_asn = 0;
uint32_t jump, match, matchlen;
Instruction opcode;
while (pos != endpos) {
opcode = DecodeType(pos, endpos);
if (opcode == Instruction::RETURN) {
default_asn = DecodeASN(pos, endpos);
if (default_asn == INVALID) break; // ASN straddles EOF
return default_asn;
} else if (opcode == Instruction::JUMP) {
jump = DecodeJump(pos, endpos);
if (jump == INVALID) break; // Jump offset straddles EOF
if (bits == 0) break; // No input bits left
if (int64_t{jump} >= int64_t{endpos - pos}) break; // Jumping past EOF
if (ip[ip.size() - bits]) {
pos += jump;
}
bits--;
} else if (opcode == Instruction::MATCH) {
match = DecodeMatch(pos, endpos);
if (match == INVALID) break; // Match bits straddle EOF
matchlen = CountBits(match) - 1;
if (bits < matchlen) break; // Not enough input bits
for (uint32_t bit = 0; bit < matchlen; bit++) {
if ((ip[ip.size() - bits]) != ((match >> (matchlen - 1 - bit)) & 1)) {
return default_asn;
}
bits--;
}
} else if (opcode == Instruction::DEFAULT) {
default_asn = DecodeASN(pos, endpos);
if (default_asn == INVALID) break; // ASN straddles EOF
} else {
break; // Instruction straddles EOF
}
}
assert(false); // Reached EOF without RETURN, or aborted (see any of the breaks above) - should have been caught by SanityCheckASMap below
return 0; // 0 is not a valid ASN
}
bool SanityCheckASMap(const std::vector<bool>& asmap, int bits)
{
const std::vector<bool>::const_iterator begin = asmap.begin(), endpos = asmap.end();
std::vector<bool>::const_iterator pos = begin;
std::vector<std::pair<uint32_t, int>> jumps; // All future positions we may jump to (bit offset in asmap -> bits to consume left)
jumps.reserve(bits);
Instruction prevopcode = Instruction::JUMP;
bool had_incomplete_match = false;
while (pos != endpos) {
uint32_t offset = pos - begin;
if (!jumps.empty() && offset >= jumps.back().first) return false; // There was a jump into the middle of the previous instruction
Instruction opcode = DecodeType(pos, endpos);
if (opcode == Instruction::RETURN) {
if (prevopcode == Instruction::DEFAULT) return false; // There should not be any RETURN immediately after a DEFAULT (could be combined into just RETURN)
uint32_t asn = DecodeASN(pos, endpos);
if (asn == INVALID) return false; // ASN straddles EOF
if (jumps.empty()) {
// Nothing to execute anymore
if (endpos - pos > 7) return false; // Excessive padding
while (pos != endpos) {
if (*pos) return false; // Nonzero padding bit
++pos;
}
return true; // Sanely reached EOF
} else {
// Continue by pretending we jumped to the next instruction
offset = pos - begin;
if (offset != jumps.back().first) return false; // Unreachable code
bits = jumps.back().second; // Restore the number of bits we would have had left after this jump
jumps.pop_back();
prevopcode = Instruction::JUMP;
}
} else if (opcode == Instruction::JUMP) {
uint32_t jump = DecodeJump(pos, endpos);
if (jump == INVALID) return false; // Jump offset straddles EOF
if (int64_t{jump} > int64_t{endpos - pos}) return false; // Jump out of range
if (bits == 0) return false; // Consuming bits past the end of the input
--bits;
uint32_t jump_offset = pos - begin + jump;
if (!jumps.empty() && jump_offset >= jumps.back().first) return false; // Intersecting jumps
jumps.emplace_back(jump_offset, bits);
prevopcode = Instruction::JUMP;
} else if (opcode == Instruction::MATCH) {
uint32_t match = DecodeMatch(pos, endpos);
if (match == INVALID) return false; // Match bits straddle EOF
int matchlen = CountBits(match) - 1;
if (prevopcode != Instruction::MATCH) had_incomplete_match = false;
if (matchlen < 8 && had_incomplete_match) return false; // Within a sequence of matches only at most one should be incomplete
had_incomplete_match = (matchlen < 8);
if (bits < matchlen) return false; // Consuming bits past the end of the input
bits -= matchlen;
prevopcode = Instruction::MATCH;
} else if (opcode == Instruction::DEFAULT) {
if (prevopcode == Instruction::DEFAULT) return false; // There should not be two successive DEFAULTs (they could be combined into one)
uint32_t asn = DecodeASN(pos, endpos);
if (asn == INVALID) return false; // ASN straddles EOF
prevopcode = Instruction::DEFAULT;
} else {
return false; // Instruction straddles EOF
}
}
return false; // Reached EOF without RETURN instruction
}

15
src/util/asmap.h Normal file
View file

@ -0,0 +1,15 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_ASMAP_H
#define DIGIBYTE_UTIL_ASMAP_H
#include <stdint.h>
#include <vector>
uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip);
bool SanityCheckASMap(const std::vector<bool>& asmap, int bits);
#endif // DIGIBYTE_UTIL_ASMAP_H

66
src/util/bip32.cpp Normal file
View file

@ -0,0 +1,66 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sstream>
#include <stdio.h>
#include <tinyformat.h>
#include <util/bip32.h>
#include <util/strencodings.h>
bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath)
{
std::stringstream ss(keypath_str);
std::string item;
bool first = true;
while (std::getline(ss, item, '/')) {
if (item.compare("m") == 0) {
if (first) {
first = false;
continue;
}
return false;
}
// Finds whether it is hardened
uint32_t path = 0;
size_t pos = item.find("'");
if (pos != std::string::npos) {
// The hardened tick can only be in the last index of the string
if (pos != item.size() - 1) {
return false;
}
path |= 0x80000000;
item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick
}
// Ensure this is only numbers
if (item.find_first_not_of( "0123456789" ) != std::string::npos) {
return false;
}
uint32_t number;
if (!ParseUInt32(item, &number)) {
return false;
}
path |= number;
keypath.push_back(path);
first = false;
}
return true;
}
std::string FormatHDKeypath(const std::vector<uint32_t>& path)
{
std::string ret;
for (auto i : path) {
ret += strprintf("/%i", (i << 1) >> 1);
if (i >> 31) ret += '\'';
}
return ret;
}
std::string WriteHDKeypath(const std::vector<uint32_t>& keypath)
{
return "m" + FormatHDKeypath(keypath);
}

20
src/util/bip32.h Normal file
View file

@ -0,0 +1,20 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_BIP32_H
#define DIGIBYTE_UTIL_BIP32_H
#include <attributes.h>
#include <string>
#include <vector>
#include <cstdint>
/** Parse an HD keypaths like "m/7/0'/2000". */
[[nodiscard]] bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath);
/** Write HD keypaths as strings */
std::string WriteHDKeypath(const std::vector<uint32_t>& keypath);
std::string FormatHDKeypath(const std::vector<uint32_t>& path);
#endif // DIGIBYTE_UTIL_BIP32_H

View file

@ -0,0 +1,18 @@
// Copyright (c) 2018 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <crypto/siphash.h>
#include <random.h>
#include <util/bytevectorhash.h>
ByteVectorHash::ByteVectorHash()
{
GetRandBytes(reinterpret_cast<unsigned char*>(&m_k0), sizeof(m_k0));
GetRandBytes(reinterpret_cast<unsigned char*>(&m_k1), sizeof(m_k1));
}
size_t ByteVectorHash::operator()(const std::vector<unsigned char>& input) const
{
return CSipHasher(m_k0, m_k1).Write(input.data(), input.size()).Finalize();
}

26
src/util/bytevectorhash.h Normal file
View file

@ -0,0 +1,26 @@
// Copyright (c) 2018 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_BYTEVECTORHASH_H
#define DIGIBYTE_UTIL_BYTEVECTORHASH_H
#include <stdint.h>
#include <vector>
/**
* Implementation of Hash named requirement for types that internally store a byte array. This may
* be used as the hash function in std::unordered_set or std::unordered_map over such types.
* Internally, this uses a random instance of SipHash-2-4.
*/
class ByteVectorHash final
{
private:
uint64_t m_k0, m_k1;
public:
ByteVectorHash();
size_t operator()(const std::vector<unsigned char>& input) const;
};
#endif // DIGIBYTE_UTIL_BYTEVECTORHASH_H

75
src/util/check.h Normal file
View file

@ -0,0 +1,75 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_CHECK_H
#define DIGIBYTE_UTIL_CHECK_H
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <tinyformat.h>
#include <stdexcept>
class NonFatalCheckError : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
/**
* Throw a NonFatalCheckError when the condition evaluates to false
*
* This should only be used
* - where the condition is assumed to be true, not for error handling or validating user input
* - where a failure to fulfill the condition is recoverable and does not abort the program
*
* For example in RPC code, where it is undesirable to crash the whole program, this can be generally used to replace
* asserts or recoverable logic errors. A NonFatalCheckError in RPC code is caught and passed as a string to the RPC
* caller, which can then report the issue to the developers.
*/
#define CHECK_NONFATAL(condition) \
do { \
if (!(condition)) { \
throw NonFatalCheckError( \
strprintf("%s:%d (%s)\n" \
"Internal bug detected: '%s'\n" \
"You may report this issue here: %s\n", \
__FILE__, __LINE__, __func__, \
(#condition), \
PACKAGE_BUGREPORT)); \
} \
} while (false)
#if defined(NDEBUG)
#error "Cannot compile without assertions!"
#endif
/** Helper for Assert() */
template <typename T>
T get_pure_r_value(T&& val)
{
return std::forward<T>(val);
}
/** Identity function. Abort if the value compares equal to zero */
#define Assert(val) ([&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); assert(#val && check); return std::forward<decltype(get_pure_r_value(val))>(check); }())
/**
* Assume is the identity function.
*
* - Should be used to run non-fatal checks. In debug builds it behaves like
* Assert()/assert() to notify developers and testers about non-fatal errors.
* In production it doesn't warn or log anything.
* - For fatal errors, use Assert().
* - For non-fatal errors in interactive sessions (e.g. RPC or command line
* interfaces), CHECK_NONFATAL() might be more appropriate.
*/
#ifdef ABORT_ON_FAILED_ASSUME
#define Assume(val) Assert(val)
#else
#define Assume(val) ([&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); return std::forward<decltype(get_pure_r_value(val))>(check); }())
#endif
#endif // DIGIBYTE_UTIL_CHECK_H

91
src/util/epochguard.h Normal file
View file

@ -0,0 +1,91 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_EPOCHGUARD_H
#define DIGIBYTE_UTIL_EPOCHGUARD_H
#include <threadsafety.h>
#include <cassert>
/** Epoch: RAII-style guard for using epoch-based graph traversal algorithms.
* When walking ancestors or descendants, we generally want to avoid
* visiting the same transactions twice. Some traversal algorithms use
* std::set (or setEntries) to deduplicate the transaction we visit.
* However, use of std::set is algorithmically undesirable because it both
* adds an asymptotic factor of O(log n) to traversals cost and triggers O(n)
* more dynamic memory allocations.
* In many algorithms we can replace std::set with an internal mempool
* counter to track the time (or, "epoch") that we began a traversal, and
* check + update a per-transaction epoch for each transaction we look at to
* determine if that transaction has not yet been visited during the current
* traversal's epoch.
* Algorithms using std::set can be replaced on a one by one basis.
* Both techniques are not fundamentally incompatible across the codebase.
* Generally speaking, however, the remaining use of std::set for mempool
* traversal should be viewed as a TODO for replacement with an epoch based
* traversal, rather than a preference for std::set over epochs in that
* algorithm.
*/
class LOCKABLE Epoch
{
private:
uint64_t m_raw_epoch = 0;
bool m_guarded = false;
public:
Epoch() = default;
Epoch(const Epoch&) = delete;
Epoch& operator=(const Epoch&) = delete;
bool guarded() const { return m_guarded; }
class Marker
{
private:
uint64_t m_marker = 0;
// only allow modification via Epoch member functions
friend class Epoch;
Marker& operator=(const Marker&) = delete;
};
class SCOPED_LOCKABLE Guard
{
private:
Epoch& m_epoch;
public:
explicit Guard(Epoch& epoch) EXCLUSIVE_LOCK_FUNCTION(epoch) : m_epoch(epoch)
{
assert(!m_epoch.m_guarded);
++m_epoch.m_raw_epoch;
m_epoch.m_guarded = true;
}
~Guard() UNLOCK_FUNCTION()
{
assert(m_epoch.m_guarded);
++m_epoch.m_raw_epoch; // ensure clear separation between epochs
m_epoch.m_guarded = false;
}
};
bool visited(Marker& marker) const EXCLUSIVE_LOCKS_REQUIRED(*this)
{
assert(m_guarded);
if (marker.m_marker < m_raw_epoch) {
// marker is from a previous epoch, so this is its first visit
marker.m_marker = m_raw_epoch;
return false;
} else {
return true;
}
}
};
#define WITH_FRESH_EPOCH(epoch) const Epoch::Guard PASTE2(epoch_guard_, __COUNTER__)(epoch)
#endif // DIGIBYTE_UTIL_EPOCHGUARD_H

56
src/util/error.cpp Normal file
View file

@ -0,0 +1,56 @@
// Copyright (c) 2010-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/error.h>
#include <tinyformat.h>
#include <util/system.h>
#include <util/translation.h>
bilingual_str TransactionErrorString(const TransactionError err)
{
switch (err) {
case TransactionError::OK:
return Untranslated("No error");
case TransactionError::MISSING_INPUTS:
return Untranslated("Inputs missing or spent");
case TransactionError::ALREADY_IN_CHAIN:
return Untranslated("Transaction already in block chain");
case TransactionError::P2P_DISABLED:
return Untranslated("Peer-to-peer functionality missing or disabled");
case TransactionError::MEMPOOL_REJECTED:
return Untranslated("Transaction rejected by AcceptToMemoryPool");
case TransactionError::MEMPOOL_ERROR:
return Untranslated("AcceptToMemoryPool failed");
case TransactionError::INVALID_PSBT:
return Untranslated("PSBT is not well-formed");
case TransactionError::PSBT_MISMATCH:
return Untranslated("PSBTs not compatible (different transactions)");
case TransactionError::SIGHASH_MISMATCH:
return Untranslated("Specified sighash value does not match value stored in PSBT");
case TransactionError::MAX_FEE_EXCEEDED:
return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
case TransactionError::EXTERNAL_SIGNER_NOT_FOUND:
return Untranslated("External signer not found");
case TransactionError::EXTERNAL_SIGNER_FAILED:
return Untranslated("External signer failed to sign");
// no default case, so the compiler can warn about missing cases
}
assert(false);
}
bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBind)
{
return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
}
bilingual_str AmountHighWarn(const std::string& optname)
{
return strprintf(_("%s is set very high!"), optname);
}
bilingual_str AmountErrMsg(const std::string& optname, const std::string& strValue)
{
return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
}

45
src/util/error.h Normal file
View file

@ -0,0 +1,45 @@
// Copyright (c) 2010-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_ERROR_H
#define DIGIBYTE_UTIL_ERROR_H
/**
* util/error.h is a common place for definitions of simple error types and
* string functions. Types and functions defined here should not require any
* outside dependencies.
*
* Error types defined here can be used in different parts of the
* codebase, to avoid the need to write boilerplate code catching and
* translating errors passed across wallet/node/rpc/gui code boundaries.
*/
#include <string>
struct bilingual_str;
enum class TransactionError {
OK, //!< No error
MISSING_INPUTS,
ALREADY_IN_CHAIN,
P2P_DISABLED,
MEMPOOL_REJECTED,
MEMPOOL_ERROR,
INVALID_PSBT,
PSBT_MISMATCH,
SIGHASH_MISMATCH,
MAX_FEE_EXCEEDED,
EXTERNAL_SIGNER_NOT_FOUND,
EXTERNAL_SIGNER_FAILED,
};
bilingual_str TransactionErrorString(const TransactionError error);
bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBind);
bilingual_str AmountHighWarn(const std::string& optname);
bilingual_str AmountErrMsg(const std::string& optname, const std::string& strValue);
#endif // DIGIBYTE_UTIL_ERROR_H

67
src/util/fees.cpp Normal file
View file

@ -0,0 +1,67 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/fees.h>
#include <policy/fees.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <map>
#include <string>
#include <vector>
#include <utility>
std::string StringForFeeReason(FeeReason reason)
{
static const std::map<FeeReason, std::string> fee_reason_strings = {
{FeeReason::NONE, "None"},
{FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
{FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
{FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
{FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
{FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
{FeeReason::PAYTXFEE, "PayTxFee set"},
{FeeReason::FALLBACK, "Fallback fee"},
{FeeReason::REQUIRED, "Minimum Required Fee"},
};
auto reason_string = fee_reason_strings.find(reason);
if (reason_string == fee_reason_strings.end()) return "Unknown";
return reason_string->second;
}
const std::vector<std::pair<std::string, FeeEstimateMode>>& FeeModeMap()
{
static const std::vector<std::pair<std::string, FeeEstimateMode>> FEE_MODES = {
{"unset", FeeEstimateMode::UNSET},
{"economical", FeeEstimateMode::ECONOMICAL},
{"conservative", FeeEstimateMode::CONSERVATIVE},
};
return FEE_MODES;
}
std::string FeeModes(const std::string& delimiter)
{
return Join(FeeModeMap(), delimiter, [&](const std::pair<std::string, FeeEstimateMode>& i) { return i.first; });
}
const std::string InvalidEstimateModeErrorMessage()
{
return "Invalid estimate_mode parameter, must be one of: \"" + FeeModes("\", \"") + "\"";
}
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode)
{
auto searchkey = ToUpper(mode_string);
for (const auto& pair : FeeModeMap()) {
if (ToUpper(pair.first) == searchkey) {
fee_estimate_mode = pair.second;
return true;
}
}
return false;
}

18
src/util/fees.h Normal file
View file

@ -0,0 +1,18 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_FEES_H
#define DIGIBYTE_UTIL_FEES_H
#include <string>
enum class FeeEstimateMode;
enum class FeeReason;
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
std::string StringForFeeReason(FeeReason reason);
std::string FeeModes(const std::string& delimiter);
const std::string InvalidEstimateModeErrorMessage();
#endif // DIGIBYTE_UTIL_FEES_H

View file

@ -0,0 +1,10 @@
#include <random.h>
#include <fs.h>
#include <util/strencodings.h>
fs::path GetUniquePath(const fs::path& base)
{
FastRandomContext rnd;
fs::path tmpFile = base / HexStr(rnd.randbytes(8));
return tmpFile;
}

19
src/util/getuniquepath.h Normal file
View file

@ -0,0 +1,19 @@
// Copyright (c) 2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_GETUNIQUEPATH_H
#define DIGIBYTE_UTIL_GETUNIQUEPATH_H
#include <fs.h>
/**
* Helper function for getting a unique path
*
* @param[in] base Base path
* @returns base joined with a random 8-character long string.
* @post Returned path is unique with high probability.
*/
fs::path GetUniquePath(const fs::path& base);
#endif // DIGIBYTE_UTIL_GETUNIQUEPATH_H

43
src/util/golombrice.h Normal file
View file

@ -0,0 +1,43 @@
// Copyright (c) 2018-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_GOLOMBRICE_H
#define DIGIBYTE_UTIL_GOLOMBRICE_H
#include <streams.h>
#include <cstdint>
template <typename OStream>
void GolombRiceEncode(BitStreamWriter<OStream>& bitwriter, uint8_t P, uint64_t x)
{
// Write quotient as unary-encoded: q 1's followed by one 0.
uint64_t q = x >> P;
while (q > 0) {
int nbits = q <= 64 ? static_cast<int>(q) : 64;
bitwriter.Write(~0ULL, nbits);
q -= nbits;
}
bitwriter.Write(0, 1);
// Write the remainder in P bits. Since the remainder is just the bottom
// P bits of x, there is no need to mask first.
bitwriter.Write(x, P);
}
template <typename IStream>
uint64_t GolombRiceDecode(BitStreamReader<IStream>& bitreader, uint8_t P)
{
// Read unary-encoded quotient: q 1's followed by one 0.
uint64_t q = 0;
while (bitreader.Read(1) == 1) {
++q;
}
uint64_t r = bitreader.Read(P);
return (q << P) + r;
}
#endif // DIGIBYTE_UTIL_GOLOMBRICE_H

72
src/util/hash_type.h Normal file
View file

@ -0,0 +1,72 @@
// Copyright (c) 2020-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_HASH_TYPE_H
#define DIGIBYTE_UTIL_HASH_TYPE_H
template <typename HashType>
class BaseHash
{
protected:
HashType m_hash;
public:
BaseHash() : m_hash() {}
explicit BaseHash(const HashType& in) : m_hash(in) {}
unsigned char* begin()
{
return m_hash.begin();
}
const unsigned char* begin() const
{
return m_hash.begin();
}
unsigned char* end()
{
return m_hash.end();
}
const unsigned char* end() const
{
return m_hash.end();
}
operator std::vector<unsigned char>() const
{
return std::vector<unsigned char>{m_hash.begin(), m_hash.end()};
}
std::string ToString() const
{
return m_hash.ToString();
}
bool operator==(const BaseHash<HashType>& other) const noexcept
{
return m_hash == other.m_hash;
}
bool operator!=(const BaseHash<HashType>& other) const noexcept
{
return !(m_hash == other.m_hash);
}
bool operator<(const BaseHash<HashType>& other) const noexcept
{
return m_hash < other.m_hash;
}
size_t size() const
{
return m_hash.size();
}
unsigned char* data() { return m_hash.data(); }
const unsigned char* data() const { return m_hash.data(); }
};
#endif // DIGIBYTE_UTIL_HASH_TYPE_H

19
src/util/hasher.cpp Normal file
View file

@ -0,0 +1,19 @@
// Copyright (c) 2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <random.h>
#include <util/hasher.h>
#include <limits>
SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
SaltedSipHasher::SaltedSipHasher() : m_k0(GetRand(std::numeric_limits<uint64_t>::max())), m_k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
size_t SaltedSipHasher::operator()(const Span<const unsigned char>& script) const
{
return CSipHasher(m_k0, m_k1).Write(script.data(), script.size()).Finalize();
}

99
src/util/hasher.h Normal file
View file

@ -0,0 +1,99 @@
// Copyright (c) 2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_HASHER_H
#define DIGIBYTE_UTIL_HASHER_H
#include <crypto/siphash.h>
#include <primitives/transaction.h>
#include <uint256.h>
class SaltedTxidHasher
{
private:
/** Salt */
const uint64_t k0, k1;
public:
SaltedTxidHasher();
size_t operator()(const uint256& txid) const {
return SipHashUint256(k0, k1, txid);
}
};
class SaltedOutpointHasher
{
private:
/** Salt */
const uint64_t k0, k1;
public:
SaltedOutpointHasher();
/**
* This *must* return size_t. With Boost 1.46 on 32-bit systems the
* unordered_map will behave unpredictably if the custom hasher returns a
* uint64_t, resulting in failures when syncing the chain (#4634).
*
* Having the hash noexcept allows libstdc++'s unordered_map to recalculate
* the hash during rehash, so it does not have to cache the value. This
* reduces node's memory by sizeof(size_t). The required recalculation has
* a slight performance penalty (around 1.6%), but this is compensated by
* memory savings of about 9% which allow for a larger dbcache setting.
*
* @see https://gcc.gnu.org/onlinedocs/gcc-9.2.0/libstdc++/manual/manual/unordered_associative.html
*/
size_t operator()(const COutPoint& id) const noexcept {
return SipHashUint256Extra(k0, k1, id.hash, id.n);
}
};
struct FilterHeaderHasher
{
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
};
/**
* We're hashing a nonce into the entries themselves, so we don't need extra
* blinding in the set hash computation.
*
* This may exhibit platform endian dependent behavior but because these are
* nonced hashes (random) and this state is only ever used locally it is safe.
* All that matters is local consistency.
*/
class SignatureCacheHasher
{
public:
template <uint8_t hash_select>
uint32_t operator()(const uint256& key) const
{
static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
uint32_t u;
std::memcpy(&u, key.begin()+4*hash_select, 4);
return u;
}
};
struct BlockHasher
{
// this used to call `GetCheapHash()` in uint256, which was later moved; the
// cheap hash function simply calls ReadLE64() however, so the end result is
// identical
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
};
class SaltedSipHasher
{
private:
/** Salt */
const uint64_t m_k0, m_k1;
public:
SaltedSipHasher();
size_t operator()(const Span<const unsigned char>& script) const;
};
#endif // DIGIBYTE_UTIL_HASHER_H

18
src/util/macros.h Normal file
View file

@ -0,0 +1,18 @@
// Copyright (c) 2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_MACROS_H
#define DIGIBYTE_UTIL_MACROS_H
#define PASTE(x, y) x ## y
#define PASTE2(x, y) PASTE(x, y)
/**
* Converts the parameter X to a string after macro replacement on X has been performed.
* Don't merge these into one macro!
*/
#define STRINGIZE(X) DO_STRINGIZE(X)
#define DO_STRINGIZE(X) #X
#endif // DIGIBYTE_UTIL_MACROS_H

92
src/util/message.cpp Normal file
View file

@ -0,0 +1,92 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <hash.h> // For CHashWriter
#include <key.h> // For CKey
#include <key_io.h> // For DecodeDestination()
#include <pubkey.h> // For CPubKey
#include <script/standard.h> // For CTxDestination, IsValidDestination(), PKHash
#include <serialize.h> // For SER_GETHASH
#include <util/message.h>
#include <util/strencodings.h> // For DecodeBase64()
#include <string>
#include <vector>
/**
* Text used to signify that a signed message follows and to prevent
* inadvertently signing a transaction.
*/
const std::string MESSAGE_MAGIC = "Vexta Signed Message:\n";
MessageVerificationResult MessageVerify(
const std::string& address,
const std::string& signature,
const std::string& message)
{
CTxDestination destination = DecodeDestination(address);
if (!IsValidDestination(destination)) {
return MessageVerificationResult::ERR_INVALID_ADDRESS;
}
if (std::get_if<PKHash>(&destination) == nullptr) {
return MessageVerificationResult::ERR_ADDRESS_NO_KEY;
}
bool invalid = false;
std::vector<unsigned char> signature_bytes = DecodeBase64(signature.c_str(), &invalid);
if (invalid) {
return MessageVerificationResult::ERR_MALFORMED_SIGNATURE;
}
CPubKey pubkey;
if (!pubkey.RecoverCompact(MessageHash(message), signature_bytes)) {
return MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED;
}
if (!(CTxDestination(PKHash(pubkey)) == destination)) {
return MessageVerificationResult::ERR_NOT_SIGNED;
}
return MessageVerificationResult::OK;
}
bool MessageSign(
const CKey& privkey,
const std::string& message,
std::string& signature)
{
std::vector<unsigned char> signature_bytes;
if (!privkey.SignCompact(MessageHash(message), signature_bytes)) {
return false;
}
signature = EncodeBase64(signature_bytes);
return true;
}
uint256 MessageHash(const std::string& message)
{
CHashWriter hasher(SER_GETHASH, 0);
hasher << MESSAGE_MAGIC << message;
return hasher.GetHash();
}
std::string SigningResultString(const SigningResult res)
{
switch (res) {
case SigningResult::OK:
return "No error";
case SigningResult::PRIVATE_KEY_NOT_AVAILABLE:
return "Private key not available";
case SigningResult::SIGNING_FAILED:
return "Sign failed";
// no default case, so the compiler can warn about missing cases
}
assert(false);
}

76
src/util/message.h Normal file
View file

@ -0,0 +1,76 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_MESSAGE_H
#define DIGIBYTE_UTIL_MESSAGE_H
#include <key.h> // For CKey
#include <uint256.h>
#include <string>
extern const std::string MESSAGE_MAGIC;
/** The result of a signed message verification.
* Message verification takes as an input:
* - address (with whose private key the message is supposed to have been signed)
* - signature
* - message
*/
enum class MessageVerificationResult {
//! The provided address is invalid.
ERR_INVALID_ADDRESS,
//! The provided address is valid but does not refer to a public key.
ERR_ADDRESS_NO_KEY,
//! The provided signature couldn't be parsed (maybe invalid base64).
ERR_MALFORMED_SIGNATURE,
//! A public key could not be recovered from the provided signature and message.
ERR_PUBKEY_NOT_RECOVERED,
//! The message was not signed with the private key of the provided address.
ERR_NOT_SIGNED,
//! The message verification was successful.
OK
};
enum class SigningResult {
OK, //!< No error
PRIVATE_KEY_NOT_AVAILABLE,
SIGNING_FAILED,
};
/** Verify a signed message.
* @param[in] address Signer's Vexta address, it must refer to a public key.
* @param[in] signature The signature in base64 format.
* @param[in] message The message that was signed.
* @return result code */
MessageVerificationResult MessageVerify(
const std::string& address,
const std::string& signature,
const std::string& message);
/** Sign a message.
* @param[in] privkey Private key to sign with.
* @param[in] message The message to sign.
* @param[out] signature Signature, base64 encoded, only set if true is returned.
* @return true if signing was successful. */
bool MessageSign(
const CKey& privkey,
const std::string& message,
std::string& signature);
/**
* Hashes a message for signing and verification in a manner that prevents
* inadvertently signing a transaction.
*/
uint256 MessageHash(const std::string& message);
std::string SigningResultString(const SigningResult res);
#endif // DIGIBYTE_UTIL_MESSAGE_H

83
src/util/moneystr.cpp Normal file
View file

@ -0,0 +1,83 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Copyright (c) 2014-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/moneystr.h>
#include <tinyformat.h>
#include <util/strencodings.h>
#include <util/string.h>
std::string FormatMoney(const CAmount n)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
static_assert(COIN > 1);
int64_t quotient = n / COIN;
int64_t remainder = n % COIN;
if (n < 0) {
quotient = -quotient;
remainder = -remainder;
}
std::string str = strprintf("%d.%08d", quotient, remainder);
// Right-trim excess zeros before the decimal point:
int nTrim = 0;
for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
++nTrim;
if (nTrim)
str.erase(str.size()-nTrim, nTrim);
if (n < 0)
str.insert((unsigned int)0, 1, '-');
return str;
}
bool ParseMoney(const std::string& money_string, CAmount& nRet)
{
if (!ValidAsCString(money_string)) {
return false;
}
const std::string str = TrimString(money_string);
if (str.empty()) {
return false;
}
std::string strWhole;
int64_t nUnits = 0;
const char* p = str.c_str();
for (; *p; p++)
{
if (*p == '.')
{
p++;
int64_t nMult = COIN / 10;
while (IsDigit(*p) && (nMult > 0))
{
nUnits += nMult * (*p++ - '0');
nMult /= 10;
}
break;
}
if (IsSpace(*p))
return false;
if (!IsDigit(*p))
return false;
strWhole.insert(strWhole.end(), *p);
}
if (*p) {
return false;
}
if (strWhole.size() > 10) // guard against 63 bit overflow
return false;
if (nUnits < 0 || nUnits > COIN)
return false;
int64_t nWhole = atoi64(strWhole);
CAmount nValue = nWhole*COIN + nUnits;
nRet = nValue;
return true;
}

25
src/util/moneystr.h Normal file
View file

@ -0,0 +1,25 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Copyright (c) 2014-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/**
* Money parsing/formatting utilities.
*/
#ifndef DIGIBYTE_UTIL_MONEYSTR_H
#define DIGIBYTE_UTIL_MONEYSTR_H
#include <amount.h>
#include <attributes.h>
#include <string>
/* Do not use these functions to represent or parse monetary amounts to or from
* JSON but use AmountFromValue and ValueFromAmount for that.
*/
std::string FormatMoney(const CAmount n);
/** Parse an amount denoted in full coins. E.g. "0.0034" supplied on the command line. **/
[[nodiscard]] bool ParseMoney(const std::string& str, CAmount& nRet);
#endif // DIGIBYTE_UTIL_MONEYSTR_H

17
src/util/rbf.cpp Normal file
View file

@ -0,0 +1,17 @@
// Copyright (c) 2016-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/rbf.h>
#include <primitives/transaction.h>
bool SignalsOptInRBF(const CTransaction &tx)
{
for (const CTxIn &txin : tx.vin) {
if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
return true;
}
}
return false;
}

18
src/util/rbf.h Normal file
View file

@ -0,0 +1,18 @@
// Copyright (c) 2016-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_RBF_H
#define DIGIBYTE_UTIL_RBF_H
#include <cstdint>
class CTransaction;
static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
// Check whether the sequence numbers on this transaction are signaling
// opt-in to replace-by-fee, according to BIP 125
bool SignalsOptInRBF(const CTransaction &tx);
#endif // DIGIBYTE_UTIL_RBF_H

View file

@ -0,0 +1,47 @@
// Copyright (c) 2015-2020 The DigiByte Core developers
// Copyright (c) 2017 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
#include <limits>
#include <stdio.h>
#include <string>
#include <utility>
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
{
FILE *f = fsbridge::fopen(filename, "rb");
if (f == nullptr)
return std::make_pair(false,"");
std::string retval;
char buffer[128];
do {
const size_t n = fread(buffer, 1, sizeof(buffer), f);
// Check for reading errors so we don't return any data if we couldn't
// read the entire file (or up to maxsize)
if (ferror(f)) {
fclose(f);
return std::make_pair(false,"");
}
retval.append(buffer, buffer+n);
} while (!feof(f) && retval.size() <= maxsize);
fclose(f);
return std::make_pair(true,retval);
}
bool WriteBinaryFile(const fs::path &filename, const std::string &data)
{
FILE *f = fsbridge::fopen(filename, "wb");
if (f == nullptr)
return false;
if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
fclose(f);
return false;
}
if (fclose(f) != 0) {
return false;
}
return true;
}

28
src/util/readwritefile.h Normal file
View file

@ -0,0 +1,28 @@
// Copyright (c) 2015-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_READWRITEFILE_H
#define DIGIBYTE_UTIL_READWRITEFILE_H
#include <fs.h>
#include <limits>
#include <string>
#include <utility>
/** Read full contents of a file and return them in a std::string.
* Returns a pair <status, string>.
* If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
*
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
* (with len > maxsize) will be returned.
*/
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max());
/** Write contents of std::string to a file.
* @return true on success.
*/
bool WriteBinaryFile(const fs::path &filename, const std::string &data);
#endif /* DIGIBYTE_UTIL_READWRITEFILE_H */

64
src/util/serfloat.cpp Normal file
View file

@ -0,0 +1,64 @@
// Copyright (c) 2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/serfloat.h>
#include <cmath>
#include <limits>
double DecodeDouble(uint64_t v) noexcept {
static constexpr double NANVAL = std::numeric_limits<double>::quiet_NaN();
static constexpr double INFVAL = std::numeric_limits<double>::infinity();
double sign = 1.0;
if (v & 0x8000000000000000) {
sign = -1.0;
v ^= 0x8000000000000000;
}
// Zero
if (v == 0) return copysign(0.0, sign);
// Infinity
if (v == 0x7ff0000000000000) return copysign(INFVAL, sign);
// Other numbers
int exp = (v & 0x7FF0000000000000) >> 52;
uint64_t man = v & 0xFFFFFFFFFFFFF;
if (exp == 2047) {
// NaN
return NANVAL;
} else if (exp == 0) {
// Subnormal
return copysign(ldexp((double)man, -1074), sign);
} else {
// Normal
return copysign(ldexp((double)(man + 0x10000000000000), -1075 + exp), sign);
}
}
uint64_t EncodeDouble(double f) noexcept {
int cls = std::fpclassify(f);
uint64_t sign = 0;
if (copysign(1.0, f) == -1.0) {
f = -f;
sign = 0x8000000000000000;
}
// Zero
if (cls == FP_ZERO) return sign;
// Infinity
if (cls == FP_INFINITE) return sign | 0x7ff0000000000000;
// NaN
if (cls == FP_NAN) return 0x7ff8000000000000;
// Other numbers
int exp;
uint64_t man = std::round(std::frexp(f, &exp) * 9007199254740992.0);
if (exp < -1021) {
// Too small to represent, encode 0
if (exp < -1084) return sign;
// Subnormal numbers
return sign | (man >> (-1021 - exp));
} else {
// Too big to represent, encode infinity
if (exp > 1024) return sign | 0x7ff0000000000000;
// Normal numbers
return sign | (((uint64_t)(1022 + exp)) << 52) | (man & 0xFFFFFFFFFFFFF);
}
}

16
src/util/serfloat.h Normal file
View file

@ -0,0 +1,16 @@
// Copyright (c) 2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_SERFLOAT_H
#define DIGIBYTE_UTIL_SERFLOAT_H
#include <stdint.h>
/* Encode a double using the IEEE 754 binary64 format. All NaNs are encoded as x86/ARM's
* positive quiet NaN with payload 0. */
uint64_t EncodeDouble(double f) noexcept;
/* Reverse operation of DecodeDouble. DecodeDouble(EncodeDouble(f))==f unless isnan(f). */
double DecodeDouble(uint64_t v) noexcept;
#endif // DIGIBYTE_UTIL_SERFLOAT_H

240
src/util/settings.cpp Normal file
View file

@ -0,0 +1,240 @@
// Copyright (c) 2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/settings.h>
#include <tinyformat.h>
#include <univalue.h>
namespace util {
namespace {
enum class Source {
FORCED,
COMMAND_LINE,
RW_SETTINGS,
CONFIG_FILE_NETWORK_SECTION,
CONFIG_FILE_DEFAULT_SECTION
};
//! Merge settings from multiple sources in precedence order:
//! Forced config > command line > read-write settings file > config file network-specific section > config file default section
//!
//! This function is provided with a callback function fn that contains
//! specific logic for how to merge the sources.
template <typename Fn>
static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
{
// Merge in the forced settings
if (auto* value = FindKey(settings.forced_settings, name)) {
fn(SettingsSpan(*value), Source::FORCED);
}
// Merge in the command-line options
if (auto* values = FindKey(settings.command_line_options, name)) {
fn(SettingsSpan(*values), Source::COMMAND_LINE);
}
// Merge in the read-write settings
if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
fn(SettingsSpan(*value), Source::RW_SETTINGS);
}
// Merge in the network-specific section of the config file
if (!section.empty()) {
if (auto* map = FindKey(settings.ro_config, section)) {
if (auto* values = FindKey(*map, name)) {
fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
}
}
}
// Merge in the default section of the config file
if (auto* map = FindKey(settings.ro_config, "")) {
if (auto* values = FindKey(*map, name)) {
fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
}
}
}
} // namespace
bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)
{
values.clear();
errors.clear();
fsbridge::ifstream file;
file.open(path);
if (!file.is_open()) return true; // Ok for file not to exist.
SettingsValue in;
if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
errors.emplace_back(strprintf("Unable to parse settings file %s", path.string()));
return false;
}
if (file.fail()) {
errors.emplace_back(strprintf("Failed reading settings file %s", path.string()));
return false;
}
file.close(); // Done with file descriptor. Release while copying data.
if (!in.isObject()) {
errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), path.string()));
return false;
}
const std::vector<std::string>& in_keys = in.getKeys();
const std::vector<SettingsValue>& in_values = in.getValues();
for (size_t i = 0; i < in_keys.size(); ++i) {
auto inserted = values.emplace(in_keys[i], in_values[i]);
if (!inserted.second) {
errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], path.string()));
}
}
return errors.empty();
}
bool WriteSettings(const fs::path& path,
const std::map<std::string, SettingsValue>& values,
std::vector<std::string>& errors)
{
SettingsValue out(SettingsValue::VOBJ);
for (const auto& value : values) {
out.__pushKV(value.first, value.second);
}
fsbridge::ofstream file;
file.open(path);
if (file.fail()) {
errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", path.string()));
return false;
}
file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
file.close();
return true;
}
SettingsValue GetSetting(const Settings& settings,
const std::string& section,
const std::string& name,
bool ignore_default_section_config,
bool get_chain_name)
{
SettingsValue result;
bool done = false; // Done merging any more settings sources.
MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
// Weird behavior preserved for backwards compatibility: Apply negated
// setting even if non-negated setting would be ignored. A negated
// value in the default section is applied to network specific options,
// even though normal non-negated values there would be ignored.
const bool never_ignore_negated_setting = span.last_negated();
// Weird behavior preserved for backwards compatibility: Take first
// assigned value instead of last. In general, later settings take
// precedence over early settings, but for backwards compatibility in
// the config file the precedence is reversed for all settings except
// chain name settings.
const bool reverse_precedence =
(source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
!get_chain_name;
// Weird behavior preserved for backwards compatibility: Negated
// -regtest and -testnet arguments which you would expect to override
// values set in the configuration file are currently accepted but
// silently ignored. It would be better to apply these just like other
// negated values, or at least warn they are ignored.
const bool skip_negated_command_line = get_chain_name;
if (done) return;
// Ignore settings in default config section if requested.
if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION &&
!never_ignore_negated_setting) {
return;
}
// Skip negated command line settings.
if (skip_negated_command_line && span.last_negated()) return;
if (!span.empty()) {
result = reverse_precedence ? span.begin()[0] : span.end()[-1];
done = true;
} else if (span.last_negated()) {
result = false;
done = true;
}
});
return result;
}
std::vector<SettingsValue> GetSettingsList(const Settings& settings,
const std::string& section,
const std::string& name,
bool ignore_default_section_config)
{
std::vector<SettingsValue> result;
bool done = false; // Done merging any more settings sources.
bool prev_negated_empty = false;
MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
// Weird behavior preserved for backwards compatibility: Apply config
// file settings even if negated on command line. Negating a setting on
// command line will ignore earlier settings on the command line and
// ignore settings in the config file, unless the negated command line
// value is followed by non-negated value, in which case config file
// settings will be brought back from the dead (but earlier command
// line settings will still be ignored).
const bool add_zombie_config_values =
(source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
!prev_negated_empty;
// Ignore settings in default config section if requested.
if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return;
// Add new settings to the result if isn't already complete, or if the
// values are zombies.
if (!done || add_zombie_config_values) {
for (const auto& value : span) {
if (value.isArray()) {
result.insert(result.end(), value.getValues().begin(), value.getValues().end());
} else {
result.push_back(value);
}
}
}
// If a setting was negated, or if a setting was forced, set
// done to true to ignore any later lower priority settings.
done |= span.negated() > 0 || source == Source::FORCED;
// Update the negated and empty state used for the zombie values check.
prev_negated_empty |= span.last_negated() && result.empty();
});
return result;
}
bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
{
bool has_default_section_setting = false;
bool has_other_setting = false;
MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
if (span.empty()) return;
else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
else has_other_setting = true;
});
// If a value is set in the default section and not explicitly overwritten by the
// user on the command line or in a different section, then we want to enable
// warnings about the value being ignored.
return has_default_section_setting && !has_other_setting;
}
SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
const SettingsValue* SettingsSpan::end() const { return data + size; }
bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
size_t SettingsSpan::negated() const
{
for (size_t i = size; i > 0; --i) {
if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
}
return 0;
}
} // namespace util

108
src/util/settings.h Normal file
View file

@ -0,0 +1,108 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_SETTINGS_H
#define DIGIBYTE_UTIL_SETTINGS_H
#include <fs.h>
#include <map>
#include <string>
#include <vector>
class UniValue;
namespace util {
//! Settings value type (string/integer/boolean/null variant).
//!
//! @note UniValue is used here for convenience and because it can be easily
//! serialized in a readable format. But any other variant type that can
//! be assigned strings, int64_t, and bool values and has get_str(),
//! get_int64(), get_bool(), isNum(), isBool(), isFalse(), isTrue() and
//! isNull() methods can be substituted if there's a need to move away
//! from UniValue. (An implementation with boost::variant was posted at
//! https://github.com/digibyte/digibyte/pull/15934/files#r337691812)
using SettingsValue = UniValue;
//! Stored settings. This struct combines settings from the command line, a
//! read-only configuration file, and a read-write runtime settings file.
struct Settings {
//! Map of setting name to forced setting value.
std::map<std::string, SettingsValue> forced_settings;
//! Map of setting name to list of command line values.
std::map<std::string, std::vector<SettingsValue>> command_line_options;
//! Map of setting name to read-write file setting value.
std::map<std::string, SettingsValue> rw_settings;
//! Map of config section name and setting name to list of config file values.
std::map<std::string, std::map<std::string, std::vector<SettingsValue>>> ro_config;
};
//! Read settings file.
bool ReadSettings(const fs::path& path,
std::map<std::string, SettingsValue>& values,
std::vector<std::string>& errors);
//! Write settings file.
bool WriteSettings(const fs::path& path,
const std::map<std::string, SettingsValue>& values,
std::vector<std::string>& errors);
//! Get settings value from combined sources: forced settings, command line
//! arguments, runtime read-write settings, and the read-only config file.
//!
//! @param ignore_default_section_config - ignore values in the default section
//! of the config file (part before any
//! [section] keywords)
//! @param get_chain_name - enable special backwards compatible behavior
//! for GetChainName
SettingsValue GetSetting(const Settings& settings,
const std::string& section,
const std::string& name,
bool ignore_default_section_config,
bool get_chain_name);
//! Get combined setting value similar to GetSetting(), except if setting was
//! specified multiple times, return a list of all the values specified.
std::vector<SettingsValue> GetSettingsList(const Settings& settings,
const std::string& section,
const std::string& name,
bool ignore_default_section_config);
//! Return true if a setting is set in the default config file section, and not
//! overridden by a higher priority command-line or network section value.
//!
//! This is used to provide user warnings about values that might be getting
//! ignored unintentionally.
bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name);
//! Accessor for list of settings that skips negated values when iterated over.
//! The last boolean `false` value in the list and all earlier values are
//! considered negated.
struct SettingsSpan {
explicit SettingsSpan() = default;
explicit SettingsSpan(const SettingsValue& value) noexcept : SettingsSpan(&value, 1) {}
explicit SettingsSpan(const SettingsValue* data, size_t size) noexcept : data(data), size(size) {}
explicit SettingsSpan(const std::vector<SettingsValue>& vec) noexcept;
const SettingsValue* begin() const; //!< Pointer to first non-negated value.
const SettingsValue* end() const; //!< Pointer to end of values.
bool empty() const; //!< True if there are any non-negated values.
bool last_negated() const; //!< True if the last value is negated.
size_t negated() const; //!< Number of negated values.
const SettingsValue* data = nullptr;
size_t size = 0;
};
//! Map lookup helper.
template <typename Map, typename Key>
auto FindKey(Map&& map, Key&& key) -> decltype(&map.at(key))
{
auto it = map.find(key);
return it == map.end() ? nullptr : &it->second;
}
} // namespace util
#endif // DIGIBYTE_UTIL_SETTINGS_H

343
src/util/sock.cpp Normal file
View file

@ -0,0 +1,343 @@
// Copyright (c) 2020-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compat.h>
#include <logging.h>
#include <threadinterrupt.h>
#include <tinyformat.h>
#include <util/sock.h>
#include <util/system.h>
#include <util/time.h>
#include <codecvt>
#include <cwchar>
#include <locale>
#include <stdexcept>
#include <string>
#ifdef USE_POLL
#include <poll.h>
#endif
static inline bool IOErrorIsPermanent(int err)
{
return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
}
Sock::Sock() : m_socket(INVALID_SOCKET) {}
Sock::Sock(SOCKET s) : m_socket(s) {}
Sock::Sock(Sock&& other)
{
m_socket = other.m_socket;
other.m_socket = INVALID_SOCKET;
}
Sock::~Sock() { Reset(); }
Sock& Sock::operator=(Sock&& other)
{
Reset();
m_socket = other.m_socket;
other.m_socket = INVALID_SOCKET;
return *this;
}
SOCKET Sock::Get() const { return m_socket; }
SOCKET Sock::Release()
{
const SOCKET s = m_socket;
m_socket = INVALID_SOCKET;
return s;
}
void Sock::Reset() { CloseSocket(m_socket); }
ssize_t Sock::Send(const void* data, size_t len, int flags) const
{
return send(m_socket, static_cast<const char*>(data), len, flags);
}
ssize_t Sock::Recv(void* buf, size_t len, int flags) const
{
return recv(m_socket, static_cast<char*>(buf), len, flags);
}
int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
{
return connect(m_socket, addr, addr_len);
}
int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
{
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
}
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
{
#ifdef USE_POLL
pollfd fd;
fd.fd = m_socket;
fd.events = 0;
if (requested & RECV) {
fd.events |= POLLIN;
}
if (requested & SEND) {
fd.events |= POLLOUT;
}
if (poll(&fd, 1, count_milliseconds(timeout)) == SOCKET_ERROR) {
return false;
}
if (occurred != nullptr) {
*occurred = 0;
if (fd.revents & POLLIN) {
*occurred |= RECV;
}
if (fd.revents & POLLOUT) {
*occurred |= SEND;
}
}
return true;
#else
if (!IsSelectableSocket(m_socket)) {
return false;
}
fd_set fdset_recv;
fd_set fdset_send;
FD_ZERO(&fdset_recv);
FD_ZERO(&fdset_send);
if (requested & RECV) {
FD_SET(m_socket, &fdset_recv);
}
if (requested & SEND) {
FD_SET(m_socket, &fdset_send);
}
timeval timeout_struct = MillisToTimeval(timeout);
if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) == SOCKET_ERROR) {
return false;
}
if (occurred != nullptr) {
*occurred = 0;
if (FD_ISSET(m_socket, &fdset_recv)) {
*occurred |= RECV;
}
if (FD_ISSET(m_socket, &fdset_send)) {
*occurred |= SEND;
}
}
return true;
#endif /* USE_POLL */
}
void Sock::SendComplete(const std::string& data,
std::chrono::milliseconds timeout,
CThreadInterrupt& interrupt) const
{
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
size_t sent{0};
for (;;) {
const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
if (ret > 0) {
sent += static_cast<size_t>(ret);
if (sent == data.size()) {
break;
}
} else {
const int err{WSAGetLastError()};
if (IOErrorIsPermanent(err)) {
throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));
}
}
const auto now = GetTime<std::chrono::milliseconds>();
if (now >= deadline) {
throw std::runtime_error(strprintf(
"Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
}
if (interrupt) {
throw std::runtime_error(strprintf(
"Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
}
// Wait for a short while (or the socket to become ready for sending) before retrying
// if nothing was sent.
const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
(void)Wait(wait_time, SEND);
}
}
std::string Sock::RecvUntilTerminator(uint8_t terminator,
std::chrono::milliseconds timeout,
CThreadInterrupt& interrupt,
size_t max_data) const
{
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
std::string data;
bool terminator_found{false};
// We must not consume any bytes past the terminator from the socket.
// One option is to read one byte at a time and check if we have read a terminator.
// However that is very slow. Instead, we peek at what is in the socket and only read
// as many bytes as possible without crossing the terminator.
// Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
// one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
// at a time is about 50 times slower.
for (;;) {
if (data.size() >= max_data) {
throw std::runtime_error(
strprintf("Received too many bytes without a terminator (%u)", data.size()));
}
char buf[512];
const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
switch (peek_ret) {
case -1: {
const int err{WSAGetLastError()};
if (IOErrorIsPermanent(err)) {
throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
}
break;
}
case 0:
throw std::runtime_error("Connection unexpectedly closed by peer");
default:
auto end = buf + peek_ret;
auto terminator_pos = std::find(buf, end, terminator);
terminator_found = terminator_pos != end;
const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
static_cast<size_t>(peek_ret)};
const ssize_t read_ret{Recv(buf, try_len, 0)};
if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
throw std::runtime_error(
strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
"peek claimed %u bytes are available",
read_ret, try_len, peek_ret));
}
// Don't include the terminator in the output.
const size_t append_len{terminator_found ? try_len - 1 : try_len};
data.append(buf, buf + append_len);
if (terminator_found) {
return data;
}
}
const auto now = GetTime<std::chrono::milliseconds>();
if (now >= deadline) {
throw std::runtime_error(strprintf(
"Receive timeout (received %u bytes without terminator before that)", data.size()));
}
if (interrupt) {
throw std::runtime_error(strprintf(
"Receive interrupted (received %u bytes without terminator before that)",
data.size()));
}
// Wait for a short while (or the socket to become ready for reading) before retrying.
const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
(void)Wait(wait_time, RECV);
}
}
bool Sock::IsConnected(std::string& errmsg) const
{
if (m_socket == INVALID_SOCKET) {
errmsg = "not connected";
return false;
}
char c;
switch (Recv(&c, sizeof(c), MSG_PEEK)) {
case -1: {
const int err = WSAGetLastError();
if (IOErrorIsPermanent(err)) {
errmsg = NetworkErrorString(err);
return false;
}
return true;
}
case 0:
errmsg = "closed";
return false;
default:
return true;
}
}
#ifdef WIN32
std::string NetworkErrorString(int err)
{
wchar_t buf[256];
buf[0] = 0;
if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, ARRAYSIZE(buf), nullptr))
{
return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
}
else
{
return strprintf("Unknown error (%d)", err);
}
}
#else
std::string NetworkErrorString(int err)
{
char buf[256];
buf[0] = 0;
/* Too bad there are two incompatible implementations of the
* thread-safe strerror. */
const char *s;
#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
s = strerror_r(err, buf, sizeof(buf));
#else /* POSIX variant always returns message in buffer */
s = buf;
if (strerror_r(err, buf, sizeof(buf)))
buf[0] = 0;
#endif
return strprintf("%s (%d)", s, err);
}
#endif
bool CloseSocket(SOCKET& hSocket)
{
if (hSocket == INVALID_SOCKET)
return false;
#ifdef WIN32
int ret = closesocket(hSocket);
#else
int ret = close(hSocket);
#endif
if (ret) {
LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
}
hSocket = INVALID_SOCKET;
return ret != SOCKET_ERROR;
}

185
src/util/sock.h Normal file
View file

@ -0,0 +1,185 @@
// Copyright (c) 2020-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_SOCK_H
#define DIGIBYTE_UTIL_SOCK_H
#include <compat.h>
#include <threadinterrupt.h>
#include <util/time.h>
#include <chrono>
#include <string>
/**
* Maximum time to wait for I/O readiness.
* It will take up until this time to break off in case of an interruption.
*/
static constexpr auto MAX_WAIT_FOR_IO = 1s;
/**
* RAII helper class that manages a socket. Mimics `std::unique_ptr`, but instead of a pointer it
* contains a socket and closes it automatically when it goes out of scope.
*/
class Sock
{
public:
/**
* Default constructor, creates an empty object that does nothing when destroyed.
*/
Sock();
/**
* Take ownership of an existent socket.
*/
explicit Sock(SOCKET s);
/**
* Copy constructor, disabled because closing the same socket twice is undesirable.
*/
Sock(const Sock&) = delete;
/**
* Move constructor, grab the socket from another object and close ours (if set).
*/
Sock(Sock&& other);
/**
* Destructor, close the socket or do nothing if empty.
*/
virtual ~Sock();
/**
* Copy assignment operator, disabled because closing the same socket twice is undesirable.
*/
Sock& operator=(const Sock&) = delete;
/**
* Move assignment operator, grab the socket from another object and close ours (if set).
*/
virtual Sock& operator=(Sock&& other);
/**
* Get the value of the contained socket.
* @return socket or INVALID_SOCKET if empty
*/
[[nodiscard]] virtual SOCKET Get() const;
/**
* Get the value of the contained socket and drop ownership. It will not be closed by the
* destructor after this call.
* @return socket or INVALID_SOCKET if empty
*/
virtual SOCKET Release();
/**
* Close if non-empty.
*/
virtual void Reset();
/**
* send(2) wrapper. Equivalent to `send(this->Get(), data, len, flags);`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
[[nodiscard]] virtual ssize_t Send(const void* data, size_t len, int flags) const;
/**
* recv(2) wrapper. Equivalent to `recv(this->Get(), buf, len, flags);`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
[[nodiscard]] virtual ssize_t Recv(void* buf, size_t len, int flags) const;
/**
* connect(2) wrapper. Equivalent to `connect(this->Get(), addr, addrlen)`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
[[nodiscard]] virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
/**
* getsockopt(2) wrapper. Equivalent to
* `getsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
[[nodiscard]] virtual int GetSockOpt(int level,
int opt_name,
void* opt_val,
socklen_t* opt_len) const;
using Event = uint8_t;
/**
* If passed to `Wait()`, then it will wait for readiness to read from the socket.
*/
static constexpr Event RECV = 0b01;
/**
* If passed to `Wait()`, then it will wait for readiness to send to the socket.
*/
static constexpr Event SEND = 0b10;
/**
* Wait for readiness for input (recv) or output (send).
* @param[in] timeout Wait this much for at least one of the requested events to occur.
* @param[in] requested Wait for those events, bitwise-or of `RECV` and `SEND`.
* @param[out] occurred If not nullptr and `true` is returned, then upon return this
* indicates which of the requested events occurred. A timeout is indicated by return
* value of `true` and `occurred` being set to 0.
* @return true on success and false otherwise
*/
[[nodiscard]] virtual bool Wait(std::chrono::milliseconds timeout,
Event requested,
Event* occurred = nullptr) const;
/* Higher level, convenience, methods. These may throw. */
/**
* Send the given data, retrying on transient errors.
* @param[in] data Data to send.
* @param[in] timeout Timeout for the entire operation.
* @param[in] interrupt If this is signaled then the operation is canceled.
* @throws std::runtime_error if the operation cannot be completed. In this case only some of
* the data will be written to the socket.
*/
virtual void SendComplete(const std::string& data,
std::chrono::milliseconds timeout,
CThreadInterrupt& interrupt) const;
/**
* Read from socket until a terminator character is encountered. Will never consume bytes past
* the terminator from the socket.
* @param[in] terminator Character up to which to read from the socket.
* @param[in] timeout Timeout for the entire operation.
* @param[in] interrupt If this is signaled then the operation is canceled.
* @param[in] max_data The maximum amount of data (in bytes) to receive. If this many bytes
* are received and there is still no terminator, then this method will throw an exception.
* @return The data that has been read, without the terminating character.
* @throws std::runtime_error if the operation cannot be completed. In this case some bytes may
* have been consumed from the socket.
*/
[[nodiscard]] virtual std::string RecvUntilTerminator(uint8_t terminator,
std::chrono::milliseconds timeout,
CThreadInterrupt& interrupt,
size_t max_data) const;
/**
* Check if still connected.
* @param[out] errmsg The error string, if the socket has been disconnected.
* @return true if connected
*/
[[nodiscard]] virtual bool IsConnected(std::string& errmsg) const;
protected:
/**
* Contained socket. `INVALID_SOCKET` designates the object is empty.
*/
SOCKET m_socket;
};
/** Return readable error string for a network error code */
std::string NetworkErrorString(int err);
/** Close socket and set hSocket to INVALID_SOCKET */
bool CloseSocket(SOCKET& hSocket);
#endif // DIGIBYTE_UTIL_SOCK_H

67
src/util/spanparsing.cpp Normal file
View file

@ -0,0 +1,67 @@
// Copyright (c) 2018-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/spanparsing.h>
#include <span.h>
#include <string>
#include <vector>
namespace spanparsing {
bool Const(const std::string& str, Span<const char>& sp)
{
if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) {
sp = sp.subspan(str.size());
return true;
}
return false;
}
bool Func(const std::string& str, Span<const char>& sp)
{
if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) {
sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2);
return true;
}
return false;
}
Span<const char> Expr(Span<const char>& sp)
{
int level = 0;
auto it = sp.begin();
while (it != sp.end()) {
if (*it == '(' || *it == '{') {
++level;
} else if (level && (*it == ')' || *it == '}')) {
--level;
} else if (level == 0 && (*it == ')' || *it == '}' || *it == ',')) {
break;
}
++it;
}
Span<const char> ret = sp.first(it - sp.begin());
sp = sp.subspan(it - sp.begin());
return ret;
}
std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
{
std::vector<Span<const char>> ret;
auto it = sp.begin();
auto start = it;
while (it != sp.end()) {
if (*it == sep) {
ret.emplace_back(start, it);
start = it + 1;
}
++it;
}
ret.emplace_back(start, it);
return ret;
}
} // namespace spanparsing

50
src/util/spanparsing.h Normal file
View file

@ -0,0 +1,50 @@
// Copyright (c) 2018-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_SPANPARSING_H
#define DIGIBYTE_UTIL_SPANPARSING_H
#include <span.h>
#include <string>
#include <vector>
namespace spanparsing {
/** Parse a constant.
*
* If sp's initial part matches str, sp is updated to skip that part, and true is returned.
* Otherwise sp is unmodified and false is returned.
*/
bool Const(const std::string& str, Span<const char>& sp);
/** Parse a function call.
*
* If sp's initial part matches str + "(", and sp ends with ")", sp is updated to be the
* section between the braces, and true is returned. Otherwise sp is unmodified and false
* is returned.
*/
bool Func(const std::string& str, Span<const char>& sp);
/** Extract the expression that sp begins with.
*
* This function will return the initial part of sp, up to (but not including) the first
* comma or closing brace, skipping ones that are surrounded by braces. So for example,
* for "foo(bar(1),2),3" the initial part "foo(bar(1),2)" will be returned. sp will be
* updated to skip the initial part that is returned.
*/
Span<const char> Expr(Span<const char>& sp);
/** Split a string on every instance of sep, returning a vector.
*
* If sep does not occur in sp, a singleton with the entirety of sp is returned.
*
* Note that this function does not care about braces, so splitting
* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.
*/
std::vector<Span<const char>> Split(const Span<const char>& sp, char sep);
} // namespace spanparsing
#endif // DIGIBYTE_UTIL_SPANPARSING_H

607
src/util/strencodings.cpp Normal file
View file

@ -0,0 +1,607 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Copyright (c) 2014-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/strencodings.h>
#include <util/string.h>
#include <tinyformat.h>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <limits>
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static const std::string SAFE_CHARS[] =
{
CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
};
std::string SanitizeString(const std::string& str, int rule)
{
std::string strResult;
for (std::string::size_type i = 0; i < str.size(); i++)
{
if (SAFE_CHARS[rule].find(str[i]) != std::string::npos)
strResult.push_back(str[i]);
}
return strResult;
}
const signed char p_util_hexdigit[256] =
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, };
signed char HexDigit(char c)
{
return p_util_hexdigit[(unsigned char)c];
}
bool IsHex(const std::string& str)
{
for(std::string::const_iterator it(str.begin()); it != str.end(); ++it)
{
if (HexDigit(*it) < 0)
return false;
}
return (str.size() > 0) && (str.size()%2 == 0);
}
bool IsHexNumber(const std::string& str)
{
size_t starting_location = 0;
if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') {
starting_location = 2;
}
for (const char c : str.substr(starting_location)) {
if (HexDigit(c) < 0) return false;
}
// Return false for empty string or "0x".
return (str.size() > starting_location);
}
std::vector<unsigned char> ParseHex(const char* psz)
{
// convert hex dump to vector
std::vector<unsigned char> vch;
while (true)
{
while (IsSpace(*psz))
psz++;
signed char c = HexDigit(*psz++);
if (c == (signed char)-1)
break;
unsigned char n = (c << 4);
c = HexDigit(*psz++);
if (c == (signed char)-1)
break;
n |= c;
vch.push_back(n);
}
return vch;
}
std::vector<unsigned char> ParseHex(const std::string& str)
{
return ParseHex(str.c_str());
}
void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut)
{
size_t colon = in.find_last_of(':');
// if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
bool fHaveColon = colon != in.npos;
bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
bool fMultiColon = fHaveColon && (in.find_last_of(':', colon - 1) != in.npos);
if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
uint16_t n;
if (ParseUInt16(in.substr(colon + 1), &n)) {
in = in.substr(0, colon);
portOut = n;
}
}
if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
hostOut = in.substr(1, in.size() - 2);
} else {
hostOut = in;
}
}
std::string EncodeBase64(Span<const unsigned char> input)
{
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string str;
str.reserve(((input.size() + 2) / 3) * 4);
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
while (str.size() % 4) str += '=';
return str;
}
std::string EncodeBase64(const std::string& str)
{
return EncodeBase64(MakeUCharSpan(str));
}
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid)
{
static const int decode64_table[256] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
const char* e = p;
std::vector<uint8_t> val;
val.reserve(strlen(p));
while (*p != 0) {
int x = decode64_table[(unsigned char)*p];
if (x == -1) break;
val.push_back(x);
++p;
}
std::vector<unsigned char> ret;
ret.reserve((val.size() * 3) / 4);
bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
const char* q = p;
while (valid && *p != 0) {
if (*p != '=') {
valid = false;
break;
}
++p;
}
valid = valid && (p - e) % 4 == 0 && p - q < 4;
if (pf_invalid) *pf_invalid = !valid;
return ret;
}
std::string DecodeBase64(const std::string& str, bool* pf_invalid)
{
if (!ValidAsCString(str)) {
if (pf_invalid) {
*pf_invalid = true;
}
return {};
}
std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid);
return std::string((const char*)vchRet.data(), vchRet.size());
}
std::string EncodeBase32(Span<const unsigned char> input, bool pad)
{
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
std::string str;
str.reserve(((input.size() + 4) / 5) * 8);
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
if (pad) {
while (str.size() % 8) {
str += '=';
}
}
return str;
}
std::string EncodeBase32(const std::string& str, bool pad)
{
return EncodeBase32(MakeUCharSpan(str), pad);
}
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid)
{
static const int decode32_table[256] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2,
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
const char* e = p;
std::vector<uint8_t> val;
val.reserve(strlen(p));
while (*p != 0) {
int x = decode32_table[(unsigned char)*p];
if (x == -1) break;
val.push_back(x);
++p;
}
std::vector<unsigned char> ret;
ret.reserve((val.size() * 5) / 8);
bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
const char* q = p;
while (valid && *p != 0) {
if (*p != '=') {
valid = false;
break;
}
++p;
}
valid = valid && (p - e) % 8 == 0 && p - q < 8;
if (pf_invalid) *pf_invalid = !valid;
return ret;
}
std::string DecodeBase32(const std::string& str, bool* pf_invalid)
{
if (!ValidAsCString(str)) {
if (pf_invalid) {
*pf_invalid = true;
}
return {};
}
std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid);
return std::string((const char*)vchRet.data(), vchRet.size());
}
[[nodiscard]] static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
return false;
if (!ValidAsCString(str)) // No embedded NUL characters allowed
return false;
return true;
}
bool ParseInt32(const std::string& str, int32_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = nullptr;
errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int32_t)n;
// Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int32_t>::min() &&
n <= std::numeric_limits<int32_t>::max();
}
bool ParseInt64(const std::string& str, int64_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = nullptr;
errno = 0; // strtoll will not set errno if valid
long long int n = strtoll(str.c_str(), &endp, 10);
if(out) *out = (int64_t)n;
// Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow
// we still have to check that the returned value is within the range of an *int64_t*.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int64_t>::min() &&
n <= std::numeric_limits<int64_t>::max();
}
bool ParseUInt8(const std::string& str, uint8_t *out)
{
uint32_t u32;
if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) {
return false;
}
if (out != nullptr) {
*out = static_cast<uint8_t>(u32);
}
return true;
}
bool ParseUInt16(const std::string& str, uint16_t* out)
{
uint32_t u32;
if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint16_t>::max()) {
return false;
}
if (out != nullptr) {
*out = static_cast<uint16_t>(u32);
}
return true;
}
bool ParseUInt32(const std::string& str, uint32_t *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range
return false;
char *endp = nullptr;
errno = 0; // strtoul will not set errno if valid
unsigned long int n = strtoul(str.c_str(), &endp, 10);
if(out) *out = (uint32_t)n;
// Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow
// we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n <= std::numeric_limits<uint32_t>::max();
}
bool ParseUInt64(const std::string& str, uint64_t *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range
return false;
char *endp = nullptr;
errno = 0; // strtoull will not set errno if valid
unsigned long long int n = strtoull(str.c_str(), &endp, 10);
if(out) *out = (uint64_t)n;
// Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow
// we still have to check that the returned value is within the range of an *uint64_t*.
return endp && *endp == 0 && !errno &&
n <= std::numeric_limits<uint64_t>::max();
}
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
return false;
std::istringstream text(str);
text.imbue(std::locale::classic());
double result;
text >> result;
if(out) *out = result;
return text.eof() && !text.fail();
}
std::string FormatParagraph(const std::string& in, size_t width, size_t indent)
{
std::stringstream out;
size_t ptr = 0;
size_t indented = 0;
while (ptr < in.size())
{
size_t lineend = in.find_first_of('\n', ptr);
if (lineend == std::string::npos) {
lineend = in.size();
}
const size_t linelen = lineend - ptr;
const size_t rem_width = width - indented;
if (linelen <= rem_width) {
out << in.substr(ptr, linelen + 1);
ptr = lineend + 1;
indented = 0;
} else {
size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
if (finalspace == std::string::npos || finalspace < ptr) {
// No place to break; just include the entire word and move on
finalspace = in.find_first_of("\n ", ptr);
if (finalspace == std::string::npos) {
// End of the string, just add it and break
out << in.substr(ptr);
break;
}
}
out << in.substr(ptr, finalspace - ptr) << "\n";
if (in[finalspace] == '\n') {
indented = 0;
} else if (indent) {
out << std::string(indent, ' ');
indented = indent;
}
ptr = finalspace + 1;
}
}
return out.str();
}
int64_t atoi64(const std::string& str)
{
#ifdef _MSC_VER
return _atoi64(str.c_str());
#else
return strtoll(str.c_str(), nullptr, 10);
#endif
}
int atoi(const std::string& str)
{
return atoi(str.c_str());
}
/** Upper bound for mantissa.
* 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
* Larger integers cannot consist of arbitrary combinations of 0-9:
*
* 999999999999999999 1^18-1
* 9223372036854775807 (1<<63)-1 (max int64_t)
* 9999999999999999999 1^19-1 (would overflow)
*/
static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
/** Helper function for ParseFixedPoint */
static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
{
if(ch == '0')
++mantissa_tzeros;
else {
for (int i=0; i<=mantissa_tzeros; ++i) {
if (mantissa > (UPPER_BOUND / 10LL))
return false; /* overflow */
mantissa *= 10;
}
mantissa += ch - '0';
mantissa_tzeros = 0;
}
return true;
}
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
{
int64_t mantissa = 0;
int64_t exponent = 0;
int mantissa_tzeros = 0;
bool mantissa_sign = false;
bool exponent_sign = false;
int ptr = 0;
int end = val.size();
int point_ofs = 0;
if (ptr < end && val[ptr] == '-') {
mantissa_sign = true;
++ptr;
}
if (ptr < end)
{
if (val[ptr] == '0') {
/* pass single 0 */
++ptr;
} else if (val[ptr] >= '1' && val[ptr] <= '9') {
while (ptr < end && IsDigit(val[ptr])) {
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
return false; /* overflow */
++ptr;
}
} else return false; /* missing expected digit */
} else return false; /* empty string or loose '-' */
if (ptr < end && val[ptr] == '.')
{
++ptr;
if (ptr < end && IsDigit(val[ptr]))
{
while (ptr < end && IsDigit(val[ptr])) {
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
return false; /* overflow */
++ptr;
++point_ofs;
}
} else return false; /* missing expected digit */
}
if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
{
++ptr;
if (ptr < end && val[ptr] == '+')
++ptr;
else if (ptr < end && val[ptr] == '-') {
exponent_sign = true;
++ptr;
}
if (ptr < end && IsDigit(val[ptr])) {
while (ptr < end && IsDigit(val[ptr])) {
if (exponent > (UPPER_BOUND / 10LL))
return false; /* overflow */
exponent = exponent * 10 + val[ptr] - '0';
++ptr;
}
} else return false; /* missing expected digit */
}
if (ptr != end)
return false; /* trailing garbage */
/* finalize exponent */
if (exponent_sign)
exponent = -exponent;
exponent = exponent - point_ofs + mantissa_tzeros;
/* finalize mantissa */
if (mantissa_sign)
mantissa = -mantissa;
/* convert to one 64-bit fixed-point value */
exponent += decimals;
if (exponent < 0)
return false; /* cannot represent values smaller than 10^-decimals */
if (exponent >= 18)
return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
for (int i=0; i < exponent; ++i) {
if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
return false; /* overflow */
mantissa *= 10;
}
if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
return false; /* overflow */
if (amount_out)
*amount_out = mantissa;
return true;
}
std::string ToLower(const std::string& str)
{
std::string r;
for (auto ch : str) r += ToLower((unsigned char)ch);
return r;
}
std::string ToUpper(const std::string& str)
{
std::string r;
for (auto ch : str) r += ToUpper((unsigned char)ch);
return r;
}
std::string Capitalize(std::string str)
{
if (str.empty()) return str;
str[0] = ToUpper(str.front());
return str;
}
std::string HexStr(const Span<const uint8_t> s)
{
std::string rv(s.size() * 2, '\0');
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
auto it = rv.begin();
for (uint8_t v : s) {
*it++ = hexmap[v >> 4];
*it++ = hexmap[v & 15];
}
assert(it == rv.end());
return rv;
}

273
src/util/strencodings.h Normal file
View file

@ -0,0 +1,273 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/**
* Utilities for converting data from/to strings.
*/
#ifndef DIGIBYTE_UTIL_STRENCODINGS_H
#define DIGIBYTE_UTIL_STRENCODINGS_H
#include <attributes.h>
#include <span.h>
#include <cstdint>
#include <iterator>
#include <string>
#include <vector>
#define BEGIN(a) ((char*)&(a))
#define END(a) ((char*)&((&(a))[1]))
#define UBEGIN(a) ((unsigned char*)&(a))
#define UEND(a) ((unsigned char*)&((&(a))[1]))
#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0]))
/** Used by SanitizeString() */
enum SafeChars
{
SAFE_CHARS_DEFAULT, //!< The full set of allowed chars
SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset
SAFE_CHARS_FILENAME, //!< Chars allowed in filenames
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
};
/**
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email
* addresses, but avoid anything even possibly remotely dangerous like & or >
* @param[in] str The string to sanitize
* @param[in] rule The set of safe chars to choose (default: least restrictive)
* @return A new string without unsafe chars
*/
std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT);
std::vector<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);
signed char HexDigit(char c);
/* Returns true if each character in str is a hex character, and has an even
* number of hex digits.*/
bool IsHex(const std::string& str);
/**
* Return true if the string is a hex number, optionally prefixed with "0x"
*/
bool IsHexNumber(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr);
std::string EncodeBase64(Span<const unsigned char> input);
std::string EncodeBase64(const std::string& str);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr);
/**
* Base32 encode.
* If `pad` is true, then the output will be padded with '=' so that its length
* is a multiple of 8.
*/
std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
/**
* Base32 encode.
* If `pad` is true, then the output will be padded with '=' so that its length
* is a multiple of 8.
*/
std::string EncodeBase32(const std::string& str, bool pad = true);
void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut);
int64_t atoi64(const std::string& str);
int atoi(const std::string& str);
/**
* Tests if the given character is a decimal digit.
* @param[in] c character to test
* @return true if the argument is a decimal digit; otherwise false.
*/
constexpr bool IsDigit(char c)
{
return c >= '0' && c <= '9';
}
/**
* Tests if the given character is a whitespace character. The whitespace characters
* are: space, form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal
* tab ('\t'), and vertical tab ('\v').
*
* This function is locale independent. Under the C locale this function gives the
* same result as std::isspace.
*
* @param[in] c character to test
* @return true if the argument is a whitespace character; otherwise false
*/
constexpr inline bool IsSpace(char c) noexcept {
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
}
/**
* Convert string to signed 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
[[nodiscard]] bool ParseInt32(const std::string& str, int32_t *out);
/**
* Convert string to signed 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
[[nodiscard]] bool ParseInt64(const std::string& str, int64_t *out);
/**
* Convert decimal string to unsigned 8-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
[[nodiscard]] bool ParseUInt8(const std::string& str, uint8_t *out);
/**
* Convert decimal string to unsigned 16-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if the entire string could not be parsed or if overflow or underflow occurred.
*/
[[nodiscard]] bool ParseUInt16(const std::string& str, uint16_t* out);
/**
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
[[nodiscard]] bool ParseUInt32(const std::string& str, uint32_t *out);
/**
* Convert decimal string to unsigned 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
[[nodiscard]] bool ParseUInt64(const std::string& str, uint64_t *out);
/**
* Convert string to double with strict parse error feedback.
* @returns true if the entire string could be parsed as valid double,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
[[nodiscard]] bool ParseDouble(const std::string& str, double *out);
/**
* Convert a span of bytes to a lower-case hexadecimal string.
*/
std::string HexStr(const Span<const uint8_t> s);
inline std::string HexStr(const Span<const char> s) { return HexStr(MakeUCharSpan(s)); }
/**
* Format a paragraph of text to a fixed width, adding spaces for
* indentation to any added line.
*/
std::string FormatParagraph(const std::string& in, size_t width = 79, size_t indent = 0);
/**
* Timing-attack-resistant comparison.
* Takes time proportional to length
* of first argument.
*/
template <typename T>
bool TimingResistantEqual(const T& a, const T& b)
{
if (b.size() == 0) return a.size() == 0;
size_t accumulator = a.size() ^ b.size();
for (size_t i = 0; i < a.size(); i++)
accumulator |= a[i] ^ b[i%b.size()];
return accumulator == 0;
}
/** Parse number as fixed point according to JSON number syntax.
* See https://json.org/number.gif
* @returns true on success, false on error.
* @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger.
*/
[[nodiscard]] bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad, typename O, typename I>
bool ConvertBits(const O& outfn, I it, I end) {
size_t acc = 0;
size_t bits = 0;
constexpr size_t maxv = (1 << tobits) - 1;
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1;
while (it != end) {
acc = ((acc << frombits) | *it) & max_acc;
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
outfn((acc >> bits) & maxv);
}
++it;
}
if (pad) {
if (bits) outfn((acc << (tobits - bits)) & maxv);
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
return false;
}
return true;
}
/**
* Converts the given character to its lowercase equivalent.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] c the character to convert to lowercase.
* @return the lowercase equivalent of c; or the argument
* if no conversion is possible.
*/
constexpr char ToLower(char c)
{
return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c);
}
/**
* Returns the lowercase equivalent of the given string.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to convert to lowercase.
* @returns lowercased equivalent of str
*/
std::string ToLower(const std::string& str);
/**
* Converts the given character to its uppercase equivalent.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] c the character to convert to uppercase.
* @return the uppercase equivalent of c; or the argument
* if no conversion is possible.
*/
constexpr char ToUpper(char c)
{
return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c);
}
/**
* Returns the uppercase equivalent of the given string.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to convert to uppercase.
* @returns UPPERCASED EQUIVALENT OF str
*/
std::string ToUpper(const std::string& str);
/**
* Capitalizes the first character of the given string.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to capitalize.
* @returns string with the first letter capitalized.
*/
std::string Capitalize(std::string str);
#endif // DIGIBYTE_UTIL_STRENCODINGS_H

5
src/util/string.cpp Normal file
View file

@ -0,0 +1,5 @@
// Copyright (c) 2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/string.h>

99
src/util/string.h Normal file
View file

@ -0,0 +1,99 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_STRING_H
#define DIGIBYTE_UTIL_STRING_H
#include <attributes.h>
#include <algorithm>
#include <array>
#include <cstring>
#include <locale>
#include <sstream>
#include <string>
#include <vector>
#include <cstdint>
[[nodiscard]] inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v")
{
std::string::size_type front = str.find_first_not_of(pattern);
if (front == std::string::npos) {
return std::string();
}
std::string::size_type end = str.find_last_not_of(pattern);
return str.substr(front, end - front + 1);
}
[[nodiscard]] inline std::string RemovePrefix(const std::string& str, const std::string& prefix)
{
if (str.substr(0, prefix.size()) == prefix) {
return str.substr(prefix.size());
}
return str;
}
/**
* Join a list of items
*
* @param list The list to join
* @param separator The separator
* @param unary_op Apply this operator to each item in the list
*/
template <typename T, typename BaseType, typename UnaryOp>
auto Join(const std::vector<T>& list, const BaseType& separator, UnaryOp unary_op)
-> decltype(unary_op(list.at(0)))
{
decltype(unary_op(list.at(0))) ret;
for (size_t i = 0; i < list.size(); ++i) {
if (i > 0) ret += separator;
ret += unary_op(list.at(i));
}
return ret;
}
template <typename T>
T Join(const std::vector<T>& list, const T& separator)
{
return Join(list, separator, [](const T& i) { return i; });
}
// Explicit overload needed for c_str arguments, which would otherwise cause a substitution failure in the template above.
inline std::string Join(const std::vector<std::string>& list, const std::string& separator)
{
return Join<std::string>(list, separator);
}
/**
* Check if a string does not contain any embedded NUL (\0) characters
*/
[[nodiscard]] inline bool ValidAsCString(const std::string& str) noexcept
{
return str.size() == strlen(str.c_str());
}
/**
* Locale-independent version of std::to_string
*/
template <typename T>
std::string ToString(const T& t)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << t;
return oss.str();
}
/**
* Check whether a container begins with the given prefix.
*/
template <typename T1, size_t PREFIX_LEN>
[[nodiscard]] inline bool HasPrefix(const T1& obj,
const std::array<uint8_t, PREFIX_LEN>& prefix)
{
return obj.size() >= PREFIX_LEN &&
std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
}
#endif // DIGIBYTE_UTIL_STRENCODINGS_H

1411
src/util/system.cpp Normal file

File diff suppressed because it is too large Load diff

544
src/util/system.h Normal file
View file

@ -0,0 +1,544 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/**
* Server/client environment: argument handling, config file parsing,
* thread wrappers, startup time
*/
#ifndef DIGIBYTE_UTIL_SYSTEM_H
#define DIGIBYTE_UTIL_SYSTEM_H
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <attributes.h>
#include <compat.h>
#include <compat/assumptions.h>
#include <fs.h>
#include <logging.h>
#include <sync.h>
#include <tinyformat.h>
#include <util/settings.h>
#include <util/time.h>
#include <any>
#include <exception>
#include <map>
#include <optional>
#include <set>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
class UniValue;
// Application startup time (used for uptime calculation)
int64_t GetStartupTime();
extern const char * const DIGIBYTE_CONF_FILENAME;
extern const char * const DIGIBYTE_SETTINGS_FILENAME;
void SetupEnvironment();
bool SetupNetworking();
template<typename... Args>
bool error(const char* fmt, const Args&... args)
{
LogPrintf("ERROR: %s\n", tfm::format(fmt, args...));
return false;
}
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
/**
* Ensure file contents are fully committed to disk, using a platform-specific
* feature analogous to fsync().
*/
bool FileCommit(FILE *file);
/**
* Sync directory contents. This is required on some environments to ensure that
* newly created files are committed to disk.
*/
void DirectoryCommit(const fs::path &dirname);
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
[[nodiscard]] bool RenameOver(fs::path src, fs::path dest);
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false);
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name);
bool DirIsWritable(const fs::path& directory);
bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes = 0);
/** Get the size of a file by scanning it.
*
* @param[in] path The file path
* @param[in] max Stop seeking beyond this limit
* @return The file size or max
*/
std::streampos GetFileSize(const char* path, std::streamsize max = std::numeric_limits<std::streamsize>::max());
/** Release all directory locks. This is used for unit testing only, at runtime
* the global destructor will take care of the locks.
*/
void ReleaseDirectoryLocks();
bool TryCreateDirectories(const fs::path& p);
fs::path GetDefaultDataDir();
// Return true if -datadir option points to a valid directory or is not specified.
bool CheckDataDirOption();
fs::path GetConfigFile(const std::string& confPath);
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
#ifndef WIN32
std::string ShellEscape(const std::string& arg);
#endif
#if HAVE_SYSTEM
void runCommand(const std::string& strCommand);
#endif
/**
* Execute a command which returns JSON, and parse the result.
*
* @param str_command The command to execute, including any arguments
* @param str_std_in string to pass to stdin
* @return parsed JSON
*/
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in="");
/**
* Most paths passed as configuration arguments are treated as relative to
* the datadir if they are not absolute.
*
* @param path The path to be conditionally prefixed with datadir.
* @param net_specific Use network specific datadir variant
* @return The normalized path.
*/
fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific = true);
inline bool IsSwitchChar(char c)
{
#ifdef WIN32
return c == '-' || c == '/';
#else
return c == '-';
#endif
}
enum class OptionsCategory {
OPTIONS,
CONNECTION,
WALLET,
WALLET_DEBUG_TEST,
ZMQ,
DEBUG_TEST,
CHAINPARAMS,
NODE_RELAY,
BLOCK_CREATION,
RPC,
GUI,
COMMANDS,
REGISTER_COMMANDS,
HIDDEN // Always the last option to avoid printing these in the help
};
struct SectionInfo
{
std::string m_name;
std::string m_file;
int m_line;
};
class ArgsManager
{
public:
enum Flags : uint32_t {
// Boolean options can accept negation syntax -noOPTION or -noOPTION=1
ALLOW_BOOL = 0x01,
ALLOW_INT = 0x02,
ALLOW_STRING = 0x04,
ALLOW_ANY = ALLOW_BOOL | ALLOW_INT | ALLOW_STRING,
DEBUG_ONLY = 0x100,
/* Some options would cause cross-contamination if values for
* mainnet were used while running on regtest/testnet (or vice-versa).
* Setting them as NETWORK_ONLY ensures that sharing a config file
* between mainnet and regtest/testnet won't cause problems due to these
* parameters by accident. */
NETWORK_ONLY = 0x200,
// This argument's value is sensitive (such as a password).
SENSITIVE = 0x400,
COMMAND = 0x800,
};
protected:
struct Arg
{
std::string m_help_param;
std::string m_help_text;
unsigned int m_flags;
};
mutable RecursiveMutex cs_args;
util::Settings m_settings GUARDED_BY(cs_args);
std::vector<std::string> m_command GUARDED_BY(cs_args);
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
bool m_accept_any_command GUARDED_BY(cs_args){true};
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
/**
* Returns true if settings values from the default section should be used,
* depending on the current network and whether the setting is
* network-specific.
*/
bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
/**
* Get setting value.
*
* Result will be null if setting was unset, true if "-setting" argument was passed
* false if "-nosetting" argument was passed, and a string if a "-setting=value"
* argument was passed.
*/
util::SettingsValue GetSetting(const std::string& arg) const;
/**
* Get list of setting values.
*/
std::vector<util::SettingsValue> GetSettingsList(const std::string& arg) const;
public:
ArgsManager();
~ArgsManager();
/**
* Select the network in use
*/
void SelectConfigNetwork(const std::string& network);
[[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error);
[[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
/**
* Log warnings for options in m_section_only_args when
* they are specified in the default section but not overridden
* on the command line or in a network-specific section in the
* config file.
*/
const std::set<std::string> GetUnsuitableSectionOnlyArgs() const;
/**
* Log warnings for unrecognized section names in the config file.
*/
const std::list<SectionInfo> GetUnrecognizedSections() const;
struct Command {
/** The command (if one has been registered with AddCommand), or empty */
std::string command;
/**
* If command is non-empty: Any args that followed it
* If command is empty: The unregistered command and any args that followed it
*/
std::vector<std::string> args;
};
/**
* Get the command and command args (returns std::nullopt if no command provided)
*/
std::optional<const Command> GetCommand() const;
/**
* Get blocks directory path
*
* @return Blocks path which is network specific
*/
const fs::path& GetBlocksDirPath() const;
/**
* Get data directory path
*
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
* @post Returned directory path is created unless it is empty
*/
const fs::path& GetDataDirBase() const { return GetDataDir(false); }
/**
* Get data directory path with appended network identifier
*
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
* @post Returned directory path is created unless it is empty
*/
const fs::path& GetDataDirNet() const { return GetDataDir(true); }
/**
* Clear cached directory paths
*/
void ClearPathCache();
/**
* Return a vector of strings of the given argument
*
* @param strArg Argument to get (e.g. "-foo")
* @return command-line arguments
*/
std::vector<std::string> GetArgs(const std::string& strArg) const;
/**
* Return true if the given argument has been manually set
*
* @param strArg Argument to get (e.g. "-foo")
* @return true if the argument has been set
*/
bool IsArgSet(const std::string& strArg) const;
/**
* Return true if the argument was originally passed as a negated option,
* i.e. -nofoo.
*
* @param strArg Argument to get (e.g. "-foo")
* @return true if the argument was passed negated
*/
bool IsArgNegated(const std::string& strArg) const;
/**
* Return string argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param strDefault (e.g. "1")
* @return command-line argument or default value
*/
std::string GetArg(const std::string& strArg, const std::string& strDefault) const;
/**
* Return integer argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param nDefault (e.g. 1)
* @return command-line argument (0 if invalid number) or default value
*/
int64_t GetArg(const std::string& strArg, int64_t nDefault) const;
/**
* Return boolean argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param fDefault (true or false)
* @return command-line argument or default value
*/
bool GetBoolArg(const std::string& strArg, bool fDefault) const;
/**
* Set an argument if it doesn't already have a value
*
* @param strArg Argument to set (e.g. "-foo")
* @param strValue Value (e.g. "1")
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetArg(const std::string& strArg, const std::string& strValue);
/**
* Set a boolean argument if it doesn't already have a value
*
* @param strArg Argument to set (e.g. "-foo")
* @param fValue Value (e.g. false)
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetBoolArg(const std::string& strArg, bool fValue);
// Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
// been set. Also called directly in testing.
void ForceSetArg(const std::string& strArg, const std::string& strValue);
/**
* Returns the appropriate chain name from the program arguments.
* @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given.
*/
std::string GetChainName() const;
/**
* Add argument
*/
void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
/**
* Add subcommand
*/
void AddCommand(const std::string& cmd, const std::string& help);
/**
* Add many hidden arguments
*/
void AddHiddenArgs(const std::vector<std::string>& args);
/**
* Clear available arguments
*/
void ClearArgs() {
LOCK(cs_args);
m_available_args.clear();
m_network_only_args.clear();
}
/**
* Get the help string
*/
std::string GetHelpMessage() const;
/**
* Return Flags for known arg.
* Return nullopt for unknown arg.
*/
std::optional<unsigned int> GetArgFlags(const std::string& name) const;
/**
* Read and update settings file with saved settings. This needs to be
* called after SelectParams() because the settings file location is
* network-specific.
*/
bool InitSettings(std::string& error);
/**
* Get settings file path, or return false if read-write settings were
* disabled with -nosettings.
*/
bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false) const;
/**
* Read settings file. Push errors to vector, or log them if null.
*/
bool ReadSettingsFile(std::vector<std::string>* errors = nullptr);
/**
* Write settings file. Push errors to vector, or log them if null.
*/
bool WriteSettingsFile(std::vector<std::string>* errors = nullptr) const;
/**
* Access settings with lock held.
*/
template <typename Fn>
void LockSettings(Fn&& fn)
{
LOCK(cs_args);
fn(m_settings);
}
/**
* Log the config file options and the command line arguments,
* useful for troubleshooting.
*/
void LogArgs() const;
private:
/**
* Get data directory path
*
* @param net_specific Append network identifier to the returned path
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
* @post Returned directory path is created unless it is empty
*/
const fs::path& GetDataDir(bool net_specific) const;
// Helper function for LogArgs().
void logArgsPrefix(
const std::string& prefix,
const std::string& section,
const std::map<std::string, std::vector<util::SettingsValue>>& args) const;
};
extern ArgsManager gArgs;
/**
* @return true if help has been requested via a command-line arg
*/
bool HelpRequested(const ArgsManager& args);
/** Add help options to the args manager */
void SetupHelpOptions(ArgsManager& args);
/**
* Format a string to be used as group of options in help messages
*
* @param message Group name (e.g. "RPC server options:")
* @return the formatted string
*/
std::string HelpMessageGroup(const std::string& message);
/**
* Format a string to be used as option description in help messages
*
* @param option Option message (e.g. "-rpcuser=<user>")
* @param message Option description (e.g. "Username for JSON-RPC connections")
* @return the formatted string
*/
std::string HelpMessageOpt(const std::string& option, const std::string& message);
/**
* Return the number of cores available on the current system.
* @note This does count virtual cores, such as those provided by HyperThreading.
*/
int GetNumCores();
std::string CopyrightHolders(const std::string& strPrefix);
/**
* On platforms that support it, tell the kernel the calling thread is
* CPU-intensive and non-interactive. See SCHED_BATCH in sched(7) for details.
*
*/
void ScheduleBatchPriority();
namespace util {
//! Simplification of std insertion
template <typename Tdst, typename Tsrc>
inline void insert(Tdst& dst, const Tsrc& src) {
dst.insert(dst.begin(), src.begin(), src.end());
}
template <typename TsetT, typename Tsrc>
inline void insert(std::set<TsetT>& dst, const Tsrc& src) {
dst.insert(src.begin(), src.end());
}
/**
* Helper function to access the contained object of a std::any instance.
* Returns a pointer to the object if passed instance has a value and the type
* matches, nullptr otherwise.
*/
template<typename T>
T* AnyPtr(const std::any& any) noexcept
{
T* const* ptr = std::any_cast<T*>(&any);
return ptr ? *ptr : nullptr;
}
#ifdef WIN32
class WinCmdLineArgs
{
public:
WinCmdLineArgs();
~WinCmdLineArgs();
std::pair<int, char**> get();
private:
int argc;
char** argv;
std::vector<std::string> args;
};
#endif
} // namespace util
#endif // DIGIBYTE_UTIL_SYSTEM_H

27
src/util/thread.cpp Normal file
View file

@ -0,0 +1,27 @@
// Copyright (c) 2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/thread.h>
#include <logging.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <exception>
void util::TraceThread(const char* thread_name, std::function<void()> thread_func)
{
util::ThreadRename(thread_name);
try {
LogPrintf("%s thread start\n", thread_name);
thread_func();
LogPrintf("%s thread exit\n", thread_name);
} catch (const std::exception& e) {
PrintExceptionContinue(&e, thread_name);
throw;
} catch (...) {
PrintExceptionContinue(nullptr, thread_name);
throw;
}
}

18
src/util/thread.h Normal file
View file

@ -0,0 +1,18 @@
// Copyright (c) 2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_THREAD_H
#define DIGIBYTE_UTIL_THREAD_H
#include <functional>
namespace util {
/**
* A wrapper for do-something-once thread functions.
*/
void TraceThread(const char* thread_name, std::function<void()> thread_func);
} // namespace util
#endif // DIGIBYTE_UTIL_THREAD_H

66
src/util/threadnames.cpp Normal file
View file

@ -0,0 +1,66 @@
// Copyright (c) 2018-2019 The DigiByte 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/vexta-config.h>
#endif
#include <thread>
#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
#include <pthread.h>
#include <pthread_np.h>
#endif
#include <util/threadnames.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h> // For prctl, PR_SET_NAME, PR_GET_NAME
#endif
//! Set the thread's name at the process level. Does not affect the
//! internal name.
static void SetThreadName(const char* name)
{
#if defined(PR_SET_NAME)
// Only the first 15 characters are used (16 - NUL terminator)
::prctl(PR_SET_NAME, name, 0, 0, 0);
#elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
pthread_set_name_np(pthread_self(), name);
#elif defined(MAC_OSX)
pthread_setname_np(name);
#else
// Prevent warnings for unused parameters...
(void)name;
#endif
}
// If we have thread_local, just keep thread ID and name in a thread_local
// global.
#if defined(HAVE_THREAD_LOCAL)
static thread_local std::string g_thread_name;
const std::string& util::ThreadGetInternalName() { return g_thread_name; }
//! Set the in-memory internal name for this thread. Does not affect the process
//! name.
static void SetInternalName(std::string name) { g_thread_name = std::move(name); }
// Without thread_local available, don't handle internal name at all.
#else
static const std::string empty_string;
const std::string& util::ThreadGetInternalName() { return empty_string; }
static void SetInternalName(std::string name) { }
#endif
void util::ThreadRename(std::string&& name)
{
SetThreadName(("b-" + name).c_str());
SetInternalName(std::move(name));
}
void util::ThreadSetInternalName(std::string&& name)
{
SetInternalName(std::move(name));
}

26
src/util/threadnames.h Normal file
View file

@ -0,0 +1,26 @@
// Copyright (c) 2018-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_THREADNAMES_H
#define DIGIBYTE_UTIL_THREADNAMES_H
#include <string>
namespace util {
//! Rename a thread both in terms of an internal (in-memory) name as well
//! as its system thread name.
//! @note Do not call this for the main thread, as this will interfere with
//! UNIX utilities such as top and killall. Use ThreadSetInternalName instead.
void ThreadRename(std::string&&);
//! Set the internal (in-memory) name of the current thread only.
void ThreadSetInternalName(std::string&&);
//! Get the thread's internal (in-memory) name; used e.g. for identification in
//! logging.
const std::string& ThreadGetInternalName();
} // namespace util
#endif // DIGIBYTE_UTIL_THREADNAMES_H

183
src/util/time.cpp Normal file
View file

@ -0,0 +1,183 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte 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/vexta-config.h>
#endif
#include <compat.h>
#include <util/time.h>
#include <util/check.h>
#include <atomic>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <ctime>
#include <thread>
#include <tinyformat.h>
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
static std::atomic<int64_t> nMockTime(0); //!< For testing
int64_t GetTime()
{
int64_t mocktime = nMockTime.load(std::memory_order_relaxed);
if (mocktime) return mocktime;
time_t now = time(nullptr);
assert(now > 0);
return now;
}
bool ChronoSanityCheck()
{
// std::chrono::system_clock.time_since_epoch and time_t(0) are not guaranteed
// to use the Unix epoch timestamp, prior to C++20, but in practice they almost
// certainly will. Any differing behavior will be assumed to be an error, unless
// certain platforms prove to consistently deviate, at which point we'll cope
// with it by adding offsets.
// Create a new clock from time_t(0) and make sure that it represents 0
// seconds from the system_clock's time_since_epoch. Then convert that back
// to a time_t and verify that it's the same as before.
const time_t time_t_epoch{};
auto clock = std::chrono::system_clock::from_time_t(time_t_epoch);
if (std::chrono::duration_cast<std::chrono::seconds>(clock.time_since_epoch()).count() != 0) {
return false;
}
time_t time_val = std::chrono::system_clock::to_time_t(clock);
if (time_val != time_t_epoch) {
return false;
}
// Check that the above zero time is actually equal to the known unix timestamp.
struct tm epoch;
#ifdef HAVE_GMTIME_R
if (gmtime_r(&time_val, &epoch) == nullptr) {
#else
if (gmtime_s(&epoch, &time_val) != 0) {
#endif
return false;
}
if ((epoch.tm_sec != 0) ||
(epoch.tm_min != 0) ||
(epoch.tm_hour != 0) ||
(epoch.tm_mday != 1) ||
(epoch.tm_mon != 0) ||
(epoch.tm_year != 70)) {
return false;
}
return true;
}
template <typename T>
T GetTime()
{
const std::chrono::seconds mocktime{nMockTime.load(std::memory_order_relaxed)};
return std::chrono::duration_cast<T>(
mocktime.count() ?
mocktime :
std::chrono::microseconds{GetTimeMicros()});
}
template std::chrono::seconds GetTime();
template std::chrono::milliseconds GetTime();
template std::chrono::microseconds GetTime();
template <typename T>
static T GetSystemTime()
{
const auto now = std::chrono::duration_cast<T>(std::chrono::system_clock::now().time_since_epoch());
assert(now.count() > 0);
return now;
}
void SetMockTime(int64_t nMockTimeIn)
{
Assert(nMockTimeIn >= 0);
nMockTime.store(nMockTimeIn, std::memory_order_relaxed);
}
void SetMockTime(std::chrono::seconds mock_time_in)
{
nMockTime.store(mock_time_in.count(), std::memory_order_relaxed);
}
std::chrono::seconds GetMockTime()
{
return std::chrono::seconds(nMockTime.load(std::memory_order_relaxed));
}
int64_t GetTimeMillis()
{
return int64_t{GetSystemTime<std::chrono::milliseconds>().count()};
}
int64_t GetTimeMicros()
{
return int64_t{GetSystemTime<std::chrono::microseconds>().count()};
}
int64_t GetTimeSeconds()
{
return int64_t{GetSystemTime<std::chrono::seconds>().count()};
}
std::string FormatISO8601DateTime(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
#ifdef HAVE_GMTIME_R
if (gmtime_r(&time_val, &ts) == nullptr) {
#else
if (gmtime_s(&ts, &time_val) != 0) {
#endif
return {};
}
return strprintf("%04i-%02i-%02iT%02i:%02i:%02iZ", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
}
std::string FormatISO8601Date(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
#ifdef HAVE_GMTIME_R
if (gmtime_r(&time_val, &ts) == nullptr) {
#else
if (gmtime_s(&ts, &time_val) != 0) {
#endif
return {};
}
return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
}
int64_t ParseISO8601DateTime(const std::string& str)
{
static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
static const std::locale loc(std::locale::classic(),
new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
std::istringstream iss(str);
iss.imbue(loc);
boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
iss >> ptime;
if (ptime.is_not_a_date_time() || epoch > ptime)
return 0;
return (ptime - epoch).total_seconds();
}
struct timeval MillisToTimeval(int64_t nTimeout)
{
struct timeval timeout;
timeout.tv_sec = nTimeout / 1000;
timeout.tv_usec = (nTimeout % 1000) * 1000;
return timeout;
}
struct timeval MillisToTimeval(std::chrono::milliseconds ms)
{
return MillisToTimeval(count_milliseconds(ms));
}

92
src/util/time.h Normal file
View file

@ -0,0 +1,92 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_TIME_H
#define DIGIBYTE_UTIL_TIME_H
#include <compat.h>
#include <chrono>
#include <stdint.h>
#include <string>
using namespace std::chrono_literals;
void UninterruptibleSleep(const std::chrono::microseconds& n);
/**
* Helper to count the seconds of a duration.
*
* All durations should be using std::chrono and calling this should generally
* be avoided in code. Though, it is still preferred to an inline t.count() to
* protect against a reliance on the exact type of t.
*
* This helper is used to convert durations before passing them over an
* interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
*/
constexpr int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
constexpr int64_t count_milliseconds(std::chrono::milliseconds t) { return t.count(); }
constexpr int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
using SecondsDouble = std::chrono::duration<double, std::chrono::seconds::period>;
/**
* Helper to count the seconds in any std::chrono::duration type
*/
inline double CountSecondsDouble(SecondsDouble t) { return t.count(); }
/**
* DEPRECATED
* Use either GetTimeSeconds (not mockable) or GetTime<T> (mockable)
*/
int64_t GetTime();
/** Returns the system time (not mockable) */
int64_t GetTimeMillis();
/** Returns the system time (not mockable) */
int64_t GetTimeMicros();
/** Returns the system time (not mockable) */
int64_t GetTimeSeconds(); // Like GetTime(), but not mockable
/**
* DEPRECATED
* Use SetMockTime with chrono type
*
* @param[in] nMockTimeIn Time in seconds.
*/
void SetMockTime(int64_t nMockTimeIn);
/** For testing. Set e.g. with the setmocktime rpc, or -mocktime argument */
void SetMockTime(std::chrono::seconds mock_time_in);
/** For testing */
std::chrono::seconds GetMockTime();
/** Return system time (or mocked time, if set) */
template <typename T>
T GetTime();
/**
* ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date}
* helper functions if possible.
*/
std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
int64_t ParseISO8601DateTime(const std::string& str);
/**
* Convert milliseconds to a struct timeval for e.g. select.
*/
struct timeval MillisToTimeval(int64_t nTimeout);
/**
* Convert milliseconds to a struct timeval for e.g. select.
*/
struct timeval MillisToTimeval(std::chrono::milliseconds ms);
/** Sanity check epoch match normal Unix epoch */
bool ChronoSanityCheck();
#endif // DIGIBYTE_UTIL_TIME_H

109
src/util/tokenpipe.cpp Normal file
View file

@ -0,0 +1,109 @@
// Copyright (c) 2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/tokenpipe.h>
#include <config/vexta-config.h>
#ifndef WIN32
#include <errno.h>
#include <fcntl.h>
#include <optional>
#include <unistd.h>
TokenPipeEnd TokenPipe::TakeReadEnd()
{
TokenPipeEnd res(m_fds[0]);
m_fds[0] = -1;
return res;
}
TokenPipeEnd TokenPipe::TakeWriteEnd()
{
TokenPipeEnd res(m_fds[1]);
m_fds[1] = -1;
return res;
}
TokenPipeEnd::TokenPipeEnd(int fd) : m_fd(fd)
{
}
TokenPipeEnd::~TokenPipeEnd()
{
Close();
}
int TokenPipeEnd::TokenWrite(uint8_t token)
{
while (true) {
ssize_t result = write(m_fd, &token, 1);
if (result < 0) {
// Failure. It's possible that the write was interrupted by a signal,
// in that case retry.
if (errno != EINTR) {
return TS_ERR;
}
} else if (result == 0) {
return TS_EOS;
} else { // ==1
return 0;
}
}
}
int TokenPipeEnd::TokenRead()
{
uint8_t token;
while (true) {
ssize_t result = read(m_fd, &token, 1);
if (result < 0) {
// Failure. Check if the read was interrupted by a signal,
// in that case retry.
if (errno != EINTR) {
return TS_ERR;
}
} else if (result == 0) {
return TS_EOS;
} else { // ==1
return token;
}
}
return token;
}
void TokenPipeEnd::Close()
{
if (m_fd != -1) close(m_fd);
m_fd = -1;
}
std::optional<TokenPipe> TokenPipe::Make()
{
int fds[2] = {-1, -1};
#if HAVE_O_CLOEXEC && HAVE_DECL_PIPE2
if (pipe2(fds, O_CLOEXEC) != 0) {
return std::nullopt;
}
#else
if (pipe(fds) != 0) {
return std::nullopt;
}
#endif
return TokenPipe(fds);
}
TokenPipe::~TokenPipe()
{
Close();
}
void TokenPipe::Close()
{
if (m_fds[0] != -1) close(m_fds[0]);
if (m_fds[1] != -1) close(m_fds[1]);
m_fds[0] = m_fds[1] = -1;
}
#endif // WIN32

127
src/util/tokenpipe.h Normal file
View file

@ -0,0 +1,127 @@
// Copyright (c) 2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_TOKENPIPE_H
#define DIGIBYTE_UTIL_TOKENPIPE_H
#ifndef WIN32
#include <cstdint>
#include <optional>
/** One end of a token pipe. */
class TokenPipeEnd
{
private:
int m_fd = -1;
public:
TokenPipeEnd(int fd = -1);
~TokenPipeEnd();
/** Return value constants for TokenWrite and TokenRead. */
enum Status {
TS_ERR = -1, //!< I/O error
TS_EOS = -2, //!< Unexpected end of stream
};
/** Write token to endpoint.
*
* @returns 0 If successful.
* <0 if error:
* TS_ERR If an error happened.
* TS_EOS If end of stream happened.
*/
int TokenWrite(uint8_t token);
/** Read token from endpoint.
*
* @returns >=0 Token value, if successful.
* <0 if error:
* TS_ERR If an error happened.
* TS_EOS If end of stream happened.
*/
int TokenRead();
/** Explicit close function.
*/
void Close();
/** Return whether endpoint is open.
*/
bool IsOpen() { return m_fd != -1; }
// Move-only class.
TokenPipeEnd(TokenPipeEnd&& other)
{
m_fd = other.m_fd;
other.m_fd = -1;
}
TokenPipeEnd& operator=(TokenPipeEnd&& other)
{
Close();
m_fd = other.m_fd;
other.m_fd = -1;
return *this;
}
TokenPipeEnd(const TokenPipeEnd&) = delete;
TokenPipeEnd& operator=(const TokenPipeEnd&) = delete;
};
/** An interprocess or interthread pipe for sending tokens (one-byte values)
* over.
*/
class TokenPipe
{
private:
int m_fds[2] = {-1, -1};
TokenPipe(int fds[2]) : m_fds{fds[0], fds[1]} {}
public:
~TokenPipe();
/** Create a new pipe.
* @returns The created TokenPipe, or an empty std::nullopt in case of error.
*/
static std::optional<TokenPipe> Make();
/** Take the read end of this pipe. This can only be called once,
* as the object will be moved out.
*/
TokenPipeEnd TakeReadEnd();
/** Take the write end of this pipe. This should only be called once,
* as the object will be moved out.
*/
TokenPipeEnd TakeWriteEnd();
/** Close and end of the pipe that hasn't been moved out.
*/
void Close();
// Move-only class.
TokenPipe(TokenPipe&& other)
{
for (int i = 0; i < 2; ++i) {
m_fds[i] = other.m_fds[i];
other.m_fds[i] = -1;
}
}
TokenPipe& operator=(TokenPipe&& other)
{
Close();
for (int i = 0; i < 2; ++i) {
m_fds[i] = other.m_fds[i];
other.m_fds[i] = -1;
}
return *this;
}
TokenPipe(const TokenPipe&) = delete;
TokenPipe& operator=(const TokenPipe&) = delete;
};
#endif // WIN32
#endif // DIGIBYTE_UTIL_TOKENPIPE_H

45
src/util/trace.h Normal file
View file

@ -0,0 +1,45 @@
// Copyright (c) 2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_TRACE_H
#define DIGIBYTE_UTIL_TRACE_H
#ifdef ENABLE_TRACING
#include <sys/sdt.h>
#define TRACE(context, event) DTRACE_PROBE(context, event)
#define TRACE1(context, event, a) DTRACE_PROBE1(context, event, a)
#define TRACE2(context, event, a, b) DTRACE_PROBE2(context, event, a, b)
#define TRACE3(context, event, a, b, c) DTRACE_PROBE3(context, event, a, b, c)
#define TRACE4(context, event, a, b, c, d) DTRACE_PROBE4(context, event, a, b, c, d)
#define TRACE5(context, event, a, b, c, d, e) DTRACE_PROBE5(context, event, a, b, c, d, e)
#define TRACE6(context, event, a, b, c, d, e, f) DTRACE_PROBE6(context, event, a, b, c, d, e, f)
#define TRACE7(context, event, a, b, c, d, e, f, g) DTRACE_PROBE7(context, event, a, b, c, d, e, f, g)
#define TRACE8(context, event, a, b, c, d, e, f, g, h) DTRACE_PROBE8(context, event, a, b, c, d, e, f, g, h)
#define TRACE9(context, event, a, b, c, d, e, f, g, h, i) DTRACE_PROBE9(context, event, a, b, c, d, e, f, g, h, i)
#define TRACE10(context, event, a, b, c, d, e, f, g, h, i, j) DTRACE_PROBE10(context, event, a, b, c, d, e, f, g, h, i, j)
#define TRACE11(context, event, a, b, c, d, e, f, g, h, i, j, k) DTRACE_PROBE11(context, event, a, b, c, d, e, f, g, h, i, j, k)
#define TRACE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l) DTRACE_PROBE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l)
#else
#define TRACE(context, event)
#define TRACE1(context, event, a)
#define TRACE2(context, event, a, b)
#define TRACE3(context, event, a, b, c)
#define TRACE4(context, event, a, b, c, d)
#define TRACE5(context, event, a, b, c, d, e)
#define TRACE6(context, event, a, b, c, d, e, f)
#define TRACE7(context, event, a, b, c, d, e, f, g)
#define TRACE8(context, event, a, b, c, d, e, f, g, h)
#define TRACE9(context, event, a, b, c, d, e, f, g, h, i)
#define TRACE10(context, event, a, b, c, d, e, f, g, h, i, j)
#define TRACE11(context, event, a, b, c, d, e, f, g, h, i, j, k)
#define TRACE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l)
#endif
#endif /* DIGIBYTE_UTIL_TRACE_H */

62
src/util/translation.h Normal file
View file

@ -0,0 +1,62 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_TRANSLATION_H
#define DIGIBYTE_UTIL_TRANSLATION_H
#include <tinyformat.h>
#include <functional>
/**
* Bilingual messages:
* - in GUI: user's native language + untranslated (i.e. English)
* - in log and stderr: untranslated only
*/
struct bilingual_str {
std::string original;
std::string translated;
bilingual_str& operator+=(const bilingual_str& rhs)
{
original += rhs.original;
translated += rhs.translated;
return *this;
}
bool empty() const
{
return original.empty();
}
};
inline bilingual_str operator+(bilingual_str lhs, const bilingual_str& rhs)
{
lhs += rhs;
return lhs;
}
/** Mark a bilingual_str as untranslated */
inline bilingual_str Untranslated(std::string original) { return {original, original}; }
namespace tinyformat {
template <typename... Args>
bilingual_str format(const bilingual_str& fmt, const Args&... args)
{
return bilingual_str{format(fmt.original, args...), format(fmt.translated, args...)};
}
} // namespace tinyformat
/** Translate a message to the native language of the user. */
const extern std::function<std::string(const char*)> G_TRANSLATION_FUN;
/**
* Translation function.
* If no translation function is set, simply return the input.
*/
inline bilingual_str _(const char* psz)
{
return bilingual_str{psz, G_TRANSLATION_FUN ? (G_TRANSLATION_FUN)(psz) : psz};
}
#endif // DIGIBYTE_UTIL_TRANSLATION_H

15
src/util/ui_change_type.h Normal file
View file

@ -0,0 +1,15 @@
// Copyright (c) 2012-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_UI_CHANGE_TYPE_H
#define DIGIBYTE_UTIL_UI_CHANGE_TYPE_H
/** General change type (added, updated, removed). */
enum ChangeType {
CT_NEW,
CT_UPDATED,
CT_DELETED
};
#endif // DIGIBYTE_UTIL_UI_CHANGE_TYPE_H

21
src/util/url.cpp Normal file
View file

@ -0,0 +1,21 @@
// Copyright (c) 2015-2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/url.h>
#include <event2/http.h>
#include <stdlib.h>
#include <string>
std::string urlDecode(const std::string &urlEncoded) {
std::string res;
if (!urlEncoded.empty()) {
char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
if (decoded) {
res = std::string(decoded);
free(decoded);
}
}
return res;
}

14
src/util/url.h Normal file
View file

@ -0,0 +1,14 @@
// Copyright (c) 2015-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_URL_H
#define DIGIBYTE_UTIL_URL_H
#include <string>
using UrlDecodeFn = std::string(const std::string& url_encoded);
UrlDecodeFn urlDecode;
extern UrlDecodeFn* const URL_DECODE;
#endif // DIGIBYTE_UTIL_URL_H

51
src/util/vector.h Normal file
View file

@ -0,0 +1,51 @@
// Copyright (c) 2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_UTIL_VECTOR_H
#define DIGIBYTE_UTIL_VECTOR_H
#include <initializer_list>
#include <type_traits>
#include <vector>
/** Construct a vector with the specified elements.
*
* This is preferable over the list initializing constructor of std::vector:
* - It automatically infers the element type from its arguments.
* - If any arguments are rvalue references, they will be moved into the vector
* (list initialization always copies).
*/
template<typename... Args>
inline std::vector<typename std::common_type<Args...>::type> Vector(Args&&... args)
{
std::vector<typename std::common_type<Args...>::type> ret;
ret.reserve(sizeof...(args));
// The line below uses the trick from https://www.experts-exchange.com/articles/32502/None-recursive-variadic-templates-with-std-initializer-list.html
(void)std::initializer_list<int>{(ret.emplace_back(std::forward<Args>(args)), 0)...};
return ret;
}
/** Concatenate two vectors, moving elements. */
template<typename V>
inline V Cat(V v1, V&& v2)
{
v1.reserve(v1.size() + v2.size());
for (auto& arg : v2) {
v1.push_back(std::move(arg));
}
return v1;
}
/** Concatenate two vectors. */
template<typename V>
inline V Cat(V v1, const V& v2)
{
v1.reserve(v1.size() + v2.size());
for (const auto& arg : v2) {
v1.push_back(arg);
}
return v1;
}
#endif // DIGIBYTE_UTIL_VECTOR_H