Initial Vexta Core v0.1.0 release
This commit is contained in:
commit
25c454329d
2481 changed files with 689289 additions and 0 deletions
185
src/util/asmap.cpp
Normal file
185
src/util/asmap.cpp
Normal 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
15
src/util/asmap.h
Normal 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
66
src/util/bip32.cpp
Normal 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
20
src/util/bip32.h
Normal 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
|
||||
18
src/util/bytevectorhash.cpp
Normal file
18
src/util/bytevectorhash.cpp
Normal 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
26
src/util/bytevectorhash.h
Normal 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
75
src/util/check.h
Normal 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
91
src/util/epochguard.h
Normal 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
56
src/util/error.cpp
Normal 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
45
src/util/error.h
Normal 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
67
src/util/fees.cpp
Normal 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
18
src/util/fees.h
Normal 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
|
||||
10
src/util/getuniquepath.cpp
Normal file
10
src/util/getuniquepath.cpp
Normal 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
19
src/util/getuniquepath.h
Normal 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
43
src/util/golombrice.h
Normal 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
72
src/util/hash_type.h
Normal 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
19
src/util/hasher.cpp
Normal 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
99
src/util/hasher.h
Normal 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
18
src/util/macros.h
Normal 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
92
src/util/message.cpp
Normal 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
76
src/util/message.h
Normal 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
83
src/util/moneystr.cpp
Normal 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
25
src/util/moneystr.h
Normal 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
17
src/util/rbf.cpp
Normal 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
18
src/util/rbf.h
Normal 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
|
||||
47
src/util/readwritefile.cpp
Normal file
47
src/util/readwritefile.cpp
Normal 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
28
src/util/readwritefile.h
Normal 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
64
src/util/serfloat.cpp
Normal 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
16
src/util/serfloat.h
Normal 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
240
src/util/settings.cpp
Normal 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
108
src/util/settings.h
Normal 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
343
src/util/sock.cpp
Normal 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
185
src/util/sock.h
Normal 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
67
src/util/spanparsing.cpp
Normal 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
50
src/util/spanparsing.h
Normal 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
607
src/util/strencodings.cpp
Normal 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
273
src/util/strencodings.h
Normal 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
5
src/util/string.cpp
Normal 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
99
src/util/string.h
Normal 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
1411
src/util/system.cpp
Normal file
File diff suppressed because it is too large
Load diff
544
src/util/system.h
Normal file
544
src/util/system.h
Normal 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
27
src/util/thread.cpp
Normal 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
18
src/util/thread.h
Normal 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
66
src/util/threadnames.cpp
Normal 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
26
src/util/threadnames.h
Normal 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
183
src/util/time.cpp
Normal 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
92
src/util/time.h
Normal 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
109
src/util/tokenpipe.cpp
Normal 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
127
src/util/tokenpipe.h
Normal 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
45
src/util/trace.h
Normal 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
62
src/util/translation.h
Normal 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
15
src/util/ui_change_type.h
Normal 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
21
src/util/url.cpp
Normal 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
14
src/util/url.h
Normal 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
51
src/util/vector.h
Normal 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
|
||||
Loading…
Reference in a new issue