Page MenuHomePhabricator

No OneTemporary

diff --git a/src/key.cpp b/src/key.cpp
index bc05c9bef..9c5379c80 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -1,472 +1,472 @@
// Copyright (c) 2009-2016 The Bitcoin 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 <key.h>
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
#include <random.h>
#include <secp256k1.h>
#include <secp256k1_recovery.h>
#include <secp256k1_schnorr.h>
static secp256k1_context *secp256k1_context_sign = nullptr;
/**
* These functions are taken from the libsecp256k1 distribution and are very
* ugly.
*/
/**
* This parses a format loosely based on a DER encoding of the ECPrivateKey type
* from section C.4 of SEC 1 <http://www.secg.org/sec1-v2.pdf>, with the
* following caveats:
*
* * The octet-length of the SEQUENCE must be encoded as 1 or 2 octets. It is
* not required to be encoded as one octet if it is less than 256, as DER would
* require.
* * The octet-length of the SEQUENCE must not be greater than the remaining
* length of the key encoding, but need not match it (i.e. the encoding may
* contain junk after the encoded SEQUENCE).
* * The privateKey OCTET STRING is zero-filled on the left to 32 octets.
* * Anything after the encoding of the privateKey OCTET STRING is ignored,
* whether or not it is validly encoded DER.
*
* out32 must point to an output buffer of length at least 32 bytes.
*/
int ec_privkey_import_der(const secp256k1_context *ctx, uint8_t *out32,
const uint8_t *privkey, size_t privkeylen) {
const uint8_t *end = privkey + privkeylen;
memset(out32, 0, 32);
/* sequence header */
if (end - privkey < 1 || *privkey != 0x30u) {
return 0;
}
privkey++;
/* sequence length constructor */
if (end - privkey < 1 || !(*privkey & 0x80u)) {
return 0;
}
ptrdiff_t lenb = *privkey & ~0x80u;
privkey++;
if (lenb < 1 || lenb > 2) {
return 0;
}
if (end - privkey < lenb) {
return 0;
}
/* sequence length */
ptrdiff_t len =
privkey[lenb - 1] | (lenb > 1 ? privkey[lenb - 2] << 8 : 0u);
privkey += lenb;
if (end - privkey < len) {
return 0;
}
/* sequence element 0: version number (=1) */
if (end - privkey < 3 || privkey[0] != 0x02u || privkey[1] != 0x01u ||
privkey[2] != 0x01u) {
return 0;
}
privkey += 3;
/* sequence element 1: octet string, up to 32 bytes */
if (end - privkey < 2 || privkey[0] != 0x04u) {
return 0;
}
ptrdiff_t oslen = privkey[1];
privkey += 2;
if (oslen > 32 || end - privkey < oslen) {
return 0;
}
memcpy(out32 + (32 - oslen), privkey, oslen);
if (!secp256k1_ec_seckey_verify(ctx, out32)) {
memset(out32, 0, 32);
return 0;
}
return 1;
}
/**
* This serializes to a DER encoding of the ECPrivateKey type from section C.4
* of SEC 1 <http://www.secg.org/sec1-v2.pdf>. The optional parameters and
* publicKey fields are included.
*
* privkey must point to an output buffer of length at least
* CKey::SIZE bytes. privkeylen must initially be set to the size of
* the privkey buffer. Upon return it will be set to the number of bytes used in
* the buffer. key32 must point to a 32-byte raw private key.
*/
int ec_privkey_export_der(const secp256k1_context *ctx, uint8_t *privkey,
size_t *privkeylen, const uint8_t *key32,
bool compressed) {
assert(*privkeylen >= CKey::SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
*privkeylen = 0;
return 0;
}
if (compressed) {
static const uint8_t begin[] = {0x30, 0x81, 0xD3, 0x02,
0x01, 0x01, 0x04, 0x20};
static const uint8_t middle[] = {
0xA0, 0x81, 0x85, 0x30, 0x81, 0x82, 0x02, 0x01, 0x01, 0x30, 0x2C,
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F,
0x30, 0x06, 0x04, 0x01, 0x00, 0x04, 0x01, 0x07, 0x04, 0x21, 0x02,
0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62,
0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE,
0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x02,
0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6,
0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41,
0x41, 0x02, 0x01, 0x01, 0xA1, 0x24, 0x03, 0x22, 0x00};
uint8_t *ptr = privkey;
memcpy(ptr, begin, sizeof(begin));
ptr += sizeof(begin);
memcpy(ptr, key32, 32);
ptr += 32;
memcpy(ptr, middle, sizeof(middle));
ptr += sizeof(middle);
pubkeylen = CPubKey::COMPRESSED_SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey,
SECP256K1_EC_COMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
assert(*privkeylen == CKey::COMPRESSED_SIZE);
} else {
static const uint8_t begin[] = {0x30, 0x82, 0x01, 0x13, 0x02,
0x01, 0x01, 0x04, 0x20};
static const uint8_t middle[] = {
0xA0, 0x81, 0xA5, 0x30, 0x81, 0xA2, 0x02, 0x01, 0x01, 0x30, 0x2C,
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F,
0x30, 0x06, 0x04, 0x01, 0x00, 0x04, 0x01, 0x07, 0x04, 0x41, 0x04,
0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62,
0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE,
0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x48,
0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC,
0x0E, 0x11, 0x08, 0xA8, 0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54,
0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, 0xB8, 0x02, 0x21,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF,
0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41,
0x02, 0x01, 0x01, 0xA1, 0x44, 0x03, 0x42, 0x00};
uint8_t *ptr = privkey;
memcpy(ptr, begin, sizeof(begin));
ptr += sizeof(begin);
memcpy(ptr, key32, 32);
ptr += 32;
memcpy(ptr, middle, sizeof(middle));
ptr += sizeof(middle);
pubkeylen = CPubKey::SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey,
SECP256K1_EC_UNCOMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
assert(*privkeylen == CKey::SIZE);
}
return 1;
}
bool CKey::Check(const uint8_t *vch) {
return secp256k1_ec_seckey_verify(secp256k1_context_sign, vch);
}
void CKey::MakeNewKey(bool fCompressedIn) {
do {
GetStrongRandBytes(keydata);
} while (!Check(keydata.data()));
fValid = true;
fCompressed = fCompressedIn;
}
bool CKey::Negate() {
assert(fValid);
return secp256k1_ec_privkey_negate(secp256k1_context_sign, keydata.data());
}
CPrivKey CKey::GetPrivKey() const {
assert(fValid);
CPrivKey privkey;
int ret;
size_t privkeylen;
privkey.resize(SIZE);
privkeylen = SIZE;
ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(),
&privkeylen, begin(), fCompressed);
assert(ret);
privkey.resize(privkeylen);
return privkey;
}
CPubKey CKey::GetPubKey() const {
assert(fValid);
secp256k1_pubkey pubkey;
size_t clen = CPubKey::SIZE;
CPubKey result;
int ret =
secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, begin());
assert(ret);
secp256k1_ec_pubkey_serialize(
secp256k1_context_sign, (uint8_t *)result.begin(), &clen, &pubkey,
fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
assert(result.size() == clen);
assert(result.IsValid());
return result;
}
// Check that the sig has a low R value and will be less than 71 bytes
bool SigHasLowR(const secp256k1_ecdsa_signature *sig) {
uint8_t compact_sig[64];
secp256k1_ecdsa_signature_serialize_compact(secp256k1_context_sign,
compact_sig, sig);
// In DER serialization, all values are interpreted as big-endian, signed
// integers. The highest bit in the integer indicates its signed-ness; 0 is
// positive, 1 is negative. When the value is interpreted as a negative
// integer, it must be converted to a positive value by prepending a 0x00
// byte so that the highest bit is 0. We can avoid this prepending by
// ensuring that our highest bit is always 0, and thus we must check that
// the first byte is less than 0x80.
return compact_sig[0] < 0x80;
}
bool CKey::SignECDSA(const uint256 &hash, std::vector<uint8_t> &vchSig,
bool grind, uint32_t test_case) const {
if (!fValid) {
return false;
}
vchSig.resize(CPubKey::SIGNATURE_SIZE);
size_t nSigLen = CPubKey::SIGNATURE_SIZE;
uint8_t extra_entropy[32] = {0};
WriteLE32(extra_entropy, test_case);
secp256k1_ecdsa_signature sig;
uint32_t counter = 0;
int ret =
secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(),
begin(), secp256k1_nonce_function_rfc6979,
(!grind && test_case) ? extra_entropy : nullptr);
// Grind for low R
while (ret && !SigHasLowR(&sig) && grind) {
WriteLE32(extra_entropy, ++counter);
ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(),
begin(), secp256k1_nonce_function_rfc6979,
extra_entropy);
}
assert(ret);
secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign,
vchSig.data(), &nSigLen, &sig);
vchSig.resize(nSigLen);
return true;
}
static bool DoSignSchnorr(const CKey &key, const uint256 &hash, uint8_t *buf,
uint32_t test_case) {
if (!key.IsValid()) {
return false;
}
uint8_t extra_entropy[32] = {0};
WriteLE32(extra_entropy, test_case);
int ret = secp256k1_schnorr_sign(
secp256k1_context_sign, buf, hash.begin(), key.begin(),
secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : nullptr);
assert(ret);
return true;
}
bool CKey::SignSchnorr(const uint256 &hash, SchnorrSig &sig,
uint32_t test_case) const {
return DoSignSchnorr(*this, hash, sig.data(), test_case);
}
bool CKey::SignSchnorr(const uint256 &hash, std::vector<uint8_t> &vchSig,
uint32_t test_case) const {
if (!fValid) {
return false;
}
vchSig.resize(CPubKey::SCHNORR_SIZE);
return DoSignSchnorr(*this, hash, vchSig.data(), test_case);
}
bool CKey::VerifyPubKey(const CPubKey &pubkey) const {
if (pubkey.IsCompressed() != fCompressed) {
return false;
}
uint8_t rnd[8];
std::string str = "Bitcoin key verification\n";
GetRandBytes(rnd);
uint256 hash;
CHash256().Write(MakeUCharSpan(str)).Write(rnd).Finalize(hash);
std::vector<uint8_t> vchSig;
SignECDSA(hash, vchSig);
return pubkey.VerifyECDSA(hash, vchSig);
}
bool CKey::SignCompact(const uint256 &hash,
std::vector<uint8_t> &vchSig) const {
if (!fValid) {
return false;
}
vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE);
int rec = -1;
secp256k1_ecdsa_recoverable_signature sig;
int ret = secp256k1_ecdsa_sign_recoverable(
secp256k1_context_sign, &sig, hash.begin(), begin(),
secp256k1_nonce_function_rfc6979, nullptr);
assert(ret);
ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(
secp256k1_context_sign, &vchSig[1], &rec, &sig);
assert(ret);
assert(rec != -1);
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
return true;
}
bool CKey::Load(const CPrivKey &privkey, const CPubKey &vchPubKey,
bool fSkipCheck = false) {
if (!ec_privkey_import_der(secp256k1_context_sign, (uint8_t *)begin(),
privkey.data(), privkey.size())) {
return false;
}
fCompressed = vchPubKey.IsCompressed();
fValid = true;
if (fSkipCheck) {
return true;
}
return VerifyPubKey(vchPubKey);
}
bool CKey::Derive(CKey &keyChild, ChainCode &ccChild, unsigned int nChild,
const ChainCode &cc) const {
assert(IsValid());
assert(IsCompressed());
std::vector<uint8_t, secure_allocator<uint8_t>> vout(64);
if ((nChild >> 31) == 0) {
CPubKey pubkey = GetPubKey();
assert(pubkey.size() == CPubKey::COMPRESSED_SIZE);
BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin() + 1, vout.data());
} else {
assert(size() == 32);
BIP32Hash(cc, nChild, 0, begin(), vout.data());
}
memcpy(ccChild.begin(), vout.data() + 32, 32);
memcpy((uint8_t *)keyChild.begin(), begin(), 32);
bool ret = secp256k1_ec_privkey_tweak_add(
secp256k1_context_sign, (uint8_t *)keyChild.begin(), vout.data());
keyChild.fCompressed = true;
keyChild.fValid = ret;
return ret;
}
bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
out.nDepth = nDepth + 1;
CKeyID id = key.GetPubKey().GetID();
memcpy(out.vchFingerprint, &id, 4);
out.nChild = _nChild;
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
}
-void CExtKey::SetSeed(Span<const uint8_t> seed) {
+void CExtKey::SetSeed(Span<const std::byte> seed) {
static const uint8_t hashkey[] = {'B', 'i', 't', 'c', 'o', 'i',
'n', ' ', 's', 'e', 'e', 'd'};
std::vector<uint8_t, secure_allocator<uint8_t>> vout(64);
CHMAC_SHA512{hashkey, sizeof(hashkey)}
- .Write(seed.data(), seed.size())
+ .Write(UCharCast(seed.data()), seed.size())
.Finalize(vout.data());
key.Set(vout.data(), vout.data() + 32, true);
memcpy(chaincode.begin(), vout.data() + 32, 32);
nDepth = 0;
nChild = 0;
memset(vchFingerprint, 0, sizeof(vchFingerprint));
}
CExtPubKey CExtKey::Neuter() const {
CExtPubKey ret;
ret.nDepth = nDepth;
memcpy(ret.vchFingerprint, vchFingerprint, 4);
ret.nChild = nChild;
ret.pubkey = key.GetPubKey();
ret.chaincode = chaincode;
return ret;
}
void CExtKey::Encode(uint8_t code[BIP32_EXTKEY_SIZE]) const {
code[0] = nDepth;
memcpy(code + 1, vchFingerprint, 4);
code[5] = (nChild >> 24) & 0xFF;
code[6] = (nChild >> 16) & 0xFF;
code[7] = (nChild >> 8) & 0xFF;
code[8] = (nChild >> 0) & 0xFF;
memcpy(code + 9, chaincode.begin(), 32);
code[41] = 0;
assert(key.size() == 32);
memcpy(code + 42, key.begin(), 32);
}
void CExtKey::Decode(const uint8_t code[BIP32_EXTKEY_SIZE]) {
nDepth = code[0];
memcpy(vchFingerprint, code + 1, 4);
nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
memcpy(chaincode.begin(), code + 9, 32);
key.Set(code + 42, code + BIP32_EXTKEY_SIZE, true);
}
bool ECC_InitSanityCheck() {
CKey key;
key.MakeNewKey(true);
CPubKey pubkey = key.GetPubKey();
return key.VerifyPubKey(pubkey);
}
void ECC_Start() {
assert(secp256k1_context_sign == nullptr);
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
assert(ctx != nullptr);
{
// Pass in a random blinding seed to the secp256k1 context.
std::vector<uint8_t, secure_allocator<uint8_t>> vseed(32);
GetRandBytes(vseed);
bool ret = secp256k1_context_randomize(ctx, vseed.data());
assert(ret);
}
secp256k1_context_sign = ctx;
}
void ECC_Stop() {
secp256k1_context *ctx = secp256k1_context_sign;
secp256k1_context_sign = nullptr;
if (ctx) {
secp256k1_context_destroy(ctx);
}
}
static CKey validKey(bool compressed) {
CKey ret;
ret.MakeNewKey(compressed);
return ret;
}
CKey CKey::MakeCompressedKey() {
return validKey(true);
}
CKey CKey::MakeUncompressedKey() {
return validKey(false);
}
diff --git a/src/key.h b/src/key.h
index 4999dd8f2..ba756a467 100644
--- a/src/key.h
+++ b/src/key.h
@@ -1,204 +1,206 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin 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.
#ifndef BITCOIN_KEY_H
#define BITCOIN_KEY_H
#include <pubkey.h>
#include <support/allocators/secure.h>
#include <uint256.h>
#include <stdexcept>
#include <vector>
/**
* secure_allocator is defined in allocators.h
* CPrivKey is a serialized private key, with all parameters included
* (SIZE bytes)
*/
typedef std::vector<uint8_t, secure_allocator<uint8_t>> CPrivKey;
//! a Schnorr signature
using SchnorrSig = std::array<uint8_t, CPubKey::SCHNORR_SIZE>;
/** An encapsulated secp256k1 private key. */
class CKey {
public:
/**
* secp256k1:
*/
static const unsigned int SIZE = 279;
static const unsigned int COMPRESSED_SIZE = 214;
/**
* see www.keylength.com
* script supports up to 75 for single byte push
*/
static_assert(SIZE >= COMPRESSED_SIZE,
"COMPRESSED_SIZE is larger than SIZE");
private:
//! Whether this private key is valid. We check for correctness when
//! modifying the key data, so fValid should always correspond to the actual
//! state.
bool fValid;
//! Whether the public key corresponding to this private key is (to be)
//! compressed.
bool fCompressed;
//! The actual byte data
std::vector<uint8_t, secure_allocator<uint8_t>> keydata;
//! Check whether the 32-byte array pointed to by vch is valid keydata.
static bool Check(const uint8_t *vch);
public:
//! Construct an invalid private key.
CKey() : fValid(false), fCompressed(false) {
// Important: vch must be 32 bytes in length to not break serialization
keydata.resize(32);
}
//! Produce a valid compressed key
static CKey MakeCompressedKey();
//! Produce a valid uncompressed key
static CKey MakeUncompressedKey();
friend bool operator==(const CKey &a, const CKey &b) {
return a.fCompressed == b.fCompressed && a.size() == b.size() &&
memcmp(a.keydata.data(), b.keydata.data(), a.size()) == 0;
}
//! Initialize using begin and end iterators to byte data.
template <typename T>
void Set(const T pbegin, const T pend, bool fCompressedIn) {
if (size_t(pend - pbegin) != keydata.size()) {
fValid = false;
} else if (Check(&pbegin[0])) {
memcpy(keydata.data(), (uint8_t *)&pbegin[0], keydata.size());
fValid = true;
fCompressed = fCompressedIn;
} else {
fValid = false;
}
}
//! Simple read-only vector-like interface.
unsigned int size() const { return (fValid ? keydata.size() : 0); }
- const uint8_t *data() const { return keydata.data(); }
+ const std::byte *data() const {
+ return reinterpret_cast<const std::byte *>(keydata.data());
+ }
const uint8_t *begin() const { return keydata.data(); }
const uint8_t *end() const { return keydata.data() + size(); }
//! Check whether this private key is valid.
bool IsValid() const { return fValid; }
//! Check whether the public key corresponding to this private key is (to
//! be) compressed.
bool IsCompressed() const { return fCompressed; }
//! Generate a new private key using a cryptographic PRNG.
void MakeNewKey(bool fCompressed);
//! Negate private key
bool Negate();
/**
* Convert the private key to a CPrivKey (serialized OpenSSL private key
* data).
* This is expensive.
*/
CPrivKey GetPrivKey() const;
/**
* Compute the public key from a private key.
* This is expensive.
*/
CPubKey GetPubKey() const;
/**
* Create a DER-serialized ECDSA signature.
* The test_case parameter tweaks the deterministic nonce.
*/
bool SignECDSA(const uint256 &hash, std::vector<uint8_t> &vchSig,
bool grind = true, uint32_t test_case = 0) const;
/**
* Create a Schnorr signature.
* The test_case parameter tweaks the deterministic nonce.
*/
bool SignSchnorr(const uint256 &hash, SchnorrSig &sig,
uint32_t test_case = 0) const;
bool SignSchnorr(const uint256 &hash, std::vector<uint8_t> &vchSig,
uint32_t test_case = 0) const;
/**
* Create a compact ECDSA signature (65 bytes), which allows reconstructing
* the used public key.
* The format is one header byte, followed by two times 32 bytes for the
* serialized r and s values.
* The header byte: 0x1B = first key with even y, 0x1C = first key with odd
* y,
* 0x1D = second key with even y, 0x1E = second key with
* odd y,
* add 0x04 for compressed keys.
*/
bool SignCompact(const uint256 &hash, std::vector<uint8_t> &vchSig) const;
//! Derive BIP32 child key.
bool Derive(CKey &keyChild, ChainCode &ccChild, unsigned int nChild,
const ChainCode &cc) const;
/**
* Verify thoroughly whether a private key and a public key match.
* This is done using a different mechanism than just regenerating it.
* (An ECDSA signature is created then verified.)
*/
bool VerifyPubKey(const CPubKey &vchPubKey) const;
//! Load private key and check that public key matches.
bool Load(const CPrivKey &privkey, const CPubKey &vchPubKey,
bool fSkipCheck);
};
struct CExtKey {
uint8_t nDepth;
uint8_t vchFingerprint[4];
unsigned int nChild;
ChainCode chaincode;
CKey key;
friend bool operator==(const CExtKey &a, const CExtKey &b) {
return a.nDepth == b.nDepth &&
memcmp(a.vchFingerprint, b.vchFingerprint,
sizeof(vchFingerprint)) == 0 &&
a.nChild == b.nChild && a.chaincode == b.chaincode &&
a.key == b.key;
}
void Encode(uint8_t code[BIP32_EXTKEY_SIZE]) const;
void Decode(const uint8_t code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtKey &out, unsigned int nChild) const;
CExtPubKey Neuter() const;
- void SetSeed(Span<const uint8_t> seed);
+ void SetSeed(Span<const std::byte> seed);
CExtKey() = default;
};
/**
* Initialize the elliptic curve support. May not be called twice without
* calling ECC_Stop first.
*/
void ECC_Start();
/**
* Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called
* first.
*/
void ECC_Stop();
/** Check that required EC support is available at runtime. */
bool ECC_InitSanityCheck();
#endif // BITCOIN_KEY_H
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index 2422b36df..76b2b0403 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -1,145 +1,145 @@
// Copyright (c) 2013-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <clientversion.h>
#include <key.h>
#include <key_io.h>
#include <streams.h>
#include <util/strencodings.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <string>
#include <vector>
struct TestDerivation {
std::string pub;
std::string prv;
unsigned int nChild;
};
struct TestVector {
std::string strHexMaster;
std::vector<TestDerivation> vDerive;
explicit TestVector(std::string strHexMasterIn)
: strHexMaster(strHexMasterIn) {}
TestVector &operator()(std::string pub, std::string prv,
unsigned int nChild) {
vDerive.push_back(TestDerivation());
TestDerivation &der = vDerive.back();
der.pub = pub;
der.prv = prv;
der.nChild = nChild;
return *this;
}
};
// clang-format off
TestVector test1 =
TestVector("000102030405060708090a0b0c0d0e0f")
("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
0x80000000)
("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
1)
("xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
0x80000002)
("xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
2)
("xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
1000000000)
("xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy",
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
0);
TestVector test2 =
TestVector("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542")
("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
0)
("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
0xFFFFFFFF)
("xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a",
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
1)
("xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon",
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
0xFFFFFFFE)
("xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
2)
("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt",
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
0);
TestVector test3 =
TestVector("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be")
("xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13",
"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6",
0x80000000)
("xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y",
"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
0);
// clang-format on
static void RunTest(const TestVector &test) {
- std::vector<uint8_t> seed = ParseHex(test.strHexMaster);
+ std::vector<std::byte> seed{ParseHex<std::byte>(test.strHexMaster)};
CExtKey key;
CExtPubKey pubkey;
key.SetSeed(seed);
pubkey = key.Neuter();
for (const TestDerivation &derive : test.vDerive) {
uint8_t data[74];
key.Encode(data);
pubkey.Encode(data);
// Test private key
BOOST_CHECK(EncodeExtKey(key) == derive.prv);
// Ensure a base58 decoded key also matches
BOOST_CHECK(DecodeExtKey(derive.prv) == key);
// Test public key
BOOST_CHECK(EncodeExtPubKey(pubkey) == derive.pub);
// Ensure a base58 decoded pubkey also matches
BOOST_CHECK(DecodeExtPubKey(derive.pub) == pubkey);
// Derive new keys
CExtKey keyNew;
BOOST_CHECK(key.Derive(keyNew, derive.nChild));
CExtPubKey pubkeyNew = keyNew.Neuter();
if (!(derive.nChild & 0x80000000)) {
// Compare with public derivation
CExtPubKey pubkeyNew2;
BOOST_CHECK(pubkey.Derive(pubkeyNew2, derive.nChild));
BOOST_CHECK(pubkeyNew == pubkeyNew2);
}
key = keyNew;
pubkey = pubkeyNew;
}
}
BOOST_FIXTURE_TEST_SUITE(bip32_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(bip32_test1) {
RunTest(test1);
}
BOOST_AUTO_TEST_CASE(bip32_test2) {
RunTest(test2);
}
BOOST_AUTO_TEST_CASE(bip32_test3) {
RunTest(test3);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp
index 407d660a2..21d3be3f9 100644
--- a/src/test/fuzz/hex.cpp
+++ b/src/test/fuzz/hex.cpp
@@ -1,44 +1,46 @@
// Copyright (c) 2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <core_io.h>
#include <primitives/block.h>
#include <pubkey.h>
#include <rpc/util.h>
#include <uint256.h>
#include <util/strencodings.h>
#include <univalue.h>
#include <test/fuzz/fuzz.h>
#include <cassert>
#include <cstdint>
#include <string>
#include <vector>
void initialize_hex() {
static const ECCVerifyHandle verify_handle;
}
FUZZ_TARGET_INIT(hex, initialize_hex) {
const std::string random_hex_string(buffer.begin(), buffer.end());
const std::vector<uint8_t> data = ParseHex(random_hex_string);
+ const std::vector<std::byte> bytes{ParseHex<std::byte>(random_hex_string)};
+ assert(AsBytes(Span{data}) == Span{bytes});
const std::string hex_data = HexStr(data);
if (IsHex(random_hex_string)) {
assert(ToLower(random_hex_string) == hex_data);
}
(void)IsHexNumber(random_hex_string);
uint256 result;
(void)ParseHashStr(random_hex_string, result);
(void)uint256S(random_hex_string);
try {
(void)HexToPubKey(random_hex_string);
} catch (const UniValue &) {
}
CBlockHeader block_header;
(void)DecodeHexBlockHeader(block_header, random_hex_string);
CBlock block;
(void)DecodeHexBlk(block, random_hex_string);
}
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index cf79e0fb0..9d6f9d882 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -1,165 +1,165 @@
// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/data/key_io_invalid.json.h>
#include <test/data/key_io_valid.json.h>
#include <chainparams.h>
#include <key.h>
#include <key_io.h>
#include <script/script.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
extern UniValue read_json(const std::string &jsondata);
BOOST_FIXTURE_TEST_SUITE(key_io_tests, BasicTestingSetup)
// Goal: check that parsed keys match test payload
BOOST_AUTO_TEST_CASE(key_io_valid_parse) {
UniValue tests = read_json(json_tests::key_io_valid);
CKey privkey;
CTxDestination destination;
SelectParams(CBaseChainParams::MAIN);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
// Allow for extra stuff (useful for comments)
if (test.size() < 3) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::string exp_base58string = test[0].get_str();
- std::vector<uint8_t> exp_payload = ParseHex(test[1].get_str());
+ const std::vector<std::byte> exp_payload{
+ ParseHex<std::byte>(test[1].get_str())};
const UniValue &metadata = test[2].get_obj();
bool isPrivkey = metadata.find_value("isPrivkey").get_bool();
SelectParams(metadata.find_value("chain").get_str());
bool try_case_flip =
metadata.find_value("tryCaseFlip").isNull()
? false
: metadata.find_value("tryCaseFlip").get_bool();
if (isPrivkey) {
bool isCompressed = metadata.find_value("isCompressed").get_bool();
// Must be valid private key
privkey = DecodeSecret(exp_base58string);
BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest);
BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed,
"compressed mismatch:" + strTest);
- BOOST_CHECK_MESSAGE(Span<const uint8_t>{privkey} ==
- Span<const uint8_t>{exp_payload},
+ BOOST_CHECK_MESSAGE(Span{privkey} == Span{exp_payload},
"key mismatch:" + strTest);
// Private key must be invalid public key
destination = DecodeLegacyAddr(exp_base58string, Params());
BOOST_CHECK_MESSAGE(!IsValidDestination(destination),
"IsValid privkey as pubkey:" + strTest);
} else {
// Must be valid public key
destination = DecodeLegacyAddr(exp_base58string, Params());
CScript script = GetScriptForDestination(destination);
BOOST_CHECK_MESSAGE(IsValidDestination(destination),
"!IsValid:" + strTest);
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
// Try flipped case version
for (char &c : exp_base58string) {
if (c >= 'a' && c <= 'z') {
c = (c - 'a') + 'A';
} else if (c >= 'A' && c <= 'Z') {
c = (c - 'A') + 'a';
}
}
destination = DecodeLegacyAddr(exp_base58string, Params());
BOOST_CHECK_MESSAGE(IsValidDestination(destination) ==
try_case_flip,
"!IsValid case flipped:" + strTest);
if (IsValidDestination(destination)) {
script = GetScriptForDestination(destination);
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
}
// Public key must be invalid private key
privkey = DecodeSecret(exp_base58string);
BOOST_CHECK_MESSAGE(!privkey.IsValid(),
"IsValid pubkey as privkey:" + strTest);
}
}
}
// Goal: check that generated keys match test vectors
BOOST_AUTO_TEST_CASE(key_io_valid_gen) {
UniValue tests = read_json(json_tests::key_io_valid);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
// Allow for extra stuff (useful for comments)
if (test.size() < 3) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::string exp_base58string = test[0].get_str();
std::vector<uint8_t> exp_payload = ParseHex(test[1].get_str());
const UniValue &metadata = test[2].get_obj();
bool isPrivkey = metadata.find_value("isPrivkey").get_bool();
SelectParams(metadata.find_value("chain").get_str());
if (isPrivkey) {
bool isCompressed = metadata.find_value("isCompressed").get_bool();
CKey key;
key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
assert(key.IsValid());
BOOST_CHECK_MESSAGE(EncodeSecret(key) == exp_base58string,
"result mismatch: " + strTest);
} else {
CTxDestination dest;
CScript exp_script(exp_payload.begin(), exp_payload.end());
BOOST_CHECK(ExtractDestination(exp_script, dest));
std::string address = EncodeLegacyAddr(dest, Params());
BOOST_CHECK_EQUAL(address, exp_base58string);
}
}
SelectParams(CBaseChainParams::MAIN);
}
// Goal: check that base58 parsing code is robust against a variety of corrupted
// data
BOOST_AUTO_TEST_CASE(key_io_invalid) {
// Negative testcases
UniValue tests = read_json(json_tests::key_io_invalid);
CKey privkey;
CTxDestination destination;
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
// Allow for extra stuff (useful for comments)
if (test.size() < 1) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::string exp_base58string = test[0].get_str();
// must be invalid as public and as private key
for (const auto &chain :
{CBaseChainParams::MAIN, CBaseChainParams::TESTNET,
CBaseChainParams::REGTEST}) {
SelectParams(chain);
destination = DecodeLegacyAddr(exp_base58string, Params());
BOOST_CHECK_MESSAGE(!IsValidDestination(destination),
"IsValid pubkey in mainnet:" + strTest);
privkey = DecodeSecret(exp_base58string);
BOOST_CHECK_MESSAGE(!privkey.IsValid(),
"IsValid privkey in mainnet:" + strTest);
}
}
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 6fb961677..9542b014a 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1,2867 +1,2875 @@
// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
#include <clientversion.h>
#include <hash.h> // For Hash()
#include <key.h> // For CKey
#include <sync.h>
#include <test/util/logging.h>
#include <test/util/str.h>
#include <uint256.h>
#include <util/bitdeque.h>
#include <util/check.h>
#include <util/fs.h>
#include <util/fs_helpers.h>
#include <util/getuniquepath.h>
#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
#include <util/moneystr.h>
#include <util/spanparsing.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/time.h>
#include <util/vector.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <univalue.h>
#include <boost/test/unit_test.hpp>
#include <array>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <optional>
#include <utility>
#ifndef WIN32
#include <csignal>
#include <sys/types.h>
#include <sys/wait.h>
#endif
#include <thread>
#include <vector>
using namespace std::literals;
static const std::string STRING_WITH_EMBEDDED_NULL_CHAR{"1"s
"\0"
"1"s};
/* defined in logging.cpp */
namespace BCLog {
std::string LogEscapeMessage(const std::string &str);
}
BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(util_datadir) {
// Use local args variable instead of m_args to avoid making assumptions
// about test setup
ArgsManager args;
args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
const fs::path dd_norm = args.GetDataDirBase();
args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/./");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
}
namespace {
class NoCopyOrMove {
public:
int i;
explicit NoCopyOrMove(int i) : i{i} {}
NoCopyOrMove() = delete;
NoCopyOrMove(const NoCopyOrMove &) = delete;
NoCopyOrMove(NoCopyOrMove &&) = delete;
NoCopyOrMove &operator=(const NoCopyOrMove &) = delete;
NoCopyOrMove &operator=(NoCopyOrMove &&) = delete;
operator bool() const { return i != 0; }
int get_ip1() { return i + 1; }
bool test() {
// Check that Assume can be used within a lambda and still call methods
[&]() { Assume(get_ip1()); }();
return Assume(get_ip1() != 5);
}
};
} // namespace
BOOST_AUTO_TEST_CASE(util_check) {
// Check that Assert can forward
const std::unique_ptr<int> p_two = Assert(std::make_unique<int>(2));
// Check that Assert works on lvalues and rvalues
const int two = *Assert(p_two);
Assert(two == 2);
Assert(true);
// Check that Assume can be used as unary expression
const bool result{Assume(two == 2)};
Assert(result);
// Check that Assert doesn't require copy/move
NoCopyOrMove x{9};
Assert(x).i += 3;
Assert(x).test();
// Check nested Asserts
BOOST_CHECK_EQUAL(Assert((Assert(x).test() ? 3 : 0)), 3);
// Check -Wdangling-gsl does not trigger when copying the int. (It would
// trigger on "const int&")
const int nine{*Assert(std::optional<int>{9})};
BOOST_CHECK_EQUAL(9, nine);
}
BOOST_AUTO_TEST_CASE(util_criticalsection) {
RecursiveMutex cs;
do {
LOCK(cs);
break;
BOOST_ERROR("break was swallowed!");
} while (0);
do {
TRY_LOCK(cs, lockTest);
if (lockTest) {
// Needed to suppress "Test case [...] did not check any assertions"
BOOST_CHECK(true);
break;
}
BOOST_ERROR("break was swallowed!");
} while (0);
}
static const uint8_t ParseHex_expected[65] = {
0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, 0x19, 0x67,
0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0,
0x39, 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6,
0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, 0xf3, 0x55, 0x04,
0xe5, 0x1e, 0xc1, 0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b,
0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f};
-BOOST_AUTO_TEST_CASE(util_ParseHex) {
+BOOST_AUTO_TEST_CASE(parse_hex) {
std::vector<uint8_t> result;
std::vector<uint8_t> expected(
ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected));
// Basic test vector
result = ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0"
"ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d"
"578a4c702b6bf11d5f");
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
expected.begin(), expected.end());
// Spaces between bytes must be supported
result = ParseHex("12 34 56 78");
BOOST_CHECK(result.size() == 4 && result[0] == 0x12 && result[1] == 0x34 &&
result[2] == 0x56 && result[3] == 0x78);
// Leading space must be supported (used in BerkeleyEnvironment::Salvage)
result = ParseHex(" 89 34 56 78");
BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 &&
result[2] == 0x56 && result[3] == 0x78);
+ // Embedded null is treated as end
+ const std::string with_embedded_null{" 11 "s
+ " \0 "
+ " 22 "s};
+ BOOST_CHECK_EQUAL(with_embedded_null.size(), 11);
+ result = ParseHex(with_embedded_null);
+ BOOST_CHECK(result.size() == 1 && result[0] == 0x11);
+
// Stop parsing at invalid value
result = ParseHex("1234 invalid 1234");
BOOST_CHECK(result.size() == 2 && result[0] == 0x12 && result[1] == 0x34);
}
BOOST_AUTO_TEST_CASE(util_HexStr) {
BOOST_CHECK_EQUAL(HexStr(ParseHex_expected),
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0"
"ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d"
"578a4c702b6bf11d5f");
BOOST_CHECK_EQUAL(HexStr(Span{ParseHex_expected}.last(0)), "");
BOOST_CHECK_EQUAL(HexStr(Span{ParseHex_expected}.first(0)), "");
{
const std::vector<char> in_s{ParseHex_expected, ParseHex_expected + 5};
const Span<const uint8_t> in_u{MakeUCharSpan(in_s)};
const Span<const std::byte> in_b{MakeByteSpan(in_s)};
const std::string out_exp{"04678afdb0"};
BOOST_CHECK_EQUAL(HexStr(in_u), out_exp);
BOOST_CHECK_EQUAL(HexStr(in_s), out_exp);
BOOST_CHECK_EQUAL(HexStr(in_b), out_exp);
}
}
BOOST_AUTO_TEST_CASE(span_write_bytes) {
std::array<uint8_t, 2> mut_arr{{0xaa, 0xbb}};
const auto mut_bytes{MakeWritableByteSpan(mut_arr)};
mut_bytes[1] = std::byte{0x11};
BOOST_CHECK_EQUAL(mut_arr.at(0), 0xaa);
BOOST_CHECK_EQUAL(mut_arr.at(1), 0x11);
}
BOOST_AUTO_TEST_CASE(util_Join) {
// Normal version
BOOST_CHECK_EQUAL(Join({}, ", "), "");
BOOST_CHECK_EQUAL(Join({"foo"}, ", "), "foo");
BOOST_CHECK_EQUAL(Join({"foo", "bar"}, ", "), "foo, bar");
// Version with unary operator
const auto op_upper = [](const std::string &s) { return ToUpper(s); };
BOOST_CHECK_EQUAL(Join<std::string>({}, ", ", op_upper), "");
BOOST_CHECK_EQUAL(Join<std::string>({"foo"}, ", ", op_upper), "FOO");
BOOST_CHECK_EQUAL(Join<std::string>({"foo", "bar"}, ", ", op_upper),
"FOO, BAR");
}
BOOST_AUTO_TEST_CASE(util_TrimString) {
BOOST_CHECK_EQUAL(TrimString(" foo bar "), "foo bar");
BOOST_CHECK_EQUAL(TrimStringView("\t \n \n \f\n\r\t\v\tfoo \n "
"\f\n\r\t\v\tbar\t \n \f\n\r\t\v\t\n "),
"foo \n \f\n\r\t\v\tbar");
BOOST_CHECK_EQUAL(TrimString("\t \n foo \n\tbar\t \n "), "foo \n\tbar");
BOOST_CHECK_EQUAL(TrimStringView("\t \n foo \n\tbar\t \n ", "fobar"),
"\t \n foo \n\tbar\t \n ");
BOOST_CHECK_EQUAL(TrimString("foo bar"), "foo bar");
BOOST_CHECK_EQUAL(TrimStringView("foo bar", "fobar"), " ");
BOOST_CHECK_EQUAL(TrimString(std::string("\0 foo \0 ", 8)),
std::string("\0 foo \0", 7));
BOOST_CHECK_EQUAL(TrimStringView(std::string(" foo ", 5)),
std::string("foo", 3));
BOOST_CHECK_EQUAL(TrimString(std::string("\t\t\0\0\n\n", 6)),
std::string("\0\0", 2));
BOOST_CHECK_EQUAL(
TrimStringView(std::string("\x05\x04\x03\x02\x01\x00", 6)),
std::string("\x05\x04\x03\x02\x01\x00", 6));
BOOST_CHECK_EQUAL(TrimString(std::string("\x05\x04\x03\x02\x01\x00", 6),
std::string("\x05\x04\x03\x02\x01", 5)),
std::string("\0", 1));
BOOST_CHECK_EQUAL(
TrimStringView(std::string("\x05\x04\x03\x02\x01\x00", 6),
std::string("\x05\x04\x03\x02\x01\x00", 6)),
"");
}
BOOST_AUTO_TEST_CASE(util_ReplaceAll) {
const std::string original("A test \"%s\" string '%s'.");
auto test_replaceall = [&original](const std::string &search,
const std::string &substitute,
const std::string &expected) {
auto test = original;
ReplaceAll(test, search, substitute);
BOOST_CHECK_EQUAL(test, expected);
};
test_replaceall("", "foo", original);
test_replaceall(original, "foo", "foo");
test_replaceall("%s", "foo", "A test \"foo\" string 'foo'.");
test_replaceall("\"", "foo", "A test foo%sfoo string '%s'.");
test_replaceall("'", "foo", "A test \"%s\" string foo%sfoo.");
}
BOOST_AUTO_TEST_CASE(util_FormatParseISO8601DateTime) {
BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777),
"2011-09-30T23:36:17Z");
BOOST_CHECK_EQUAL(FormatISO8601DateTime(0), "1970-01-01T00:00:00Z");
BOOST_CHECK_EQUAL(ParseISO8601DateTime("1970-01-01T00:00:00Z"), 0);
BOOST_CHECK_EQUAL(ParseISO8601DateTime("1960-01-01T00:00:00Z"), 0);
BOOST_CHECK_EQUAL(ParseISO8601DateTime("2011-09-30T23:36:17Z"), 1317425777);
}
BOOST_AUTO_TEST_CASE(util_FormatISO8601Date) {
BOOST_CHECK_EQUAL(FormatISO8601Date(1317425777), "2011-09-30");
}
struct TestArgsManager : public ArgsManager {
TestArgsManager() { m_network_only_args.clear(); }
void ReadConfigString(const std::string str_config) {
std::istringstream streamConfig(str_config);
{
LOCK(cs_args);
m_settings.ro_config.clear();
m_config_sections.clear();
}
std::string error;
BOOST_REQUIRE(ReadConfigStream(streamConfig, "", error));
}
void SetNetworkOnlyArg(const std::string arg) {
LOCK(cs_args);
m_network_only_args.insert(arg);
}
void
SetupArgs(const std::vector<std::pair<std::string, unsigned int>> &args) {
for (const auto &arg : args) {
AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
}
}
using ArgsManager::cs_args;
using ArgsManager::GetSetting;
using ArgsManager::GetSettingsList;
using ArgsManager::m_network;
using ArgsManager::m_settings;
using ArgsManager::ReadConfigStream;
};
//! Test GetSetting and GetArg type coercion, negation, and default value
//! handling.
class CheckValueTest : public TestChain100Setup {
public:
struct Expect {
util::SettingsValue setting;
bool default_string = false;
bool default_int = false;
bool default_bool = false;
const char *string_value = nullptr;
std::optional<int64_t> int_value;
std::optional<bool> bool_value;
std::optional<std::vector<std::string>> list_value;
const char *error = nullptr;
explicit Expect(util::SettingsValue s) : setting(std::move(s)) {}
Expect &DefaultString() {
default_string = true;
return *this;
}
Expect &DefaultInt() {
default_int = true;
return *this;
}
Expect &DefaultBool() {
default_bool = true;
return *this;
}
Expect &String(const char *s) {
string_value = s;
return *this;
}
Expect &Int(int64_t i) {
int_value = i;
return *this;
}
Expect &Bool(bool b) {
bool_value = b;
return *this;
}
Expect &List(std::vector<std::string> m) {
list_value = std::move(m);
return *this;
}
Expect &Error(const char *e) {
error = e;
return *this;
}
};
void CheckValue(unsigned int flags, const char *arg, const Expect &expect) {
TestArgsManager test;
test.SetupArgs({{"-value", flags}});
const char *argv[] = {"ignored", arg};
std::string error;
bool success = test.ParseParameters(arg ? 2 : 1, (char **)argv, error);
BOOST_CHECK_EQUAL(test.GetSetting("-value").write(),
expect.setting.write());
auto settings_list = test.GetSettingsList("-value");
if (expect.setting.isNull() || expect.setting.isFalse()) {
BOOST_CHECK_EQUAL(settings_list.size(), 0U);
} else {
BOOST_CHECK_EQUAL(settings_list.size(), 1U);
BOOST_CHECK_EQUAL(settings_list[0].write(), expect.setting.write());
}
if (expect.error) {
BOOST_CHECK(!success);
BOOST_CHECK_NE(error.find(expect.error), std::string::npos);
} else {
BOOST_CHECK(success);
BOOST_CHECK_EQUAL(error, "");
}
if (expect.default_string) {
BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), "zzzzz");
} else if (expect.string_value) {
BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"),
expect.string_value);
} else {
BOOST_CHECK(!success);
}
if (expect.default_int) {
BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), 99999);
} else if (expect.int_value) {
BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999),
*expect.int_value);
} else {
BOOST_CHECK(!success);
}
if (expect.default_bool) {
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), false);
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), true);
} else if (expect.bool_value) {
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false),
*expect.bool_value);
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true),
*expect.bool_value);
} else {
BOOST_CHECK(!success);
}
if (expect.list_value) {
auto l = test.GetArgs("-value");
BOOST_CHECK_EQUAL_COLLECTIONS(l.begin(), l.end(),
expect.list_value->begin(),
expect.list_value->end());
} else {
BOOST_CHECK(!success);
}
}
};
BOOST_FIXTURE_TEST_CASE(util_CheckValue, CheckValueTest) {
using M = ArgsManager;
CheckValue(M::ALLOW_ANY, nullptr,
Expect{{}}.DefaultString().DefaultInt().DefaultBool().List({}));
CheckValue(M::ALLOW_ANY, "-novalue",
Expect{false}.String("0").Int(0).Bool(false).List({}));
CheckValue(M::ALLOW_ANY, "-novalue=",
Expect{false}.String("0").Int(0).Bool(false).List({}));
CheckValue(M::ALLOW_ANY, "-novalue=0",
Expect{true}.String("1").Int(1).Bool(true).List({"1"}));
CheckValue(M::ALLOW_ANY, "-novalue=1",
Expect{false}.String("0").Int(0).Bool(false).List({}));
CheckValue(M::ALLOW_ANY, "-novalue=2",
Expect{false}.String("0").Int(0).Bool(false).List({}));
CheckValue(M::ALLOW_ANY, "-novalue=abc",
Expect{true}.String("1").Int(1).Bool(true).List({"1"}));
CheckValue(M::ALLOW_ANY, "-value",
Expect{""}.String("").Int(0).Bool(true).List({""}));
CheckValue(M::ALLOW_ANY,
"-value=", Expect{""}.String("").Int(0).Bool(true).List({""}));
CheckValue(M::ALLOW_ANY, "-value=0",
Expect{"0"}.String("0").Int(0).Bool(false).List({"0"}));
CheckValue(M::ALLOW_ANY, "-value=1",
Expect{"1"}.String("1").Int(1).Bool(true).List({"1"}));
CheckValue(M::ALLOW_ANY, "-value=2",
Expect{"2"}.String("2").Int(2).Bool(true).List({"2"}));
CheckValue(M::ALLOW_ANY, "-value=abc",
Expect{"abc"}.String("abc").Int(0).Bool(false).List({"abc"}));
}
BOOST_AUTO_TEST_CASE(util_ParseParameters) {
TestArgsManager testArgs;
const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY);
const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY);
const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY);
const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY);
const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument",
"-ccc=multiple", "f", "-d=e"};
std::string error;
LOCK(testArgs.cs_args);
testArgs.SetupArgs({a, b, ccc, d});
BOOST_CHECK(testArgs.ParseParameters(0, (char **)argv_test, error));
BOOST_CHECK(testArgs.m_settings.command_line_options.empty() &&
testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.ParseParameters(1, (char **)argv_test, error));
BOOST_CHECK(testArgs.m_settings.command_line_options.empty() &&
testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.ParseParameters(7, (char **)argv_test, error));
// expectation: -ignored is ignored (program name argument),
// -a, -b and -ccc end up in map, -d ignored because it is after
// a non-option argument (non-GNU option parsing)
BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 3 &&
testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") &&
testArgs.IsArgSet("-ccc") && !testArgs.IsArgSet("f") &&
!testArgs.IsArgSet("-d"));
BOOST_CHECK(testArgs.m_settings.command_line_options.count("a") &&
testArgs.m_settings.command_line_options.count("b") &&
testArgs.m_settings.command_line_options.count("ccc") &&
!testArgs.m_settings.command_line_options.count("f") &&
!testArgs.m_settings.command_line_options.count("d"));
BOOST_CHECK(testArgs.m_settings.command_line_options["a"].size() == 1);
BOOST_CHECK(
testArgs.m_settings.command_line_options["a"].front().get_str() == "");
BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].size() == 2);
BOOST_CHECK(
testArgs.m_settings.command_line_options["ccc"].front().get_str() ==
"argument");
BOOST_CHECK(
testArgs.m_settings.command_line_options["ccc"].back().get_str() ==
"multiple");
BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2);
}
BOOST_AUTO_TEST_CASE(util_ParseKeyValue) {
{
std::string key = "badarg";
std::string value;
BOOST_CHECK(!ParseKeyValue(key, value));
}
{
std::string key = "badarg=v";
std::string value;
BOOST_CHECK(!ParseKeyValue(key, value));
}
{
std::string key = "-a";
std::string value;
BOOST_CHECK(ParseKeyValue(key, value));
BOOST_CHECK_EQUAL(key, "-a");
BOOST_CHECK_EQUAL(value, "");
}
{
std::string key = "-a=1";
std::string value;
BOOST_CHECK(ParseKeyValue(key, value));
BOOST_CHECK_EQUAL(key, "-a");
BOOST_CHECK_EQUAL(value, "1");
}
{
std::string key = "--b";
std::string value;
BOOST_CHECK(ParseKeyValue(key, value));
BOOST_CHECK_EQUAL(key, "-b");
BOOST_CHECK_EQUAL(value, "");
}
{
std::string key = "--b=abc";
std::string value;
BOOST_CHECK(ParseKeyValue(key, value));
BOOST_CHECK_EQUAL(key, "-b");
BOOST_CHECK_EQUAL(value, "abc");
}
}
BOOST_AUTO_TEST_CASE(util_ParseInvalidParameters) {
TestArgsManager test;
test.SetupArgs({{"-registered", ArgsManager::ALLOW_ANY}});
const char *argv[] = {"ignored", "-registered"};
std::string error;
BOOST_CHECK(test.ParseParameters(2, (char **)argv, error));
BOOST_CHECK_EQUAL(error, "");
argv[1] = "-unregistered";
BOOST_CHECK(!test.ParseParameters(2, (char **)argv, error));
BOOST_CHECK_EQUAL(error, "Invalid parameter -unregistered");
// Make sure registered parameters prefixed with a chain name trigger
// errors. (Previously, they were accepted and ignored.)
argv[1] = "-test.registered";
BOOST_CHECK(!test.ParseParameters(2, (char **)argv, error));
BOOST_CHECK_EQUAL(error, "Invalid parameter -test.registered");
}
static void TestParse(const std::string &str, bool expected_bool,
int64_t expected_int) {
TestArgsManager test;
test.SetupArgs({{"-value", ArgsManager::ALLOW_ANY}});
std::string arg = "-value=" + str;
const char *argv[] = {"ignored", arg.c_str()};
std::string error;
BOOST_CHECK(test.ParseParameters(2, (char **)argv, error));
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), expected_bool);
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), expected_bool);
BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99998), expected_int);
BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), expected_int);
}
// Test bool and int parsing.
BOOST_AUTO_TEST_CASE(util_ArgParsing) {
// Some of these cases could be ambiguous or surprising to users, and might
// be worth triggering errors or warnings in the future. But for now basic
// test coverage is useful to avoid breaking backwards compatibility
// unintentionally.
TestParse("", true, 0);
TestParse(" ", false, 0);
TestParse("0", false, 0);
TestParse("0 ", false, 0);
TestParse(" 0", false, 0);
TestParse("+0", false, 0);
TestParse("-0", false, 0);
TestParse("5", true, 5);
TestParse("5 ", true, 5);
TestParse(" 5", true, 5);
TestParse("+5", true, 5);
TestParse("-5", true, -5);
TestParse("0 5", false, 0);
TestParse("5 0", true, 5);
TestParse("050", true, 50);
TestParse("0.", false, 0);
TestParse("5.", true, 5);
TestParse("0.0", false, 0);
TestParse("0.5", false, 0);
TestParse("5.0", true, 5);
TestParse("5.5", true, 5);
TestParse("x", false, 0);
TestParse("x0", false, 0);
TestParse("x5", false, 0);
TestParse("0x", false, 0);
TestParse("5x", true, 5);
TestParse("0x5", false, 0);
TestParse("false", false, 0);
TestParse("true", false, 0);
TestParse("yes", false, 0);
TestParse("no", false, 0);
}
BOOST_AUTO_TEST_CASE(util_GetBoolArg) {
TestArgsManager testArgs;
const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY);
const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY);
const auto c = std::make_pair("-c", ArgsManager::ALLOW_ANY);
const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY);
const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY);
const auto f = std::make_pair("-f", ArgsManager::ALLOW_ANY);
const char *argv_test[] = {"ignored", "-a", "-nob", "-c=0",
"-d=1", "-e=false", "-f=true"};
std::string error;
LOCK(testArgs.cs_args);
testArgs.SetupArgs({a, b, c, d, e, f});
BOOST_CHECK(testArgs.ParseParameters(7, (char **)argv_test, error));
// Each letter should be set.
for (const char opt : "abcdef") {
BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt);
}
// Nothing else should be in the map
BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 6 &&
testArgs.m_settings.ro_config.empty());
// The -no prefix should get stripped on the way in.
BOOST_CHECK(!testArgs.IsArgSet("-nob"));
// The -b option is flagged as negated, and nothing else is
BOOST_CHECK(testArgs.IsArgNegated("-b"));
BOOST_CHECK(!testArgs.IsArgNegated("-a"));
// Check expected values.
BOOST_CHECK(testArgs.GetBoolArg("-a", false) == true);
BOOST_CHECK(testArgs.GetBoolArg("-b", true) == false);
BOOST_CHECK(testArgs.GetBoolArg("-c", true) == false);
BOOST_CHECK(testArgs.GetBoolArg("-d", false) == true);
BOOST_CHECK(testArgs.GetBoolArg("-e", true) == false);
BOOST_CHECK(testArgs.GetBoolArg("-f", true) == false);
}
BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) {
// Test some awful edge cases that hopefully no user will ever exercise.
TestArgsManager testArgs;
// Params test
const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"};
testArgs.SetupArgs({foo, bar});
std::string error;
BOOST_CHECK(testArgs.ParseParameters(4, (char **)argv_test, error));
// This was passed twice, second one overrides the negative setting.
BOOST_CHECK(!testArgs.IsArgNegated("-foo"));
BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "");
// A double negative is a positive, and not marked as negated.
BOOST_CHECK(!testArgs.IsArgNegated("-bar"));
BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1");
// Config test
const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n";
BOOST_CHECK(testArgs.ParseParameters(1, (char **)argv_test, error));
testArgs.ReadConfigString(conf_test);
// This was passed twice, second one overrides the negative setting,
// and the value.
BOOST_CHECK(!testArgs.IsArgNegated("-foo"));
BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "1");
// A double negative is a positive, and does not count as negated.
BOOST_CHECK(!testArgs.IsArgNegated("-bar"));
BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1");
// Combined test
const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"};
const char *combo_test_conf = "foo=1\nnobar=1\n";
BOOST_CHECK(testArgs.ParseParameters(3, (char **)combo_test_args, error));
testArgs.ReadConfigString(combo_test_conf);
// Command line overrides, but doesn't erase old setting
BOOST_CHECK(testArgs.IsArgNegated("-foo"));
BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0");
BOOST_CHECK(testArgs.GetArgs("-foo").size() == 0);
// Command line overrides, but doesn't erase old setting
BOOST_CHECK(!testArgs.IsArgNegated("-bar"));
BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "");
BOOST_CHECK(testArgs.GetArgs("-bar").size() == 1 &&
testArgs.GetArgs("-bar").front() == "");
}
BOOST_AUTO_TEST_CASE(util_ReadConfigStream) {
const char *str_config = "a=\n"
"b=1\n"
"ccc=argument\n"
"ccc=multiple\n"
"d=e\n"
"nofff=1\n"
"noggg=0\n"
"h=1\n"
"noh=1\n"
"noi=1\n"
"i=1\n"
"sec1.ccc=extend1\n"
"\n"
"[sec1]\n"
"ccc=extend2\n"
"d=eee\n"
"h=1\n"
"[sec2]\n"
"ccc=extend3\n"
"iii=2\n";
TestArgsManager test_args;
LOCK(test_args.cs_args);
const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY);
const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY);
const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY);
const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY);
const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY);
const auto fff = std::make_pair("-fff", ArgsManager::ALLOW_ANY);
const auto ggg = std::make_pair("-ggg", ArgsManager::ALLOW_ANY);
const auto h = std::make_pair("-h", ArgsManager::ALLOW_ANY);
const auto i = std::make_pair("-i", ArgsManager::ALLOW_ANY);
const auto iii = std::make_pair("-iii", ArgsManager::ALLOW_ANY);
test_args.SetupArgs({a, b, ccc, d, e, fff, ggg, h, i, iii});
test_args.ReadConfigString(str_config);
// expectation: a, b, ccc, d, fff, ggg, h, i end up in map
// so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii
BOOST_CHECK(test_args.m_settings.command_line_options.empty());
BOOST_CHECK(test_args.m_settings.ro_config.size() == 3);
BOOST_CHECK(test_args.m_settings.ro_config[""].size() == 8);
BOOST_CHECK(test_args.m_settings.ro_config["sec1"].size() == 3);
BOOST_CHECK(test_args.m_settings.ro_config["sec2"].size() == 2);
BOOST_CHECK(test_args.m_settings.ro_config[""].count("a") &&
test_args.m_settings.ro_config[""].count("b") &&
test_args.m_settings.ro_config[""].count("ccc") &&
test_args.m_settings.ro_config[""].count("d") &&
test_args.m_settings.ro_config[""].count("fff") &&
test_args.m_settings.ro_config[""].count("ggg") &&
test_args.m_settings.ro_config[""].count("h") &&
test_args.m_settings.ro_config[""].count("i"));
BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc") &&
test_args.m_settings.ro_config["sec1"].count("h") &&
test_args.m_settings.ro_config["sec2"].count("ccc") &&
test_args.m_settings.ro_config["sec2"].count("iii"));
BOOST_CHECK(test_args.IsArgSet("-a") && test_args.IsArgSet("-b") &&
test_args.IsArgSet("-ccc") && test_args.IsArgSet("-d") &&
test_args.IsArgSet("-fff") && test_args.IsArgSet("-ggg") &&
test_args.IsArgSet("-h") && test_args.IsArgSet("-i") &&
!test_args.IsArgSet("-zzz") && !test_args.IsArgSet("-iii"));
BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" &&
test_args.GetArg("-b", "xxx") == "1" &&
test_args.GetArg("-ccc", "xxx") == "argument" &&
test_args.GetArg("-d", "xxx") == "e" &&
test_args.GetArg("-fff", "xxx") == "0" &&
test_args.GetArg("-ggg", "xxx") == "1" &&
test_args.GetArg("-h", "xxx") == "0" &&
test_args.GetArg("-i", "xxx") == "1" &&
test_args.GetArg("-zzz", "xxx") == "xxx" &&
test_args.GetArg("-iii", "xxx") == "xxx");
for (const bool def : {false, true}) {
BOOST_CHECK(test_args.GetBoolArg("-a", def) &&
test_args.GetBoolArg("-b", def) &&
!test_args.GetBoolArg("-ccc", def) &&
!test_args.GetBoolArg("-d", def) &&
!test_args.GetBoolArg("-fff", def) &&
test_args.GetBoolArg("-ggg", def) &&
!test_args.GetBoolArg("-h", def) &&
test_args.GetBoolArg("-i", def) &&
test_args.GetBoolArg("-zzz", def) == def &&
test_args.GetBoolArg("-iii", def) == def);
}
BOOST_CHECK(test_args.GetArgs("-a").size() == 1 &&
test_args.GetArgs("-a").front() == "");
BOOST_CHECK(test_args.GetArgs("-b").size() == 1 &&
test_args.GetArgs("-b").front() == "1");
BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2 &&
test_args.GetArgs("-ccc").front() == "argument" &&
test_args.GetArgs("-ccc").back() == "multiple");
BOOST_CHECK(test_args.GetArgs("-fff").size() == 0);
BOOST_CHECK(test_args.GetArgs("-nofff").size() == 0);
BOOST_CHECK(test_args.GetArgs("-ggg").size() == 1 &&
test_args.GetArgs("-ggg").front() == "1");
BOOST_CHECK(test_args.GetArgs("-noggg").size() == 0);
BOOST_CHECK(test_args.GetArgs("-h").size() == 0);
BOOST_CHECK(test_args.GetArgs("-noh").size() == 0);
BOOST_CHECK(test_args.GetArgs("-i").size() == 1 &&
test_args.GetArgs("-i").front() == "1");
BOOST_CHECK(test_args.GetArgs("-noi").size() == 0);
BOOST_CHECK(test_args.GetArgs("-zzz").size() == 0);
BOOST_CHECK(!test_args.IsArgNegated("-a"));
BOOST_CHECK(!test_args.IsArgNegated("-b"));
BOOST_CHECK(!test_args.IsArgNegated("-ccc"));
BOOST_CHECK(!test_args.IsArgNegated("-d"));
BOOST_CHECK(test_args.IsArgNegated("-fff"));
BOOST_CHECK(!test_args.IsArgNegated("-ggg"));
// last setting takes precedence
BOOST_CHECK(test_args.IsArgNegated("-h"));
// last setting takes precedence
BOOST_CHECK(!test_args.IsArgNegated("-i"));
BOOST_CHECK(!test_args.IsArgNegated("-zzz"));
// Test sections work
test_args.SelectConfigNetwork("sec1");
// same as original
BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" &&
test_args.GetArg("-b", "xxx") == "1" &&
test_args.GetArg("-fff", "xxx") == "0" &&
test_args.GetArg("-ggg", "xxx") == "1" &&
test_args.GetArg("-zzz", "xxx") == "xxx" &&
test_args.GetArg("-iii", "xxx") == "xxx");
// d is overridden
BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee");
// section-specific setting
BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1");
// section takes priority for multiple values
BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend1");
// check multiple values works
const std::vector<std::string> sec1_ccc_expected = {"extend1", "extend2",
"argument", "multiple"};
const auto &sec1_ccc_res = test_args.GetArgs("-ccc");
BOOST_CHECK_EQUAL_COLLECTIONS(sec1_ccc_res.begin(), sec1_ccc_res.end(),
sec1_ccc_expected.begin(),
sec1_ccc_expected.end());
test_args.SelectConfigNetwork("sec2");
// same as original
BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" &&
test_args.GetArg("-b", "xxx") == "1" &&
test_args.GetArg("-d", "xxx") == "e" &&
test_args.GetArg("-fff", "xxx") == "0" &&
test_args.GetArg("-ggg", "xxx") == "1" &&
test_args.GetArg("-zzz", "xxx") == "xxx" &&
test_args.GetArg("-h", "xxx") == "0");
// section-specific setting
BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2");
// section takes priority for multiple values
BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend3");
// check multiple values works
const std::vector<std::string> sec2_ccc_expected = {"extend3", "argument",
"multiple"};
const auto &sec2_ccc_res = test_args.GetArgs("-ccc");
BOOST_CHECK_EQUAL_COLLECTIONS(sec2_ccc_res.begin(), sec2_ccc_res.end(),
sec2_ccc_expected.begin(),
sec2_ccc_expected.end());
// Test section only options
test_args.SetNetworkOnlyArg("-d");
test_args.SetNetworkOnlyArg("-ccc");
test_args.SetNetworkOnlyArg("-h");
test_args.SelectConfigNetwork(CBaseChainParams::MAIN);
BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e");
BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2);
BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0");
test_args.SelectConfigNetwork("sec1");
BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee");
BOOST_CHECK(test_args.GetArgs("-d").size() == 1);
BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2);
BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1");
test_args.SelectConfigNetwork("sec2");
BOOST_CHECK(test_args.GetArg("-d", "xxx") == "xxx");
BOOST_CHECK(test_args.GetArgs("-d").size() == 0);
BOOST_CHECK(test_args.GetArgs("-ccc").size() == 1);
BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0");
}
BOOST_AUTO_TEST_CASE(util_GetArg) {
TestArgsManager testArgs;
LOCK(testArgs.cs_args);
testArgs.m_settings.command_line_options.clear();
testArgs.m_settings.command_line_options["strtest1"] = {"string..."};
// strtest2 undefined on purpose
testArgs.m_settings.command_line_options["inttest1"] = {"12345"};
testArgs.m_settings.command_line_options["inttest2"] = {
"81985529216486895"};
// inttest3 undefined on purpose
testArgs.m_settings.command_line_options["booltest1"] = {""};
// booltest2 undefined on purpose
testArgs.m_settings.command_line_options["booltest3"] = {"0"};
testArgs.m_settings.command_line_options["booltest4"] = {"1"};
// priorities
testArgs.m_settings.command_line_options["pritest1"] = {"a", "b"};
testArgs.m_settings.ro_config[""]["pritest2"] = {"a", "b"};
testArgs.m_settings.command_line_options["pritest3"] = {"a"};
testArgs.m_settings.ro_config[""]["pritest3"] = {"b"};
testArgs.m_settings.command_line_options["pritest4"] = {"a", "b"};
testArgs.m_settings.ro_config[""]["pritest4"] = {"c", "d"};
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest1", -1), 12345);
BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest2", -1), 81985529216486895LL);
BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest3", -1), -1);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), true);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest4", false), true);
BOOST_CHECK_EQUAL(testArgs.GetArg("pritest1", "default"), "b");
BOOST_CHECK_EQUAL(testArgs.GetArg("pritest2", "default"), "a");
BOOST_CHECK_EQUAL(testArgs.GetArg("pritest3", "default"), "a");
BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b");
}
BOOST_AUTO_TEST_CASE(util_ClearForcedArg) {
TestArgsManager testArgs;
LOCK(testArgs.cs_args);
// Clear command line arg
testArgs.m_settings.command_line_options["cmdarg"] = {"cmdval"};
BOOST_CHECK_EQUAL(testArgs.GetArg("cmdarg", "default"), "cmdval");
testArgs.ClearForcedArg("cmdarg");
BOOST_CHECK_EQUAL(testArgs.GetArg("cmdarg", "default"), "cmdval");
// Clear config arg
testArgs.m_settings.ro_config[""]["configarg"] = {"configval"};
BOOST_CHECK_EQUAL(testArgs.GetArg("configarg", "default"), "configval");
testArgs.ClearForcedArg("configarg");
BOOST_CHECK_EQUAL(testArgs.GetArg("configarg", "default"), "configval");
// Clear forced arg
testArgs.m_settings.forced_settings["forcedarg"] = {"forcedval"};
BOOST_CHECK_EQUAL(testArgs.GetArg("forcedarg", "default"), "forcedval");
testArgs.ClearForcedArg("forcedarg");
BOOST_CHECK_EQUAL(testArgs.GetArg("forcedarg", "default"), "default");
}
BOOST_AUTO_TEST_CASE(util_SetArg) {
TestArgsManager testArgs;
// SoftSetArg
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "default");
BOOST_CHECK_EQUAL(testArgs.SoftSetArg("strtest1", "string..."), true);
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest1").size(), 1);
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest1").front(), "string...");
BOOST_CHECK_EQUAL(testArgs.SoftSetArg("strtest1", "...gnirts"), false);
testArgs.ClearForcedArg("strtest1");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "default");
BOOST_CHECK_EQUAL(testArgs.SoftSetArg("strtest1", "...gnirts"), true);
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "...gnirts");
// SoftSetBoolArg
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), false);
BOOST_CHECK_EQUAL(testArgs.SoftSetBoolArg("booltest1", true), true);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), true);
BOOST_CHECK_EQUAL(testArgs.SoftSetBoolArg("booltest1", false), false);
testArgs.ClearForcedArg("booltest1");
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", true), true);
BOOST_CHECK_EQUAL(testArgs.SoftSetBoolArg("booltest1", false), true);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", true), false);
// ForceSetArg
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
testArgs.ForceSetArg("strtest2", "string...");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "string...");
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").size(), 1);
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").front(), "string...");
testArgs.ForceSetArg("strtest2", "...gnirts");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "...gnirts");
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").size(), 1);
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").front(), "...gnirts");
// ForceSetMultiArg
testArgs.ForceSetMultiArg("strtest2", {"string...", "...gnirts"});
BOOST_CHECK_THROW(testArgs.GetArg("strtest2", "default"),
std::runtime_error);
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").size(), 2);
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").front(), "string...");
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").back(), "...gnirts");
testArgs.ClearForcedArg("strtest2");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").size(), 0);
// If there are multi args, ForceSetArg should erase them
testArgs.ForceSetMultiArg("strtest2", {"string..."});
BOOST_CHECK_THROW(testArgs.GetArg("strtest2", "default"),
std::runtime_error);
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").size(), 1);
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").front(), "string...");
testArgs.ForceSetArg("strtest2", "...gnirts");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "...gnirts");
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").size(), 1);
BOOST_CHECK_EQUAL(testArgs.GetArgs("strtest2").front(), "...gnirts");
}
BOOST_AUTO_TEST_CASE(util_GetChainName) {
TestArgsManager test_args;
const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_ANY);
const auto regtest = std::make_pair("-regtest", ArgsManager::ALLOW_ANY);
test_args.SetupArgs({testnet, regtest});
const char *argv_testnet[] = {"cmd", "-testnet"};
const char *argv_regtest[] = {"cmd", "-regtest"};
const char *argv_test_no_reg[] = {"cmd", "-testnet", "-noregtest"};
const char *argv_both[] = {"cmd", "-testnet", "-regtest"};
// equivalent to "-testnet"
// regtest in testnet section is ignored
const char *testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1";
std::string error;
BOOST_CHECK(test_args.ParseParameters(0, (char **)argv_testnet, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "main");
BOOST_CHECK(test_args.ParseParameters(2, (char **)argv_testnet, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
BOOST_CHECK(test_args.ParseParameters(2, (char **)argv_regtest, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest");
BOOST_CHECK(test_args.ParseParameters(3, (char **)argv_test_no_reg, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
BOOST_CHECK(test_args.ParseParameters(3, (char **)argv_both, error));
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
BOOST_CHECK(test_args.ParseParameters(0, (char **)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
BOOST_CHECK(test_args.ParseParameters(2, (char **)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
BOOST_CHECK(test_args.ParseParameters(2, (char **)argv_regtest, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
BOOST_CHECK(test_args.ParseParameters(3, (char **)argv_test_no_reg, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
BOOST_CHECK(test_args.ParseParameters(3, (char **)argv_both, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
// check setting the network to test (and thus making
// [test] regtest=1 potentially relevant) doesn't break things
test_args.SelectConfigNetwork("test");
BOOST_CHECK(test_args.ParseParameters(0, (char **)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
BOOST_CHECK(test_args.ParseParameters(2, (char **)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
BOOST_CHECK(test_args.ParseParameters(2, (char **)argv_regtest, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
BOOST_CHECK(test_args.ParseParameters(2, (char **)argv_test_no_reg, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
BOOST_CHECK(test_args.ParseParameters(3, (char **)argv_both, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
}
// Test different ways settings can be merged, and verify results. This test can
// be used to confirm that updates to settings code don't change behavior
// unintentionally.
//
// The test covers:
//
// - Combining different setting actions. Possible actions are: configuring a
// setting, negating a setting (adding "-no" prefix), and configuring/negating
// settings in a network section (adding "main." or "test." prefixes).
//
// - Combining settings from command line arguments and a config file.
//
// - Combining SoftSet and ForceSet calls.
//
// - Testing "main" and "test" network values to make sure settings from network
// sections are applied and to check for mainnet-specific behaviors like
// inheriting settings from the default section.
//
// - Testing network-specific settings like "-wallet", that may be ignored
// outside a network section, and non-network specific settings like "-server"
// that aren't sensitive to the network.
//
struct ArgsMergeTestingSetup : public BasicTestingSetup {
//! Max number of actions to sequence together. Can decrease this when
//! debugging to make test results easier to understand.
static constexpr int MAX_ACTIONS = 3;
enum Action { NONE, SET, NEGATE, SECTION_SET, SECTION_NEGATE };
using ActionList = Action[MAX_ACTIONS];
//! Enumerate all possible test configurations.
template <typename Fn> void ForEachMergeSetup(Fn &&fn) {
ActionList arg_actions = {};
// command_line_options do not have sections. Only iterate over SET and
// NEGATE
ForEachNoDup(arg_actions, SET, NEGATE, [&] {
ActionList conf_actions = {};
ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] {
for (bool soft_set : {false, true}) {
for (bool force_set : {false, true}) {
for (const std::string &section :
{CBaseChainParams::MAIN,
CBaseChainParams::TESTNET}) {
for (const std::string &network :
{CBaseChainParams::MAIN,
CBaseChainParams::TESTNET}) {
for (bool net_specific : {false, true}) {
fn(arg_actions, conf_actions, soft_set,
force_set, section, network,
net_specific);
}
}
}
}
}
});
});
}
//! Translate actions into a list of <key>=<value> setting strings.
std::vector<std::string> GetValues(const ActionList &actions,
const std::string &section,
const std::string &name,
const std::string &value_prefix) {
std::vector<std::string> values;
int suffix = 0;
for (Action action : actions) {
if (action == NONE) {
break;
}
std::string prefix;
if (action == SECTION_SET || action == SECTION_NEGATE) {
prefix = section + ".";
}
if (action == SET || action == SECTION_SET) {
for (int i = 0; i < 2; ++i) {
values.push_back(prefix + name + "=" + value_prefix +
ToString(++suffix));
}
}
if (action == NEGATE || action == SECTION_NEGATE) {
values.push_back(prefix + "no" + name + "=1");
}
}
return values;
}
};
// Regression test covering different ways config settings can be merged. The
// test parses and merges settings, representing the results as strings that get
// compared against an expected hash. To debug, the result strings can be dumped
// to a file (see comments below).
BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup) {
CHash256 out_sha;
FILE *out_file = nullptr;
if (const char *out_path = getenv("ARGS_MERGE_TEST_OUT")) {
out_file = fsbridge::fopen(out_path, "w");
if (!out_file) {
throw std::system_error(errno, std::generic_category(),
"fopen failed");
}
}
ForEachMergeSetup([&](const ActionList &arg_actions,
const ActionList &conf_actions, bool soft_set,
bool force_set, const std::string &section,
const std::string &network, bool net_specific) {
TestArgsManager parser;
LOCK(parser.cs_args);
std::string desc = "net=";
desc += network;
parser.m_network = network;
const std::string &name = net_specific ? "wallet" : "server";
const std::string key = "-" + name;
parser.AddArg(key, name, ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
if (net_specific) {
parser.SetNetworkOnlyArg(key);
}
auto args = GetValues(arg_actions, section, name, "a");
std::vector<const char *> argv = {"ignored"};
for (auto &arg : args) {
arg.insert(0, "-");
desc += " ";
desc += arg;
argv.push_back(arg.c_str());
}
std::string error;
BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
BOOST_CHECK_EQUAL(error, "");
std::string conf;
for (auto &conf_val : GetValues(conf_actions, section, name, "c")) {
desc += " ";
desc += conf_val;
conf += conf_val;
conf += "\n";
}
std::istringstream conf_stream(conf);
BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
BOOST_CHECK_EQUAL(error, "");
if (soft_set) {
desc += " soft";
parser.SoftSetArg(key, "soft1");
parser.SoftSetArg(key, "soft2");
}
if (force_set) {
desc += " force";
parser.ForceSetArg(key, "force1");
parser.ForceSetArg(key, "force2");
}
desc += " || ";
if (!parser.IsArgSet(key)) {
desc += "unset";
BOOST_CHECK(!parser.IsArgNegated(key));
BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "default");
BOOST_CHECK(parser.GetArgs(key).empty());
} else if (parser.IsArgNegated(key)) {
desc += "negated";
BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "0");
BOOST_CHECK(parser.GetArgs(key).empty());
} else {
desc += parser.GetArg(key, "default");
desc += " |";
for (const auto &arg : parser.GetArgs(key)) {
desc += " ";
desc += arg;
}
}
std::set<std::string> ignored = parser.GetUnsuitableSectionOnlyArgs();
if (!ignored.empty()) {
desc += " | ignored";
for (const auto &arg : ignored) {
desc += " ";
desc += arg;
}
}
desc += "\n";
out_sha.Write(MakeUCharSpan(desc));
if (out_file) {
BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) ==
desc.size());
}
});
if (out_file) {
if (fclose(out_file)) {
throw std::system_error(errno, std::generic_category(),
"fclose failed");
}
out_file = nullptr;
}
uint8_t out_sha_bytes[CSHA256::OUTPUT_SIZE];
out_sha.Finalize(out_sha_bytes);
std::string out_sha_hex = HexStr(out_sha_bytes);
// If check below fails, should manually dump the results with:
//
// ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin
// --run_test=util_tests/util_ArgsMerge
//
// And verify diff against previous results to make sure the changes are
// expected.
//
// Results file is formatted like:
//
// <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> |
// <GetUnsuitable output>
BOOST_CHECK_EQUAL(
out_sha_hex,
"8fd4877bb8bf337badca950ede6c917441901962f160e52514e06a60dea46cde");
}
// Similar test as above, but for ArgsManager::GetChainName function.
struct ChainMergeTestingSetup : public BasicTestingSetup {
static constexpr int MAX_ACTIONS = 2;
enum Action {
NONE,
ENABLE_TEST,
DISABLE_TEST,
NEGATE_TEST,
ENABLE_REG,
DISABLE_REG,
NEGATE_REG
};
using ActionList = Action[MAX_ACTIONS];
//! Enumerate all possible test configurations.
template <typename Fn> void ForEachMergeSetup(Fn &&fn) {
ActionList arg_actions = {};
ForEachNoDup(arg_actions, ENABLE_TEST, NEGATE_REG, [&] {
ActionList conf_actions = {};
ForEachNoDup(conf_actions, ENABLE_TEST, NEGATE_REG,
[&] { fn(arg_actions, conf_actions); });
});
}
};
BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup) {
CHash256 out_sha;
FILE *out_file = nullptr;
if (const char *out_path = getenv("CHAIN_MERGE_TEST_OUT")) {
out_file = fsbridge::fopen(out_path, "w");
if (!out_file) {
throw std::system_error(errno, std::generic_category(),
"fopen failed");
}
}
ForEachMergeSetup([&](const ActionList &arg_actions,
const ActionList &conf_actions) {
TestArgsManager parser;
LOCK(parser.cs_args);
parser.AddArg("-regtest", "regtest", ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
parser.AddArg("-testnet", "testnet", ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
auto arg = [](Action action) -> const char * {
switch (action) {
case ENABLE_TEST:
return "-testnet=1";
case DISABLE_TEST:
return "-testnet=0";
case NEGATE_TEST:
return "-notestnet=1";
case ENABLE_REG:
return "-regtest=1";
case DISABLE_REG:
return "-regtest=0";
case NEGATE_REG:
return "-noregtest=1";
default:
return nullptr;
}
};
std::string desc;
std::vector<const char *> argv = {"ignored"};
for (Action action : arg_actions) {
const char *argstr = arg(action);
if (!argstr) {
break;
}
argv.push_back(argstr);
desc += " ";
desc += argv.back();
}
std::string error;
BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
BOOST_CHECK_EQUAL(error, "");
std::string conf;
for (Action action : conf_actions) {
const char *argstr = arg(action);
if (!argstr) {
break;
}
desc += " ";
desc += argstr + 1;
conf += argstr + 1;
conf += "\n";
}
std::istringstream conf_stream(conf);
BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
BOOST_CHECK_EQUAL(error, "");
desc += " || ";
try {
desc += parser.GetChainName();
} catch (const std::runtime_error &e) {
desc += "error: ";
desc += e.what();
}
desc += "\n";
out_sha.Write(MakeUCharSpan(desc));
if (out_file) {
BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) ==
desc.size());
}
});
if (out_file) {
if (fclose(out_file)) {
throw std::system_error(errno, std::generic_category(),
"fclose failed");
}
out_file = nullptr;
}
uint8_t out_sha_bytes[CSHA256::OUTPUT_SIZE];
out_sha.Finalize(out_sha_bytes);
std::string out_sha_hex = HexStr(out_sha_bytes);
// If check below fails, should manually dump the results with:
//
// CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin
// --run_test=util_tests/util_ChainMerge
//
// And verify diff against previous results to make sure the changes are
// expected.
//
// Results file is formatted like:
//
// <input> || <output>
BOOST_CHECK_EQUAL(
out_sha_hex,
"f0b3a3c29869edc765d579c928f7f1690a71fbb673b49ccf39cbc4de18156a0d");
}
BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) {
// Test writing setting.
TestArgsManager args1;
args1.ForceSetArg("-datadir", fs::PathToString(m_path_root));
args1.LockSettings([&](util::Settings &settings) {
settings.rw_settings["name"] = "value";
});
args1.WriteSettingsFile();
// Test reading setting.
TestArgsManager args2;
args2.ForceSetArg("-datadir", fs::PathToString(m_path_root));
args2.ReadSettingsFile();
args2.LockSettings([&](util::Settings &settings) {
BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value");
});
// Test error logging, and remove previously written setting.
{
ASSERT_DEBUG_LOG("Failed renaming settings file");
fs::remove(args1.GetDataDirBase() / "settings.json");
fs::create_directory(args1.GetDataDirBase() / "settings.json");
args2.WriteSettingsFile();
fs::remove(args1.GetDataDirBase() / "settings.json");
}
}
BOOST_AUTO_TEST_CASE(util_FormatMoney) {
BOOST_CHECK_EQUAL(FormatMoney(Amount::zero()), "0.00");
BOOST_CHECK_EQUAL(FormatMoney(123456789 * (COIN / 10000)),
"12345678900.00");
BOOST_CHECK_EQUAL(FormatMoney(-1 * COIN), "-1000000.00");
BOOST_CHECK_EQUAL(FormatMoney(100000000 * COIN), "100000000000000.00");
BOOST_CHECK_EQUAL(FormatMoney(10000000 * COIN), "10000000000000.00");
BOOST_CHECK_EQUAL(FormatMoney(1000000 * COIN), "1000000000000.00");
BOOST_CHECK_EQUAL(FormatMoney(100000 * COIN), "100000000000.00");
BOOST_CHECK_EQUAL(FormatMoney(10000 * COIN), "10000000000.00");
BOOST_CHECK_EQUAL(FormatMoney(1000 * COIN), "1000000000.00");
BOOST_CHECK_EQUAL(FormatMoney(100 * COIN), "100000000.00");
BOOST_CHECK_EQUAL(FormatMoney(10 * COIN), "10000000.00");
BOOST_CHECK_EQUAL(FormatMoney(COIN), "1000000.00");
BOOST_CHECK_EQUAL(FormatMoney(COIN / 10), "100000.00");
BOOST_CHECK_EQUAL(FormatMoney(COIN / 100), "10000.00");
BOOST_CHECK_EQUAL(FormatMoney(COIN / 1000), "1000.00");
BOOST_CHECK_EQUAL(FormatMoney(COIN / 10000), "100.00");
BOOST_CHECK_EQUAL(FormatMoney(COIN / 100000), "10.00");
BOOST_CHECK_EQUAL(FormatMoney(COIN / 1000000), "1.00");
BOOST_CHECK_EQUAL(FormatMoney(COIN / 10000000), "0.10");
BOOST_CHECK_EQUAL(FormatMoney(COIN / 100000000), "0.01");
}
BOOST_AUTO_TEST_CASE(util_ParseMoney) {
Amount ret = Amount::zero();
BOOST_CHECK(ParseMoney("0.0", ret));
BOOST_CHECK_EQUAL(ret, Amount::zero());
BOOST_CHECK(ParseMoney("1234567.89", ret));
BOOST_CHECK_EQUAL(ret, 123456789 * SATOSHI);
BOOST_CHECK(ParseMoney("21000000000000.00", ret));
BOOST_CHECK_EQUAL(ret, MAX_MONEY);
const auto XEC = Currency::get().baseunit;
BOOST_CHECK(ParseMoney("100000000.00", ret));
BOOST_CHECK_EQUAL(ret, 100000000 * XEC);
BOOST_CHECK(ParseMoney("10000000.00", ret));
BOOST_CHECK_EQUAL(ret, 10000000 * XEC);
BOOST_CHECK(ParseMoney("1000000.00", ret));
BOOST_CHECK_EQUAL(ret, 1000000 * XEC);
BOOST_CHECK(ParseMoney("100000.00", ret));
BOOST_CHECK_EQUAL(ret, 100000 * XEC);
BOOST_CHECK(ParseMoney("10000.00", ret));
BOOST_CHECK_EQUAL(ret, 10000 * XEC);
BOOST_CHECK(ParseMoney("1000.00", ret));
BOOST_CHECK_EQUAL(ret, 1000 * XEC);
BOOST_CHECK(ParseMoney("100.00", ret));
BOOST_CHECK_EQUAL(ret, 100 * XEC);
BOOST_CHECK(ParseMoney("10.00", ret));
BOOST_CHECK_EQUAL(ret, 10 * XEC);
BOOST_CHECK(ParseMoney("1.00", ret));
BOOST_CHECK_EQUAL(ret, XEC);
BOOST_CHECK(ParseMoney("1", ret));
BOOST_CHECK_EQUAL(ret, XEC);
BOOST_CHECK(ParseMoney(" 1", ret));
BOOST_CHECK_EQUAL(ret, XEC);
BOOST_CHECK(ParseMoney("1 ", ret));
BOOST_CHECK_EQUAL(ret, XEC);
BOOST_CHECK(ParseMoney(" 1 ", ret));
BOOST_CHECK_EQUAL(ret, XEC);
BOOST_CHECK(ParseMoney("0.1", ret));
BOOST_CHECK_EQUAL(ret, XEC / 10);
BOOST_CHECK(ParseMoney("0.01", ret));
BOOST_CHECK_EQUAL(ret, XEC / 100);
BOOST_CHECK(ParseMoney(" 0.01 ", ret));
BOOST_CHECK_EQUAL(ret, XEC / 100);
BOOST_CHECK(ParseMoney("0.01 ", ret));
BOOST_CHECK_EQUAL(ret, XEC / 100);
BOOST_CHECK(ParseMoney(" 0.01", ret));
BOOST_CHECK_EQUAL(ret, XEC / 100);
// Parsing amount that can not be represented in ret should fail
BOOST_CHECK(!ParseMoney("0.001", ret));
BOOST_CHECK(!ParseMoney("0.0001", ret));
BOOST_CHECK(!ParseMoney("0.00001", ret));
BOOST_CHECK(!ParseMoney("0.000001", ret));
BOOST_CHECK(!ParseMoney("0.0000001", ret));
BOOST_CHECK(!ParseMoney("0.00000001", ret));
BOOST_CHECK(!ParseMoney("0.000000001", ret));
// Parsing empty string should fail
BOOST_CHECK(!ParseMoney("", ret));
BOOST_CHECK(!ParseMoney(" ", ret));
BOOST_CHECK(!ParseMoney(" ", ret));
// Parsing two numbers should fail
BOOST_CHECK(!ParseMoney("1 2", ret));
BOOST_CHECK(!ParseMoney(" 1 2 ", ret));
BOOST_CHECK(!ParseMoney(" 1.2 3 ", ret));
BOOST_CHECK(!ParseMoney(" 1 2.3 ", ret));
// Max possible value with the max number of digits
BOOST_CHECK(ParseMoney("9999999999999999.99", ret));
BOOST_CHECK_EQUAL(ret, int64_t(999999999999999999) * SATOSHI);
// Attempted 63 bit overflow should fail
BOOST_CHECK(!ParseMoney("10000000000000000.00", ret));
BOOST_CHECK(!ParseMoney("92233720368547758.08", ret));
// Parsing negative amounts must fail
BOOST_CHECK(!ParseMoney("-1", ret));
// Parsing strings with embedded NUL characters should fail
BOOST_CHECK(!ParseMoney("\0-1"s, ret));
BOOST_CHECK(!ParseMoney(STRING_WITH_EMBEDDED_NULL_CHAR, ret));
BOOST_CHECK(!ParseMoney("1\0"s, ret));
}
BOOST_AUTO_TEST_CASE(util_IsHex) {
BOOST_CHECK(IsHex("00"));
BOOST_CHECK(IsHex("00112233445566778899aabbccddeeffAABBCCDDEEFF"));
BOOST_CHECK(IsHex("ff"));
BOOST_CHECK(IsHex("FF"));
BOOST_CHECK(!IsHex(""));
BOOST_CHECK(!IsHex("0"));
BOOST_CHECK(!IsHex("a"));
BOOST_CHECK(!IsHex("eleven"));
BOOST_CHECK(!IsHex("00xx00"));
BOOST_CHECK(!IsHex("0x0000"));
}
BOOST_AUTO_TEST_CASE(util_IsHexNumber) {
BOOST_CHECK(IsHexNumber("0x0"));
BOOST_CHECK(IsHexNumber("0"));
BOOST_CHECK(IsHexNumber("0x10"));
BOOST_CHECK(IsHexNumber("10"));
BOOST_CHECK(IsHexNumber("0xff"));
BOOST_CHECK(IsHexNumber("ff"));
BOOST_CHECK(IsHexNumber("0xFfa"));
BOOST_CHECK(IsHexNumber("Ffa"));
BOOST_CHECK(IsHexNumber("0x00112233445566778899aabbccddeeffAABBCCDDEEFF"));
BOOST_CHECK(IsHexNumber("00112233445566778899aabbccddeeffAABBCCDDEEFF"));
BOOST_CHECK(!IsHexNumber("")); // empty string not allowed
BOOST_CHECK(!IsHexNumber("0x")); // empty string after prefix not allowed
BOOST_CHECK(!IsHexNumber("0x0 ")); // no spaces at end,
BOOST_CHECK(!IsHexNumber(" 0x0")); // or beginning,
BOOST_CHECK(!IsHexNumber("0x 0")); // or middle,
BOOST_CHECK(!IsHexNumber(" ")); // etc.
BOOST_CHECK(!IsHexNumber("0x0ga")); // invalid character
BOOST_CHECK(!IsHexNumber("x0")); // broken prefix
BOOST_CHECK(!IsHexNumber("0x0x00")); // two prefixes not allowed
}
BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) {
SeedInsecureRand(SeedRand::ZEROS);
for (int mod = 2; mod < 11; mod++) {
int mask = 1;
// Really rough binomial confidence approximation.
int err =
30 * 10000. / mod * sqrt((1. / mod * (1 - 1. / mod)) / 10000.);
// mask is 2^ceil(log2(mod))-1
while (mask < mod - 1) {
mask = (mask << 1) + 1;
}
int count = 0;
// How often does it get a zero from the uniform range [0,mod)?
for (int i = 0; i < 10000; i++) {
uint32_t rval;
do {
rval = InsecureRand32() & mask;
} while (rval >= uint32_t(mod));
count += rval == 0;
}
BOOST_CHECK(count <= 10000 / mod + err);
BOOST_CHECK(count >= 10000 / mod - err);
}
}
BOOST_AUTO_TEST_CASE(util_TimingResistantEqual) {
BOOST_CHECK(TimingResistantEqual(std::string(""), std::string("")));
BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("")));
BOOST_CHECK(!TimingResistantEqual(std::string(""), std::string("abc")));
BOOST_CHECK(!TimingResistantEqual(std::string("a"), std::string("aa")));
BOOST_CHECK(!TimingResistantEqual(std::string("aa"), std::string("a")));
BOOST_CHECK(TimingResistantEqual(std::string("abc"), std::string("abc")));
BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("aba")));
}
/* Test strprintf formatting directives.
* Put a string before and after to ensure sanity of element sizes on stack. */
#define B "check_prefix"
#define E "check_postfix"
BOOST_AUTO_TEST_CASE(strprintf_numbers) {
int64_t s64t = -9223372036854775807LL; /* signed 64 bit test value */
uint64_t u64t = 18446744073709551615ULL; /* unsigned 64 bit test value */
BOOST_CHECK(strprintf("%s %d %s", B, s64t, E) == B
" -9223372036854775807 " E);
BOOST_CHECK(strprintf("%s %u %s", B, u64t, E) == B
" 18446744073709551615 " E);
BOOST_CHECK(strprintf("%s %x %s", B, u64t, E) == B " ffffffffffffffff " E);
size_t st = 12345678; /* unsigned size_t test value */
ssize_t sst = -12345678; /* signed size_t test value */
BOOST_CHECK(strprintf("%s %d %s", B, sst, E) == B " -12345678 " E);
BOOST_CHECK(strprintf("%s %u %s", B, st, E) == B " 12345678 " E);
BOOST_CHECK(strprintf("%s %x %s", B, st, E) == B " bc614e " E);
ptrdiff_t pt = 87654321; /* positive ptrdiff_t test value */
ptrdiff_t spt = -87654321; /* negative ptrdiff_t test value */
BOOST_CHECK(strprintf("%s %d %s", B, spt, E) == B " -87654321 " E);
BOOST_CHECK(strprintf("%s %u %s", B, pt, E) == B " 87654321 " E);
BOOST_CHECK(strprintf("%s %x %s", B, pt, E) == B " 5397fb1 " E);
}
#undef B
#undef E
/* Check for mingw/wine issue #3494
* Remove this test before time.ctime(0xffffffff) == 'Sun Feb 7 07:28:15 2106'
*/
BOOST_AUTO_TEST_CASE(gettime) {
BOOST_CHECK((GetTime() & ~0xFFFFFFFFLL) == 0);
}
BOOST_AUTO_TEST_CASE(util_time_GetTime) {
SetMockTime(111);
// Check that mock time does not change after a sleep
for (const auto &num_sleep : {0ms, 1ms}) {
UninterruptibleSleep(num_sleep);
BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter
BOOST_CHECK_EQUAL(111, Now<NodeSeconds>().time_since_epoch().count());
BOOST_CHECK_EQUAL(
111, TicksSinceEpoch<std::chrono::seconds>(NodeClock::now()));
BOOST_CHECK_EQUAL(111,
TicksSinceEpoch<SecondsDouble>(Now<NodeSeconds>()));
BOOST_CHECK_EQUAL(111, GetTime<std::chrono::seconds>().count());
BOOST_CHECK_EQUAL(111000, GetTime<std::chrono::milliseconds>().count());
BOOST_CHECK_EQUAL(111000, TicksSinceEpoch<std::chrono::milliseconds>(
NodeClock::now()));
BOOST_CHECK_EQUAL(111000000,
GetTime<std::chrono::microseconds>().count());
}
SetMockTime(0);
// Check that steady time and system time changes after a sleep
const auto steady_ms_0 = Now<SteadyMilliseconds>();
const auto steady_0 = std::chrono::steady_clock::now();
const auto ms_0 = GetTime<std::chrono::milliseconds>();
const auto us_0 = GetTime<std::chrono::microseconds>();
UninterruptibleSleep(1ms);
BOOST_CHECK(steady_ms_0 < Now<SteadyMilliseconds>());
BOOST_CHECK(steady_0 + 1ms <= std::chrono::steady_clock::now());
BOOST_CHECK(ms_0 < GetTime<std::chrono::milliseconds>());
BOOST_CHECK(us_0 < GetTime<std::chrono::microseconds>());
}
BOOST_AUTO_TEST_CASE(test_IsDigit) {
BOOST_CHECK_EQUAL(IsDigit('0'), true);
BOOST_CHECK_EQUAL(IsDigit('1'), true);
BOOST_CHECK_EQUAL(IsDigit('8'), true);
BOOST_CHECK_EQUAL(IsDigit('9'), true);
BOOST_CHECK_EQUAL(IsDigit('0' - 1), false);
BOOST_CHECK_EQUAL(IsDigit('9' + 1), false);
BOOST_CHECK_EQUAL(IsDigit(0), false);
BOOST_CHECK_EQUAL(IsDigit(1), false);
BOOST_CHECK_EQUAL(IsDigit(8), false);
BOOST_CHECK_EQUAL(IsDigit(9), false);
}
BOOST_AUTO_TEST_CASE(test_ParseInt32) {
int32_t n;
// Valid values
BOOST_CHECK(ParseInt32("1234", nullptr));
BOOST_CHECK(ParseInt32("0", &n) && n == 0);
BOOST_CHECK(ParseInt32("1234", &n) && n == 1234);
BOOST_CHECK(ParseInt32("01234", &n) && n == 1234); // no octal
BOOST_CHECK(ParseInt32("2147483647", &n) && n == 2147483647);
// (-2147483647 - 1) equals INT_MIN
BOOST_CHECK(ParseInt32("-2147483648", &n) && n == (-2147483647 - 1));
BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234);
BOOST_CHECK(ParseInt32("00000000000000001234", &n) && n == 1234);
BOOST_CHECK(ParseInt32("-00000000000000001234", &n) && n == -1234);
BOOST_CHECK(ParseInt32("00000000000000000000", &n) && n == 0);
BOOST_CHECK(ParseInt32("-00000000000000000000", &n) && n == 0);
// Invalid values
BOOST_CHECK(!ParseInt32("", &n));
BOOST_CHECK(!ParseInt32(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseInt32("1 ", &n));
BOOST_CHECK(!ParseInt32("++1", &n));
BOOST_CHECK(!ParseInt32("+-1", &n));
BOOST_CHECK(!ParseInt32("-+1", &n));
BOOST_CHECK(!ParseInt32("--1", &n));
BOOST_CHECK(!ParseInt32("1a", &n));
BOOST_CHECK(!ParseInt32("aap", &n));
BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex
BOOST_CHECK(!ParseInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseInt32("-2147483649", nullptr));
BOOST_CHECK(!ParseInt32("2147483648", nullptr));
BOOST_CHECK(!ParseInt32("-32482348723847471234", nullptr));
BOOST_CHECK(!ParseInt32("32482348723847471234", nullptr));
}
template <typename T> static void RunToIntegralTests() {
BOOST_CHECK(!ToIntegral<T>(STRING_WITH_EMBEDDED_NULL_CHAR));
BOOST_CHECK(!ToIntegral<T>(" 1"));
BOOST_CHECK(!ToIntegral<T>("1 "));
BOOST_CHECK(!ToIntegral<T>("1a"));
BOOST_CHECK(!ToIntegral<T>("1.1"));
BOOST_CHECK(!ToIntegral<T>("1.9"));
BOOST_CHECK(!ToIntegral<T>("+01.9"));
BOOST_CHECK(!ToIntegral<T>("-"));
BOOST_CHECK(!ToIntegral<T>("+"));
BOOST_CHECK(!ToIntegral<T>(" -1"));
BOOST_CHECK(!ToIntegral<T>("-1 "));
BOOST_CHECK(!ToIntegral<T>(" -1 "));
BOOST_CHECK(!ToIntegral<T>("+1"));
BOOST_CHECK(!ToIntegral<T>(" +1"));
BOOST_CHECK(!ToIntegral<T>(" +1 "));
BOOST_CHECK(!ToIntegral<T>("+-1"));
BOOST_CHECK(!ToIntegral<T>("-+1"));
BOOST_CHECK(!ToIntegral<T>("++1"));
BOOST_CHECK(!ToIntegral<T>("--1"));
BOOST_CHECK(!ToIntegral<T>(""));
BOOST_CHECK(!ToIntegral<T>("aap"));
BOOST_CHECK(!ToIntegral<T>("0x1"));
BOOST_CHECK(!ToIntegral<T>("-32482348723847471234"));
BOOST_CHECK(!ToIntegral<T>("32482348723847471234"));
}
BOOST_AUTO_TEST_CASE(test_ToIntegral) {
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("1234").value(), 1'234);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("0").value(), 0);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("01234").value(), 1'234);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("00000000000000001234").value(),
1'234);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-00000000000000001234").value(),
-1'234);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("00000000000000000000").value(), 0);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-00000000000000000000").value(), 0);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-1234").value(), -1'234);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-1").value(), -1);
RunToIntegralTests<uint64_t>();
RunToIntegralTests<int64_t>();
RunToIntegralTests<uint32_t>();
RunToIntegralTests<int32_t>();
RunToIntegralTests<uint16_t>();
RunToIntegralTests<int16_t>();
RunToIntegralTests<uint8_t>();
RunToIntegralTests<int8_t>();
BOOST_CHECK(!ToIntegral<int64_t>("-9223372036854775809"));
BOOST_CHECK_EQUAL(ToIntegral<int64_t>("-9223372036854775808").value(),
-9'223'372'036'854'775'807LL - 1LL);
BOOST_CHECK_EQUAL(ToIntegral<int64_t>("9223372036854775807").value(),
9'223'372'036'854'775'807);
BOOST_CHECK(!ToIntegral<int64_t>("9223372036854775808"));
BOOST_CHECK(!ToIntegral<uint64_t>("-1"));
BOOST_CHECK_EQUAL(ToIntegral<uint64_t>("0").value(), 0U);
BOOST_CHECK_EQUAL(ToIntegral<uint64_t>("18446744073709551615").value(),
18'446'744'073'709'551'615ULL);
BOOST_CHECK(!ToIntegral<uint64_t>("18446744073709551616"));
BOOST_CHECK(!ToIntegral<int32_t>("-2147483649"));
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-2147483648").value(),
-2'147'483'648LL);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("2147483647").value(), 2'147'483'647);
BOOST_CHECK(!ToIntegral<int32_t>("2147483648"));
BOOST_CHECK(!ToIntegral<uint32_t>("-1"));
BOOST_CHECK_EQUAL(ToIntegral<uint32_t>("0").value(), 0U);
BOOST_CHECK_EQUAL(ToIntegral<uint32_t>("4294967295").value(),
4'294'967'295U);
BOOST_CHECK(!ToIntegral<uint32_t>("4294967296"));
BOOST_CHECK(!ToIntegral<int16_t>("-32769"));
BOOST_CHECK_EQUAL(ToIntegral<int16_t>("-32768").value(), -32'768);
BOOST_CHECK_EQUAL(ToIntegral<int16_t>("32767").value(), 32'767);
BOOST_CHECK(!ToIntegral<int16_t>("32768"));
BOOST_CHECK(!ToIntegral<uint16_t>("-1"));
BOOST_CHECK_EQUAL(ToIntegral<uint16_t>("0").value(), 0U);
BOOST_CHECK_EQUAL(ToIntegral<uint16_t>("65535").value(), 65'535U);
BOOST_CHECK(!ToIntegral<uint16_t>("65536"));
BOOST_CHECK(!ToIntegral<int8_t>("-129"));
BOOST_CHECK_EQUAL(ToIntegral<int8_t>("-128").value(), -128);
BOOST_CHECK_EQUAL(ToIntegral<int8_t>("127").value(), 127);
BOOST_CHECK(!ToIntegral<int8_t>("128"));
BOOST_CHECK(!ToIntegral<uint8_t>("-1"));
BOOST_CHECK_EQUAL(ToIntegral<uint8_t>("0").value(), 0U);
BOOST_CHECK_EQUAL(ToIntegral<uint8_t>("255").value(), 255U);
BOOST_CHECK(!ToIntegral<uint8_t>("256"));
}
BOOST_AUTO_TEST_CASE(test_ParseInt64) {
int64_t n;
// Valid values
BOOST_CHECK(ParseInt64("1234", nullptr));
BOOST_CHECK(ParseInt64("0", &n) && n == 0LL);
BOOST_CHECK(ParseInt64("1234", &n) && n == 1234LL);
BOOST_CHECK(ParseInt64("01234", &n) && n == 1234LL); // no octal
BOOST_CHECK(ParseInt64("2147483647", &n) && n == 2147483647LL);
BOOST_CHECK(ParseInt64("-2147483648", &n) && n == -2147483648LL);
BOOST_CHECK(ParseInt64("9223372036854775807", &n) &&
n == (int64_t)9223372036854775807);
BOOST_CHECK(ParseInt64("-9223372036854775808", &n) &&
n == (int64_t)-9223372036854775807 - 1);
BOOST_CHECK(ParseInt64("-1234", &n) && n == -1234LL);
// Invalid values
BOOST_CHECK(!ParseInt64("", &n));
BOOST_CHECK(!ParseInt64(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseInt64("1 ", &n));
BOOST_CHECK(!ParseInt64("1a", &n));
BOOST_CHECK(!ParseInt64("aap", &n));
BOOST_CHECK(!ParseInt64("0x1", &n)); // no hex
BOOST_CHECK(!ParseInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseInt64("-9223372036854775809", nullptr));
BOOST_CHECK(!ParseInt64("9223372036854775808", nullptr));
BOOST_CHECK(!ParseInt64("-32482348723847471234", nullptr));
BOOST_CHECK(!ParseInt64("32482348723847471234", nullptr));
}
BOOST_AUTO_TEST_CASE(test_ParseUInt8) {
uint8_t n;
// Valid values
BOOST_CHECK(ParseUInt8("255", nullptr));
BOOST_CHECK(ParseUInt8("0", &n) && n == 0);
BOOST_CHECK(ParseUInt8("255", &n) && n == 255);
BOOST_CHECK(ParseUInt8("0255", &n) && n == 255); // no octal
BOOST_CHECK(ParseUInt8("255", &n) && n == static_cast<uint8_t>(255));
BOOST_CHECK(ParseUInt8("+255", &n) && n == 255);
BOOST_CHECK(ParseUInt8("00000000000000000012", &n) && n == 12);
BOOST_CHECK(ParseUInt8("00000000000000000000", &n) && n == 0);
// Invalid values
BOOST_CHECK(!ParseUInt8("-00000000000000000000", &n));
BOOST_CHECK(!ParseUInt8("", &n));
BOOST_CHECK(!ParseUInt8(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseUInt8(" -1", &n));
BOOST_CHECK(!ParseUInt8("++1", &n));
BOOST_CHECK(!ParseUInt8("+-1", &n));
BOOST_CHECK(!ParseUInt8("-+1", &n));
BOOST_CHECK(!ParseUInt8("--1", &n));
BOOST_CHECK(!ParseUInt8("-1", &n));
BOOST_CHECK(!ParseUInt8("1 ", &n));
BOOST_CHECK(!ParseUInt8("1a", &n));
BOOST_CHECK(!ParseUInt8("aap", &n));
BOOST_CHECK(!ParseUInt8("0x1", &n)); // no hex
BOOST_CHECK(!ParseUInt8(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseUInt8("-255", &n));
BOOST_CHECK(!ParseUInt8("256", &n));
BOOST_CHECK(!ParseUInt8("-123", &n));
BOOST_CHECK(!ParseUInt8("-123", nullptr));
BOOST_CHECK(!ParseUInt8("256", nullptr));
}
BOOST_AUTO_TEST_CASE(test_ParseUInt16) {
uint16_t n;
// Valid values
BOOST_CHECK(ParseUInt16("1234", nullptr));
BOOST_CHECK(ParseUInt16("0", &n) && n == 0);
BOOST_CHECK(ParseUInt16("1234", &n) && n == 1234);
BOOST_CHECK(ParseUInt16("01234", &n) && n == 1234); // no octal
BOOST_CHECK(ParseUInt16("65535", &n) && n == static_cast<uint16_t>(65535));
BOOST_CHECK(ParseUInt16("+65535", &n) && n == 65535);
BOOST_CHECK(ParseUInt16("00000000000000000012", &n) && n == 12);
BOOST_CHECK(ParseUInt16("00000000000000000000", &n) && n == 0);
// Invalid values
BOOST_CHECK(!ParseUInt16("-00000000000000000000", &n));
BOOST_CHECK(!ParseUInt16("", &n));
BOOST_CHECK(!ParseUInt16(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseUInt16(" -1", &n));
BOOST_CHECK(!ParseUInt16("++1", &n));
BOOST_CHECK(!ParseUInt16("+-1", &n));
BOOST_CHECK(!ParseUInt16("-+1", &n));
BOOST_CHECK(!ParseUInt16("--1", &n));
BOOST_CHECK(!ParseUInt16("-1", &n));
BOOST_CHECK(!ParseUInt16("1 ", &n));
BOOST_CHECK(!ParseUInt16("1a", &n));
BOOST_CHECK(!ParseUInt16("aap", &n));
BOOST_CHECK(!ParseUInt16("0x1", &n)); // no hex
BOOST_CHECK(!ParseUInt16(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseUInt16("-65535", &n));
BOOST_CHECK(!ParseUInt16("65536", &n));
BOOST_CHECK(!ParseUInt16("-123", &n));
BOOST_CHECK(!ParseUInt16("-123", nullptr));
BOOST_CHECK(!ParseUInt16("65536", nullptr));
}
BOOST_AUTO_TEST_CASE(test_ParseUInt32) {
uint32_t n;
// Valid values
BOOST_CHECK(ParseUInt32("1234", nullptr));
BOOST_CHECK(ParseUInt32("0", &n) && n == 0);
BOOST_CHECK(ParseUInt32("1234", &n) && n == 1234);
BOOST_CHECK(ParseUInt32("01234", &n) && n == 1234); // no octal
BOOST_CHECK(ParseUInt32("2147483647", &n) && n == 2147483647);
BOOST_CHECK(ParseUInt32("2147483648", &n) && n == (uint32_t)2147483648);
BOOST_CHECK(ParseUInt32("4294967295", &n) && n == (uint32_t)4294967295);
BOOST_CHECK(ParseUInt32("+1234", &n) && n == 1234);
BOOST_CHECK(ParseUInt32("00000000000000001234", &n) && n == 1234);
BOOST_CHECK(ParseUInt32("00000000000000000000", &n) && n == 0);
// Invalid values
BOOST_CHECK(!ParseUInt32("-00000000000000000000", &n));
BOOST_CHECK(!ParseUInt32("", &n));
BOOST_CHECK(!ParseUInt32(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseUInt32(" -1", &n));
BOOST_CHECK(!ParseUInt32("++1", &n));
BOOST_CHECK(!ParseUInt32("+-1", &n));
BOOST_CHECK(!ParseUInt32("-+1", &n));
BOOST_CHECK(!ParseUInt32("--1", &n));
BOOST_CHECK(!ParseUInt32("-1", &n));
BOOST_CHECK(!ParseUInt32("1 ", &n));
BOOST_CHECK(!ParseUInt32("1a", &n));
BOOST_CHECK(!ParseUInt32("aap", &n));
BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex
BOOST_CHECK(!ParseUInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseUInt32("-2147483648", &n));
BOOST_CHECK(!ParseUInt32("4294967296", &n));
BOOST_CHECK(!ParseUInt32("-1234", &n));
BOOST_CHECK(!ParseUInt32("-32482348723847471234", nullptr));
BOOST_CHECK(!ParseUInt32("32482348723847471234", nullptr));
}
BOOST_AUTO_TEST_CASE(test_ParseUInt64) {
uint64_t n;
// Valid values
BOOST_CHECK(ParseUInt64("1234", nullptr));
BOOST_CHECK(ParseUInt64("0", &n) && n == 0LL);
BOOST_CHECK(ParseUInt64("1234", &n) && n == 1234LL);
BOOST_CHECK(ParseUInt64("01234", &n) && n == 1234LL); // no octal
BOOST_CHECK(ParseUInt64("2147483647", &n) && n == 2147483647LL);
BOOST_CHECK(ParseUInt64("9223372036854775807", &n) &&
n == 9223372036854775807ULL);
BOOST_CHECK(ParseUInt64("9223372036854775808", &n) &&
n == 9223372036854775808ULL);
BOOST_CHECK(ParseUInt64("18446744073709551615", &n) &&
n == 18446744073709551615ULL);
// Invalid values
BOOST_CHECK(!ParseUInt64("", &n));
BOOST_CHECK(!ParseUInt64(" 1", &n)); // no padding inside
BOOST_CHECK(!ParseUInt64(" -1", &n));
BOOST_CHECK(!ParseUInt64("1 ", &n));
BOOST_CHECK(!ParseUInt64("1a", &n));
BOOST_CHECK(!ParseUInt64("aap", &n));
BOOST_CHECK(!ParseUInt64("0x1", &n)); // no hex
BOOST_CHECK(!ParseUInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseUInt64("-9223372036854775809", nullptr));
BOOST_CHECK(!ParseUInt64("18446744073709551616", nullptr));
BOOST_CHECK(!ParseUInt64("-32482348723847471234", nullptr));
BOOST_CHECK(!ParseUInt64("-2147483648", &n));
BOOST_CHECK(!ParseUInt64("-9223372036854775808", &n));
BOOST_CHECK(!ParseUInt64("-1234", &n));
}
BOOST_AUTO_TEST_CASE(test_FormatParagraph) {
BOOST_CHECK_EQUAL(FormatParagraph("", 79, 0), "");
BOOST_CHECK_EQUAL(FormatParagraph("test", 79, 0), "test");
BOOST_CHECK_EQUAL(FormatParagraph(" test", 79, 0), " test");
BOOST_CHECK_EQUAL(FormatParagraph("test test", 79, 0), "test test");
BOOST_CHECK_EQUAL(FormatParagraph("test test", 4, 0), "test\ntest");
BOOST_CHECK_EQUAL(FormatParagraph("testerde test", 4, 0), "testerde\ntest");
BOOST_CHECK_EQUAL(FormatParagraph("test test", 4, 4), "test\n test");
// Make sure we don't indent a fully-new line following a too-long line
// ending
BOOST_CHECK_EQUAL(FormatParagraph("test test\nabc", 4, 4),
"test\n test\nabc");
BOOST_CHECK_EQUAL(
FormatParagraph("This_is_a_very_long_test_string_without_any_spaces_so_"
"it_should_just_get_returned_as_is_despite_the_length "
"until it gets here",
79),
"This_is_a_very_long_test_string_without_any_spaces_so_it_should_just_"
"get_returned_as_is_despite_the_length\nuntil it gets here");
// Test wrap length is exact
BOOST_CHECK_EQUAL(
FormatParagraph("a b c d e f g h i j k l m n o p q r s t u v w x y z 1 "
"2 3 4 5 6 7 8 9 a b c de f g h i j k l m n o p",
79),
"a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 "
"a b c de\nf g h i j k l m n o p");
BOOST_CHECK_EQUAL(
FormatParagraph("x\na b c d e f g h i j k l m n o p q r s t u v w x y "
"z 1 2 3 4 5 6 7 8 9 a b c de f g h i j k l m n o p",
79),
"x\na b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 "
"8 9 a b c de\nf g h i j k l m n o p");
// Indent should be included in length of lines
BOOST_CHECK_EQUAL(
FormatParagraph("x\na b c d e f g h i j k l m n o p q r s t u v w x y "
"z 1 2 3 4 5 6 7 8 9 a b c de f g h i j k l m n o p q "
"r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 a b c d e fg h "
"i j k",
79, 4),
"x\na b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 "
"8 9 a b c de\n f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 "
"5 6 7 8 9 a b c d e fg\n h i j k");
BOOST_CHECK_EQUAL(
FormatParagraph("This is a very long test string. This is a second "
"sentence in the very long test string.",
79),
"This is a very long test string. This is a second sentence in the "
"very long\ntest string.");
BOOST_CHECK_EQUAL(
FormatParagraph("This is a very long test string.\nThis is a second "
"sentence in the very long test string. This is a "
"third sentence in the very long test string.",
79),
"This is a very long test string.\nThis is a second sentence in the "
"very long test string. This is a third\nsentence in the very long "
"test string.");
BOOST_CHECK_EQUAL(
FormatParagraph("This is a very long test string.\n\nThis is a second "
"sentence in the very long test string. This is a "
"third sentence in the very long test string.",
79),
"This is a very long test string.\n\nThis is a second sentence in the "
"very long test string. This is a third\nsentence in the very long "
"test string.");
BOOST_CHECK_EQUAL(
FormatParagraph(
"Testing that normal newlines do not get indented.\nLike here.",
79),
"Testing that normal newlines do not get indented.\nLike here.");
}
BOOST_AUTO_TEST_CASE(test_FormatVersion) {
BOOST_CHECK_EQUAL(FormatVersion(98700), std::string("0.9.87"));
BOOST_CHECK_EQUAL(FormatVersion(98701), std::string("0.9.87.1"));
BOOST_CHECK_EQUAL(FormatVersion(9098700), std::string("9.9.87"));
BOOST_CHECK_EQUAL(FormatVersion(9098701), std::string("9.9.87.1"));
}
BOOST_AUTO_TEST_CASE(test_FormatUserAgent) {
std::vector<std::string> comments;
comments.push_back(std::string("comment1"));
std::vector<std::string> comments2;
comments2.push_back(std::string("comment1"));
// Semicolon is discouraged but not forbidden by BIP-0014
comments2.push_back(SanitizeString(
std::string("Comment2; .,_?@-; !\"#$%&'()*+/<=>[]\\^`{|}~"),
SAFE_CHARS_UA_COMMENT));
BOOST_CHECK_EQUAL(
FormatUserAgent("Test", "0.9.99", std::vector<std::string>()),
std::string("/Test:0.9.99/"));
BOOST_CHECK_EQUAL(FormatUserAgent("Test", "0.9.99", comments),
std::string("/Test:0.9.99(comment1)/"));
BOOST_CHECK_EQUAL(
FormatUserAgent("Test", "0.9.99", comments2),
std::string("/Test:0.9.99(comment1; Comment2; .,_?@-; )/"));
}
BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) {
int64_t amount = 0;
BOOST_CHECK(ParseFixedPoint("0", 8, &amount));
BOOST_CHECK_EQUAL(amount, 0LL);
BOOST_CHECK(ParseFixedPoint("1", 8, &amount));
BOOST_CHECK_EQUAL(amount, 100000000LL);
BOOST_CHECK(ParseFixedPoint("0.0", 8, &amount));
BOOST_CHECK_EQUAL(amount, 0LL);
BOOST_CHECK(ParseFixedPoint("-0.1", 8, &amount));
BOOST_CHECK_EQUAL(amount, -10000000LL);
BOOST_CHECK(ParseFixedPoint("1.1", 8, &amount));
BOOST_CHECK_EQUAL(amount, 110000000LL);
BOOST_CHECK(ParseFixedPoint("1.10000000000000000", 8, &amount));
BOOST_CHECK_EQUAL(amount, 110000000LL);
BOOST_CHECK(ParseFixedPoint("1.1e1", 8, &amount));
BOOST_CHECK_EQUAL(amount, 1100000000LL);
BOOST_CHECK(ParseFixedPoint("1.1e-1", 8, &amount));
BOOST_CHECK_EQUAL(amount, 11000000LL);
BOOST_CHECK(ParseFixedPoint("1000", 8, &amount));
BOOST_CHECK_EQUAL(amount, 100000000000LL);
BOOST_CHECK(ParseFixedPoint("-1000", 8, &amount));
BOOST_CHECK_EQUAL(amount, -100000000000LL);
BOOST_CHECK(ParseFixedPoint("0.00000001", 8, &amount));
BOOST_CHECK_EQUAL(amount, 1LL);
BOOST_CHECK(ParseFixedPoint("0.0000000100000000", 8, &amount));
BOOST_CHECK_EQUAL(amount, 1LL);
BOOST_CHECK(ParseFixedPoint("-0.00000001", 8, &amount));
BOOST_CHECK_EQUAL(amount, -1LL);
BOOST_CHECK(ParseFixedPoint("1000000000.00000001", 8, &amount));
BOOST_CHECK_EQUAL(amount, 100000000000000001LL);
BOOST_CHECK(ParseFixedPoint("9999999999.99999999", 8, &amount));
BOOST_CHECK_EQUAL(amount, 999999999999999999LL);
BOOST_CHECK(ParseFixedPoint("-9999999999.99999999", 8, &amount));
BOOST_CHECK_EQUAL(amount, -999999999999999999LL);
BOOST_CHECK(!ParseFixedPoint("", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("a-1000", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-a1000", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-1000a", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-01000", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("00.1", 8, &amount));
BOOST_CHECK(!ParseFixedPoint(".1", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("--0.1", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("0.000000001", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-0.000000001", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("0.00000001000000001", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-10000000000.00000000", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("10000000000.00000000", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-10000000000.00000001", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("10000000000.00000001", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-10000000000.00000009", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("10000000000.00000009", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-99999999999.99999999", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("99999909999.09999999", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("92233720368.54775807", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("92233720368.54775808", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-92233720368.54775808", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("-92233720368.54775809", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("1.1e", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("1.1e-", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount));
}
static void TestOtherThread(fs::path dirname, std::string lockname,
bool *result) {
*result = LockDirectory(dirname, lockname);
}
#ifndef WIN32 // Cannot do this test on WIN32 due to lack of fork()
static constexpr char LockCommand = 'L';
static constexpr char UnlockCommand = 'U';
static constexpr char ExitCommand = 'X';
static void TestOtherProcess(fs::path dirname, std::string lockname, int fd) {
char ch;
while (true) {
// Wait for command
int rv = read(fd, &ch, 1);
assert(rv == 1);
switch (ch) {
case LockCommand:
ch = LockDirectory(dirname, lockname);
rv = write(fd, &ch, 1);
assert(rv == 1);
break;
case UnlockCommand:
ReleaseDirectoryLocks();
ch = true; // Always succeeds
rv = write(fd, &ch, 1);
assert(rv == 1);
break;
case ExitCommand:
close(fd);
// As an alternative to exit() which runs the exit handlers
// (which seem to be flakey with Boost test suite with JUNIT
// logging in a forked process), just vanish this process as
// fast as possible. `quick_exit()` would also work, but it is
// not available on all non glibc platforms.
// Using exec also stops valgrind from thinking it needs to
// analyze the memory leaks in this forked process.
execlp("true", "true", (char *)NULL);
default:
assert(0);
}
}
}
#endif
BOOST_AUTO_TEST_CASE(test_LockDirectory) {
fs::path dirname = m_args.GetDataDirBase() / "lock_dir";
const std::string lockname = ".lock";
#ifndef WIN32
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
// it: there is BOOST_TEST_IGNORE_SIGCHLD but that only works when defined
// at build-time of the boost library
void (*old_handler)(int) = signal(SIGCHLD, SIG_DFL);
// Fork another process for testing before creating the lock, so that we
// won't fork while holding the lock (which might be undefined, and is not
// relevant as test case as that is avoided with -daemonize).
int fd[2];
BOOST_CHECK_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, fd), 0);
pid_t pid = fork();
if (!pid) {
BOOST_CHECK_EQUAL(close(fd[1]), 0); // Child: close parent end
TestOtherProcess(dirname, lockname, fd[0]);
}
BOOST_CHECK_EQUAL(close(fd[0]), 0); // Parent: close child end
#endif
// Lock on non-existent directory should fail
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), false);
fs::create_directories(dirname);
// Probing lock on new directory should succeed
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
// Persistent lock on new directory should succeed
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true);
// Another lock on the directory from the same thread should succeed
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true);
// Another lock on the directory from a different thread within the same
// process should succeed
bool threadresult;
std::thread thr(TestOtherThread, dirname, lockname, &threadresult);
thr.join();
BOOST_CHECK_EQUAL(threadresult, true);
#ifndef WIN32
// Try to acquire lock in child process while we're holding it, this should
// fail.
char ch;
BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
BOOST_CHECK_EQUAL((bool)ch, false);
// Give up our lock
ReleaseDirectoryLocks();
// Probing lock from our side now should succeed, but not hold on to the
// lock.
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
// Try to acquire the lock in the child process, this should be successful.
BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
BOOST_CHECK_EQUAL((bool)ch, true);
// When we try to probe the lock now, it should fail.
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), false);
// Unlock the lock in the child process
BOOST_CHECK_EQUAL(write(fd[1], &UnlockCommand, 1), 1);
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
BOOST_CHECK_EQUAL((bool)ch, true);
// When we try to probe the lock now, it should succeed.
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
// Re-lock the lock in the child process, then wait for it to exit, check
// successful return. After that, we check that exiting the process
// has released the lock as we would expect by probing it.
int processstatus;
BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
BOOST_CHECK_EQUAL(write(fd[1], &ExitCommand, 1), 1);
BOOST_CHECK_EQUAL(waitpid(pid, &processstatus, 0), pid);
BOOST_CHECK_EQUAL(processstatus, 0);
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
// Restore SIGCHLD
signal(SIGCHLD, old_handler);
BOOST_CHECK_EQUAL(close(fd[1]), 0); // Close our side of the socketpair
#endif
// Clean up
ReleaseDirectoryLocks();
fs::remove_all(dirname);
}
BOOST_AUTO_TEST_CASE(test_DirIsWritable) {
// Should be able to write to the data dir.
fs::path tmpdirname = m_args.GetDataDirBase();
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
// Should not be able to write to a non-existent dir.
tmpdirname = GetUniquePath(tmpdirname);
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), false);
fs::create_directory(tmpdirname);
// Should be able to write to it now.
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
fs::remove(tmpdirname);
}
template <int F, int T>
static void CheckConvertBits(const std::vector<uint8_t> &in,
const std::vector<uint8_t> &expected) {
std::vector<uint8_t> outpad;
bool ret = ConvertBits<F, T, true>([&](uint8_t c) { outpad.push_back(c); },
in.begin(), in.end());
BOOST_CHECK(ret);
BOOST_CHECK(outpad == expected);
const bool dopad = (in.size() * F) % T;
std::vector<uint8_t> outnopad;
ret = ConvertBits<F, T, false>([&](uint8_t c) { outnopad.push_back(c); },
in.begin(), in.end());
BOOST_CHECK(ret != (dopad && !outpad.empty() && outpad.back()));
if (dopad) {
// We should have skipped the last digit.
outnopad.push_back(expected.back());
}
BOOST_CHECK(outnopad == expected);
// Check the other way around.
// Check with padding. We may get an extra 0 in that case.
std::vector<uint8_t> origpad;
ret = ConvertBits<T, F, true>([&](uint8_t c) { origpad.push_back(c); },
expected.begin(), expected.end());
BOOST_CHECK(ret);
std::vector<uint8_t> orignopad;
ret = ConvertBits<T, F, false>([&](uint8_t c) { orignopad.push_back(c); },
expected.begin(), expected.end());
BOOST_CHECK(ret != ((expected.size() * T) % F && !origpad.empty() &&
origpad.back()));
BOOST_CHECK(orignopad == in);
if (dopad) {
BOOST_CHECK_EQUAL(origpad.back(), 0);
origpad.pop_back();
}
BOOST_CHECK(origpad == in);
}
BOOST_AUTO_TEST_CASE(test_ConvertBits) {
CheckConvertBits<8, 5>({}, {});
CheckConvertBits<8, 5>({0xff}, {0x1f, 0x1c});
CheckConvertBits<8, 5>({0xff, 0xff}, {0x1f, 0x1f, 0x1f, 0x10});
CheckConvertBits<8, 5>({0xff, 0xff, 0xff}, {0x1f, 0x1f, 0x1f, 0x1f, 0x1e});
CheckConvertBits<8, 5>({0xff, 0xff, 0xff, 0xff},
{0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x18});
CheckConvertBits<8, 5>({0xff, 0xff, 0xff, 0xff, 0xff},
{0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f});
CheckConvertBits<8, 5>({0xff, 0xff, 0xff, 0xff, 0xff},
{0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f});
CheckConvertBits<8, 5>({0xff, 0xff, 0xff, 0xff, 0xff},
{0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f});
CheckConvertBits<8, 5>({0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
{0x00, 0x04, 0x11, 0x14, 0x0a, 0x19, 0x1c, 0x09,
0x15, 0x0f, 0x06, 0x1e, 0x1e});
CheckConvertBits<8, 5>({0x00}, {0x00, 0x00});
CheckConvertBits<8, 5>({0xf8}, {0x1f, 0x00});
CheckConvertBits<8, 5>({0x00, 0x00}, {0x00, 0x00, 0x00, 0x00});
}
BOOST_AUTO_TEST_CASE(test_ToLower) {
BOOST_CHECK_EQUAL(ToLower('@'), '@');
BOOST_CHECK_EQUAL(ToLower('A'), 'a');
BOOST_CHECK_EQUAL(ToLower('Z'), 'z');
BOOST_CHECK_EQUAL(ToLower('['), '[');
BOOST_CHECK_EQUAL(ToLower(0), 0);
BOOST_CHECK_EQUAL(ToLower('\xff'), '\xff');
BOOST_CHECK_EQUAL(ToLower(""), "");
BOOST_CHECK_EQUAL(ToLower("#HODL"), "#hodl");
BOOST_CHECK_EQUAL(ToLower("\x00\xfe\xff"), "\x00\xfe\xff");
}
BOOST_AUTO_TEST_CASE(test_ToUpper) {
BOOST_CHECK_EQUAL(ToUpper('`'), '`');
BOOST_CHECK_EQUAL(ToUpper('a'), 'A');
BOOST_CHECK_EQUAL(ToUpper('z'), 'Z');
BOOST_CHECK_EQUAL(ToUpper('{'), '{');
BOOST_CHECK_EQUAL(ToUpper(0), 0);
BOOST_CHECK_EQUAL(ToUpper('\xff'), '\xff');
BOOST_CHECK_EQUAL(ToUpper(""), "");
BOOST_CHECK_EQUAL(ToUpper("#hodl"), "#HODL");
BOOST_CHECK_EQUAL(ToUpper("\x00\xfe\xff"), "\x00\xfe\xff");
}
BOOST_AUTO_TEST_CASE(test_Capitalize) {
BOOST_CHECK_EQUAL(Capitalize(""), "");
BOOST_CHECK_EQUAL(Capitalize("bitcoin"), "Bitcoin");
BOOST_CHECK_EQUAL(Capitalize("\x00\xfe\xff"), "\x00\xfe\xff");
}
static std::string SpanToStr(const Span<const char> &span) {
return std::string(span.begin(), span.end());
}
BOOST_AUTO_TEST_CASE(test_spanparsing) {
using namespace spanparsing;
std::string input;
Span<const char> sp;
bool success;
// Const(...): parse a constant, update span to skip it if successful
input = "MilkToastHoney";
sp = input;
success = Const("", sp); // empty
BOOST_CHECK(success);
BOOST_CHECK_EQUAL(SpanToStr(sp), "MilkToastHoney");
success = Const("Milk", sp);
BOOST_CHECK(success);
BOOST_CHECK_EQUAL(SpanToStr(sp), "ToastHoney");
success = Const("Bread", sp);
BOOST_CHECK(!success);
success = Const("Toast", sp);
BOOST_CHECK(success);
BOOST_CHECK_EQUAL(SpanToStr(sp), "Honey");
success = Const("Honeybadger", sp);
BOOST_CHECK(!success);
success = Const("Honey", sp);
BOOST_CHECK(success);
BOOST_CHECK_EQUAL(SpanToStr(sp), "");
// Func(...): parse a function call, update span to argument if successful
input = "Foo(Bar(xy,z()))";
sp = input;
success = Func("FooBar", sp);
BOOST_CHECK(!success);
success = Func("Foo(", sp);
BOOST_CHECK(!success);
success = Func("Foo", sp);
BOOST_CHECK(success);
BOOST_CHECK_EQUAL(SpanToStr(sp), "Bar(xy,z())");
success = Func("Bar", sp);
BOOST_CHECK(success);
BOOST_CHECK_EQUAL(SpanToStr(sp), "xy,z()");
success = Func("xy", sp);
BOOST_CHECK(!success);
// Expr(...): return expression that span begins with, update span to skip
// it
Span<const char> result;
input = "(n*(n-1))/2";
sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "(n*(n-1))/2");
BOOST_CHECK_EQUAL(SpanToStr(sp), "");
input = "foo,bar";
sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "foo");
BOOST_CHECK_EQUAL(SpanToStr(sp), ",bar");
input = "(aaaaa,bbbbb()),c";
sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "(aaaaa,bbbbb())");
BOOST_CHECK_EQUAL(SpanToStr(sp), ",c");
input = "xyz)foo";
sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "xyz");
BOOST_CHECK_EQUAL(SpanToStr(sp), ")foo");
input = "((a),(b),(c)),xxx";
sp = input;
result = Expr(sp);
BOOST_CHECK_EQUAL(SpanToStr(result), "((a),(b),(c))");
BOOST_CHECK_EQUAL(SpanToStr(sp), ",xxx");
// Split(...): split a string on every instance of sep, return vector
std::vector<Span<const char>> results;
input = "xxx";
results = Split(input, 'x');
BOOST_CHECK_EQUAL(results.size(), 4U);
BOOST_CHECK_EQUAL(SpanToStr(results[0]), "");
BOOST_CHECK_EQUAL(SpanToStr(results[1]), "");
BOOST_CHECK_EQUAL(SpanToStr(results[2]), "");
BOOST_CHECK_EQUAL(SpanToStr(results[3]), "");
input = "one#two#three";
results = Split(input, '-');
BOOST_CHECK_EQUAL(results.size(), 1U);
BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one#two#three");
input = "one#two#three";
results = Split(input, '#');
BOOST_CHECK_EQUAL(results.size(), 3U);
BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one");
BOOST_CHECK_EQUAL(SpanToStr(results[1]), "two");
BOOST_CHECK_EQUAL(SpanToStr(results[2]), "three");
input = "*foo*bar*";
results = Split(input, '*');
BOOST_CHECK_EQUAL(results.size(), 4U);
BOOST_CHECK_EQUAL(SpanToStr(results[0]), "");
BOOST_CHECK_EQUAL(SpanToStr(results[1]), "foo");
BOOST_CHECK_EQUAL(SpanToStr(results[2]), "bar");
BOOST_CHECK_EQUAL(SpanToStr(results[3]), "");
}
BOOST_AUTO_TEST_CASE(test_SplitString) {
// Empty string.
{
std::vector<std::string> result = SplitString("", '-');
BOOST_CHECK_EQUAL(result.size(), 1);
BOOST_CHECK_EQUAL(result[0], "");
}
// Empty items.
{
std::vector<std::string> result = SplitString("-", '-');
BOOST_CHECK_EQUAL(result.size(), 2);
BOOST_CHECK_EQUAL(result[0], "");
BOOST_CHECK_EQUAL(result[1], "");
}
// More empty items.
{
std::vector<std::string> result = SplitString("--", '-');
BOOST_CHECK_EQUAL(result.size(), 3);
BOOST_CHECK_EQUAL(result[0], "");
BOOST_CHECK_EQUAL(result[1], "");
BOOST_CHECK_EQUAL(result[2], "");
}
// Separator is not present.
{
std::vector<std::string> result = SplitString("abc", '-');
BOOST_CHECK_EQUAL(result.size(), 1);
BOOST_CHECK_EQUAL(result[0], "abc");
}
// Basic behavior.
{
std::vector<std::string> result = SplitString("a-b", '-');
BOOST_CHECK_EQUAL(result.size(), 2);
BOOST_CHECK_EQUAL(result[0], "a");
BOOST_CHECK_EQUAL(result[1], "b");
}
// Case-sensitivity of the separator.
{
std::vector<std::string> result = SplitString("AAA", 'a');
BOOST_CHECK_EQUAL(result.size(), 1);
BOOST_CHECK_EQUAL(result[0], "AAA");
}
}
BOOST_AUTO_TEST_CASE(test_LogEscapeMessage) {
// ASCII and UTF-8 must pass through unaltered.
BOOST_CHECK_EQUAL(BCLog::LogEscapeMessage("Valid log message貓"),
"Valid log message貓");
// Newlines must pass through unaltered.
BOOST_CHECK_EQUAL(BCLog::LogEscapeMessage("Message\n with newlines\n"),
"Message\n with newlines\n");
// Other control characters are escaped in C syntax.
BOOST_CHECK_EQUAL(
BCLog::LogEscapeMessage("\x01\x7f Corrupted log message\x0d"),
R"(\x01\x7f Corrupted log message\x0d)");
// Embedded NULL characters are escaped too.
const std::string NUL("O\x00O", 3);
BOOST_CHECK_EQUAL(BCLog::LogEscapeMessage(NUL), R"(O\x00O)");
}
namespace {
struct Tracker {
//! Points to the original object (possibly itself) we moved/copied from
const Tracker *origin;
//! How many copies where involved between the original object and this one
//! (moves are not counted)
int copies;
Tracker() noexcept : origin(this), copies(0) {}
Tracker(const Tracker &t) noexcept
: origin(t.origin), copies(t.copies + 1) {}
Tracker(Tracker &&t) noexcept : origin(t.origin), copies(t.copies) {}
Tracker &operator=(const Tracker &t) noexcept {
origin = t.origin;
copies = t.copies + 1;
return *this;
}
Tracker &operator=(Tracker &&t) noexcept {
origin = t.origin;
copies = t.copies;
return *this;
}
};
} // namespace
BOOST_AUTO_TEST_CASE(test_tracked_vector) {
Tracker t1;
Tracker t2;
Tracker t3;
BOOST_CHECK(t1.origin == &t1);
BOOST_CHECK(t2.origin == &t2);
BOOST_CHECK(t3.origin == &t3);
auto v1 = Vector(t1);
BOOST_CHECK_EQUAL(v1.size(), 1U);
BOOST_CHECK(v1[0].origin == &t1);
BOOST_CHECK_EQUAL(v1[0].copies, 1);
auto v2 = Vector(std::move(t2));
BOOST_CHECK_EQUAL(v2.size(), 1U);
BOOST_CHECK(v2[0].origin == &t2);
BOOST_CHECK_EQUAL(v2[0].copies, 0);
auto v3 = Vector(t1, std::move(t2));
BOOST_CHECK_EQUAL(v3.size(), 2U);
BOOST_CHECK(v3[0].origin == &t1);
BOOST_CHECK(v3[1].origin == &t2);
BOOST_CHECK_EQUAL(v3[0].copies, 1);
BOOST_CHECK_EQUAL(v3[1].copies, 0);
auto v4 = Vector(std::move(v3[0]), v3[1], std::move(t3));
BOOST_CHECK_EQUAL(v4.size(), 3U);
BOOST_CHECK(v4[0].origin == &t1);
BOOST_CHECK(v4[1].origin == &t2);
BOOST_CHECK(v4[2].origin == &t3);
BOOST_CHECK_EQUAL(v4[0].copies, 1);
BOOST_CHECK_EQUAL(v4[1].copies, 1);
BOOST_CHECK_EQUAL(v4[2].copies, 0);
auto v5 = Cat(v1, v4);
BOOST_CHECK_EQUAL(v5.size(), 4U);
BOOST_CHECK(v5[0].origin == &t1);
BOOST_CHECK(v5[1].origin == &t1);
BOOST_CHECK(v5[2].origin == &t2);
BOOST_CHECK(v5[3].origin == &t3);
BOOST_CHECK_EQUAL(v5[0].copies, 2);
BOOST_CHECK_EQUAL(v5[1].copies, 2);
BOOST_CHECK_EQUAL(v5[2].copies, 2);
BOOST_CHECK_EQUAL(v5[3].copies, 1);
auto v6 = Cat(std::move(v1), v3);
BOOST_CHECK_EQUAL(v6.size(), 3U);
BOOST_CHECK(v6[0].origin == &t1);
BOOST_CHECK(v6[1].origin == &t1);
BOOST_CHECK(v6[2].origin == &t2);
BOOST_CHECK_EQUAL(v6[0].copies, 1);
BOOST_CHECK_EQUAL(v6[1].copies, 2);
BOOST_CHECK_EQUAL(v6[2].copies, 1);
auto v7 = Cat(v2, std::move(v4));
BOOST_CHECK_EQUAL(v7.size(), 4U);
BOOST_CHECK(v7[0].origin == &t2);
BOOST_CHECK(v7[1].origin == &t1);
BOOST_CHECK(v7[2].origin == &t2);
BOOST_CHECK(v7[3].origin == &t3);
BOOST_CHECK_EQUAL(v7[0].copies, 1);
BOOST_CHECK_EQUAL(v7[1].copies, 1);
BOOST_CHECK_EQUAL(v7[2].copies, 1);
BOOST_CHECK_EQUAL(v7[3].copies, 0);
auto v8 = Cat(std::move(v2), std::move(v3));
BOOST_CHECK_EQUAL(v8.size(), 3U);
BOOST_CHECK(v8[0].origin == &t2);
BOOST_CHECK(v8[1].origin == &t1);
BOOST_CHECK(v8[2].origin == &t2);
BOOST_CHECK_EQUAL(v8[0].copies, 0);
BOOST_CHECK_EQUAL(v8[1].copies, 1);
BOOST_CHECK_EQUAL(v8[2].copies, 0);
}
BOOST_AUTO_TEST_CASE(message_sign) {
const std::array<uint8_t, 32> privkey_bytes = {
{// just some random data
// derived address from this private key:
// 15CRxFdyRpGZLW9w8HnHvVduizdL5jKNbs
0xD9, 0x7F, 0x51, 0x08, 0xF1, 0x1C, 0xDA, 0x6E, 0xEE, 0xBA, 0xAA,
0x42, 0x0F, 0xEF, 0x07, 0x26, 0xB1, 0xF8, 0x98, 0x06, 0x0B, 0x98,
0x48, 0x9F, 0xA3, 0x09, 0x84, 0x63, 0xC0, 0x03, 0x28, 0x66}};
const std::string message = "Trust no one";
const std::string expected_signature =
"H3CcyS18y471Ya5WTYAke25spvA5eyBMvAuNhxDkuAjsdNGX+"
"NoouNlY1KoselGvF65Werf7OdtKAoOZ9WB17Rc=";
CKey privkey;
std::string generated_signature;
BOOST_REQUIRE_MESSAGE(!privkey.IsValid(),
"Confirm the private key is invalid");
BOOST_CHECK_MESSAGE(!MessageSign(privkey, message, generated_signature),
"Sign with an invalid private key");
privkey.Set(privkey_bytes.begin(), privkey_bytes.end(), true);
BOOST_REQUIRE_MESSAGE(privkey.IsValid(),
"Confirm the private key is valid");
BOOST_CHECK_MESSAGE(MessageSign(privkey, message, generated_signature),
"Sign with a valid private key");
BOOST_CHECK_EQUAL(expected_signature, generated_signature);
}
BOOST_AUTO_TEST_CASE(message_verify) {
const auto params = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
BOOST_CHECK_EQUAL(MessageVerify(*params, "invalid address",
"signature should be irrelevant",
"message too"),
MessageVerificationResult::ERR_INVALID_ADDRESS);
BOOST_CHECK_EQUAL(
MessageVerify(*params, "3B5fQsEXEaV8v6U3ejYc8XaKXAkyQj2MjV",
"signature should be irrelevant", "message too"),
MessageVerificationResult::ERR_ADDRESS_NO_KEY);
BOOST_CHECK_EQUAL(MessageVerify(*params,
"1KqbBpLy5FARmTPD4VZnDDpYjkUvkr82Pm",
"invalid signature, not in base64 encoding",
"message should be irrelevant"),
MessageVerificationResult::ERR_MALFORMED_SIGNATURE);
BOOST_CHECK_EQUAL(
MessageVerify(*params, "1KqbBpLy5FARmTPD4VZnDDpYjkUvkr82Pm",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"message should be irrelevant"),
MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED);
BOOST_CHECK_EQUAL(
MessageVerify(*params, "15CRxFdyRpGZLW9w8HnHvVduizdL5jKNbs",
"IPojfrX2dfPnH26UegfbGQQLrdK844DlHq5157/P6h57WyuS/Qsl+h/"
"WSVGDF4MUi4rWSswW38oimDYfNNUBUOk=",
"I never signed this"),
MessageVerificationResult::ERR_NOT_SIGNED);
BOOST_CHECK_EQUAL(
MessageVerify(*params, "15CRxFdyRpGZLW9w8HnHvVduizdL5jKNbs",
"H3CcyS18y471Ya5WTYAke25spvA5eyBMvAuNhxDkuAjsdNGX+"
"NoouNlY1KoselGvF65Werf7OdtKAoOZ9WB17Rc=",
"Trust no one"),
MessageVerificationResult::OK);
BOOST_CHECK_EQUAL(
MessageVerify(*params, "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs",
"HxO6qEsMrNvPA7QTMUbdzF/uXbW78yPG6gFITses9XVvMdBULIFwrfhN"
"f196N+rxnK5eSl8eF3aKe4INunzFJXg=",
"Trust me"),
MessageVerificationResult::OK);
}
BOOST_AUTO_TEST_CASE(message_hash) {
const std::string unsigned_tx = "...";
const std::string prefixed_message =
std::string(1, (char)MESSAGE_MAGIC.length()) + MESSAGE_MAGIC +
std::string(1, (char)unsigned_tx.length()) + unsigned_tx;
const uint256 signature_hash = Hash(unsigned_tx);
const uint256 message_hash1 = Hash(prefixed_message);
const uint256 message_hash2 = MessageHash(unsigned_tx);
BOOST_CHECK_EQUAL(message_hash1, message_hash2);
BOOST_CHECK_NE(message_hash1, signature_hash);
}
BOOST_AUTO_TEST_CASE(remove_prefix) {
BOOST_CHECK_EQUAL(RemovePrefix("./util/system.h", "./"), "util/system.h");
BOOST_CHECK_EQUAL(RemovePrefixView("foo", "foo"), "");
BOOST_CHECK_EQUAL(RemovePrefix("foo", "fo"), "o");
BOOST_CHECK_EQUAL(RemovePrefixView("foo", "f"), "oo");
BOOST_CHECK_EQUAL(RemovePrefix("foo", ""), "foo");
BOOST_CHECK_EQUAL(RemovePrefixView("fo", "foo"), "fo");
BOOST_CHECK_EQUAL(RemovePrefix("f", "foo"), "f");
BOOST_CHECK_EQUAL(RemovePrefixView("", "foo"), "");
BOOST_CHECK_EQUAL(RemovePrefix("", ""), "");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index 7516446a5..a4955931d 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -1,530 +1,531 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/strencodings.h>
#include <util/string.h>
#include <tinyformat.h>
#include <cstdlib>
#include <cstring>
#include <optional>
static const std::string CHARS_ALPHA_NUM =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static const std::string SAFE_CHARS[] = {
// SAFE_CHARS_DEFAULT
CHARS_ALPHA_NUM + " .,;-_/:?@()",
// SAFE_CHARS_UA_COMMENT
CHARS_ALPHA_NUM + " .,;-_?@",
// SAFE_CHARS_FILENAME
CHARS_ALPHA_NUM + ".-_",
// SAFE_CHARS_URI
CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%",
};
std::string SanitizeString(std::string_view str, int rule) {
std::string result;
for (char c : str) {
if (SAFE_CHARS[rule].find(c) != std::string::npos) {
result.push_back(c);
}
}
return result;
}
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[(uint8_t)c];
}
bool IsHex(std::string_view str) {
for (char c : str) {
if (HexDigit(c) < 0) {
return false;
}
}
return (str.size() > 0) && (str.size() % 2 == 0);
}
bool IsHexNumber(std::string_view str) {
if (str.substr(0, 2) == "0x") {
str.remove_prefix(2);
}
for (char c : str) {
if (HexDigit(c) < 0) {
return false;
}
}
// Return false for empty string or "0x".
return str.size() > 0;
}
-std::vector<uint8_t> ParseHex(std::string_view str) {
- // convert hex dump to vector
- std::vector<uint8_t> vch;
+template <typename Byte> std::vector<Byte> ParseHex(std::string_view str) {
+ std::vector<Byte> vch;
auto it = str.begin();
while (it != str.end() && it + 1 != str.end()) {
if (IsSpace(*it)) {
++it;
continue;
}
auto c1 = HexDigit(*(it++));
auto c2 = HexDigit(*(it++));
if (c1 < 0 || c2 < 0) {
break;
}
- vch.push_back(uint8_t(c1 << 4) | c2);
+ vch.push_back(Byte(c1 << 4) | Byte(c2));
}
return vch;
}
+template std::vector<std::byte> ParseHex(std::string_view);
+template std::vector<uint8_t> ParseHex(std::string_view);
void SplitHostPort(std::string_view 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;
// if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is
// safe
bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']');
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 uint8_t> 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::optional<std::vector<uint8_t>> DecodeBase64(std::string_view str) {
static const int8_t 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};
if (str.size() % 4 != 0) {
return {};
}
/* One or two = characters at the end are permitted. */
if (str.size() >= 1 && str.back() == '=') {
str.remove_suffix(1);
}
if (str.size() >= 1 && str.back() == '=') {
str.remove_suffix(1);
}
std::vector<uint8_t> ret;
ret.reserve((str.size() * 3) / 4);
bool valid = ConvertBits<6, 8, false>(
[&](uint8_t c) { ret.push_back(c); }, str.begin(), str.end(),
[](char c) { return decode64_table[uint8_t(c)]; });
if (!valid) {
return {};
}
return ret;
}
std::string EncodeBase32(Span<const uint8_t> 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(std::string_view str, bool pad) {
return EncodeBase32(MakeUCharSpan(str), pad);
}
std::optional<std::vector<uint8_t>> DecodeBase32(std::string_view str) {
static const int8_t 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};
if (str.size() % 8 != 0) {
return {};
}
/* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
if (str.size() >= 1 && str.back() == '=') {
str.remove_suffix(1);
}
if (str.size() >= 2 && str.substr(str.size() - 2) == "==") {
str.remove_suffix(2);
}
if (str.size() >= 1 && str.back() == '=') {
str.remove_suffix(1);
}
if (str.size() >= 2 && str.substr(str.size() - 2) == "==") {
str.remove_suffix(2);
}
std::vector<uint8_t> ret;
ret.reserve((str.size() * 5) / 8);
bool valid = ConvertBits<5, 8, false>(
[&](uint8_t c) { ret.push_back(c); }, str.begin(), str.end(),
[](char c) { return decode32_table[uint8_t(c)]; });
if (!valid) {
return {};
}
return ret;
}
namespace {
template <typename T> bool ParseIntegral(std::string_view str, T *out) {
static_assert(std::is_integral<T>::value);
// Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
// handling leading +/- for backwards compatibility.
if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
return false;
}
const std::optional<T> opt_int =
ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
if (!opt_int) {
return false;
}
if (out != nullptr) {
*out = *opt_int;
}
return true;
}
}; // namespace
bool ParseInt32(std::string_view str, int32_t *out) {
return ParseIntegral<int32_t>(str, out);
}
bool ParseInt64(std::string_view str, int64_t *out) {
return ParseIntegral<int64_t>(str, out);
}
bool ParseUInt8(std::string_view str, uint8_t *out) {
return ParseIntegral<uint8_t>(str, out);
}
bool ParseUInt16(std::string_view str, uint16_t *out) {
return ParseIntegral<uint16_t>(str, out);
}
bool ParseUInt32(std::string_view str, uint32_t *out) {
return ParseIntegral<uint32_t>(str, out);
}
bool ParseUInt64(std::string_view str, uint64_t *out) {
return ParseIntegral<uint64_t>(str, out);
}
std::string FormatParagraph(std::string_view 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) {
// overflow
if (mantissa > (UPPER_BOUND / 10LL)) {
return false;
}
mantissa *= 10;
}
mantissa += ch - '0';
mantissa_tzeros = 0;
}
return true;
}
bool ParseFixedPoint(std::string_view 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)) {
// overflow
return false;
}
++ptr;
}
} else {
// missing expected digit
return false;
}
} else {
// empty string or loose '-'
return false;
}
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)) {
// overflow
return false;
}
++ptr;
++point_ofs;
}
} else {
// missing expected digit
return false;
}
}
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)) {
// overflow
return false;
}
exponent = exponent * 10 + val[ptr] - '0';
++ptr;
}
} else {
// missing expected digit
return false;
}
}
if (ptr != end) {
// trailing garbage
return false;
}
// 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) {
// cannot represent values smaller than 10^-decimals
return false;
}
if (exponent >= 18) {
// cannot represent values larger than or equal to 10^(18-decimals)
return false;
}
for (int i = 0; i < exponent; ++i) {
if (mantissa > (UPPER_BOUND / 10LL) ||
mantissa < -(UPPER_BOUND / 10LL)) {
// overflow
return false;
}
mantissa *= 10;
}
if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND) {
// overflow
return false;
}
if (amount_out) {
*amount_out = mantissa;
}
return true;
}
std::string ToLower(std::string_view str) {
std::string r;
for (auto ch : str) {
r += ToLower(ch);
}
return r;
}
std::string ToUpper(std::string_view str) {
std::string r;
for (auto ch : str) {
r += ToUpper(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;
}
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index c76af6ee4..e994b9634 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -1,329 +1,334 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/**
* Utilities for converting data from/to strings.
*/
#ifndef BITCOIN_UTIL_STRENCODINGS_H
#define BITCOIN_UTIL_STRENCODINGS_H
#include <span.h>
#include <charconv>
#include <cstdint>
#include <iterator>
#include <optional>
#include <string>
#include <vector>
/** Used by SanitizeString() */
enum SafeChars {
//! The full set of allowed chars
SAFE_CHARS_DEFAULT,
//! BIP-0014 subset
SAFE_CHARS_UA_COMMENT,
//! Chars allowed in filenames
SAFE_CHARS_FILENAME,
//! Chars allowed in URIs (RFC 3986)
SAFE_CHARS_URI,
};
/**
* 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(std::string_view str, int rule = SAFE_CHARS_DEFAULT);
-std::vector<uint8_t> ParseHex(std::string_view str);
+/**
+ * Parse the hex string into bytes (uint8_t or std::byte).
+ * Ignores whitespace.
+ */
+template <typename Byte = uint8_t>
+std::vector<Byte> ParseHex(std::string_view 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(std::string_view str);
/**
* Return true if the string is a hex number, optionally prefixed with "0x"
*/
bool IsHexNumber(std::string_view str);
std::optional<std::vector<uint8_t>> DecodeBase64(std::string_view str);
std::string EncodeBase64(Span<const uint8_t> input);
inline std::string EncodeBase64(Span<const std::byte> input) {
return EncodeBase64(MakeUCharSpan(input));
}
inline std::string EncodeBase64(std::string_view str) {
return EncodeBase64(MakeUCharSpan(str));
}
std::optional<std::vector<uint8_t>> DecodeBase32(std::string_view str);
/**
* 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 uint8_t> 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(std::string_view str, bool pad = true);
void SplitHostPort(std::string_view 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 integral type T. Leading whitespace, a leading +, or any
* trailing character fail the parsing. The required format expressed as regex
* is `-?[0-9]+`.
*
* @returns std::nullopt if the entire string could not be parsed, or if the
* parsed value is not in the range representable by the type T.
*/
template <typename T> std::optional<T> ToIntegral(std::string_view str) {
static_assert(std::is_integral<T>::value);
T result;
const auto [first_nonmatching, error_condition] =
std::from_chars(str.data(), str.data() + str.size(), result);
if (first_nonmatching != str.data() + str.size() ||
error_condition != std::errc{}) {
return std::nullopt;
}
return result;
}
/**
* 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(std::string_view 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(std::string_view 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(std::string_view 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(std::string_view 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(std::string_view 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(std::string_view str, uint64_t *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));
}
inline std::string HexStr(const Span<const std::byte> 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(std::string_view 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 |= size_t(a[i] ^ b[i % b.size()]);
}
return accumulator == 0;
}
/**
* Parse number as fixed point according to JSON number syntax.
* See http://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(std::string_view, int decimals,
int64_t *amount_out);
namespace {
/**
* Helper class for the default infn argument to ConvertBits (just returns
*the input).
*/
struct IntIdentity {
[[maybe_unused]] int operator()(int x) const { return x; }
};
} // namespace
/**
* Convert from one power-of-2 number base to another.
*
* If padding is enabled, this always return true. If not, then it returns true
* of all the bits of the input are encoded in the output.
*/
template <int frombits, int tobits, bool pad, typename O, typename It,
typename I = IntIdentity>
bool ConvertBits(O outfn, It it, It end, I infn = {}) {
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) {
int v = infn(*it);
if (v < 0) {
return false;
}
acc = ((acc << frombits) | v) & 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(std::string_view 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(std::string_view 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 // BITCOIN_UTIL_STRENCODINGS_H

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 23:58 (1 d, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5866182
Default Alt Text
(185 KB)

Event Timeline