diff --git a/src/bench/chacha_poly_aead.cpp b/src/bench/chacha_poly_aead.cpp index 95c2c3243..91fdb751d 100644 --- a/src/bench/chacha_poly_aead.cpp +++ b/src/bench/chacha_poly_aead.cpp @@ -1,123 +1,123 @@ // 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 <iostream> #include <bench/bench.h> #include <crypto/chacha_poly_aead.h> #include <crypto/poly1305.h> // for the POLY1305_TAGLEN constant #include <hash.h> #include <cassert> #include <limits> /* Number of bytes to process per iteration */ static constexpr uint64_t BUFFER_SIZE_TINY = 64; static constexpr uint64_t BUFFER_SIZE_SMALL = 256; static constexpr uint64_t BUFFER_SIZE_LARGE = 1024 * 1024; static const uint8_t k1[32] = {0}; static const uint8_t k2[32] = {0}; static ChaCha20Poly1305AEAD aead(k1, 32, k2, 32); static void CHACHA20_POLY1305_AEAD(benchmark::State &state, size_t buffersize, bool include_decryption) { std::vector<uint8_t> in( buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); std::vector<uint8_t> out( buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); uint64_t seqnr_payload = 0; uint64_t seqnr_aad = 0; int aad_pos = 0; uint32_t len = 0; while (state.KeepRunning()) { // encrypt or decrypt the buffer with a static key assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true)); if (include_decryption) { // if we decrypt, include the GetLength assert(aead.GetLength(&len, seqnr_aad, aad_pos, in.data())); assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true)); } // increase main sequence number seqnr_payload++; // increase aad position (position in AAD keystream) aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { aad_pos = 0; seqnr_aad++; } if (seqnr_payload + 1 == std::numeric_limits<uint64_t>::max()) { // reuse of nonce+key is okay while benchmarking. seqnr_payload = 0; seqnr_aad = 0; aad_pos = 0; } } } static void CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT(benchmark::State &state) { CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, false); } static void CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT(benchmark::State &state) { CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, false); } static void CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT(benchmark::State &state) { CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, false); } static void CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT(benchmark::State &state) { CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, true); } static void CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT(benchmark::State &state) { CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, true); } static void CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT(benchmark::State &state) { CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, true); } // Add Hash() (dbl-sha256) bench for comparison static void HASH(benchmark::State &state, size_t buffersize) { uint8_t hash[CHash256::OUTPUT_SIZE]; std::vector<uint8_t> in(buffersize, 0); while (state.KeepRunning()) { - CHash256().Write(in.data(), in.size()).Finalize(hash); + CHash256().Write(in).Finalize(hash); } } static void HASH_64BYTES(benchmark::State &state) { HASH(state, BUFFER_SIZE_TINY); } static void HASH_256BYTES(benchmark::State &state) { HASH(state, BUFFER_SIZE_SMALL); } static void HASH_1MB(benchmark::State &state) { HASH(state, BUFFER_SIZE_LARGE); } BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT, 500000); BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT, 250000); BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT, 340); BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT, 500000); BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT, 250000); BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT, 340); BENCHMARK(HASH_64BYTES, 500000); BENCHMARK(HASH_256BYTES, 250000); BENCHMARK(HASH_1MB, 340); diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index bfac02ff2..bb7633683 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -1,305 +1,302 @@ // Copyright (c) 2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <blockfilter.h> #include <crypto/siphash.h> #include <hash.h> #include <primitives/transaction.h> #include <script/script.h> #include <streams.h> #include <util/golombrice.h> #include <mutex> #include <sstream> /// SerType used to serialize parameters in GCS filter encoding. static constexpr int GCS_SER_TYPE = SER_NETWORK; /// Protocol version used to serialize parameters in GCS filter encoding. static constexpr int GCS_SER_VERSION = 0; static const std::map<BlockFilterType, std::string> g_filter_types = { {BlockFilterType::BASIC, "basic"}, }; // Map a value x that is uniformly distributed in the range [0, 2^64) to a // value uniformly distributed in [0, n) by returning the upper 64 bits of // x * n. // // See: // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ static uint64_t MapIntoRange(uint64_t x, uint64_t n) { #ifdef __SIZEOF_INT128__ return (static_cast<unsigned __int128>(x) * static_cast<unsigned __int128>(n)) >> 64; #else // To perform the calculation on 64-bit numbers without losing the // result to overflow, split the numbers into the most significant and // least significant 32 bits and perform multiplication piece-wise. // // See: https://stackoverflow.com/a/26855440 uint64_t x_hi = x >> 32; uint64_t x_lo = x & 0xFFFFFFFF; uint64_t n_hi = n >> 32; uint64_t n_lo = n & 0xFFFFFFFF; uint64_t ac = x_hi * n_hi; uint64_t ad = x_hi * n_lo; uint64_t bc = x_lo * n_hi; uint64_t bd = x_lo * n_lo; uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF); uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32); return upper64; #endif } uint64_t GCSFilter::HashToRange(const Element &element) const { uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1) .Write(element.data(), element.size()) .Finalize(); return MapIntoRange(hash, m_F); } std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet &elements) const { std::vector<uint64_t> hashed_elements; hashed_elements.reserve(elements.size()); for (const Element &element : elements) { hashed_elements.push_back(HashToRange(element)); } std::sort(hashed_elements.begin(), hashed_elements.end()); return hashed_elements; } GCSFilter::GCSFilter(const Params ¶ms) : m_params(params), m_N(0), m_F(0), m_encoded{0} {} GCSFilter::GCSFilter(const Params ¶ms, std::vector<uint8_t> encoded_filter) : m_params(params), m_encoded(std::move(encoded_filter)) { VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); uint64_t N = ReadCompactSize(stream); m_N = static_cast<uint32_t>(N); if (m_N != N) { throw std::ios_base::failure("N must be <2^32"); } m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M); // Verify that the encoded filter contains exactly N elements. If it has too // much or too little data, a std::ios_base::failure exception will be // raised. BitStreamReader<VectorReader> bitreader(stream); for (uint64_t i = 0; i < m_N; ++i) { GolombRiceDecode(bitreader, m_params.m_P); } if (!stream.empty()) { throw std::ios_base::failure("encoded_filter contains excess data"); } } GCSFilter::GCSFilter(const Params ¶ms, const ElementSet &elements) : m_params(params) { size_t N = elements.size(); m_N = static_cast<uint32_t>(N); if (m_N != N) { throw std::invalid_argument("N must be <2^32"); } m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M); CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); WriteCompactSize(stream, m_N); if (elements.empty()) { return; } BitStreamWriter<CVectorWriter> bitwriter(stream); uint64_t last_value = 0; for (uint64_t value : BuildHashedSet(elements)) { uint64_t delta = value - last_value; GolombRiceEncode(bitwriter, m_params.m_P, delta); last_value = value; } bitwriter.Flush(); } bool GCSFilter::MatchInternal(const uint64_t *element_hashes, size_t size) const { VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); // Seek forward by size of N uint64_t N = ReadCompactSize(stream); assert(N == m_N); BitStreamReader<VectorReader> bitreader(stream); uint64_t value = 0; size_t hashes_index = 0; for (uint32_t i = 0; i < m_N; ++i) { uint64_t delta = GolombRiceDecode(bitreader, m_params.m_P); value += delta; while (true) { if (hashes_index == size) { return false; } else if (element_hashes[hashes_index] == value) { return true; } else if (element_hashes[hashes_index] > value) { break; } hashes_index++; } } return false; } bool GCSFilter::Match(const Element &element) const { uint64_t query = HashToRange(element); return MatchInternal(&query, 1); } bool GCSFilter::MatchAny(const ElementSet &elements) const { const std::vector<uint64_t> queries = BuildHashedSet(elements); return MatchInternal(queries.data(), queries.size()); } const std::string &BlockFilterTypeName(BlockFilterType filter_type) { static std::string unknown_retval = ""; auto it = g_filter_types.find(filter_type); return it != g_filter_types.end() ? it->second : unknown_retval; } bool BlockFilterTypeByName(const std::string &name, BlockFilterType &filter_type) { for (const auto &entry : g_filter_types) { if (entry.second == name) { filter_type = entry.first; return true; } } return false; } const std::set<BlockFilterType> &AllBlockFilterTypes() { static std::set<BlockFilterType> types; static std::once_flag flag; std::call_once(flag, []() { for (auto entry : g_filter_types) { types.insert(entry.first); } }); return types; } const std::string &ListBlockFilterTypes() { static std::string type_list; static std::once_flag flag; std::call_once(flag, []() { std::stringstream ret; bool first = true; for (auto entry : g_filter_types) { if (!first) { ret << ", "; } ret << entry.second; first = false; } type_list = ret.str(); }); return type_list; } static GCSFilter::ElementSet BasicFilterElements(const CBlock &block, const CBlockUndo &block_undo) { GCSFilter::ElementSet elements; for (const CTransactionRef &tx : block.vtx) { for (const CTxOut &txout : tx->vout) { const CScript &script = txout.scriptPubKey; if (script.empty() || script[0] == OP_RETURN) { continue; } elements.emplace(script.begin(), script.end()); } } for (const CTxUndo &tx_undo : block_undo.vtxundo) { for (const Coin &prevout : tx_undo.vprevout) { const CScript &script = prevout.GetTxOut().scriptPubKey; if (script.empty()) { continue; } elements.emplace(script.begin(), script.end()); } } return elements; } BlockFilter::BlockFilter(BlockFilterType filter_type, const BlockHash &block_hash, std::vector<uint8_t> filter) : m_filter_type(filter_type), m_block_hash(block_hash) { GCSFilter::Params params; if (!BuildParams(params)) { throw std::invalid_argument("unknown filter_type"); } m_filter = GCSFilter(params, std::move(filter)); } BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock &block, const CBlockUndo &block_undo) : m_filter_type(filter_type), m_block_hash(block.GetHash()) { GCSFilter::Params params; if (!BuildParams(params)) { throw std::invalid_argument("unknown filter_type"); } m_filter = GCSFilter(params, BasicFilterElements(block, block_undo)); } bool BlockFilter::BuildParams(GCSFilter::Params ¶ms) const { switch (m_filter_type) { case BlockFilterType::BASIC: params.m_siphash_k0 = m_block_hash.GetUint64(0); params.m_siphash_k1 = m_block_hash.GetUint64(1); params.m_P = BASIC_FILTER_P; params.m_M = BASIC_FILTER_M; return true; case BlockFilterType::INVALID: return false; } return false; } uint256 BlockFilter::GetHash() const { const std::vector<uint8_t> &data = GetEncodedFilter(); uint256 result; - CHash256().Write(data.data(), data.size()).Finalize(result.begin()); + CHash256().Write(data).Finalize(result.begin()); return result; } uint256 BlockFilter::ComputeHeader(const uint256 &prev_header) const { const uint256 &filter_hash = GetHash(); uint256 result; - CHash256() - .Write(filter_hash.begin(), filter_hash.size()) - .Write(prev_header.begin(), prev_header.size()) - .Finalize(result.begin()); + CHash256().Write(filter_hash).Write(prev_header).Finalize(result.begin()); return result; } diff --git a/src/hash.h b/src/hash.h index 445b55a18..73ddff181 100644 --- a/src/hash.h +++ b/src/hash.h @@ -1,209 +1,209 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_HASH_H #define BITCOIN_HASH_H #include <crypto/common.h> #include <crypto/ripemd160.h> #include <crypto/sha256.h> #include <prevector.h> #include <serialize.h> #include <uint256.h> #include <version.h> #include <vector> typedef uint256 ChainCode; /** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */ class CHash256 { private: CSHA256 sha; public: static const size_t OUTPUT_SIZE = CSHA256::OUTPUT_SIZE; void Finalize(uint8_t hash[OUTPUT_SIZE]) { uint8_t buf[CSHA256::OUTPUT_SIZE]; sha.Finalize(buf); sha.Reset().Write(buf, CSHA256::OUTPUT_SIZE).Finalize(hash); } - CHash256 &Write(const uint8_t *data, size_t len) { - sha.Write(data, len); + CHash256 &Write(Span<const uint8_t> input) { + sha.Write(input.data(), input.size()); return *this; } CHash256 &Reset() { sha.Reset(); return *this; } }; /** A hasher class for Bitcoin's 160-bit hash (SHA-256 + RIPEMD-160). */ class CHash160 { private: CSHA256 sha; public: static const size_t OUTPUT_SIZE = CRIPEMD160::OUTPUT_SIZE; void Finalize(uint8_t hash[OUTPUT_SIZE]) { uint8_t buf[CSHA256::OUTPUT_SIZE]; sha.Finalize(buf); CRIPEMD160().Write(buf, CSHA256::OUTPUT_SIZE).Finalize(hash); } - CHash160 &Write(const uint8_t *data, size_t len) { - sha.Write(data, len); + CHash160 &Write(Span<const uint8_t> input) { + sha.Write(input.data(), input.size()); return *this; } CHash160 &Reset() { sha.Reset(); return *this; } }; /** Compute the 256-bit hash of an object. */ template <typename T1> inline uint256 Hash(const T1 pbegin, const T1 pend) { static const uint8_t pblank[1] = {}; uint256 result; CHash256() - .Write(pbegin == pend ? pblank : (const uint8_t *)&pbegin[0], - (pend - pbegin) * sizeof(pbegin[0])) + .Write({pbegin == pend ? pblank : (const uint8_t *)&pbegin[0], + (pend - pbegin) * sizeof(pbegin[0])}) .Finalize((uint8_t *)&result); return result; } /** Compute the 256-bit hash of the concatenation of two objects. */ template <typename T1, typename T2> inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end) { static const uint8_t pblank[1] = {}; uint256 result; CHash256() - .Write(p1begin == p1end ? pblank : (const uint8_t *)&p1begin[0], - (p1end - p1begin) * sizeof(p1begin[0])) - .Write(p2begin == p2end ? pblank : (const uint8_t *)&p2begin[0], - (p2end - p2begin) * sizeof(p2begin[0])) + .Write({p1begin == p1end ? pblank : (const uint8_t *)&p1begin[0], + (p1end - p1begin) * sizeof(p1begin[0])}) + .Write({p2begin == p2end ? pblank : (const uint8_t *)&p2begin[0], + (p2end - p2begin) * sizeof(p2begin[0])}) .Finalize((uint8_t *)&result); return result; } /** Compute the 160-bit hash an object. */ template <typename T1> inline uint160 Hash160(const T1 pbegin, const T1 pend) { static uint8_t pblank[1] = {}; uint160 result; CHash160() - .Write(pbegin == pend ? pblank : (const uint8_t *)&pbegin[0], - (pend - pbegin) * sizeof(pbegin[0])) + .Write({pbegin == pend ? pblank : (const uint8_t *)&pbegin[0], + (pend - pbegin) * sizeof(pbegin[0])}) .Finalize((uint8_t *)&result); return result; } /** Compute the 160-bit hash of a vector. */ inline uint160 Hash160(const std::vector<uint8_t> &vch) { return Hash160(vch.begin(), vch.end()); } /** Compute the 160-bit hash of a vector. */ template <unsigned int N> inline uint160 Hash160(const prevector<N, uint8_t> &vch) { return Hash160(vch.begin(), vch.end()); } /** A writer stream (for serialization) that computes a 256-bit hash. */ class CHashWriter { private: CHash256 ctx; const int nType; const int nVersion; public: CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) {} int GetType() const { return nType; } int GetVersion() const { return nVersion; } void write(const char *pch, size_t size) { - ctx.Write((const uint8_t *)pch, size); + ctx.Write({(const uint8_t *)pch, size}); } // invalidates the object uint256 GetHash() { uint256 result; ctx.Finalize((uint8_t *)&result); return result; } /** * Returns the first 64 bits from the resulting hash. */ inline uint64_t GetCheapHash() { uint8_t result[CHash256::OUTPUT_SIZE]; ctx.Finalize(result); return ReadLE64(result); } template <typename T> CHashWriter &operator<<(const T &obj) { // Serialize to this stream ::Serialize(*this, obj); return (*this); } }; /** * Reads data from an underlying stream, while hashing the read data. */ template <typename Source> class CHashVerifier : public CHashWriter { private: Source *source; public: explicit CHashVerifier(Source *source_) : CHashWriter(source_->GetType(), source_->GetVersion()), source(source_) {} void read(char *pch, size_t nSize) { source->read(pch, nSize); this->write(pch, nSize); } void ignore(size_t nSize) { char data[1024]; while (nSize > 0) { size_t now = std::min<size_t>(nSize, 1024); read(data, now); nSize -= now; } } template <typename T> CHashVerifier<Source> &operator>>(T &&obj) { // Unserialize from this stream ::Unserialize(*this, obj); return (*this); } }; /** Compute the 256-bit hash of an object's serialization. */ template <typename T> uint256 SerializeHash(const T &obj, int nType = SER_GETHASH, int nVersion = PROTOCOL_VERSION) { CHashWriter ss(nType, nVersion); ss << obj; return ss.GetHash(); } uint32_t MurmurHash3(uint32_t nHashSeed, const std::vector<uint8_t> &vDataToHash); void BIP32Hash(const ChainCode &chainCode, uint32_t nChild, uint8_t header, const uint8_t data[32], uint8_t output[64]); #endif // BITCOIN_HASH_H diff --git a/src/key.cpp b/src/key.cpp index dd28c0ffc..4380abd92 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,462 +1,459 @@ // 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. */ static 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. */ static 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.data(), keydata.size()); } 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 static 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, std::array<uint8_t, CPubKey::SCHNORR_SIZE> &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, sizeof(rnd)); uint256 hash; - CHash256() - .Write((uint8_t *)str.data(), str.size()) - .Write(rnd, sizeof(rnd)) - .Finalize(hash.begin()); + CHash256().Write(MakeUCharSpan(str)).Write(rnd).Finalize(hash.begin()); 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[0], &id, 4); out.nChild = _nChild; return key.Derive(out.key, out.chaincode, _nChild, chaincode); } void CExtKey::SetSeed(const uint8_t *seed, unsigned int nSeedLen) { 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, nSeedLen) .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[0], &vchFingerprint[0], 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.data(), 32); 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); } } diff --git a/src/net.cpp b/src/net.cpp index d2c08fbd3..fa122bd77 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1,3186 +1,3186 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include <config/bitcoin-config.h> #endif #include <net.h> #include <banman.h> #include <clientversion.h> #include <config.h> #include <consensus/consensus.h> #include <crypto/sha256.h> #include <netbase.h> #include <node/ui_interface.h> #include <protocol.h> #include <random.h> #include <scheduler.h> #include <util/strencodings.h> #include <util/translation.h> #ifdef WIN32 #include <cstring> #else #include <fcntl.h> #endif #ifdef USE_POLL #include <poll.h> #endif #ifdef USE_UPNP #include <miniupnpc/miniupnpc.h> #include <miniupnpc/upnpcommands.h> #include <miniupnpc/upnperrors.h> // The minimum supported miniUPnPc API version is set to 10. This keeps // compatibility with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); #endif #include <unordered_map> #include <cmath> // How often to dump addresses to peers.dat static constexpr std::chrono::minutes DUMP_PEERS_INTERVAL{15}; /** * Number of DNS seeds to query when the number of connections is low. */ static constexpr int DNSSEEDS_TO_QUERY_AT_ONCE = 3; /** * How long to delay before querying DNS seeds * * If we have more than THRESHOLD entries in addrman, then it's likely * that we got those addresses from having previously connected to the P2P * network, and that we'll be able to successfully reconnect to the P2P * network via contacting one of them. So if that's the case, spend a * little longer trying to connect to known peers before querying the * DNS seeds. */ static constexpr std::chrono::seconds DNSSEEDS_DELAY_FEW_PEERS{11}; static constexpr std::chrono::minutes DNSSEEDS_DELAY_MANY_PEERS{5}; // "many" vs "few" peers static constexpr int DNSSEEDS_DELAY_PEER_THRESHOLD = 1000; // We add a random period time (0 to 1 seconds) to feeler connections to prevent // synchronization. #define FEELER_SLEEP_WINDOW 1 // MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define // it as 0 #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif // MSG_DONTWAIT is not available on some platforms, if it doesn't exist define // it as 0 #if !defined(MSG_DONTWAIT) #define MSG_DONTWAIT 0 #endif /** Used to pass flags to the Bind() function */ enum BindFlags { BF_NONE = 0, BF_EXPLICIT = (1U << 0), BF_REPORT_ERROR = (1U << 1), }; // The set of sockets cannot be modified while waiting // The sleep time needs to be small to avoid new sockets stalling static const uint64_t SELECT_TIMEOUT_MILLISECONDS = 50; const std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; // SHA256("netgroup")[0:8] static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("localhostnonce")[0:8] static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[8:16] static const uint64_t RANDOMIZER_ID_EXTRAENTROPY = 0x94b05d41679a4ff7ULL; // // Global state variables // bool fDiscover = true; bool fListen = true; bool g_relay_txes = !DEFAULT_BLOCKSONLY; RecursiveMutex cs_mapLocalHost; std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost); static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {}; void CConnman::AddAddrFetch(const std::string &strDest) { LOCK(m_addr_fetches_mutex); m_addr_fetches.push_back(strDest); } unsigned short GetListenPort() { return (unsigned short)(gArgs.GetArg("-port", Params().GetDefaultPort())); } // find 'best' local address for a particular peer bool GetLocal(CService &addr, const CNetAddr *paddrPeer) { if (!fListen) { return false; } int nBestScore = -1; int nBestReachability = -1; { LOCK(cs_mapLocalHost); for (const auto &entry : mapLocalHost) { int nScore = entry.second.nScore; int nReachability = entry.first.GetReachabilityFrom(paddrPeer); if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { addr = CService(entry.first, entry.second.nPort); nBestReachability = nReachability; nBestScore = nScore; } } } return nBestScore >= 0; } //! Convert the pnSeed6 array into usable address objects. static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn) { // It'll only connect to one or two seed nodes because once it connects, // it'll get a pile of addresses with newer timestamps. Seed nodes are given // a random 'last seen time' of between one and two weeks ago. const int64_t nOneWeek = 7 * 24 * 60 * 60; std::vector<CAddress> vSeedsOut; vSeedsOut.reserve(vSeedsIn.size()); FastRandomContext rng; for (const auto &seed_in : vSeedsIn) { struct in6_addr ip; memcpy(&ip, seed_in.addr, sizeof(ip)); CAddress addr(CService(ip, seed_in.port), GetDesirableServiceFlags(NODE_NONE)); addr.nTime = GetTime() - rng.randrange(nOneWeek) - nOneWeek; vSeedsOut.push_back(addr); } return vSeedsOut; } // Get best local address for a particular peer as a CAddress. Otherwise, return // the unroutable 0.0.0.0 but filled in with the normal parameters, since the IP // may be changed to a useful one by discovery. CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices) { CAddress ret(CService(CNetAddr(), GetListenPort()), nLocalServices); CService addr; if (GetLocal(addr, paddrPeer)) { ret = CAddress(addr, nLocalServices); } ret.nTime = GetAdjustedTime(); return ret; } static int GetnScore(const CService &addr) { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == 0) { return 0; } return mapLocalHost[addr].nScore; } // Is our peer's addrLocal potentially useful as an external IP source? bool IsPeerAddrLocalGood(CNode *pnode) { CService addrLocal = pnode->GetAddrLocal(); return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() && IsReachable(addrLocal.GetNetwork()); } // Pushes our own address to a peer. void AdvertiseLocal(CNode *pnode) { if (fListen && pnode->fSuccessfullyConnected) { CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); if (gArgs.GetBoolArg("-addrmantest", false)) { // use IPv4 loopback during addrmantest addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices()); } // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. FastRandomContext rng; if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() || rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0)) { addrLocal.SetIP(pnode->GetAddrLocal()); } if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false)) { LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); pnode->PushAddress(addrLocal, rng); } } } // Learn a new local address. bool AddLocal(const CService &addr, int nScore) { if (!addr.IsRoutable()) { return false; } if (!fDiscover && nScore < LOCAL_MANUAL) { return false; } if (!IsReachable(addr)) { return false; } LogPrintf("AddLocal(%s,%i)\n", addr.ToString(), nScore); { LOCK(cs_mapLocalHost); bool fAlready = mapLocalHost.count(addr) > 0; LocalServiceInfo &info = mapLocalHost[addr]; if (!fAlready || nScore >= info.nScore) { info.nScore = nScore + (fAlready ? 1 : 0); info.nPort = addr.GetPort(); } } return true; } bool AddLocal(const CNetAddr &addr, int nScore) { return AddLocal(CService(addr, GetListenPort()), nScore); } void RemoveLocal(const CService &addr) { LOCK(cs_mapLocalHost); LogPrintf("RemoveLocal(%s)\n", addr.ToString()); mapLocalHost.erase(addr); } void SetReachable(enum Network net, bool reachable) { if (net == NET_UNROUTABLE || net == NET_INTERNAL) { return; } LOCK(cs_mapLocalHost); vfLimited[net] = !reachable; } bool IsReachable(enum Network net) { LOCK(cs_mapLocalHost); return !vfLimited[net]; } bool IsReachable(const CNetAddr &addr) { return IsReachable(addr.GetNetwork()); } /** vote for a local address */ bool SeenLocal(const CService &addr) { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == 0) { return false; } mapLocalHost[addr].nScore++; return true; } /** check whether a given address is potentially local */ bool IsLocal(const CService &addr) { LOCK(cs_mapLocalHost); return mapLocalHost.count(addr) > 0; } CNode *CConnman::FindNode(const CNetAddr &ip) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (static_cast<CNetAddr>(pnode->addr) == ip) { return pnode; } } return nullptr; } CNode *CConnman::FindNode(const CSubNet &subNet) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (subNet.Match(static_cast<CNetAddr>(pnode->addr))) { return pnode; } } return nullptr; } CNode *CConnman::FindNode(const std::string &addrName) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (pnode->GetAddrName() == addrName) { return pnode; } } return nullptr; } CNode *CConnman::FindNode(const CService &addr) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (static_cast<CService>(pnode->addr) == addr) { return pnode; } } return nullptr; } bool CConnman::CheckIncomingNonce(uint64_t nonce) { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (!pnode->fSuccessfullyConnected && !pnode->IsInboundConn() && pnode->GetLocalNonce() == nonce) { return false; } } return true; } /** Get the bind address for a socket as CAddress */ static CAddress GetBindAddress(SOCKET sock) { CAddress addr_bind; struct sockaddr_storage sockaddr_bind; socklen_t sockaddr_bind_len = sizeof(sockaddr_bind); if (sock != INVALID_SOCKET) { if (!getsockname(sock, (struct sockaddr *)&sockaddr_bind, &sockaddr_bind_len)) { addr_bind.SetSockAddr((const struct sockaddr *)&sockaddr_bind); } else { LogPrint(BCLog::NET, "Warning: getsockname failed\n"); } } return addr_bind; } CNode *CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type) { assert(conn_type != ConnectionType::INBOUND); if (pszDest == nullptr) { if (IsLocal(addrConnect)) { return nullptr; } // Look for an existing connection CNode *pnode = FindNode(static_cast<CService>(addrConnect)); if (pnode) { LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } } /// debug print LogPrint(BCLog::NET, "trying connection %s lastseen=%.1fhrs\n", pszDest ? pszDest : addrConnect.ToString(), pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime) / 3600.0); // Resolve const int default_port = Params().GetDefaultPort(); if (pszDest) { std::vector<CService> resolved; if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE); if (!addrConnect.IsValid()) { LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToString(), pszDest); return nullptr; } // It is possible that we already have a connection to the IP/port // pszDest resolved to. In that case, drop the connection that was // just created, and return the existing CNode instead. Also store // the name we used to connect in that CNode, so that future // FindNode() calls to that name catch this early. LOCK(cs_vNodes); CNode *pnode = FindNode(static_cast<CService>(addrConnect)); if (pnode) { pnode->MaybeSetAddrName(std::string(pszDest)); LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } } } // Connect bool connected = false; SOCKET hSocket = INVALID_SOCKET; proxyType proxy; if (addrConnect.IsValid()) { bool proxyConnectionFailed = false; if (GetProxy(addrConnect.GetNetwork(), proxy)) { hSocket = CreateSocket(proxy.proxy); if (hSocket == INVALID_SOCKET) { return nullptr; } connected = ConnectThroughProxy( proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, proxyConnectionFailed); } else { // no proxy needed (none set for target network) hSocket = CreateSocket(addrConnect); if (hSocket == INVALID_SOCKET) { return nullptr; } connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, conn_type == ConnectionType::MANUAL); } if (!proxyConnectionFailed) { // If a connection to the node was attempted, and failure (if any) // is not caused by a problem connecting to the proxy, mark this as // an attempt. addrman.Attempt(addrConnect, fCountFailure); } } else if (pszDest && GetNameProxy(proxy)) { hSocket = CreateSocket(proxy.proxy); if (hSocket == INVALID_SOCKET) { return nullptr; } std::string host; int port = default_port; SplitHostPort(std::string(pszDest), port, host); bool proxyConnectionFailed; connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, proxyConnectionFailed); } if (!connected) { CloseSocket(hSocket); return nullptr; } // Add node NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE) .Write(id) .Finalize(); uint64_t extra_entropy = GetDeterministicRandomizer(RANDOMIZER_ID_EXTRAENTROPY) .Write(id) .Finalize(); CAddress addr_bind = GetBindAddress(hSocket); CNode *pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, extra_entropy, addr_bind, pszDest ? pszDest : "", conn_type); pnode->AddRef(); // We're making a new connection, harvest entropy from the time (and our // peer count) RandAddEvent(uint32_t(id)); return pnode; } void CNode::CloseSocketDisconnect() { fDisconnect = true; LOCK(cs_hSocket); if (hSocket != INVALID_SOCKET) { LogPrint(BCLog::NET, "disconnecting peer=%d\n", id); CloseSocket(hSocket); } } void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags &flags, const CNetAddr &addr) const { for (const auto &subnet : vWhitelistedRange) { if (subnet.m_subnet.Match(addr)) { NetPermissions::AddFlag(flags, subnet.m_flags); } } } std::string CNode::GetAddrName() const { LOCK(cs_addrName); return addrName; } void CNode::MaybeSetAddrName(const std::string &addrNameIn) { LOCK(cs_addrName); if (addrName.empty()) { addrName = addrNameIn; } } CService CNode::GetAddrLocal() const { LOCK(cs_addrLocal); return addrLocal; } void CNode::SetAddrLocal(const CService &addrLocalIn) { LOCK(cs_addrLocal); if (addrLocal.IsValid()) { error("Addr local already set for node: %i. Refusing to change from %s " "to %s", id, addrLocal.ToString(), addrLocalIn.ToString()); } else { addrLocal = addrLocalIn; } } void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) { stats.nodeid = this->GetId(); stats.nServices = nServices; stats.addr = addr; stats.addrBind = addrBind; stats.m_mapped_as = addr.GetMappedAS(m_asmap); if (m_tx_relay != nullptr) { LOCK(m_tx_relay->cs_filter); stats.fRelayTxes = m_tx_relay->fRelayTxes; } else { stats.fRelayTxes = false; } stats.nLastSend = nLastSend; stats.nLastRecv = nLastRecv; stats.nTimeConnected = nTimeConnected; stats.nTimeOffset = nTimeOffset; stats.addrName = GetAddrName(); stats.nVersion = nVersion; { LOCK(cs_SubVer); stats.cleanSubVer = cleanSubVer; } stats.fInbound = IsInboundConn(); stats.m_manual_connection = IsManualConn(); stats.nStartingHeight = nStartingHeight; { LOCK(cs_vSend); stats.mapSendBytesPerMsgCmd = mapSendBytesPerMsgCmd; stats.nSendBytes = nSendBytes; } { LOCK(cs_vRecv); stats.mapRecvBytesPerMsgCmd = mapRecvBytesPerMsgCmd; stats.nRecvBytes = nRecvBytes; } stats.m_legacyWhitelisted = m_legacyWhitelisted; stats.m_permissionFlags = m_permissionFlags; if (m_tx_relay != nullptr) { LOCK(m_tx_relay->cs_feeFilter); stats.minFeeFilter = m_tx_relay->minFeeFilter; } else { stats.minFeeFilter = Amount::zero(); } // It is common for nodes with good ping times to suddenly become lagged, // due to a new block arriving or other large transfer. Merely reporting // pingtime might fool the caller into thinking the node was still // responsive, since pingtime does not update until the ping is complete, // which might take a while. So, if a ping is taking an unusually long time // in flight, the caller can immediately detect that this is happening. std::chrono::microseconds ping_wait{0}; if ((0 != nPingNonceSent) && (0 != m_ping_start.load().count())) { ping_wait = GetTime<std::chrono::microseconds>() - m_ping_start.load(); } // Raw ping time is in microseconds, but show it to user as whole seconds // (Bitcoin users should be well used to small numbers with many decimal // places by now :) stats.m_ping_usec = nPingUsecTime; stats.m_min_ping_usec = nMinPingUsecTime; stats.m_ping_wait_usec = count_microseconds(ping_wait); // Leave string empty if addrLocal invalid (not filled in yet) CService addrLocalUnlocked = GetAddrLocal(); stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : ""; } bool CNode::ReceiveMsgBytes(const Config &config, const char *pch, uint32_t nBytes, bool &complete) { complete = false; const auto time = GetTime<std::chrono::microseconds>(); LOCK(cs_vRecv); nLastRecv = std::chrono::duration_cast<std::chrono::seconds>(time).count(); nRecvBytes += nBytes; while (nBytes > 0) { // Absorb network data. int handled = m_deserializer->Read(config, pch, nBytes); if (handled < 0) { return false; } pch += handled; nBytes -= handled; if (m_deserializer->Complete()) { // decompose a transport agnostic CNetMessage from the deserializer CNetMessage msg = m_deserializer->GetMessage(config, time); // Store received bytes per message command to prevent a memory DOS, // only allow valid commands. mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(msg.m_command); if (i == mapRecvBytesPerMsgCmd.end()) { i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER); } assert(i != mapRecvBytesPerMsgCmd.end()); i->second += msg.m_raw_message_size; // push the message to the process queue, vRecvMsg.push_back(std::move(msg)); complete = true; } } return true; } void CNode::SetSendVersion(int nVersionIn) { // Send version may only be changed in the version message, and only one // version message is allowed per session. We can therefore treat this value // as const and even atomic as long as it's only used once a version message // has been successfully processed. Any attempt to set this twice is an // error. if (nSendVersion != 0) { error("Send version already set for node: %i. Refusing to change from " "%i to %i", id, nSendVersion, nVersionIn); } else { nSendVersion = nVersionIn; } } int CNode::GetSendVersion() const { // The send version should always be explicitly set to INIT_PROTO_VERSION // rather than using this value until SetSendVersion has been called. if (nSendVersion == 0) { error("Requesting unset send version for node: %i. Using %i", id, INIT_PROTO_VERSION); return INIT_PROTO_VERSION; } return nSendVersion; } int V1TransportDeserializer::readHeader(const Config &config, const char *pch, uint32_t nBytes) { // copy data to temporary parsing buffer uint32_t nRemaining = CMessageHeader::HEADER_SIZE - nHdrPos; uint32_t nCopy = std::min(nRemaining, nBytes); memcpy(&hdrbuf[nHdrPos], pch, nCopy); nHdrPos += nCopy; // if header incomplete, exit if (nHdrPos < CMessageHeader::HEADER_SIZE) { return nCopy; } // deserialize to CMessageHeader try { hdrbuf >> hdr; } catch (const std::exception &) { return -1; } // Reject oversized messages if (hdr.IsOversized(config)) { LogPrint(BCLog::NET, "Oversized header detected\n"); return -1; } // switch state to reading message data in_data = true; return nCopy; } int V1TransportDeserializer::readData(const char *pch, uint32_t nBytes) { unsigned int nRemaining = hdr.nMessageSize - nDataPos; unsigned int nCopy = std::min(nRemaining, nBytes); if (vRecv.size() < nDataPos + nCopy) { // Allocate up to 256 KiB ahead, but never more than the total message // size. vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024)); } - hasher.Write((const uint8_t *)pch, nCopy); + hasher.Write({(const uint8_t *)pch, nCopy}); memcpy(&vRecv[nDataPos], pch, nCopy); nDataPos += nCopy; return nCopy; } const uint256 &V1TransportDeserializer::GetMessageHash() const { assert(Complete()); if (data_hash.IsNull()) { hasher.Finalize(data_hash.begin()); } return data_hash; } CNetMessage V1TransportDeserializer::GetMessage(const Config &config, const std::chrono::microseconds time) { // decompose a single CNetMessage from the TransportDeserializer CNetMessage msg(std::move(vRecv)); // store state about valid header, netmagic and checksum msg.m_valid_header = hdr.IsValid(config); // FIXME Split CheckHeaderMagicAndCommand() into CheckHeaderMagic() and // CheckCommand() to prevent the net magic check code duplication. msg.m_valid_netmagic = (memcmp(std::begin(hdr.pchMessageStart), std::begin(config.GetChainParams().NetMagic()), CMessageHeader::MESSAGE_START_SIZE) == 0); uint256 hash = GetMessageHash(); // store command string, payload size msg.m_command = hdr.GetCommand(); msg.m_message_size = hdr.nMessageSize; msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE; // We just received a message off the wire, harvest entropy from the time // (and the message checksum) RandAddEvent(ReadLE32(hash.begin())); msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0); if (!msg.m_valid_checksum) { LogPrint( BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n", SanitizeString(msg.m_command), msg.m_message_size, HexStr(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE), HexStr(hdr.pchChecksum, hdr.pchChecksum + CMessageHeader::CHECKSUM_SIZE)); } // store receive time msg.m_time = time; // reset the network deserializer (prepare for the next message) Reset(); return msg; } void V1TransportSerializer::prepareForTransport(const Config &config, CSerializedNetMsg &msg, std::vector<uint8_t> &header) { // create dbl-sha256 checksum uint256 hash = Hash(msg.data.begin(), msg.data.end()); // create header CMessageHeader hdr(config.GetChainParams().NetMagic(), msg.m_type.c_str(), msg.data.size()); memcpy(hdr.pchChecksum, hash.begin(), CMessageHeader::CHECKSUM_SIZE); // serialize header header.reserve(CMessageHeader::HEADER_SIZE); CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, header, 0, hdr}; } size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_vSend) { size_t nSentSize = 0; size_t nMsgCount = 0; for (const auto &data : pnode->vSendMsg) { assert(data.size() > pnode->nSendOffset); int nBytes = 0; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { break; } nBytes = send(pnode->hSocket, reinterpret_cast<const char *>(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); } if (nBytes == 0) { // couldn't send anything at all break; } if (nBytes < 0) { // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { LogPrintf("socket send error %s\n", NetworkErrorString(nErr)); pnode->CloseSocketDisconnect(); } break; } assert(nBytes > 0); pnode->nLastSend = GetSystemTimeInSeconds(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; nSentSize += nBytes; if (pnode->nSendOffset != data.size()) { // could not send full message; stop sending more break; } pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); pnode->fPauseSend = pnode->nSendSize > nSendBufferMaxSize; nMsgCount++; } pnode->vSendMsg.erase(pnode->vSendMsg.begin(), pnode->vSendMsg.begin() + nMsgCount); if (pnode->vSendMsg.empty()) { assert(pnode->nSendOffset == 0); assert(pnode->nSendSize == 0); } return nSentSize; } struct NodeEvictionCandidate { NodeId id; int64_t nTimeConnected; int64_t nMinPingUsecTime; int64_t nLastBlockTime; int64_t nLastTXTime; bool fRelevantServices; bool fRelayTxes; bool fBloomFilter; CAddress addr; uint64_t nKeyedNetGroup; bool prefer_evict; }; static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nMinPingUsecTime > b.nMinPingUsecTime; } static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nTimeConnected > b.nTimeConnected; } static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nKeyedNetGroup < b.nKeyedNetGroup; } static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { // There is a fall-through here because it is common for a node to have many // peers which have not yet relayed a block. if (a.nLastBlockTime != b.nLastBlockTime) { return a.nLastBlockTime < b.nLastBlockTime; } if (a.fRelevantServices != b.fRelevantServices) { return b.fRelevantServices; } return a.nTimeConnected > b.nTimeConnected; } static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { // There is a fall-through here because it is common for a node to have more // than a few peers that have not yet relayed txn. if (a.nLastTXTime != b.nLastTXTime) { return a.nLastTXTime < b.nLastTXTime; } if (a.fRelayTxes != b.fRelayTxes) { return b.fRelayTxes; } if (a.fBloomFilter != b.fBloomFilter) { return a.fBloomFilter; } return a.nTimeConnected > b.nTimeConnected; } //! Sort an array by the specified comparator, then erase the last K elements. template <typename T, typename Comparator> static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, size_t k) { std::sort(elements.begin(), elements.end(), comparator); size_t eraseSize = std::min(k, elements.size()); elements.erase(elements.end() - eraseSize, elements.end()); } /** * Try to find a connection to evict when the node is full. * Extreme care must be taken to avoid opening the node to attacker triggered * network partitioning. * The strategy used here is to protect a small number of peers for each of * several distinct characteristics which are difficult to forge. In order to * partition a node the attacker must be simultaneously better at all of them * than honest peers. */ bool CConnman::AttemptToEvictConnection() { std::vector<NodeEvictionCandidate> vEvictionCandidates; { LOCK(cs_vNodes); for (const CNode *node : vNodes) { if (node->HasPermission(PF_NOBAN)) { continue; } if (!node->IsInboundConn()) { continue; } if (node->fDisconnect) { continue; } bool peer_relay_txes = false; bool peer_filter_not_null = false; if (node->m_tx_relay != nullptr) { LOCK(node->m_tx_relay->cs_filter); peer_relay_txes = node->m_tx_relay->fRelayTxes; peer_filter_not_null = node->m_tx_relay->pfilter != nullptr; } NodeEvictionCandidate candidate = { node->GetId(), node->nTimeConnected, node->nMinPingUsecTime, node->nLastBlockTime, node->nLastTXTime, HasAllDesirableServiceFlags(node->nServices), peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup, node->m_prefer_evict}; vEvictionCandidates.push_back(candidate); } } // Protect connections with certain characteristics // Deterministically select 4 peers to protect by netgroup. // An attacker cannot predict which netgroups will be protected EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4); // Protect the 8 nodes with the lowest minimum ping time. // An attacker cannot manipulate this metric without physically moving nodes // closer to the target. EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8); // Protect 4 nodes that most recently sent us transactions. // An attacker cannot manipulate this metric without performing useful work. EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4); // Protect 4 nodes that most recently sent us blocks. // An attacker cannot manipulate this metric without performing useful work. EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4); // Protect the half of the remaining nodes which have been connected the // longest. This replicates the non-eviction implicit behavior, and // precludes attacks that start later. EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2); if (vEvictionCandidates.empty()) { return false; } // If any remaining peers are preferred for eviction consider only them. // This happens after the other preferences since if a peer is really the // best by other criteria (esp relaying blocks) // then we probably don't want to evict it no matter what. if (std::any_of( vEvictionCandidates.begin(), vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.prefer_evict; })) { vEvictionCandidates.erase( std::remove_if( vEvictionCandidates.begin(), vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return !n.prefer_evict; }), vEvictionCandidates.end()); } // Identify the network group with the most connections and youngest member. // (vEvictionCandidates is already sorted by reverse connect time) uint64_t naMostConnections; unsigned int nMostConnections = 0; int64_t nMostConnectionsTime = 0; std::map<uint64_t, std::vector<NodeEvictionCandidate>> mapNetGroupNodes; for (const NodeEvictionCandidate &node : vEvictionCandidates) { std::vector<NodeEvictionCandidate> &group = mapNetGroupNodes[node.nKeyedNetGroup]; group.push_back(node); int64_t grouptime = group[0].nTimeConnected; size_t group_size = group.size(); if (group_size > nMostConnections || (group_size == nMostConnections && grouptime > nMostConnectionsTime)) { nMostConnections = group_size; nMostConnectionsTime = grouptime; naMostConnections = node.nKeyedNetGroup; } } // Reduce to the network group with the most connections vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (pnode->GetId() == evicted) { pnode->fDisconnect = true; return true; } } return false; } void CConnman::AcceptConnection(const ListenSocket &hListenSocket) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr *)&sockaddr, &len); CAddress addr; int nInbound = 0; int nMaxInbound = nMaxConnections - m_max_outbound; if (hSocket != INVALID_SOCKET) { if (!addr.SetSockAddr((const struct sockaddr *)&sockaddr)) { LogPrintf("Warning: Unknown socket family\n"); } } NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE; hListenSocket.AddSocketPermissionFlags(permissionFlags); AddWhitelistPermissionFlags(permissionFlags, addr); bool legacyWhitelisted = false; if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) { NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT); if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY); } if (gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) { NetPermissions::AddFlag(permissionFlags, PF_RELAY); } NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL); NetPermissions::AddFlag(permissionFlags, PF_NOBAN); legacyWhitelisted = true; } { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (pnode->IsInboundConn()) { nInbound++; } } } if (hSocket == INVALID_SOCKET) { int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK) { LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); } return; } if (!fNetworkActive) { LogPrintf("connection from %s dropped: not accepting new connections\n", addr.ToString()); CloseSocket(hSocket); return; } if (!IsSelectableSocket(hSocket)) { LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString()); CloseSocket(hSocket); return; } // According to the internet TCP_NODELAY is not carried into accepted // sockets on all platforms. Set it again here just to be sure. SetSocketNoDelay(hSocket); // Don't accept connections from banned peers. bool banned = m_banman && m_banman->IsBanned(addr); if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && banned) { LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString()); CloseSocket(hSocket); return; } // Only accept connections from discouraged peers if our inbound slots // aren't (almost) full. bool discouraged = m_banman && m_banman->IsDiscouraged(addr); if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && nInbound + 1 >= nMaxInbound && discouraged) { LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToString()); CloseSocket(hSocket); return; } if (nInbound >= nMaxInbound) { if (!AttemptToEvictConnection()) { // No connection to evict, disconnect the new connection LogPrint(BCLog::NET, "failed to find an eviction candidate - " "connection dropped (full)\n"); CloseSocket(hSocket); return; } } NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE) .Write(id) .Finalize(); uint64_t extra_entropy = GetDeterministicRandomizer(RANDOMIZER_ID_EXTRAENTROPY) .Write(id) .Finalize(); CAddress addr_bind = GetBindAddress(hSocket); ServiceFlags nodeServices = nLocalServices; if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) { nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM); } CNode *pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, extra_entropy, addr_bind, "", ConnectionType::INBOUND); pnode->AddRef(); pnode->m_permissionFlags = permissionFlags; // If this flag is present, the user probably expect that RPC and QT report // it as whitelisted (backward compatibility) pnode->m_legacyWhitelisted = legacyWhitelisted; pnode->m_prefer_evict = discouraged; m_msgproc->InitializeNode(*config, pnode); LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); { LOCK(cs_vNodes); vNodes.push_back(pnode); } // We received a new connection, harvest entropy from the time (and our peer // count) RandAddEvent(uint32_t(id)); } void CConnman::DisconnectNodes() { { LOCK(cs_vNodes); if (!fNetworkActive) { // Disconnect any connected nodes for (CNode *pnode : vNodes) { if (!pnode->fDisconnect) { LogPrint(BCLog::NET, "Network not active, dropping peer=%d\n", pnode->GetId()); pnode->fDisconnect = true; } } } // Disconnect unused nodes std::vector<CNode *> vNodesCopy = vNodes; for (CNode *pnode : vNodesCopy) { if (pnode->fDisconnect) { // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); // release outbound grant (if any) pnode->grantOutbound.Release(); // close socket and cleanup pnode->CloseSocketDisconnect(); // hold in disconnected pool until all refs are released pnode->Release(); vNodesDisconnected.push_back(pnode); } } } { // Delete disconnected nodes std::list<CNode *> vNodesDisconnectedCopy = vNodesDisconnected; for (CNode *pnode : vNodesDisconnectedCopy) { // wait until threads are done using it if (pnode->GetRefCount() <= 0) { bool fDelete = false; { TRY_LOCK(pnode->cs_inventory, lockInv); if (lockInv) { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) { fDelete = true; } } } if (fDelete) { vNodesDisconnected.remove(pnode); DeleteNode(pnode); } } } } } void CConnman::NotifyNumConnectionsChanged() { size_t vNodesSize; { LOCK(cs_vNodes); vNodesSize = vNodes.size(); } if (vNodesSize != nPrevNodeCount) { nPrevNodeCount = vNodesSize; if (clientInterface) { clientInterface->NotifyNumConnectionsChanged(vNodesSize); } } } void CConnman::InactivityCheck(CNode *pnode) { int64_t nTime = GetSystemTimeInSeconds(); if (nTime - pnode->nTimeConnected > m_peer_connect_timeout) { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) { LogPrint(BCLog::NET, "socket no message in first %i seconds, %d %d from %d\n", m_peer_connect_timeout, pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId()); pnode->fDisconnect = true; } else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) { LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend); pnode->fDisconnect = true; } else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90 * 60)) { LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv); pnode->fDisconnect = true; } else if (pnode->nPingNonceSent && pnode->m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL} < GetTime<std::chrono::microseconds>()) { LogPrintf("ping timeout: %fs\n", 0.000001 * count_microseconds( GetTime<std::chrono::microseconds>() - pnode->m_ping_start.load())); pnode->fDisconnect = true; } else if (!pnode->fSuccessfullyConnected) { LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId()); pnode->fDisconnect = true; } } } bool CConnman::GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set) { for (const ListenSocket &hListenSocket : vhListenSocket) { recv_set.insert(hListenSocket.socket); } { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { // Implement the following logic: // * If there is data to send, select() for sending data. As this // only happens when optimistic write failed, we choose to first // drain the write buffer in this case before receiving more. This // avoids needlessly queueing received data, if the remote peer is // not themselves receiving data. This means properly utilizing // TCP flow control signalling. // * Otherwise, if there is space left in the receive buffer, // select() for receiving data. // * Hand off all complete messages to the processor, to be handled // without blocking here. bool select_recv = !pnode->fPauseRecv; bool select_send; { LOCK(pnode->cs_vSend); select_send = !pnode->vSendMsg.empty(); } LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { continue; } error_set.insert(pnode->hSocket); if (select_send) { send_set.insert(pnode->hSocket); continue; } if (select_recv) { recv_set.insert(pnode->hSocket); } } } return !recv_set.empty() || !send_set.empty() || !error_set.empty(); } #ifdef USE_POLL void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set) { std::set<SOCKET> recv_select_set, send_select_set, error_select_set; if (!GenerateSelectSet(recv_select_set, send_select_set, error_select_set)) { interruptNet.sleep_for( std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); return; } std::unordered_map<SOCKET, struct pollfd> pollfds; for (SOCKET socket_id : recv_select_set) { pollfds[socket_id].fd = socket_id; pollfds[socket_id].events |= POLLIN; } for (SOCKET socket_id : send_select_set) { pollfds[socket_id].fd = socket_id; pollfds[socket_id].events |= POLLOUT; } for (SOCKET socket_id : error_select_set) { pollfds[socket_id].fd = socket_id; // These flags are ignored, but we set them for clarity pollfds[socket_id].events |= POLLERR | POLLHUP; } std::vector<struct pollfd> vpollfds; vpollfds.reserve(pollfds.size()); for (auto it : pollfds) { vpollfds.push_back(std::move(it.second)); } if (poll(vpollfds.data(), vpollfds.size(), SELECT_TIMEOUT_MILLISECONDS) < 0) { return; } if (interruptNet) { return; } for (struct pollfd pollfd_entry : vpollfds) { if (pollfd_entry.revents & POLLIN) { recv_set.insert(pollfd_entry.fd); } if (pollfd_entry.revents & POLLOUT) { send_set.insert(pollfd_entry.fd); } if (pollfd_entry.revents & (POLLERR | POLLHUP)) { error_set.insert(pollfd_entry.fd); } } } #else void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set) { std::set<SOCKET> recv_select_set, send_select_set, error_select_set; if (!GenerateSelectSet(recv_select_set, send_select_set, error_select_set)) { interruptNet.sleep_for( std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); return; } // // Find which sockets have data to receive // struct timeval timeout; timeout.tv_sec = 0; // frequency to poll pnode->vSend timeout.tv_usec = SELECT_TIMEOUT_MILLISECONDS * 1000; fd_set fdsetRecv; fd_set fdsetSend; fd_set fdsetError; FD_ZERO(&fdsetRecv); FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); SOCKET hSocketMax = 0; for (SOCKET hSocket : recv_select_set) { FD_SET(hSocket, &fdsetRecv); hSocketMax = std::max(hSocketMax, hSocket); } for (SOCKET hSocket : send_select_set) { FD_SET(hSocket, &fdsetSend); hSocketMax = std::max(hSocketMax, hSocket); } for (SOCKET hSocket : error_select_set) { FD_SET(hSocket, &fdsetError); hSocketMax = std::max(hSocketMax, hSocket); } int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); if (interruptNet) { return; } if (nSelect == SOCKET_ERROR) { int nErr = WSAGetLastError(); LogPrintf("socket select error %s\n", NetworkErrorString(nErr)); for (unsigned int i = 0; i <= hSocketMax; i++) { FD_SET(i, &fdsetRecv); } FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); if (!interruptNet.sleep_for( std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS))) { return; } } for (SOCKET hSocket : recv_select_set) { if (FD_ISSET(hSocket, &fdsetRecv)) { recv_set.insert(hSocket); } } for (SOCKET hSocket : send_select_set) { if (FD_ISSET(hSocket, &fdsetSend)) { send_set.insert(hSocket); } } for (SOCKET hSocket : error_select_set) { if (FD_ISSET(hSocket, &fdsetError)) { error_set.insert(hSocket); } } } #endif void CConnman::SocketHandler() { std::set<SOCKET> recv_set, send_set, error_set; SocketEvents(recv_set, send_set, error_set); if (interruptNet) { return; } // // Accept new connections // for (const ListenSocket &hListenSocket : vhListenSocket) { if (hListenSocket.socket != INVALID_SOCKET && recv_set.count(hListenSocket.socket) > 0) { AcceptConnection(hListenSocket); } } // // Service each socket // std::vector<CNode *> vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; for (CNode *pnode : vNodesCopy) { pnode->AddRef(); } } for (CNode *pnode : vNodesCopy) { if (interruptNet) { return; } // // Receive // bool recvSet = false; bool sendSet = false; bool errorSet = false; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { continue; } recvSet = recv_set.count(pnode->hSocket) > 0; sendSet = send_set.count(pnode->hSocket) > 0; errorSet = error_set.count(pnode->hSocket) > 0; } if (recvSet || errorSet) { // typical socket buffer is 8K-64K char pchBuf[0x10000]; int32_t nBytes = 0; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { continue; } nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); } if (nBytes > 0) { bool notify = false; if (!pnode->ReceiveMsgBytes(*config, pchBuf, nBytes, notify)) { pnode->CloseSocketDisconnect(); } RecordBytesRecv(nBytes); if (notify) { size_t nSizeAdded = 0; auto it(pnode->vRecvMsg.begin()); for (; it != pnode->vRecvMsg.end(); ++it) { // vRecvMsg contains only completed CNetMessage // the single possible partially deserialized message // are held by TransportDeserializer nSizeAdded += it->m_raw_message_size; } { LOCK(pnode->cs_vProcessMsg); pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it); pnode->nProcessQueueSize += nSizeAdded; pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize; } WakeMessageHandler(); } } else if (nBytes == 0) { // socket closed gracefully if (!pnode->fDisconnect) { LogPrint(BCLog::NET, "socket closed for peer=%d\n", pnode->GetId()); } pnode->CloseSocketDisconnect(); } else if (nBytes < 0) { // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { if (!pnode->fDisconnect) { LogPrint(BCLog::NET, "socket recv error for peer=%d: %s\n", pnode->GetId(), NetworkErrorString(nErr)); } pnode->CloseSocketDisconnect(); } } } // // Send // if (sendSet) { LOCK(pnode->cs_vSend); size_t nBytes = SocketSendData(pnode); if (nBytes) { RecordBytesSent(nBytes); } } InactivityCheck(pnode); } { LOCK(cs_vNodes); for (CNode *pnode : vNodesCopy) { pnode->Release(); } } } void CConnman::ThreadSocketHandler() { while (!interruptNet) { DisconnectNodes(); NotifyNumConnectionsChanged(); SocketHandler(); } } void CConnman::WakeMessageHandler() { { LOCK(mutexMsgProc); fMsgProcWake = true; } condMsgProc.notify_one(); } #ifdef USE_UPNP static CThreadInterrupt g_upnp_interrupt; static std::thread g_upnp_thread; static void ThreadMapPort() { std::string port = strprintf("%u", GetListenPort()); const char *multicastif = nullptr; const char *minissdpdpath = nullptr; struct UPNPDev *devlist = nullptr; char lanaddr[64]; int error = 0; #if MINIUPNPC_API_VERSION < 14 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); #else devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); #endif struct UPNPUrls urls; struct IGDdatas data; int r; r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); if (r == 1) { if (fDiscover) { char externalIPAddress[40]; r = UPNP_GetExternalIPAddress( urls.controlURL, data.first.servicetype, externalIPAddress); if (r != UPNPCOMMAND_SUCCESS) { LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); } else { if (externalIPAddress[0]) { CNetAddr resolved; if (LookupHost(externalIPAddress, resolved, false)) { LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString()); AddLocal(resolved, LOCAL_UPNP); } } else { LogPrintf("UPnP: GetExternalIPAddress failed.\n"); } } } std::string strDesc = PACKAGE_NAME " " + FormatFullVersion(); do { r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); if (r != UPNPCOMMAND_SUCCESS) { LogPrintf( "AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); } else { LogPrintf("UPnP Port Mapping successful.\n"); } } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); freeUPNPDevlist(devlist); devlist = nullptr; FreeUPNPUrls(&urls); } else { LogPrintf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); devlist = nullptr; if (r != 0) { FreeUPNPUrls(&urls); } } } void StartMapPort() { if (!g_upnp_thread.joinable()) { assert(!g_upnp_interrupt); g_upnp_thread = std::thread( (std::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort))); } } void InterruptMapPort() { if (g_upnp_thread.joinable()) { g_upnp_interrupt(); } } void StopMapPort() { if (g_upnp_thread.joinable()) { g_upnp_thread.join(); g_upnp_interrupt.reset(); } } #else void StartMapPort() { // Intentionally left blank. } void InterruptMapPort() { // Intentionally left blank. } void StopMapPort() { // Intentionally left blank. } #endif void CConnman::ThreadDNSAddressSeed() { FastRandomContext rng; std::vector<std::string> seeds = config->GetChainParams().DNSSeeds(); Shuffle(seeds.begin(), seeds.end(), rng); // Number of seeds left before testing if we have enough connections int seeds_right_now = 0; int found = 0; if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) { // When -forcednsseed is provided, query all. seeds_right_now = seeds.size(); } else if (addrman.size() == 0) { // If we have no known peers, query all. // This will occur on the first run, or if peers.dat has been // deleted. seeds_right_now = seeds.size(); } // goal: only query DNS seed if address need is acute // * If we have a reasonable number of peers in addrman, spend // some time trying them first. This improves user privacy by // creating fewer identifying DNS requests, reduces trust by // giving seeds less influence on the network topology, and // reduces traffic to the seeds. // * When querying DNS seeds query a few at once, this ensures // that we don't give DNS seeds the ability to eclipse nodes // that query them. // * If we continue having problems, eventually query all the // DNS seeds, and if that fails too, also try the fixed seeds. // (done in ThreadOpenConnections) const std::chrono::seconds seeds_wait_time = (addrman.size() >= DNSSEEDS_DELAY_PEER_THRESHOLD ? DNSSEEDS_DELAY_MANY_PEERS : DNSSEEDS_DELAY_FEW_PEERS); for (const std::string &seed : seeds) { if (seeds_right_now == 0) { seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE; if (addrman.size() > 0) { LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count()); std::chrono::seconds to_wait = seeds_wait_time; while (to_wait.count() > 0) { // if sleeping for the MANY_PEERS interval, wake up // early to see if we have enough peers and can stop // this thread entirely freeing up its resources std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait); if (!interruptNet.sleep_for(w)) { return; } to_wait -= w; int nRelevant = 0; { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (pnode->fSuccessfullyConnected && pnode->IsOutboundOrBlockRelayConn()) { ++nRelevant; } } } if (nRelevant >= 2) { if (found > 0) { LogPrintf("%d addresses found from DNS seeds\n", found); LogPrintf( "P2P peers available. Finished DNS seeding.\n"); } else { LogPrintf( "P2P peers available. Skipped DNS seeding.\n"); } return; } } } } if (interruptNet) { return; } // hold off on querying seeds if P2P network deactivated if (!fNetworkActive) { LogPrintf("Waiting for network to be reactivated before querying " "DNS seeds.\n"); do { if (!interruptNet.sleep_for(std::chrono::seconds{1})) { return; } } while (!fNetworkActive); } LogPrintf("Loading addresses from DNS seed %s\n", seed); if (HaveNameProxy()) { AddAddrFetch(seed); } else { std::vector<CNetAddr> vIPs; std::vector<CAddress> vAdd; ServiceFlags requiredServiceBits = GetDesirableServiceFlags(NODE_NONE); std::string host = strprintf("x%x.%s", requiredServiceBits, seed); CNetAddr resolveSource; if (!resolveSource.SetInternal(host)) { continue; } // Limits number of IPs learned from a DNS seed unsigned int nMaxIPs = 256; if (LookupHost(host, vIPs, nMaxIPs, true)) { for (const CNetAddr &ip : vIPs) { int nOneDay = 24 * 3600; CAddress addr = CAddress( CService(ip, config->GetChainParams().GetDefaultPort()), requiredServiceBits); // Use a random age between 3 and 7 days old. addr.nTime = GetTime() - 3 * nOneDay - rng.randrange(4 * nOneDay); vAdd.push_back(addr); found++; } addrman.Add(vAdd, resolveSource); } else { // We now avoid directly using results from DNS Seeds which do // not support service bit filtering, instead using them as a // addrfetch to get nodes with our desired service bits. AddAddrFetch(seed); } } --seeds_right_now; } LogPrintf("%d addresses found from DNS seeds\n", found); } void CConnman::DumpAddresses() { int64_t nStart = GetTimeMillis(); CAddrDB adb(config->GetChainParams()); adb.Write(addrman); LogPrint(BCLog::NET, "Flushed %d addresses to peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); } void CConnman::ProcessAddrFetch() { std::string strDest; { LOCK(m_addr_fetches_mutex); if (m_addr_fetches.empty()) { return; } strDest = m_addr_fetches.front(); m_addr_fetches.pop_front(); } CAddress addr; CSemaphoreGrant grant(*semOutbound, true); if (grant) { OpenNetworkConnection(addr, false, &grant, strDest.c_str(), ConnectionType::ADDR_FETCH); } } bool CConnman::GetTryNewOutboundPeer() { return m_try_another_outbound_peer; } void CConnman::SetTryNewOutboundPeer(bool flag) { m_try_another_outbound_peer = flag; LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false"); } // Return the number of peers we have over our outbound connection limit. // Exclude peers that are marked for disconnect, or are going to be disconnected // soon (eg one-shots and feelers). // Also exclude peers that haven't finished initial connection handshake yet (so // that we don't decide we're over our desired connection limit, and then evict // some peer that has finished the handshake). int CConnman::GetExtraOutboundCount() { int nOutbound = 0; { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsOutboundOrBlockRelayConn()) { ++nOutbound; } } } return std::max( nOutbound - m_max_outbound_full_relay - m_max_outbound_block_relay, 0); } void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) { // Connect to specific addresses if (!connect.empty()) { for (int64_t nLoop = 0;; nLoop++) { ProcessAddrFetch(); for (const std::string &strAddr : connect) { CAddress addr(CService(), NODE_NONE); OpenNetworkConnection(addr, false, nullptr, strAddr.c_str(), ConnectionType::MANUAL); for (int i = 0; i < 10 && i < nLoop; i++) { if (!interruptNet.sleep_for( std::chrono::milliseconds(500))) { return; } } } if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) { return; } } } // Initiate network connections int64_t nStart = GetTime(); // Minimum time before next feeler connection (in microseconds). int64_t nNextFeeler = PoissonNextSend(nStart * 1000 * 1000, FEELER_INTERVAL); while (!interruptNet) { ProcessAddrFetch(); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) { return; } CSemaphoreGrant grant(*semOutbound); if (interruptNet) { return; } // Add seed nodes if DNS seeds are all down (an infrastructure attack?). // Note that we only do this if we started with an empty peers.dat, // (in which case we will query DNS seeds immediately) *and* the DNS // seeds have not returned any results. if (addrman.size() == 0 && (GetTime() - nStart > 60)) { static bool done = false; if (!done) { LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be " "available.\n"); CNetAddr local; local.SetInternal("fixedseeds"); addrman.Add(convertSeed6(config->GetChainParams().FixedSeeds()), local); done = true; } } // // Choose an address to connect to based on most recently seen // CAddress addrConnect; // Only connect out to one peer per network group (/16 for IPv4). int nOutboundFullRelay = 0; int nOutboundBlockRelay = 0; std::set<std::vector<uint8_t>> setConnected; { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (pnode->IsFullOutboundConn()) { nOutboundFullRelay++; } if (pnode->IsBlockOnlyConn()) { nOutboundBlockRelay++; } // Netgroups for inbound and manual peers are not excluded // because our goal here is to not use multiple of our // limited outbound slots on a single netgroup but inbound // and manual peers do not use our outbound slots. Inbound // peers also have the added issue that they could be attacker // controlled and could be used to prevent us from connecting // to particular hosts if we used them here. switch (pnode->m_conn_type) { case ConnectionType::INBOUND: case ConnectionType::MANUAL: break; case ConnectionType::OUTBOUND: case ConnectionType::BLOCK_RELAY: case ConnectionType::ADDR_FETCH: case ConnectionType::FEELER: setConnected.insert( pnode->addr.GetGroup(addrman.m_asmap)); } } } // Feeler Connections // // Design goals: // * Increase the number of connectable addresses in the tried table. // // Method: // * Choose a random address from new and attempt to connect to it if // we can connect successfully it is added to tried. // * Start attempting feeler connections only after node finishes // making outbound connections. // * Only make a feeler connection once every few minutes. // bool fFeeler = false; if (nOutboundFullRelay >= m_max_outbound_full_relay && nOutboundBlockRelay >= m_max_outbound_block_relay && !GetTryNewOutboundPeer()) { // The current time right now (in microseconds). int64_t nTime = GetTimeMicros(); if (nTime > nNextFeeler) { nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); fFeeler = true; } else { continue; } } addrman.ResolveCollisions(); int64_t nANow = GetAdjustedTime(); int nTries = 0; while (!interruptNet) { CAddrInfo addr = addrman.SelectTriedCollision(); // SelectTriedCollision returns an invalid address if it is empty. if (!fFeeler || !addr.IsValid()) { addr = addrman.Select(fFeeler); } // Require outbound connections, other than feelers, to be to // distinct network groups if (!fFeeler && setConnected.count(addr.GetGroup(addrman.m_asmap))) { break; } // if we selected an invalid or local address, restart if (!addr.IsValid() || IsLocal(addr)) { break; } // If we didn't find an appropriate destination after trying 100 // addresses fetched from addrman, stop this loop, and let the outer // loop run again (which sleeps, adds seed nodes, recalculates // already-connected network ranges, ...) before trying new addrman // addresses. nTries++; if (nTries > 100) { break; } if (!IsReachable(addr)) { continue; } // only consider very recently tried nodes after 30 failed attempts if (nANow - addr.nLastTry < 600 && nTries < 30) { continue; } // for non-feelers, require all the services we'll want, // for feelers, only require they be a full node (only because most // SPV clients don't have a good address DB available) if (!fFeeler && !HasAllDesirableServiceFlags(addr.nServices)) { continue; } if (fFeeler && !MayHaveUsefulAddressDB(addr.nServices)) { continue; } // do not allow non-default ports, unless after 50 invalid addresses // selected already. if (addr.GetPort() != config->GetChainParams().GetDefaultPort() && nTries < 50) { continue; } addrConnect = addr; break; } if (addrConnect.IsValid()) { if (fFeeler) { // Add small amount of random noise before connection to avoid // synchronization. int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000); if (!interruptNet.sleep_for( std::chrono::milliseconds(randsleep))) { return; } LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString()); } ConnectionType conn_type; // Determine what type of connection to open. If fFeeler is not // set, open OUTBOUND connections until we meet our full-relay // capacity. Then open BLOCK_RELAY connections until we hit our // block-relay peer limit. Otherwise, default to opening an // OUTBOUND connection. if (fFeeler) { conn_type = ConnectionType::FEELER; } else if (nOutboundFullRelay < m_max_outbound_full_relay) { conn_type = ConnectionType::OUTBOUND; } else if (nOutboundBlockRelay < m_max_outbound_block_relay) { conn_type = ConnectionType::BLOCK_RELAY; } else { // GetTryNewOutboundPeer() is true conn_type = ConnectionType::OUTBOUND; } OpenNetworkConnection(addrConnect, int(setConnected.size()) >= std::min(nMaxConnections - 1, 2), &grant, nullptr, conn_type); } } } std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() { std::vector<AddedNodeInfo> ret; std::list<std::string> lAddresses(0); { LOCK(cs_vAddedNodes); ret.reserve(vAddedNodes.size()); std::copy(vAddedNodes.cbegin(), vAddedNodes.cend(), std::back_inserter(lAddresses)); } // Build a map of all already connected addresses (by IP:port and by name) // to inbound/outbound and resolved CService std::map<CService, bool> mapConnected; std::map<std::string, std::pair<bool, CService>> mapConnectedByName; { LOCK(cs_vNodes); for (const CNode *pnode : vNodes) { if (pnode->addr.IsValid()) { mapConnected[pnode->addr] = pnode->IsInboundConn(); } std::string addrName = pnode->GetAddrName(); if (!addrName.empty()) { mapConnectedByName[std::move(addrName)] = std::make_pair(pnode->IsInboundConn(), static_cast<const CService &>(pnode->addr)); } } } for (const std::string &strAddNode : lAddresses) { CService service(LookupNumeric(strAddNode, Params().GetDefaultPort())); AddedNodeInfo addedNode{strAddNode, CService(), false, false}; if (service.IsValid()) { // strAddNode is an IP:port auto it = mapConnected.find(service); if (it != mapConnected.end()) { addedNode.resolvedAddress = service; addedNode.fConnected = true; addedNode.fInbound = it->second; } } else { // strAddNode is a name auto it = mapConnectedByName.find(strAddNode); if (it != mapConnectedByName.end()) { addedNode.resolvedAddress = it->second.second; addedNode.fConnected = true; addedNode.fInbound = it->second.first; } } ret.emplace_back(std::move(addedNode)); } return ret; } void CConnman::ThreadOpenAddedConnections() { while (true) { CSemaphoreGrant grant(*semAddnode); std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo(); bool tried = false; for (const AddedNodeInfo &info : vInfo) { if (!info.fConnected) { if (!grant.TryAcquire()) { // If we've used up our semaphore and need a new one, let's // not wait here since while we are waiting the // addednodeinfo state might change. break; } tried = true; CAddress addr(CService(), NODE_NONE); OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), ConnectionType::MANUAL); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) { return; } } } // Retry every 60 seconds if a connection was attempted, otherwise two // seconds. if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2))) { return; } } } // If successful, this moves the passed grant to the constructed node. void CConnman::OpenNetworkConnection(const CAddress &addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, ConnectionType conn_type) { assert(conn_type != ConnectionType::INBOUND); // // Initiate outbound network connection // if (interruptNet) { return; } if (!fNetworkActive) { return; } if (!pszDest) { bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect)); if (IsLocal(addrConnect) || FindNode(static_cast<CNetAddr>(addrConnect)) || banned_or_discouraged || FindNode(addrConnect.ToStringIPPort())) { return; } } else if (FindNode(std::string(pszDest))) { return; } CNode *pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type); if (!pnode) { return; } if (grantOutbound) { grantOutbound->MoveTo(pnode->grantOutbound); } m_msgproc->InitializeNode(*config, pnode); { LOCK(cs_vNodes); vNodes.push_back(pnode); } } void CConnman::ThreadMessageHandler() { while (!flagInterruptMsgProc) { std::vector<CNode *> vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; for (CNode *pnode : vNodesCopy) { pnode->AddRef(); } } bool fMoreWork = false; for (CNode *pnode : vNodesCopy) { if (pnode->fDisconnect) { continue; } // Receive messages bool fMoreNodeWork = m_msgproc->ProcessMessages( *config, pnode, flagInterruptMsgProc); fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) { return; } // Send messages { LOCK(pnode->cs_sendProcessing); m_msgproc->SendMessages(*config, pnode, flagInterruptMsgProc); } if (flagInterruptMsgProc) { return; } } { LOCK(cs_vNodes); for (CNode *pnode : vNodesCopy) { pnode->Release(); } } WAIT_LOCK(mutexMsgProc, lock); if (!fMoreWork) { condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this]() EXCLUSIVE_LOCKS_REQUIRED( mutexMsgProc) { return fMsgProcWake; }); } fMsgProcWake = false; } } bool CConnman::BindListenPort(const CService &addrBind, bilingual_str &strError, NetPermissionFlags permissions) { int nOne = 1; // Create socket for listening for incoming connections struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); if (!addrBind.GetSockAddr((struct sockaddr *)&sockaddr, &len)) { strError = strprintf( Untranslated("Error: Bind address family for %s not supported"), addrBind.ToString()); LogPrintf("%s\n", strError.original); return false; } SOCKET hListenSocket = CreateSocket(addrBind); if (hListenSocket == INVALID_SOCKET) { strError = strprintf(Untranslated("Error: Couldn't open socket for incoming " "connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError.original); return false; } // Allow binding if the port is still in TIME_WAIT state after // the program was closed and restarted. setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)); // Some systems don't have IPV6_V6ONLY but are always v6only; others do have // the option and enable it by default or not. Try to enable it, if // possible. if (addrBind.IsIPv6()) { #ifdef IPV6_V6ONLY setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)); #endif #ifdef WIN32 int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (sockopt_arg_type)&nProtLevel, sizeof(int)); #endif } if (::bind(hListenSocket, (struct sockaddr *)&sockaddr, len) == SOCKET_ERROR) { int nErr = WSAGetLastError(); if (nErr == WSAEADDRINUSE) { strError = strprintf(_("Unable to bind to %s on this computer. %s " "is probably already running."), addrBind.ToString(), PACKAGE_NAME); } else { strError = strprintf(_("Unable to bind to %s on this computer " "(bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); } LogPrintf("%s\n", strError.original); CloseSocket(hListenSocket); return false; } LogPrintf("Bound to %s\n", addrBind.ToString()); // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) { strError = strprintf(_("Error: Listening for incoming connections " "failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError.original); CloseSocket(hListenSocket); return false; } vhListenSocket.push_back(ListenSocket(hListenSocket, permissions)); if (addrBind.IsRoutable() && fDiscover && (permissions & PF_NOBAN) == 0) { AddLocal(addrBind, LOCAL_BIND); } return true; } void Discover() { if (!fDiscover) { return; } #ifdef WIN32 // Get local host IP char pszHostName[256] = ""; if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { std::vector<CNetAddr> vaddr; if (LookupHost(pszHostName, vaddr, 0, true)) { for (const CNetAddr &addr : vaddr) { if (AddLocal(addr, LOCAL_IF)) { LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToString()); } } } } #elif (HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS) // Get local host ip struct ifaddrs *myaddrs; if (getifaddrs(&myaddrs) == 0) { for (struct ifaddrs *ifa = myaddrs; ifa != nullptr; ifa = ifa->ifa_next) { if (ifa->ifa_addr == nullptr || (ifa->ifa_flags & IFF_UP) == 0 || strcmp(ifa->ifa_name, "lo") == 0 || strcmp(ifa->ifa_name, "lo0") == 0) { continue; } if (ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *s4 = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr); CNetAddr addr(s4->sin_addr); if (AddLocal(addr, LOCAL_IF)) { LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); } } else if (ifa->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6 *s6 = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr); CNetAddr addr(s6->sin6_addr); if (AddLocal(addr, LOCAL_IF)) { LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); } } } freeifaddrs(myaddrs); } #endif } void CConnman::SetNetworkActive(bool active) { LogPrint(BCLog::NET, "SetNetworkActive: %s\n", active); if (fNetworkActive == active) { return; } fNetworkActive = active; uiInterface.NotifyNetworkActiveChanged(fNetworkActive); } CConnman::CConnman(const Config &configIn, uint64_t nSeed0In, uint64_t nSeed1In) : config(&configIn), nSeed0(nSeed0In), nSeed1(nSeed1In) { SetTryNewOutboundPeer(false); Options connOptions; Init(connOptions); } NodeId CConnman::GetNewNodeId() { return nLastNodeId.fetch_add(1); } bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) { if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) { return false; } bilingual_str strError; if (!BindListenPort(addr, strError, permissions)) { if ((flags & BF_REPORT_ERROR) && clientInterface) { clientInterface->ThreadSafeMessageBox( strError, "", CClientUIInterface::MSG_ERROR); } return false; } return true; } bool CConnman::InitBinds( const std::vector<CService> &binds, const std::vector<NetWhitebindPermissions> &whiteBinds) { bool fBound = false; for (const auto &addrBind : binds) { fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::PF_NONE); } for (const auto &addrBind : whiteBinds) { fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags); } if (binds.empty() && whiteBinds.empty()) { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT; fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE); fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE); } return fBound; } bool CConnman::Start(CScheduler &scheduler, const Options &connOptions) { Init(connOptions); { LOCK(cs_totalBytesRecv); nTotalBytesRecv = 0; } { LOCK(cs_totalBytesSent); nTotalBytesSent = 0; nMaxOutboundTotalBytesSentInCycle = 0; nMaxOutboundCycleStartTime = 0; } if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( _("Failed to listen on any port. Use -listen=0 if you want " "this."), "", CClientUIInterface::MSG_ERROR); } return false; } for (const auto &strDest : connOptions.vSeedNodes) { AddAddrFetch(strDest); } if (clientInterface) { clientInterface->InitMessage(_("Loading P2P addresses...").translated); } // Load addresses from peers.dat int64_t nStart = GetTimeMillis(); { CAddrDB adb(config->GetChainParams()); if (adb.Read(addrman)) { LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); } else { // Addrman can be in an inconsistent state after failure, reset it addrman.Clear(); LogPrintf("Invalid or missing peers.dat; recreating\n"); DumpAddresses(); } } uiInterface.InitMessage(_("Starting network threads...").translated); fAddressesInitialized = true; if (semOutbound == nullptr) { // initialize semaphore semOutbound = std::make_unique<CSemaphore>( std::min(m_max_outbound, nMaxConnections)); } if (semAddnode == nullptr) { // initialize semaphore semAddnode = std::make_unique<CSemaphore>(nMaxAddnode); } // // Start threads // assert(m_msgproc); InterruptSocks5(false); interruptNet.reset(); flagInterruptMsgProc = false; { LOCK(mutexMsgProc); fMsgProcWake = false; } // Send and receive from sockets, accept connections threadSocketHandler = std::thread( &TraceThread<std::function<void()>>, "net", std::function<void()>(std::bind(&CConnman::ThreadSocketHandler, this))); if (!gArgs.GetBoolArg("-dnsseed", true)) { LogPrintf("DNS seeding disabled\n"); } else { threadDNSAddressSeed = std::thread(&TraceThread<std::function<void()>>, "dnsseed", std::function<void()>( std::bind(&CConnman::ThreadDNSAddressSeed, this))); } // Initiate manual connections threadOpenAddedConnections = std::thread(&TraceThread<std::function<void()>>, "addcon", std::function<void()>(std::bind( &CConnman::ThreadOpenAddedConnections, this))); if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( _("Cannot provide specific connections and have addrman find " "outgoing connections at the same."), "", CClientUIInterface::MSG_ERROR); } return false; } if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty()) { threadOpenConnections = std::thread(&TraceThread<std::function<void()>>, "opencon", std::function<void()>( std::bind(&CConnman::ThreadOpenConnections, this, connOptions.m_specified_outgoing))); } // Process messages threadMessageHandler = std::thread(&TraceThread<std::function<void()>>, "msghand", std::function<void()>( std::bind(&CConnman::ThreadMessageHandler, this))); // Dump network addresses scheduler.scheduleEvery( [this]() { this->DumpAddresses(); return true; }, DUMP_PEERS_INTERVAL); return true; } class CNetCleanup { public: CNetCleanup() {} ~CNetCleanup() { #ifdef WIN32 // Shutdown Windows Sockets WSACleanup(); #endif } }; static CNetCleanup instance_of_cnetcleanup; void CConnman::Interrupt() { { LOCK(mutexMsgProc); flagInterruptMsgProc = true; } condMsgProc.notify_all(); interruptNet(); InterruptSocks5(true); if (semOutbound) { for (int i = 0; i < m_max_outbound; i++) { semOutbound->post(); } } if (semAddnode) { for (int i = 0; i < nMaxAddnode; i++) { semAddnode->post(); } } } void CConnman::StopThreads() { if (threadMessageHandler.joinable()) { threadMessageHandler.join(); } if (threadOpenConnections.joinable()) { threadOpenConnections.join(); } if (threadOpenAddedConnections.joinable()) { threadOpenAddedConnections.join(); } if (threadDNSAddressSeed.joinable()) { threadDNSAddressSeed.join(); } if (threadSocketHandler.joinable()) { threadSocketHandler.join(); } } void CConnman::StopNodes() { if (fAddressesInitialized) { DumpAddresses(); fAddressesInitialized = false; } // Close sockets LOCK(cs_vNodes); for (CNode *pnode : vNodes) { pnode->CloseSocketDisconnect(); } for (ListenSocket &hListenSocket : vhListenSocket) { if (hListenSocket.socket != INVALID_SOCKET) { if (!CloseSocket(hListenSocket.socket)) { LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); } } } // clean up some globals (to help leak detection) for (CNode *pnode : vNodes) { DeleteNode(pnode); } for (CNode *pnode : vNodesDisconnected) { DeleteNode(pnode); } vNodes.clear(); vNodesDisconnected.clear(); vhListenSocket.clear(); semOutbound.reset(); semAddnode.reset(); } void CConnman::DeleteNode(CNode *pnode) { assert(pnode); bool fUpdateConnectionTime = false; m_msgproc->FinalizeNode(*config, pnode->GetId(), fUpdateConnectionTime); if (fUpdateConnectionTime) { addrman.Connected(pnode->addr); } delete pnode; } CConnman::~CConnman() { Interrupt(); Stop(); } void CConnman::SetServices(const CService &addr, ServiceFlags nServices) { addrman.SetServices(addr, nServices); } void CConnman::MarkAddressGood(const CAddress &addr) { addrman.Good(addr); } void CConnman::AddNewAddresses(const std::vector<CAddress> &vAddr, const CAddress &addrFrom, int64_t nTimePenalty) { addrman.Add(vAddr, addrFrom, nTimePenalty); } std::vector<CAddress> CConnman::GetAddresses() { return addrman.GetAddr(); } bool CConnman::AddNode(const std::string &strNode) { LOCK(cs_vAddedNodes); for (const std::string &it : vAddedNodes) { if (strNode == it) { return false; } } vAddedNodes.push_back(strNode); return true; } bool CConnman::RemoveAddedNode(const std::string &strNode) { LOCK(cs_vAddedNodes); for (std::vector<std::string>::iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { if (strNode == *it) { vAddedNodes.erase(it); return true; } } return false; } size_t CConnman::GetNodeCount(NumConnections flags) { LOCK(cs_vNodes); // Shortcut if we want total if (flags == CConnman::CONNECTIONS_ALL) { return vNodes.size(); } int nNum = 0; for (const auto &pnode : vNodes) { if (flags & (pnode->IsInboundConn() ? CONNECTIONS_IN : CONNECTIONS_OUT)) { nNum++; } } return nNum; } void CConnman::GetNodeStats(std::vector<CNodeStats> &vstats) { vstats.clear(); LOCK(cs_vNodes); vstats.reserve(vNodes.size()); for (CNode *pnode : vNodes) { vstats.emplace_back(); pnode->copyStats(vstats.back(), addrman.m_asmap); } } bool CConnman::DisconnectNode(const std::string &strNode) { LOCK(cs_vNodes); if (CNode *pnode = FindNode(strNode)) { pnode->fDisconnect = true; return true; } return false; } bool CConnman::DisconnectNode(const CSubNet &subnet) { bool disconnected = false; LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (subnet.Match(pnode->addr)) { pnode->fDisconnect = true; disconnected = true; } } return disconnected; } bool CConnman::DisconnectNode(const CNetAddr &addr) { return DisconnectNode(CSubNet(addr)); } bool CConnman::DisconnectNode(NodeId id) { LOCK(cs_vNodes); for (CNode *pnode : vNodes) { if (id == pnode->GetId()) { pnode->fDisconnect = true; return true; } } return false; } void CConnman::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); nTotalBytesRecv += bytes; } void CConnman::RecordBytesSent(uint64_t bytes) { LOCK(cs_totalBytesSent); nTotalBytesSent += bytes; uint64_t now = GetTime(); if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now) { // timeframe expired, reset cycle nMaxOutboundCycleStartTime = now; nMaxOutboundTotalBytesSentInCycle = 0; } // TODO, exclude whitebind peers nMaxOutboundTotalBytesSentInCycle += bytes; } void CConnman::SetMaxOutboundTarget(uint64_t limit) { LOCK(cs_totalBytesSent); nMaxOutboundLimit = limit; } uint64_t CConnman::GetMaxOutboundTarget() { LOCK(cs_totalBytesSent); return nMaxOutboundLimit; } uint64_t CConnman::GetMaxOutboundTimeframe() { LOCK(cs_totalBytesSent); return nMaxOutboundTimeframe; } uint64_t CConnman::GetMaxOutboundTimeLeftInCycle() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) { return 0; } if (nMaxOutboundCycleStartTime == 0) { return nMaxOutboundTimeframe; } uint64_t cycleEndTime = nMaxOutboundCycleStartTime + nMaxOutboundTimeframe; uint64_t now = GetTime(); return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); } void CConnman::SetMaxOutboundTimeframe(uint64_t timeframe) { LOCK(cs_totalBytesSent); if (nMaxOutboundTimeframe != timeframe) { // reset measure-cycle in case of changing the timeframe. nMaxOutboundCycleStartTime = GetTime(); } nMaxOutboundTimeframe = timeframe; } bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) { return false; } if (historicalBlockServingLimit) { // keep a large enough buffer to at least relay each block once. uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); uint64_t buffer = timeLeftInCycle / 600 * ONE_MEGABYTE; if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer) { return true; } } else if (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) { return true; } return false; } uint64_t CConnman::GetOutboundTargetBytesLeft() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) { return 0; } return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; } uint64_t CConnman::GetTotalBytesRecv() { LOCK(cs_totalBytesRecv); return nTotalBytesRecv; } uint64_t CConnman::GetTotalBytesSent() { LOCK(cs_totalBytesSent); return nTotalBytesSent; } ServiceFlags CConnman::GetLocalServices() const { return nLocalServices; } void CConnman::SetBestHeight(int height) { nBestHeight.store(height, std::memory_order_release); } int CConnman::GetBestHeight() const { return nBestHeight.load(std::memory_order_acquire); } unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; } CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, uint64_t nLocalExtraEntropyIn, const CAddress &addrBindIn, const std::string &addrNameIn, ConnectionType conn_type_in) : nTimeConnected(GetSystemTimeInSeconds()), addr(addrIn), addrBind(addrBindIn), nKeyedNetGroup(nKeyedNetGroupIn), // Don't relay addr messages to peers that we connect to as // block-relay-only peers (to prevent adversaries from inferring these // links from addr traffic). id(idIn), nLocalHostNonce(nLocalHostNonceIn), nLocalExtraEntropy(nLocalExtraEntropyIn), m_conn_type(conn_type_in), nLocalServices(nLocalServicesIn), nMyStartingHeight(nMyStartingHeightIn) { hSocket = hSocketIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; hashContinue = BlockHash(); if (conn_type_in != ConnectionType::BLOCK_RELAY) { m_tx_relay = std::make_unique<TxRelay>(); m_addr_known = std::make_unique<CRollingBloomFilter>(5000, 0.001); } for (const std::string &msg : getAllNetMessageTypes()) { mapRecvBytesPerMsgCmd[msg] = 0; } mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0; if (fLogIPs) { LogPrint(BCLog::NET, "Added connection to %s peer=%d\n", addrName, id); } else { LogPrint(BCLog::NET, "Added connection peer=%d\n", id); } m_deserializer = std::make_unique<V1TransportDeserializer>( V1TransportDeserializer(GetConfig().GetChainParams().NetMagic(), SER_NETWORK, INIT_PROTO_VERSION)); m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer()); } CNode::~CNode() { CloseSocket(hSocket); } bool CConnman::NodeFullyConnected(const CNode *pnode) { return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect; } void CConnman::PushMessage(CNode *pnode, CSerializedNetMsg &&msg) { size_t nMessageSize = msg.data.size(); LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.m_type), nMessageSize, pnode->GetId()); // make sure we use the appropriate network transport format std::vector<uint8_t> serializedHeader; pnode->m_serializer->prepareForTransport(*config, msg, serializedHeader); size_t nTotalSize = nMessageSize + serializedHeader.size(); size_t nBytesSent = 0; { LOCK(pnode->cs_vSend); bool optimisticSend(pnode->vSendMsg.empty()); // log total amount of bytes per message type pnode->mapSendBytesPerMsgCmd[msg.m_type] += nTotalSize; pnode->nSendSize += nTotalSize; if (pnode->nSendSize > nSendBufferMaxSize) { pnode->fPauseSend = true; } pnode->vSendMsg.push_back(std::move(serializedHeader)); if (nMessageSize) { pnode->vSendMsg.push_back(std::move(msg.data)); } // If write queue empty, attempt "optimistic write" if (optimisticSend == true) { nBytesSent = SocketSendData(pnode); } } if (nBytesSent) { RecordBytesSent(nBytesSent); } } bool CConnman::ForNode(NodeId id, std::function<bool(CNode *pnode)> func) { CNode *found = nullptr; LOCK(cs_vNodes); for (auto &&pnode : vNodes) { if (pnode->GetId() == id) { found = pnode; break; } } return found != nullptr && NodeFullyConnected(found) && func(found); } int64_t CConnman::PoissonNextSendInbound(int64_t now, int average_interval_seconds) { if (m_next_send_inv_to_incoming < now) { // If this function were called from multiple threads simultaneously // it would be possible that both update the next send variable, and // return a different result to their caller. This is not possible in // practice as only the net processing thread invokes this function. m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval_seconds); } return m_next_send_inv_to_incoming; } int64_t PoissonNextSend(int64_t now, int average_interval_seconds) { return now + int64_t(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); } CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const { return CSipHasher(nSeed0, nSeed1).Write(id); } uint64_t CConnman::CalculateKeyedNetGroup(const CAddress &ad) const { std::vector<uint8_t> vchNetGroup(ad.GetGroup(addrman.m_asmap)); return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP) .Write(vchNetGroup.data(), vchNetGroup.size()) .Finalize(); } /** * This function convert MaxBlockSize from byte to * MB with a decimal precision one digit rounded down * E.g. * 1660000 -> 1.6 * 2010000 -> 2.0 * 1000000 -> 1.0 * 230000 -> 0.2 * 50000 -> 0.0 * * NB behavior for EB<1MB not standardized yet still * the function applies the same algo used for * EB greater or equal to 1MB */ std::string getSubVersionEB(uint64_t MaxBlockSize) { // Prepare EB string we are going to add to SubVer: // 1) translate from byte to MB and convert to string // 2) limit the EB string to the first decimal digit (floored) std::stringstream ebMBs; ebMBs << (MaxBlockSize / (ONE_MEGABYTE / 10)); std::string eb = ebMBs.str(); eb.insert(eb.size() - 1, ".", 1); if (eb.substr(0, 1) == ".") { eb = "0" + eb; } return eb; } std::string userAgent(const Config &config) { // format excessive blocksize value std::string eb = getSubVersionEB(config.GetMaxBlockSize()); std::vector<std::string> uacomments; uacomments.push_back("EB" + eb); // Comments are checked for char compliance at startup, it is safe to add // them to the user agent string for (const std::string &cmt : gArgs.GetArgs("-uacomment")) { uacomments.push_back(cmt); } // Size compliance is checked at startup, it is safe to not check it again std::string subversion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); return subversion; } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index e142c79a8..e4bcfefcd 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,1899 +1,1895 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017-2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <script/interpreter.h> #include <crypto/ripemd160.h> #include <crypto/sha1.h> #include <crypto/sha256.h> #include <pubkey.h> #include <script/bitfield.h> #include <script/script.h> #include <script/sigencoding.h> #include <uint256.h> #include <util/bitmanip.h> bool CastToBool(const valtype &vch) { for (size_t i = 0; i < vch.size(); i++) { if (vch[i] != 0) { // Can be negative zero if (i == vch.size() - 1 && vch[i] == 0x80) { return false; } return true; } } return false; } /** * Script is a stack machine (like Forth) that evaluates a predicate * returning a bool indicating valid or not. There are no loops. */ #define stacktop(i) (stack.at(stack.size() + (i))) #define altstacktop(i) (altstack.at(altstack.size() + (i))) static inline void popstack(std::vector<valtype> &stack) { if (stack.empty()) { throw std::runtime_error("popstack(): stack empty"); } stack.pop_back(); } int FindAndDelete(CScript &script, const CScript &b) { int nFound = 0; if (b.empty()) { return nFound; } CScript result; CScript::const_iterator pc = script.begin(), pc2 = script.begin(), end = script.end(); opcodetype opcode; do { result.insert(result.end(), pc2, pc); while (static_cast<size_t>(end - pc) >= b.size() && std::equal(b.begin(), b.end(), pc)) { pc = pc + b.size(); ++nFound; } pc2 = pc; } while (script.GetOp(pc, opcode)); if (nFound > 0) { result.insert(result.end(), pc2, end); script = std::move(result); } return nFound; } static void CleanupScriptCode(CScript &scriptCode, const std::vector<uint8_t> &vchSig, uint32_t flags) { // Drop the signature in scripts when SIGHASH_FORKID is not used. SigHashType sigHashType = GetHashType(vchSig); if (!(flags & SCRIPT_ENABLE_SIGHASH_FORKID) || !sigHashType.hasForkId()) { FindAndDelete(scriptCode, CScript() << vchSig); } } static bool IsOpcodeDisabled(opcodetype opcode, uint32_t flags) { switch (opcode) { case OP_INVERT: case OP_2MUL: case OP_2DIV: case OP_MUL: case OP_LSHIFT: case OP_RSHIFT: // Disabled opcodes. return true; default: break; } return false; } namespace { /** * A data type to abstract out the condition stack during script execution. * * Conceptually it acts like a vector of booleans, one for each level of nested * IF/THEN/ELSE, indicating whether we're in the active or inactive branch of * each. * * The elements on the stack cannot be observed individually; we only need to * expose whether the stack is empty and whether or not any false values are * present at all. To implement OP_ELSE, a toggle_top modifier is added, which * flips the last value without returning it. * * This uses an optimized implementation that does not materialize the * actual stack. Instead, it just stores the size of the would-be stack, * and the position of the first false value in it. */ class ConditionStack { private: //! A constant for m_first_false_pos to indicate there are no falses. static constexpr uint32_t NO_FALSE = std::numeric_limits<uint32_t>::max(); //! The size of the implied stack. uint32_t m_stack_size = 0; //! The position of the first false value on the implied stack, or NO_FALSE //! if all true. uint32_t m_first_false_pos = NO_FALSE; public: bool empty() { return m_stack_size == 0; } bool all_true() { return m_first_false_pos == NO_FALSE; } void push_back(bool f) { if (m_first_false_pos == NO_FALSE && !f) { // The stack consists of all true values, and a false is added. // The first false value will appear at the current size. m_first_false_pos = m_stack_size; } ++m_stack_size; } void pop_back() { assert(m_stack_size > 0); --m_stack_size; if (m_first_false_pos == m_stack_size) { // When popping off the first false value, everything becomes true. m_first_false_pos = NO_FALSE; } } void toggle_top() { assert(m_stack_size > 0); if (m_first_false_pos == NO_FALSE) { // The current stack is all true values; the first false will be the // top. m_first_false_pos = m_stack_size - 1; } else if (m_first_false_pos == m_stack_size - 1) { // The top is the first false value; toggling it will make // everything true. m_first_false_pos = NO_FALSE; } else { // There is a false value, but not on top. No action is needed as // toggling anything but the first false value is unobservable. } } }; } // namespace /** * Helper for OP_CHECKSIG and OP_CHECKSIGVERIFY * * A return value of false means the script fails entirely. When true is * returned, the fSuccess variable indicates whether the signature check itself * succeeded. */ static bool EvalChecksig(const valtype &vchSig, const valtype &vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, uint32_t flags, const BaseSignatureChecker &checker, ScriptExecutionMetrics &metrics, ScriptError *serror, bool &fSuccess) { if (!CheckTransactionSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { // serror is set return false; } if (vchSig.size()) { // Subset of script starting at the most recent // codeseparator CScript scriptCode(pbegincodehash, pend); // Remove signature for pre-fork scripts CleanupScriptCode(scriptCode, vchSig, flags); fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, flags); metrics.nSigChecks += 1; if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL)) { return set_error(serror, ScriptError::SIG_NULLFAIL); } } return true; } bool EvalScript(std::vector<valtype> &stack, const CScript &script, uint32_t flags, const BaseSignatureChecker &checker, ScriptExecutionMetrics &metrics, ScriptError *serror) { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); static const valtype vchFalse(0); static const valtype vchTrue(1, 1); CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); CScript::const_iterator pbegincodehash = script.begin(); opcodetype opcode; valtype vchPushValue; ConditionStack vfExec; std::vector<valtype> altstack; set_error(serror, ScriptError::UNKNOWN); if (script.size() > MAX_SCRIPT_SIZE) { return set_error(serror, ScriptError::SCRIPT_SIZE); } int nOpCount = 0; bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0; try { while (pc < pend) { bool fExec = vfExec.all_true(); // // Read instruction // if (!script.GetOp(pc, opcode, vchPushValue)) { return set_error(serror, ScriptError::BAD_OPCODE); } if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE) { return set_error(serror, ScriptError::PUSH_SIZE); } // Note how OP_RESERVED does not count towards the opcode limit. if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) { return set_error(serror, ScriptError::OP_COUNT); } // Some opcodes are disabled (CVE-2010-5137). if (IsOpcodeDisabled(opcode, flags)) { return set_error(serror, ScriptError::DISABLED_OPCODE); } if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { return set_error(serror, ScriptError::MINIMALDATA); } stack.push_back(vchPushValue); } else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) { switch (opcode) { // // Push value // case OP_1NEGATE: case OP_1: case OP_2: case OP_3: case OP_4: case OP_5: case OP_6: case OP_7: case OP_8: case OP_9: case OP_10: case OP_11: case OP_12: case OP_13: case OP_14: case OP_15: case OP_16: { // ( -- value) CScriptNum bn((int)opcode - (int)(OP_1 - 1)); stack.push_back(bn.getvch()); // The result of these opcodes should always be the // minimal way to push the data they push, so no need // for a CheckMinimalPush here. } break; // // Control // case OP_NOP: break; case OP_CHECKLOCKTIMEVERIFY: { if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { break; } if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } // Note that elsewhere numeric opcodes are limited to // operands in the range -2**31+1 to 2**31-1, however it // is legal for opcodes to produce results exceeding // that range. This limitation is implemented by // CScriptNum's default 4-byte limit. // // If we kept to that limit we'd have a year 2038 // problem, even though the nLockTime field in // transactions themselves is uint32 which only becomes // meaningless after the year 2106. // // Thus as a special case we tell CScriptNum to accept // up to 5-byte bignums, which are good until 2**39-1, // well beyond the 2**32-1 limit of the nLockTime field // itself. const CScriptNum nLockTime(stacktop(-1), fRequireMinimal, 5); // In the rare event that the argument may be < 0 due to // some arithmetic being done first, you can always use // 0 MAX CHECKLOCKTIMEVERIFY. if (nLockTime < 0) { return set_error(serror, ScriptError::NEGATIVE_LOCKTIME); } // Actually compare the specified lock time with the // transaction. if (!checker.CheckLockTime(nLockTime)) { return set_error(serror, ScriptError::UNSATISFIED_LOCKTIME); } break; } case OP_CHECKSEQUENCEVERIFY: { if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) { break; } if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } // nSequence, like nLockTime, is a 32-bit unsigned // integer field. See the comment in CHECKLOCKTIMEVERIFY // regarding 5-byte numeric operands. const CScriptNum nSequence(stacktop(-1), fRequireMinimal, 5); // In the rare event that the argument may be < 0 due to // some arithmetic being done first, you can always use // 0 MAX CHECKSEQUENCEVERIFY. if (nSequence < 0) { return set_error(serror, ScriptError::NEGATIVE_LOCKTIME); } // To provide for future soft-fork extensibility, if the // operand has the disabled lock-time flag set, // CHECKSEQUENCEVERIFY behaves as a NOP. if ((nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) { break; } // Compare the specified sequence number with the input. if (!checker.CheckSequence(nSequence)) { return set_error(serror, ScriptError::UNSATISFIED_LOCKTIME); } break; } case OP_NOP1: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { return set_error( serror, ScriptError::DISCOURAGE_UPGRADABLE_NOPS); } } break; case OP_IF: case OP_NOTIF: { // <expression> if [statements] [else [statements]] // endif bool fValue = false; if (fExec) { if (stack.size() < 1) { return set_error( serror, ScriptError::UNBALANCED_CONDITIONAL); } valtype &vch = stacktop(-1); if (flags & SCRIPT_VERIFY_MINIMALIF) { if (vch.size() > 1) { return set_error(serror, ScriptError::MINIMALIF); } if (vch.size() == 1 && vch[0] != 1) { return set_error(serror, ScriptError::MINIMALIF); } } fValue = CastToBool(vch); if (opcode == OP_NOTIF) { fValue = !fValue; } popstack(stack); } vfExec.push_back(fValue); } break; case OP_ELSE: { if (vfExec.empty()) { return set_error( serror, ScriptError::UNBALANCED_CONDITIONAL); } vfExec.toggle_top(); } break; case OP_ENDIF: { if (vfExec.empty()) { return set_error( serror, ScriptError::UNBALANCED_CONDITIONAL); } vfExec.pop_back(); } break; case OP_VERIFY: { // (true -- ) or // (false -- false) and return if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } bool fValue = CastToBool(stacktop(-1)); if (fValue) { popstack(stack); } else { return set_error(serror, ScriptError::VERIFY); } } break; case OP_RETURN: { return set_error(serror, ScriptError::OP_RETURN); } break; // // Stack ops // case OP_TOALTSTACK: { if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } altstack.push_back(stacktop(-1)); popstack(stack); } break; case OP_FROMALTSTACK: { if (altstack.size() < 1) { return set_error( serror, ScriptError::INVALID_ALTSTACK_OPERATION); } stack.push_back(altstacktop(-1)); popstack(altstack); } break; case OP_2DROP: { // (x1 x2 -- ) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } popstack(stack); popstack(stack); } break; case OP_2DUP: { // (x1 x2 -- x1 x2 x1 x2) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch1 = stacktop(-2); valtype vch2 = stacktop(-1); stack.push_back(vch1); stack.push_back(vch2); } break; case OP_3DUP: { // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) if (stack.size() < 3) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch1 = stacktop(-3); valtype vch2 = stacktop(-2); valtype vch3 = stacktop(-1); stack.push_back(vch1); stack.push_back(vch2); stack.push_back(vch3); } break; case OP_2OVER: { // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) if (stack.size() < 4) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch1 = stacktop(-4); valtype vch2 = stacktop(-3); stack.push_back(vch1); stack.push_back(vch2); } break; case OP_2ROT: { // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) if (stack.size() < 6) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch1 = stacktop(-6); valtype vch2 = stacktop(-5); stack.erase(stack.end() - 6, stack.end() - 4); stack.push_back(vch1); stack.push_back(vch2); } break; case OP_2SWAP: { // (x1 x2 x3 x4 -- x3 x4 x1 x2) if (stack.size() < 4) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } swap(stacktop(-4), stacktop(-2)); swap(stacktop(-3), stacktop(-1)); } break; case OP_IFDUP: { // (x - 0 | x x) if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch = stacktop(-1); if (CastToBool(vch)) { stack.push_back(vch); } } break; case OP_DEPTH: { // -- stacksize CScriptNum bn(stack.size()); stack.push_back(bn.getvch()); } break; case OP_DROP: { // (x -- ) if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } popstack(stack); } break; case OP_DUP: { // (x -- x x) if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch = stacktop(-1); stack.push_back(vch); } break; case OP_NIP: { // (x1 x2 -- x2) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } stack.erase(stack.end() - 2); } break; case OP_OVER: { // (x1 x2 -- x1 x2 x1) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch = stacktop(-2); stack.push_back(vch); } break; case OP_PICK: case OP_ROLL: { // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } int n = CScriptNum(stacktop(-1), fRequireMinimal).getint(); popstack(stack); if (n < 0 || n >= (int)stack.size()) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch = stacktop(-n - 1); if (opcode == OP_ROLL) { stack.erase(stack.end() - n - 1); } stack.push_back(vch); } break; case OP_ROT: { // (x1 x2 x3 -- x2 x3 x1) // x2 x1 x3 after first swap // x2 x3 x1 after second swap if (stack.size() < 3) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } swap(stacktop(-3), stacktop(-2)); swap(stacktop(-2), stacktop(-1)); } break; case OP_SWAP: { // (x1 x2 -- x2 x1) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } swap(stacktop(-2), stacktop(-1)); } break; case OP_TUCK: { // (x1 x2 -- x2 x1 x2) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype vch = stacktop(-1); stack.insert(stack.end() - 2, vch); } break; case OP_SIZE: { // (in -- in size) if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } CScriptNum bn(stacktop(-1).size()); stack.push_back(bn.getvch()); } break; // // Bitwise logic // case OP_AND: case OP_OR: case OP_XOR: { // (x1 x2 - out) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype &vch1 = stacktop(-2); valtype &vch2 = stacktop(-1); // Inputs must be the same size if (vch1.size() != vch2.size()) { return set_error(serror, ScriptError::INVALID_OPERAND_SIZE); } // To avoid allocating, we modify vch1 in place. switch (opcode) { case OP_AND: for (size_t i = 0; i < vch1.size(); ++i) { vch1[i] &= vch2[i]; } break; case OP_OR: for (size_t i = 0; i < vch1.size(); ++i) { vch1[i] |= vch2[i]; } break; case OP_XOR: for (size_t i = 0; i < vch1.size(); ++i) { vch1[i] ^= vch2[i]; } break; default: break; } // And pop vch2. popstack(stack); } break; case OP_EQUAL: case OP_EQUALVERIFY: // case OP_NOTEQUAL: // use OP_NUMNOTEQUAL { // (x1 x2 - bool) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype &vch1 = stacktop(-2); valtype &vch2 = stacktop(-1); bool fEqual = (vch1 == vch2); // OP_NOTEQUAL is disabled because it would be too // easy to say something like n != 1 and have some // wiseguy pass in 1 with extra zero bytes after it // (numerically, 0x01 == 0x0001 == 0x000001) // if (opcode == OP_NOTEQUAL) // fEqual = !fEqual; popstack(stack); popstack(stack); stack.push_back(fEqual ? vchTrue : vchFalse); if (opcode == OP_EQUALVERIFY) { if (fEqual) { popstack(stack); } else { return set_error(serror, ScriptError::EQUALVERIFY); } } } break; // // Numeric // case OP_1ADD: case OP_1SUB: case OP_NEGATE: case OP_ABS: case OP_NOT: case OP_0NOTEQUAL: { // (in -- out) if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } CScriptNum bn(stacktop(-1), fRequireMinimal); switch (opcode) { case OP_1ADD: bn += bnOne; break; case OP_1SUB: bn -= bnOne; break; case OP_NEGATE: bn = -bn; break; case OP_ABS: if (bn < bnZero) { bn = -bn; } break; case OP_NOT: bn = (bn == bnZero); break; case OP_0NOTEQUAL: bn = (bn != bnZero); break; default: assert(!"invalid opcode"); break; } popstack(stack); stack.push_back(bn.getvch()); } break; case OP_ADD: case OP_SUB: case OP_DIV: case OP_MOD: case OP_BOOLAND: case OP_BOOLOR: case OP_NUMEQUAL: case OP_NUMEQUALVERIFY: case OP_NUMNOTEQUAL: case OP_LESSTHAN: case OP_GREATERTHAN: case OP_LESSTHANOREQUAL: case OP_GREATERTHANOREQUAL: case OP_MIN: case OP_MAX: { // (x1 x2 -- out) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } CScriptNum bn1(stacktop(-2), fRequireMinimal); CScriptNum bn2(stacktop(-1), fRequireMinimal); CScriptNum bn(0); switch (opcode) { case OP_ADD: bn = bn1 + bn2; break; case OP_SUB: bn = bn1 - bn2; break; case OP_DIV: // denominator must not be 0 if (bn2 == 0) { return set_error(serror, ScriptError::DIV_BY_ZERO); } bn = bn1 / bn2; break; case OP_MOD: // divisor must not be 0 if (bn2 == 0) { return set_error(serror, ScriptError::MOD_BY_ZERO); } bn = bn1 % bn2; break; case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; case OP_NUMEQUAL: bn = (bn1 == bn2); break; case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; case OP_LESSTHAN: bn = (bn1 < bn2); break; case OP_GREATERTHAN: bn = (bn1 > bn2); break; case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; default: assert(!"invalid opcode"); break; } popstack(stack); popstack(stack); stack.push_back(bn.getvch()); if (opcode == OP_NUMEQUALVERIFY) { if (CastToBool(stacktop(-1))) { popstack(stack); } else { return set_error(serror, ScriptError::NUMEQUALVERIFY); } } } break; case OP_WITHIN: { // (x min max -- out) if (stack.size() < 3) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } CScriptNum bn1(stacktop(-3), fRequireMinimal); CScriptNum bn2(stacktop(-2), fRequireMinimal); CScriptNum bn3(stacktop(-1), fRequireMinimal); bool fValue = (bn2 <= bn1 && bn1 < bn3); popstack(stack); popstack(stack); popstack(stack); stack.push_back(fValue ? vchTrue : vchFalse); } break; // // Crypto // case OP_RIPEMD160: case OP_SHA1: case OP_SHA256: case OP_HASH160: case OP_HASH256: { // (in -- hash) if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype &vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) { CRIPEMD160() .Write(vch.data(), vch.size()) .Finalize(vchHash.data()); } else if (opcode == OP_SHA1) { CSHA1() .Write(vch.data(), vch.size()) .Finalize(vchHash.data()); } else if (opcode == OP_SHA256) { CSHA256() .Write(vch.data(), vch.size()) .Finalize(vchHash.data()); } else if (opcode == OP_HASH160) { - CHash160() - .Write(vch.data(), vch.size()) - .Finalize(vchHash.data()); + CHash160().Write(vch).Finalize(vchHash.data()); } else if (opcode == OP_HASH256) { - CHash256() - .Write(vch.data(), vch.size()) - .Finalize(vchHash.data()); + CHash256().Write(vch).Finalize(vchHash.data()); } popstack(stack); stack.push_back(vchHash); } break; case OP_CODESEPARATOR: { // Hash starts after the code separator pbegincodehash = pc; } break; case OP_CHECKSIG: case OP_CHECKSIGVERIFY: { // (sig pubkey -- bool) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype &vchSig = stacktop(-2); valtype &vchPubKey = stacktop(-1); bool fSuccess = false; if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, flags, checker, metrics, serror, fSuccess)) { return false; } popstack(stack); popstack(stack); stack.push_back(fSuccess ? vchTrue : vchFalse); if (opcode == OP_CHECKSIGVERIFY) { if (fSuccess) { popstack(stack); } else { return set_error(serror, ScriptError::CHECKSIGVERIFY); } } } break; case OP_CHECKDATASIG: case OP_CHECKDATASIGVERIFY: { // (sig message pubkey -- bool) if (stack.size() < 3) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype &vchSig = stacktop(-3); valtype &vchMessage = stacktop(-2); valtype &vchPubKey = stacktop(-1); if (!CheckDataSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { // serror is set return false; } bool fSuccess = false; if (vchSig.size()) { valtype vchHash(32); CSHA256() .Write(vchMessage.data(), vchMessage.size()) .Finalize(vchHash.data()); fSuccess = checker.VerifySignature( vchSig, CPubKey(vchPubKey), uint256(vchHash)); metrics.nSigChecks += 1; if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL)) { return set_error(serror, ScriptError::SIG_NULLFAIL); } } popstack(stack); popstack(stack); popstack(stack); stack.push_back(fSuccess ? vchTrue : vchFalse); if (opcode == OP_CHECKDATASIGVERIFY) { if (fSuccess) { popstack(stack); } else { return set_error( serror, ScriptError::CHECKDATASIGVERIFY); } } } break; case OP_CHECKMULTISIG: case OP_CHECKMULTISIGVERIFY: { // ([dummy] [sig ...] num_of_signatures [pubkey ...] // num_of_pubkeys -- bool) const size_t idxKeyCount = 1; if (stack.size() < idxKeyCount) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } const int nKeysCount = CScriptNum(stacktop(-idxKeyCount), fRequireMinimal) .getint(); if (nKeysCount < 0 || nKeysCount > MAX_PUBKEYS_PER_MULTISIG) { return set_error(serror, ScriptError::PUBKEY_COUNT); } nOpCount += nKeysCount; if (nOpCount > MAX_OPS_PER_SCRIPT) { return set_error(serror, ScriptError::OP_COUNT); } // stack depth of the top pubkey const size_t idxTopKey = idxKeyCount + 1; // stack depth of nSigsCount const size_t idxSigCount = idxTopKey + nKeysCount; if (stack.size() < idxSigCount) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } const int nSigsCount = CScriptNum(stacktop(-idxSigCount), fRequireMinimal) .getint(); if (nSigsCount < 0 || nSigsCount > nKeysCount) { return set_error(serror, ScriptError::SIG_COUNT); } // stack depth of the top signature const size_t idxTopSig = idxSigCount + 1; // stack depth of the dummy element const size_t idxDummy = idxTopSig + nSigsCount; if (stack.size() < idxDummy) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } // Subset of script starting at the most recent // codeseparator CScript scriptCode(pbegincodehash, pend); // Assuming success is usually a bad idea, but the // schnorr path can only succeed. bool fSuccess = true; if ((flags & SCRIPT_ENABLE_SCHNORR_MULTISIG) && stacktop(-idxDummy).size() != 0) { // SCHNORR MULTISIG static_assert( MAX_PUBKEYS_PER_MULTISIG < 32, "Schnorr multisig checkbits implementation " "assumes < 32 pubkeys."); uint32_t checkBits = 0; // Dummy element is to be interpreted as a bitfield // that represent which pubkeys should be checked. valtype &vchDummy = stacktop(-idxDummy); if (!DecodeBitfield(vchDummy, nKeysCount, checkBits, serror)) { // serror is set return false; } // The bitfield doesn't set the right number of // signatures. if (countBits(checkBits) != uint32_t(nSigsCount)) { return set_error( serror, ScriptError::INVALID_BIT_COUNT); } const size_t idxBottomKey = idxTopKey + nKeysCount - 1; const size_t idxBottomSig = idxTopSig + nSigsCount - 1; int iKey = 0; for (int iSig = 0; iSig < nSigsCount; iSig++, iKey++) { if ((checkBits >> iKey) == 0) { // This is a sanity check and should be // unrecheable. return set_error( serror, ScriptError::INVALID_BIT_RANGE); } // Find the next suitable key. while (((checkBits >> iKey) & 0x01) == 0) { iKey++; } if (iKey >= nKeysCount) { // This is a sanity check and should be // unrecheable. return set_error(serror, ScriptError::PUBKEY_COUNT); } // Check the signature. valtype &vchSig = stacktop(-idxBottomSig + iSig); valtype &vchPubKey = stacktop(-idxBottomKey + iKey); // Note that only pubkeys associated with a // signature are checked for validity. if (!CheckTransactionSchnorrSignatureEncoding( vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { // serror is set return false; } // Check signature if (!checker.CheckSig(vchSig, vchPubKey, scriptCode, flags)) { // This can fail if the signature is empty, // which also is a NULLFAIL error as the // bitfield should have been null in this // situation. return set_error(serror, ScriptError::SIG_NULLFAIL); } // this is guaranteed to execute exactly // nSigsCount times (if not script error) metrics.nSigChecks += 1; } if ((checkBits >> iKey) != 0) { // This is a sanity check and should be // unrecheable. return set_error( serror, ScriptError::INVALID_BIT_COUNT); } } else { // LEGACY MULTISIG (ECDSA / NULL) // Remove signature for pre-fork scripts for (int k = 0; k < nSigsCount; k++) { valtype &vchSig = stacktop(-idxTopSig - k); CleanupScriptCode(scriptCode, vchSig, flags); } int nSigsRemaining = nSigsCount; int nKeysRemaining = nKeysCount; while (fSuccess && nSigsRemaining > 0) { valtype &vchSig = stacktop( -idxTopSig - (nSigsCount - nSigsRemaining)); valtype &vchPubKey = stacktop( -idxTopKey - (nKeysCount - nKeysRemaining)); // Note how this makes the exact order of // pubkey/signature evaluation distinguishable // by CHECKMULTISIG NOT if the STRICTENC flag is // set. See the script_(in)valid tests for // details. if (!CheckTransactionECDSASignatureEncoding( vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { // serror is set return false; } // Check signature bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, flags); if (fOk) { nSigsRemaining--; } nKeysRemaining--; // If there are more signatures left than keys // left, then too many signatures have failed. // Exit early, without checking any further // signatures. if (nSigsRemaining > nKeysRemaining) { fSuccess = false; } } bool areAllSignaturesNull = true; for (int i = 0; i < nSigsCount; i++) { if (stacktop(-idxTopSig - i).size()) { areAllSignaturesNull = false; break; } } // If the operation failed, we may require that all // signatures must be empty vector if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && !areAllSignaturesNull) { return set_error(serror, ScriptError::SIG_NULLFAIL); } if (!areAllSignaturesNull) { // This is not identical to the number of actual // ECDSA verifies, but, it is an upper bound // that can be easily determined without doing // CPU-intensive checks. metrics.nSigChecks += nKeysCount; } } // Clean up stack of all arguments for (size_t i = 0; i < idxDummy; i++) { popstack(stack); } stack.push_back(fSuccess ? vchTrue : vchFalse); if (opcode == OP_CHECKMULTISIGVERIFY) { if (fSuccess) { popstack(stack); } else { return set_error( serror, ScriptError::CHECKMULTISIGVERIFY); } } } break; // // Byte string operations // case OP_CAT: { // (x1 x2 -- out) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype &vch1 = stacktop(-2); valtype &vch2 = stacktop(-1); if (vch1.size() + vch2.size() > MAX_SCRIPT_ELEMENT_SIZE) { return set_error(serror, ScriptError::PUSH_SIZE); } vch1.insert(vch1.end(), vch2.begin(), vch2.end()); popstack(stack); } break; case OP_SPLIT: { // (in position -- x1 x2) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } const valtype &data = stacktop(-2); // Make sure the split point is appropriate. uint64_t position = CScriptNum(stacktop(-1), fRequireMinimal).getint(); if (position > data.size()) { return set_error(serror, ScriptError::INVALID_SPLIT_RANGE); } // Prepare the results in their own buffer as `data` // will be invalidated. valtype n1(data.begin(), data.begin() + position); valtype n2(data.begin() + position, data.end()); // Replace existing stack values by the new values. stacktop(-2) = std::move(n1); stacktop(-1) = std::move(n2); } break; case OP_REVERSEBYTES: { // (in -- out) if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype &data = stacktop(-1); std::reverse(data.begin(), data.end()); } break; // // Conversion operations // case OP_NUM2BIN: { // (in size -- out) if (stack.size() < 2) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } uint64_t size = CScriptNum(stacktop(-1), fRequireMinimal).getint(); if (size > MAX_SCRIPT_ELEMENT_SIZE) { return set_error(serror, ScriptError::PUSH_SIZE); } popstack(stack); valtype &rawnum = stacktop(-1); // Try to see if we can fit that number in the number of // byte requested. CScriptNum::MinimallyEncode(rawnum); if (rawnum.size() > size) { // We definitively cannot. return set_error(serror, ScriptError::IMPOSSIBLE_ENCODING); } // We already have an element of the right size, we // don't need to do anything. if (rawnum.size() == size) { break; } uint8_t signbit = 0x00; if (rawnum.size() > 0) { signbit = rawnum.back() & 0x80; rawnum[rawnum.size() - 1] &= 0x7f; } rawnum.reserve(size); while (rawnum.size() < size - 1) { rawnum.push_back(0x00); } rawnum.push_back(signbit); } break; case OP_BIN2NUM: { // (in -- out) if (stack.size() < 1) { return set_error( serror, ScriptError::INVALID_STACK_OPERATION); } valtype &n = stacktop(-1); CScriptNum::MinimallyEncode(n); // The resulting number must be a valid number. if (!CScriptNum::IsMinimallyEncoded(n)) { return set_error(serror, ScriptError::INVALID_NUMBER_RANGE); } } break; default: return set_error(serror, ScriptError::BAD_OPCODE); } } // Size limits if (stack.size() + altstack.size() > MAX_STACK_SIZE) { return set_error(serror, ScriptError::STACK_SIZE); } } } catch (...) { return set_error(serror, ScriptError::UNKNOWN); } if (!vfExec.empty()) { return set_error(serror, ScriptError::UNBALANCED_CONDITIONAL); } return set_success(serror); } namespace { /** * Wrapper that serializes like CTransaction, but with the modifications * required for the signature hash done in-place */ template <class T> class CTransactionSignatureSerializer { private: //! reference to the spending transaction (the one being serialized) const T &txTo; //! output script being consumed const CScript &scriptCode; //! input index of txTo being signed const unsigned int nIn; //! container for hashtype flags const SigHashType sigHashType; public: CTransactionSignatureSerializer(const T &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, SigHashType sigHashTypeIn) : txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn), sigHashType(sigHashTypeIn) {} /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */ template <typename S> void SerializeScriptCode(S &s) const { CScript::const_iterator it = scriptCode.begin(); CScript::const_iterator itBegin = it; opcodetype opcode; unsigned int nCodeSeparators = 0; while (scriptCode.GetOp(it, opcode)) { if (opcode == OP_CODESEPARATOR) { nCodeSeparators++; } } ::WriteCompactSize(s, scriptCode.size() - nCodeSeparators); it = itBegin; while (scriptCode.GetOp(it, opcode)) { if (opcode == OP_CODESEPARATOR) { s.write((char *)&itBegin[0], it - itBegin - 1); itBegin = it; } } if (itBegin != scriptCode.end()) { s.write((char *)&itBegin[0], it - itBegin); } } /** Serialize an input of txTo */ template <typename S> void SerializeInput(S &s, unsigned int nInput) const { // In case of SIGHASH_ANYONECANPAY, only the input being signed is // serialized if (sigHashType.hasAnyoneCanPay()) { nInput = nIn; } // Serialize the prevout ::Serialize(s, txTo.vin[nInput].prevout); // Serialize the script if (nInput != nIn) { // Blank out other inputs' signatures ::Serialize(s, CScript()); } else { SerializeScriptCode(s); } // Serialize the nSequence if (nInput != nIn && (sigHashType.getBaseType() == BaseSigHashType::SINGLE || sigHashType.getBaseType() == BaseSigHashType::NONE)) { // let the others update at will ::Serialize(s, (int)0); } else { ::Serialize(s, txTo.vin[nInput].nSequence); } } /** Serialize an output of txTo */ template <typename S> void SerializeOutput(S &s, unsigned int nOutput) const { if (sigHashType.getBaseType() == BaseSigHashType::SINGLE && nOutput != nIn) { // Do not lock-in the txout payee at other indices as txin ::Serialize(s, CTxOut()); } else { ::Serialize(s, txTo.vout[nOutput]); } } /** Serialize txTo */ template <typename S> void Serialize(S &s) const { // Serialize nVersion ::Serialize(s, txTo.nVersion); // Serialize vin unsigned int nInputs = sigHashType.hasAnyoneCanPay() ? 1 : txTo.vin.size(); ::WriteCompactSize(s, nInputs); for (unsigned int nInput = 0; nInput < nInputs; nInput++) { SerializeInput(s, nInput); } // Serialize vout unsigned int nOutputs = (sigHashType.getBaseType() == BaseSigHashType::NONE) ? 0 : ((sigHashType.getBaseType() == BaseSigHashType::SINGLE) ? nIn + 1 : txTo.vout.size()); ::WriteCompactSize(s, nOutputs); for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++) { SerializeOutput(s, nOutput); } // Serialize nLockTime ::Serialize(s, txTo.nLockTime); } }; template <class T> uint256 GetPrevoutHash(const T &txTo) { CHashWriter ss(SER_GETHASH, 0); for (const auto &txin : txTo.vin) { ss << txin.prevout; } return ss.GetHash(); } template <class T> uint256 GetSequenceHash(const T &txTo) { CHashWriter ss(SER_GETHASH, 0); for (const auto &txin : txTo.vin) { ss << txin.nSequence; } return ss.GetHash(); } template <class T> uint256 GetOutputsHash(const T &txTo) { CHashWriter ss(SER_GETHASH, 0); for (const auto &txout : txTo.vout) { ss << txout; } return ss.GetHash(); } } // namespace template <class T> PrecomputedTransactionData::PrecomputedTransactionData(const T &txTo) { hashPrevouts = GetPrevoutHash(txTo); hashSequence = GetSequenceHash(txTo); hashOutputs = GetOutputsHash(txTo); } // explicit instantiation template PrecomputedTransactionData::PrecomputedTransactionData( const CTransaction &txTo); template PrecomputedTransactionData::PrecomputedTransactionData( const CMutableTransaction &txTo); template <class T> uint256 SignatureHash(const CScript &scriptCode, const T &txTo, unsigned int nIn, SigHashType sigHashType, const Amount amount, const PrecomputedTransactionData *cache, uint32_t flags) { assert(nIn < txTo.vin.size()); if (flags & SCRIPT_ENABLE_REPLAY_PROTECTION) { // Legacy chain's value for fork id must be of the form 0xffxxxx. // By xoring with 0xdead, we ensure that the value will be different // from the original one, even if it already starts with 0xff. uint32_t newForkValue = sigHashType.getForkValue() ^ 0xdead; sigHashType = sigHashType.withForkValue(0xff0000 | newForkValue); } if (sigHashType.hasForkId() && (flags & SCRIPT_ENABLE_SIGHASH_FORKID)) { uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; if (!sigHashType.hasAnyoneCanPay()) { hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); } if (!sigHashType.hasAnyoneCanPay() && (sigHashType.getBaseType() != BaseSigHashType::SINGLE) && (sigHashType.getBaseType() != BaseSigHashType::NONE)) { hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); } if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) && (sigHashType.getBaseType() != BaseSigHashType::NONE)) { hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); } else if ((sigHashType.getBaseType() == BaseSigHashType::SINGLE) && (nIn < txTo.vout.size())) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; hashOutputs = ss.GetHash(); } CHashWriter ss(SER_GETHASH, 0); // Version ss << txTo.nVersion; // Input prevouts/nSequence (none/all, depending on flags) ss << hashPrevouts; ss << hashSequence; // The input being signed (replacing the scriptSig with scriptCode + // amount). The prevout may already be contained in hashPrevout, and the // nSequence may already be contain in hashSequence. ss << txTo.vin[nIn].prevout; ss << scriptCode; ss << amount; ss << txTo.vin[nIn].nSequence; // Outputs (none/one/all, depending on flags) ss << hashOutputs; // Locktime ss << txTo.nLockTime; // Sighash type ss << sigHashType; return ss.GetHash(); } // Check for invalid use of SIGHASH_SINGLE if ((sigHashType.getBaseType() == BaseSigHashType::SINGLE) && (nIn >= txTo.vout.size())) { // nOut out of range return UINT256_ONE(); } // Wrapper to serialize only the necessary parts of the transaction being // signed CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, sigHashType); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); ss << txTmp << sigHashType; return ss.GetHash(); } bool BaseSignatureChecker::VerifySignature(const std::vector<uint8_t> &vchSig, const CPubKey &pubkey, const uint256 &sighash) const { if (vchSig.size() == 64) { return pubkey.VerifySchnorr(sighash, vchSig); } else { return pubkey.VerifyECDSA(sighash, vchSig); } } template <class T> bool GenericTransactionSignatureChecker<T>::CheckSig( const std::vector<uint8_t> &vchSigIn, const std::vector<uint8_t> &vchPubKey, const CScript &scriptCode, uint32_t flags) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) { return false; } // Hash type is one byte tacked on to the end of the signature std::vector<uint8_t> vchSig(vchSigIn); if (vchSig.empty()) { return false; } SigHashType sigHashType = GetHashType(vchSig); vchSig.pop_back(); uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, sigHashType, amount, this->txdata, flags); if (!VerifySignature(vchSig, pubkey, sighash)) { return false; } return true; } template <class T> bool GenericTransactionSignatureChecker<T>::CheckLockTime( const CScriptNum &nLockTime) const { // There are two kinds of nLockTime: lock-by-blockheight and // lock-by-blocktime, distinguished by whether nLockTime < // LOCKTIME_THRESHOLD. // // We want to compare apples to apples, so fail the script unless the type // of nLockTime being tested is the same as the nLockTime in the // transaction. if (!((txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD))) { return false; } // Now that we know we're comparing apples-to-apples, the comparison is a // simple numeric one. if (nLockTime > int64_t(txTo->nLockTime)) { return false; } // Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been finalized by setting // nSequence to maxint. The transaction would be allowed into the // blockchain, making the opcode ineffective. // // Testing if this vin is not final is sufficient to prevent this condition. // Alternatively we could test all inputs, but testing just this input // minimizes the data required to prove correct CHECKLOCKTIMEVERIFY // execution. if (CTxIn::SEQUENCE_FINAL == txTo->vin[nIn].nSequence) { return false; } return true; } template <class T> bool GenericTransactionSignatureChecker<T>::CheckSequence( const CScriptNum &nSequence) const { // Relative lock times are supported by comparing the passed in operand to // the sequence number of the input. const int64_t txToSequence = int64_t(txTo->vin[nIn].nSequence); // Fail if the transaction's version number is not set high enough to // trigger BIP 68 rules. if (static_cast<uint32_t>(txTo->nVersion) < 2) { return false; } // Sequence numbers with their most significant bit set are not consensus // constrained. Testing that the transaction's sequence number do not have // this bit set prevents using this property to get around a // CHECKSEQUENCEVERIFY check. if (txToSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) { return false; } // Mask off any bits that do not have consensus-enforced meaning before // doing the integer comparisons const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | CTxIn::SEQUENCE_LOCKTIME_MASK; const int64_t txToSequenceMasked = txToSequence & nLockTimeMask; const CScriptNum nSequenceMasked = nSequence & nLockTimeMask; // There are two kinds of nSequence: lock-by-blockheight and // lock-by-blocktime, distinguished by whether nSequenceMasked < // CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. // // We want to compare apples to apples, so fail the script unless the type // of nSequenceMasked being tested is the same as the nSequenceMasked in the // transaction. if (!((txToSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) || (txToSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG))) { return false; } // Now that we know we're comparing apples-to-apples, the comparison is a // simple numeric one. if (nSequenceMasked > txToSequenceMasked) { return false; } return true; } // explicit instantiation template class GenericTransactionSignatureChecker<CTransaction>; template class GenericTransactionSignatureChecker<CMutableTransaction>; bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, uint32_t flags, const BaseSignatureChecker &checker, ScriptExecutionMetrics &metricsOut, ScriptError *serror) { set_error(serror, ScriptError::UNKNOWN); // If FORKID is enabled, we also ensure strict encoding. if (flags & SCRIPT_ENABLE_SIGHASH_FORKID) { flags |= SCRIPT_VERIFY_STRICTENC; } if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) { return set_error(serror, ScriptError::SIG_PUSHONLY); } ScriptExecutionMetrics metrics = {}; // scriptSig and scriptPubKey must be evaluated sequentially on the same // stack rather than being simply concatenated (see CVE-2010-5141) std::vector<valtype> stack, stackCopy; if (!EvalScript(stack, scriptSig, flags, checker, metrics, serror)) { // serror is set return false; } if (flags & SCRIPT_VERIFY_P2SH) { stackCopy = stack; } if (!EvalScript(stack, scriptPubKey, flags, checker, metrics, serror)) { // serror is set return false; } if (stack.empty()) { return set_error(serror, ScriptError::EVAL_FALSE); } if (CastToBool(stack.back()) == false) { return set_error(serror, ScriptError::EVAL_FALSE); } // Additional validation for spend-to-script-hash transactions: if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { // scriptSig must be literals-only or validation fails if (!scriptSig.IsPushOnly()) { return set_error(serror, ScriptError::SIG_PUSHONLY); } // Restore stack. swap(stack, stackCopy); // stack cannot be empty here, because if it was the P2SH HASH <> EQUAL // scriptPubKey would be evaluated with an empty stack and the // EvalScript above would return false. assert(!stack.empty()); const valtype &pubKeySerialized = stack.back(); CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stack); // Bail out early if SCRIPT_DISALLOW_SEGWIT_RECOVERY is not set, the // redeem script is a p2sh segwit program, and it was the only item // pushed onto the stack. if ((flags & SCRIPT_DISALLOW_SEGWIT_RECOVERY) == 0 && stack.empty() && pubKey2.IsWitnessProgram()) { // must set metricsOut for all successful returns metricsOut = metrics; return set_success(serror); } if (!EvalScript(stack, pubKey2, flags, checker, metrics, serror)) { // serror is set return false; } if (stack.empty()) { return set_error(serror, ScriptError::EVAL_FALSE); } if (!CastToBool(stack.back())) { return set_error(serror, ScriptError::EVAL_FALSE); } } // The CLEANSTACK check is only performed after potential P2SH evaluation, // as the non-P2SH evaluation of a P2SH script will obviously not result in // a clean stack (the P2SH inputs remain). The same holds for witness // evaluation. if ((flags & SCRIPT_VERIFY_CLEANSTACK) != 0) { // Disallow CLEANSTACK without P2SH, as otherwise a switch // CLEANSTACK->P2SH+CLEANSTACK would be possible, which is not a // softfork (and P2SH should be one). assert((flags & SCRIPT_VERIFY_P2SH) != 0); if (stack.size() != 1) { return set_error(serror, ScriptError::CLEANSTACK); } } if (flags & SCRIPT_VERIFY_INPUT_SIGCHECKS) { // This limit is intended for standard use, and is based on an // examination of typical and historical standard uses. // - allowing P2SH ECDSA multisig with compressed keys, which at an // extreme (1-of-15) may have 15 SigChecks in ~590 bytes of scriptSig. // - allowing Bare ECDSA multisig, which at an extreme (1-of-3) may have // 3 sigchecks in ~72 bytes of scriptSig. // - Since the size of an input is 41 bytes + length of scriptSig, then // the most dense possible inputs satisfying this rule would be: // 2 sigchecks and 26 bytes: 1/33.50 sigchecks/byte. // 3 sigchecks and 69 bytes: 1/36.66 sigchecks/byte. // The latter can be readily done with 1-of-3 bare multisignatures, // however the former is not practically doable with standard scripts, // so the practical density limit is 1/36.66. static_assert(INT_MAX > MAX_SCRIPT_SIZE, "overflow sanity check on max script size"); static_assert(INT_MAX / 43 / 3 > MAX_OPS_PER_SCRIPT, "overflow sanity check on maximum possible sigchecks " "from sig+redeem+pub scripts"); if (int(scriptSig.size()) < metrics.nSigChecks * 43 - 60) { return set_error(serror, ScriptError::INPUT_SIGCHECKS); } } metricsOut = metrics; return set_success(serror); } diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 23d7cb6aa..e97900600 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -1,1073 +1,1073 @@ // Copyright (c) 2014-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 <crypto/aes.h> #include <crypto/chacha20.h> #include <crypto/chacha_poly_aead.h> #include <crypto/hkdf_sha256_32.h> #include <crypto/hmac_sha256.h> #include <crypto/hmac_sha512.h> #include <crypto/poly1305.h> #include <crypto/ripemd160.h> #include <crypto/sha1.h> #include <crypto/sha256.h> #include <crypto/sha512.h> #include <random.h> #include <util/strencodings.h> #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> #include <vector> BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup) template <typename Hasher, typename In, typename Out> static void TestVector(const Hasher &h, const In &in, const Out &out) { Out hash; BOOST_CHECK(out.size() == h.OUTPUT_SIZE); hash.resize(out.size()); { // Test that writing the whole input string at once works. Hasher(h).Write((uint8_t *)&in[0], in.size()).Finalize(&hash[0]); BOOST_CHECK(hash == out); } for (int i = 0; i < 32; i++) { // Test that writing the string broken up in random pieces works. Hasher hasher(h); size_t pos = 0; while (pos < in.size()) { size_t len = InsecureRandRange((in.size() - pos + 1) / 2 + 1); hasher.Write((uint8_t *)&in[pos], len); pos += len; if (pos > 0 && pos + 2 * out.size() > in.size() && pos < in.size()) { // Test that writing the rest at once to a copy of a hasher // works. Hasher(hasher) .Write((uint8_t *)&in[pos], in.size() - pos) .Finalize(&hash[0]); BOOST_CHECK(hash == out); } } hasher.Finalize(&hash[0]); BOOST_CHECK(hash == out); } } static void TestSHA1(const std::string &in, const std::string &hexout) { TestVector(CSHA1(), in, ParseHex(hexout)); } static void TestSHA256(const std::string &in, const std::string &hexout) { TestVector(CSHA256(), in, ParseHex(hexout)); } static void TestSHA512(const std::string &in, const std::string &hexout) { TestVector(CSHA512(), in, ParseHex(hexout)); } static void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVector(CRIPEMD160(), in, ParseHex(hexout)); } static void TestHMACSHA256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector<uint8_t> key = ParseHex(hexkey); TestVector(CHMAC_SHA256(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout)); } static void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector<uint8_t> key = ParseHex(hexkey); TestVector(CHMAC_SHA512(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout)); } static void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector<uint8_t> key = ParseHex(hexkey); std::vector<uint8_t> in = ParseHex(hexin); std::vector<uint8_t> correctout = ParseHex(hexout); std::vector<uint8_t> buf, buf2; assert(key.size() == 16); assert(in.size() == 16); assert(correctout.size() == 16); AES128Encrypt enc(key.data()); buf.resize(correctout.size()); buf2.resize(correctout.size()); enc.Encrypt(buf.data(), in.data()); BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout)); AES128Decrypt dec(key.data()); dec.Decrypt(buf2.data(), buf.data()); BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in)); } static void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector<uint8_t> key = ParseHex(hexkey); std::vector<uint8_t> in = ParseHex(hexin); std::vector<uint8_t> correctout = ParseHex(hexout); std::vector<uint8_t> buf; assert(key.size() == 32); assert(in.size() == 16); assert(correctout.size() == 16); AES256Encrypt enc(key.data()); buf.resize(correctout.size()); enc.Encrypt(buf.data(), in.data()); BOOST_CHECK(buf == correctout); AES256Decrypt dec(key.data()); dec.Decrypt(buf.data(), buf.data()); BOOST_CHECK(buf == in); } static void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout) { std::vector<uint8_t> key = ParseHex(hexkey); std::vector<uint8_t> iv = ParseHex(hexiv); std::vector<uint8_t> in = ParseHex(hexin); std::vector<uint8_t> correctout = ParseHex(hexout); std::vector<uint8_t> realout(in.size() + AES_BLOCKSIZE); // Encrypt the plaintext and verify that it equals the cipher AES128CBCEncrypt enc(key.data(), iv.data(), pad); int size = enc.Encrypt(in.data(), in.size(), realout.data()); realout.resize(size); BOOST_CHECK(realout.size() == correctout.size()); BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); // Decrypt the cipher and verify that it equals the plaintext std::vector<uint8_t> decrypted(correctout.size()); AES128CBCDecrypt dec(key.data(), iv.data(), pad); size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data()); decrypted.resize(size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); // Encrypt and re-decrypt substrings of the plaintext and verify that they // equal each-other for (std::vector<uint8_t>::iterator i(in.begin()); i != in.end(); ++i) { std::vector<uint8_t> sub(i, in.end()); std::vector<uint8_t> subout(sub.size() + AES_BLOCKSIZE); int _size = enc.Encrypt(sub.data(), sub.size(), subout.data()); if (_size != 0) { subout.resize(_size); std::vector<uint8_t> subdecrypted(subout.size()); _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data()); subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); } } } static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout) { std::vector<uint8_t> key = ParseHex(hexkey); std::vector<uint8_t> iv = ParseHex(hexiv); std::vector<uint8_t> in = ParseHex(hexin); std::vector<uint8_t> correctout = ParseHex(hexout); std::vector<uint8_t> realout(in.size() + AES_BLOCKSIZE); // Encrypt the plaintext and verify that it equals the cipher AES256CBCEncrypt enc(key.data(), iv.data(), pad); int size = enc.Encrypt(in.data(), in.size(), realout.data()); realout.resize(size); BOOST_CHECK(realout.size() == correctout.size()); BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); // Decrypt the cipher and verify that it equals the plaintext std::vector<uint8_t> decrypted(correctout.size()); AES256CBCDecrypt dec(key.data(), iv.data(), pad); size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data()); decrypted.resize(size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); // Encrypt and re-decrypt substrings of the plaintext and verify that they // equal each-other for (std::vector<uint8_t>::iterator i(in.begin()); i != in.end(); ++i) { std::vector<uint8_t> sub(i, in.end()); std::vector<uint8_t> subout(sub.size() + AES_BLOCKSIZE); int _size = enc.Encrypt(sub.data(), sub.size(), subout.data()); if (_size != 0) { subout.resize(_size); std::vector<uint8_t> subdecrypted(subout.size()); _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data()); subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); } } } static void TestChaCha20(const std::string &hex_message, const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string &hexout) { std::vector<uint8_t> key = ParseHex(hexkey); std::vector<uint8_t> m = ParseHex(hex_message); ChaCha20 rng(key.data(), key.size()); rng.SetIV(nonce); rng.Seek(seek); std::vector<uint8_t> out = ParseHex(hexout); std::vector<uint8_t> outres; outres.resize(out.size()); assert(hex_message.empty() || m.size() == out.size()); // perform the ChaCha20 round(s), if message is provided it will output the // encrypted ciphertext otherwise the keystream if (!hex_message.empty()) { rng.Crypt(m.data(), outres.data(), outres.size()); } else { rng.Keystream(outres.data(), outres.size()); } BOOST_CHECK(out == outres); if (!hex_message.empty()) { // Manually XOR with the keystream and compare the output rng.SetIV(nonce); rng.Seek(seek); std::vector<uint8_t> only_keystream(outres.size()); rng.Keystream(only_keystream.data(), only_keystream.size()); for (size_t i = 0; i != m.size(); i++) { outres[i] = m[i] ^ only_keystream[i]; } BOOST_CHECK(out == outres); } } static void TestPoly1305(const std::string &hexmessage, const std::string &hexkey, const std::string &hextag) { std::vector<uint8_t> key = ParseHex(hexkey); std::vector<uint8_t> m = ParseHex(hexmessage); std::vector<uint8_t> tag = ParseHex(hextag); std::vector<uint8_t> tagres; tagres.resize(POLY1305_TAGLEN); poly1305_auth(tagres.data(), m.data(), m.size(), key.data()); BOOST_CHECK(tag == tagres); } static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &salt_hex, const std::string &info_hex, const std::string &okm_check_hex) { std::vector<uint8_t> initial_key_material = ParseHex(ikm_hex); std::vector<uint8_t> salt = ParseHex(salt_hex); std::vector<uint8_t> info = ParseHex(info_hex); // our implementation only supports strings for the "info" and "salt", // stringify them std::string salt_stringified(reinterpret_cast<char *>(salt.data()), salt.size()); std::string info_stringified(reinterpret_cast<char *>(info.data()), info.size()); CHKDF_HMAC_SHA256_L32 hkdf32(initial_key_material.data(), initial_key_material.size(), salt_stringified); uint8_t out[32]; hkdf32.Expand32(info_stringified, out); BOOST_CHECK(HexStr(out, out + 32) == okm_check_hex); } static std::string LongTestString() { std::string ret; for (int i = 0; i < 200000; i++) { ret += uint8_t(i); ret += uint8_t(i >> 4); ret += uint8_t(i >> 8); ret += uint8_t(i >> 12); ret += uint8_t(i >> 16); } return ret; } const std::string test1 = LongTestString(); BOOST_AUTO_TEST_CASE(ripemd160_testvectors) { TestRIPEMD160("", "9c1185a5c5e9fc54612808977ee8f548b2258d31"); TestRIPEMD160("abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); TestRIPEMD160("message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36"); TestRIPEMD160("secure hash algorithm", "20397528223b6a5f4cbc2808aba0464e645544f9"); TestRIPEMD160("RIPEMD160 is considered to be safe", "a7d78608c7af8a8e728778e81576870734122b66"); TestRIPEMD160("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "12a053384a9c0c88e405a06c27dcf49ada62eb2b"); TestRIPEMD160( "For this sample, this 63-byte string will be used as input data", "de90dbfee14b63fb5abf27c2ad4a82aaa5f27a11"); TestRIPEMD160( "This is exactly 64 bytes long, not counting the terminating byte", "eda31d51d3a623b81e19eb02e24ff65d27d67b37"); TestRIPEMD160(std::string(1000000, 'a'), "52783243c1697bdbe16d37f97f68f08325dc1528"); TestRIPEMD160(test1, "464243587bd146ea835cdf57bdae582f25ec45f1"); } BOOST_AUTO_TEST_CASE(sha1_testvectors) { TestSHA1("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"); TestSHA1("abc", "a9993e364706816aba3e25717850c26c9cd0d89d"); TestSHA1("message digest", "c12252ceda8be8994d5fa0290a47231c1d16aae3"); TestSHA1("secure hash algorithm", "d4d6d2f0ebe317513bbd8d967d89bac5819c2f60"); TestSHA1("SHA1 is considered to be safe", "f2b6650569ad3a8720348dd6ea6c497dee3a842a"); TestSHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1"); TestSHA1("For this sample, this 63-byte string will be used as input data", "4f0ea5cd0585a23d028abdc1a6684e5a8094dc49"); TestSHA1("This is exactly 64 bytes long, not counting the terminating byte", "fb679f23e7d1ce053313e66e127ab1b444397057"); TestSHA1(std::string(1000000, 'a'), "34aa973cd4c4daa4f61eeb2bdbad27316534016f"); TestSHA1(test1, "b7755760681cbfd971451668f32af5774f4656b5"); } BOOST_AUTO_TEST_CASE(sha256_testvectors) { TestSHA256( "", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); TestSHA256( "abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); TestSHA256( "message digest", "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"); TestSHA256( "secure hash algorithm", "f30ceb2bb2829e79e4ca9753d35a8ecc00262d164cc077080295381cbd643f0d"); TestSHA256( "SHA256 is considered to be safe", "6819d915c73f4d1e77e4e1b52d1fa0f9cf9beaead3939f15874bd988e2a23630"); TestSHA256( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); TestSHA256( "For this sample, this 63-byte string will be used as input data", "f08a78cbbaee082b052ae0708f32fa1e50c5c421aa772ba5dbb406a2ea6be342"); TestSHA256( "This is exactly 64 bytes long, not counting the terminating byte", "ab64eff7e88e2e46165e29f2bce41826bd4c7b3552f6b382a9e7d3af47c245f8"); TestSHA256( "As Bitcoin relies on 80 byte header hashes, we want to have an " "example for that.", "7406e8de7d6e4fffc573daef05aefb8806e7790f55eab5576f31349743cca743"); TestSHA256( std::string(1000000, 'a'), "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); TestSHA256( test1, "a316d55510b49662420f49d145d42fb83f31ef8dc016aa4e32df049991a91e26"); } BOOST_AUTO_TEST_CASE(sha512_testvectors) { TestSHA512( "", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce" "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); TestSHA512( "abc", "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); TestSHA512( "message digest", "107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f33" "09e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c"); TestSHA512( "secure hash algorithm", "7746d91f3de30c68cec0dd693120a7e8b04d8073cb699bdce1a3f64127bca7a3" "d5db502e814bb63c063a7a5043b2df87c61133395f4ad1edca7fcf4b30c3236e"); TestSHA512( "SHA512 is considered to be safe", "099e6468d889e1c79092a89ae925a9499b5408e01b66cb5b0a3bd0dfa51a9964" "6b4a3901caab1318189f74cd8cf2e941829012f2449df52067d3dd5b978456c2"); TestSHA512( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c335" "96fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"); TestSHA512( "For this sample, this 63-byte string will be used as input data", "b3de4afbc516d2478fe9b518d063bda6c8dd65fc38402dd81d1eb7364e72fb6e" "6663cf6d2771c8f5a6da09601712fb3d2a36c6ffea3e28b0818b05b0a8660766"); TestSHA512( "This is exactly 64 bytes long, not counting the terminating byte", "70aefeaa0e7ac4f8fe17532d7185a289bee3b428d950c14fa8b713ca09814a38" "7d245870e007a80ad97c369d193e41701aa07f3221d15f0e65a1ff970cedf030"); TestSHA512( "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno" "ijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"); TestSHA512( std::string(1000000, 'a'), "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); TestSHA512( test1, "40cac46c147e6131c5193dd5f34e9d8bb4951395f27b08c558c65ff4ba2de594" "37de8c3ef5459d76a52cedc02dc499a3c9ed9dedbfb3281afd9653b8a112fafc"); } BOOST_AUTO_TEST_CASE(hmac_sha256_testvectors) { // test cases 1, 2, 3, 4, 6 and 7 of RFC 4231 TestHMACSHA256( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "4869205468657265", "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"); TestHMACSHA256( "4a656665", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"); TestHMACSHA256( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" "dddddddddddddddddddddddddddddddddddd", "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"); TestHMACSHA256( "0102030405060708090a0b0c0d0e0f10111213141516171819", "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"); TestHMACSHA256( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" "65204b6579202d2048617368204b6579204669727374", "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"); TestHMACSHA256( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", "5468697320697320612074657374207573696e672061206c6172676572207468" "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" "647320746f20626520686173686564206265666f7265206265696e6720757365" "642062792074686520484d414320616c676f726974686d2e", "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"); // Test case with key length 63 bytes. TestHMACSHA256( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "9de4b546756c83516720a4ad7fe7bdbeac4298c6fdd82b15f895a6d10b0769a6"); // Test case with key length 64 bytes. TestHMACSHA256( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd"); // Test case with key length 65 bytes. TestHMACSHA256( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "d06af337f359a2330deffb8e3cbe4b5b7aa8ca1f208528cdbd245d5dc63c4483"); } BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) { // test cases 1, 2, 3, 4, 6 and 7 of RFC 4231 TestHMACSHA512( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "4869205468657265", "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde" "daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"); TestHMACSHA512( "4a656665", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554" "9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"); TestHMACSHA512( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" "dddddddddddddddddddddddddddddddddddd", "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39" "bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"); TestHMACSHA512( "0102030405060708090a0b0c0d0e0f10111213141516171819", "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db" "a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"); TestHMACSHA512( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" "65204b6579202d2048617368204b6579204669727374", "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352" "6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"); TestHMACSHA512( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", "5468697320697320612074657374207573696e672061206c6172676572207468" "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" "647320746f20626520686173686564206265666f7265206265696e6720757365" "642062792074686520484d414320616c676f726974686d2e", "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944" "b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"); // Test case with key length 127 bytes. TestHMACSHA512( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "267424dfb8eeb999f3e5ec39a4fe9fd14c923e6187e0897063e5c9e02b2e624a" "c04413e762977df71a9fb5d562b37f89dfdfb930fce2ed1fa783bbc2a203d80e"); // Test case with key length 128 bytes. TestHMACSHA512( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "43aaac07bb1dd97c82c04df921f83b16a68d76815cd1a30d3455ad43a3d80484" "2bb35462be42cc2e4b5902de4d204c1c66d93b47d1383e3e13a3788687d61258"); // Test case with key length 129 bytes. TestHMACSHA512( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665" "4a", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "0b273325191cfc1b4b71d5075c8fcad67696309d292b1dad2cd23983a35feb8e" "fb29795e79f2ef27f68cb1e16d76178c307a67beaad9456fac5fdffeadb16e2c"); } BOOST_AUTO_TEST_CASE(aes_testvectors) { // AES test vectors from FIPS 197. TestAES128("000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a"); TestAES256( "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089"); // AES-ECB test vectors from NIST sp800-38a. TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97"); TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf"); TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688"); TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4"); TestAES256( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8"); TestAES256( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "ae2d8a571e03ac9c9eb76fac45af8e51", "591ccb10d410ed26dc5ba74a31362870"); TestAES256( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "30c81c46a35ce411e5fbc1191a0a52ef", "b6ed21b99ca6f4f9f153e7b1beafed1d"); TestAES256( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "f69f2445df4f9b17ad2b417be66c3710", "23304b7a39f9f3ff067d8d8f9e24ecc7"); } BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) { // NIST AES CBC 128-bit encryption test-vectors TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", false, "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d"); TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", false, "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b2"); TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", false, "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516"); TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", false, "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a7"); // The same vectors with padding enabled TestAES128CBC( "2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", true, "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d8964e0b149c10b7b682e6e39aaeb731c"); TestAES128CBC( "2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", true, "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b255e21d7100b988ffec32feeafaf23538"); TestAES128CBC( "2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", true, "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516f6eccda327bf8e5ec43718b0039adceb"); TestAES128CBC( "2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", true, "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a78cb82807230e1321d3fae00d18cc2012"); // NIST AES CBC 256-bit encryption test-vectors TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "000102030405060708090A0B0C0D0E0F", false, "6bc1bee22e409f96e93d7e117393172a", "f58c4c04d6e5f1ba779eabfb5f7bfbd6"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "F58C4C04D6E5F1BA779EABFB5F7BFBD6", false, "ae2d8a571e03ac9c9eb76fac45af8e51", "9cfc4e967edb808d679f777bc6702c7d"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "9CFC4E967EDB808D679F777BC6702C7D", false, "30c81c46a35ce411e5fbc1191a0a52ef", "39f23369a9d9bacfa530e26304231461"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "39F23369A9D9BACFA530E26304231461", false, "f69f2445df4f9b17ad2b417be66c3710", "b2eb05e2c39be9fcda6c19078c6a9d1b"); // The same vectors with padding enabled TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "000102030405060708090A0B0C0D0E0F", true, "6bc1bee22e409f96e93d7e117393172a", "f58c4c04d6e5f1ba779eabfb5f7bfbd6485a5c81519cf378fa36d42b8547edc0"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "F58C4C04D6E5F1BA779EABFB5F7BFBD6", true, "ae2d8a571e03ac9c9eb76fac45af8e51", "9cfc4e967edb808d679f777bc6702c7d3a3aa5e0213db1a9901f9036cf5102d2"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "9CFC4E967EDB808D679F777BC6702C7D", true, "30c81c46a35ce411e5fbc1191a0a52ef", "39f23369a9d9bacfa530e263042314612f8da707643c90a6f732b3de1d3f5cee"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "39F23369A9D9BACFA530E26304231461", true, "f69f2445df4f9b17ad2b417be66c3710", "b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644"); } BOOST_AUTO_TEST_CASE(chacha20_testvector) { // Test vector from RFC 7539 // test encryption TestChaCha20( "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f" "66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e" "652074697020666f7220746865206675747572652c2073756e73637265656e20776f75" "6c642062652069742e", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65" "c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d" "6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b4" "0b8eedf2785e42874d"); // test keystream output TestChaCha20( "", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0a" "a372600a92b57974cded2b9334794cba40c63e34cdea212c4cf07d41b769a6749f3f63" "0f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a832c89c1" "67eacd901d7e2bf363"); // Test vectors from // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 TestChaCha20( "", "0000000000000000000000000000000000000000000000000000000000000000", 0, 0, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da4159" "7c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); TestChaCha20( "", "0000000000000000000000000000000000000000000000000000000000000001", 0, 0, "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0" "b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963"); TestChaCha20( "", "0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0, "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a05" "0278a7084527214f73efc7fa5b5277062eb7a0433e445f41e3"); TestChaCha20( "", "0000000000000000000000000000000000000000000000000000000000000000", 1, 0, "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4c" "af237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"); TestChaCha20( "", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0, "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a454" "7b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc" "35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563e" "b9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a4750" "32b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d" "6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c89" "4c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d" "38407a6deb3ab78fab78c9"); } BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests) { // Use rfc5869 test vectors but trucated to 32 bytes (our implementation // only support length 32) TestHKDF_SHA256_32( /* IKM */ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", /* salt */ "000102030405060708090a0b0c", /* info */ "f0f1f2f3f4f5f6f7f8f9", /* expected OKM */ "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf"); TestHKDF_SHA256_32( "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122" "232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445" "464748494a4b4c4d4e4f", "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182" "838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5" "a6a7a8a9aaabacadaeaf", "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2" "d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5" "f6f7f8f9fafbfcfdfeff", "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c"); TestHKDF_SHA256_32( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "", "", "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d"); } BOOST_AUTO_TEST_CASE(poly1305_testvector) { // RFC 7539, section 2.5.2. TestPoly1305( "43727970746f6772617068696320466f72756d2052657365617263682047726f7570", "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b", "a8061dc1305136c6c22b8baf0c0127a9"); // RFC 7539, section A.3. TestPoly1305( "0000000000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "00000000000000000000000000000000"); TestPoly1305( "416e79207375626d697373696f6e20746f20746865204945544620696e74656e646564" "2062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e" "20617320616c6c206f722070617274206f6620616e204945544620496e7465726e6574" "2d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164" "652077697468696e2074686520636f6e74657874206f6620616e204945544620616374" "697669747920697320636f6e7369646572656420616e20224945544620436f6e747269" "627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72" "616c2073746174656d656e747320696e20494554462073657373696f6e732c20617320" "77656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d" "756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163" "652c207768696368206172652061646472657373656420746f", "0000000000000000000000000000000036e5f6b5c5e06070f0efca96227a863e", "36e5f6b5c5e06070f0efca96227a863e"); TestPoly1305( "416e79207375626d697373696f6e20746f20746865204945544620696e74656e646564" "2062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e" "20617320616c6c206f722070617274206f6620616e204945544620496e7465726e6574" "2d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164" "652077697468696e2074686520636f6e74657874206f6620616e204945544620616374" "697669747920697320636f6e7369646572656420616e20224945544620436f6e747269" "627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72" "616c2073746174656d656e747320696e20494554462073657373696f6e732c20617320" "77656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d" "756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163" "652c207768696368206172652061646472657373656420746f", "36e5f6b5c5e06070f0efca96227a863e00000000000000000000000000000000", "f3477e7cd95417af89a6b8794c310cf0"); TestPoly1305( "2754776173206272696c6c69672c20616e642074686520736c6974687920746f766573" "0a446964206779726520616e642067696d626c6520696e2074686520776162653a0a41" "6c6c206d696d737920776572652074686520626f726f676f7665732c0a416e64207468" "65206d6f6d65207261746873206f757467726162652e", "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", "4541669a7eaaee61e708dc7cbcc5eb62"); TestPoly1305( "ffffffffffffffffffffffffffffffff", "0200000000000000000000000000000000000000000000000000000000000000", "03000000000000000000000000000000"); TestPoly1305( "02000000000000000000000000000000", "02000000000000000000000000000000ffffffffffffffffffffffffffffffff", "03000000000000000000000000000000"); TestPoly1305( "fffffffffffffffffffffffffffffffff0ffffffffffffffffffffffffffffff110000" "00000000000000000000000000", "0100000000000000000000000000000000000000000000000000000000000000", "05000000000000000000000000000000"); TestPoly1305( "fffffffffffffffffffffffffffffffffbfefefefefefefefefefefefefefefe010101" "01010101010101010101010101", "0100000000000000000000000000000000000000000000000000000000000000", "00000000000000000000000000000000"); TestPoly1305( "fdffffffffffffffffffffffffffffff", "0200000000000000000000000000000000000000000000000000000000000000", "faffffffffffffffffffffffffffffff"); TestPoly1305( "e33594d7505e43b900000000000000003394d7505e4379cd0100000000000000000000" "0000000000000000000000000001000000000000000000000000000000", "0100000000000000040000000000000000000000000000000000000000000000", "14000000000000005500000000000000"); TestPoly1305( "e33594d7505e43b900000000000000003394d7505e4379cd0100000000000000000000" "00000000000000000000000000", "0100000000000000040000000000000000000000000000000000000000000000", "13000000000000000000000000000000"); } static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aad_length, const std::string &hex_m, const std::string &hex_k1, const std::string &hex_k2, const std::string &hex_aad_keystream, const std::string &hex_encrypted_message, const std::string &hex_encrypted_message_seq_999) { // we need two sequence numbers, one for the payload cipher instance... uint32_t seqnr_payload = 0; // ... and one for the AAD (length) cipher instance uint32_t seqnr_aad = 0; // we need to keep track of the position in the AAD cipher instance // keystream since we use the same 64byte output 21 times // (21 times 3 bytes length < 64) int aad_pos = 0; std::vector<uint8_t> aead_K_1 = ParseHex(hex_k1); std::vector<uint8_t> aead_K_2 = ParseHex(hex_k2); std::vector<uint8_t> plaintext_buf = ParseHex(hex_m); std::vector<uint8_t> expected_aad_keystream = ParseHex(hex_aad_keystream); std::vector<uint8_t> expected_ciphertext_and_mac = ParseHex(hex_encrypted_message); std::vector<uint8_t> expected_ciphertext_and_mac_sequence999 = ParseHex(hex_encrypted_message_seq_999); std::vector<uint8_t> ciphertext_buf(plaintext_buf.size() + POLY1305_TAGLEN, 0); std::vector<uint8_t> plaintext_buf_new(plaintext_buf.size(), 0); std::vector<uint8_t> cmp_ctx_buffer(64); uint32_t out_len = 0; // create the AEAD instance ChaCha20Poly1305AEAD aead(aead_K_1.data(), aead_K_1.size(), aead_K_2.data(), aead_K_2.size()); // create a chacha20 instance to compare against ChaCha20 cmp_ctx(aead_K_2.data(), 32); // encipher bool res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true); // make sure the operation succeeded if expected to succeed BOOST_CHECK_EQUAL(res, must_succeed); if (!res) { return; } // verify ciphertext & mac against the test vector BOOST_CHECK_EQUAL(expected_ciphertext_and_mac.size(), ciphertext_buf.size()); BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac.data(), ciphertext_buf.size()) == 0); // manually construct the AAD keystream cmp_ctx.SetIV(seqnr_aad); cmp_ctx.Seek(0); cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64); BOOST_CHECK(memcmp(expected_aad_keystream.data(), cmp_ctx_buffer.data(), expected_aad_keystream.size()) == 0); // crypt the 3 length bytes and compare the length uint32_t len_cmp = 0; len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) | (ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 | (ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16; BOOST_CHECK_EQUAL(len_cmp, expected_aad_length); // encrypt / decrypt 1000 packets for (size_t i = 0; i < 1000; ++i) { res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true); BOOST_CHECK(res); BOOST_CHECK(aead.GetLength(&out_len, seqnr_aad, aad_pos, ciphertext_buf.data())); BOOST_CHECK_EQUAL(out_len, expected_aad_length); res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, plaintext_buf_new.data(), plaintext_buf_new.size(), ciphertext_buf.data(), ciphertext_buf.size(), false); BOOST_CHECK(res); // make sure we repetitive get the same plaintext BOOST_CHECK(memcmp(plaintext_buf.data(), plaintext_buf_new.data(), plaintext_buf.size()) == 0); // compare sequence number 999 against the test vector if (seqnr_payload == 999) { BOOST_CHECK( memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac_sequence999.data(), expected_ciphertext_and_mac_sequence999.size()) == 0); } // set nonce and block counter, output the keystream cmp_ctx.SetIV(seqnr_aad); cmp_ctx.Seek(0); cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64); // crypt the 3 length bytes and compare the length len_cmp = 0; len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) | (ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 | (ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16; BOOST_CHECK_EQUAL(len_cmp, expected_aad_length); // increment the sequence number(s) // always increment the payload sequence number // increment the AAD keystream position by its size (3) // increment the AAD sequence number if we would hit the 64 byte limit seqnr_payload++; aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { aad_pos = 0; seqnr_aad++; } } } BOOST_AUTO_TEST_CASE(chacha20_poly1305_aead_testvector) { /* test chacha20poly1305@bitcoin AEAD */ // must fail with no message TestChaCha20Poly1305AEAD( false, 0, "", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "", "", ""); TestChaCha20Poly1305AEAD( true, 0, /* m */ "0000000000000000000000000000000000000000000000000000000000000000", /* k1 (payload) */ "0000000000000000000000000000000000000000000000000000000000000000", /* k2 (AAD) */ "0000000000000000000000000000000000000000000000000000000000000000", /* AAD keystream */ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da4159" "7c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586", /* encrypted message & MAC */ "76b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32d2fc11" "829c1b6c1df1f551cd6131ff08", /* encrypted message & MAC at sequence 999 */ "b0a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3aaa7aa1" "6ec62c5e24f040c08bb20c3598"); TestChaCha20Poly1305AEAD( true, 1, "0100000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da4159" "7c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586", "77b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32baf0c8" "5b6dff8602b06cf52a6aefc62e", "b1a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3a8bd94d" "54b5ecabbc41ffbb0c90924080"); TestChaCha20Poly1305AEAD( true, 255, "ff0000f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b" "733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35" "941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9" "b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032" "b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d69" "59660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c" "94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38" "407a6deb3ab78fab78c9", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "ff0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "c640c1711e3ee904ac35c57ab9791c8a1c408603a90b77a83b54f6c844cb4b06d94e7f" "c6c800e165acd66147e80ec45a567f6ce66d05ec0cae679dceeb890017", "3940c1e92da4582ff6f92a776aeb14d014d384eeb30f660dacf70a14a23fd31e912127" "01334e2ce1acf5199dc84f4d61ddbe6571bca5af874b4c9226c26e650995d157644e18" "48b96ed6c2102d5489a050e71d29a5a66ece11de5fb5c9558d54da28fe45b0bc4db4e5" "b88030bfc4a352b4b7068eccf656bae7ad6a35615315fc7c49d4200388d5eca67c2e82" "2e069336c69b40db67e0f3c81209c50f3216a4b89fb3ae1b984b7851a2ec6f68ab12b1" "01ab120e1ea7313bb93b5a0f71185c7fea017ddb92769861c29dba4fbc432280d5dff2" "1b36d1c4c790128b22699950bb18bf74c448cdfe547d8ed4f657d8005fdc0cd7a050c2" "d46050a44c4376355858981fbe8b184288276e7a93eabc899c4a", "f039c6689eaeef0456685200feaab9d54bbd9acde4410a3b6f4321296f4a8ca2604b49" "727d8892c57e005d799b2a38e85e809f20146e08eec75169691c8d4f54a0d51a1e1c7b" "381e0474eb02f994be9415ef3ffcbd2343f0601e1f3b172a1d494f838824e4df570f8e" "3b0c04e27966e36c82abd352d07054ef7bd36b84c63f9369afe7ed79b94f953873006b" "920c3fa251a771de1b63da927058ade119aa898b8c97e42a606b2f6df1e2d957c22f75" "93c1e2002f4252f4c9ae4bf773499e5cfcfe14dfc1ede26508953f88553bf4a76a802f" "6a0068d59295b01503fd9a600067624203e880fdf53933b96e1f4d9eb3f4e363dd8165" "a278ff667a41ee42b9892b077cefff92b93441f7be74cf10e6cd"); } BOOST_AUTO_TEST_CASE(countbits_tests) { FastRandomContext ctx; for (unsigned int i = 0; i <= 64; ++i) { if (i == 0) { // Check handling of zero. BOOST_CHECK_EQUAL(CountBits(0), 0U); } else if (i < 10) { for (uint64_t j = 1 << (i - 1); (j >> i) == 0; ++j) { // Exhaustively test up to 10 bits BOOST_CHECK_EQUAL(CountBits(j), i); } } else { for (int k = 0; k < 1000; k++) { // Randomly test 1000 samples of each length above 10 bits. uint64_t j = uint64_t(1) << (i - 1) | ctx.randbits(i - 1); BOOST_CHECK_EQUAL(CountBits(j), i); } } } } BOOST_AUTO_TEST_CASE(sha256d64) { for (int i = 0; i <= 32; ++i) { uint8_t in[64 * 32]; uint8_t out1[32 * 32], out2[32 * 32]; for (int j = 0; j < 64 * i; ++j) { in[j] = InsecureRandBits(8); } for (int j = 0; j < i; ++j) { - CHash256().Write(in + 64 * j, 64).Finalize(out1 + 32 * j); + CHash256().Write({in + 64 * j, 64}).Finalize(out1 + 32 * j); } SHA256D64(out2, in, i); BOOST_CHECK(memcmp(out1, out2, 32 * i) == 0); } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp index 593c7224e..06262a7d1 100644 --- a/src/test/fuzz/crypto.cpp +++ b/src/test/fuzz/crypto.cpp @@ -1,132 +1,132 @@ // Copyright (c) 2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <crypto/hmac_sha256.h> #include <crypto/hmac_sha512.h> #include <crypto/ripemd160.h> #include <crypto/sha1.h> #include <crypto/sha256.h> #include <crypto/sha512.h> #include <hash.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> #include <cstdint> #include <vector> void test_one_input(const std::vector<uint8_t> &buffer) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider); if (data.empty()) { data.resize( fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>()); } CHash160 hash160; CHash256 hash256; CHMAC_SHA256 hmac_sha256{data.data(), data.size()}; CHMAC_SHA512 hmac_sha512{data.data(), data.size()}; CRIPEMD160 ripemd160; CSHA1 sha1; CSHA256 sha256; CSHA512 sha512; CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()}; while (fuzzed_data_provider.ConsumeBool()) { switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) { case 0: { if (fuzzed_data_provider.ConsumeBool()) { data = ConsumeRandomLengthByteVector(fuzzed_data_provider); if (data.empty()) { data.resize( fuzzed_data_provider.ConsumeIntegralInRange<size_t>( 1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>()); } } - (void)hash160.Write(data.data(), data.size()); - (void)hash256.Write(data.data(), data.size()); + (void)hash160.Write(data); + (void)hash256.Write(data); (void)hmac_sha256.Write(data.data(), data.size()); (void)hmac_sha512.Write(data.data(), data.size()); (void)ripemd160.Write(data.data(), data.size()); (void)sha1.Write(data.data(), data.size()); (void)sha256.Write(data.data(), data.size()); (void)sha512.Write(data.data(), data.size()); (void)sip_hasher.Write(data.data(), data.size()); (void)Hash(data.begin(), data.end()); (void)Hash160(data); (void)Hash160(data.begin(), data.end()); (void)sha512.Size(); break; } case 1: { (void)hash160.Reset(); (void)hash256.Reset(); (void)ripemd160.Reset(); (void)sha1.Reset(); (void)sha256.Reset(); (void)sha512.Reset(); break; } case 2: { switch ( fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 8)) { case 0: { data.resize(CHash160::OUTPUT_SIZE); hash160.Finalize(data.data()); break; } case 1: { data.resize(CHash256::OUTPUT_SIZE); hash256.Finalize(data.data()); break; } case 2: { data.resize(CHMAC_SHA256::OUTPUT_SIZE); hmac_sha256.Finalize(data.data()); break; } case 3: { data.resize(CHMAC_SHA512::OUTPUT_SIZE); hmac_sha512.Finalize(data.data()); break; } case 4: { data.resize(CRIPEMD160::OUTPUT_SIZE); ripemd160.Finalize(data.data()); break; } case 5: { data.resize(CSHA1::OUTPUT_SIZE); sha1.Finalize(data.data()); break; } case 6: { data.resize(CSHA256::OUTPUT_SIZE); sha256.Finalize(data.data()); break; } case 7: { data.resize(CSHA512::OUTPUT_SIZE); sha512.Finalize(data.data()); break; } case 8: { data.resize(1); data[0] = sip_hasher.Finalize() % 256; break; } } break; } } } } diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index ac2a12336..60908088a 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -1,410 +1,407 @@ // Copyright (c) 2012-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 <key.h> #include <chainparams.h> // For Params() #include <key_io.h> #include <uint256.h> #include <util/strencodings.h> #include <util/string.h> #include <util/system.h> #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> #include <string> #include <vector> static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; static const std::string strTestSecret1C = "cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN"; static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"; static const std::string addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"; static const std::string addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"; static const std::string addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"; static const std::string addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"; static const std::string strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"; // get r value produced by ECDSA signing algorithm // (assumes ECDSA r is encoded in the canonical manner) static std::vector<uint8_t> get_r_ECDSA(std::vector<uint8_t> sigECDSA) { std::vector<uint8_t> ret(32, 0); assert(sigECDSA[2] == 2); int rlen = sigECDSA[3]; assert(rlen <= 33); assert(sigECDSA[4 + rlen] == 2); if (rlen == 33) { assert(sigECDSA[4] == 0); std::copy(sigECDSA.begin() + 5, sigECDSA.begin() + 37, ret.begin()); } else { std::copy(sigECDSA.begin() + 4, sigECDSA.begin() + (4 + rlen), ret.begin() + (32 - rlen)); } return ret; } BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(internal_test) { // test get_r_ECDSA (defined above) to make sure it's working properly BOOST_CHECK(get_r_ECDSA(ParseHex( "3045022100c6ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" "c82e56a6cc37f9b50463111c9f9229b8f3b3")) == ParseHex("c6ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f78")); BOOST_CHECK(get_r_ECDSA(ParseHex( "3045022046ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" "c82e56a6cc37f9b50463111c9f9229b8f3b3")) == ParseHex("46ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f78")); BOOST_CHECK(get_r_ECDSA(ParseHex( "3045021f4b5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" "c82e56a6cc37f9b50463111c9f9229b8f3b3")) == ParseHex("004b5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f78")); BOOST_CHECK(get_r_ECDSA(ParseHex( "3045021e5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" "c82e56a6cc37f9b50463111c9f9229b8f3b3")) == ParseHex("00005f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f78")); } BOOST_AUTO_TEST_CASE(encode_decode_secret_test) { const auto mainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testParams = CreateChainParams(CBaseChainParams::TESTNET); const auto regParams = CreateChainParams(CBaseChainParams::TESTNET); { // Check the mainnet base58 key CKey mainKey = DecodeSecret(strSecret1C, *mainParams); BOOST_CHECK(mainKey.IsValid() && mainKey.IsCompressed()); CKey testKey = DecodeSecret(strSecret1C, *testParams); BOOST_CHECK(!testKey.IsValid()); CKey regKey = DecodeSecret(strSecret1C, *regParams); BOOST_CHECK(!regKey.IsValid()); } { // Check the testnet and regnet base58 key CKey mainKey = DecodeSecret(strTestSecret1C, *mainParams); BOOST_CHECK(!mainKey.IsValid()); CKey testKey = DecodeSecret(strTestSecret1C, *testParams); BOOST_CHECK(testKey.IsValid() && testKey.IsCompressed()); CKey regKey = DecodeSecret(strTestSecret1C, *regParams); BOOST_CHECK(regKey.IsValid() && regKey.IsCompressed()); } CKey mainKey = DecodeSecret(strSecret1C, *mainParams); CKey testKey = DecodeSecret(strTestSecret1C, *testParams); // Check key conversion. BOOST_CHECK_EQUAL(EncodeSecret(mainKey, *mainParams), strSecret1C); BOOST_CHECK_EQUAL(EncodeSecret(mainKey, *testParams), strTestSecret1C); BOOST_CHECK_EQUAL(EncodeSecret(mainKey, *regParams), strTestSecret1C); BOOST_CHECK_EQUAL(EncodeSecret(testKey, *mainParams), strSecret1C); BOOST_CHECK_EQUAL(EncodeSecret(testKey, *testParams), strTestSecret1C); BOOST_CHECK_EQUAL(EncodeSecret(testKey, *regParams), strTestSecret1C); } BOOST_AUTO_TEST_CASE(key_test1) { CKey key1 = DecodeSecret(strSecret1); BOOST_CHECK(key1.IsValid() && !key1.IsCompressed()); CKey key2 = DecodeSecret(strSecret2); BOOST_CHECK(key2.IsValid() && !key2.IsCompressed()); CKey key1C = DecodeSecret(strSecret1C); BOOST_CHECK(key1C.IsValid() && key1C.IsCompressed()); CKey key2C = DecodeSecret(strSecret2C); BOOST_CHECK(key2C.IsValid() && key2C.IsCompressed()); CKey bad_key = DecodeSecret(strAddressBad); BOOST_CHECK(!bad_key.IsValid()); CPubKey pubkey1 = key1.GetPubKey(); CPubKey pubkey2 = key2.GetPubKey(); CPubKey pubkey1C = key1C.GetPubKey(); CPubKey pubkey2C = key2C.GetPubKey(); BOOST_CHECK(key1.VerifyPubKey(pubkey1)); BOOST_CHECK(!key1.VerifyPubKey(pubkey1C)); BOOST_CHECK(!key1.VerifyPubKey(pubkey2)); BOOST_CHECK(!key1.VerifyPubKey(pubkey2C)); BOOST_CHECK(!key1C.VerifyPubKey(pubkey1)); BOOST_CHECK(key1C.VerifyPubKey(pubkey1C)); BOOST_CHECK(!key1C.VerifyPubKey(pubkey2)); BOOST_CHECK(!key1C.VerifyPubKey(pubkey2C)); BOOST_CHECK(!key2.VerifyPubKey(pubkey1)); BOOST_CHECK(!key2.VerifyPubKey(pubkey1C)); BOOST_CHECK(key2.VerifyPubKey(pubkey2)); BOOST_CHECK(!key2.VerifyPubKey(pubkey2C)); BOOST_CHECK(!key2C.VerifyPubKey(pubkey1)); BOOST_CHECK(!key2C.VerifyPubKey(pubkey1C)); BOOST_CHECK(!key2C.VerifyPubKey(pubkey2)); BOOST_CHECK(key2C.VerifyPubKey(pubkey2C)); const CChainParams &chainParams = Params(); BOOST_CHECK(DecodeDestination(addr1, chainParams) == CTxDestination(PKHash(pubkey1))); BOOST_CHECK(DecodeDestination(addr2, chainParams) == CTxDestination(PKHash(pubkey2))); BOOST_CHECK(DecodeDestination(addr1C, chainParams) == CTxDestination(PKHash(pubkey1C))); BOOST_CHECK(DecodeDestination(addr2C, chainParams) == CTxDestination(PKHash(pubkey2C))); for (int n = 0; n < 16; n++) { std::string strMsg = strprintf("Very secret message %i: 11", n); uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); // normal ECDSA signatures std::vector<uint8_t> sign1, sign2, sign1C, sign2C; BOOST_CHECK(key1.SignECDSA(hashMsg, sign1)); BOOST_CHECK(key2.SignECDSA(hashMsg, sign2)); BOOST_CHECK(key1C.SignECDSA(hashMsg, sign1C)); BOOST_CHECK(key2C.SignECDSA(hashMsg, sign2C)); BOOST_CHECK(pubkey1.VerifyECDSA(hashMsg, sign1)); BOOST_CHECK(!pubkey1.VerifyECDSA(hashMsg, sign2)); BOOST_CHECK(pubkey1.VerifyECDSA(hashMsg, sign1C)); BOOST_CHECK(!pubkey1.VerifyECDSA(hashMsg, sign2C)); BOOST_CHECK(!pubkey2.VerifyECDSA(hashMsg, sign1)); BOOST_CHECK(pubkey2.VerifyECDSA(hashMsg, sign2)); BOOST_CHECK(!pubkey2.VerifyECDSA(hashMsg, sign1C)); BOOST_CHECK(pubkey2.VerifyECDSA(hashMsg, sign2C)); BOOST_CHECK(pubkey1C.VerifyECDSA(hashMsg, sign1)); BOOST_CHECK(!pubkey1C.VerifyECDSA(hashMsg, sign2)); BOOST_CHECK(pubkey1C.VerifyECDSA(hashMsg, sign1C)); BOOST_CHECK(!pubkey1C.VerifyECDSA(hashMsg, sign2C)); BOOST_CHECK(!pubkey2C.VerifyECDSA(hashMsg, sign1)); BOOST_CHECK(pubkey2C.VerifyECDSA(hashMsg, sign2)); BOOST_CHECK(!pubkey2C.VerifyECDSA(hashMsg, sign1C)); BOOST_CHECK(pubkey2C.VerifyECDSA(hashMsg, sign2C)); // compact ECDSA signatures (with key recovery) std::vector<uint8_t> csign1, csign2, csign1C, csign2C; BOOST_CHECK(key1.SignCompact(hashMsg, csign1)); BOOST_CHECK(key2.SignCompact(hashMsg, csign2)); BOOST_CHECK(key1C.SignCompact(hashMsg, csign1C)); BOOST_CHECK(key2C.SignCompact(hashMsg, csign2C)); CPubKey rkey1, rkey2, rkey1C, rkey2C; BOOST_CHECK(rkey1.RecoverCompact(hashMsg, csign1)); BOOST_CHECK(rkey2.RecoverCompact(hashMsg, csign2)); BOOST_CHECK(rkey1C.RecoverCompact(hashMsg, csign1C)); BOOST_CHECK(rkey2C.RecoverCompact(hashMsg, csign2C)); BOOST_CHECK(rkey1 == pubkey1); BOOST_CHECK(rkey2 == pubkey2); BOOST_CHECK(rkey1C == pubkey1C); BOOST_CHECK(rkey2C == pubkey2C); // Schnorr signatures std::vector<uint8_t> ssign1, ssign2, ssign1C, ssign2C; BOOST_CHECK(key1.SignSchnorr(hashMsg, ssign1)); BOOST_CHECK(key2.SignSchnorr(hashMsg, ssign2)); BOOST_CHECK(key1C.SignSchnorr(hashMsg, ssign1C)); BOOST_CHECK(key2C.SignSchnorr(hashMsg, ssign2C)); BOOST_CHECK(pubkey1.VerifySchnorr(hashMsg, ssign1)); BOOST_CHECK(!pubkey1.VerifySchnorr(hashMsg, ssign2)); BOOST_CHECK(pubkey1.VerifySchnorr(hashMsg, ssign1C)); BOOST_CHECK(!pubkey1.VerifySchnorr(hashMsg, ssign2C)); BOOST_CHECK(!pubkey2.VerifySchnorr(hashMsg, ssign1)); BOOST_CHECK(pubkey2.VerifySchnorr(hashMsg, ssign2)); BOOST_CHECK(!pubkey2.VerifySchnorr(hashMsg, ssign1C)); BOOST_CHECK(pubkey2.VerifySchnorr(hashMsg, ssign2C)); BOOST_CHECK(pubkey1C.VerifySchnorr(hashMsg, ssign1)); BOOST_CHECK(!pubkey1C.VerifySchnorr(hashMsg, ssign2)); BOOST_CHECK(pubkey1C.VerifySchnorr(hashMsg, ssign1C)); BOOST_CHECK(!pubkey1C.VerifySchnorr(hashMsg, ssign2C)); BOOST_CHECK(!pubkey2C.VerifySchnorr(hashMsg, ssign1)); BOOST_CHECK(pubkey2C.VerifySchnorr(hashMsg, ssign2)); BOOST_CHECK(!pubkey2C.VerifySchnorr(hashMsg, ssign1C)); BOOST_CHECK(pubkey2C.VerifySchnorr(hashMsg, ssign2C)); // check deterministicity of ECDSA & Schnorr BOOST_CHECK(sign1 == sign1C); BOOST_CHECK(sign2 == sign2C); BOOST_CHECK(ssign1 == ssign1C); BOOST_CHECK(ssign2 == ssign2C); // Extract r value from ECDSA and Schnorr. Make sure they are // distinct (nonce reuse would be dangerous and can leak private key). std::vector<uint8_t> rE1 = get_r_ECDSA(sign1); BOOST_CHECK(ssign1.size() == 64); std::vector<uint8_t> rS1(ssign1.begin(), ssign1.begin() + 32); BOOST_CHECK(rE1.size() == 32); BOOST_CHECK(rS1.size() == 32); BOOST_CHECK(rE1 != rS1); std::vector<uint8_t> rE2 = get_r_ECDSA(sign2); BOOST_CHECK(ssign2.size() == 64); std::vector<uint8_t> rS2(ssign2.begin(), ssign2.begin() + 32); BOOST_CHECK(rE2.size() == 32); BOOST_CHECK(rS2.size() == 32); BOOST_CHECK(rE2 != rS2); } // test deterministic signing expected values std::vector<uint8_t> detsig, detsigc; std::string strMsg = "Very deterministic message"; uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); // ECDSA BOOST_CHECK(key1.SignECDSA(hashMsg, detsig)); BOOST_CHECK(key1C.SignECDSA(hashMsg, detsigc)); BOOST_CHECK(detsig == detsigc); BOOST_CHECK(detsig == ParseHex("304402200c648ad9936cae4006f0b0d7bcbacdcdf5a14260eb550" "c31ddb1eb1a13b1b58602201b868673bb5926d1610a07cd03692d" "fdcb98ed059314f66b457a794f2c4b8e79")); BOOST_CHECK(key2.SignECDSA(hashMsg, detsig)); BOOST_CHECK(key2C.SignECDSA(hashMsg, detsigc)); BOOST_CHECK(detsig == detsigc); BOOST_CHECK(detsig == ParseHex("304402205fb8ff5dbba6110d877169812f5cd939866d9487c6b62" "5785c6876e4fb8ea69a0220711dc4ecff142f7f808905c04bbd41" "89e3d2c689c4be396ed22883c463d6ad7a")); // Compact BOOST_CHECK(key1.SignCompact(hashMsg, detsig)); BOOST_CHECK(key1C.SignCompact(hashMsg, detsigc)); BOOST_CHECK(detsig == ParseHex("1b8c56f224d51415e6ce329144aa1e1c1563e297a005f450df015" "14f3d047681760277e79d57502df27b8feebb001a588aa3a8c2bc" "f5b2367273c15f840638cfc8")); BOOST_CHECK(detsigc == ParseHex("1f8c56f224d51415e6ce329144aa1e1c1563e297a005f450df015" "14f3d047681760277e79d57502df27b8feebb001a588aa3a8c2bc" "f5b2367273c15f840638cfc8")); BOOST_CHECK(key2.SignCompact(hashMsg, detsig)); BOOST_CHECK(key2C.SignCompact(hashMsg, detsigc)); BOOST_CHECK(detsig == ParseHex("1c9ffc56b38fbfc0e3eb2c42dff99d2375982449f35019c1b3d56" "ca62bef187c5103e483a0ad481eaacc224fef4ee2995027300d5f" "2457f7a20c43547aeddbae6e")); BOOST_CHECK(detsigc == ParseHex("209ffc56b38fbfc0e3eb2c42dff99d2375982449f35019c1b3d56" "ca62bef187c5103e483a0ad481eaacc224fef4ee2995027300d5f" "2457f7a20c43547aeddbae6e")); // Schnorr BOOST_CHECK(key1.SignSchnorr(hashMsg, detsig)); BOOST_CHECK(key1C.SignSchnorr(hashMsg, detsigc)); BOOST_CHECK(detsig == detsigc); BOOST_CHECK(detsig == ParseHex("2c56731ac2f7a7e7f11518fc7722a166b02438924ca9d8b4d1113" "47b81d0717571846de67ad3d913a8fdf9d8f3f73161a4c48ae81c" "b183b214765feb86e255ce")); BOOST_CHECK(key2.SignSchnorr(hashMsg, detsig)); BOOST_CHECK(key2C.SignSchnorr(hashMsg, detsigc)); BOOST_CHECK(detsig == detsigc); BOOST_CHECK(detsig == ParseHex("e7167ae0afbba6019b4c7fcfe6de79165d555e8295bd72da1b8aa" "1a5b54305880517cace1bcb0cb515e2eeaffd49f1e4dd49fd7282" "6b4b1573c84da49a38405d")); } BOOST_AUTO_TEST_CASE(key_signature_tests) { // When entropy is specified, we should see at least one high R signature // within 20 signatures CKey key = DecodeSecret(strSecret1); std::string msg = "A message to be signed"; uint256 msg_hash = Hash(msg.begin(), msg.end()); std::vector<uint8_t> sig; bool found = false; for (int i = 1; i <= 20; ++i) { sig.clear(); BOOST_CHECK(key.SignECDSA(msg_hash, sig, false, i)); found = sig[3] == 0x21 && sig[4] == 0x00; if (found) { break; } } BOOST_CHECK(found); // When entropy is not specified, we should always see low R signatures that // are less than 70 bytes in 256 tries We should see at least one signature // that is less than 70 bytes. found = true; bool found_small = false; for (int i = 0; i < 256; ++i) { sig.clear(); msg = "A message to be signed" + ToString(i); msg_hash = Hash(msg.begin(), msg.end()); BOOST_CHECK(key.SignECDSA(msg_hash, sig)); found = sig[3] == 0x20; BOOST_CHECK(sig.size() <= 70); found_small |= sig.size() < 70; } BOOST_CHECK(found); BOOST_CHECK(found_small); } BOOST_AUTO_TEST_CASE(key_key_negation) { // create a dummy hash for signature comparison uint8_t rnd[8]; std::string str = "Bitcoin key verification\n"; GetRandBytes(rnd, sizeof(rnd)); uint256 hash; - CHash256() - .Write((uint8_t *)str.data(), str.size()) - .Write(rnd, sizeof(rnd)) - .Finalize(hash.begin()); + CHash256().Write(MakeUCharSpan(str)).Write(rnd).Finalize(hash.begin()); // import the static test key CKey key = DecodeSecret(strSecret1C); // create a signature std::vector<uint8_t> vch_sig; std::vector<uint8_t> vch_sig_cmp; key.SignECDSA(hash, vch_sig); // negate the key twice BOOST_CHECK(key.GetPubKey().data()[0] == 0x03); key.Negate(); // after the first negation, the signature must be different key.SignECDSA(hash, vch_sig_cmp); BOOST_CHECK(vch_sig_cmp != vch_sig); BOOST_CHECK(key.GetPubKey().data()[0] == 0x02); key.Negate(); // after the second negation, we should have the original key and thus the // same signature key.SignECDSA(hash, vch_sig_cmp); BOOST_CHECK(vch_sig_cmp == vch_sig); BOOST_CHECK(key.GetPubKey().data()[0] == 0x03); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 61643817d..032135093 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -1,387 +1,378 @@ // Copyright (c) 2015-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <consensus/merkle.h> #include <util/strencodings.h> #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> BOOST_FIXTURE_TEST_SUITE(merkle_tests, TestingSetup) static uint256 ComputeMerkleRootFromBranch(const uint256 &leaf, const std::vector<uint256> &vMerkleBranch, uint32_t nIndex) { uint256 hash = leaf; for (std::vector<uint256>::const_iterator it = vMerkleBranch.begin(); it != vMerkleBranch.end(); ++it) { if (nIndex & 1) { hash = Hash(it->begin(), it->end(), hash.begin(), hash.end()); } else { hash = Hash(hash.begin(), hash.end(), it->begin(), it->end()); } nIndex >>= 1; } return hash; } /** * This implements a constant-space merkle root/path calculator, limited to 2^32 * leaves. */ static void MerkleComputation(const std::vector<uint256> &leaves, uint256 *proot, bool *pmutated, uint32_t branchpos, std::vector<uint256> *pbranch) { if (pbranch) { pbranch->clear(); } if (leaves.size() == 0) { if (pmutated) { *pmutated = false; } if (proot) { *proot = uint256(); } return; } bool mutated = false; // count is the number of leaves processed so far. uint32_t count = 0; // inner is an array of eagerly computed subtree hashes, indexed by tree // level (0 being the leaves). // For example, when count is 25 (11001 in binary), inner[4] is the hash of // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to // the last leaf. The other inner entries are undefined. uint256 inner[32]; // Which position in inner is a hash that depends on the matching leaf. int matchlevel = -1; // First process all leaves into 'inner' values. while (count < leaves.size()) { uint256 h = leaves[count]; bool matchh = count == branchpos; count++; int level; // For each of the lower bits in count that are 0, do 1 step. Each // corresponds to an inner value that existed before processing the // current leaf, and each needs a hash to combine it. for (level = 0; !(count & (((uint32_t)1) << level)); level++) { if (pbranch) { if (matchh) { pbranch->push_back(inner[level]); } else if (matchlevel == level) { pbranch->push_back(h); matchh = true; } } mutated |= (inner[level] == h); - CHash256() - .Write(inner[level].begin(), 32) - .Write(h.begin(), 32) - .Finalize(h.begin()); + CHash256().Write(inner[level]).Write(h).Finalize(h.begin()); } // Store the resulting hash at inner position level. inner[level] = h; if (matchh) { matchlevel = level; } } // Do a final 'sweep' over the rightmost branch of the tree to process // odd levels, and reduce everything to a single top value. // Level is the level (counted from the bottom) up to which we've sweeped. int level = 0; // As long as bit number level in count is zero, skip it. It means there // is nothing left at this level. while (!(count & (((uint32_t)1) << level))) { level++; } uint256 h = inner[level]; bool matchh = matchlevel == level; while (count != (((uint32_t)1) << level)) { // If we reach this point, h is an inner value that is not the top. // We combine it with itself (Bitcoin's special rule for odd levels in // the tree) to produce a higher level one. if (pbranch && matchh) { pbranch->push_back(h); } - CHash256() - .Write(h.begin(), 32) - .Write(h.begin(), 32) - .Finalize(h.begin()); + CHash256().Write(h).Write(h).Finalize(h.begin()); // Increment count to the value it would have if two entries at this // level had existed. count += (((uint32_t)1) << level); level++; // And propagate the result upwards accordingly. while (!(count & (((uint32_t)1) << level))) { if (pbranch) { if (matchh) { pbranch->push_back(inner[level]); } else if (matchlevel == level) { pbranch->push_back(h); matchh = true; } } - CHash256() - .Write(inner[level].begin(), 32) - .Write(h.begin(), 32) - .Finalize(h.begin()); + CHash256().Write(inner[level]).Write(h).Finalize(h.begin()); level++; } } // Return result. if (pmutated) { *pmutated = mutated; } if (proot) { *proot = h; } } static std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256> &leaves, uint32_t position) { std::vector<uint256> ret; MerkleComputation(leaves, nullptr, nullptr, position, &ret); return ret; } static std::vector<uint256> BlockMerkleBranch(const CBlock &block, uint32_t position) { std::vector<uint256> leaves; leaves.resize(block.vtx.size()); for (size_t s = 0; s < block.vtx.size(); s++) { leaves[s] = block.vtx[s]->GetHash(); } return ComputeMerkleBranch(leaves, position); } // Older version of the merkle root computation code, for comparison. static uint256 BlockBuildMerkleTree(const CBlock &block, bool *fMutated, std::vector<uint256> &vMerkleTree) { vMerkleTree.clear(); // Safe upper bound for the number of total nodes. vMerkleTree.reserve(block.vtx.size() * 2 + 16); for (std::vector<CTransactionRef>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it) { vMerkleTree.push_back((*it)->GetId()); } int j = 0; bool mutated = false; for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) { for (int i = 0; i < nSize; i += 2) { int i2 = std::min(i + 1, nSize - 1); if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j + i] == vMerkleTree[j + i2]) { // Two identical hashes at the end of the list at a particular // level. mutated = true; } vMerkleTree.push_back( Hash(vMerkleTree[j + i].begin(), vMerkleTree[j + i].end(), vMerkleTree[j + i2].begin(), vMerkleTree[j + i2].end())); } j += nSize; } if (fMutated) { *fMutated = mutated; } return (vMerkleTree.empty() ? uint256() : vMerkleTree.back()); } // Older version of the merkle branch computation code, for comparison. static std::vector<uint256> BlockGetMerkleBranch(const CBlock &block, const std::vector<uint256> &vMerkleTree, int nIndex) { std::vector<uint256> vMerkleBranch; int j = 0; for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) { int i = std::min(nIndex ^ 1, nSize - 1); vMerkleBranch.push_back(vMerkleTree[j + i]); nIndex >>= 1; j += nSize; } return vMerkleBranch; } static inline int ctz(uint32_t i) { if (i == 0) { return 0; } int j = 0; while (!(i & 1)) { j++; i >>= 1; } return j; } BOOST_AUTO_TEST_CASE(merkle_test) { for (int i = 0; i < 32; i++) { // Try 32 block sizes: all sizes from 0 to 16 inclusive, and then 15 // random sizes. int ntx = (i <= 16) ? i : 17 + (InsecureRandRange(4000)); // Try up to 3 mutations. for (int mutate = 0; mutate <= 3; mutate++) { // The last how many transactions to duplicate first. int duplicate1 = mutate >= 1 ? 1 << ctz(ntx) : 0; if (duplicate1 >= ntx) { // Duplication of the entire tree results in a different root // (it adds a level). break; } // The resulting number of transactions after the first duplication. int ntx1 = ntx + duplicate1; // Likewise for the second mutation. int duplicate2 = mutate >= 2 ? 1 << ctz(ntx1) : 0; if (duplicate2 >= ntx1) { break; } int ntx2 = ntx1 + duplicate2; // And for the third mutation. int duplicate3 = mutate >= 3 ? 1 << ctz(ntx2) : 0; if (duplicate3 >= ntx2) { break; } int ntx3 = ntx2 + duplicate3; // Build a block with ntx different transactions. CBlock block; block.vtx.resize(ntx); for (int j = 0; j < ntx; j++) { CMutableTransaction mtx; mtx.nLockTime = j; block.vtx[j] = MakeTransactionRef(std::move(mtx)); } // Compute the root of the block before mutating it. bool unmutatedMutated = false; uint256 unmutatedRoot = BlockMerkleRoot(block, &unmutatedMutated); BOOST_CHECK(unmutatedMutated == false); // Optionally mutate by duplicating the last transactions, resulting // in the same merkle root. block.vtx.resize(ntx3); for (int j = 0; j < duplicate1; j++) { block.vtx[ntx + j] = block.vtx[ntx + j - duplicate1]; } for (int j = 0; j < duplicate2; j++) { block.vtx[ntx1 + j] = block.vtx[ntx1 + j - duplicate2]; } for (int j = 0; j < duplicate3; j++) { block.vtx[ntx2 + j] = block.vtx[ntx2 + j - duplicate3]; } // Compute the merkle root and merkle tree using the old mechanism. bool oldMutated = false; std::vector<uint256> merkleTree; uint256 oldRoot = BlockBuildMerkleTree(block, &oldMutated, merkleTree); // Compute the merkle root using the new mechanism. bool newMutated = false; uint256 newRoot = BlockMerkleRoot(block, &newMutated); BOOST_CHECK(oldRoot == newRoot); BOOST_CHECK(newRoot == unmutatedRoot); BOOST_CHECK((newRoot == uint256()) == (ntx == 0)); BOOST_CHECK(oldMutated == newMutated); BOOST_CHECK(newMutated == !!mutate); // If no mutation was done (once for every ntx value), try up to 16 // branches. if (mutate == 0) { for (int loop = 0; loop < std::min(ntx, 16); loop++) { // If ntx <= 16, try all branches. Otherwise, try 16 random // ones. int mtx = loop; if (ntx > 16) { mtx = InsecureRandRange(ntx); } std::vector<uint256> newBranch = BlockMerkleBranch(block, mtx); std::vector<uint256> oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx); BOOST_CHECK(oldBranch == newBranch); BOOST_CHECK( ComputeMerkleRootFromBranch(block.vtx[mtx]->GetId(), newBranch, mtx) == oldRoot); } } } } } BOOST_AUTO_TEST_CASE(merkle_test_empty_block) { bool mutated = false; CBlock block; uint256 root = BlockMerkleRoot(block, &mutated); BOOST_CHECK_EQUAL(root.IsNull(), true); BOOST_CHECK_EQUAL(mutated, false); } BOOST_AUTO_TEST_CASE(merkle_test_oneTx_block) { bool mutated = false; CBlock block; block.vtx.resize(1); CMutableTransaction mtx; mtx.nLockTime = 0; block.vtx[0] = MakeTransactionRef(std::move(mtx)); uint256 root = BlockMerkleRoot(block, &mutated); BOOST_CHECK_EQUAL(root, block.vtx[0]->GetHash()); BOOST_CHECK_EQUAL(mutated, false); } BOOST_AUTO_TEST_CASE(merkle_test_OddTxWithRepeatedLastTx_block) { bool mutated; CBlock block, blockWithRepeatedLastTx; block.vtx.resize(3); for (std::size_t pos = 0; pos < block.vtx.size(); pos++) { CMutableTransaction mtx; mtx.nLockTime = pos; block.vtx[pos] = MakeTransactionRef(std::move(mtx)); } blockWithRepeatedLastTx = block; blockWithRepeatedLastTx.vtx.push_back(blockWithRepeatedLastTx.vtx.back()); uint256 rootofBlock = BlockMerkleRoot(block, &mutated); BOOST_CHECK_EQUAL(mutated, false); uint256 rootofBlockWithRepeatedLastTx = BlockMerkleRoot(blockWithRepeatedLastTx, &mutated); BOOST_CHECK_EQUAL(rootofBlock, rootofBlockWithRepeatedLastTx); BOOST_CHECK_EQUAL(mutated, true); } BOOST_AUTO_TEST_CASE(merkle_test_LeftSubtreeRightSubtree) { CBlock block, leftSubtreeBlock, rightSubtreeBlock; block.vtx.resize(4); std::size_t pos; for (pos = 0; pos < block.vtx.size(); pos++) { CMutableTransaction mtx; mtx.nLockTime = pos; block.vtx[pos] = MakeTransactionRef(std::move(mtx)); } for (pos = 0; pos < block.vtx.size() / 2; pos++) { leftSubtreeBlock.vtx.push_back(block.vtx[pos]); } for (pos = block.vtx.size() / 2; pos < block.vtx.size(); pos++) { rightSubtreeBlock.vtx.push_back(block.vtx[pos]); } uint256 root = BlockMerkleRoot(block); uint256 rootOfLeftSubtree = BlockMerkleRoot(leftSubtreeBlock); uint256 rootOfRightSubtree = BlockMerkleRoot(rightSubtreeBlock); std::vector<uint256> leftRight; leftRight.push_back(rootOfLeftSubtree); leftRight.push_back(rootOfRightSubtree); uint256 rootOfLR = ComputeMerkleRoot(leftRight); BOOST_CHECK_EQUAL(root, rootOfLR); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp index c254a6fe5..813cda719 100644 --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -1,295 +1,295 @@ // 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 <util/settings.h> #include <test/util/setup_common.h> #include <test/util/str.h> #include <util/strencodings.h> #include <util/string.h> #include <util/system.h> #include <univalue.h> #include <boost/test/unit_test.hpp> #include <vector> inline bool operator==(const util::SettingsValue &a, const util::SettingsValue &b) { return a.write() == b.write(); } inline std::ostream &operator<<(std::ostream &os, const util::SettingsValue &value) { os << value.write(); return os; } inline std::ostream & operator<<(std::ostream &os, const std::pair<std::string, util::SettingsValue> &kv) { util::SettingsValue out(util::SettingsValue::VOBJ); out.__pushKV(kv.first, kv.second); os << out.write(); return os; } inline void WriteText(const fs::path &path, const std::string &text) { fsbridge::ofstream file; file.open(path); file << text; } BOOST_FIXTURE_TEST_SUITE(settings_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(ReadWrite) { fs::path path = GetDataDir() / "settings.json"; WriteText(path, R"({ "string": "string", "num": 5, "bool": true, "null": null })"); std::map<std::string, util::SettingsValue> expected{ {"string", "string"}, {"num", 5}, {"bool", true}, {"null", {}}, }; // Check file read. std::map<std::string, util::SettingsValue> values; std::vector<std::string> errors; BOOST_CHECK(util::ReadSettings(path, values, errors)); BOOST_CHECK_EQUAL_COLLECTIONS(values.begin(), values.end(), expected.begin(), expected.end()); BOOST_CHECK(errors.empty()); // Check no errors if file doesn't exist. fs::remove(path); BOOST_CHECK(util::ReadSettings(path, values, errors)); BOOST_CHECK(values.empty()); BOOST_CHECK(errors.empty()); // Check duplicate keys not allowed WriteText(path, R"({ "dupe": "string", "dupe": "dupe" })"); BOOST_CHECK(!util::ReadSettings(path, values, errors)); std::vector<std::string> dup_keys = {strprintf( "Found duplicate key dupe in settings file %s", path.string())}; BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), dup_keys.begin(), dup_keys.end()); // Check non-kv json files not allowed WriteText(path, R"("non-kv")"); BOOST_CHECK(!util::ReadSettings(path, values, errors)); std::vector<std::string> non_kv = { strprintf("Found non-object value \"non-kv\" in settings file %s", path.string())}; BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), non_kv.begin(), non_kv.end()); // Check invalid json not allowed WriteText(path, R"(invalid json)"); BOOST_CHECK(!util::ReadSettings(path, values, errors)); std::vector<std::string> fail_parse = { strprintf("Unable to parse settings file %s", path.string())}; BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), fail_parse.begin(), fail_parse.end()); } //! Check settings struct contents against expected json strings. static void CheckValues(const util::Settings &settings, const std::string &single_val, const std::string &list_val) { util::SettingsValue single_value = GetSetting(settings, "section", "name", false, false); util::SettingsValue list_value(util::SettingsValue::VARR); for (const auto &item : GetSettingsList(settings, "section", "name", false)) { list_value.push_back(item); } BOOST_CHECK_EQUAL(single_value.write().c_str(), single_val); BOOST_CHECK_EQUAL(list_value.write().c_str(), list_val); }; // Simple settings merge test case. BOOST_AUTO_TEST_CASE(Simple) { util::Settings settings; settings.command_line_options["name"].push_back("val1"); settings.command_line_options["name"].push_back("val2"); settings.ro_config["section"]["name"].push_back(2); // The last given arg takes precedence when specified via commandline. CheckValues(settings, R"("val2")", R"(["val1","val2",2])"); util::Settings settings2; settings2.ro_config["section"]["name"].push_back("val2"); settings2.ro_config["section"]["name"].push_back("val3"); // The first given arg takes precedence when specified via config file. CheckValues(settings2, R"("val2")", R"(["val2","val3"])"); } // Confirm that a high priority setting overrides a lower priority setting even // if the high priority setting is null. This behavior is useful for a high // priority setting source to be able to effectively reset any setting back to // its default value. BOOST_AUTO_TEST_CASE(NullOverride) { util::Settings settings; settings.command_line_options["name"].push_back("value"); BOOST_CHECK_EQUAL( R"("value")", GetSetting(settings, "section", "name", false, false).write().c_str()); settings.forced_settings["name"] = {}; BOOST_CHECK_EQUAL( R"(null)", GetSetting(settings, "section", "name", false, false).write().c_str()); } // 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. struct MergeTestingSetup : 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 { END, 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 force_set : {false, true}) { for (bool ignore_default_section_config : {false, true}) { fn(arg_actions, conf_actions, force_set, ignore_default_section_config); } } }); }); } }; // 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(Merge, MergeTestingSetup) { CHash256 out_sha; FILE *out_file = nullptr; if (const char *out_path = getenv("SETTINGS_MERGE_TEST_OUT")) { out_file = fsbridge::fopen(out_path, "w"); if (!out_file) { throw std::system_error(errno, std::generic_category(), "fopen failed"); } } const std::string &network = CBaseChainParams::MAIN; ForEachMergeSetup([&](const ActionList &arg_actions, const ActionList &conf_actions, bool force_set, bool ignore_default_section_config) { std::string desc; int value_suffix = 0; util::Settings settings; const std::string &name = ignore_default_section_config ? "wallet" : "server"; auto push_values = [&](Action action, const char *value_prefix, const std::string &name_prefix, std::vector<util::SettingsValue> &dest) { if (action == SET || action == SECTION_SET) { for (int i = 0; i < 2; ++i) { dest.push_back(value_prefix + ToString(++value_suffix)); desc += " " + name_prefix + name + "=" + dest.back().get_str(); } } else if (action == NEGATE || action == SECTION_NEGATE) { dest.push_back(false); desc += " " + name_prefix + "no" + name; } }; if (force_set) { settings.forced_settings[name] = "forced"; desc += " " + name + "=forced"; } for (Action arg_action : arg_actions) { push_values(arg_action, "a", "-", settings.command_line_options[name]); } for (Action conf_action : conf_actions) { bool use_section = conf_action == SECTION_SET || conf_action == SECTION_NEGATE; push_values(conf_action, "c", use_section ? network + "." : "", settings.ro_config[use_section ? network : ""][name]); } desc += " || "; desc += GetSetting(settings, network, name, ignore_default_section_config, /* get_chain_name= */ false) .write(); desc += " |"; for (const auto &s : GetSettingsList(settings, network, name, ignore_default_section_config)) { desc += " "; desc += s.write(); } desc += " |"; if (OnlyHasDefaultSectionSetting(settings, network, name)) { desc += " ignored"; } desc += "\n"; - out_sha.Write((const uint8_t *)desc.data(), desc.size()); + 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(std::begin(out_sha_bytes), std::end(out_sha_bytes)); // If check below fails, should manually dump the results with: // // SETTINGS_MERGE_TEST_OUT=results.txt ./test_bitcoin // --run_test=settings_tests/Merge // // And verify diff against previous results to make sure the changes are // expected. // // Results file is formatted like: // // <input> || GetSetting() | GetSettingsList() | // OnlyHasDefaultSectionSetting() BOOST_CHECK_EQUAL( out_sha_hex, "79db02d74e3e193196541b67c068b40ebd0c124a24b3ecbe9cbf7e85b1c4ba7a"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 2a056091a..256eb5023 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1,2527 +1,2527 @@ // 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 <util/system.h> #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/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/setup_common.h> #include <univalue.h> #include <boost/test/unit_test.hpp> #include <array> #include <cstdint> #include <utility> #ifndef WIN32 #include <csignal> #include <sys/types.h> #include <sys/wait.h> #endif #include <thread> #include <vector> /* 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_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) { 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); // 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, ParseHex_expected + sizeof(ParseHex_expected)), "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0" "ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d" "578a4c702b6bf11d5f"); BOOST_CHECK_EQUAL(HexStr(ParseHex_expected + sizeof(ParseHex_expected), ParseHex_expected + sizeof(ParseHex_expected)), ""); BOOST_CHECK_EQUAL(HexStr(ParseHex_expected, ParseHex_expected), ""); std::vector<uint8_t> ParseHex_vec(ParseHex_expected, ParseHex_expected + 5); BOOST_CHECK_EQUAL(HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend()), "b0fd8a6704"); BOOST_CHECK_EQUAL( HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected), std::reverse_iterator<const uint8_t *>(ParseHex_expected)), ""); BOOST_CHECK_EQUAL( HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1), std::reverse_iterator<const uint8_t *>(ParseHex_expected)), "04"); BOOST_CHECK_EQUAL( HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5), std::reverse_iterator<const uint8_t *>(ParseHex_expected)), "b0fd8a6704"); BOOST_CHECK_EQUAL( HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 65), std::reverse_iterator<const uint8_t *>(ParseHex_expected)), "5f1df16b2b704c8a578d0bbaf74d385cde12c11ee50455f3c438ef4c3fbcf649b6de61" "1feae06279a60939e028a8d65c10b73071a6f16719274855feb0fd8a6704"); } 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_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); auto time = GetSystemTimeInSeconds(); BOOST_CHECK_EQUAL(ParseISO8601DateTime(FormatISO8601DateTime(time)), time); } 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(), 0); } else { BOOST_CHECK_EQUAL(settings_list.size(), 1); 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.GetArg("-value", 99999), 99999); } else if (expect.int_value) { BOOST_CHECK_EQUAL(test.GetArg("-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.GetArg("-value", 99998), expected_int); BOOST_CHECK_EQUAL(test.GetArg("-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.GetArg("inttest1", -1), 12345); BOOST_CHECK_EQUAL(testArgs.GetArg("inttest2", -1), 81985529216486895LL); BOOST_CHECK_EQUAL(testArgs.GetArg("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 §ion : {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 §ion, 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 §ion, 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((const uint8_t *)desc.data(), desc.size()); + 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(std::begin(out_sha_bytes), std::end(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((const uint8_t *)desc.data(), desc.size()); + 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(std::begin(out_sha_bytes), std::end(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.LockSettings([&](util::Settings &settings) { settings.rw_settings["name"] = "value"; }); args1.WriteSettingsFile(); // Test reading setting. TestArgsManager args2; 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(GetDataDir() / "settings.json"); fs::create_directory(GetDataDir() / "settings.json"); args2.WriteSettingsFile(); fs::remove(GetDataDir() / "settings.json"); } } BOOST_AUTO_TEST_CASE(util_FormatMoney) { BOOST_CHECK_EQUAL(FormatMoney(Amount::zero()), "0.00"); BOOST_CHECK_EQUAL(FormatMoney(123456789 * (COIN / 10000)), "12345.6789"); BOOST_CHECK_EQUAL(FormatMoney(-1 * COIN), "-1.00"); BOOST_CHECK_EQUAL(FormatMoney(100000000 * COIN), "100000000.00"); BOOST_CHECK_EQUAL(FormatMoney(10000000 * COIN), "10000000.00"); BOOST_CHECK_EQUAL(FormatMoney(1000000 * COIN), "1000000.00"); BOOST_CHECK_EQUAL(FormatMoney(100000 * COIN), "100000.00"); BOOST_CHECK_EQUAL(FormatMoney(10000 * COIN), "10000.00"); BOOST_CHECK_EQUAL(FormatMoney(1000 * COIN), "1000.00"); BOOST_CHECK_EQUAL(FormatMoney(100 * COIN), "100.00"); BOOST_CHECK_EQUAL(FormatMoney(10 * COIN), "10.00"); BOOST_CHECK_EQUAL(FormatMoney(COIN), "1.00"); BOOST_CHECK_EQUAL(FormatMoney(COIN / 10), "0.10"); BOOST_CHECK_EQUAL(FormatMoney(COIN / 100), "0.01"); BOOST_CHECK_EQUAL(FormatMoney(COIN / 1000), "0.001"); BOOST_CHECK_EQUAL(FormatMoney(COIN / 10000), "0.0001"); BOOST_CHECK_EQUAL(FormatMoney(COIN / 100000), "0.00001"); BOOST_CHECK_EQUAL(FormatMoney(COIN / 1000000), "0.000001"); BOOST_CHECK_EQUAL(FormatMoney(COIN / 10000000), "0.0000001"); BOOST_CHECK_EQUAL(FormatMoney(COIN / 100000000), "0.00000001"); } 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("12345.6789", ret)); BOOST_CHECK_EQUAL(ret, 123456789 * (COIN / 10000)); BOOST_CHECK(ParseMoney("100000000.00", ret)); BOOST_CHECK_EQUAL(ret, 100000000 * COIN); BOOST_CHECK(ParseMoney("10000000.00", ret)); BOOST_CHECK_EQUAL(ret, 10000000 * COIN); BOOST_CHECK(ParseMoney("1000000.00", ret)); BOOST_CHECK_EQUAL(ret, 1000000 * COIN); BOOST_CHECK(ParseMoney("100000.00", ret)); BOOST_CHECK_EQUAL(ret, 100000 * COIN); BOOST_CHECK(ParseMoney("10000.00", ret)); BOOST_CHECK_EQUAL(ret, 10000 * COIN); BOOST_CHECK(ParseMoney("1000.00", ret)); BOOST_CHECK_EQUAL(ret, 1000 * COIN); BOOST_CHECK(ParseMoney("100.00", ret)); BOOST_CHECK_EQUAL(ret, 100 * COIN); BOOST_CHECK(ParseMoney("10.00", ret)); BOOST_CHECK_EQUAL(ret, 10 * COIN); BOOST_CHECK(ParseMoney("1.00", ret)); BOOST_CHECK_EQUAL(ret, COIN); BOOST_CHECK(ParseMoney("1", ret)); BOOST_CHECK_EQUAL(ret, COIN); BOOST_CHECK(ParseMoney(" 1", ret)); BOOST_CHECK_EQUAL(ret, COIN); BOOST_CHECK(ParseMoney("1 ", ret)); BOOST_CHECK_EQUAL(ret, COIN); BOOST_CHECK(ParseMoney(" 1 ", ret)); BOOST_CHECK_EQUAL(ret, COIN); BOOST_CHECK(ParseMoney("0.1", ret)); BOOST_CHECK_EQUAL(ret, COIN / 10); BOOST_CHECK(ParseMoney("0.01", ret)); BOOST_CHECK_EQUAL(ret, COIN / 100); BOOST_CHECK(ParseMoney("0.001", ret)); BOOST_CHECK_EQUAL(ret, COIN / 1000); BOOST_CHECK(ParseMoney("0.0001", ret)); BOOST_CHECK_EQUAL(ret, COIN / 10000); BOOST_CHECK(ParseMoney("0.00001", ret)); BOOST_CHECK_EQUAL(ret, COIN / 100000); BOOST_CHECK(ParseMoney("0.000001", ret)); BOOST_CHECK_EQUAL(ret, COIN / 1000000); BOOST_CHECK(ParseMoney("0.0000001", ret)); BOOST_CHECK_EQUAL(ret, COIN / 10000000); BOOST_CHECK(ParseMoney("0.00000001", ret)); BOOST_CHECK_EQUAL(ret, COIN / 100000000); BOOST_CHECK(ParseMoney(" 0.00000001 ", ret)); BOOST_CHECK_EQUAL(ret, COIN / 100000000); BOOST_CHECK(ParseMoney("0.00000001 ", ret)); BOOST_CHECK_EQUAL(ret, COIN / 100000000); BOOST_CHECK(ParseMoney(" 0.00000001", ret)); BOOST_CHECK_EQUAL(ret, COIN / 100000000); // Parsing amount that can not be represented in ret should fail 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)); // Attempted 63 bit overflow should fail BOOST_CHECK(!ParseMoney("92233720368.54775808", ret)); // Parsing negative amounts must fail BOOST_CHECK(!ParseMoney("-1", ret)); // Parsing strings with embedded NUL characters should fail BOOST_CHECK(!ParseMoney(std::string("\0-1", 3), ret)); BOOST_CHECK(!ParseMoney(std::string("\01", 2), ret)); BOOST_CHECK(!ParseMoney(std::string("1\0", 2), 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 : {0, 1}) { UninterruptibleSleep(std::chrono::milliseconds{num_sleep}); BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter BOOST_CHECK_EQUAL(111, GetTime<std::chrono::seconds>().count()); BOOST_CHECK_EQUAL(111000, GetTime<std::chrono::milliseconds>().count()); BOOST_CHECK_EQUAL(111000000, GetTime<std::chrono::microseconds>().count()); } SetMockTime(0); // Check that system time changes after a sleep const auto ms_0 = GetTime<std::chrono::milliseconds>(); const auto us_0 = GetTime<std::chrono::microseconds>(); UninterruptibleSleep(std::chrono::milliseconds{1}); 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); // Invalid values BOOST_CHECK(!ParseInt32("", &n)); BOOST_CHECK(!ParseInt32(" 1", &n)); // no padding inside 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("0x1", &n)); // no hex const char test_bytes[] = {'1', 0, '1'}; std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseInt32(teststr, &n)); // no embedded NULs // Overflow and underflow BOOST_CHECK(!ParseInt32("-2147483649", nullptr)); BOOST_CHECK(!ParseInt32("2147483648", nullptr)); BOOST_CHECK(!ParseInt32("-32482348723847471234", nullptr)); BOOST_CHECK(!ParseInt32("32482348723847471234", nullptr)); } 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 const char test_bytes[] = {'1', 0, '1'}; std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseInt64(teststr, &n)); // no embedded NULs // 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_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); // Invalid values 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("1a", &n)); BOOST_CHECK(!ParseUInt32("aap", &n)); BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex const char test_bytes[] = {'1', 0, '1'}; std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseUInt32(teststr, &n)); // no embedded NULs // 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 const char test_bytes[] = {'1', 0, '1'}; std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseUInt64(teststr, &n)); // no embedded NULs // 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_ParseDouble) { double n; // Valid values BOOST_CHECK(ParseDouble("1234", nullptr)); BOOST_CHECK(ParseDouble("0", &n) && n == 0.0); BOOST_CHECK(ParseDouble("1234", &n) && n == 1234.0); BOOST_CHECK(ParseDouble("01234", &n) && n == 1234.0); // no octal BOOST_CHECK(ParseDouble("2147483647", &n) && n == 2147483647.0); BOOST_CHECK(ParseDouble("-2147483648", &n) && n == -2147483648.0); BOOST_CHECK(ParseDouble("-1234", &n) && n == -1234.0); BOOST_CHECK(ParseDouble("1e6", &n) && n == 1e6); BOOST_CHECK(ParseDouble("-1e6", &n) && n == -1e6); // Invalid values BOOST_CHECK(!ParseDouble("", &n)); BOOST_CHECK(!ParseDouble(" 1", &n)); // no padding inside BOOST_CHECK(!ParseDouble("1 ", &n)); BOOST_CHECK(!ParseDouble("1a", &n)); BOOST_CHECK(!ParseDouble("aap", &n)); BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex const char test_bytes[] = {'1', 0, '1'}; std::string teststr(test_bytes, sizeof(test_bytes)); BOOST_CHECK(!ParseDouble(teststr, &n)); // no embedded NULs // Overflow and underflow BOOST_CHECK(!ParseDouble("-1e10000", nullptr)); BOOST_CHECK(!ParseDouble("1e10000", nullptr)); } 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_FormatSubVersion) { 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( FormatSubVersion("Test", 99900, std::vector<std::string>()), std::string("/Test:0.9.99/")); BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments), std::string("/Test:0.9.99(comment1)/")); BOOST_CHECK_EQUAL( FormatSubVersion("Test", 99900, 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 = GetDataDir() / "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 = GetDataDir(); BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true); // Should not be able to write to a non-existent dir. tmpdirname = tmpdirname / fs::unique_path(); 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(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(), 4); 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(), 1); BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one#two#three"); input = "one#two#three"; results = Split(input, '#'); BOOST_CHECK_EQUAL(results.size(), 3); 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(), 4); 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_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(), 1); BOOST_CHECK(v1[0].origin == &t1); BOOST_CHECK_EQUAL(v1[0].copies, 1); auto v2 = Vector(std::move(t2)); BOOST_CHECK_EQUAL(v2.size(), 1); 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(), 2); 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(), 3); 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(), 4); 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(), 3); 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(), 4); 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(), 3); 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 = "IED/" "JtZs3huKX9JEQIBPZPSZwOiMyDQ+" "yNWQvL7YcFzCOGTcfleWOWSvfggenKCinqvOX8t1Iw+HYZqQjxzXQm0="; 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(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", "IPojfrX2dfPnH26UegfbGQQLrdK844DlHq5157/P6h57WyuS/Qsl+h/" "WSVGDF4MUi4rWSswW38oimDYfNNUBUOk=", "Trust no one"), MessageVerificationResult::OK); BOOST_CHECK_EQUAL( MessageVerify(*params, "11canuhp9X2NocwCq7xNrQYTmUgZAnLK3", "IIcaIENoYW5jZWxsb3Igb24gYnJpbmsgb2Ygc2Vjb25kIGJhaWxvdXQg" "Zm9yIGJhbmtzIAaHRtbCeDZINyavx14=", "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.begin(), unsigned_tx.end()); const uint256 message_hash1 = Hash(prefixed_message.begin(), prefixed_message.end()); const uint256 message_hash2 = MessageHash(unsigned_tx); BOOST_CHECK_EQUAL(message_hash1, message_hash2); BOOST_CHECK_NE(message_hash1, signature_hash); } BOOST_AUTO_TEST_SUITE_END()