From de911c4e8a11eb23b3c344043e1e3170dd9021da Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Sat, 2 Mar 2024 15:01:13 -0500 Subject: [PATCH 01/60] qt: remove BIP70 Completely removes BIP70 support from Dogecoin Qt code. Also removes protobuf-compatibility tests as these rely on paymentserverplus code and any TLS related configuration. --- src/Makefile.qt.include | 25 +- src/Makefile.qttest.include | 19 +- src/qt/bitcoin.cpp | 18 +- src/qt/coincontroldialog.cpp | 1 + src/qt/guiutil.cpp | 1 + src/qt/optionsmodel.cpp | 19 - src/qt/paymentrequest.proto | 49 --- src/qt/paymentrequestplus.cpp | 216 ----------- src/qt/paymentrequestplus.h | 51 --- src/qt/paymentserver.cpp | 582 +---------------------------- src/qt/paymentserver.h | 45 --- src/qt/rpcconsole.cpp | 2 - src/qt/sendcoinsdialog.cpp | 30 +- src/qt/sendcoinsentry.cpp | 49 +-- src/qt/test/compattests.cpp | 23 -- src/qt/test/compattests.h | 19 - src/qt/test/paymentrequestdata.h | 460 ----------------------- src/qt/test/paymentservertests.cpp | 217 ----------- src/qt/test/paymentservertests.h | 35 -- src/qt/test/test_main.cpp | 17 - src/qt/transactiondesc.cpp | 15 - src/qt/utilitydialog.cpp | 7 +- src/qt/walletmodel.cpp | 100 ++--- src/qt/walletmodel.h | 20 +- src/qt/walletmodeltransaction.cpp | 24 +- src/qt/walletmodeltransaction.h | 2 + 26 files changed, 83 insertions(+), 1963 deletions(-) delete mode 100644 src/qt/paymentrequest.proto delete mode 100644 src/qt/paymentrequestplus.cpp delete mode 100644 src/qt/paymentrequestplus.h delete mode 100644 src/qt/test/compattests.cpp delete mode 100644 src/qt/test/compattests.h delete mode 100644 src/qt/test/paymentrequestdata.h delete mode 100644 src/qt/test/paymentservertests.cpp delete mode 100644 src/qt/test/paymentservertests.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 8b00c8e888e..71cd30cc324 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -162,10 +162,6 @@ QT_QRC = qt/bitcoin.qrc QT_QRC_LOCALE_CPP = qt/qrc_bitcoin_locale.cpp QT_QRC_LOCALE = qt/bitcoin_locale.qrc -PROTOBUF_CC = qt/paymentrequest.pb.cc -PROTOBUF_H = qt/paymentrequest.pb.h -PROTOBUF_PROTO = qt/paymentrequest.proto - BITCOIN_QT_H = \ qt/addressbookpage.h \ qt/addresstablemodel.h \ @@ -193,7 +189,6 @@ BITCOIN_QT_H = \ qt/optionsdialog.h \ qt/optionsmodel.h \ qt/overviewpage.h \ - qt/paymentrequestplus.h \ qt/paymentserver.h \ qt/peertablemodel.h \ qt/platformstyle.h \ @@ -321,7 +316,6 @@ BITCOIN_QT_WALLET_CPP = \ qt/importkeysdialog.cpp \ qt/openuridialog.cpp \ qt/overviewpage.cpp \ - qt/paymentrequestplus.cpp \ qt/paymentserver.cpp \ qt/receivecoinsdialog.cpp \ qt/receiverequestdialog.cpp \ @@ -371,15 +365,15 @@ BITCOIN_QT_INCLUDES = -I$(builddir)/qt -I$(srcdir)/qt -I$(srcdir)/qt/forms \ -I$(builddir)/qt/forms -DQT_NO_KEYWORDS qt_libdogecoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ - $(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS) + $(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(QR_CFLAGS) qt_libdogecoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) qt_libdogecoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS) qt_libdogecoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ - $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) $(RES_FONTS) + $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) $(RES_FONTS) -nodist_qt_libdogecoinqt_a_SOURCES = $(QT_MOC_CPP) $(QT_MOC) $(PROTOBUF_CC) \ - $(PROTOBUF_H) $(QT_QRC_CPP) $(QT_QRC_LOCALE_CPP) +nodist_qt_libdogecoinqt_a_SOURCES = $(QT_MOC_CPP) $(QT_MOC) \ + $(QT_QRC_CPP) $(QT_QRC_LOCALE_CPP) # forms/foo.h -> forms/ui_foo.h QT_FORMS_H=$(join $(dir $(QT_FORMS_UI)),$(addprefix ui_, $(notdir $(QT_FORMS_UI:.ui=.h)))) @@ -389,14 +383,9 @@ QT_FORMS_H=$(join $(dir $(QT_FORMS_UI)),$(addprefix ui_, $(notdir $(QT_FORMS_UI: $(QT_MOC): $(QT_FORMS_H) $(qt_libdogecoinqt_a_OBJECTS) $(qt_dogecoin_qt_OBJECTS) : | $(QT_MOC) -#Generating these with a half-written protobuf header leads to wacky results. -#This makes sure it's done. -$(QT_MOC): $(PROTOBUF_H) -$(QT_MOC_CPP): $(PROTOBUF_H) - # bitcoin-qt binary # qt_dogecoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ - $(QT_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS) + $(QT_INCLUDES) $(QR_CFLAGS) qt_dogecoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) qt_dogecoin_qt_SOURCES = qt/bitcoin.cpp @@ -414,7 +403,7 @@ if ENABLE_ZMQ qt_dogecoin_qt_LDADD += $(LIBDOGECOIN_ZMQ) $(ZMQ_LIBS) endif qt_dogecoin_qt_LDADD += $(LIBDOGECOIN_CLI) $(LIBDOGECOIN_COMMON) $(LIBDOGECOIN_UTIL) $(LIBDOGECOIN_CONSENSUS) $(LIBDOGECOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ + $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) qt_dogecoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_dogecoin_qt_LIBTOOLFLAGS = --tag CXX @@ -445,7 +434,7 @@ $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM) $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@ @rm $(@D)/temp_$( $@ diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 6094027dc3c..a6a3f3e1f96 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -6,34 +6,21 @@ bin_PROGRAMS += qt/test/test_dogecoin-qt TESTS += qt/test/test_dogecoin-qt TEST_QT_MOC_CPP = \ - qt/test/moc_compattests.cpp \ qt/test/moc_rpcnestedtests.cpp \ qt/test/moc_uritests.cpp -if ENABLE_WALLET -TEST_QT_MOC_CPP += qt/test/moc_paymentservertests.cpp -endif - TEST_QT_H = \ - qt/test/compattests.h \ qt/test/rpcnestedtests.h \ - qt/test/uritests.h \ - qt/test/paymentrequestdata.h \ - qt/test/paymentservertests.h + qt/test/uritests.h qt_test_test_dogecoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ - $(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS) + $(QT_INCLUDES) $(QT_TEST_INCLUDES) qt_test_test_dogecoin_qt_SOURCES = \ - qt/test/compattests.cpp \ qt/test/rpcnestedtests.cpp \ qt/test/test_main.cpp \ qt/test/uritests.cpp \ $(TEST_QT_H) -if ENABLE_WALLET -qt_test_test_dogecoin_qt_SOURCES += \ - qt/test/paymentservertests.cpp -endif nodist_qt_test_test_dogecoin_qt_SOURCES = $(TEST_QT_MOC_CPP) @@ -46,7 +33,7 @@ qt_test_test_dogecoin_qt_LDADD += $(LIBDOGECOIN_ZMQ) $(ZMQ_LIBS) endif qt_test_test_dogecoin_qt_LDADD += $(LIBDOGECOIN_CLI) $(LIBDOGECOIN_COMMON) $(LIBDOGECOIN_UTIL) $(LIBDOGECOIN_CONSENSUS) $(LIBDOGECOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ - $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ + $(QR_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) qt_test_test_dogecoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_test_test_dogecoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c44c157bb8e..0a93e7f6a0c 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -52,7 +52,6 @@ #include #include #include -#include #if defined(QT_STATICPLUGIN) #include @@ -348,7 +347,7 @@ BitcoinApplication::~BitcoinApplication() #ifdef ENABLE_WALLET void BitcoinApplication::createPaymentServer() { - paymentServer = new PaymentServer(this, true, GetBoolArg("-enable-bip70", false)); + paymentServer = new PaymentServer(this, true); } #endif @@ -474,14 +473,6 @@ void BitcoinApplication::initializeResult(int retval) #ifdef ENABLE_WALLET paymentServer->setOptionsModel(optionsModel); - if(GetBoolArg("-enable-bip70", false)) - { - PaymentServer::LoadRootCAs(); - if(pwalletMain) - connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), - paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); - } - // Now that initialization/startup is done, process any command-line // payment requests: connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), @@ -543,13 +534,6 @@ MAIN_FUNCTION #ifdef Q_OS_MAC QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); #endif -#if QT_VERSION >= 0x050500 - // Because of the POODLE attack it is recommended to disable SSLv3 (https://disablessl3.com/), - // so set SSL protocols to TLS1.0+. - QSslConfiguration sslconf = QSslConfiguration::defaultConfiguration(); - sslconf.setProtocol(QSsl::TlsV1_0OrLater); - QSslConfiguration::setDefaultConfiguration(sslconf); -#endif BitcoinApplication app; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index d6c3dc6c01f..c264ae5587c 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -14,6 +14,7 @@ #include "txmempool.h" #include "walletmodel.h" +#include "base58.h" #include "wallet/coincontrol.h" #include "init.h" #include "policy/policy.h" diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index a6e3bdc7e16..c69166a2c52 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -11,6 +11,7 @@ #include "qvalidatedlineedit.h" #include "walletmodel.h" +#include "base58.h" #include "primitives/transaction.h" #include "init.h" #include "policy/policy.h" diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 2622939e9be..b32b5fa4360 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -26,7 +26,6 @@ #include "wallet/walletdb.h" #endif -#include #include #include @@ -445,24 +444,6 @@ void OptionsModel::setDisplayUnit(const QVariant &value) } } -bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const -{ - // Directly query current base proxy, because - // GUI settings can be overridden with -proxy. - proxyType curProxy; - if (GetProxy(NET_IPV4, curProxy)) { - proxy.setType(QNetworkProxy::Socks5Proxy); - proxy.setHostName(QString::fromStdString(curProxy.proxy.ToStringIP())); - proxy.setPort(curProxy.proxy.GetPort()); - - return true; - } - else - proxy.setType(QNetworkProxy::NoProxy); - - return false; -} - void OptionsModel::setRestartRequired(bool fRequired) { QSettings settings; diff --git a/src/qt/paymentrequest.proto b/src/qt/paymentrequest.proto deleted file mode 100644 index 1ff0209ccd5..00000000000 --- a/src/qt/paymentrequest.proto +++ /dev/null @@ -1,49 +0,0 @@ -// -// Simple Dogecoin Payment Protocol messages -// Derived from the Bitcoin Payment Protocol -// -// Use fields 100+ for extensions; -// to avoid conflicts, register extensions via pull-req at: -// https://github.com/dogecoin/dips -// - -syntax = "proto2"; - -package payments; -option java_package = "com.dogecoin.protocols.payments"; -option java_outer_classname = "Protos"; - -// Generalized form of "send payment to this/these dogecoin addresses" -message Output { - optional uint64 amount = 1 [default = 0]; // amount is integer-number-of-satoshis - required bytes script = 2; // usually one of the standard Script forms -} -message PaymentDetails { - optional string genesis = 1 [default = "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691"]; // Hash of the network genesis block - repeated Output outputs = 2; // Where payment should be sent - required uint64 time = 3; // Timestamp; when payment request created - optional uint64 expires = 4; // Timestamp; when this request should be considered invalid - optional string memo = 5; // Human-readable description of request for the customer - optional string payment_url = 6; // URL to send Payment and get PaymentACK - optional bytes merchant_data = 7; // Arbitrary data to include in the Payment message -} -message PaymentRequest { - optional uint32 payment_details_version = 1 [default = 1]; - optional string pki_type = 2 [default = "none"]; // none / x509+sha256 / x509+sha1 - optional bytes pki_data = 3; // depends on pki_type - required bytes serialized_payment_details = 4; // PaymentDetails - optional bytes signature = 5; // pki-dependent signature -} -message X509Certificates { - repeated bytes certificate = 1; // DER-encoded X.509 certificate chain -} -message Payment { - optional bytes merchant_data = 1; // From PaymentDetails.merchant_data - repeated bytes transactions = 2; // Signed transactions that satisfy PaymentDetails.outputs - repeated Output refund_to = 3; // Where to send refunds, if a refund is necessary - optional string memo = 4; // Human-readable message for the merchant -} -message PaymentACK { - required Payment payment = 1; // Payment message that triggered this ACK - optional string memo = 2; // human-readable message for customer -} diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp deleted file mode 100644 index 4b00f724d6e..00000000000 --- a/src/qt/paymentrequestplus.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) 2011-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. - -// -// Wraps dumb protocol buffer paymentRequest -// with some extra methods -// - -#include "paymentrequestplus.h" - -#include "util.h" - -#include - -#include - -#include -#include -#include - -class SSLVerifyError : public std::runtime_error -{ -public: - SSLVerifyError(std::string err) : std::runtime_error(err) { } -}; - -bool PaymentRequestPlus::parse(const QByteArray& data) -{ - bool parseOK = paymentRequest.ParseFromArray(data.data(), data.size()); - if (!parseOK) { - qWarning() << "PaymentRequestPlus::parse: Error parsing payment request"; - return false; - } - if (paymentRequest.payment_details_version() > 1) { - qWarning() << "PaymentRequestPlus::parse: Received up-version payment details, version=" << paymentRequest.payment_details_version(); - return false; - } - - parseOK = details.ParseFromString(paymentRequest.serialized_payment_details()); - if (!parseOK) - { - qWarning() << "PaymentRequestPlus::parse: Error parsing payment details"; - paymentRequest.Clear(); - return false; - } - return true; -} - -bool PaymentRequestPlus::SerializeToString(std::string* output) const -{ - return paymentRequest.SerializeToString(output); -} - -bool PaymentRequestPlus::IsInitialized() const -{ - return paymentRequest.IsInitialized(); -} - -bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) const -{ - merchant.clear(); - - if (!IsInitialized()) - return false; - - // One day we'll support more PKI types, but just - // x509 for now: - const EVP_MD* digestAlgorithm = NULL; - if (paymentRequest.pki_type() == "x509+sha256") { - digestAlgorithm = EVP_sha256(); - } - else if (paymentRequest.pki_type() == "x509+sha1") { - digestAlgorithm = EVP_sha1(); - } - else if (paymentRequest.pki_type() == "none") { - qWarning() << "PaymentRequestPlus::getMerchant: Payment request: pki_type == none"; - return false; - } - else { - qWarning() << "PaymentRequestPlus::getMerchant: Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type()); - return false; - } - - payments::X509Certificates certChain; - if (!certChain.ParseFromString(paymentRequest.pki_data())) { - qWarning() << "PaymentRequestPlus::getMerchant: Payment request: error parsing pki_data"; - return false; - } - - std::vector certs; - const QDateTime currentTime = QDateTime::currentDateTime(); - for (int i = 0; i < certChain.certificate_size(); i++) { - QByteArray certData(certChain.certificate(i).data(), certChain.certificate(i).size()); - QSslCertificate qCert(certData, QSsl::Der); - if (currentTime < qCert.effectiveDate() || currentTime > qCert.expiryDate()) { - qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate expired or not yet active: " << qCert; - return false; - } - if (qCert.isBlacklisted()) { - qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate blacklisted: " << qCert; - return false; - } - - const unsigned char *data = (const unsigned char *)certChain.certificate(i).data(); - X509 *cert = d2i_X509(NULL, &data, certChain.certificate(i).size()); - if (cert) - certs.push_back(cert); - } - if (certs.empty()) { - qWarning() << "PaymentRequestPlus::getMerchant: Payment request: empty certificate chain"; - return false; - } - - // The first cert is the signing cert, the rest are untrusted certs that chain - // to a valid root authority. OpenSSL needs them separately. - STACK_OF(X509) *chain = sk_X509_new_null(); - for (int i = certs.size() - 1; i > 0; i--) { - sk_X509_push(chain, certs[i]); - } - X509 *signing_cert = certs[0]; - - // Now create a "store context", which is a single use object for checking, - // load the signing cert into it and verify. - X509_STORE_CTX *store_ctx = X509_STORE_CTX_new(); - if (!store_ctx) { - qWarning() << "PaymentRequestPlus::getMerchant: Payment request: error creating X509_STORE_CTX"; - return false; - } - - char *website = NULL; - bool fResult = true; - try - { - if (!X509_STORE_CTX_init(store_ctx, certStore, signing_cert, chain)) - { - int error = X509_STORE_CTX_get_error(store_ctx); - throw SSLVerifyError(X509_verify_cert_error_string(error)); - } - - // Now do the verification! - int result = X509_verify_cert(store_ctx); - if (result != 1) { - int error = X509_STORE_CTX_get_error(store_ctx); - // For testing payment requests, we allow self signed root certs! - // This option is just shown in the UI options, if -help-debug is enabled. - if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GetBoolArg("-allowselfsignedrootcertificates", DEFAULT_SELFSIGNED_ROOTCERTS))) { - throw SSLVerifyError(X509_verify_cert_error_string(error)); - } else { - qDebug() << "PaymentRequestPlus::getMerchant: Allowing self signed root certificate, because -allowselfsignedrootcertificates is true."; - } - } - X509_NAME *certname = X509_get_subject_name(signing_cert); - - // Valid cert; check signature: - payments::PaymentRequest rcopy(paymentRequest); // Copy - rcopy.set_signature(std::string("")); - std::string data_to_verify; // Everything but the signature - rcopy.SerializeToString(&data_to_verify); - -#if HAVE_DECL_EVP_MD_CTX_NEW - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - if (!ctx) throw SSLVerifyError("Error allocating OpenSSL context."); -#else - EVP_MD_CTX _ctx; - EVP_MD_CTX *ctx; - ctx = &_ctx; -#endif - EVP_PKEY *pubkey = X509_get_pubkey(signing_cert); - EVP_MD_CTX_init(ctx); - if (!EVP_VerifyInit_ex(ctx, digestAlgorithm, NULL) || - !EVP_VerifyUpdate(ctx, data_to_verify.data(), data_to_verify.size()) || - !EVP_VerifyFinal(ctx, (const unsigned char*)paymentRequest.signature().data(), (unsigned int)paymentRequest.signature().size(), pubkey)) { - throw SSLVerifyError("Bad signature, invalid payment request."); - } -#if HAVE_DECL_EVP_MD_CTX_NEW - EVP_MD_CTX_free(ctx); -#endif - - // OpenSSL API for getting human printable strings from certs is baroque. - int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0); - website = new char[textlen + 1]; - if (X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen && textlen > 0) { - merchant = website; - } - else { - throw SSLVerifyError("Bad certificate, missing common name."); - } - // TODO: detect EV certificates and set merchant = business name instead of unfriendly NID_commonName ? - } - catch (const SSLVerifyError& err) { - fResult = false; - qWarning() << "PaymentRequestPlus::getMerchant: SSL error: " << err.what(); - } - - if (website) - delete[] website; - X509_STORE_CTX_free(store_ctx); - for (unsigned int i = 0; i < certs.size(); i++) - X509_free(certs[i]); - - return fResult; -} - -QList > PaymentRequestPlus::getPayTo() const -{ - QList > result; - for (int i = 0; i < details.outputs_size(); i++) - { - const unsigned char* scriptStr = (const unsigned char*)details.outputs(i).script().data(); - CScript s(scriptStr, scriptStr+details.outputs(i).script().size()); - - result.append(std::make_pair(s, details.outputs(i).amount())); - } - return result; -} diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h deleted file mode 100644 index a2fea3fdc64..00000000000 --- a/src/qt/paymentrequestplus.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2011-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_QT_PAYMENTREQUESTPLUS_H -#define BITCOIN_QT_PAYMENTREQUESTPLUS_H - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#include "paymentrequest.pb.h" -#pragma GCC diagnostic pop - -#include "base58.h" - -#include - -#include -#include -#include - -static const bool DEFAULT_SELFSIGNED_ROOTCERTS = false; - -// -// Wraps dumb protocol buffer paymentRequest -// with extra methods -// - -class PaymentRequestPlus -{ -public: - PaymentRequestPlus() { } - - bool parse(const QByteArray& data); - bool SerializeToString(std::string* output) const; - - bool IsInitialized() const; - // Returns true if merchant's identity is authenticated, and - // returns human-readable merchant identity in merchant - bool getMerchant(X509_STORE* certStore, QString& merchant) const; - - // Returns list of outputs, amount - QList > getPayTo() const; - - const payments::PaymentDetails& getDetails() const { return details; } - -private: - payments::PaymentRequest paymentRequest; - payments::PaymentDetails details; -}; - -#endif // BITCOIN_QT_PAYMENTREQUESTPLUS_H diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 2d870a451ec..669f64b1596 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -19,8 +19,6 @@ #include -#include - #include #include #include @@ -33,12 +31,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include #include @@ -46,30 +38,8 @@ const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds const QString BITCOIN_IPC_PREFIX("dogecoin:"); -// BIP70 payment protocol messages -const char* BIP70_MESSAGE_PAYMENTACK = "PaymentACK"; -const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest"; -// BIP71 payment protocol media types -const char* BIP71_MIMETYPE_PAYMENT = "application/bitcoin-payment"; -const char* BIP71_MIMETYPE_PAYMENTACK = "application/bitcoin-paymentack"; -const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest"; const int IPC_SOCKET_HASH = GetRandInt(INT_MAX); -struct X509StoreDeleter { - void operator()(X509_STORE* b) { - X509_STORE_free(b); - } -}; - -struct X509Deleter { - void operator()(X509* b) { X509_free(b); } -}; - -namespace // Anon namespace -{ - std::unique_ptr certStore; -} - // // Create a name that is unique for: // testnet / non-testnet @@ -95,95 +65,6 @@ static QString ipcServerName() static QList savedPaymentRequests; -static void ReportInvalidCertificate(const QSslCertificate& cert) -{ - qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName); -} - -// -// Load OpenSSL's list of root certificate authorities -// -void PaymentServer::LoadRootCAs(X509_STORE* _store) -{ - // Unit tests mostly use this, to pass in fake root CAs: - if (_store) - { - certStore.reset(_store); - return; - } - - // Normal execution, use either -rootcertificates or system certs: - certStore.reset(X509_STORE_new()); - - // Note: use "-system-" default here so that users can pass -rootcertificates="" - // and get 'I don't like X.509 certificates, don't trust anybody' behavior: - QString certFile = QString::fromStdString(GetArg("-rootcertificates", "-system-")); - - // Empty store - if (certFile.isEmpty()) { - qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__); - return; - } - - QList certList; - - if (certFile != "-system-") { - qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile); - - certList = QSslCertificate::fromPath(certFile); - // Use those certificates when fetching payment requests, too: - QSslSocket::setDefaultCaCertificates(certList); - } else - certList = QSslSocket::systemCaCertificates(); - - int nRootCerts = 0; - const QDateTime currentTime = QDateTime::currentDateTime(); - - Q_FOREACH (const QSslCertificate& cert, certList) { - // Don't log NULL certificates - if (cert.isNull()) - continue; - - // Not yet active/valid, or expired certificate - if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) { - ReportInvalidCertificate(cert); - continue; - } - - // Blacklisted certificate - if (cert.isBlacklisted()) { - ReportInvalidCertificate(cert); - continue; - } - - QByteArray certData = cert.toDer(); - const unsigned char *data = (const unsigned char *)certData.data(); - - std::unique_ptr x509(d2i_X509(0, &data, certData.size())); - if (x509 && X509_STORE_add_cert(certStore.get(), x509.get())) - { - // Note: X509_STORE increases the reference count to the X509 object, - // we still have to release our reference to it. - ++nRootCerts; - } - else - { - ReportInvalidCertificate(cert); - continue; - } - } - qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates"; - - // Project for another day: - // Fetch certificate revocation lists, and add them to certStore. - // Issues to consider: - // performance (start a thread to fetch in background?) - // privacy (fetch through tor/proxy so IP address isn't revealed) - // would it be easier to just use a compiled-in blacklist? - // or use Qt's blacklist? - // "certificate stapling" with server-side caching is more efficient -} - // // Sending to the server is done synchronously, at startup. // If the server isn't already running, startup continues, @@ -224,23 +105,6 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[]) } } } - else if (QFile::exists(arg)) // Filename - { - savedPaymentRequests.append(arg); - - PaymentRequestPlus request; - if (readPaymentRequestFromFile(arg, request)) - { - if (request.getDetails().genesis() == "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691") - { - SelectParams(CBaseChainParams::MAIN); - } - else if (request.getDetails().genesis() == "bb0a78264637406b6360aad926284d544d7049f45189db5664f3c4d07350559e") - { - SelectParams(CBaseChainParams::TESTNET); - } - } - } else { // Printing to debug.log is about the best we can do here, the @@ -290,11 +154,6 @@ bool PaymentServer::ipcSendCommandLine() } void PaymentServer::initializeServer(QObject* parent, QString ipcServerName, bool startLocalServer, bool enableBip70) { - // Verify that the version of the library that we linked against is - // compatible with the version of the headers we compiled against. - GOOGLE_PROTOBUF_VERIFY_VERSION; - this->enableBip70 = enableBip70; - // Install global event filter to catch QFileOpenEvents // on Mac: sent when you click bitcoin: links // other OSes: helpful when dealing with payment request files @@ -312,7 +171,6 @@ void PaymentServer::initializeServer(QObject* parent, QString ipcServerName, boo } else { connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); - connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString))); } } } @@ -321,50 +179,20 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : QObject(parent), saveURIs(true), uriServer(0), - netManager(0), optionsModel(0) { this->initializeServer(parent, ipcServerName(), startLocalServer, false); } - -PaymentServer::PaymentServer(QObject* parent, bool startLocalServer, bool enableBip70) : - QObject(parent), - saveURIs(true), - uriServer(0), - netManager(0), - optionsModel(0) -{ - this->initializeServer(parent, ipcServerName(), startLocalServer, enableBip70); -} - - PaymentServer::PaymentServer(QObject* parent, QString ipcServerName, bool startLocalServer) : QObject(parent), saveURIs(true), uriServer(0), - netManager(0), optionsModel(0) { this->initializeServer(parent, ipcServerName, startLocalServer, false); } - -PaymentServer::PaymentServer(QObject* parent, QString ipcServerName, bool startLocalServer, bool enableBip70Flag) : - QObject(parent), - saveURIs(true), - uriServer(0), - netManager(0), - optionsModel(0) -{ - this->initializeServer(parent, ipcServerName, startLocalServer, enableBip70Flag); -} - -PaymentServer::~PaymentServer() -{ - google::protobuf::ShutdownProtobufLibrary(); -} - // // OSX-specific way of handling bitcoin: URIs and PaymentRequest mime types. // Also used by paymentservertests.cpp and when opening a payment request file @@ -385,37 +213,8 @@ bool PaymentServer::eventFilter(QObject *object, QEvent *event) return QObject::eventFilter(object, event); } -void PaymentServer::initNetManager() -{ - if (!optionsModel) - return; - if (netManager != NULL) - delete netManager; - - // netManager is used to fetch paymentrequests given in bitcoin: URIs - netManager = new QNetworkAccessManager(this); - - QNetworkProxy proxy; - - // Query active SOCKS5 proxy - if (optionsModel->getProxySettings(proxy)) { - netManager->setProxy(proxy); - - qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port(); - } - else - qDebug() << "PaymentServer::initNetManager: No active proxy server found."; - - connect(netManager, SIGNAL(finished(QNetworkReply*)), - this, SLOT(netRequestFinished(QNetworkReply*))); - connect(netManager, SIGNAL(sslErrors(QNetworkReply*, const QList &)), - this, SLOT(reportSslErrors(QNetworkReply*, const QList &))); -} - void PaymentServer::uiReady() { - initNetManager(); - saveURIs = false; Q_FOREACH (const QString& s, savedPaymentRequests) { @@ -436,81 +235,21 @@ void PaymentServer::handleURIOrFile(const QString& s) { QUrlQuery uri((QUrl(s))); - if (uri.hasQueryItem("r")) // payment request URI - { - if (!enableBip70) { - qDebug() << "PaymentServer::handleURIOrFile: 'r' item supplied but BIP70 disabled."; - Q_EMIT message(tr("URI handling"), tr( - "BIP70 payment requests are deprecated and disabled by default. " - "Restart with -enable-bip70 if you absolutely have to use this functionality.\n\n" - "Use this functionality with extreme caution."), - CClientUIInterface::MSG_ERROR); - return; - } - QByteArray temp; - temp.append(uri.queryItemValue("r").toUtf8()); - QString decoded = QUrl::fromPercentEncoding(temp); - QUrl fetchUrl(decoded, QUrl::StrictMode); - - if (fetchUrl.isValid()) - { - qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl << ")"; - fetchRequest(fetchUrl); - } - else - { - qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl; - Q_EMIT message(tr("URI handling"), - tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()), - CClientUIInterface::ICON_WARNING); - } - - return; - } - else // normal URI + SendCoinsRecipient recipient; + if (GUIUtil::parseBitcoinURI(s, &recipient)) { - SendCoinsRecipient recipient; - if (GUIUtil::parseBitcoinURI(s, &recipient)) - { - CBitcoinAddress address(recipient.address.toStdString()); - if (!address.IsValid()) { - Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), - CClientUIInterface::MSG_ERROR); - } - else - Q_EMIT receivedPaymentRequest(recipient); + CBitcoinAddress address(recipient.address.toStdString()); + if (!address.IsValid()) { + Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), + CClientUIInterface::MSG_ERROR); } else - Q_EMIT message(tr("URI handling"), - tr("URI cannot be parsed! This can be caused by an invalid Dogecoin address or malformed URI parameters."), - CClientUIInterface::ICON_WARNING); - - return; + Q_EMIT receivedPaymentRequest(recipient); } - } - - // payment request file - if (!enableBip70) { - Q_EMIT message(tr("Payment request file handling"), tr( - "Payment request file handling is disabled by default. " - "Restart with -enable-bip70 if you absolutely have to use this functionality.\n\n" - "Use this functionality with extreme caution."), - CClientUIInterface::MSG_ERROR); - return; - } - - if (QFile::exists(s)) - { - PaymentRequestPlus request; - SendCoinsRecipient recipient; - if (!readPaymentRequestFromFile(s, request)) - { - Q_EMIT message(tr("Payment request file handling"), - tr("Payment request file cannot be read! This can be caused by an invalid payment request file."), + else + Q_EMIT message(tr("URI handling"), + tr("URI cannot be parsed! This can be caused by an invalid Dogecoin address or malformed URI parameters."), CClientUIInterface::ICON_WARNING); - } - else if (processPaymentRequest(request, recipient)) - Q_EMIT receivedPaymentRequest(recipient); return; } @@ -537,307 +276,11 @@ void PaymentServer::handleURIConnection() handleURIOrFile(msg); } -// -// Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine() -// so don't use "Q_EMIT message()", but "QMessageBox::"! -// -bool PaymentServer::readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request) -{ - QFile f(filename); - if (!f.open(QIODevice::ReadOnly)) { - qWarning() << QString("PaymentServer::%1: Failed to open %2").arg(__func__).arg(filename); - return false; - } - - // BIP70 DoS protection - if (!verifySize(f.size())) { - return false; - } - - QByteArray data = f.readAll(); - - return request.parse(data); -} - -bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, SendCoinsRecipient& recipient) -{ - if (!optionsModel) - return false; - - if (request.IsInitialized()) { - // Payment request network matches client network? - if (!verifyNetwork(request.getDetails())) { - Q_EMIT message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."), - CClientUIInterface::MSG_ERROR); - - return false; - } - - // Make sure any payment requests involved are still valid. - // This is re-checked just before sending coins in WalletModel::sendCoins(). - if (verifyExpired(request.getDetails())) { - Q_EMIT message(tr("Payment request rejected"), tr("Payment request expired."), - CClientUIInterface::MSG_ERROR); - - return false; - } - } else { - Q_EMIT message(tr("Payment request error"), tr("Payment request is not initialized."), - CClientUIInterface::MSG_ERROR); - - return false; - } - - recipient.paymentRequest = request; - recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo()); - - request.getMerchant(certStore.get(), recipient.authenticatedMerchant); - - QList > sendingTos = request.getPayTo(); - QStringList addresses; - - for (const PAIRTYPE(CScript, CAmount)& sendingTo : sendingTos) { - // Extract and check destination addresses - CTxDestination dest; - if (ExtractDestination(sendingTo.first, dest)) { - // Append destination address - addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString())); - } - else if (!recipient.authenticatedMerchant.isEmpty()) { - // Unauthenticated payment requests to custom bitcoin addresses are not supported - // (there is no good way to tell the user where they are paying in a way they'd - // have a chance of understanding). - Q_EMIT message(tr("Payment request rejected"), - tr("Unverified payment requests to custom payment scripts are unsupported."), - CClientUIInterface::MSG_ERROR); - return false; - } - - // Bitcoin amounts are stored as (optional) uint64 in the protobuf messages (see paymentrequest.proto), - // but CAmount is defined as int64_t. Because of that we need to verify that amounts are in a valid range - // and no overflow has happened. - if (!verifyAmount(sendingTo.second)) { - Q_EMIT message(tr("Payment request rejected"), tr("Invalid payment request."), CClientUIInterface::MSG_ERROR); - return false; - } - - // Extract and check amounts - CTxOut txOut(sendingTo.second, sendingTo.first); - if (txOut.IsDust(CWallet::discardThreshold)) { - Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (below discard threshold).") - .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)), - CClientUIInterface::MSG_ERROR); - - return false; - } - - recipient.amount += sendingTo.second; - // Also verify that the final amount is still in a valid range after adding additional amounts. - if (!verifyAmount(recipient.amount)) { - Q_EMIT message(tr("Payment request rejected"), tr("Invalid payment request."), CClientUIInterface::MSG_ERROR); - return false; - } - } - // Store addresses and format them to fit nicely into the GUI - recipient.address = addresses.join("
"); - - if (!recipient.authenticatedMerchant.isEmpty()) { - qDebug() << "PaymentServer::processPaymentRequest: Secure payment request from " << recipient.authenticatedMerchant; - } - else { - qDebug() << "PaymentServer::processPaymentRequest: Insecure payment request to " << addresses.join(", "); - } - - return true; -} - -void PaymentServer::fetchRequest(const QUrl& url) -{ - QNetworkRequest netRequest; - netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTREQUEST); - netRequest.setUrl(url); - netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); - netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTREQUEST); - netManager->get(netRequest); -} - -void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction) -{ - const payments::PaymentDetails& details = recipient.paymentRequest.getDetails(); - if (!details.has_payment_url()) - return; - - QNetworkRequest netRequest; - netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTACK); - netRequest.setUrl(QString::fromStdString(details.payment_url())); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, BIP71_MIMETYPE_PAYMENT); - netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); - netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTACK); - - payments::Payment payment; - payment.set_merchant_data(details.merchant_data()); - payment.add_transactions(transaction.data(), transaction.size()); - - // Create a new refund address, or re-use: - QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant); - std::string strAccount = account.toStdString(); - std::set refundAddresses = wallet->GetAccountAddresses(strAccount); - if (!refundAddresses.empty()) { - CScript s = GetScriptForDestination(*refundAddresses.begin()); - payments::Output* refund_to = payment.add_refund_to(); - refund_to->set_script(&s[0], s.size()); - } - else { - CPubKey newKey; - if (wallet->GetKeyFromPool(newKey)) { - CKeyID keyID = newKey.GetID(); - wallet->SetAddressBook(keyID, strAccount, "refund"); - - CScript s = GetScriptForDestination(keyID); - payments::Output* refund_to = payment.add_refund_to(); - refund_to->set_script(&s[0], s.size()); - } - else { - // This should never happen, because sending coins should have - // just unlocked the wallet and refilled the keypool. - qWarning() << "PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set"; - } - } - - quint64 length = payment.ByteSizeLong(); - netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length); - QByteArray serData(length, '\0'); - if (payment.SerializeToArray(serData.data(), length)) { - netManager->post(netRequest, serData); - } - else { - // This should never happen, either. - qWarning() << "PaymentServer::fetchPaymentACK: Error serializing payment message"; - } -} - -void PaymentServer::netRequestFinished(QNetworkReply* reply) -{ - reply->deleteLater(); - - // BIP70 DoS protection - if (!verifySize(reply->size())) { - Q_EMIT message(tr("Payment request rejected"), - tr("Payment request %1 is too large (%2 bytes, allowed %3 bytes).") - .arg(reply->request().url().toString()) - .arg(reply->size()) - .arg(BIP70_MAX_PAYMENTREQUEST_SIZE), - CClientUIInterface::MSG_ERROR); - return; - } - - if (reply->error() != QNetworkReply::NoError) { - QString msg = tr("Error communicating with %1: %2") - .arg(reply->request().url().toString()) - .arg(reply->errorString()); - - qWarning() << "PaymentServer::netRequestFinished: " << msg; - Q_EMIT message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR); - return; - } - - QByteArray data = reply->readAll(); - - QString requestType = reply->request().attribute(QNetworkRequest::User).toString(); - if (requestType == BIP70_MESSAGE_PAYMENTREQUEST) - { - PaymentRequestPlus request; - SendCoinsRecipient recipient; - if (!request.parse(data)) - { - qWarning() << "PaymentServer::netRequestFinished: Error parsing payment request"; - Q_EMIT message(tr("Payment request error"), - tr("Payment request cannot be parsed!"), - CClientUIInterface::MSG_ERROR); - } - else if (processPaymentRequest(request, recipient)) - Q_EMIT receivedPaymentRequest(recipient); - - return; - } - else if (requestType == BIP70_MESSAGE_PAYMENTACK) - { - payments::PaymentACK paymentACK; - if (!paymentACK.ParseFromArray(data.data(), data.size())) - { - QString msg = tr("Bad response from server %1") - .arg(reply->request().url().toString()); - - qWarning() << "PaymentServer::netRequestFinished: " << msg; - Q_EMIT message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR); - } - else - { - Q_EMIT receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK.memo())); - } - } -} - -void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList &errs) -{ - Q_UNUSED(reply); - - QString errString; - Q_FOREACH (const QSslError& err, errs) { - qWarning() << "PaymentServer::reportSslErrors: " << err; - errString += err.errorString() + "\n"; - } - Q_EMIT message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR); -} - void PaymentServer::setOptionsModel(OptionsModel *_optionsModel) { this->optionsModel = _optionsModel; } -void PaymentServer::handlePaymentACK(const QString& paymentACKMsg) -{ - // currently we don't further process or store the paymentACK message - Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL); -} - -bool PaymentServer::verifyNetwork(const payments::PaymentDetails& requestDetails) -{ - Consensus::Params consensus = Params().GetConsensus(0); - bool fVerified = requestDetails.genesis() == consensus.hashGenesisBlock.GetHex(); - if (!fVerified) { - qWarning() << QString("PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".") - .arg(__func__) - .arg(QString::fromStdString(requestDetails.genesis())) - .arg(QString::fromStdString(consensus.hashGenesisBlock.GetHex())); - } - return fVerified; -} - -bool PaymentServer::verifyExpired(const payments::PaymentDetails& requestDetails) -{ - bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() < GetTime()); - if (fVerified) { - const QString requestExpires = QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.expires())); - qWarning() << QString("PaymentServer::%1: Payment request expired \"%2\".") - .arg(__func__) - .arg(requestExpires); - } - return fVerified; -} - -bool PaymentServer::verifySize(qint64 requestSize) -{ - bool fVerified = (requestSize <= BIP70_MAX_PAYMENTREQUEST_SIZE); - if (!fVerified) { - qWarning() << QString("PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes).") - .arg(__func__) - .arg(requestSize) - .arg(BIP70_MAX_PAYMENTREQUEST_SIZE); - } - return fVerified; -} - bool PaymentServer::verifyAmount(const CAmount& requestAmount) { bool fVerified = MoneyRange(requestAmount); @@ -849,8 +292,3 @@ bool PaymentServer::verifyAmount(const CAmount& requestAmount) } return fVerified; } - -X509_STORE* PaymentServer::getCertStore() -{ - return certStore.get(); -} diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 75c77eea1b3..d6450fcc01a 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -33,7 +33,6 @@ // sends them to the server. // -#include "paymentrequestplus.h" #include "walletmodel.h" #include @@ -47,15 +46,9 @@ QT_BEGIN_NAMESPACE class QApplication; class QByteArray; class QLocalServer; -class QNetworkAccessManager; -class QNetworkReply; -class QSslError; class QUrl; QT_END_NAMESPACE -// BIP70 max payment request size in bytes (DoS protection) -static const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE = 50000; - class PaymentServer : public QObject { Q_OBJECT @@ -74,30 +67,11 @@ class PaymentServer : public QObject // parent should be QApplication object PaymentServer(QObject* parent, bool startLocalServer = true); - PaymentServer(QObject* parent, bool startLocalServer = true, bool enableBip70Flag = false); PaymentServer(QObject* parent, QString ipcServerName, bool startLocalServer = true); - PaymentServer(QObject* parent, QString ipcServerName, bool startLocalServer = true, bool enableBip70Flag = false); - ~PaymentServer(); - - // Load root certificate authorities. Pass NULL (default) - // to read from the file specified in the -rootcertificates setting, - // or, if that's not set, to use the system default root certificates. - // If you pass in a store, you should not X509_STORE_free it: it will be - // freed either at exit or when another set of CAs are loaded. - static void LoadRootCAs(X509_STORE* store = NULL); - - // Return certificate store - static X509_STORE* getCertStore(); // OptionsModel is used for getting proxy settings and display unit void setOptionsModel(OptionsModel *optionsModel); - // Verify that the payment request network matches the client network - static bool verifyNetwork(const payments::PaymentDetails& requestDetails); - // Verify if the payment request is expired - static bool verifyExpired(const payments::PaymentDetails& requestDetails); - // Verify the payment request size is valid as per BIP70 - static bool verifySize(qint64 requestSize); // Verify the payment request amount is valid static bool verifyAmount(const CAmount& requestAmount); @@ -105,9 +79,6 @@ class PaymentServer : public QObject // Fired when a valid payment request is received void receivedPaymentRequest(SendCoinsRecipient); - // Fired when a valid PaymentACK is received - void receivedPaymentACK(const QString &paymentACKMsg); - // Fired when a message should be reported to the user void message(const QString &title, const QString &message, unsigned int style); @@ -116,17 +87,11 @@ public Q_SLOTS: // to display payment requests to the user void uiReady(); - // Submit Payment message to a merchant, get back PaymentACK: - void fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction); - // Handle an incoming URI, URI with local file scheme or file void handleURIOrFile(const QString& s); private Q_SLOTS: void handleURIConnection(); - void netRequestFinished(QNetworkReply*); - void reportSslErrors(QNetworkReply*, const QList &); - void handlePaymentACK(const QString& paymentACKMsg); protected: // Constructor registers this on the parent QApplication to @@ -134,21 +99,11 @@ private Q_SLOTS: bool eventFilter(QObject *object, QEvent *event); private: - static bool readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request); - bool processPaymentRequest(const PaymentRequestPlus& request, SendCoinsRecipient& recipient); - void fetchRequest(const QUrl& url); - - // Setup networking - void initNetManager(); - bool saveURIs; // true during startup void initializeServer(QObject* parent, QString ipcServerName, bool startLocalServer, bool enableBip70Flag); - bool enableBip70; // false by default QLocalServer* uriServer; - QNetworkAccessManager* netManager; // Used to fetch payment requests - OptionsModel *optionsModel; }; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 84554fdecbe..1997a9aaa18 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -25,8 +25,6 @@ #include "rpc/client.h" #include "util.h" -#include - #include #ifdef ENABLE_WALLET diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index c3e90c85375..a9b5098fa59 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -273,28 +273,18 @@ void SendCoinsDialog::on_sendButton_clicked() QString recipientElement; - if (!rcp.paymentRequest.IsInitialized()) // normal payment + + if(rcp.label.length() > 0) // label with address { - if(rcp.label.length() > 0) // label with address - { - QString displayedLabel = rcp.label; - if (rcp.label.length() > CHARACTERS_DISPLAY_LIMIT_IN_LABEL) - { - displayedLabel = displayedLabel.left(CHARACTERS_DISPLAY_LIMIT_IN_LABEL).append("..."); // limit the amount of characters displayed in label - } - recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(displayedLabel)); - recipientElement.append(QString(" (%1)").arg(address)); - } - else // just address + QString displayedLabel = rcp.label; + if (rcp.label.length() > CHARACTERS_DISPLAY_LIMIT_IN_LABEL) { - recipientElement = tr("%1 to %2").arg(amount, address); + displayedLabel = displayedLabel.left(CHARACTERS_DISPLAY_LIMIT_IN_LABEL).append("..."); // limit the amount of characters displayed in label } + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(displayedLabel)); + recipientElement.append(QString(" (%1)").arg(address)); } - else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request - { - recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); - } - else // unauthenticated payment request + else // just address { recipientElement = tr("%1 to %2").arg(amount, address); } @@ -547,10 +537,6 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn case WalletModel::AbsurdFee: msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), maxTxFee)); break; - case WalletModel::PaymentRequestExpired: - msgParams.first = tr("Payment request expired."); - msgParams.second = CClientUIInterface::MSG_ERROR; - break; // included to prevent a compiler warning. case WalletModel::OK: default: diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 1944e48060b..4a595a972dc 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -123,10 +123,6 @@ bool SendCoinsEntry::validate() // Check input validity bool retval = true; - // Skip checks for payment request - if (recipient.paymentRequest.IsInitialized()) - return retval; - if (!model->validateAddress(ui->payTo->text())) { ui->payTo->setValid(false); @@ -156,9 +152,6 @@ bool SendCoinsEntry::validate() SendCoinsRecipient SendCoinsEntry::getValue() { - // Payment request - if (recipient.paymentRequest.IsInitialized()) - return recipient; // Normal payment recipient.address = ui->payTo->text(); @@ -186,38 +179,16 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value) { recipient = value; - if (recipient.paymentRequest.IsInitialized()) // payment request - { - if (recipient.authenticatedMerchant.isEmpty()) // unauthenticated - { - ui->payTo_is->setText(recipient.address); - ui->memoTextLabel_is->setText(recipient.message); - ui->payAmount_is->setValue(recipient.amount); - ui->payAmount_is->setReadOnly(true); - setCurrentWidget(ui->SendCoins_UnauthenticatedPaymentRequest); - } - else // authenticated - { - ui->payTo_s->setText(recipient.authenticatedMerchant); - ui->memoTextLabel_s->setText(recipient.message); - ui->payAmount_s->setValue(recipient.amount); - ui->payAmount_s->setReadOnly(true); - setCurrentWidget(ui->SendCoins_AuthenticatedPaymentRequest); - } - } - else // normal payment - { - // message - ui->messageTextLabel->setText(recipient.message); - ui->messageTextLabel->setVisible(!recipient.message.isEmpty()); - ui->messageLabel->setVisible(!recipient.message.isEmpty()); - - ui->addAsLabel->clear(); - ui->payTo->setText(recipient.address); // this may set a label from addressbook - if (!recipient.label.isEmpty()) // if a label had been set from the addressbook, don't overwrite with an empty label - ui->addAsLabel->setText(recipient.label); - ui->payAmount->setValue(recipient.amount); - } + // message + ui->messageTextLabel->setText(recipient.message); + ui->messageTextLabel->setVisible(!recipient.message.isEmpty()); + ui->messageLabel->setVisible(!recipient.message.isEmpty()); + + ui->addAsLabel->clear(); + ui->payTo->setText(recipient.address); // this may set a label from addressbook + if (!recipient.label.isEmpty()) // if a label had been set from the addressbook, don't overwrite with an empty label + ui->addAsLabel->setText(recipient.label); + ui->payAmount->setValue(recipient.amount); } void SendCoinsEntry::setAddress(const QString &address) diff --git a/src/qt/test/compattests.cpp b/src/qt/test/compattests.cpp deleted file mode 100644 index 2a7284b5b22..00000000000 --- a/src/qt/test/compattests.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2016 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "paymentrequestplus.h" // this includes protobuf's port.h which defines its own bswap macos - -#include "compattests.h" - -#include "compat/byteswap.h" - -void CompatTests::bswapTests() -{ - // Sibling in bitcoin/src/test/bswap_tests.cpp - uint16_t u1 = 0x1234; - uint32_t u2 = 0x56789abc; - uint64_t u3 = 0xdef0123456789abc; - uint16_t e1 = 0x3412; - uint32_t e2 = 0xbc9a7856; - uint64_t e3 = 0xbc9a78563412f0de; - QVERIFY(bswap_16(u1) == e1); - QVERIFY(bswap_32(u2) == e2); - QVERIFY(bswap_64(u3) == e3); -} diff --git a/src/qt/test/compattests.h b/src/qt/test/compattests.h deleted file mode 100644 index 1af97696b2f..00000000000 --- a/src/qt/test/compattests.h +++ /dev/null @@ -1,19 +0,0 @@ -// 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_QT_TEST_COMPATTESTS_H -#define BITCOIN_QT_TEST_COMPATTESTS_H - -#include -#include - -class CompatTests : public QObject -{ - Q_OBJECT - -private Q_SLOTS: - void bswapTests(); -}; - -#endif // BITCOIN_QT_TEST_COMPATTESTS_H diff --git a/src/qt/test/paymentrequestdata.h b/src/qt/test/paymentrequestdata.h deleted file mode 100644 index 74a2db8ea29..00000000000 --- a/src/qt/test/paymentrequestdata.h +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -// -// Data for paymentservertests.cpp -// - -// Base64/DER-encoded fake certificate authority certificates. -// Convert pem to base64/der with: -// openssl x509 -in cert.pem -inform PEM -outform DER | openssl enc -base64 - -// Serial Number: 10302349811211485352 (0x8ef94c91b112c0a8) -// Issuer: CN=PaymentRequest Test CA -// Subject: CN=PaymentRequest Test CA -// Not Valid After : Dec 8 16:37:24 2022 GMT -// -const char* caCert1_BASE64 = -"\ -MIIB0DCCATmgAwIBAgIJAI75TJGxEsCoMA0GCSqGSIb3DQEBCwUAMCExHzAdBgNV\ -BAMTFlBheW1lbnRSZXF1ZXN0IFRlc3QgQ0EwHhcNMTIxMjEwMTYzNzI0WhcNMjIx\ -MjA4MTYzNzI0WjAhMR8wHQYDVQQDExZQYXltZW50UmVxdWVzdCBUZXN0IENBMIGf\ -MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvua59nX9radoqDYyplcns5qdVDTN1\ -7tmcGixmMYOYU3UYMU55VSsJs0dWKnMm3COQDY+N63c0XSbRqarBcsLTkaNASuPX\ -FCv1VWuEKSyy5xe4zeoDU7CVSzlxtQD9wbZW/s3ISjgaXBpwn6eVmntb0JwYxxPc\ -M1u/hrMD8BDbSQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA\ -A4GBADSaRgK5xe47XxycXBhHhr0Wgl4pAsFsufqA9aB9r8KNEHJ0yUvvbD/jaJJM\ -RtQcf0AJ9olzUMY4syehxbzUJP6aeXhZEYiMvdvcv9D55clq6+WLLlNT3jBgAaVn\ -p3waRjPD4bUX3nv+ojz5s4puw7Qq5QUZlhGsMzPvwDGCmZkL\ -"; - -// Serial Number: f0:da:97:e4:38:d7:64:16 -// Issuer: CN=PaymentRequest Test CA -// Subject: CN=PaymentRequest Test CA -// Not Valid After : Jan 8 18:21:06 2025 GMT -// -const char* caCert2_BASE64 = -"\ -MIIC1TCCAb2gAwIBAgIJAPDal+Q412QWMA0GCSqGSIb3DQEBCwUAMCExHzAdBgNV\ -BAMMFlBheW1lbnRSZXF1ZXN0IFRlc3QgQ0EwHhcNMTUwMTExMTgyMTA2WhcNMjUw\ -MTA4MTgyMTA2WjAhMR8wHQYDVQQDDBZQYXltZW50UmVxdWVzdCBUZXN0IENBMIIB\ -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1S9wVLfTplJuT/1OaaBgl/Mb\ -I392v8S9kHbzYz7B4OTMslaO7piz0v3SO3TKMh0dswjiRdHrIgpO7XdIUQiU/ugg\ -xDw0kuNehfz1ycaGedlFFtFHTNXqLyIUF3dlwHhQwaomM6RXoJmxLny5BhYHEcmk\ -yWwr3Cdjd9gAZpblugVJB9C1e40uyL8ao4PHdLzOqO27iSe6riP8SwwisJZEbMaz\ -AZpgNEEMbIXPJEFvm5HTRXSMtQCOTSZYMFF0M2yrtmlECnz7hWP19b9bcoDzZQB4\ -ylIsFG/7q2jV7MC/e2STZv+niJiHL08RUdoFpAgzaxMgqj63C7B55HgNDNHJYQID\ -AQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBGejPxLxj9\ -+crv6gUeEBMZPiUx7pUgcI22Wm5yymP96B4fwI3Y0DBehq20d76vbWGPN17Z6pH3\ -ge7PVY1SYqXtS6hXTo4olCm/BZADli+2Bs2xCiaa+Ltve4ufVej+bKJXN/YnrhvO\ -Kq+klQkuuHywU+GJV/NQeBqToIrSOBgi477NgLFCCCmmx2QWsxHoCFGfuRCBVseT\ -z2k/tMuALCDXGeZBRPTsGHu1y4cj84swAeoDK5QSQcI+Ub7GKc+zkoj02sdDLiMo\ -3wokYPcIy47oclhmb4xubHc+y7nF610yZBoC/zgbhbawnZ65hDDWkdQ/SVAnWZD7\ -9PFfmNnYPTQH\ -"; - -// -// This payment request validates directly against the -// caCert1 certificate authority. -// -const char* paymentrequest1_cert1_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhrxAwruAzCCAeowggFToAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwITEfMB0GA1UEAxMWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xMjEyMTAx\ -NjM3MjRaFw0yMjEyMDgxNjM3MjRaMEMxGTAXBgNVBAMMEHRlc3RtZXJjaGFudC5v\ -cmcxJjAkBgNVBAoMHVBheW1lbnQgUmVxdWVzdCBUZXN0IE1lcmNoYW50MIGfMA0G\ -CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHkMy8W1u6HsWlSqdWTmMKf54gICxNfxbY\ -+rcMtAftr62hCYx2d2QiSRd1pCUzmo12IiSX3WxSHwaTnT3MFD6jRx6+zM6XdGar\ -I2zpYle11ANzu4gAthN17uRQHV2O5QxVtzNaMdKeJLXT2L9tfEdyL++9ZUqoQmdA\ -YG9ix330hQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GB\ -AIkyO99KC68bi9PFRyQQ7nvn5GlQEb3Ca1bRG5+AKN9N5vc8rZ9G2hejtM8wEXni\ -eGBP+chVMsbTPEHKLrwREn7IvcyCcbAStaklPC3w0B/2idQSHskb6P3X13OR2bTH\ -a2+6wuhsOZRUrVNr24rM95DKx/eCC6JN1VW+qRPU6fqzIjQSHwiw2wYSGXapFJVg\ -igPI+6XpExtNLO/i1WFV8ZmoiKwYsuHFiwUqC1VuaXRUZXN0T25lKoABS0j59iMU\ -Uc9MdIfwsO1BskIET0eJSGNZ7eXb9N62u+qf831PMpEHkmlGpk8rHy92nPcgua/U\ -Yt8oZMn3QaTZ5A6HjJbc3A73eLylp1a0SwCl+KDMEvDQhqMn1jAVu2v92AH3uB7n\ -SiWVbw0tX/68iSQEGGfh9n6ee/8Myb3ICdw=\ -"; - -// -// Signed, but expired, merchant cert in the request -// -const char* paymentrequest2_cert1_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhrsAwrpAzCCAeUwggFOoAMCAQICAQMwDQYJKoZIhvcNAQEL\ -BQAwITEfMB0GA1UEAxMWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xMzAyMjMy\ -MTI2NDNaFw0xMzAyMjQyMTI2NDNaMD4xHDAaBgNVBAMME2V4cGlyZWRtZXJjaGFu\ -dC5vcmcxHjAcBgNVBAoMFUV4cGlyZWQgVGVzdCBNZXJjaGFudDCBnzANBgkqhkiG\ -9w0BAQEFAAOBjQAwgYkCgYEAx5DMvFtbuh7FpUqnVk5jCn+eICAsTX8W2Pq3DLQH\ -7a+toQmMdndkIkkXdaQlM5qNdiIkl91sUh8Gk509zBQ+o0cevszOl3RmqyNs6WJX\ -tdQDc7uIALYTde7kUB1djuUMVbczWjHSniS109i/bXxHci/vvWVKqEJnQGBvYsd9\ -9IUCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAaU137\ -j53rvSjlmYZpZ4RWTP7EdD6fl5ZxBeXHytN6DQL33H0eD7OFHt+ofc7E6D7keubl\ -UfCu+jOvt/MvvPUmtCI9yXZ0dNC4sjyETv+wQpxO0UNZwOM4uegdCzlo6Bi3pD4/\ -KKLdMkWuUfuPBmoammny74lZaOVr5deKXztTuCI0Eh8IsNsGEhl2qRSVYIoDyPul\ -6RMbTSzv4tVhVfGZqIisGLLhxYsFKgtVbml0VGVzdFR3byqAAXHuo4nZEPniLpkd\ -y30TkwBxVgprWJ18a9z/7Py35Qss/JMbOXbnBhJtmJCdIowHRI0aa+zqt3KKKAXi\ -mm+V4seMgxTcxMS+eDDkiTcB/RtWWSyRcS2ANjFeY0T4SLMwiCL9qWPi03hr8j96\ -tejrSPOBNSJ3Mi/q5u2Yl4gJZY2b\ -"; - -// -// 10-long certificate chain, all intermediates valid -// -const char* paymentrequest3_cert1_BASE64 = -"\ -Egt4NTA5K3NoYTI1Nhq8JAr/AzCCAfswggFkoAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwPzEUMBIGA1UEAwwLdGVzdGNhOC5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVx\ -dWVzdCBJbnRlcm1lZGlhdGUgODAeFw0xMzAyMjMyMjQyMzFaFw0yMzAyMjEyMjQy\ -MzFaMDYxGjAYBgNVBAMMEXRlc3RtZXJjaGFudDgub3JnMRgwFgYDVQQKDA9UZXN0\ -IE1lcmNoYW50IDgwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMMCHA3hiHbS\ -TKZ5K9jHRwE8NxkGp3IOx56PDB2diNkldG8XweTcRq7bBm7pdiBt4IVggtfs+6hE\ -hDYIOecyoAnVzPFTdvQ7KQdQ/fD9YLe6lk+o0edOqutPMyrxLFjSluXxEQyk7fdt\ -URloMMYfp3p1/hFCboA1rAsQ2RW38hR5AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8w\ -DQYJKoZIhvcNAQELBQADgYEAPsdFatnc2RJSpvZsw+nCiPVsllycw5ELglq9vfJz\ -nJJucRxgzmqI2iuas1ugwbXn0BEIRLK7vMF/qBzQR6M/nTxttah+KEu+okjps9vJ\ -cIyhfTyGPC5xkHaHZ7sG+UHOFhPw0/kXn0x+pbVgBZ5315axqcp1R+DTSj/whMAr\ -n0AKiAQwggIEMIIBbaADAgECAgECMA0GCSqGSIb3DQEBCwUAMD8xFDASBgNVBAMM\ -C3Rlc3RjYTcub3JnMScwJQYDVQQKDB5QYXltZW50IFJlcXVlc3QgSW50ZXJtZWRp\ -YXRlIDcwHhcNMTMwMjIzMjI0MjMxWhcNMjMwMjIxMjI0MjMxWjA/MRQwEgYDVQQD\ -DAt0ZXN0Y2E4Lm9yZzEnMCUGA1UECgweUGF5bWVudCBSZXF1ZXN0IEludGVybWVk\ -aWF0ZSA4MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDexUFfxb1sThvabp7u\ -dZz59ciThGmmAW0nP4tjrgEACgvWIInr2dZpTHbiQNF34ycsk0le1JD93D7Qb8rd\ -25OrpaO8XS2Li2zjR9cleixXjSLwV/zv8zJ8yPl/27XL++PDTKBXVpJ8/Syp+9Ty\ -plV1BqDhqtIHb/QSHEkTQXjeYQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqG\ -SIb3DQEBCwUAA4GBACMooQVbkbIZ2DaPwHDc4ULwguG3VI2Kzj50UdExmHtzm2S4\ -MQei+n+HEPjtJAx5OY520+10nfuP+12H2DRLQmWmdvDpeQ/Cv0yavlw4ZRejRFo7\ -KS83C0wo5rd+qTvvOmAN4UTArWkzYcEUulPdiXnRamb0WQHTeVdIbHVkMormCogE\ -MIICBDCCAW2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADA/MRQwEgYDVQQDDAt0ZXN0\ -Y2E2Lm9yZzEnMCUGA1UECgweUGF5bWVudCBSZXF1ZXN0IEludGVybWVkaWF0ZSA2\ -MB4XDTEzMDIyMzIyNDIzMVoXDTIzMDIyMTIyNDIzMVowPzEUMBIGA1UEAwwLdGVz\ -dGNhNy5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlhdGUg\ -NzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtjBRazrkebXAhXsbjimrMIRm\ -W/f9SwAHwXfc042keNtl0t2z6XE6UPcR2v/KrssXuCZgodeYxz6IM6lWosCM1xot\ -C3ChKKFBfVO30reuKBRUxXfKAFqxaG0YOAEzdZkkY9AGhqWloeSmgxpIfhInU0EF\ -JjCwrJ6IkijBatGoAAECAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B\ -AQsFAAOBgQDBRTi1MolmOA0niHYX0A2lN5QWHkCfX0A7GwyoMA3dvM45m/NYd4WB\ -X+HwfnfYcI6X9jOgNo5OWmc4GGsld0HlxwMYEKISBS9PbSHPBrb3TBOlw5ztQpXZ\ -91+bOhLux52Fr03sK7v9qExmBM12M8UR2ltpzAMiUgLLMHyPfiWkvQqIBDCCAgQw\ -ggFtoAMCAQICAQIwDQYJKoZIhvcNAQELBQAwPzEUMBIGA1UEAwwLdGVzdGNhNS5v\ -cmcxJzAlBgNVBAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlhdGUgNTAeFw0x\ -MzAyMjMyMjQyMzBaFw0yMzAyMjEyMjQyMzBaMD8xFDASBgNVBAMMC3Rlc3RjYTYu\ -b3JnMScwJQYDVQQKDB5QYXltZW50IFJlcXVlc3QgSW50ZXJtZWRpYXRlIDYwgZ8w\ -DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANJSH3xivX1t9olIdHsznI1aE9SD7t9i\ -SZJsIB0otoETHZRVv9M9LvyzBNK98ZV+kTOlST7PJgC0d9BQM9sgYApSRq5oqKDM\ -9FXbOm/yaReAbU3mkFNFw5roTlJ5ThEy0yOGT/DS0YBRaGIvRPRj2DiqDVdCZZ+w\ -4jo1IYHkZt4FAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQAD\ -gYEATm6+J1OmbrothO60xALKonWMBKr6hudb4amkFBqKbA9wMeM3jl+I/yKfz/Uf\ -xWuJ071IhiNv6Gxx5YwNvhUe1xMhUqHv0gpyK1Z47bD+kYS2se5sWNPNo3Y9qZDG\ -IXiGQxwHmrzaFk79Uy1xsmvsEz42w6hr25Yaw7HkIgrFveoKiAQwggIEMIIBbaAD\ -AgECAgECMA0GCSqGSIb3DQEBCwUAMD8xFDASBgNVBAMMC3Rlc3RjYTQub3JnMScw\ -JQYDVQQKDB5QYXltZW50IFJlcXVlc3QgSW50ZXJtZWRpYXRlIDQwHhcNMTMwMjIz\ -MjI0MjMwWhcNMjMwMjIxMjI0MjMwWjA/MRQwEgYDVQQDDAt0ZXN0Y2E1Lm9yZzEn\ -MCUGA1UECgweUGF5bWVudCBSZXF1ZXN0IEludGVybWVkaWF0ZSA1MIGfMA0GCSqG\ -SIb3DQEBAQUAA4GNADCBiQKBgQC7vVUFpxHzz2Tr/xij3k58s8d/BPA0R6D5RXTV\ -vmhAzc1Zuin4zUKRFs/aCj/0yED8Wu/COfNGF4tVlRNMdl9EcFsxa8XGEL4eAZa+\ -H/rOHH+7/1EINrrVWhZlUecyhilN8jmCZmqEM3ecuD0NAViqyMrgmaiFmsLoQZpE\ -GepDUQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAEdJ\ -Ss8jWiooja3WZzHXeF95QkBJNjIlpDLGcpl4opOYLSuEl9Uxp//LaQQiXuzpj4/I\ -pkWGQmMy5HOyH1lqDyiMgXpcG8PE0jEQAoEUGZ0QEqB1mZ6BCrYvmUuf/5aSVd8Y\ -6lKMR3WzFDYU9Zy0nzuHB/3nvp6MeDRQeRMtYvz4CogEMIICBDCCAW2gAwIBAgIB\ -AjANBgkqhkiG9w0BAQsFADA/MRQwEgYDVQQDDAt0ZXN0Y2EzLm9yZzEnMCUGA1UE\ -CgweUGF5bWVudCBSZXF1ZXN0IEludGVybWVkaWF0ZSAzMB4XDTEzMDIyMzIyNDIy\ -OVoXDTIzMDIyMTIyNDIyOVowPzEUMBIGA1UEAwwLdGVzdGNhNC5vcmcxJzAlBgNV\ -BAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlhdGUgNDCBnzANBgkqhkiG9w0B\ -AQEFAAOBjQAwgYkCgYEAxYYo3w2UXiYg6O8b4QgwN/vgreTkiW122Ep/z2TiDrhV\ -MhfOOiKdwYESPflfnXnVaQQzCGexYTQqsvqvzHSyna5hL0zPTRJxSKmTVrXRsWtp\ -dCRhjxCGipS3tlQBDi7vb+7SNRIBK4dBjjGzALNk7gMCpy+yM8f6I043jTlmGb0C\ -AwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQDU+IQxt3Oh\ -KqaUYWC23+cB2gekvWqwMBnrCNrX/Dp+kjoJKUoR2Fs3qw53raHES4SIhpGT9l9l\ -rppNQgFe/JMHeYqOZMZO+6kuU0olJanBJ14tPIc7zlMTQ9OfmZ6v07IpyFbsQDtR\ -hpe80DpuvSFPfJ4fh0WrQf6kn3KDVpGDnAqIBDCCAgQwggFtoAMCAQICAQIwDQYJ\ -KoZIhvcNAQELBQAwPzEUMBIGA1UEAwwLdGVzdGNhMi5vcmcxJzAlBgNVBAoMHlBh\ -eW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlhdGUgMjAeFw0xMzAyMjMyMjQyMjlaFw0y\ -MzAyMjEyMjQyMjlaMD8xFDASBgNVBAMMC3Rlc3RjYTMub3JnMScwJQYDVQQKDB5Q\ -YXltZW50IFJlcXVlc3QgSW50ZXJtZWRpYXRlIDMwgZ8wDQYJKoZIhvcNAQEBBQAD\ -gY0AMIGJAoGBANzgVP99Qg98e6NsKEz1v5KqRB7NTBRRsYnBvb/TSWipvMQaCYuE\ -yk1xG57x++QuASKeR3QHRQJOoAhQaj9JLUhSSv9GQ5PrFLLsOFv7L1tpzXHh2dOB\ -IW92X2yFRW2s39q+Q21yvN+N8uoKdqXhzRA+dDoXh3cavaVeHX1G+IrlAgMBAAGj\ -EDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEASTwg84cX+1UhOG9s\ -ejFV3m34QuI1hPZ+qhqVJlRYUtego8Wng1BburDSwqVAv4ch2wi3c2s4e8J7AXyL\ -tzSbSQG4RN0oZi0mR8EtTTN+Mix/hBIk79dMZg85+I29uFA6Zj2d9oAhQv2qkHhc\ -6tcaheNvkQRlCyH68k3iF1Fqf+4KiAQwggIEMIIBbaADAgECAgECMA0GCSqGSIb3\ -DQEBCwUAMD8xFDASBgNVBAMMC3Rlc3RjYTEub3JnMScwJQYDVQQKDB5QYXltZW50\ -IFJlcXVlc3QgSW50ZXJtZWRpYXRlIDEwHhcNMTMwMjIzMjI0MjI5WhcNMjMwMjIx\ -MjI0MjI5WjA/MRQwEgYDVQQDDAt0ZXN0Y2EyLm9yZzEnMCUGA1UECgweUGF5bWVu\ -dCBSZXF1ZXN0IEludGVybWVkaWF0ZSAyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB\ -iQKBgQDaV8zhfyQuSf/f+fauMfgs3g/RnWy9yxxUkvQneQQPH3uZzCyk3A6q72ip\ -TtwNqiibG9455L9A7SaUjGtnpUz0NKT/VWUdqbfCl1PqXjEZbDobbAQ5hxLGOTyL\ -RQhLIcgeq2/BnmeCqHsC4md04nUp+nBo1HwKyygvK+9sMbCp/wIDAQABoxAwDjAM\ -BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBACvYyE+PPmWFkbjyRu9LAt8D\ -crtyYYLRClKSg6tVvutwukLG2l//kDOohYkJtgTqr6LnCIIIwYdXN+4wxugmw4cn\ -PIZmP6kovxjhhVM95okilor1zniTAo3RN7JDIfTGNgxLdGu1btt7DOFL4zTbeSJM\ -b8M1JpPftehH+x/VLyuUCuoDMIIB5jCCAU+gAwIBAgIBBTANBgkqhkiG9w0BAQsF\ -ADAhMR8wHQYDVQQDExZQYXltZW50UmVxdWVzdCBUZXN0IENBMB4XDTEzMDIyMzIy\ -NDIyOFoXDTIzMDIyMTIyNDIyOFowPzEUMBIGA1UEAwwLdGVzdGNhMS5vcmcxJzAl\ -BgNVBAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlhdGUgMTCBnzANBgkqhkiG\ -9w0BAQEFAAOBjQAwgYkCgYEAo5Vy9H3nA/OOkF5Ap89yfVNSiTay/LYCaB0eALpc\ -U690U75O9Q3w2M+2AN8wpbbHsJHZMIjEeBRoQfjlYXW1ucQTxWKyT+liu0D25mGX\ -X27CBXBd4iXTxVII/iX+u3lcjORjoHOBy7QgeIDIIS9y0vYu8eArpjh7m4thrVgI\ -RtMCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQB9LKcV\ -JK9sjASNzpQlpUp7nCiw5FSjVY+XMRIKK/kavzlKjZ+InsmmyRVGjDoZi9GrqG9P\ -VHgLBxi2VtVjmokZoNPqao3OfhqORAubC+JR/JLepM7aDaxDdTHVhSUk4lgNAvi2\ -6dGY7nZMsnHlPQ2tPp/HvRRiMq1oDjlylc8VTCI2Eh8IsNsGEhl2qRSVYIoDyPul\ -6RMbTSzv4tVhVfGZqIisGLLhxYsFKg1Vbml0VGVzdFRocmVlKoABn2HTsUQtMNI4\ -yNvkfkFNka3pRvTUTydJrvyfmEeLzImfM1BWddZjnywku9RToNFZZNgow5QnljmF\ -chhR/aHOuEMTxmc12K4rNlgYtHCsxLP9zd+6u0cva3TucZ6EzS8PKEib/+r12/52\ -664NuWA9WtsK7QCFrK2K95PnVCRmWl0=\ -"; - -// -// Long certificate chain, with an expired certificate in the middle -// -const char* paymentrequest4_cert1_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhqeJAr/AzCCAfswggFkoAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwPzEUMBIGA1UEAwwLdGVzdGNhOC5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVx\ -dWVzdCBJbnRlcm1lZGlhdGUgODAeFw0xMzAyMjMyMjQyMzFaFw0yMzAyMjEyMjQy\ -MzFaMDYxGjAYBgNVBAMMEXRlc3RtZXJjaGFudDgub3JnMRgwFgYDVQQKDA9UZXN0\ -IE1lcmNoYW50IDgwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMMCHA3hiHbS\ -TKZ5K9jHRwE8NxkGp3IOx56PDB2diNkldG8XweTcRq7bBm7pdiBt4IVggtfs+6hE\ -hDYIOecyoAnVzPFTdvQ7KQdQ/fD9YLe6lk+o0edOqutPMyrxLFjSluXxEQyk7fdt\ -URloMMYfp3p1/hFCboA1rAsQ2RW38hR5AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8w\ -DQYJKoZIhvcNAQELBQADgYEAPsdFatnc2RJSpvZsw+nCiPVsllycw5ELglq9vfJz\ -nJJucRxgzmqI2iuas1ugwbXn0BEIRLK7vMF/qBzQR6M/nTxttah+KEu+okjps9vJ\ -cIyhfTyGPC5xkHaHZ7sG+UHOFhPw0/kXn0x+pbVgBZ5315axqcp1R+DTSj/whMAr\ -n0AKiAQwggIEMIIBbaADAgECAgECMA0GCSqGSIb3DQEBCwUAMD8xFDASBgNVBAMM\ -C3Rlc3RjYTcub3JnMScwJQYDVQQKDB5QYXltZW50IFJlcXVlc3QgSW50ZXJtZWRp\ -YXRlIDcwHhcNMTMwMjIzMjI0MjMxWhcNMjMwMjIxMjI0MjMxWjA/MRQwEgYDVQQD\ -DAt0ZXN0Y2E4Lm9yZzEnMCUGA1UECgweUGF5bWVudCBSZXF1ZXN0IEludGVybWVk\ -aWF0ZSA4MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDexUFfxb1sThvabp7u\ -dZz59ciThGmmAW0nP4tjrgEACgvWIInr2dZpTHbiQNF34ycsk0le1JD93D7Qb8rd\ -25OrpaO8XS2Li2zjR9cleixXjSLwV/zv8zJ8yPl/27XL++PDTKBXVpJ8/Syp+9Ty\ -plV1BqDhqtIHb/QSHEkTQXjeYQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqG\ -SIb3DQEBCwUAA4GBACMooQVbkbIZ2DaPwHDc4ULwguG3VI2Kzj50UdExmHtzm2S4\ -MQei+n+HEPjtJAx5OY520+10nfuP+12H2DRLQmWmdvDpeQ/Cv0yavlw4ZRejRFo7\ -KS83C0wo5rd+qTvvOmAN4UTArWkzYcEUulPdiXnRamb0WQHTeVdIbHVkMormCogE\ -MIICBDCCAW2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADA/MRQwEgYDVQQDDAt0ZXN0\ -Y2E2Lm9yZzEnMCUGA1UECgweUGF5bWVudCBSZXF1ZXN0IEludGVybWVkaWF0ZSA2\ -MB4XDTEzMDIyMzIyNDIzMVoXDTIzMDIyMTIyNDIzMVowPzEUMBIGA1UEAwwLdGVz\ -dGNhNy5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlhdGUg\ -NzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtjBRazrkebXAhXsbjimrMIRm\ -W/f9SwAHwXfc042keNtl0t2z6XE6UPcR2v/KrssXuCZgodeYxz6IM6lWosCM1xot\ -C3ChKKFBfVO30reuKBRUxXfKAFqxaG0YOAEzdZkkY9AGhqWloeSmgxpIfhInU0EF\ -JjCwrJ6IkijBatGoAAECAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B\ -AQsFAAOBgQDBRTi1MolmOA0niHYX0A2lN5QWHkCfX0A7GwyoMA3dvM45m/NYd4WB\ -X+HwfnfYcI6X9jOgNo5OWmc4GGsld0HlxwMYEKISBS9PbSHPBrb3TBOlw5ztQpXZ\ -91+bOhLux52Fr03sK7v9qExmBM12M8UR2ltpzAMiUgLLMHyPfiWkvQqIBDCCAgQw\ -ggFtoAMCAQICAQIwDQYJKoZIhvcNAQELBQAwPzEUMBIGA1UEAwwLdGVzdGNhNS5v\ -cmcxJzAlBgNVBAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlhdGUgNTAeFw0x\ -MzAyMjMyMjQyMzBaFw0yMzAyMjEyMjQyMzBaMD8xFDASBgNVBAMMC3Rlc3RjYTYu\ -b3JnMScwJQYDVQQKDB5QYXltZW50IFJlcXVlc3QgSW50ZXJtZWRpYXRlIDYwgZ8w\ -DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANJSH3xivX1t9olIdHsznI1aE9SD7t9i\ -SZJsIB0otoETHZRVv9M9LvyzBNK98ZV+kTOlST7PJgC0d9BQM9sgYApSRq5oqKDM\ -9FXbOm/yaReAbU3mkFNFw5roTlJ5ThEy0yOGT/DS0YBRaGIvRPRj2DiqDVdCZZ+w\ -4jo1IYHkZt4FAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQAD\ -gYEATm6+J1OmbrothO60xALKonWMBKr6hudb4amkFBqKbA9wMeM3jl+I/yKfz/Uf\ -xWuJ071IhiNv6Gxx5YwNvhUe1xMhUqHv0gpyK1Z47bD+kYS2se5sWNPNo3Y9qZDG\ -IXiGQxwHmrzaFk79Uy1xsmvsEz42w6hr25Yaw7HkIgrFveoK6gMwggHmMIIBT6AD\ -AgECAgEGMA0GCSqGSIb3DQEBCwUAMCExHzAdBgNVBAMTFlBheW1lbnRSZXF1ZXN0\ -IFRlc3QgQ0EwHhcNMTMwMjIzMjI1OTUxWhcNMTMwMjI0MjI1OTUxWjA/MRQwEgYD\ -VQQDDAt0ZXN0Y2E1Lm9yZzEnMCUGA1UECgweUGF5bWVudCBSZXF1ZXN0IEludGVy\ -bWVkaWF0ZSA1MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7vVUFpxHzz2Tr\ -/xij3k58s8d/BPA0R6D5RXTVvmhAzc1Zuin4zUKRFs/aCj/0yED8Wu/COfNGF4tV\ -lRNMdl9EcFsxa8XGEL4eAZa+H/rOHH+7/1EINrrVWhZlUecyhilN8jmCZmqEM3ec\ -uD0NAViqyMrgmaiFmsLoQZpEGepDUQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0G\ -CSqGSIb3DQEBCwUAA4GBAEmcUEnhua/oiXy1fwScLgMqt+jk9mHRpE6SVsIop23Q\ -CY2JfpG6RxhMMzzzhGklEGN6cxG0HCi6B3HJx6PYrFEfTB0rW4K6m0Tvx3WpS9mN\ -uoEuJHLy18ausI/sYAPDHCL+SfBVcqorpaIG2sSpZouRBjRHAyqFAYlwlW87uq5n\ -CogEMIICBDCCAW2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADA/MRQwEgYDVQQDDAt0\ -ZXN0Y2EzLm9yZzEnMCUGA1UECgweUGF5bWVudCBSZXF1ZXN0IEludGVybWVkaWF0\ -ZSAzMB4XDTEzMDIyMzIyNDIyOVoXDTIzMDIyMTIyNDIyOVowPzEUMBIGA1UEAwwL\ -dGVzdGNhNC5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlh\ -dGUgNDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxYYo3w2UXiYg6O8b4Qgw\ -N/vgreTkiW122Ep/z2TiDrhVMhfOOiKdwYESPflfnXnVaQQzCGexYTQqsvqvzHSy\ -na5hL0zPTRJxSKmTVrXRsWtpdCRhjxCGipS3tlQBDi7vb+7SNRIBK4dBjjGzALNk\ -7gMCpy+yM8f6I043jTlmGb0CAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG\ -9w0BAQsFAAOBgQDU+IQxt3OhKqaUYWC23+cB2gekvWqwMBnrCNrX/Dp+kjoJKUoR\ -2Fs3qw53raHES4SIhpGT9l9lrppNQgFe/JMHeYqOZMZO+6kuU0olJanBJ14tPIc7\ -zlMTQ9OfmZ6v07IpyFbsQDtRhpe80DpuvSFPfJ4fh0WrQf6kn3KDVpGDnAqIBDCC\ -AgQwggFtoAMCAQICAQIwDQYJKoZIhvcNAQELBQAwPzEUMBIGA1UEAwwLdGVzdGNh\ -Mi5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1lZGlhdGUgMjAe\ -Fw0xMzAyMjMyMjQyMjlaFw0yMzAyMjEyMjQyMjlaMD8xFDASBgNVBAMMC3Rlc3Rj\ -YTMub3JnMScwJQYDVQQKDB5QYXltZW50IFJlcXVlc3QgSW50ZXJtZWRpYXRlIDMw\ -gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANzgVP99Qg98e6NsKEz1v5KqRB7N\ -TBRRsYnBvb/TSWipvMQaCYuEyk1xG57x++QuASKeR3QHRQJOoAhQaj9JLUhSSv9G\ -Q5PrFLLsOFv7L1tpzXHh2dOBIW92X2yFRW2s39q+Q21yvN+N8uoKdqXhzRA+dDoX\ -h3cavaVeHX1G+IrlAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL\ -BQADgYEASTwg84cX+1UhOG9sejFV3m34QuI1hPZ+qhqVJlRYUtego8Wng1BburDS\ -wqVAv4ch2wi3c2s4e8J7AXyLtzSbSQG4RN0oZi0mR8EtTTN+Mix/hBIk79dMZg85\ -+I29uFA6Zj2d9oAhQv2qkHhc6tcaheNvkQRlCyH68k3iF1Fqf+4KiAQwggIEMIIB\ -baADAgECAgECMA0GCSqGSIb3DQEBCwUAMD8xFDASBgNVBAMMC3Rlc3RjYTEub3Jn\ -MScwJQYDVQQKDB5QYXltZW50IFJlcXVlc3QgSW50ZXJtZWRpYXRlIDEwHhcNMTMw\ -MjIzMjI0MjI5WhcNMjMwMjIxMjI0MjI5WjA/MRQwEgYDVQQDDAt0ZXN0Y2EyLm9y\ -ZzEnMCUGA1UECgweUGF5bWVudCBSZXF1ZXN0IEludGVybWVkaWF0ZSAyMIGfMA0G\ -CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaV8zhfyQuSf/f+fauMfgs3g/RnWy9yxxU\ -kvQneQQPH3uZzCyk3A6q72ipTtwNqiibG9455L9A7SaUjGtnpUz0NKT/VWUdqbfC\ -l1PqXjEZbDobbAQ5hxLGOTyLRQhLIcgeq2/BnmeCqHsC4md04nUp+nBo1HwKyygv\ -K+9sMbCp/wIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GB\ -ACvYyE+PPmWFkbjyRu9LAt8DcrtyYYLRClKSg6tVvutwukLG2l//kDOohYkJtgTq\ -r6LnCIIIwYdXN+4wxugmw4cnPIZmP6kovxjhhVM95okilor1zniTAo3RN7JDIfTG\ -NgxLdGu1btt7DOFL4zTbeSJMb8M1JpPftehH+x/VLyuUCuoDMIIB5jCCAU+gAwIB\ -AgIBBTANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDExZQYXltZW50UmVxdWVzdCBU\ -ZXN0IENBMB4XDTEzMDIyMzIyNDIyOFoXDTIzMDIyMTIyNDIyOFowPzEUMBIGA1UE\ -AwwLdGVzdGNhMS5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVxdWVzdCBJbnRlcm1l\ -ZGlhdGUgMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAo5Vy9H3nA/OOkF5A\ -p89yfVNSiTay/LYCaB0eALpcU690U75O9Q3w2M+2AN8wpbbHsJHZMIjEeBRoQfjl\ -YXW1ucQTxWKyT+liu0D25mGXX27CBXBd4iXTxVII/iX+u3lcjORjoHOBy7QgeIDI\ -IS9y0vYu8eArpjh7m4thrVgIRtMCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkq\ -hkiG9w0BAQsFAAOBgQB9LKcVJK9sjASNzpQlpUp7nCiw5FSjVY+XMRIKK/kavzlK\ -jZ+InsmmyRVGjDoZi9GrqG9PVHgLBxi2VtVjmokZoNPqao3OfhqORAubC+JR/JLe\ -pM7aDaxDdTHVhSUk4lgNAvi26dGY7nZMsnHlPQ2tPp/HvRRiMq1oDjlylc8VTCI1\ -Eh8IsNsGEhl2qRSVYIoDyPul6RMbTSzv4tVhVfGZqIisGLLhxYsFKgxVbml0VGVz\ -dEZvdXIqgAEBE1PP93Tkpif35F+dYmXn9kLA/1djcPjCs2o2rwRMM4Uk356O5dgu\ -HXQjsfdR58qZQS9CS5DAtRUf0R8+43/wijO/hb49VNaNXmY+/cPHMkahP2aV3tZi\ -FAyZblLik9A7ZvF+UsjeFQiHB5wzWQvbqk5wQ4yabHIXoYv/E0q+eQ==\ -"; - -// -// Validly signed, but by a CA not in our root CA list -// -const char* paymentrequest5_cert1_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhrxAwruAzCCAeowggFToAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwITEfMB0GA1UEAxMWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xMzA0MTkx\ -NzIwMDZaFw0yMzA0MTcxNzIwMDZaMEMxGTAXBgNVBAMMEHRlc3RtZXJjaGFudC5v\ -cmcxJjAkBgNVBAoMHVBheW1lbnQgUmVxdWVzdCBUZXN0IE1lcmNoYW50MIGfMA0G\ -CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhV6Yn47aEEmbl50YLvXoqGEJA51I/40wr\ -Z6VQGdXYaRqYktagrWDlgYY9h0JQ1bQhm8HgW7ju0R4NaDTXUqxg4HjprF0z3Mfm\ -/6mmebkLOOptfkVD7ceAteNI7cyuqWGIAZA7D9mV97mXoCAtTlBUycvkmoiClCCS\ -h0EpF/UTaQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GB\ -AGIRwW7I0QvLga+RnJoJSZNZQbtu4rQW3xmoz8WfZMBYXX3QBYg5ftycbdK+/IbP\ -qozfjGW2AS6DNArvpveSPDTK9+GJBNo1paiNtVqwXkC3Ddscv5AIms1eZGiIOQNC\ -mUvdLkpoXo48WAer3EGsZ3B15GyNEELc0q9W5yUebba1IjUSHwiw2wYSGXapFJVg\ -igPI+6XpExtNLO/i1WFV8ZmoiKwYuPvFiwUqDFVuaXRUZXN0Rml2ZSqAAXdsMgdG\ -ssymvca1S/1KeM3n8Ydi2fi1JUzAAr59xPvNJRUeqCLP9upHn5z7br3P12Oz9A20\ -5/4wL4ClPRPVnOHgij0bEg+y0tGESqmF1rfOfXDszlo2U92wCxS07kq79YAZJ1Zo\ -XYh860/Q4wvc7lfiTe+dXBzPKAKhMy91yETY\ -"; - -// -// Contains a testnet paytoaddress, so payment request network doesn't match client network -// -const char* paymentrequest1_cert2_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhrQBArNBDCCAkkwggExoAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwITEfMB0GA1UEAwwWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xNTAxMTEx\ -ODIxMDhaFw0yNTAxMDgxODIxMDhaMCExHzAdBgNVBAMMFlBheW1lbnRSZXF1ZXN0\ -IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMsZqzkzeBGo+i2N\ -mUak3Ciodr1V7S062VOy7N0OQYNDQHYkgDFAUET7cEb5VJaHPv5m3ppTBpU9xBcf\ -wbHHUt4VjA+mhRmYrl1khjvZM+X8kEqvWn20BtcM9R6r0yIYec8UERDDHBleL/P8\ -RkxEnVLjYTV9zigCXfMsgYb3EQShAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ\ -KoZIhvcNAQELBQADggEBABUJpl3QCqsoDSxAsQdV6zKT4VGV76AzoGj7etQsQY+r\ -+S26VfWh/fMobEzuxFChr0USgLJ6FoK78hAtoZvt1lrye9yqFv/ig3WLWsJKWHHb\ -3RT6oR03CIwZXFSUasi08QDVLxafwsU5OMcPLucF3a1lRL1ccYrNgVCCx1+X7Bos\ -tIgDGRQQ4AyoHTcfVd2hEGeUv7k14mOxFsAp6851yosHq9Q2kwmdH+rHEJbjof87\ -yyKLagc4owyXBZYkQmkeHWCNqnuRmO5vUsfVb0UUrkD64o7Th/NjwooA7SCiUXl6\ -dfygT1b7ggpx7GC+sP2DsIM47IAZ55drjqX5u2f+Ba0iPQoEdGVzdBIhCIDWwowE\ -Ehl2qRQErGqUUwSsaMpDvWIaGnJGNQqi8oisGNeMy6UFKgxKdXN0IFRlc3Rpbmcq\ -gAFwThsozZxkZxzCn4R8WxNiLFV6m0ye9fEtSbolfaW+EjBMpO03lr/dwNnrclhg\ -ew+A05xfZztrAt16XKEY7qKJ/eY2nLd0fVAIu/nIt+7/VYVXT83zLrWc150aRS7W\ -AdJbL3JOJLs6Eyp5zrPbfI8faRttFAdONKDrJgIpuW1E3g==\ -"; - -// -// Expired payment request (expires is set to 1 = 1970-01-01 00:00:01) -// -const char* paymentrequest2_cert2_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhrQBArNBDCCAkkwggExoAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwITEfMB0GA1UEAwwWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xNTAxMTEx\ -ODIxMDhaFw0yNTAxMDgxODIxMDhaMCExHzAdBgNVBAMMFlBheW1lbnRSZXF1ZXN0\ -IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMsZqzkzeBGo+i2N\ -mUak3Ciodr1V7S062VOy7N0OQYNDQHYkgDFAUET7cEb5VJaHPv5m3ppTBpU9xBcf\ -wbHHUt4VjA+mhRmYrl1khjvZM+X8kEqvWn20BtcM9R6r0yIYec8UERDDHBleL/P8\ -RkxEnVLjYTV9zigCXfMsgYb3EQShAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ\ -KoZIhvcNAQELBQADggEBABUJpl3QCqsoDSxAsQdV6zKT4VGV76AzoGj7etQsQY+r\ -+S26VfWh/fMobEzuxFChr0USgLJ6FoK78hAtoZvt1lrye9yqFv/ig3WLWsJKWHHb\ -3RT6oR03CIwZXFSUasi08QDVLxafwsU5OMcPLucF3a1lRL1ccYrNgVCCx1+X7Bos\ -tIgDGRQQ4AyoHTcfVd2hEGeUv7k14mOxFsAp6851yosHq9Q2kwmdH+rHEJbjof87\ -yyKLagc4owyXBZYkQmkeHWCNqnuRmO5vUsfVb0UUrkD64o7Th/NjwooA7SCiUXl6\ -dfygT1b7ggpx7GC+sP2DsIM47IAZ55drjqX5u2f+Ba0iQgoEdGVzdBIgCICt4gQS\ -GXapFASsapRTBKxoykO9YhoackY1CqLyiKwYiNLUpQUgASoQVGVzdGluZyB0ZXN0\ -bmV0ISqAATXq9A5nmJgtmee/bQTeHeif4w1YYFPBlKghwx6qbVgXTWnwBJtOQhhV\ -sZdzbTl95ENR7/Y7VJupW9kDWobCK7zUUhLAzUlwmLlcx6itHw8LTUF5HK+AwsZm\ -Zs85lISGvOS0NZW/ENa6l+oQRnL87oqVZr/EDGiuqjz6T0ThQi0l\ -"; - -// -// Unexpired payment request (expires is set to 0x7FFFFFFFFFFFFFFF = max. int64_t) -// -const char* paymentrequest3_cert2_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhrQBArNBDCCAkkwggExoAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwITEfMB0GA1UEAwwWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xNTAxMTEx\ -ODIxMDhaFw0yNTAxMDgxODIxMDhaMCExHzAdBgNVBAMMFlBheW1lbnRSZXF1ZXN0\ -IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMsZqzkzeBGo+i2N\ -mUak3Ciodr1V7S062VOy7N0OQYNDQHYkgDFAUET7cEb5VJaHPv5m3ppTBpU9xBcf\ -wbHHUt4VjA+mhRmYrl1khjvZM+X8kEqvWn20BtcM9R6r0yIYec8UERDDHBleL/P8\ -RkxEnVLjYTV9zigCXfMsgYb3EQShAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ\ -KoZIhvcNAQELBQADggEBABUJpl3QCqsoDSxAsQdV6zKT4VGV76AzoGj7etQsQY+r\ -+S26VfWh/fMobEzuxFChr0USgLJ6FoK78hAtoZvt1lrye9yqFv/ig3WLWsJKWHHb\ -3RT6oR03CIwZXFSUasi08QDVLxafwsU5OMcPLucF3a1lRL1ccYrNgVCCx1+X7Bos\ -tIgDGRQQ4AyoHTcfVd2hEGeUv7k14mOxFsAp6851yosHq9Q2kwmdH+rHEJbjof87\ -yyKLagc4owyXBZYkQmkeHWCNqnuRmO5vUsfVb0UUrkD64o7Th/NjwooA7SCiUXl6\ -dfygT1b7ggpx7GC+sP2DsIM47IAZ55drjqX5u2f+Ba0iSgoEdGVzdBIgCICt4gQS\ -GXapFASsapRTBKxoykO9YhoackY1CqLyiKwYyNfZpQUg//////////9/KhBUZXN0\ -aW5nIHRlc3RuZXQhKoABNwi8WnMW4aMvbmvorTiiWJLFhofLFnsoWCJnj3rWLnLh\ -n3w6q/fZ26p50ERL/noxdTUfeFsKnlECkUu/fOcOrqyYDiwvxI0SZ034DleVyFU1\ -Z3T+X0zcL8oe7bX01Yf+s2V+5JXQXarKnKBrZCGgv2ARjFNSZe7E7vGg5K4Q6Q8=\ -"; - -// -// Unexpired payment request (expires is set to 0x8000000000000000 > max. int64_t, allowed uint64) -// -const char* paymentrequest4_cert2_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhrQBArNBDCCAkkwggExoAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwITEfMB0GA1UEAwwWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xNTAxMTEx\ -ODIxMDhaFw0yNTAxMDgxODIxMDhaMCExHzAdBgNVBAMMFlBheW1lbnRSZXF1ZXN0\ -IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMsZqzkzeBGo+i2N\ -mUak3Ciodr1V7S062VOy7N0OQYNDQHYkgDFAUET7cEb5VJaHPv5m3ppTBpU9xBcf\ -wbHHUt4VjA+mhRmYrl1khjvZM+X8kEqvWn20BtcM9R6r0yIYec8UERDDHBleL/P8\ -RkxEnVLjYTV9zigCXfMsgYb3EQShAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ\ -KoZIhvcNAQELBQADggEBABUJpl3QCqsoDSxAsQdV6zKT4VGV76AzoGj7etQsQY+r\ -+S26VfWh/fMobEzuxFChr0USgLJ6FoK78hAtoZvt1lrye9yqFv/ig3WLWsJKWHHb\ -3RT6oR03CIwZXFSUasi08QDVLxafwsU5OMcPLucF3a1lRL1ccYrNgVCCx1+X7Bos\ -tIgDGRQQ4AyoHTcfVd2hEGeUv7k14mOxFsAp6851yosHq9Q2kwmdH+rHEJbjof87\ -yyKLagc4owyXBZYkQmkeHWCNqnuRmO5vUsfVb0UUrkD64o7Th/NjwooA7SCiUXl6\ -dfygT1b7ggpx7GC+sP2DsIM47IAZ55drjqX5u2f+Ba0iSwoEdGVzdBIgCICt4gQS\ -GXapFASsapRTBKxoykO9YhoackY1CqLyiKwYt+HZpQUggICAgICAgICAASoQVGVz\ -dGluZyB0ZXN0bmV0ISqAAXSQG8+GFA18VaKarlYrOz293rNMIub0swKGcQm8jAGX\ -HSLaRgHfUDeEPr4hydy4dtfu59KNwe2xsHOHu/SpO4L8SrA4Dm9A7SlNBVWdcLbw\ -d2hj739GDLz0b5KuJ2SG6VknMRQM976w/m2qlq0ccVGaaZ2zMIGfpzL3p6adwx/5\ -"; - -// -// Payment request with amount overflow (amount is set to 21000001 BTC) -// -const char* paymentrequest5_cert2_BASE64 = -"\ -Egt4NTA5K3NoYTI1NhrQBArNBDCCAkkwggExoAMCAQICAQEwDQYJKoZIhvcNAQEL\ -BQAwITEfMB0GA1UEAwwWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xNTAxMTEx\ -ODIxMDhaFw0yNTAxMDgxODIxMDhaMCExHzAdBgNVBAMMFlBheW1lbnRSZXF1ZXN0\ -IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMsZqzkzeBGo+i2N\ -mUak3Ciodr1V7S062VOy7N0OQYNDQHYkgDFAUET7cEb5VJaHPv5m3ppTBpU9xBcf\ -wbHHUt4VjA+mhRmYrl1khjvZM+X8kEqvWn20BtcM9R6r0yIYec8UERDDHBleL/P8\ -RkxEnVLjYTV9zigCXfMsgYb3EQShAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ\ -KoZIhvcNAQELBQADggEBABUJpl3QCqsoDSxAsQdV6zKT4VGV76AzoGj7etQsQY+r\ -+S26VfWh/fMobEzuxFChr0USgLJ6FoK78hAtoZvt1lrye9yqFv/ig3WLWsJKWHHb\ -3RT6oR03CIwZXFSUasi08QDVLxafwsU5OMcPLucF3a1lRL1ccYrNgVCCx1+X7Bos\ -tIgDGRQQ4AyoHTcfVd2hEGeUv7k14mOxFsAp6851yosHq9Q2kwmdH+rHEJbjof87\ -yyKLagc4owyXBZYkQmkeHWCNqnuRmO5vUsfVb0UUrkD64o7Th/NjwooA7SCiUXl6\ -dfygT1b7ggpx7GC+sP2DsIM47IAZ55drjqX5u2f+Ba0iTAoEdGVzdBIkCIDC9P+F\ -vt0DEhl2qRQErGqUUwSsaMpDvWIaGnJGNQqi8oisGLzcrKYFKhhUZXN0aW5nIGFt\ -b3VudCBvdmVyZmxvdyEqgAG8S7WEDUC6tCL6q2CTBjop/AitgEy31RL9IqYruytR\ -iEBFUrBDJZU+UEezGwr7/zoECjo5ZY3PmtZcM2sILNjyweJF6XVzGqTxUw6pN6sW\ -XR2T3Gy2LzRvhVA25QgGqpz0/juS2BtmNbsZPkN9gMMwKimgzc+PuCzmEKwPK9cQ\ -YQ==\ -"; diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp deleted file mode 100644 index c849e1dec93..00000000000 --- a/src/qt/test/paymentservertests.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2020-2023 The Dogecoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "paymentservertests.h" - -#include "optionsmodel.h" -#include "paymentrequestdata.h" - -#include "amount.h" -#include "random.h" -#include "script/script.h" -#include "script/standard.h" -#include "util.h" -#include "utilstrencodings.h" - -#include -#include - -#include -#include - -X509 *parse_b64der_cert(const char* cert_data) -{ - std::vector data = DecodeBase64(cert_data); - assert(data.size() > 0); - const unsigned char* dptr = &data[0]; - X509 *cert = d2i_X509(NULL, &dptr, data.size()); - assert(cert); - return cert; -} - -// -// Test payment request handling -// - -static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector& data) -{ - RecipientCatcher sigCatcher; - QObject::connect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), - &sigCatcher, SLOT(getRecipient(SendCoinsRecipient))); - - // Write data to a temp file: - QTemporaryFile f; - f.open(); - f.write((const char*)&data[0], data.size()); - f.close(); - - // Create a QObject, install event filter from PaymentServer - // and send a file open event to the object - QObject object; - object.installEventFilter(server); - QFileOpenEvent event(f.fileName()); - // If sending the event fails, this will cause sigCatcher to be empty, - // which will lead to a test failure anyway. - QCoreApplication::sendEvent(&object, &event); - - QObject::disconnect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), - &sigCatcher, SLOT(getRecipient(SendCoinsRecipient))); - - // Return results from sigCatcher - return sigCatcher.recipient; -} - -void PaymentServerTests::paymentServerTests() -{ - SelectParams(CBaseChainParams::MAIN); - OptionsModel optionsModel; - PaymentServer* server = new PaymentServer(NULL, QString("testIPCServer"), false, true); - X509_STORE* caStore = X509_STORE_new(); - X509_STORE_add_cert(caStore, parse_b64der_cert(caCert1_BASE64)); - PaymentServer::LoadRootCAs(caStore); - server->setOptionsModel(&optionsModel); - server->uiReady(); - - std::vector data; - SendCoinsRecipient r; - QString merchant; - - // Now feed PaymentRequests to server, and observe signals it produces - - // Dogecoin: Disable certificate tests as we don't touch this code, and building test - // data would take significant effort. Also pending discussion on spec - // This payment request validates directly against the - // caCert1 certificate authority: - /* data = DecodeBase64(paymentrequest1_cert1_BASE64); - r = handleRequest(server, data); - r.paymentRequest.getMerchant(caStore, merchant); - QCOMPARE(merchant, QString("testmerchant.org")); - - // Signed, but expired, merchant cert in the request: - data = DecodeBase64(paymentrequest2_cert1_BASE64); - r = handleRequest(server, data); - r.paymentRequest.getMerchant(caStore, merchant); - QCOMPARE(merchant, QString("")); - - // 10-long certificate chain, all intermediates valid: - data = DecodeBase64(paymentrequest3_cert1_BASE64); - r = handleRequest(server, data); - r.paymentRequest.getMerchant(caStore, merchant); - QCOMPARE(merchant, QString("testmerchant8.org")); - - // Long certificate chain, with an expired certificate in the middle: - data = DecodeBase64(paymentrequest4_cert1_BASE64); - r = handleRequest(server, data); - r.paymentRequest.getMerchant(caStore, merchant); - QCOMPARE(merchant, QString("")); - - // Validly signed, but by a CA not in our root CA list: - data = DecodeBase64(paymentrequest5_cert1_BASE64); - r = handleRequest(server, data); - r.paymentRequest.getMerchant(caStore, merchant); - QCOMPARE(merchant, QString("")); - - // Try again with no root CA's, verifiedMerchant should be empty: - caStore = X509_STORE_new(); - PaymentServer::LoadRootCAs(caStore); - data = DecodeBase64(paymentrequest1_cert1_BASE64); - r = handleRequest(server, data); - r.paymentRequest.getMerchant(caStore, merchant); - QCOMPARE(merchant, QString("")); - - // Load second root certificate - caStore = X509_STORE_new(); - X509_STORE_add_cert(caStore, parse_b64der_cert(caCert2_BASE64)); - PaymentServer::LoadRootCAs(caStore); */ - - QByteArray byteArray; - - // For the tests below we just need the payment request data from - // paymentrequestdata.h parsed + stored in r.paymentRequest. - // - // These tests require us to bypass the following normal client execution flow - // shown below to be able to explicitly just trigger a certain condition! - // - // handleRequest() - // -> PaymentServer::eventFilter() - // -> PaymentServer::handleURIOrFile() - // -> PaymentServer::readPaymentRequestFromFile() - // -> PaymentServer::processPaymentRequest() - - // Contains a testnet paytoaddress, so payment request network doesn't match client network: - data = DecodeBase64(paymentrequest1_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); - r.paymentRequest.parse(byteArray); - // Ensure the request is initialized, because network "main" is default, even for - // uninizialized payment requests and that will fail our test here. - QVERIFY(r.paymentRequest.IsInitialized()); - QCOMPARE(PaymentServer::verifyNetwork(r.paymentRequest.getDetails()), false); - - // Expired payment request (expires is set to 1 = 1970-01-01 00:00:01): - data = DecodeBase64(paymentrequest2_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); - r.paymentRequest.parse(byteArray); - // Ensure the request is initialized - QVERIFY(r.paymentRequest.IsInitialized()); - // compares 1 < GetTime() == false (treated as expired payment request) - QCOMPARE(PaymentServer::verifyExpired(r.paymentRequest.getDetails()), true); - - // Unexpired payment request (expires is set to 0x7FFFFFFFFFFFFFFF = max. int64_t): - // 9223372036854775807 (uint64), 9223372036854775807 (int64_t) and -1 (int32_t) - // -1 is 1969-12-31 23:59:59 (for a 32 bit time values) - data = DecodeBase64(paymentrequest3_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); - r.paymentRequest.parse(byteArray); - // Ensure the request is initialized - QVERIFY(r.paymentRequest.IsInitialized()); - // compares 9223372036854775807 < GetTime() == false (treated as unexpired payment request) - QCOMPARE(PaymentServer::verifyExpired(r.paymentRequest.getDetails()), false); - - // Unexpired payment request (expires is set to 0x8000000000000000 > max. int64_t, allowed uint64): - // 9223372036854775808 (uint64), -9223372036854775808 (int64_t) and 0 (int32_t) - // 0 is 1970-01-01 00:00:00 (for a 32 bit time values) - data = DecodeBase64(paymentrequest4_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); - r.paymentRequest.parse(byteArray); - // Ensure the request is initialized - QVERIFY(r.paymentRequest.IsInitialized()); - // compares -9223372036854775808 < GetTime() == true (treated as expired payment request) - QCOMPARE(PaymentServer::verifyExpired(r.paymentRequest.getDetails()), true); - - // Test BIP70 DoS protection: - unsigned char randData[BIP70_MAX_PAYMENTREQUEST_SIZE + 1]; - GetRandBytes(randData, sizeof(randData)); - // Write data to a temp file: - QTemporaryFile tempFile; - tempFile.open(); - tempFile.write((const char*)randData, sizeof(randData)); - tempFile.close(); - // compares 50001 <= BIP70_MAX_PAYMENTREQUEST_SIZE == false - QCOMPARE(PaymentServer::verifySize(tempFile.size()), false); - - // Payment request with amount overflow (amount is set to 21000001 BTC): - /* PL: This doesn't work for Dogecoin (as there is no actual maximum coins) - * I'm disabling this test for now. - data = DecodeBase64(paymentrequest5_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); - r.paymentRequest.parse(byteArray); - // Ensure the request is initialized - QVERIFY(r.paymentRequest.IsInitialized()); - // Extract address and amount from the request - QList > sendingTos = r.paymentRequest.getPayTo(); - Q_FOREACH (const PAIRTYPE(CScript, CAmount)& sendingTo, sendingTos) { - CTxDestination dest; - if (ExtractDestination(sendingTo.first, dest)) - QCOMPARE(PaymentServer::verifyAmount(sendingTo.second), false); - } - */ - - delete server; -} - -void RecipientCatcher::getRecipient(SendCoinsRecipient r) -{ - recipient = r; -} diff --git a/src/qt/test/paymentservertests.h b/src/qt/test/paymentservertests.h deleted file mode 100644 index 9ffcbb02ac9..00000000000 --- a/src/qt/test/paymentservertests.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2009-2015 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_QT_TEST_PAYMENTSERVERTESTS_H -#define BITCOIN_QT_TEST_PAYMENTSERVERTESTS_H - -#include "../paymentserver.h" - -#include -#include - -class PaymentServerTests : public QObject -{ - Q_OBJECT - -private Q_SLOTS: - void paymentServerTests(); -}; - -// Dummy class to receive paymentserver signals. -// If SendCoinsRecipient was a proper QObject, then -// we could use QSignalSpy... but it's not. -class RecipientCatcher : public QObject -{ - Q_OBJECT - -public Q_SLOTS: - void getRecipient(SendCoinsRecipient r); - -public: - SendCoinsRecipient recipient; -}; - -#endif // BITCOIN_QT_TEST_PAYMENTSERVERTESTS_H diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 54a1333b8da..c803f1fa3e1 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -12,18 +12,11 @@ #include "rpcnestedtests.h" #include "util.h" #include "uritests.h" -#include "compattests.h" - -#ifdef ENABLE_WALLET -#include "paymentservertests.h" -#endif #include #include #include -#include - extern void noui_connect(); static int qt_argc = 1; @@ -45,22 +38,12 @@ int main(int argc, char *argv[]) QCoreApplication app(qt_argc, const_cast(&qt_argv)); app.setApplicationName("Bitcoin-Qt-test"); - SSL_library_init(); - URITests test1; if (QTest::qExec(&test1) != 0) fInvalid = true; -#ifdef ENABLE_WALLET - PaymentServerTests test2; - if (QTest::qExec(&test2) != 0) - fInvalid = true; -#endif RPCNestedTests test3; if (QTest::qExec(&test3) != 0) fInvalid = true; - CompatTests test4; - if (QTest::qExec(&test4) != 0) - fInvalid = true; ECC_Stop(); return fInvalid; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index d2f2de4d049..afe83773c76 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -241,21 +241,6 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (r.first == "Message") strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(r.second, true) + "
"; - // - // PaymentRequest info: - // - for (const PAIRTYPE(std::string, std::string)& r : wtx.vOrderForm) - { - if (r.first == "PaymentRequest") - { - PaymentRequestPlus req; - req.parse(QByteArray::fromRawData(r.second.data(), r.second.size())); - QString merchant; - if (req.getMerchant(PaymentServer::getCertStore(), merchant)) - strHTML += "" + tr("Merchant") + ": " + GUIUtil::HtmlEscape(merchant) + "
"; - } - } - if (wtx.IsCoinBase()) { quint32 nCoinbaseMaturity = Params().GetConsensus(chainActive.Height()).nCoinbaseMaturity + 1; diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 664f75a394d..f9eee737068 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -25,11 +25,12 @@ #include "clientmodel.h" #include "guiconstants.h" #include "intro.h" -#include "paymentrequestplus.h" #include "guiutil.h" +#include "base58.h" #include "clientversion.h" #include "init.h" +#include "key.h" #include "util.h" #include "net.h" #include "utilstrencodings.h" @@ -106,13 +107,9 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) : std::string strUsage = HelpMessage(HMM_BITCOIN_QT); const bool showDebug = GetBoolArg("-help-debug", false); strUsage += HelpMessageGroup(tr("UI Options:").toStdString()); - if (showDebug) { - strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS)); - } strUsage += HelpMessageOpt("-choosedatadir", strprintf(tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR)); strUsage += HelpMessageOpt("-lang=", tr("Set language, for example \"de_DE\" (default: system locale)").toStdString()); strUsage += HelpMessageOpt("-min", tr("Start minimized").toStdString()); - strUsage += HelpMessageOpt("-rootcertificates=", tr("Set SSL root certificates for payment request (default: -system-)").toStdString()); strUsage += HelpMessageOpt("-splash", strprintf(tr("Show splash screen on startup (default: %u)").toStdString(), DEFAULT_SPLASHSCREEN)); strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString()); if (showDebug) { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 593a0fd52b6..0b06deb7f3e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -215,46 +215,23 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact if (rcp.fSubtractFeeFromAmount) fSubtractFeeFromAmount = true; - if (rcp.paymentRequest.IsInitialized()) - { // PaymentRequest... - CAmount subtotal = 0; - const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); - for (int i = 0; i < details.outputs_size(); i++) - { - const payments::Output& out = details.outputs(i); - if (out.amount() <= 0) continue; - subtotal += out.amount(); - const unsigned char* scriptStr = (const unsigned char*)out.script().data(); - CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); - CAmount nAmount = out.amount(); - CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount}; - vecSend.push_back(recipient); - } - if (subtotal <= 0) - { - return InvalidAmount; - } - total += subtotal; + if(!validateAddress(rcp.address)) + { + return InvalidAddress; } - else - { // User-entered bitcoin address / amount: - if(!validateAddress(rcp.address)) - { - return InvalidAddress; - } - if(rcp.amount <= 0) - { - return InvalidAmount; - } - setAddress.insert(rcp.address); - ++nAddresses; + if(rcp.amount <= 0) + { + return InvalidAmount; + } + setAddress.insert(rcp.address); + ++nAddresses; - CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); - CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; - vecSend.push_back(recipient); + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; + vecSend.push_back(recipient); + + total += rcp.amount; - total += rcp.amount; - } } if(setAddress.size() != nAddresses) { @@ -315,20 +292,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients()) { - if (rcp.paymentRequest.IsInitialized()) - { - // Make sure any payment requests involved are still valid. - if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) { - return PaymentRequestExpired; - } - - // Store PaymentRequests in wtx.vOrderForm in wallet. - std::string key("PaymentRequest"); - std::string value; - rcp.paymentRequest.SerializeToString(&value); - newTx->vOrderForm.push_back(make_pair(key, value)); - } - else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) + if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); } @@ -346,26 +310,22 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran // and emit coinsSent signal for each recipient Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients()) { - // Don't touch the address book when we have a payment request - if (!rcp.paymentRequest.IsInitialized()) + std::string strAddress = rcp.address.toStdString(); + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + std::string strLabel = rcp.label.toStdString(); { - std::string strAddress = rcp.address.toStdString(); - CTxDestination dest = CBitcoinAddress(strAddress).Get(); - std::string strLabel = rcp.label.toStdString(); + LOCK(wallet->cs_wallet); + + std::map::iterator mi = wallet->mapAddressBook.find(dest); + + // Check if we have a new address or an updated label + if (mi == wallet->mapAddressBook.end()) + { + wallet->SetAddressBook(dest, strLabel, "send"); + } + else if (mi->second.name != strLabel) { - LOCK(wallet->cs_wallet); - - std::map::iterator mi = wallet->mapAddressBook.find(dest); - - // Check if we have a new address or an updated label - if (mi == wallet->mapAddressBook.end()) - { - wallet->SetAddressBook(dest, strLabel, "send"); - } - else if (mi->second.name != strLabel) - { - wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose - } + wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose } } Q_EMIT coinsSent(wallet, rcp, transaction_array); @@ -528,7 +488,7 @@ void WalletModel::unsubscribeFromCoreSignals() // Disconnect signals from wallet wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, boost::placeholders::_1)); - wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, + wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, boost::placeholders::_1, boost::placeholders::_2, boost::placeholders::_3, diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index cd7585635fb..cfb03afffa8 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -5,9 +5,9 @@ #ifndef BITCOIN_QT_WALLETMODEL_H #define BITCOIN_QT_WALLETMODEL_H -#include "paymentrequestplus.h" #include "walletmodeltransaction.h" +#include "amount.h" #include "support/allocators/secure.h" #include @@ -23,6 +23,7 @@ class TransactionTableModel; class WalletModelTransaction; class CCoinControl; +class CKey; class CKeyID; class COutPoint; class COutput; @@ -52,11 +53,6 @@ class SendCoinsRecipient // If from a payment request, this is used for storing the memo QString message; - // If from a payment request, paymentRequest.IsInitialized() will be true - PaymentRequestPlus paymentRequest; - // Empty if no authentication or invalid signature/cert/etc. - QString authenticatedMerchant; - bool fSubtractFeeFromAmount; // memory only static const int CURRENT_VERSION = 1; @@ -69,10 +65,8 @@ class SendCoinsRecipient std::string sAddress = address.toStdString(); std::string sLabel = label.toStdString(); std::string sMessage = message.toStdString(); - std::string sPaymentRequest; - if (!ser_action.ForRead() && paymentRequest.IsInitialized()) - paymentRequest.SerializeToString(&sPaymentRequest); - std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString(); + std::string sPaymentRequest = ""; + std::string sAuthenticatedMerchant = ""; READWRITE(this->nVersion); READWRITE(sAddress); @@ -87,9 +81,6 @@ class SendCoinsRecipient address = QString::fromStdString(sAddress); label = QString::fromStdString(sLabel); message = QString::fromStdString(sMessage); - if (!sPaymentRequest.empty()) - paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size())); - authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant); } } }; @@ -113,8 +104,7 @@ class WalletModel : public QObject DuplicateAddress, TransactionCreationFailed, // Error returned when wallet is still locked TransactionCommitFailed, - AbsurdFee, - PaymentRequestExpired + AbsurdFee }; enum EncryptionStatus diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index b4445c8166a..3d397318aa2 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -54,28 +54,10 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) { SendCoinsRecipient& rcp = (*it); - if (rcp.paymentRequest.IsInitialized()) - { - CAmount subtotal = 0; - const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); - for (int j = 0; j < details.outputs_size(); j++) - { - const payments::Output& out = details.outputs(j); - if (out.amount() <= 0) continue; - if (i == nChangePosRet) - i++; - subtotal += walletTransaction->tx->vout[i].nValue; - i++; - } - rcp.amount = subtotal; - } - else // normal recipient (no payment request) - { - if (i == nChangePosRet) - i++; - rcp.amount = walletTransaction->tx->vout[i].nValue; + if (i == nChangePosRet) i++; - } + rcp.amount = walletTransaction->tx->vout[i].nValue; + i++; } } diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 64922efada5..5ebbd6034eb 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -7,6 +7,8 @@ #include "walletmodel.h" +#include "amount.h" + #include class SendCoinsRecipient; From 8fadf22219a80615b347d8b66c91cfc73a161120 Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Sat, 2 Mar 2024 16:21:25 -0500 Subject: [PATCH 02/60] build: remove protobuf requirement Protobuf was only needed for BIP70 payment requests --- configure.ac | 7 ------- 1 file changed, 7 deletions(-) diff --git a/configure.ac b/configure.ac index e7ab4a2e68e..67669bdc863 100644 --- a/configure.ac +++ b/configure.ac @@ -220,8 +220,6 @@ AC_ARG_WITH([armv82-crypto], [armv82_crypto=$withval], [armv82_crypto=no]) -AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], []) - AC_ARG_ENABLE(man, [AS_HELP_STRING([--disable-man], [do not install man pages (default is to install)])],, @@ -806,7 +804,6 @@ if test x$use_pkgconfig = xyes; then [ PKG_CHECK_MODULES([SSL], [libssl],, [AC_MSG_ERROR(openssl not found.)]) PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) - BITCOIN_QT_CHECK([PKG_CHECK_MODULES([PROTOBUF], [protobuf], [have_protobuf=yes], [BITCOIN_QT_FAIL(libprotobuf not found)])]) if test x$use_qr != xno; then BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])]) fi @@ -866,7 +863,6 @@ else esac fi - BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found))) if test x$use_qr != xno; then BITCOIN_QT_CHECK([AC_CHECK_LIB([qrencode], [main],[QR_LIBS=-lqrencode], [have_qrencode=no])]) BITCOIN_QT_CHECK([AC_CHECK_HEADER([qrencode.h],, have_qrencode=no)]) @@ -971,8 +967,6 @@ AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes]) AC_SUBST(UNIVALUE_CFLAGS) AC_SUBST(UNIVALUE_LIBS) -BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path) - AC_MSG_CHECKING([whether to build dogecoind]) AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) AC_MSG_RESULT($build_bitcoind) @@ -1172,7 +1166,6 @@ AC_SUBST(SSL_LIBS) AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) -AC_SUBST(PROTOBUF_LIBS) AC_SUBST(QR_LIBS) AC_SUBST(HAVE_FDATASYNC) AC_SUBST(HAVE_FULLFSYNC) From 8f96a38681c53afc1cd7cce47cc2302d528c2a7b Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Mon, 1 Apr 2024 08:32:11 -0400 Subject: [PATCH 03/60] [squashme] remove -enable-bip70 flag from init.cpp --- src/init.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 760d5ef97dc..1afbeee511d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -373,7 +373,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)")); strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP)); strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect)")); - strUsage += HelpMessageOpt("-enable-bip70", _("Enable BIP-70 PaymentServer (default: 0)")); strUsage += HelpMessageOpt("-externalip=", _("Specify your own public address")); strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED)); strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect)")); From c43d021557be099fdb7b1188651ca129149d084c Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 16 Aug 2017 13:56:02 -0400 Subject: [PATCH 04/60] Fix -Wthread-safety-analysis warnings. Change the sync.h primitives to std from boost. Commit 1. This code was written by @TheBlueMatt in the following branch: * https://github.com/TheBlueMatt/bitcoin/commits/2017-08-test-10923 This commit message was written by me (@practicalswift) who also squashed @TheBlueMatt's commits into one and tried to summarize the changes made. Commit 2. Remove boost include. Remove boost mentions in comments. Cherry-picked from: 7e319d63932c40730ee66110cd8edd14a312f297 --- src/init.cpp | 6 +++--- src/rpc/mining.cpp | 10 +++++----- src/sync.h | 46 ++++++++++++++++++++++++---------------------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 1afbeee511d..3527e4054b5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -546,14 +546,14 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex } static bool fHaveGenesis = false; -static boost::mutex cs_GenesisWait; +static CWaitableCriticalSection cs_GenesisWait; static CConditionVariable condvar_GenesisWait; static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) { if (pBlockIndex != NULL) { { - boost::unique_lock lock_GenesisWait(cs_GenesisWait); + WaitableLock lock_GenesisWait(cs_GenesisWait); fHaveGenesis = true; } condvar_GenesisWait.notify_all(); @@ -1661,7 +1661,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Wait for genesis block to be processed { - boost::unique_lock lock(cs_GenesisWait); + WaitableLock lock(cs_GenesisWait); while (!fHaveGenesis) { condvar_GenesisWait.wait(lock); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9e231baaf63..8dc435b6ec7 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -514,7 +514,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) { // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions uint256 hashWatchedChain; - boost::system_time checktxtime; + std::chrono::steady_clock::time_point checktxtime; unsigned int nTransactionsUpdatedLastLP; if (lpval.isStr()) @@ -535,17 +535,17 @@ UniValue getblocktemplate(const JSONRPCRequest& request) // Release the wallet and main lock while waiting LEAVE_CRITICAL_SECTION(cs_main); { - checktxtime = boost::get_system_time() + boost::posix_time::minutes(1); + checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - boost::unique_lock lock(csBestBlock); + WaitableLock lock(csBestBlock); while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) { - if (!cvBlockChange.timed_wait(lock, checktxtime)) + if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) { // Timeout: Check transactions for update if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) break; - checktxtime += boost::posix_time::seconds(10); + checktxtime += std::chrono::seconds(10); } } } diff --git a/src/sync.h b/src/sync.h index 3b29050e0e1..728ec4d22c0 100644 --- a/src/sync.h +++ b/src/sync.h @@ -11,7 +11,9 @@ #include #include #include -#include +#include +#include +#include //////////////////////////////////////////////// @@ -22,17 +24,17 @@ /* CCriticalSection mutex; - boost::recursive_mutex mutex; + std::recursive_mutex mutex; LOCK(mutex); - boost::unique_lock criticalblock(mutex); + std::unique_lock criticalblock(mutex); LOCK2(mutex1, mutex2); - boost::unique_lock criticalblock1(mutex1); - boost::unique_lock criticalblock2(mutex2); + std::unique_lock criticalblock1(mutex1); + std::unique_lock criticalblock2(mutex2); TRY_LOCK(mutex, name); - boost::unique_lock name(mutex, boost::try_to_lock_t); + std::unique_lock name(mutex, std::try_to_lock_t); ENTER_CRITICAL_SECTION(mutex); // no RAII mutex.lock(); @@ -86,10 +88,10 @@ void static inline DeleteLock(void* cs) {} #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) /** - * Wrapped boost mutex: supports recursive locking, but no waiting + * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. */ -class CCriticalSection : public AnnotatedMixin +class CCriticalSection : public AnnotatedMixin { public: ~CCriticalSection() { @@ -98,22 +100,24 @@ class CCriticalSection : public AnnotatedMixin }; typedef CCriticalSection CDynamicCriticalSection; -/** Wrapped boost mutex: supports waiting but not recursive locking */ -typedef AnnotatedMixin CWaitableCriticalSection; +/** Wrapped mutex: supports waiting but not recursive locking */ +typedef AnnotatedMixin CWaitableCriticalSection; -/** Just a typedef for boost::condition_variable, can be wrapped later if desired */ -typedef boost::condition_variable CConditionVariable; +/** Just a typedef for std::condition_variable, can be wrapped later if desired */ +typedef std::condition_variable CConditionVariable; + +/** Just a typedef for std::unique_lock, can be wrapped later if desired */ +typedef std::unique_lock WaitableLock; #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine); #endif -/** Wrapper around boost::unique_lock */ -template -class SCOPED_LOCKABLE CMutexLock +/** Wrapper around std::unique_lock */ +class SCOPED_LOCKABLE CCriticalBlock { private: - boost::unique_lock lock; + std::unique_lock lock; void Enter(const char* pszName, const char* pszFile, int nLine) { @@ -138,7 +142,7 @@ class SCOPED_LOCKABLE CMutexLock } public: - CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, boost::defer_lock) + CCriticalBlock(CCriticalSection& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, std::defer_lock) { if (fTry) TryEnter(pszName, pszFile, nLine); @@ -146,18 +150,18 @@ class SCOPED_LOCKABLE CMutexLock Enter(pszName, pszFile, nLine); } - CMutexLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) + CCriticalBlock(CCriticalSection* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) { if (!pmutexIn) return; - lock = boost::unique_lock(*pmutexIn, boost::defer_lock); + lock = std::unique_lock(*pmutexIn, std::defer_lock); if (fTry) TryEnter(pszName, pszFile, nLine); else Enter(pszName, pszFile, nLine); } - ~CMutexLock() UNLOCK_FUNCTION() + ~CCriticalBlock() UNLOCK_FUNCTION() { if (lock.owns_lock()) LeaveCritical(); @@ -169,8 +173,6 @@ class SCOPED_LOCKABLE CMutexLock } }; -typedef CMutexLock CCriticalBlock; - #define PASTE(x, y) x ## y #define PASTE2(x, y) PASTE(x, y) From 506cc94241ca44974a0e8016b26368560aea4815 Mon Sep 17 00:00:00 2001 From: Thomas Snider Date: Sat, 18 Nov 2017 11:35:14 -0800 Subject: [PATCH 05/60] Switched sync.{cpp,h} to std threading primitives. Cherry-picked from: bba9bd0d9dd06f13a6b0c89181864453cab5c858 --- src/sync.cpp | 20 +++++++++----------- src/sync.h | 17 ++++++----------- src/util.h | 2 +- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/sync.cpp b/src/sync.cpp index fce57f1df9f..5d3a09a2e81 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -4,13 +4,13 @@ #include "sync.h" +#include #include "util.h" #include "utilstrencodings.h" #include #include -#include #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine) @@ -46,10 +46,8 @@ struct CLockLocation { return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : ""); } - std::string MutexName() const { return mutexName; } - - bool fTry; private: + bool fTry; std::string mutexName; std::string sourceFile; int sourceLine; @@ -70,10 +68,10 @@ struct LockData { LockOrders lockorders; InvLockOrders invlockorders; - boost::mutex dd_mutex; + std::mutex dd_mutex; } static lockdata; -boost::thread_specific_ptr lockstack; +static thread_local std::unique_ptr lockstack; static void potential_deadlock_detected(const std::pair& mismatch, const LockStack& s1, const LockStack& s2) { @@ -103,12 +101,12 @@ static void potential_deadlock_detected(const std::pair& mismatch, static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) { - if (lockstack.get() == NULL) + if (!lockstack) lockstack.reset(new LockStack); - boost::unique_lock lock(lockdata.dd_mutex); + std::lock_guard lock(lockdata.dd_mutex); - (*lockstack).push_back(std::make_pair(c, locklocation)); + lockstack->push_back(std::make_pair(c, locklocation)); BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, (*lockstack)) { if (i.first == c) @@ -164,8 +162,8 @@ void DeleteLock(void* cs) // We're already shutting down. return; } - boost::unique_lock lock(lockdata.dd_mutex); - std::pair item = std::make_pair(cs, (void*)0); + std::lock_guard lock(lockdata.dd_mutex); + std::pair item = std::make_pair(cs, nullptr); LockOrders::iterator it = lockdata.lockorders.lower_bound(item); while (it != lockdata.lockorders.end() && it->first.first == cs) { std::pair invitem = std::make_pair(it->first.second, it->first.first); diff --git a/src/sync.h b/src/sync.h index 728ec4d22c0..59b19df8d44 100644 --- a/src/sync.h +++ b/src/sync.h @@ -8,9 +8,6 @@ #include "threadsafety.h" -#include -#include -#include #include #include #include @@ -195,8 +192,8 @@ class SCOPED_LOCKABLE CCriticalBlock class CSemaphore { private: - boost::condition_variable condition; - boost::mutex mutex; + std::condition_variable condition; + std::mutex mutex; int value; public: @@ -204,16 +201,14 @@ class CSemaphore void wait() { - boost::unique_lock lock(mutex); - while (value < 1) { - condition.wait(lock); - } + std::unique_lock lock(mutex); + condition.wait(lock, [&]() { return value >= 1; }); value--; } bool try_wait() { - boost::unique_lock lock(mutex); + std::lock_guard lock(mutex); if (value < 1) return false; value--; @@ -223,7 +218,7 @@ class CSemaphore void post() { { - boost::unique_lock lock(mutex); + std::lock_guard lock(mutex); value++; } condition.notify_one(); diff --git a/src/util.h b/src/util.h index 2bebf2b2257..09cee75df24 100644 --- a/src/util.h +++ b/src/util.h @@ -28,7 +28,7 @@ #include #include -#include +#include // for boost::thread_interrupted // Application startup time (used for uptime calculation) int64_t GetStartupTime(); From 6e862f8b51bdc4f59cf6af17785113337caa8fd9 Mon Sep 17 00:00:00 2001 From: Dakoda Greaves Date: Tue, 16 Apr 2024 13:13:21 -0700 Subject: [PATCH 06/60] sync: remove boost foreach --- src/sync.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sync.cpp b/src/sync.cpp index 5d3a09a2e81..82a777dcd80 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -10,8 +10,6 @@ #include -#include - #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine) { @@ -77,7 +75,7 @@ static void potential_deadlock_detected(const std::pair& mismatch, { LogPrintf("POTENTIAL DEADLOCK DETECTED\n"); LogPrintf("Previous lock order was:\n"); - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, s2) { + for (const std::pair& i : s2) { if (i.first == mismatch.first) { LogPrintf(" (1)"); } @@ -87,7 +85,7 @@ static void potential_deadlock_detected(const std::pair& mismatch, LogPrintf(" %s\n", i.second.ToString()); } LogPrintf("Current lock order is:\n"); - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, s1) { + for (const std::pair& i : s1) { if (i.first == mismatch.first) { LogPrintf(" (1)"); } @@ -108,7 +106,7 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) lockstack->push_back(std::make_pair(c, locklocation)); - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, (*lockstack)) { + for (const std::pair& i : (*lockstack)) { if (i.first == c) break; @@ -142,14 +140,14 @@ void LeaveCritical() std::string LocksHeld() { std::string result; - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, *lockstack) + for (const std::pair& i : *lockstack) result += i.second.ToString() + std::string("\n"); return result; } void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) { - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, *lockstack) + for (const std::pair& i : *lockstack) if (i.first == cs) return; fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); From 995eda15233f1b7a4a8865c57b03e55f7f7851b2 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Mon, 27 Nov 2017 17:08:27 -0500 Subject: [PATCH 07/60] threads: add a thread_local autoconf check Cherry-picked from: f7f7e2cd340c088e82d09090eb275b98b34a9812 --- configure.ac | 23 +++++++++++++++++++++++ src/sync.cpp | 3 +++ 2 files changed, 26 insertions(+) diff --git a/configure.ac b/configure.ac index 67669bdc863..366cd1379f6 100644 --- a/configure.ac +++ b/configure.ac @@ -634,6 +634,29 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([ ] ) +TEMP_LDFLAGS="$LDFLAGS" +LDFLAGS="$TEMP_LDFLAGS $PTHREAD_CFLAGS" +AC_MSG_CHECKING([for thread_local support]) +AC_LINK_IFELSE([AC_LANG_SOURCE([ + #include + static thread_local int foo = 0; + static void run_thread() { foo++;} + int main(){ + for(int i = 0; i < 10; i++) { std::thread(run_thread).detach();} + return foo; + } + ])], + [ + AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.]) + AC_MSG_RESULT(yes) + ], + [ + AC_MSG_RESULT(no) + ] +) +LDFLAGS="$TEMP_LDFLAGS" + +# Check for reduced exports if test x$use_reduce_exports = xyes; then AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], [AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])]) diff --git a/src/sync.cpp b/src/sync.cpp index 82a777dcd80..0c21e698d77 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -11,6 +11,9 @@ #include #ifdef DEBUG_LOCKCONTENTION +#if !defined(HAVE_THREAD_LOCAL) +static_assert(false, "thread_local is not supported"); +#endif void PrintLockContention(const char* pszName, const char* pszFile, int nLine) { LogPrintf("LOCKCONTENTION: %s\n", pszName); From 29dd4a09567a5c4b19f2652845273316e47ca194 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Wed, 8 Nov 2017 15:28:35 -0500 Subject: [PATCH 08/60] Add unit test for DEBUG_LOCKORDER code Cherry-picked from: 41b88e93375d57db12da923f45f87b9a2db8e730 --- src/Makefile.test.include | 1 + src/sync.cpp | 8 +++++++- src/sync.h | 7 +++++++ src/test/sync_tests.cpp | 41 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/test/sync_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index d0dc3af6f79..9facc0d3b07 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -132,6 +132,7 @@ BITCOIN_TESTS =\ test/test_random.h \ test/testutil.cpp \ test/testutil.h \ + test/sync_tests.cpp \ test/timedata_tests.cpp \ test/transaction_tests.cpp \ test/txvalidationcache_tests.cpp \ diff --git a/src/sync.cpp b/src/sync.cpp index 0c21e698d77..191923a637d 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -97,7 +97,11 @@ static void potential_deadlock_detected(const std::pair& mismatch, } LogPrintf(" %s\n", i.second.ToString()); } - assert(false); + if (g_debug_lockorder_abort) { + fprintf(stderr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__); + abort(); + } + throw std::logic_error("potential deadlock detected"); } static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) @@ -179,4 +183,6 @@ void DeleteLock(void* cs) } } +bool g_debug_lockorder_abort = true; + #endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h index 59b19df8d44..4ab375a8dce 100644 --- a/src/sync.h +++ b/src/sync.h @@ -76,6 +76,13 @@ void LeaveCritical(); std::string LocksHeld(); void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); void DeleteLock(void* cs); + +/** + * Call abort() if a potential lock order deadlock bug is detected, instead of + * just logging information and throwing a logic_error. Defaults to true, and + * set to false in DEBUG_LOCKORDER unit tests. + */ +extern bool g_debug_lockorder_abort; #else void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} void static inline LeaveCritical() {} diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp new file mode 100644 index 00000000000..2e0951c3a95 --- /dev/null +++ b/src/test/sync_tests.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2012-2017 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 +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(potential_deadlock_detected) +{ + #ifdef DEBUG_LOCKORDER + bool prev = g_debug_lockorder_abort; + g_debug_lockorder_abort = false; + #endif + + CCriticalSection mutex1, mutex2; + { + LOCK2(mutex1, mutex2); + } + bool error_thrown = false; + try { + LOCK2(mutex2, mutex1); + } catch (const std::logic_error& e) { + BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected"); + error_thrown = true; + } + #ifdef DEBUG_LOCKORDER + BOOST_CHECK(error_thrown); + #else + BOOST_CHECK(!error_thrown); + #endif + + #ifdef DEBUG_LOCKORDER + g_debug_lockorder_abort = prev; + #endif +} + +BOOST_AUTO_TEST_SUITE_END() From aa606a3491bef0937b41d4456d9c3fe98e141048 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 7 Dec 2017 12:01:53 -0500 Subject: [PATCH 09/60] MOVEONLY Move AnnotatedMixin declaration Move AnnotatedMixin closer to where it's used, and after the DEBUG_LOCKORDER function declarations so it can call them. Cherry-picked from: ba1f095aadf29bddb0bd8176d2e0b908f92a5623 --- src/sync.h | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/sync.h b/src/sync.h index 4ab375a8dce..0816f63935c 100644 --- a/src/sync.h +++ b/src/sync.h @@ -46,6 +46,27 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII // // /////////////////////////////// +#ifdef DEBUG_LOCKORDER +void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); +void LeaveCritical(); +std::string LocksHeld(); +void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); +void DeleteLock(void* cs); + +/** + * Call abort() if a potential lock order deadlock bug is detected, instead of + * just logging information and throwing a logic_error. Defaults to true, and + * set to false in DEBUG_LOCKORDER unit tests. + */ +extern bool g_debug_lockorder_abort; +#else +void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} +void static inline LeaveCritical() {} +void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} +void static inline DeleteLock(void* cs) {} +#endif +#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) + /** * Template mixin that adds -Wthread-safety locking * annotations to a subset of the mutex API. @@ -70,27 +91,6 @@ class LOCKABLE AnnotatedMixin : public PARENT } }; -#ifdef DEBUG_LOCKORDER -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); -void LeaveCritical(); -std::string LocksHeld(); -void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); -void DeleteLock(void* cs); - -/** - * Call abort() if a potential lock order deadlock bug is detected, instead of - * just logging information and throwing a logic_error. Defaults to true, and - * set to false in DEBUG_LOCKORDER unit tests. - */ -extern bool g_debug_lockorder_abort; -#else -void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} -void static inline LeaveCritical() {} -void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} -void static inline DeleteLock(void* cs) {} -#endif -#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) - /** * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. From 79afc95febe07fda7f68eaccd59c10fd821a6233 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Wed, 8 Nov 2017 16:21:13 -0500 Subject: [PATCH 10/60] Make LOCK, LOCK2, TRY_LOCK work with CWaitableCriticalSection They should also work with any other mutex type which std::unique_lock supports. There is no change in behavior for current code that calls these macros with CCriticalSection mutexes. Cherry-picked from: 1382913e61f5db6ba849b1e261e8aefcd5a1ae68 --- src/sync.h | 61 ++++++++++++++++++++++------------------- src/test/sync_tests.cpp | 29 ++++++++++++++------ 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/sync.h b/src/sync.h index 0816f63935c..4deb2ea0759 100644 --- a/src/sync.h +++ b/src/sync.h @@ -68,13 +68,17 @@ void static inline DeleteLock(void* cs) {} #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) /** - * Template mixin that adds -Wthread-safety locking - * annotations to a subset of the mutex API. + * Template mixin that adds -Wthread-safety locking annotations and lock order + * checking to a subset of the mutex API. */ template class LOCKABLE AnnotatedMixin : public PARENT { public: + ~AnnotatedMixin() { + DeleteLock((void*)this); + } + void lock() EXCLUSIVE_LOCK_FUNCTION() { PARENT::lock(); @@ -89,19 +93,15 @@ class LOCKABLE AnnotatedMixin : public PARENT { return PARENT::try_lock(); } + + using UniqueLock = std::unique_lock; }; /** * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. */ -class CCriticalSection : public AnnotatedMixin -{ -public: - ~CCriticalSection() { - DeleteLock((void*)this); - } -}; +typedef AnnotatedMixin CCriticalSection; typedef CCriticalSection CDynamicCriticalSection; /** Wrapped mutex: supports waiting but not recursive locking */ @@ -117,20 +117,19 @@ typedef std::unique_lock WaitableLock; void PrintLockContention(const char* pszName, const char* pszFile, int nLine); #endif -/** Wrapper around std::unique_lock */ -class SCOPED_LOCKABLE CCriticalBlock +/** Wrapper around std::unique_lock style lock for Mutex. */ +template +class SCOPED_LOCKABLE CCriticalBlock : public Base { private: - std::unique_lock lock; - void Enter(const char* pszName, const char* pszFile, int nLine) { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); + EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex())); #ifdef DEBUG_LOCKCONTENTION - if (!lock.try_lock()) { + if (!Base::try_lock()) { PrintLockContention(pszName, pszFile, nLine); #endif - lock.lock(); + Base::lock(); #ifdef DEBUG_LOCKCONTENTION } #endif @@ -138,15 +137,15 @@ class SCOPED_LOCKABLE CCriticalBlock bool TryEnter(const char* pszName, const char* pszFile, int nLine) { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); - lock.try_lock(); - if (!lock.owns_lock()) + EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()), true); + Base::try_lock(); + if (!Base::owns_lock()) LeaveCritical(); - return lock.owns_lock(); + return Base::owns_lock(); } public: - CCriticalBlock(CCriticalSection& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, std::defer_lock) + CCriticalBlock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock) { if (fTry) TryEnter(pszName, pszFile, nLine); @@ -154,11 +153,11 @@ class SCOPED_LOCKABLE CCriticalBlock Enter(pszName, pszFile, nLine); } - CCriticalBlock(CCriticalSection* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) + CCriticalBlock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) { if (!pmutexIn) return; - lock = std::unique_lock(*pmutexIn, std::defer_lock); + *static_cast(this) = Base(*pmutexIn, std::defer_lock); if (fTry) TryEnter(pszName, pszFile, nLine); else @@ -167,22 +166,28 @@ class SCOPED_LOCKABLE CCriticalBlock ~CCriticalBlock() UNLOCK_FUNCTION() { - if (lock.owns_lock()) + if (Base::owns_lock()) LeaveCritical(); } operator bool() { - return lock.owns_lock(); + return Base::owns_lock(); } }; +template +using DebugLock = CCriticalBlock::type>::type>; + #define PASTE(x, y) x ## y #define PASTE2(x, y) PASTE(x, y) -#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) -#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) -#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) +#define LOCK(cs) DebugLock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) +#define LOCK2(cs1, cs2) \ + DebugLock criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ + DebugLock criticalblock2(cs2, #cs2, __FILE__, __LINE__); +#define TRY_LOCK(cs, name) DebugLock name(cs, #cs, __FILE__, __LINE__, true) +#define WAIT_LOCK(cs, name) DebugLock name(cs, #cs, __FILE__, __LINE__) #define ENTER_CRITICAL_SECTION(cs) \ { \ diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp index 2e0951c3a95..539e2ff3ab4 100644 --- a/src/test/sync_tests.cpp +++ b/src/test/sync_tests.cpp @@ -7,16 +7,10 @@ #include -BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup) - -BOOST_AUTO_TEST_CASE(potential_deadlock_detected) +namespace { +template +void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2) { - #ifdef DEBUG_LOCKORDER - bool prev = g_debug_lockorder_abort; - g_debug_lockorder_abort = false; - #endif - - CCriticalSection mutex1, mutex2; { LOCK2(mutex1, mutex2); } @@ -32,6 +26,23 @@ BOOST_AUTO_TEST_CASE(potential_deadlock_detected) #else BOOST_CHECK(!error_thrown); #endif +} +} // namespace + +BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(potential_deadlock_detected) +{ + #ifdef DEBUG_LOCKORDER + bool prev = g_debug_lockorder_abort; + g_debug_lockorder_abort = false; + #endif + + CCriticalSection rmutex1, rmutex2; + TestPotentialDeadLockDetected(rmutex1, rmutex2); + + CWaitableCriticalSection mutex1, mutex2; + TestPotentialDeadLockDetected(mutex1, mutex2); #ifdef DEBUG_LOCKORDER g_debug_lockorder_abort = prev; From 7a522921f177109fe7a65af89ec353f7e230e575 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Wed, 8 Nov 2017 17:07:40 -0500 Subject: [PATCH 11/60] Use LOCK macros for non-recursive locks Instead of std::unique_lock. Cherry-picked from: 9c4dc597ddc66acfd58a945a5ab11f833731abba --- src/httpserver.cpp | 8 ++++---- src/init.cpp | 11 +++++++---- src/net.cpp | 4 ++-- src/net.h | 2 +- src/random.cpp | 5 +++-- src/rpc/blockchain.cpp | 8 ++++---- src/rpc/mining.cpp | 2 +- src/sync.h | 3 --- src/threadinterrupt.cpp | 5 +++-- src/threadinterrupt.h | 4 +++- 10 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 07db1cbf4c6..b50a3cf7fe1 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -68,7 +68,7 @@ class WorkQueue { private: /** Mutex protects entire object */ - std::mutex cs; + CWaitableCriticalSection cs; std::condition_variable cond; std::deque> queue; bool running; @@ -108,7 +108,7 @@ class WorkQueue /** Enqueue a work item */ bool Enqueue(WorkItem* item) { - std::unique_lock lock(cs); + LOCK(cs); if (queue.size() >= maxDepth) { return false; } @@ -123,7 +123,7 @@ class WorkQueue while (true) { std::unique_ptr i; { - std::unique_lock lock(cs); + WAIT_LOCK(cs, lock); while (running && queue.empty()) cond.wait(lock); if (!running) @@ -137,7 +137,7 @@ class WorkQueue /** Interrupt and exit loops */ void Interrupt() { - std::unique_lock lock(cs); + LOCK(cs); running = false; cond.notify_all(); } diff --git a/src/init.cpp b/src/init.cpp index 3527e4054b5..a30ef1bb4f6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -553,7 +553,7 @@ static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) { if (pBlockIndex != NULL) { { - WaitableLock lock_GenesisWait(cs_GenesisWait); + LOCK(cs_GenesisWait); fHaveGenesis = true; } condvar_GenesisWait.notify_all(); @@ -1661,9 +1661,12 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Wait for genesis block to be processed { - WaitableLock lock(cs_GenesisWait); - while (!fHaveGenesis) { - condvar_GenesisWait.wait(lock); + WAIT_LOCK(cs_GenesisWait, lock); + // We previously could hang here if StartShutdown() is called prior to + // ThreadImport getting started, so instead we just wait on a timer to + // check ShutdownRequested() regularly. + while (!fHaveGenesis && !ShutdownRequested()) { + condvar_GenesisWait.wait_for(lock, std::chrono::milliseconds(500)); } uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); } diff --git a/src/net.cpp b/src/net.cpp index 70f303a0de7..fa4ea79a8cd 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2051,7 +2051,7 @@ void CConnman::ThreadMessageHandler() pnode->Release(); } - std::unique_lock lock(mutexMsgProc); + WAIT_LOCK(mutexMsgProc, lock); if (!fMoreWork) { condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; }); } @@ -2334,7 +2334,7 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c flagInterruptMsgProc = false; { - std::unique_lock lock(mutexMsgProc); + LOCK(mutexMsgProc); fMsgProcWake = false; } diff --git a/src/net.h b/src/net.h index e7ad3892551..8242122742d 100644 --- a/src/net.h +++ b/src/net.h @@ -399,7 +399,7 @@ class CConnman bool fMsgProcWake; std::condition_variable condMsgProc; - std::mutex mutexMsgProc; + CWaitableCriticalSection mutexMsgProc; std::atomic flagInterruptMsgProc; CThreadInterrupt interruptNet; diff --git a/src/random.cpp b/src/random.cpp index 6634019bea8..5bb7d9c3120 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -11,8 +11,9 @@ #include "compat.h" // for Windows API #include #endif -#include "util.h" // for LogPrint() -#include "utilstrencodings.h" // for GetTime() +#include "util.h" // for LogPrint() +#include "sync.h" // for LOCK +#include "utiltime.h" // for GetTime() #include #include diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 04ad5b782a6..2d7093f424d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -43,7 +43,7 @@ struct CUpdatedBlock int height; }; -static std::mutex cs_blockchange; +static CWaitableCriticalSection cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; @@ -260,7 +260,7 @@ UniValue waitfornewblock(const JSONRPCRequest& request) CUpdatedBlock block; { - std::unique_lock lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); block = latestblock; if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); @@ -302,7 +302,7 @@ UniValue waitforblock(const JSONRPCRequest& request) CUpdatedBlock block; { - std::unique_lock lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();}); else @@ -345,7 +345,7 @@ UniValue waitforblockheight(const JSONRPCRequest& request) CUpdatedBlock block; { - std::unique_lock lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();}); else diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 8dc435b6ec7..58795317e58 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -537,7 +537,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) { checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - WaitableLock lock(csBestBlock); + WAIT_LOCK(csBestBlock, lock); while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) { if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) diff --git a/src/sync.h b/src/sync.h index 4deb2ea0759..bc8c2c1451e 100644 --- a/src/sync.h +++ b/src/sync.h @@ -110,9 +110,6 @@ typedef AnnotatedMixin CWaitableCriticalSection; /** Just a typedef for std::condition_variable, can be wrapped later if desired */ typedef std::condition_variable CConditionVariable; -/** Just a typedef for std::unique_lock, can be wrapped later if desired */ -typedef std::unique_lock WaitableLock; - #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine); #endif diff --git a/src/threadinterrupt.cpp b/src/threadinterrupt.cpp index 9d691079edb..fe65be6ad00 100644 --- a/src/threadinterrupt.cpp +++ b/src/threadinterrupt.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "threadinterrupt.h" +#include "sync.h" CThreadInterrupt::operator bool() const { @@ -18,7 +19,7 @@ void CThreadInterrupt::reset() void CThreadInterrupt::operator()() { { - std::unique_lock lock(mut); + LOCK(mut); flag.store(true, std::memory_order_release); } cond.notify_all(); @@ -26,7 +27,7 @@ void CThreadInterrupt::operator()() bool CThreadInterrupt::sleep_for(std::chrono::milliseconds rel_time) { - std::unique_lock lock(mut); + WAIT_LOCK(mut, lock); return !cond.wait_for(lock, rel_time, [this]() { return flag.load(std::memory_order_acquire); }); } diff --git a/src/threadinterrupt.h b/src/threadinterrupt.h index 54e31028089..7f0dc09f66f 100644 --- a/src/threadinterrupt.h +++ b/src/threadinterrupt.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_THREADINTERRUPT_H #define BITCOIN_THREADINTERRUPT_H +#include + #include #include #include @@ -27,7 +29,7 @@ class CThreadInterrupt private: std::condition_variable cond; - std::mutex mut; + CWaitableCriticalSection mut; std::atomic flag; }; From fc90d70691851e1e6fa599bd3e97b294da14573e Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Fri, 3 Nov 2017 07:49:16 -0400 Subject: [PATCH 12/60] scripted-diff: Small locking rename Call sync.h primitives "locks" and "mutexes" instead of "blocks" and "waitable critical sections" to match current coding conventions and c++11 standard names. This PR does not rename the "CCriticalSection" class (though this could be done as a followup) because it is used everywhere and would swamp the other changes in this PR. Plain mutexes should mostly be preferred instead of recursive mutexes in new code anyway. -BEGIN VERIFY SCRIPT- set -x set -e ren() { git grep -l $1 | xargs sed -i s/$1/$2/; } ren CCriticalBlock UniqueLock ren CWaitableCriticalSection Mutex ren CConditionVariable std::condition_variable ren cs_GenesisWait g_genesis_wait_mutex ren condvar_GenesisWait g_genesis_wait_cv perl -0777 -pi -e 's/.*typedef.*condition_variable.*\n\n?//g' src/sync.h -END VERIFY SCRIPT- Cherry-picked from: 190bf62be1214b072513c7fd7e01cc191723967c --- src/httpserver.cpp | 2 +- src/init.cpp | 14 +++++++------- src/net.h | 2 +- src/rpc/blockchain.cpp | 2 +- src/rpc/mining.cpp | 4 ++-- src/sync.h | 15 ++++++--------- src/test/sync_tests.cpp | 2 +- src/threadinterrupt.h | 2 +- src/validation.cpp | 6 +++--- src/validation.h | 5 +++-- 10 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index b50a3cf7fe1..6548d817a33 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -68,7 +68,7 @@ class WorkQueue { private: /** Mutex protects entire object */ - CWaitableCriticalSection cs; + Mutex cs; std::condition_variable cond; std::deque> queue; bool running; diff --git a/src/init.cpp b/src/init.cpp index a30ef1bb4f6..01ffb92f1b9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -305,7 +305,7 @@ void OnRPCStopped() { uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange); RPCNotifyBlockChange(false, nullptr); - cvBlockChange.notify_all(); + g_best_block_cv.notify_all(); LogPrint("rpc", "RPC stopped.\n"); } @@ -546,17 +546,17 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex } static bool fHaveGenesis = false; -static CWaitableCriticalSection cs_GenesisWait; -static CConditionVariable condvar_GenesisWait; +static Mutex g_genesis_wait_mutex; +static std::condition_variable g_genesis_wait_cv; static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) { if (pBlockIndex != NULL) { { - LOCK(cs_GenesisWait); + LOCK(g_genesis_wait_mutex); fHaveGenesis = true; } - condvar_GenesisWait.notify_all(); + g_genesis_wait_cv.notify_all(); } } @@ -1661,12 +1661,12 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Wait for genesis block to be processed { - WAIT_LOCK(cs_GenesisWait, lock); + WAIT_LOCK(g_genesis_wait_mutex, lock); // We previously could hang here if StartShutdown() is called prior to // ThreadImport getting started, so instead we just wait on a timer to // check ShutdownRequested() regularly. while (!fHaveGenesis && !ShutdownRequested()) { - condvar_GenesisWait.wait_for(lock, std::chrono::milliseconds(500)); + g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500)); } uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); } diff --git a/src/net.h b/src/net.h index 8242122742d..25f3d2430bb 100644 --- a/src/net.h +++ b/src/net.h @@ -399,7 +399,7 @@ class CConnman bool fMsgProcWake; std::condition_variable condMsgProc; - CWaitableCriticalSection mutexMsgProc; + Mutex mutexMsgProc; std::atomic flagInterruptMsgProc; CThreadInterrupt interruptNet; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 2d7093f424d..9ea249b0a97 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -43,7 +43,7 @@ struct CUpdatedBlock int height; }; -static CWaitableCriticalSection cs_blockchange; +static Mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 58795317e58..f98ae514b16 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -537,10 +537,10 @@ UniValue getblocktemplate(const JSONRPCRequest& request) { checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - WAIT_LOCK(csBestBlock, lock); + WAIT_LOCK(g_best_block_mutex, lock); while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) { - if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) + if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout) { // Timeout: Check transactions for update if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) diff --git a/src/sync.h b/src/sync.h index bc8c2c1451e..a9ea150f53a 100644 --- a/src/sync.h +++ b/src/sync.h @@ -105,10 +105,7 @@ typedef AnnotatedMixin CCriticalSection; typedef CCriticalSection CDynamicCriticalSection; /** Wrapped mutex: supports waiting but not recursive locking */ -typedef AnnotatedMixin CWaitableCriticalSection; - -/** Just a typedef for std::condition_variable, can be wrapped later if desired */ -typedef std::condition_variable CConditionVariable; +typedef AnnotatedMixin Mutex; #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine); @@ -116,7 +113,7 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine); /** Wrapper around std::unique_lock style lock for Mutex. */ template -class SCOPED_LOCKABLE CCriticalBlock : public Base +class SCOPED_LOCKABLE UniqueLock : public Base { private: void Enter(const char* pszName, const char* pszFile, int nLine) @@ -142,7 +139,7 @@ class SCOPED_LOCKABLE CCriticalBlock : public Base } public: - CCriticalBlock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock) + UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock) { if (fTry) TryEnter(pszName, pszFile, nLine); @@ -150,7 +147,7 @@ class SCOPED_LOCKABLE CCriticalBlock : public Base Enter(pszName, pszFile, nLine); } - CCriticalBlock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) + UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) { if (!pmutexIn) return; @@ -161,7 +158,7 @@ class SCOPED_LOCKABLE CCriticalBlock : public Base Enter(pszName, pszFile, nLine); } - ~CCriticalBlock() UNLOCK_FUNCTION() + ~UniqueLock() UNLOCK_FUNCTION() { if (Base::owns_lock()) LeaveCritical(); @@ -174,7 +171,7 @@ class SCOPED_LOCKABLE CCriticalBlock : public Base }; template -using DebugLock = CCriticalBlock::type>::type>; +using DebugLock = UniqueLock::type>::type>; #define PASTE(x, y) x ## y #define PASTE2(x, y) PASTE(x, y) diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp index 539e2ff3ab4..df0380546e3 100644 --- a/src/test/sync_tests.cpp +++ b/src/test/sync_tests.cpp @@ -41,7 +41,7 @@ BOOST_AUTO_TEST_CASE(potential_deadlock_detected) CCriticalSection rmutex1, rmutex2; TestPotentialDeadLockDetected(rmutex1, rmutex2); - CWaitableCriticalSection mutex1, mutex2; + Mutex mutex1, mutex2; TestPotentialDeadLockDetected(mutex1, mutex2); #ifdef DEBUG_LOCKORDER diff --git a/src/threadinterrupt.h b/src/threadinterrupt.h index 7f0dc09f66f..b7d87a10738 100644 --- a/src/threadinterrupt.h +++ b/src/threadinterrupt.h @@ -29,7 +29,7 @@ class CThreadInterrupt private: std::condition_variable cond; - CWaitableCriticalSection mut; + Mutex mut; std::atomic flag; }; diff --git a/src/validation.cpp b/src/validation.cpp index f07dc12dd09..39a7509e723 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -63,8 +63,8 @@ CCriticalSection cs_main; BlockMap mapBlockIndex; CChain chainActive; CBlockIndex *pindexBestHeader = NULL; -CWaitableCriticalSection csBestBlock; -CConditionVariable cvBlockChange; +Mutex g_best_block_mutex; +std::condition_variable g_best_block_cv; int nScriptCheckThreads = 0; std::atomic_bool fImporting(false); bool fReindex = false; @@ -2177,7 +2177,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { // New best block mempool.AddTransactionsUpdated(1); - cvBlockChange.notify_all(); + g_best_block_cv.notify_all(); static bool fWarned = false; std::vector warningMessages; diff --git a/src/validation.h b/src/validation.h index 3ea29a0b9d1..0eb80c71ac2 100644 --- a/src/validation.h +++ b/src/validation.h @@ -171,8 +171,9 @@ extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; extern uint64_t nLastBlockWeight; extern const std::string strMessageMagic; -extern CWaitableCriticalSection csBestBlock; -extern CConditionVariable cvBlockChange; +extern Mutex g_best_block_mutex; +extern std::condition_variable g_best_block_cv; +extern uint256 g_best_block; extern std::atomic_bool fImporting; extern bool fReindex; extern int nScriptCheckThreads; From e62a34532a0efa7256dad4a73fe3b04bfd0d1371 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 17 Feb 2017 16:32:16 -0800 Subject: [PATCH 13/60] Introduce FastRandomContext::randbool() Cherry-picked from: c21cbe61c6249bcfca098705df6f9b4baab9f296 --- src/random.h | 4 ++++ src/wallet/wallet.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/random.h b/src/random.h index 664f030eba2..367a99c7c6f 100644 --- a/src/random.h +++ b/src/random.h @@ -42,6 +42,10 @@ class FastRandomContext { return (Rw << 16) + Rz; } + bool randbool() { + return rand32() & 1; + } + uint32_t Rz; uint32_t Rw; }; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7b38882e626..0d59c548a29 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2123,7 +2123,7 @@ static void ApproximateBestSubset(vector Date: Wed, 15 Feb 2017 17:56:54 -0800 Subject: [PATCH 14/60] FastRandom benchmark Cherry-picked from: 663fbae7776b0c238f6f27d73811b4bc627d0b6b --- src/bench/crypto_hash.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 737d3572ae9..5257e60e810 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -7,6 +7,7 @@ #include "bench.h" #include "bloom.h" #include "hash.h" +#include "random.h" #include "uint256.h" #include "utiltime.h" #include "crypto/ripemd160.h" @@ -69,6 +70,28 @@ static void SipHash_32b(benchmark::State& state) } } +static void FastRandom_32bit(benchmark::State& state) +{ + FastRandomContext rng(true); + uint32_t x; + while (state.KeepRunning()) { + for (int i = 0; i < 1000000; i++) { + x += rng.rand32(); + } + } +} + +static void FastRandom_1bit(benchmark::State& state) +{ + FastRandomContext rng(true); + uint32_t x; + while (state.KeepRunning()) { + for (int i = 0; i < 1000000; i++) { + x += rng.randbool(); + } + } +} + BENCHMARK(RIPEMD160); BENCHMARK(SHA1); BENCHMARK(SHA256); @@ -76,3 +99,5 @@ BENCHMARK(SHA512); BENCHMARK(SHA256_32b); BENCHMARK(SipHash_32b); +BENCHMARK(FastRandom_32bit); +BENCHMARK(FastRandom_1bit); From b6ef7e92e610b6ad3f3eddcef58be210b7a35349 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 15 Feb 2017 12:29:23 -0800 Subject: [PATCH 15/60] Add ChaCha20 Cherry-picked from: e04326fe6652543dc26d90eba4a48fbdc935fd0c --- src/Makefile.am | 2 + src/crypto/chacha20.cpp | 180 ++++++++++++++++++++++++++++++++++++++ src/crypto/chacha20.h | 26 ++++++ src/test/crypto_tests.cpp | 45 ++++++++++ 4 files changed, 253 insertions(+) create mode 100644 src/crypto/chacha20.cpp create mode 100644 src/crypto/chacha20.h diff --git a/src/Makefile.am b/src/Makefile.am index 9ede7fbabca..b26afc5c1ca 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -256,6 +256,8 @@ crypto_libdogecoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libdogecoin_crypto_a_SOURCES = \ crypto/aes.cpp \ crypto/aes.h \ + crypto/chacha20.h \ + crypto/chacha20.cpp \ crypto/common.h \ crypto/hmac_sha256.cpp \ crypto/hmac_sha256.h \ diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp new file mode 100644 index 00000000000..816ae870e13 --- /dev/null +++ b/src/crypto/chacha20.cpp @@ -0,0 +1,180 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Based on the public domain implementation 'merged' by D. J. Bernstein +// See https://cr.yp.to/chacha.html. + +#include "crypto/common.h" +#include "crypto/chacha20.h" + +#include + +constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); } + +#define QUARTERROUND(a,b,c,d) \ + a += b; d = rotl32(d ^ a, 16); \ + c += d; b = rotl32(b ^ c, 12); \ + a += b; d = rotl32(d ^ a, 8); \ + c += d; b = rotl32(b ^ c, 7); + +static const unsigned char sigma[] = "expand 32-byte k"; +static const unsigned char tau[] = "expand 16-byte k"; + +void ChaCha20::SetKey(const unsigned char* k, size_t keylen) +{ + const unsigned char *constants; + + input[4] = ReadLE32(k + 0); + input[5] = ReadLE32(k + 4); + input[6] = ReadLE32(k + 8); + input[7] = ReadLE32(k + 12); + if (keylen == 32) { /* recommended */ + k += 16; + constants = sigma; + } else { /* keylen == 16 */ + constants = tau; + } + input[8] = ReadLE32(k + 0); + input[9] = ReadLE32(k + 4); + input[10] = ReadLE32(k + 8); + input[11] = ReadLE32(k + 12); + input[0] = ReadLE32(constants + 0); + input[1] = ReadLE32(constants + 4); + input[2] = ReadLE32(constants + 8); + input[3] = ReadLE32(constants + 12); + input[12] = 0; + input[13] = 0; + input[14] = 0; + input[15] = 0; +} + +ChaCha20::ChaCha20() +{ + memset(input, 0, sizeof(input)); +} + +ChaCha20::ChaCha20(const unsigned char* k, size_t keylen) +{ + SetKey(k, keylen); +} + +void ChaCha20::SetIV(uint64_t iv) +{ + input[14] = iv; + input[15] = iv >> 32; +} + +void ChaCha20::Seek(uint64_t pos) +{ + input[12] = pos; + input[13] = pos >> 32; +} + +void ChaCha20::Output(unsigned char* c, size_t bytes) +{ + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + unsigned char *ctarget = NULL; + unsigned char tmp[64]; + unsigned int i; + + if (!bytes) return; + + j0 = input[0]; + j1 = input[1]; + j2 = input[2]; + j3 = input[3]; + j4 = input[4]; + j5 = input[5]; + j6 = input[6]; + j7 = input[7]; + j8 = input[8]; + j9 = input[9]; + j10 = input[10]; + j11 = input[11]; + j12 = input[12]; + j13 = input[13]; + j14 = input[14]; + j15 = input[15]; + + for (;;) { + if (bytes < 64) { + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 += j0; + x1 += j1; + x2 += j2; + x3 += j3; + x4 += j4; + x5 += j5; + x6 += j6; + x7 += j7; + x8 += j8; + x9 += j9; + x10 += j10; + x11 += j11; + x12 += j12; + x13 += j13; + x14 += j14; + x15 += j15; + + ++j12; + if (!j12) ++j13; + + WriteLE32(c + 0, x0); + WriteLE32(c + 4, x1); + WriteLE32(c + 8, x2); + WriteLE32(c + 12, x3); + WriteLE32(c + 16, x4); + WriteLE32(c + 20, x5); + WriteLE32(c + 24, x6); + WriteLE32(c + 28, x7); + WriteLE32(c + 32, x8); + WriteLE32(c + 36, x9); + WriteLE32(c + 40, x10); + WriteLE32(c + 44, x11); + WriteLE32(c + 48, x12); + WriteLE32(c + 52, x13); + WriteLE32(c + 56, x14); + WriteLE32(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + input[12] = j12; + input[13] = j13; + return; + } + bytes -= 64; + c += 64; + } +} diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h new file mode 100644 index 00000000000..a305977bcd5 --- /dev/null +++ b/src/crypto/chacha20.h @@ -0,0 +1,26 @@ +// Copyright (c) 2017 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_CRYPTO_CHACHA20_H +#define BITCOIN_CRYPTO_CHACHA20_H + +#include +#include + +/** A PRNG class for ChaCha20. */ +class ChaCha20 +{ +private: + uint32_t input[16]; + +public: + ChaCha20(); + ChaCha20(const unsigned char* key, size_t keylen); + void SetKey(const unsigned char* key, size_t keylen); + void SetIV(uint64_t iv); + void Seek(uint64_t pos); + void Output(unsigned char* output, size_t bytes); +}; + +#endif // BITCOIN_CRYPTO_CHACHA20_H diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 4d174171797..a20182afb83 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "crypto/aes.h" +#include "crypto/chacha20.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" @@ -187,6 +188,19 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad } } +void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout) +{ + std::vector key = ParseHex(hexkey); + ChaCha20 rng(key.data(), key.size()); + rng.SetIV(nonce); + rng.Seek(seek); + std::vector out = ParseHex(hexout); + std::vector outres; + outres.resize(out.size()); + rng.Output(outres.data(), outres.size()); + BOOST_CHECK(out == outres); +} + std::string LongTestString(void) { std::string ret; for (int i=0; i<200000; i++) { @@ -439,4 +453,35 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) { "b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644"); } + +BOOST_AUTO_TEST_CASE(chacha20_testvector) +{ + // Test vector from RFC 7539 + TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, + "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb" + "a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a" + "832c89c167eacd901d7e2bf363"); + + // Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0, + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b" + "8f41518a11cc387b669b2ee6586"); + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0, + "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79" + "2b1c43fea817e9ad275ae546963"); + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0, + "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770" + "62eb7a0433e445f41e3"); + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0, + "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4" + "97a0b466e7d6bbdb0041b2f586b"); + TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0, + "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b" + "e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1" + "18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5" + "a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5" + "360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78" + "fab78c9"); +} + BOOST_AUTO_TEST_SUITE_END() From 2e80b494ca8ace0153e17f105faa9db3604376f1 Mon Sep 17 00:00:00 2001 From: Dakoda Greaves Date: Tue, 16 Apr 2024 14:42:14 -0700 Subject: [PATCH 16/60] util: Specific GetOSRandom for Linux/FreeBSD/OpenBSD These are available in sandboxes without access to files or devices. Also [they are safer and more straightforward](https://en.wikipedia.org/wiki/Entropy-supplying_system_calls) to use than `/dev/urandom` as reading from a file has quite a few edge cases: - Linux: `getrandom(buf, buflen, 0)`. [getrandom(2)](http://man7.org/linux/man-pages/man2/getrandom.2.html) was introduced in version 3.17 of the Linux kernel. - OpenBSD: `getentropy(buf, buflen)`. The [getentropy(2)](http://man.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2) function appeared in OpenBSD 5.6. - FreeBSD and NetBSD: `sysctl(KERN_ARND)`. Not sure when this was added but it has existed for quite a while. Alternatives: - Linux has sysctl `CTL_KERN` / `KERN_RANDOM` / `RANDOM_UUID` which gives 16 bytes of randomness. This may be available on older kernels, however [sysctl is deprecated on Linux](https://lwn.net/Articles/605392/) and even removed in some distros so we shouldn't use it. Add tests for `GetOSRand()`: - Test that no error happens (otherwise `RandFailure()` which aborts) - Test that all 32 bytes are overwritten (initialize with zeros, try multiple times) Discussion: - When to use these? Currently they are always used when available. Another option would be to use them only when `/dev/urandom` is not available. But this would mean these code paths receive less testing, and I'm not sure there is any reason to prefer `/dev/urandom`. Closes: #9676 Cherry-picked from: 224e6eb089a0f4977d22f3803fc27e44b5e7eea5 Contains squashed commit of aa09ccbb74ea9febd83ce3362238ac5339069909 squashme: comment that NUM_OS_RANDOM_BYTES should not be changed lightly --- configure.ac | 26 ++++++++++++++++++ src/Makefile.test.include | 1 + src/random.cpp | 56 ++++++++++++++++++++++++++++++++++----- src/random.h | 12 +++++++++ src/test/random_tests.cpp | 46 ++++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 src/test/random_tests.cpp diff --git a/configure.ac b/configure.ac index 366cd1379f6..6540be17e68 100644 --- a/configure.ac +++ b/configure.ac @@ -656,6 +656,32 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([ ) LDFLAGS="$TEMP_LDFLAGS" +# Check for different ways of gathering OS randomness +AC_MSG_CHECKING(for Linux getrandom syscall) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include + #include ]], + [[ syscall(SYS_getrandom, nullptr, 32, 0); ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYS_GETRANDOM, 1,[Define this symbol if the Linux getrandom system call is available]) ], + [ AC_MSG_RESULT(no)] +) + +AC_MSG_CHECKING(for getentropy) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ getentropy(nullptr, 32) ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY, 1,[Define this symbol if the BSD getentropy system call is available]) ], + [ AC_MSG_RESULT(no)] +) + +AC_MSG_CHECKING(for sysctl KERN_ARND) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], + [[ static const int name[2] = {CTL_KERN, KERN_ARND}; + sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL_ARND, 1,[Define this symbol if the BSD sysctl(KERN_ARND) is available]) ], + [ AC_MSG_RESULT(no)] +) + # Check for reduced exports if test x$use_reduce_exports = xyes; then AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 9facc0d3b07..2f65f201809 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -113,6 +113,7 @@ BITCOIN_TESTS =\ test/policyestimator_tests.cpp \ test/pow_tests.cpp \ test/prevector_tests.cpp \ + test/random_tests.cpp \ test/raii_event_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ diff --git a/src/random.cpp b/src/random.cpp index 5bb7d9c3120..5cc68d919f2 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -22,6 +22,17 @@ #include #endif +#ifdef HAVE_SYS_GETRANDOM +#include +#include +#endif +#ifdef HAVE_GETENTROPY +#include +#endif +#ifdef HAVE_SYSCTL_ARND +#include +#endif + #include #include @@ -93,32 +104,65 @@ static void RandAddSeedPerfmon() } /** Get 32 bytes of system entropy. */ -static void GetOSRand(unsigned char *ent32) +void GetOSRand(unsigned char *ent32) { -#ifdef WIN32 +#if defined(WIN32) HCRYPTPROV hProvider; int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!ret) { RandFailure(); } - ret = CryptGenRandom(hProvider, 32, ent32); + ret = CryptGenRandom(hProvider, NUM_OS_RANDOM_BYTES, ent32); if (!ret) { RandFailure(); } CryptReleaseContext(hProvider, 0); +#elif defined(HAVE_SYS_GETRANDOM) + /* Linux. From the getrandom(2) man page: + * "If the urandom source has been initialized, reads of up to 256 bytes + * will always return as many bytes as requested and will not be + * interrupted by signals." + */ + if (syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0) != NUM_OS_RANDOM_BYTES) { + RandFailure(); + } +#elif defined(HAVE_GETENTROPY) + /* On OpenBSD this can return up to 256 bytes of entropy, will return an + * error if more are requested. + * The call cannot return less than the requested number of bytes. + */ + if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { + RandFailure(); + } +#elif defined(HAVE_SYSCTL_ARND) + /* FreeBSD and similar. It is possible for the call to return less + * bytes than requested, so need to read in a loop. + */ + static const int name[2] = {CTL_KERN, KERN_ARND}; + int have = 0; + do { + size_t len = NUM_OS_RANDOM_BYTES - have; + if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, NULL, 0) != 0) { + RandFailure(); + } + have += len; + } while (have < NUM_OS_RANDOM_BYTES); #else + /* Fall back to /dev/urandom if there is no specific method implemented to + * get system entropy for this OS. + */ int f = open("/dev/urandom", O_RDONLY); if (f == -1) { RandFailure(); } int have = 0; do { - ssize_t n = read(f, ent32 + have, 32 - have); - if (n <= 0 || n + have > 32) { + ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); + if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { RandFailure(); } have += n; - } while (have < 32); + } while (have < NUM_OS_RANDOM_BYTES); close(f); #endif } diff --git a/src/random.h b/src/random.h index 367a99c7c6f..b0aadbc1d71 100644 --- a/src/random.h +++ b/src/random.h @@ -50,4 +50,16 @@ class FastRandomContext { uint32_t Rw; }; +/* Number of random bytes returned by GetOSRand. + * When changing this constant make sure to change all call sites, and make + * sure that the underlying OS APIs for all platforms support the number. + * (many cap out at 256 bytes). + */ +static const ssize_t NUM_OS_RANDOM_BYTES = 32; + +/** Get 32 bytes of system entropy. Do not use this in application code: use + * GetStrongRandBytes instead. + */ +void GetOSRand(unsigned char *ent32); + #endif // BITCOIN_RANDOM_H diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp new file mode 100644 index 00000000000..4f67415c7c5 --- /dev/null +++ b/src/test/random_tests.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2017 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 "random.h" + +#include "test/test_bitcoin.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(random_tests, BasicTestingSetup) + +static const ssize_t MAX_TRIES = 1024; + +BOOST_AUTO_TEST_CASE(osrandom_tests) +{ + /* This does not measure the quality of randomness, but it does test that + * OSRandom() overwrites all 32 bytes of the output given a maximum + * number of tries. + */ + uint8_t data[NUM_OS_RANDOM_BYTES]; + bool overwritten[NUM_OS_RANDOM_BYTES] = {}; /* Tracks which bytes have been overwritten at least once */ + int num_overwritten; + int tries = 0; + /* Loop until all bytes have been overwritten at least once */ + do { + memset(data, 0, NUM_OS_RANDOM_BYTES); + GetOSRand(data); + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { + overwritten[x] |= (data[x] != 0); + } + + num_overwritten = 0; + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { + if (overwritten[x]) { + num_overwritten += 1; + } + } + + tries += 1; + } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); + BOOST_CHECK(num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */ +} + +BOOST_AUTO_TEST_SUITE_END() + From 29de1fc668db767e72271e56ffd9d21c1b175b7a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 22 Feb 2017 08:02:50 +0100 Subject: [PATCH 17/60] sanity: Move OS random to sanity check function Move the OS random test to a sanity check function that is called every time bitcoind is initialized. Keep `src/test/random_tests.cpp` for the case that later random tests are added, and keep a rudimentary test that just calls the sanity check. Cherry-picked from: 7cad84929907c4294f07377453aa77887911b486 --- src/init.cpp | 6 ++++++ src/random.cpp | 30 ++++++++++++++++++++++++++++++ src/random.h | 5 +++++ src/test/random_tests.cpp | 29 +---------------------------- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 01ffb92f1b9..03f3f470d3f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -695,9 +695,15 @@ bool InitSanityCheck(void) InitError("Elliptic curve cryptography sanity check failure. Aborting."); return false; } + if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false; + if (!Random_SanityCheck()) { + InitError("OS cryptographic RNG sanity check failure. Aborting."); + return false; + } + return true; } diff --git a/src/random.cpp b/src/random.cpp index 5cc68d919f2..1dc76fbd858 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -240,3 +240,33 @@ FastRandomContext::FastRandomContext(bool fDeterministic) } } +bool Random_SanityCheck() +{ + /* This does not measure the quality of randomness, but it does test that + * OSRandom() overwrites all 32 bytes of the output given a maximum + * number of tries. + */ + static const ssize_t MAX_TRIES = 1024; + uint8_t data[NUM_OS_RANDOM_BYTES]; + bool overwritten[NUM_OS_RANDOM_BYTES] = {}; /* Tracks which bytes have been overwritten at least once */ + int num_overwritten; + int tries = 0; + /* Loop until all bytes have been overwritten at least once, or max number tries reached */ + do { + memset(data, 0, NUM_OS_RANDOM_BYTES); + GetOSRand(data); + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { + overwritten[x] |= (data[x] != 0); + } + + num_overwritten = 0; + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { + if (overwritten[x]) { + num_overwritten += 1; + } + } + + tries += 1; + } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); + return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */ +} diff --git a/src/random.h b/src/random.h index b0aadbc1d71..82886bec59b 100644 --- a/src/random.h +++ b/src/random.h @@ -62,4 +62,9 @@ static const ssize_t NUM_OS_RANDOM_BYTES = 32; */ void GetOSRand(unsigned char *ent32); +/** Check that OS randomness is available and returning the requested number + * of bytes. + */ +bool Random_SanityCheck(); + #endif // BITCOIN_RANDOM_H diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 4f67415c7c5..d2c46c0daab 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -10,36 +10,9 @@ BOOST_FIXTURE_TEST_SUITE(random_tests, BasicTestingSetup) -static const ssize_t MAX_TRIES = 1024; - BOOST_AUTO_TEST_CASE(osrandom_tests) { - /* This does not measure the quality of randomness, but it does test that - * OSRandom() overwrites all 32 bytes of the output given a maximum - * number of tries. - */ - uint8_t data[NUM_OS_RANDOM_BYTES]; - bool overwritten[NUM_OS_RANDOM_BYTES] = {}; /* Tracks which bytes have been overwritten at least once */ - int num_overwritten; - int tries = 0; - /* Loop until all bytes have been overwritten at least once */ - do { - memset(data, 0, NUM_OS_RANDOM_BYTES); - GetOSRand(data); - for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { - overwritten[x] |= (data[x] != 0); - } - - num_overwritten = 0; - for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { - if (overwritten[x]) { - num_overwritten += 1; - } - } - - tries += 1; - } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); - BOOST_CHECK(num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */ + BOOST_CHECK(Random_SanityCheck()); } BOOST_AUTO_TEST_SUITE_END() From ff6eab4e097f7c938288258aaf7f0c45b4f90104 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 22 Feb 2017 08:51:26 +0100 Subject: [PATCH 18/60] random: Add fallback if getrandom syscall not available If the code was compiled with newer (>=3.17) kernel headers but executed on a system without the system call, every use of random would crash the program. Add a fallback for that case. Cherry-picked from: 7e6dcd9995b99e894b8017f09016c405b066ca36 --- src/random.cpp | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 1dc76fbd858..68768b72027 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -103,6 +103,28 @@ static void RandAddSeedPerfmon() #endif } +#ifndef WIN32 +/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most + * compatible way to get cryptographic randomness on UNIX-ish platforms. + */ +void GetDevURandom(unsigned char *ent32) +{ + int f = open("/dev/urandom", O_RDONLY); + if (f == -1) { + RandFailure(); + } + int have = 0; + do { + ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); + if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { + RandFailure(); + } + have += n; + } while (have < NUM_OS_RANDOM_BYTES); + close(f); +} +#endif + /** Get 32 bytes of system entropy. */ void GetOSRand(unsigned char *ent32) { @@ -123,8 +145,17 @@ void GetOSRand(unsigned char *ent32) * will always return as many bytes as requested and will not be * interrupted by signals." */ - if (syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0) != NUM_OS_RANDOM_BYTES) { - RandFailure(); + int rv = syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0); + if (rv != NUM_OS_RANDOM_BYTES) { + if (rv < 0 && errno == ENOSYS) { + /* Fallback for kernel <3.17: the return value will be -1 and errno + * ENOSYS if the syscall is not available, in that case fall back + * to /dev/urandom. + */ + GetDevURandom(ent32); + } else { + RandFailure(); + } } #elif defined(HAVE_GETENTROPY) /* On OpenBSD this can return up to 256 bytes of entropy, will return an @@ -151,19 +182,7 @@ void GetOSRand(unsigned char *ent32) /* Fall back to /dev/urandom if there is no specific method implemented to * get system entropy for this OS. */ - int f = open("/dev/urandom", O_RDONLY); - if (f == -1) { - RandFailure(); - } - int have = 0; - do { - ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); - if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { - RandFailure(); - } - have += n; - } while (have < NUM_OS_RANDOM_BYTES); - close(f); + GetDevURandom(ent32); #endif } From 10d250413c8d584490d87184c7a13cdd54165ef5 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 15 Feb 2017 17:45:22 -0800 Subject: [PATCH 19/60] Switch FastRandomContext to ChaCha20 Cherry-picked from: 16329224e70d0525208f6b0ba00c5e1531a4f5ea --- src/Makefile.test.include | 2 +- src/addrman.cpp | 8 ++--- src/addrman.h | 11 ++++-- src/random.cpp | 33 ++++++++++-------- src/random.h | 66 +++++++++++++++++++++++++++++++----- src/test/addrman_tests.cpp | 9 ++--- src/test/prevector_tests.cpp | 8 ++--- src/test/random_tests.cpp | 21 +++++++++++- src/test/test_bitcoin.cpp | 3 +- src/test/test_random.h | 8 ++++- 10 files changed, 126 insertions(+), 43 deletions(-) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 2f65f201809..a648901f6c2 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -113,8 +113,8 @@ BITCOIN_TESTS =\ test/policyestimator_tests.cpp \ test/pow_tests.cpp \ test/prevector_tests.cpp \ - test/random_tests.cpp \ test/raii_event_tests.cpp \ + test/random_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ test/sanity_tests.cpp \ diff --git a/src/addrman.cpp b/src/addrman.cpp index b6ab4c63051..33a623c1f2c 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -351,8 +351,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT); int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE); while (vvTried[nKBucket][nKBucketPos] == -1) { - nKBucket = (nKBucket + insecure_rand.rand32()) % ADDRMAN_TRIED_BUCKET_COUNT; - nKBucketPos = (nKBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE; + nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT; + nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE; } int nId = vvTried[nKBucket][nKBucketPos]; assert(mapInfo.count(nId) == 1); @@ -368,8 +368,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT); int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE); while (vvNew[nUBucket][nUBucketPos] == -1) { - nUBucket = (nUBucket + insecure_rand.rand32()) % ADDRMAN_NEW_BUCKET_COUNT; - nUBucketPos = (nUBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE; + nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT; + nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE; } int nId = vvNew[nUBucket][nUBucketPos]; assert(mapInfo.count(nId) == 1); diff --git a/src/addrman.h b/src/addrman.h index 6e5f946bf28..bb45ac91ea4 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -136,13 +136,13 @@ class CAddrInfo : public CAddress */ //! total number of buckets for tried addresses -#define ADDRMAN_TRIED_BUCKET_COUNT 256 +#define ADDRMAN_TRIED_BUCKET_COUNT_LOG2 8 //! total number of buckets for new addresses -#define ADDRMAN_NEW_BUCKET_COUNT 1024 +#define ADDRMAN_NEW_BUCKET_COUNT_LOG2 10 //! maximum allowed number of entries in buckets for new and tried addresses -#define ADDRMAN_BUCKET_SIZE 64 +#define ADDRMAN_BUCKET_SIZE_LOG2 6 //! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8 @@ -171,6 +171,11 @@ class CAddrInfo : public CAddress //! the maximum number of nodes to return in a getaddr call #define ADDRMAN_GETADDR_MAX 2500 +//! Convenience +#define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2) +#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2) +#define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2) + /** * Stochastical (IP) address manager */ diff --git a/src/random.cpp b/src/random.cpp index 68768b72027..8b19c1e0d01 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -241,22 +241,16 @@ uint256 GetRandHash() return hash; } -FastRandomContext::FastRandomContext(bool fDeterministic) +void FastRandomContext::RandomSeed() { - // The seed values have some unlikely fixed points which we avoid. - if (fDeterministic) { - Rz = Rw = 11; - } else { - uint32_t tmp; - do { - GetRandBytes((unsigned char*)&tmp, 4); - } while (tmp == 0 || tmp == 0x9068ffffU); - Rz = tmp; - do { - GetRandBytes((unsigned char*)&tmp, 4); - } while (tmp == 0 || tmp == 0x464fffffU); - Rw = tmp; - } + uint256 seed = GetRandHash(); + rng.SetKey(seed.begin(), 32); + requires_seed = false; +} + +FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0) +{ + rng.SetKey(seed.begin(), 32); } bool Random_SanityCheck() @@ -289,3 +283,12 @@ bool Random_SanityCheck() } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */ } + +FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) +{ + if (!fDeterministic) { + return; + } + uint256 seed; + rng.SetKey(seed.begin(), 32); +} diff --git a/src/random.h b/src/random.h index 82886bec59b..077f58c4d97 100644 --- a/src/random.h +++ b/src/random.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_RANDOM_H #define BITCOIN_RANDOM_H +#include "crypto/chacha20.h" #include "uint256.h" #include @@ -33,21 +34,68 @@ void GetStrongRandBytes(unsigned char* buf, int num); * This class is not thread-safe. */ class FastRandomContext { +private: + bool requires_seed; + ChaCha20 rng; + + unsigned char bytebuf[64]; + int bytebuf_size; + + uint64_t bitbuf; + int bitbuf_size; + + void RandomSeed(); + + void FillByteBuffer() + { + if (requires_seed) { + RandomSeed(); + } + rng.Output(bytebuf, sizeof(bytebuf)); + bytebuf_size = sizeof(bytebuf); + } + + void FillBitBuffer() + { + bitbuf = rand64(); + bitbuf_size = 64; + } + public: - explicit FastRandomContext(bool fDeterministic=false); + explicit FastRandomContext(bool fDeterministic = false); - uint32_t rand32() { - Rz = 36969 * (Rz & 65535) + (Rz >> 16); - Rw = 18000 * (Rw & 65535) + (Rw >> 16); - return (Rw << 16) + Rz; + /** Initialize with explicit seed (only for testing) */ + explicit FastRandomContext(const uint256& seed); + + /** Generate a random 64-bit integer. */ + uint64_t rand64() + { + if (bytebuf_size < 8) FillByteBuffer(); + uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size); + bytebuf_size -= 8; + return ret; } - bool randbool() { - return rand32() & 1; + /** Generate a random (bits)-bit integer. */ + uint64_t randbits(int bits) { + if (bits == 0) { + return 0; + } else if (bits > 32) { + return rand64() >> (64 - bits); + } else { + if (bitbuf_size < bits) FillBitBuffer(); + uint64_t ret = bitbuf & (~(uint64_t)0 >> (64 - bits)); + bitbuf >>= bits; + bitbuf_size -= bits; + return ret; + } } - uint32_t Rz; - uint32_t Rw; + /** Generate a random 32-bit integer. */ + uint32_t rand32() { return randbits(32); } + + /** Generate a random boolean. */ + bool randbool() { return randbits(1); } }; /* Number of random bytes returned by GetOSRand. diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 541e4283e18..af5871a2170 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -195,10 +195,11 @@ BOOST_AUTO_TEST_CASE(addrman_select) BOOST_CHECK(addrman.size() == 7); // Test 12: Select pulls from new and tried regardless of port number. - BOOST_CHECK(addrman.Select().ToString() == "250.4.6.6:8333"); - BOOST_CHECK(addrman.Select().ToString() == "250.3.2.2:9999"); - BOOST_CHECK(addrman.Select().ToString() == "250.3.3.3:9999"); - BOOST_CHECK(addrman.Select().ToString() == "250.4.4.4:8333"); + std::set ports; + for (int i = 0; i < 20; ++i) { + ports.insert(addrman.Select().GetPort()); + } + BOOST_CHECK_EQUAL(ports.size(), 3); } BOOST_AUTO_TEST_CASE(addrman_new_collisions) diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index bd8a7819a4c..cfed5e347ed 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -28,6 +28,7 @@ class prevector_tester { typedef typename pretype::size_type Size; bool passed = true; FastRandomContext rand_cache; + uint256 rand_seed; template @@ -183,13 +184,12 @@ class prevector_tester { } ~prevector_tester() { - BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: " - << rand_cache.Rz - << ", insecure_rand_Rw: " - << rand_cache.Rw); + BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString()); } + prevector_tester() { seed_insecure_rand(); + rand_seed = insecure_rand_seed; rand_cache = insecure_rand_ctx; } }; diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index d2c46c0daab..31b993cd382 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -15,5 +15,24 @@ BOOST_AUTO_TEST_CASE(osrandom_tests) BOOST_CHECK(Random_SanityCheck()); } -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_CASE(fastrandom_tests) +{ + // Check that deterministic FastRandomContexts are deterministic + FastRandomContext ctx1(true); + FastRandomContext ctx2(true); + + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64()); + BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3)); + BOOST_CHECK_EQUAL(ctx1.randbits(7), ctx2.randbits(7)); + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3)); + // Check that a nondeterministic ones are not + FastRandomContext ctx3; + FastRandomContext ctx4; + BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 9ec06ab8dd4..8a88e8a3700 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -32,7 +32,8 @@ #include std::unique_ptr g_connman; -FastRandomContext insecure_rand_ctx(true); +uint256 insecure_rand_seed = GetRandHash(); +FastRandomContext insecure_rand_ctx(insecure_rand_seed); extern bool fPrintToConsole; extern void noui_connect(); diff --git a/src/test/test_random.h b/src/test/test_random.h index 4a1637ac724..318c44df4dc 100644 --- a/src/test/test_random.h +++ b/src/test/test_random.h @@ -8,11 +8,17 @@ #include "random.h" +extern uint256 insecure_rand_seed; extern FastRandomContext insecure_rand_ctx; static inline void seed_insecure_rand(bool fDeterministic = false) { - insecure_rand_ctx = FastRandomContext(fDeterministic); + if (fDeterministic) { + insecure_rand_seed = uint256(); + } else { + insecure_rand_seed = GetRandHash(); + } + insecure_rand_ctx = FastRandomContext(insecure_rand_seed); } static inline uint32_t insecure_rand(void) From eb3c8dae69a569b15db9cadd3eeace8c1b13ee95 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 25 Feb 2017 12:16:58 -0800 Subject: [PATCH 20/60] Add a FastRandomContext::randrange and use it Cherry-picked from: 4fd2d2fc97e21efceab849576e544160fd5e3e3d --- configure.ac | 2 ++ src/bench/checkqueue.cpp | 2 +- src/crypto/common.h | 21 +++++++++++++++++++++ src/net.h | 2 +- src/random.h | 12 ++++++++++++ src/test/crypto_tests.cpp | 23 +++++++++++++++++++++++ src/test/random_tests.cpp | 15 +++++++++++++++ 7 files changed, 75 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 6540be17e68..06d8bd48a88 100644 --- a/configure.ac +++ b/configure.ac @@ -601,6 +601,8 @@ AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, #include #endif]) +AC_CHECK_DECLS([__builtin_clz, __builtin_clzl, __builtin_clzll]) + dnl Check for MSG_NOSIGNAL AC_MSG_CHECKING(for MSG_NOSIGNAL) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp index 6fa9fe4fe8c..88a2a570f93 100644 --- a/src/bench/checkqueue.cpp +++ b/src/bench/checkqueue.cpp @@ -68,7 +68,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state) PrevectorJob(){ } PrevectorJob(FastRandomContext& insecure_rand){ - p.resize(insecure_rand.rand32() % (PREVECTOR_SIZE*2)); + p.resize(insecure_rand.randrange(PREVECTOR_SIZE*2)); } bool operator()() { diff --git a/src/crypto/common.h b/src/crypto/common.h index 4a9d1150b67..bcca3d30ea7 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -79,4 +79,25 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x) memcpy(ptr, (char*)&v, 8); } +/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ +uint64_t static inline CountBits(uint64_t x) +{ +#ifdef HAVE_DECL___BUILTIN_CLZL + if (sizeof(unsigned long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; + } +#endif +#ifdef HAVE_DECL___BUILTIN_CLZLL + if (sizeof(unsigned long long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; + } +#endif + int ret = 0; + while (x) { + x >>= 1; + ++ret; + } + return ret; +} + #endif // BITCOIN_CRYPTO_COMMON_H diff --git a/src/net.h b/src/net.h index 25f3d2430bb..cd7c082b968 100644 --- a/src/net.h +++ b/src/net.h @@ -778,7 +778,7 @@ class CNode // after addresses were pushed. if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { - vAddrToSend[insecure_rand.rand32() % vAddrToSend.size()] = _addr; + vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr; } else { vAddrToSend.push_back(_addr); } diff --git a/src/random.h b/src/random.h index 077f58c4d97..9551e1c4613 100644 --- a/src/random.h +++ b/src/random.h @@ -7,6 +7,7 @@ #define BITCOIN_RANDOM_H #include "crypto/chacha20.h" +#include "crypto/common.h" #include "uint256.h" #include @@ -91,6 +92,17 @@ class FastRandomContext { } } + /** Generate a random integer in the range [0..range). */ + uint64_t randrange(uint64_t range) + { + --range; + int bits = CountBits(range); + while (true) { + uint64_t ret = randbits(bits); + if (ret <= range) return ret; + } + } + /** Generate a random 32-bit integer. */ uint32_t rand32() { return randbits(32); } diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index a20182afb83..72e562808af 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -10,6 +10,7 @@ #include "crypto/sha512.h" #include "crypto/hmac_sha256.h" #include "crypto/hmac_sha512.h" +#include "random.h" #include "utilstrencodings.h" #include "test/test_bitcoin.h" #include "test/test_random.h" @@ -484,4 +485,26 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector) "fab78c9"); } +BOOST_AUTO_TEST_CASE(countbits_tests) +{ + FastRandomContext ctx; + for (int i = 0; i <= 64; ++i) { + if (i == 0) { + // Check handling of zero. + BOOST_CHECK_EQUAL(CountBits(0), 0); + } 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_SUITE_END() diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 31b993cd382..85967342263 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -35,4 +35,19 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests) BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal } +BOOST_AUTO_TEST_CASE(fastrandom_randbits) +{ + FastRandomContext ctx1; + FastRandomContext ctx2; + for (int bits = 0; bits < 63; ++bits) { + for (int j = 0; j < 1000; ++j) { + uint64_t rangebits = ctx1.randbits(bits); + BOOST_CHECK_EQUAL(rangebits >> bits, 0); + uint64_t range = ((uint64_t)1) << bits | rangebits; + uint64_t rand = ctx2.randrange(range); + BOOST_CHECK(rand < range); + } + } +} + BOOST_AUTO_TEST_SUITE_END() From c88237a2b29dac442c9dfad6de4bf01fea9d9233 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 27 Mar 2024 14:31:48 -0700 Subject: [PATCH 21/60] Add FastRandomContext::rand256() and ::randbytes() FastRandomContext now provides all functionality that the real Rand* functions provide. Cherry-picked from: 37e864eb9fee4b592bd61c5ec3555b00a2de2cf7 --- src/random.cpp | 20 ++++++++++++++++++++ src/random.h | 6 ++++++ src/test/cuckoocache_tests.cpp | 24 ++++++++++++------------ src/test/random_tests.cpp | 7 +++++++ 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 8b19c1e0d01..9a2c905b7c2 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -248,6 +248,26 @@ void FastRandomContext::RandomSeed() requires_seed = false; } +uint256 FastRandomContext::rand256() +{ + if (bytebuf_size < 32) { + FillByteBuffer(); + } + uint256 ret; + memcpy(ret.begin(), bytebuf + 64 - bytebuf_size, 32); + bytebuf_size -= 32; + return ret; +} + +std::vector FastRandomContext::randbytes(size_t len) +{ + std::vector ret(len); + if (len > 0) { + rng.Output(&ret[0], len); + } + return ret; +} + FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0) { rng.SetKey(seed.begin(), 32); diff --git a/src/random.h b/src/random.h index 9551e1c4613..f8e2e0dc555 100644 --- a/src/random.h +++ b/src/random.h @@ -103,9 +103,15 @@ class FastRandomContext { } } + /** Generate random bytes. */ + std::vector randbytes(size_t len); + /** Generate a random 32-bit integer. */ uint32_t rand32() { return randbits(32); } + /** generate a random uint256. */ + uint256 rand256(); + /** Generate a random boolean. */ bool randbool() { return randbits(1); } }; diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index 6004b25aae8..916b23cee87 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -24,18 +24,18 @@ * using BOOST_CHECK_CLOSE to fail. * */ -FastRandomContext insecure_rand(true); +FastRandomContext local_rand_ctx(true); BOOST_AUTO_TEST_SUITE(cuckoocache_tests); -/** insecure_GetRandHash fills in a uint256 from insecure_rand +/** insecure_GetRandHash fills in a uint256 from local_rand_ctx */ void insecure_GetRandHash(uint256& t) { uint32_t* ptr = (uint32_t*)t.begin(); for (uint8_t j = 0; j < 8; ++j) - *(ptr++) = insecure_rand.rand32(); + *(ptr++) = local_rand_ctx.rand32(); } /** Definition copied from /src/script/sigcache.cpp @@ -60,7 +60,7 @@ class uint256Hasher */ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes) { - insecure_rand = FastRandomContext(true); + local_rand_ctx = FastRandomContext(true); CuckooCache::cache cc{}; cc.setup_bytes(32 << 20); uint256 v; @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes) template double test_cache(size_t megabytes, double load) { - insecure_rand = FastRandomContext(true); + local_rand_ctx = FastRandomContext(true); std::vector hashes; Cache set{}; size_t bytes = megabytes * (1 << 20); @@ -90,7 +90,7 @@ double test_cache(size_t megabytes, double load) for (uint32_t i = 0; i < n_insert; ++i) { uint32_t* ptr = (uint32_t*)hashes[i].begin(); for (uint8_t j = 0; j < 8; ++j) - *(ptr++) = insecure_rand.rand32(); + *(ptr++) = local_rand_ctx.rand32(); } /** We make a copy of the hashes because future optimizations of the * cuckoocache may overwrite the inserted element, so the test is @@ -151,7 +151,7 @@ template void test_cache_erase(size_t megabytes) { double load = 1; - insecure_rand = FastRandomContext(true); + local_rand_ctx = FastRandomContext(true); std::vector hashes; Cache set{}; size_t bytes = megabytes * (1 << 20); @@ -161,7 +161,7 @@ void test_cache_erase(size_t megabytes) for (uint32_t i = 0; i < n_insert; ++i) { uint32_t* ptr = (uint32_t*)hashes[i].begin(); for (uint8_t j = 0; j < 8; ++j) - *(ptr++) = insecure_rand.rand32(); + *(ptr++) = local_rand_ctx.rand32(); } /** We make a copy of the hashes because future optimizations of the * cuckoocache may overwrite the inserted element, so the test is @@ -214,7 +214,7 @@ template void test_cache_erase_parallel(size_t megabytes) { double load = 1; - insecure_rand = FastRandomContext(true); + local_rand_ctx = FastRandomContext(true); std::vector hashes; Cache set{}; size_t bytes = megabytes * (1 << 20); @@ -224,7 +224,7 @@ void test_cache_erase_parallel(size_t megabytes) for (uint32_t i = 0; i < n_insert; ++i) { uint32_t* ptr = (uint32_t*)hashes[i].begin(); for (uint8_t j = 0; j < 8; ++j) - *(ptr++) = insecure_rand.rand32(); + *(ptr++) = local_rand_ctx.rand32(); } /** We make a copy of the hashes because future optimizations of the * cuckoocache may overwrite the inserted element, so the test is @@ -316,7 +316,7 @@ void test_cache_generations() // iterations with non-deterministic values, so it isn't "overfit" to the // specific entropy in FastRandomContext(true) and implementation of the // cache. - insecure_rand = FastRandomContext(true); + local_rand_ctx = FastRandomContext(true); // block_activity models a chunk of network activity. n_insert elements are // adde to the cache. The first and last n/4 are stored for removal later @@ -333,7 +333,7 @@ void test_cache_generations() for (uint32_t i = 0; i < n_insert; ++i) { uint32_t* ptr = (uint32_t*)inserts[i].begin(); for (uint8_t j = 0; j < 8; ++j) - *(ptr++) = insecure_rand.rand32(); + *(ptr++) = local_rand_ctx.rand32(); } for (uint32_t i = 0; i < n_insert / 4; ++i) reads.push_back(inserts[i]); diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 85967342263..132e190051e 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -25,14 +25,21 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests) BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64()); BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3)); + BOOST_CHECK(ctx1.randbytes(17) == ctx2.randbytes(17)); + BOOST_CHECK(ctx1.rand256() == ctx2.rand256()); BOOST_CHECK_EQUAL(ctx1.randbits(7), ctx2.randbits(7)); + BOOST_CHECK(ctx1.randbytes(128) == ctx2.randbytes(128)); BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3)); + BOOST_CHECK(ctx1.rand256() == ctx2.rand256()); + BOOST_CHECK(ctx1.randbytes(50) == ctx2.randbytes(50)); // Check that a nondeterministic ones are not FastRandomContext ctx3; FastRandomContext ctx4; BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal + BOOST_CHECK(ctx3.rand256() != ctx4.rand256()); + BOOST_CHECK(ctx3.randbytes(7) != ctx4.randbytes(7)); } BOOST_AUTO_TEST_CASE(fastrandom_randbits) From 92b332791182ff65808ffb2e5d79ecaa0bc699f8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 26 Mar 2024 16:09:38 -0700 Subject: [PATCH 22/60] Merge test_random.h into test_bitcoin.h Cherry-picked from: 124d13a58cdcd9f66eeffc7e6281e3eb129e3398 --- src/Makefile.test.include | 1 - src/test/coins_tests.cpp | 1 - src/test/crypto_tests.cpp | 1 - src/test/merkle_tests.cpp | 1 - src/test/pmt_tests.cpp | 1 - src/test/prevector_tests.cpp | 1 - src/test/sighash_tests.cpp | 1 - src/test/skiplist_tests.cpp | 1 - src/test/test_bitcoin.h | 19 +++++++++++++++++++ src/test/test_random.h | 29 ----------------------------- src/test/util_tests.cpp | 1 - src/test/versionbits_tests.cpp | 1 - src/wallet/test/crypto_tests.cpp | 3 +-- 13 files changed, 20 insertions(+), 41 deletions(-) delete mode 100644 src/test/test_random.h diff --git a/src/Makefile.test.include b/src/Makefile.test.include index a648901f6c2..0f170fdd16e 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -130,7 +130,6 @@ BITCOIN_TESTS =\ test/streams_tests.cpp \ test/test_bitcoin.cpp \ test/test_bitcoin.h \ - test/test_random.h \ test/testutil.cpp \ test/testutil.h \ test/sync_tests.cpp \ diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 42f133b7478..ae85a1edad5 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -8,7 +8,6 @@ #include "undo.h" #include "utilstrencodings.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include "validation.h" #include "consensus/validation.h" diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 72e562808af..4b76cc1021b 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -13,7 +13,6 @@ #include "random.h" #include "utilstrencodings.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index af02d67f742..2db510d6d39 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -4,7 +4,6 @@ #include "consensus/merkle.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index a1cb32019ae..e3a4f90aaff 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -10,7 +10,6 @@ #include "arith_uint256.h" #include "version.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index cfed5e347ed..45bcac68371 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -9,7 +9,6 @@ #include "streams.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 5279cb243ba..34069af450d 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -11,7 +11,6 @@ #include "serialize.h" #include "streams.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include "util.h" #include "utilstrencodings.h" #include "version.h" diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index 0b2fe0ef9db..9e09533b9be 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -5,7 +5,6 @@ #include "chain.h" #include "util.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 96f477319ad..b0cf199409b 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -9,11 +9,30 @@ #include "fs.h" #include "key.h" #include "pubkey.h" +#include "random.h" #include "txdb.h" #include "txmempool.h" #include +extern uint256 insecure_rand_seed; +extern FastRandomContext insecure_rand_ctx; + +static inline void seed_insecure_rand(bool fDeterministic = false) +{ + if (fDeterministic) { + insecure_rand_seed = uint256(); + } else { + insecure_rand_seed = GetRandHash(); + } + insecure_rand_ctx = FastRandomContext(insecure_rand_seed); +} + +static inline uint32_t insecure_rand(void) +{ + return insecure_rand_ctx.rand32(); +} + /** Basic testing setup. * This just configures logging and chain parameters. */ diff --git a/src/test/test_random.h b/src/test/test_random.h deleted file mode 100644 index 318c44df4dc..00000000000 --- a/src/test/test_random.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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_TEST_RANDOM_H -#define BITCOIN_TEST_RANDOM_H - -#include "random.h" - -extern uint256 insecure_rand_seed; -extern FastRandomContext insecure_rand_ctx; - -static inline void seed_insecure_rand(bool fDeterministic = false) -{ - if (fDeterministic) { - insecure_rand_seed = uint256(); - } else { - insecure_rand_seed = GetRandHash(); - } - insecure_rand_ctx = FastRandomContext(insecure_rand_seed); -} - -static inline uint32_t insecure_rand(void) -{ - return insecure_rand_ctx.rand32(); -} - -#endif diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 641655621cc..89f7025d064 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -10,7 +10,6 @@ #include "utilstrencodings.h" #include "utilmoneystr.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include #include diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 96d6845fcd4..10d2345bd3c 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -6,7 +6,6 @@ #include "chain.h" #include "versionbits.h" #include "test/test_bitcoin.h" -#include "test/test_random.h" #include "chainparams.h" #include "validation.h" #include "consensus/params.h" diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp index 2251d886fb6..eec916c9d90 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/crypto_tests.cpp @@ -2,9 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "test/test_random.h" -#include "utilstrencodings.h" #include "test/test_bitcoin.h" +#include "utilstrencodings.h" #include "wallet/crypter.h" #include From d378c5c13eba3113c454e5ae2a229fb399646ede Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 23 May 2017 15:00:46 -0700 Subject: [PATCH 23/60] Add various insecure_rand wrappers for tests Cherry-picked from: 1119927df03c94f9306e4d92f55d147b900522fb --- src/test/test_bitcoin.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index b0cf199409b..eecd8635e77 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -28,10 +28,12 @@ static inline void seed_insecure_rand(bool fDeterministic = false) insecure_rand_ctx = FastRandomContext(insecure_rand_seed); } -static inline uint32_t insecure_rand(void) -{ - return insecure_rand_ctx.rand32(); -} +static inline uint32_t insecure_rand() { return insecure_rand_ctx.rand32(); } +static inline uint256 insecure_rand256() { return insecure_rand_ctx.rand256(); } +static inline uint64_t insecure_randbits(int bits) { return insecure_rand_ctx.randbits(bits); } +static inline uint64_t insecure_randrange(uint64_t range) { return insecure_rand_ctx.randrange(range); } +static inline bool insecure_randbool() { return insecure_rand_ctx.randbool(); } +static inline std::vector insecure_randbytes(size_t len) { return insecure_rand_ctx.randbytes(len); } /** Basic testing setup. * This just configures logging and chain parameters. From c1ef69e61a17433ef6690afef08b7ade6f420364 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 27 Mar 2024 14:32:26 -0700 Subject: [PATCH 24/60] scripted-diff: use insecure_rand256/randrange more -BEGIN VERIFY SCRIPT- sed -i "s/\::iterator it; - it = mapOrphanTransactions.lower_bound(GetRandHash()); + it = mapOrphanTransactions.lower_bound(insecure_rand256()); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); return it->second.tx; @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; - tx.vin[0].prevout.hash = GetRandHash(); + tx.vin[0].prevout.hash = insecure_rand256(); tx.vin[0].scriptSig << OP_1; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 6d8e59bc4c8..4789152b569 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -30,16 +30,16 @@ static CBlock BuildBlockTestCase() { block.vtx.resize(3); block.vtx[0] = MakeTransactionRef(tx); block.nVersion = 1; - block.hashPrevBlock = GetRandHash(); + block.hashPrevBlock = insecure_rand256(); block.nBits = 0x207fffff; - tx.vin[0].prevout.hash = GetRandHash(); + tx.vin[0].prevout.hash = insecure_rand256(); tx.vin[0].prevout.n = 0; block.vtx[1] = MakeTransactionRef(tx); tx.vin.resize(10); for (size_t i = 0; i < tx.vin.size(); i++) { - tx.vin[i].prevout.hash = GetRandHash(); + tx.vin[i].prevout.hash = insecure_rand256(); tx.vin[i].prevout.n = 0; } block.vtx[2] = MakeTransactionRef(tx); @@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) block.vtx.resize(1); block.vtx[0] = MakeTransactionRef(std::move(coinbase)); block.nVersion = 1; - block.hashPrevBlock = GetRandHash(); + block.hashPrevBlock = insecure_rand256(); block.nBits = 0x207fffff; bool mutated; @@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) { BlockTransactionsRequest req1; - req1.blockhash = GetRandHash(); + req1.blockhash = insecure_rand256(); req1.indexes.resize(4); req1.indexes[0] = 0; req1.indexes[1] = 1; diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 4da07c12f89..5f3b21ca901 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -463,7 +463,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none) static std::vector RandomData() { - uint256 r = GetRandHash(); + uint256 r = insecure_rand256(); return std::vector(r.begin(), r.end()); } diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index af134ad31e9..2e7a4d1a53f 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -160,7 +160,7 @@ void Correct_Queue_range(std::vector range) FakeCheckCheckCompletion::n_calls = 0; CCheckQueueControl control(small_queue.get()); while (total) { - vChecks.resize(std::min(total, (size_t) GetRand(10))); + vChecks.resize(std::min(total, (size_t) insecure_randrange(10))); total -= vChecks.size(); control.Add(vChecks); } @@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) { std::vector range; range.reserve(100000/1000); - for (size_t i = 2; i < 100000; i += std::max((size_t)1, (size_t)GetRand(std::min((size_t)1000, ((size_t)100000) - i)))) + for (size_t i = 2; i < 100000; i += std::max((size_t)1, (size_t)insecure_randrange(std::min((size_t)1000, ((size_t)100000) - i)))) range.push_back(i); Correct_Queue_range(range); } @@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) CCheckQueueControl control(fail_queue.get()); size_t remaining = i; while (remaining) { - size_t r = GetRand(10); + size_t r = insecure_randrange(10); std::vector vChecks; vChecks.reserve(r); @@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) { CCheckQueueControl control(queue.get()); while (total) { - size_t r = GetRand(10); + size_t r = insecure_randrange(10); std::vector vChecks; for (size_t k = 0; k < r && total; k++) vChecks.emplace_back(--total); @@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) { CCheckQueueControl control(queue.get()); while (total) { - size_t r = GetRand(10); + size_t r = insecure_randrange(10); std::vector vChecks; for (size_t k = 0; k < r && total; k++) { total--; diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index ae85a1edad5..379a597b32e 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -34,7 +34,7 @@ class CCoinsViewTest : public CCoinsView return false; } coins = it->second; - if (coins.IsPruned() && insecure_rand() % 2 == 0) { + if (coins.IsPruned() && insecure_randrange(2) == 0) { // Randomly return false in case of an empty entry. return false; } @@ -55,7 +55,7 @@ class CCoinsViewTest : public CCoinsView if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Same optimization used in CCoinsViewDB is to only write dirty entries. map_[it->first] = it->second.coins; - if (it->second.coins.IsPruned() && insecure_rand() % 3 == 0) { + if (it->second.coins.IsPruned() && insecure_randrange(3) == 0) { // Randomly delete empty entries on write. map_.erase(it->first); } @@ -125,7 +125,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) std::vector txids; txids.resize(NUM_SIMULATION_ITERATIONS / 8); for (unsigned int i = 0; i < txids.size(); i++) { - txids[i] = GetRandHash(); + txids[i] = insecure_rand256(); } for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { @@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) CCoins& coins = result[txid]; CCoinsModifier entry = stack.back()->ModifyCoins(txid); BOOST_CHECK(coins == *entry); - if (insecure_rand() % 5 == 0 || coins.IsPruned()) { + if (insecure_randrange(5) == 0 || coins.IsPruned()) { if (coins.IsPruned()) { added_an_entry = true; } else { @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) } // Once every 1000 iterations and at the end, verify the full cache. - if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { + if (insecure_randrange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { for (std::map::iterator it = result.begin(); it != result.end(); it++) { const CCoins* coins = stack.back()->AccessCoins(it->first); if (coins) { @@ -169,22 +169,22 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) } } - if (insecure_rand() % 100 == 0) { + if (insecure_randrange(100) == 0) { // Every 100 iterations, flush an intermediate cache - if (stack.size() > 1 && insecure_rand() % 2 == 0) { + if (stack.size() > 1 && insecure_randrange(2) == 0) { unsigned int flushIndex = insecure_rand() % (stack.size() - 1); stack[flushIndex]->Flush(); } } - if (insecure_rand() % 100 == 0) { + if (insecure_randrange(100) == 0) { // Every 100 iterations, change the cache stack. - if (stack.size() > 0 && insecure_rand() % 2 == 0) { + if (stack.size() > 0 && insecure_randrange(2) == 0) { //Remove the top cache stack.back()->Flush(); delete stack.back(); stack.pop_back(); } - if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) { + if (stack.size() == 0 || (stack.size() < 4 && insecure_randrange(2))) { //Add a new cache CCoinsView* tip = &base; if (stack.size() > 0) { @@ -222,7 +222,7 @@ std::map alltxs; TxData &FindRandomFrom(const std::set &txidset) { assert(txidset.size()); - std::set::iterator txIt = txidset.lower_bound(GetRandHash()); + std::set::iterator txIt = txidset.lower_bound(insecure_rand256()); if (txIt == txidset.end()) { txIt = txidset.begin(); } @@ -269,7 +269,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) // 2/20 times create a new coinbase if (randiter % 20 < 2 || coinbaseids.size() < 10) { // 1/10 of those times create a duplicate coinbase - if (insecure_rand() % 10 == 0 && coinbaseids.size()) { + if (insecure_randrange(10) == 0 && coinbaseids.size()) { TxData &txd = FindRandomFrom(coinbaseids); // Reuse the exact same coinbase tx = std::get<0>(txd); @@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) } // Once every 1000 iterations and at the end, verify the full cache. - if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { + if (insecure_randrange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { for (std::map::iterator it = result.begin(); it != result.end(); it++) { const CCoins* coins = stack.back()->AccessCoins(it->first); if (coins) { @@ -395,21 +395,21 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) } } - if (insecure_rand() % 100 == 0) { + if (insecure_randrange(100) == 0) { // Every 100 iterations, flush an intermediate cache - if (stack.size() > 1 && insecure_rand() % 2 == 0) { + if (stack.size() > 1 && insecure_randrange(2) == 0) { unsigned int flushIndex = insecure_rand() % (stack.size() - 1); stack[flushIndex]->Flush(); } } - if (insecure_rand() % 100 == 0) { + if (insecure_randrange(100) == 0) { // Every 100 iterations, change the cache stack. - if (stack.size() > 0 && insecure_rand() % 2 == 0) { + if (stack.size() > 0 && insecure_randrange(2) == 0) { stack.back()->Flush(); delete stack.back(); stack.pop_back(); } - if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) { + if (stack.size() == 0 || (stack.size() < 4 && insecure_randrange(2))) { CCoinsView* tip = &base; if (stack.size() > 0) { tip = stack.back(); diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 9b25a58d537..2380ee20219 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper) fs::path ph = fs::temp_directory_path() / fs::unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'k'; - uint256 in = GetRandHash(); + uint256 in = insecure_rand256(); uint256 res; // Ensure that we're doing real obfuscation when obfuscate=true @@ -55,11 +55,11 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch) CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'i'; - uint256 in = GetRandHash(); + uint256 in = insecure_rand256(); char key2 = 'j'; - uint256 in2 = GetRandHash(); + uint256 in2 = insecure_rand256(); char key3 = 'k'; - uint256 in3 = GetRandHash(); + uint256 in3 = insecure_rand256(); uint256 res; CDBBatch batch(dbw); @@ -93,10 +93,10 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator) // The two keys are intentionally chosen for ordering char key = 'j'; - uint256 in = GetRandHash(); + uint256 in = insecure_rand256(); BOOST_CHECK(dbw.Write(key, in)); char key2 = 'k'; - uint256 in2 = GetRandHash(); + uint256 in2 = insecure_rand256(); BOOST_CHECK(dbw.Write(key2, in2)); std::unique_ptr it(const_cast(&dbw)->NewIterator()); @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) // Set up a non-obfuscated wrapper to write some initial data. CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false); char key = 'k'; - uint256 in = GetRandHash(); + uint256 in = insecure_rand256(); uint256 res; BOOST_CHECK(dbw->Write(key, in)); @@ -156,7 +156,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) BOOST_CHECK(!odbw.IsEmpty()); // There should be existing data BOOST_CHECK(is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); // The key should be an empty string - uint256 in2 = GetRandHash(); + uint256 in2 = insecure_rand256(); uint256 res3; // Check that we can write successfully @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) // Set up a non-obfuscated wrapper to write some initial data. CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false); char key = 'k'; - uint256 in = GetRandHash(); + uint256 in = insecure_rand256(); uint256 res; BOOST_CHECK(dbw->Write(key, in)); @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) BOOST_CHECK(!odbw.Read(key, res2)); BOOST_CHECK(!is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); - uint256 in2 = GetRandHash(); + uint256 in2 = insecure_rand256(); uint256 res3; // Check that we can write successfully diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 2db510d6d39..011e7217c3b 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -67,7 +67,7 @@ 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 + (insecure_rand() % 4000); + int ntx = (i <= 16) ? i : 17 + (insecure_randrange(4000)); // Try up to 3 mutations. for (int mutate = 0; mutate <= 3; mutate++) { int duplicate1 = mutate >= 1 ? 1 << ctz(ntx) : 0; // The last how many transactions to duplicate first. diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 2f55b498631..77eded091e5 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -363,7 +363,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) while (chainActive.Tip()->nHeight < 209999) { CBlockIndex* prev = chainActive.Tip(); CBlockIndex* next = new CBlockIndex(); - next->phashBlock = new uint256(GetRandHash()); + next->phashBlock = new uint256(insecure_rand256()); pcoinsTip->SetBestBlock(next->GetBlockHash()); next->pprev = prev; next->nHeight = prev->nHeight + 1; @@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) while (chainActive.Tip()->nHeight < 210000) { CBlockIndex* prev = chainActive.Tip(); CBlockIndex* next = new CBlockIndex(); - next->phashBlock = new uint256(GetRandHash()); + next->phashBlock = new uint256(insecure_rand256()); pcoinsTip->SetBestBlock(next->GetBlockHash()); next->pprev = prev; next->nHeight = prev->nHeight + 1; diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index e3a4f90aaff..3572505af39 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -22,7 +22,7 @@ class CPartialMerkleTreeTester : public CPartialMerkleTree // flip one bit in one of the hashes - this should break the authentication void Damage() { unsigned int n = insecure_rand() % vHash.size(); - int bit = insecure_rand() % 256; + int bit = insecure_randrange(256); *(vHash[n].begin() + (bit>>3)) ^= 1<<(bit&7); } }; diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index cff396673b0..d881edbd709 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -86,9 +86,9 @@ BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test) } for (int j = 0; j < 1000; j++) { - CBlockIndex *p1 = &blocks[GetRand(10000)]; - CBlockIndex *p2 = &blocks[GetRand(10000)]; - CBlockIndex *p3 = &blocks[GetRand(10000)]; + CBlockIndex *p1 = &blocks[insecure_randrange(10000)]; + CBlockIndex *p2 = &blocks[insecure_randrange(10000)]; + CBlockIndex *p3 = &blocks[insecure_randrange(10000)]; int64_t tdiff = GetBlockProofEquivalentTime(*p1, *p2, *p3, params); BOOST_CHECK_EQUAL(tdiff, p1->GetBlockTime() - p2->GetBlockTime()); diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index 45bcac68371..28f72a9b91a 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -206,14 +206,14 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) test.erase(insecure_rand() % test.size()); } if (((r >> 4) % 8) == 2) { - int new_size = std::max(0, std::min(30, test.size() + (insecure_rand() % 5) - 2)); + int new_size = std::max(0, std::min(30, test.size() + (insecure_randrange(5)) - 2)); test.resize(new_size); } if (((r >> 7) % 8) == 3) { - test.insert(insecure_rand() % (test.size() + 1), 1 + (insecure_rand() % 2), insecure_rand()); + test.insert(insecure_rand() % (test.size() + 1), 1 + (insecure_randrange(2)), insecure_rand()); } if (((r >> 10) % 8) == 4) { - int del = std::min(test.size(), 1 + (insecure_rand() % 2)); + int del = std::min(test.size(), 1 + (insecure_randrange(2))); int beg = insecure_rand() % (test.size() + 1 - del); test.erase(beg, beg + del); } @@ -225,20 +225,20 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) } if (((r >> 21) % 32) == 7) { int values[4]; - int num = 1 + (insecure_rand() % 4); + int num = 1 + (insecure_randrange(4)); for (int k = 0; k < num; k++) { values[k] = insecure_rand(); } test.insert_range(insecure_rand() % (test.size() + 1), values, values + num); } if (((r >> 26) % 32) == 8) { - int del = std::min(test.size(), 1 + (insecure_rand() % 4)); + int del = std::min(test.size(), 1 + (insecure_randrange(4))); int beg = insecure_rand() % (test.size() + 1 - del); test.erase(beg, beg + del); } r = insecure_rand(); if (r % 32 == 9) { - test.reserve(insecure_rand() % 32); + test.reserve(insecure_randrange(32)); } if ((r >> 5) % 64 == 10) { test.shrink_to_fit(); @@ -250,7 +250,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) test.clear(); } if (((r >> 21) % 512) == 12) { - test.assign(insecure_rand() % 32, insecure_rand()); + test.assign(insecure_randrange(32), insecure_rand()); } if (((r >> 15) % 8) == 3) { test.swap(); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 34069af450d..03a7d9ca6cd 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -89,7 +89,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un void static RandomScript(CScript &script) { static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR}; script = CScript(); - int ops = (insecure_rand() % 10); + int ops = (insecure_randrange(10)); for (int i=0; i Date: Wed, 7 Jun 2017 11:34:55 -0700 Subject: [PATCH 25/60] Replace more rand() % NUM by randranges Cherry-picked from: 3ecabae36364e905e7821fba3e60aa7f8418de6c --- src/test/coins_tests.cpp | 6 +++--- src/test/crypto_tests.cpp | 2 +- src/test/merkle_tests.cpp | 2 +- src/test/pmt_tests.cpp | 2 +- src/test/prevector_tests.cpp | 14 +++++++------- src/test/sighash_tests.cpp | 4 ++-- src/test/skiplist_tests.cpp | 8 ++++---- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 379a597b32e..fddd0dfe263 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -131,7 +131,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { // Do a random modification. { - uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration. + uint256 txid = txids[insecure_randrange(500) % txids.size()]; // txid we're going to modify in this iteration. CCoins& coins = result[txid]; CCoinsModifier entry = stack.back()->ModifyCoins(txid); BOOST_CHECK(coins == *entry); @@ -172,7 +172,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) if (insecure_randrange(100) == 0) { // Every 100 iterations, flush an intermediate cache if (stack.size() > 1 && insecure_randrange(2) == 0) { - unsigned int flushIndex = insecure_rand() % (stack.size() - 1); + unsigned int flushIndex = insecure_randrange(stack.size() - 1); stack[flushIndex]->Flush(); } } @@ -398,7 +398,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) if (insecure_randrange(100) == 0) { // Every 100 iterations, flush an intermediate cache if (stack.size() > 1 && insecure_randrange(2) == 0) { - unsigned int flushIndex = insecure_rand() % (stack.size() - 1); + unsigned int flushIndex = insecure_randrange(stack.size() - 1); stack[flushIndex]->Flush(); } } diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 4b76cc1021b..ab3e6fc1fd3 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -38,7 +38,7 @@ void TestVector(const Hasher &h, const In &in, const Out &out) { Hasher hasher(h); size_t pos = 0; while (pos < in.size()) { - size_t len = insecure_rand() % ((in.size() - pos + 1) / 2 + 1); + size_t len = insecure_randrange((in.size() - pos + 1) / 2 + 1); hasher.Write((unsigned char*)&in[pos], len); pos += len; if (pos > 0 && pos + 2 * out.size() > in.size() && pos < in.size()) { diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 011e7217c3b..118f2a66a43 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE(merkle_test) // If ntx <= 16, try all branches. Otherise, try 16 random ones. int mtx = loop; if (ntx > 16) { - mtx = insecure_rand() % ntx; + mtx = insecure_randrange(ntx); } std::vector newBranch = BlockMerkleBranch(block, mtx); std::vector oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx); diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index 3572505af39..d07a9f3959e 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -21,7 +21,7 @@ class CPartialMerkleTreeTester : public CPartialMerkleTree public: // flip one bit in one of the hashes - this should break the authentication void Damage() { - unsigned int n = insecure_rand() % vHash.size(); + unsigned int n = insecure_randrange(vHash.size()); int bit = insecure_randrange(256); *(vHash[n].begin() + (bit>>3)) ^= 1<<(bit&7); } diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index 28f72a9b91a..d951a6c3c2b 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -200,21 +200,21 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) for (int i = 0; i < 2048; i++) { int r = insecure_rand(); if ((r % 4) == 0) { - test.insert(insecure_rand() % (test.size() + 1), insecure_rand()); + test.insert(insecure_randrange(test.size() + 1), insecure_rand()); } if (test.size() > 0 && ((r >> 2) % 4) == 1) { - test.erase(insecure_rand() % test.size()); + test.erase(insecure_randrange(test.size())); } if (((r >> 4) % 8) == 2) { int new_size = std::max(0, std::min(30, test.size() + (insecure_randrange(5)) - 2)); test.resize(new_size); } if (((r >> 7) % 8) == 3) { - test.insert(insecure_rand() % (test.size() + 1), 1 + (insecure_randrange(2)), insecure_rand()); + test.insert(insecure_randrange(test.size() + 1), 1 + insecure_randrange(2), insecure_rand()); } if (((r >> 10) % 8) == 4) { int del = std::min(test.size(), 1 + (insecure_randrange(2))); - int beg = insecure_rand() % (test.size() + 1 - del); + int beg = insecure_randrange(test.size() + 1 - del); test.erase(beg, beg + del); } if (((r >> 13) % 16) == 5) { @@ -229,11 +229,11 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) for (int k = 0; k < num; k++) { values[k] = insecure_rand(); } - test.insert_range(insecure_rand() % (test.size() + 1), values, values + num); + test.insert_range(insecure_randrange(test.size() + 1), values, values + num); } if (((r >> 26) % 32) == 8) { int del = std::min(test.size(), 1 + (insecure_randrange(4))); - int beg = insecure_rand() % (test.size() + 1 - del); + int beg = insecure_randrange(test.size() + 1 - del); test.erase(beg, beg + del); } r = insecure_rand(); @@ -244,7 +244,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) test.shrink_to_fit(); } if (test.size() > 0) { - test.update(insecure_rand() % test.size(), insecure_rand()); + test.update(insecure_randrange(test.size()), insecure_rand()); } if (((r >> 11) % 1024) == 11) { test.clear(); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 03a7d9ca6cd..d65b057c60e 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -91,7 +91,7 @@ void static RandomScript(CScript &script) { script = CScript(); int ops = (insecure_randrange(10)); for (int i=0; inTimeMax >= test_time); From 4ef34f531a802f54c335f9ae2643952e66298c9b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 7 Jun 2017 11:34:58 -0700 Subject: [PATCH 26/60] Replace rand() & ((1 << N) - 1) with randbits(N) Cherry-picked from: 5f0b04eedc5bbdb9319c9f1f1a6c599337f5bbe3 --- src/test/pmt_tests.cpp | 2 +- src/test/versionbits_tests.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index d07a9f3959e..32086ead5e8 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(pmt_test1) std::vector vMatch(nTx, false); std::vector vMatchTxid1; for (unsigned int j=0; j Date: Tue, 23 May 2017 16:14:51 -0700 Subject: [PATCH 27/60] Use randbits instead of ad-hoc emulation in prevector tests Cherry-picked from: 2ada67852174a76753080d65a7adbe27241a9caa --- src/test/prevector_tests.cpp | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index d951a6c3c2b..674221e0093 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -198,32 +198,31 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) for (int j = 0; j < 64; j++) { prevector_tester<8, int> test; for (int i = 0; i < 2048; i++) { - int r = insecure_rand(); - if ((r % 4) == 0) { + if (insecure_randbits(2) == 0) { test.insert(insecure_randrange(test.size() + 1), insecure_rand()); } - if (test.size() > 0 && ((r >> 2) % 4) == 1) { + if (test.size() > 0 && insecure_randbits(2) == 1) { test.erase(insecure_randrange(test.size())); } - if (((r >> 4) % 8) == 2) { + if (insecure_randbits(3) == 2) { int new_size = std::max(0, std::min(30, test.size() + (insecure_randrange(5)) - 2)); test.resize(new_size); } - if (((r >> 7) % 8) == 3) { + if (insecure_randbits(3) == 3) { test.insert(insecure_randrange(test.size() + 1), 1 + insecure_randrange(2), insecure_rand()); } - if (((r >> 10) % 8) == 4) { + if (insecure_randbits(3) == 4) { int del = std::min(test.size(), 1 + (insecure_randrange(2))); int beg = insecure_randrange(test.size() + 1 - del); test.erase(beg, beg + del); } - if (((r >> 13) % 16) == 5) { + if (insecure_randbits(4) == 5) { test.push_back(insecure_rand()); } - if (test.size() > 0 && ((r >> 17) % 16) == 6) { + if (test.size() > 0 && insecure_randbits(4) == 6) { test.pop_back(); } - if (((r >> 21) % 32) == 7) { + if (insecure_randbits(5) == 7) { int values[4]; int num = 1 + (insecure_randrange(4)); for (int k = 0; k < num; k++) { @@ -231,34 +230,33 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) } test.insert_range(insecure_randrange(test.size() + 1), values, values + num); } - if (((r >> 26) % 32) == 8) { + if (insecure_randbits(5) == 8) { int del = std::min(test.size(), 1 + (insecure_randrange(4))); int beg = insecure_randrange(test.size() + 1 - del); test.erase(beg, beg + del); } - r = insecure_rand(); - if (r % 32 == 9) { + if (insecure_randbits(5) == 9) { test.reserve(insecure_randrange(32)); } - if ((r >> 5) % 64 == 10) { + if (insecure_randbits(6) == 10) { test.shrink_to_fit(); } if (test.size() > 0) { test.update(insecure_randrange(test.size()), insecure_rand()); } - if (((r >> 11) % 1024) == 11) { + if (insecure_randbits(10) == 11) { test.clear(); } - if (((r >> 21) % 512) == 12) { + if (insecure_randbits(9) == 12) { test.assign(insecure_randrange(32), insecure_rand()); } - if (((r >> 15) % 8) == 3) { + if (insecure_randbits(3) == 3) { test.swap(); } - if (((r >> 15) % 16) == 8) { + if (insecure_randbits(4) == 8) { test.copy(); } - if (((r >> 15) % 32) == 18) { + if (insecure_randbits(5) == 18) { test.move(); } } From e037f9c9fbbf79c2863c746de9b3f2ccf2f38d13 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 23 May 2017 16:18:06 -0700 Subject: [PATCH 28/60] scripted-diff: Use randbits/bool instead of randrange where possible -BEGIN VERIFY SCRIPT- sed -i 's/insecure_randbits(1)/insecure_randbool()/g' src/test/*_tests.cpp sed -i 's/insecure_randrange(2)/insecure_randbool()/g' src/test/*_tests.cpp sed -i 's/insecure_randrange(4)/insecure_randbits(2)/g' src/test/*_tests.cpp sed -i 's/insecure_randrange(32)/insecure_randbits(5)/g' src/test/*_tests.cpp sed -i 's/insecure_randrange(256)/insecure_randbits(8)/g' src/test/*_tests.cpp -END VERIFY SCRIPT- Cherry-picked from: 2fcd9cc86bfce944e3312e9a24685403250f3bdc --- src/test/coins_tests.cpp | 14 +++++++------- src/test/pmt_tests.cpp | 2 +- src/test/prevector_tests.cpp | 12 ++++++------ src/test/sighash_tests.cpp | 10 +++++----- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index fddd0dfe263..aaf665403c4 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -34,7 +34,7 @@ class CCoinsViewTest : public CCoinsView return false; } coins = it->second; - if (coins.IsPruned() && insecure_randrange(2) == 0) { + if (coins.IsPruned() && insecure_randbool() == 0) { // Randomly return false in case of an empty entry. return false; } @@ -171,20 +171,20 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) if (insecure_randrange(100) == 0) { // Every 100 iterations, flush an intermediate cache - if (stack.size() > 1 && insecure_randrange(2) == 0) { + if (stack.size() > 1 && insecure_randbool() == 0) { unsigned int flushIndex = insecure_randrange(stack.size() - 1); stack[flushIndex]->Flush(); } } if (insecure_randrange(100) == 0) { // Every 100 iterations, change the cache stack. - if (stack.size() > 0 && insecure_randrange(2) == 0) { + if (stack.size() > 0 && insecure_randbool() == 0) { //Remove the top cache stack.back()->Flush(); delete stack.back(); stack.pop_back(); } - if (stack.size() == 0 || (stack.size() < 4 && insecure_randrange(2))) { + if (stack.size() == 0 || (stack.size() < 4 && insecure_randbool())) { //Add a new cache CCoinsView* tip = &base; if (stack.size() > 0) { @@ -397,19 +397,19 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) if (insecure_randrange(100) == 0) { // Every 100 iterations, flush an intermediate cache - if (stack.size() > 1 && insecure_randrange(2) == 0) { + if (stack.size() > 1 && insecure_randbool() == 0) { unsigned int flushIndex = insecure_randrange(stack.size() - 1); stack[flushIndex]->Flush(); } } if (insecure_randrange(100) == 0) { // Every 100 iterations, change the cache stack. - if (stack.size() > 0 && insecure_randrange(2) == 0) { + if (stack.size() > 0 && insecure_randbool() == 0) { stack.back()->Flush(); delete stack.back(); stack.pop_back(); } - if (stack.size() == 0 || (stack.size() < 4 && insecure_randrange(2))) { + if (stack.size() == 0 || (stack.size() < 4 && insecure_randbool())) { CCoinsView* tip = &base; if (stack.size() > 0) { tip = stack.back(); diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index 32086ead5e8..40893741577 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -22,7 +22,7 @@ class CPartialMerkleTreeTester : public CPartialMerkleTree // flip one bit in one of the hashes - this should break the authentication void Damage() { unsigned int n = insecure_randrange(vHash.size()); - int bit = insecure_randrange(256); + int bit = insecure_randbits(8); *(vHash[n].begin() + (bit>>3)) ^= 1<<(bit&7); } }; diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index 674221e0093..5d9d1872682 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -209,10 +209,10 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) test.resize(new_size); } if (insecure_randbits(3) == 3) { - test.insert(insecure_randrange(test.size() + 1), 1 + insecure_randrange(2), insecure_rand()); + test.insert(insecure_randrange(test.size() + 1), 1 + insecure_randbool(), insecure_rand()); } if (insecure_randbits(3) == 4) { - int del = std::min(test.size(), 1 + (insecure_randrange(2))); + int del = std::min(test.size(), 1 + (insecure_randbool())); int beg = insecure_randrange(test.size() + 1 - del); test.erase(beg, beg + del); } @@ -224,19 +224,19 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) } if (insecure_randbits(5) == 7) { int values[4]; - int num = 1 + (insecure_randrange(4)); + int num = 1 + (insecure_randbits(2)); for (int k = 0; k < num; k++) { values[k] = insecure_rand(); } test.insert_range(insecure_randrange(test.size() + 1), values, values + num); } if (insecure_randbits(5) == 8) { - int del = std::min(test.size(), 1 + (insecure_randrange(4))); + int del = std::min(test.size(), 1 + (insecure_randbits(2))); int beg = insecure_randrange(test.size() + 1 - del); test.erase(beg, beg + del); } if (insecure_randbits(5) == 9) { - test.reserve(insecure_randrange(32)); + test.reserve(insecure_randbits(5)); } if (insecure_randbits(6) == 10) { test.shrink_to_fit(); @@ -248,7 +248,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) test.clear(); } if (insecure_randbits(9) == 12) { - test.assign(insecure_randrange(32), insecure_rand()); + test.assign(insecure_randbits(5), insecure_rand()); } if (insecure_randbits(3) == 3) { test.swap(); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index d65b057c60e..c2813286adf 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -98,16 +98,16 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) { tx.nVersion = insecure_rand(); tx.vin.clear(); tx.vout.clear(); - tx.nLockTime = (insecure_randrange(2)) ? insecure_rand() : 0; - int ins = (insecure_randrange(4)) + 1; - int outs = fSingle ? ins : (insecure_randrange(4)) + 1; + tx.nLockTime = (insecure_randbool()) ? insecure_rand() : 0; + int ins = (insecure_randbits(2)) + 1; + int outs = fSingle ? ins : (insecure_randbits(2)) + 1; for (int in = 0; in < ins; in++) { tx.vin.push_back(CTxIn()); CTxIn &txin = tx.vin.back(); txin.prevout.hash = insecure_rand256(); - txin.prevout.n = insecure_randrange(4); + txin.prevout.n = insecure_randbits(2); RandomScript(txin.scriptSig); - txin.nSequence = (insecure_randrange(2)) ? insecure_rand() : (unsigned int)-1; + txin.nSequence = (insecure_randbool()) ? insecure_rand() : (unsigned int)-1; } for (int out = 0; out < outs; out++) { tx.vout.push_back(CTxOut()); From 1316bf0e47b836d5539b50210eb23dbb5d850ed0 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 7 Jun 2017 12:03:17 -0700 Subject: [PATCH 29/60] scripted-diff: Use new naming style for insecure_rand* functions -BEGIN VERIFY SCRIPT- sed -i 's/\::iterator it; - it = mapOrphanTransactions.lower_bound(insecure_rand256()); + it = mapOrphanTransactions.lower_bound(InsecureRand256()); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); return it->second.tx; @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; - tx.vin[0].prevout.hash = insecure_rand256(); + tx.vin[0].prevout.hash = InsecureRand256(); tx.vin[0].scriptSig << OP_1; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 4789152b569..59a052f3f7d 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -30,16 +30,16 @@ static CBlock BuildBlockTestCase() { block.vtx.resize(3); block.vtx[0] = MakeTransactionRef(tx); block.nVersion = 1; - block.hashPrevBlock = insecure_rand256(); + block.hashPrevBlock = InsecureRand256(); block.nBits = 0x207fffff; - tx.vin[0].prevout.hash = insecure_rand256(); + tx.vin[0].prevout.hash = InsecureRand256(); tx.vin[0].prevout.n = 0; block.vtx[1] = MakeTransactionRef(tx); tx.vin.resize(10); for (size_t i = 0; i < tx.vin.size(); i++) { - tx.vin[i].prevout.hash = insecure_rand256(); + tx.vin[i].prevout.hash = InsecureRand256(); tx.vin[i].prevout.n = 0; } block.vtx[2] = MakeTransactionRef(tx); @@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) block.vtx.resize(1); block.vtx[0] = MakeTransactionRef(std::move(coinbase)); block.nVersion = 1; - block.hashPrevBlock = insecure_rand256(); + block.hashPrevBlock = InsecureRand256(); block.nBits = 0x207fffff; bool mutated; @@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) { BlockTransactionsRequest req1; - req1.blockhash = insecure_rand256(); + req1.blockhash = InsecureRand256(); req1.indexes.resize(4); req1.indexes[0] = 0; req1.indexes[1] = 1; diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 5f3b21ca901..29bc5dd1228 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -463,7 +463,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none) static std::vector RandomData() { - uint256 r = insecure_rand256(); + uint256 r = InsecureRand256(); return std::vector(r.begin(), r.end()); } diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 2e7a4d1a53f..36b8768cd36 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -160,7 +160,7 @@ void Correct_Queue_range(std::vector range) FakeCheckCheckCompletion::n_calls = 0; CCheckQueueControl control(small_queue.get()); while (total) { - vChecks.resize(std::min(total, (size_t) insecure_randrange(10))); + vChecks.resize(std::min(total, (size_t) InsecureRandRange(10))); total -= vChecks.size(); control.Add(vChecks); } @@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) { std::vector range; range.reserve(100000/1000); - for (size_t i = 2; i < 100000; i += std::max((size_t)1, (size_t)insecure_randrange(std::min((size_t)1000, ((size_t)100000) - i)))) + for (size_t i = 2; i < 100000; i += std::max((size_t)1, (size_t)InsecureRandRange(std::min((size_t)1000, ((size_t)100000) - i)))) range.push_back(i); Correct_Queue_range(range); } @@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) CCheckQueueControl control(fail_queue.get()); size_t remaining = i; while (remaining) { - size_t r = insecure_randrange(10); + size_t r = InsecureRandRange(10); std::vector vChecks; vChecks.reserve(r); @@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) { CCheckQueueControl control(queue.get()); while (total) { - size_t r = insecure_randrange(10); + size_t r = InsecureRandRange(10); std::vector vChecks; for (size_t k = 0; k < r && total; k++) vChecks.emplace_back(--total); @@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) { CCheckQueueControl control(queue.get()); while (total) { - size_t r = insecure_randrange(10); + size_t r = InsecureRandRange(10); std::vector vChecks; for (size_t k = 0; k < r && total; k++) { total--; diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index aaf665403c4..f480b3c9ff8 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -34,7 +34,7 @@ class CCoinsViewTest : public CCoinsView return false; } coins = it->second; - if (coins.IsPruned() && insecure_randbool() == 0) { + if (coins.IsPruned() && InsecureRandBool() == 0) { // Randomly return false in case of an empty entry. return false; } @@ -55,7 +55,7 @@ class CCoinsViewTest : public CCoinsView if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Same optimization used in CCoinsViewDB is to only write dirty entries. map_[it->first] = it->second.coins; - if (it->second.coins.IsPruned() && insecure_randrange(3) == 0) { + if (it->second.coins.IsPruned() && InsecureRandRange(3) == 0) { // Randomly delete empty entries on write. map_.erase(it->first); } @@ -125,25 +125,25 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) std::vector txids; txids.resize(NUM_SIMULATION_ITERATIONS / 8); for (unsigned int i = 0; i < txids.size(); i++) { - txids[i] = insecure_rand256(); + txids[i] = InsecureRand256(); } for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { // Do a random modification. { - uint256 txid = txids[insecure_randrange(500) % txids.size()]; // txid we're going to modify in this iteration. + uint256 txid = txids[InsecureRandRange(500) % txids.size()]; // txid we're going to modify in this iteration. CCoins& coins = result[txid]; CCoinsModifier entry = stack.back()->ModifyCoins(txid); BOOST_CHECK(coins == *entry); - if (insecure_randrange(5) == 0 || coins.IsPruned()) { + if (InsecureRandRange(5) == 0 || coins.IsPruned()) { if (coins.IsPruned()) { added_an_entry = true; } else { updated_an_entry = true; } - coins.nVersion = insecure_rand(); + coins.nVersion = InsecureRand32(); coins.vout.resize(1); - coins.vout[0].nValue = insecure_rand(); + coins.vout[0].nValue = InsecureRand32(); *entry = coins; } else { coins.Clear(); @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) } // Once every 1000 iterations and at the end, verify the full cache. - if (insecure_randrange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { + if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { for (std::map::iterator it = result.begin(); it != result.end(); it++) { const CCoins* coins = stack.back()->AccessCoins(it->first); if (coins) { @@ -169,22 +169,22 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) } } - if (insecure_randrange(100) == 0) { + if (InsecureRandRange(100) == 0) { // Every 100 iterations, flush an intermediate cache - if (stack.size() > 1 && insecure_randbool() == 0) { - unsigned int flushIndex = insecure_randrange(stack.size() - 1); + if (stack.size() > 1 && InsecureRandBool() == 0) { + unsigned int flushIndex = InsecureRandRange(stack.size() - 1); stack[flushIndex]->Flush(); } } - if (insecure_randrange(100) == 0) { + if (InsecureRandRange(100) == 0) { // Every 100 iterations, change the cache stack. - if (stack.size() > 0 && insecure_randbool() == 0) { + if (stack.size() > 0 && InsecureRandBool() == 0) { //Remove the top cache stack.back()->Flush(); delete stack.back(); stack.pop_back(); } - if (stack.size() == 0 || (stack.size() < 4 && insecure_randbool())) { + if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) { //Add a new cache CCoinsView* tip = &base; if (stack.size() > 0) { @@ -222,7 +222,7 @@ std::map alltxs; TxData &FindRandomFrom(const std::set &txidset) { assert(txidset.size()); - std::set::iterator txIt = txidset.lower_bound(insecure_rand256()); + std::set::iterator txIt = txidset.lower_bound(InsecureRand256()); if (txIt == txidset.end()) { txIt = txidset.begin(); } @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) std::set utxoset; for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { - uint32_t randiter = insecure_rand(); + uint32_t randiter = InsecureRand32(); // 19/20 txs add a new transaction if (randiter % 20 < 19) { @@ -263,13 +263,13 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) tx.vin.resize(1); tx.vout.resize(1); tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate - unsigned int height = insecure_rand(); + unsigned int height = InsecureRand32(); CCoins oldcoins; // 2/20 times create a new coinbase if (randiter % 20 < 2 || coinbaseids.size() < 10) { // 1/10 of those times create a duplicate coinbase - if (insecure_randrange(10) == 0 && coinbaseids.size()) { + if (InsecureRandRange(10) == 0 && coinbaseids.size()) { TxData &txd = FindRandomFrom(coinbaseids); // Reuse the exact same coinbase tx = std::get<0>(txd); @@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) } // Once every 1000 iterations and at the end, verify the full cache. - if (insecure_randrange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { + if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { for (std::map::iterator it = result.begin(); it != result.end(); it++) { const CCoins* coins = stack.back()->AccessCoins(it->first); if (coins) { @@ -395,21 +395,21 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) } } - if (insecure_randrange(100) == 0) { + if (InsecureRandRange(100) == 0) { // Every 100 iterations, flush an intermediate cache - if (stack.size() > 1 && insecure_randbool() == 0) { - unsigned int flushIndex = insecure_randrange(stack.size() - 1); + if (stack.size() > 1 && InsecureRandBool() == 0) { + unsigned int flushIndex = InsecureRandRange(stack.size() - 1); stack[flushIndex]->Flush(); } } - if (insecure_randrange(100) == 0) { + if (InsecureRandRange(100) == 0) { // Every 100 iterations, change the cache stack. - if (stack.size() > 0 && insecure_randbool() == 0) { + if (stack.size() > 0 && InsecureRandBool() == 0) { stack.back()->Flush(); delete stack.back(); stack.pop_back(); } - if (stack.size() == 0 || (stack.size() < 4 && insecure_randbool())) { + if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) { CCoinsView* tip = &base; if (stack.size() > 0) { tip = stack.back(); diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index ab3e6fc1fd3..a4f0e17c997 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -38,7 +38,7 @@ void TestVector(const Hasher &h, const In &in, const Out &out) { Hasher hasher(h); size_t pos = 0; while (pos < in.size()) { - size_t len = insecure_randrange((in.size() - pos + 1) / 2 + 1); + size_t len = InsecureRandRange((in.size() - pos + 1) / 2 + 1); hasher.Write((unsigned char*)&in[pos], len); pos += len; if (pos > 0 && pos + 2 * out.size() > in.size() && pos < in.size()) { diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 2380ee20219..34692c39999 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper) fs::path ph = fs::temp_directory_path() / fs::unique_path(); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'k'; - uint256 in = insecure_rand256(); + uint256 in = InsecureRand256(); uint256 res; // Ensure that we're doing real obfuscation when obfuscate=true @@ -55,11 +55,11 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch) CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'i'; - uint256 in = insecure_rand256(); + uint256 in = InsecureRand256(); char key2 = 'j'; - uint256 in2 = insecure_rand256(); + uint256 in2 = InsecureRand256(); char key3 = 'k'; - uint256 in3 = insecure_rand256(); + uint256 in3 = InsecureRand256(); uint256 res; CDBBatch batch(dbw); @@ -93,10 +93,10 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator) // The two keys are intentionally chosen for ordering char key = 'j'; - uint256 in = insecure_rand256(); + uint256 in = InsecureRand256(); BOOST_CHECK(dbw.Write(key, in)); char key2 = 'k'; - uint256 in2 = insecure_rand256(); + uint256 in2 = InsecureRand256(); BOOST_CHECK(dbw.Write(key2, in2)); std::unique_ptr it(const_cast(&dbw)->NewIterator()); @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) // Set up a non-obfuscated wrapper to write some initial data. CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false); char key = 'k'; - uint256 in = insecure_rand256(); + uint256 in = InsecureRand256(); uint256 res; BOOST_CHECK(dbw->Write(key, in)); @@ -156,7 +156,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) BOOST_CHECK(!odbw.IsEmpty()); // There should be existing data BOOST_CHECK(is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); // The key should be an empty string - uint256 in2 = insecure_rand256(); + uint256 in2 = InsecureRand256(); uint256 res3; // Check that we can write successfully @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) // Set up a non-obfuscated wrapper to write some initial data. CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false); char key = 'k'; - uint256 in = insecure_rand256(); + uint256 in = InsecureRand256(); uint256 res; BOOST_CHECK(dbw->Write(key, in)); @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) BOOST_CHECK(!odbw.Read(key, res2)); BOOST_CHECK(!is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); - uint256 in2 = insecure_rand256(); + uint256 in2 = InsecureRand256(); uint256 res3; // Check that we can write successfully diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 118f2a66a43..aae84a4fd9d 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -67,7 +67,7 @@ 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 + (insecure_randrange(4000)); + int ntx = (i <= 16) ? i : 17 + (InsecureRandRange(4000)); // Try up to 3 mutations. for (int mutate = 0; mutate <= 3; mutate++) { int duplicate1 = mutate >= 1 ? 1 << ctz(ntx) : 0; // The last how many transactions to duplicate first. @@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE(merkle_test) // If ntx <= 16, try all branches. Otherise, try 16 random ones. int mtx = loop; if (ntx > 16) { - mtx = insecure_randrange(ntx); + mtx = InsecureRandRange(ntx); } std::vector newBranch = BlockMerkleBranch(block, mtx); std::vector oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 77eded091e5..53a9874fa81 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -363,7 +363,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) while (chainActive.Tip()->nHeight < 209999) { CBlockIndex* prev = chainActive.Tip(); CBlockIndex* next = new CBlockIndex(); - next->phashBlock = new uint256(insecure_rand256()); + next->phashBlock = new uint256(InsecureRand256()); pcoinsTip->SetBestBlock(next->GetBlockHash()); next->pprev = prev; next->nHeight = prev->nHeight + 1; @@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) while (chainActive.Tip()->nHeight < 210000) { CBlockIndex* prev = chainActive.Tip(); CBlockIndex* next = new CBlockIndex(); - next->phashBlock = new uint256(insecure_rand256()); + next->phashBlock = new uint256(InsecureRand256()); pcoinsTip->SetBestBlock(next->GetBlockHash()); next->pprev = prev; next->nHeight = prev->nHeight + 1; diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index 40893741577..708a9ca508a 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -21,8 +21,8 @@ class CPartialMerkleTreeTester : public CPartialMerkleTree public: // flip one bit in one of the hashes - this should break the authentication void Damage() { - unsigned int n = insecure_randrange(vHash.size()); - int bit = insecure_randbits(8); + unsigned int n = InsecureRandRange(vHash.size()); + int bit = InsecureRandBits(8); *(vHash[n].begin() + (bit>>3)) ^= 1<<(bit&7); } }; @@ -31,7 +31,7 @@ BOOST_FIXTURE_TEST_SUITE(pmt_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(pmt_test1) { - seed_insecure_rand(false); + SeedInsecureRand(false); static const unsigned int nTxCounts[] = {1, 4, 7, 17, 56, 100, 127, 256, 312, 513, 1000, 4095}; for (int i = 0; i < 12; i++) { @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(pmt_test1) std::vector vMatch(nTx, false); std::vector vMatchTxid1; for (unsigned int j=0; jGetBlockTime() - p2->GetBlockTime()); diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index 5d9d1872682..354fed1c1da 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -187,7 +187,7 @@ class prevector_tester { } prevector_tester() { - seed_insecure_rand(); + SeedInsecureRand(); rand_seed = insecure_rand_seed; rand_cache = insecure_rand_ctx; } @@ -198,65 +198,65 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) for (int j = 0; j < 64; j++) { prevector_tester<8, int> test; for (int i = 0; i < 2048; i++) { - if (insecure_randbits(2) == 0) { - test.insert(insecure_randrange(test.size() + 1), insecure_rand()); + if (InsecureRandBits(2) == 0) { + test.insert(InsecureRandRange(test.size() + 1), InsecureRand32()); } - if (test.size() > 0 && insecure_randbits(2) == 1) { - test.erase(insecure_randrange(test.size())); + if (test.size() > 0 && InsecureRandBits(2) == 1) { + test.erase(InsecureRandRange(test.size())); } - if (insecure_randbits(3) == 2) { - int new_size = std::max(0, std::min(30, test.size() + (insecure_randrange(5)) - 2)); + if (InsecureRandBits(3) == 2) { + int new_size = std::max(0, std::min(30, test.size() + (InsecureRandRange(5)) - 2)); test.resize(new_size); } - if (insecure_randbits(3) == 3) { - test.insert(insecure_randrange(test.size() + 1), 1 + insecure_randbool(), insecure_rand()); + if (InsecureRandBits(3) == 3) { + test.insert(InsecureRandRange(test.size() + 1), 1 + InsecureRandBool(), InsecureRand32()); } - if (insecure_randbits(3) == 4) { - int del = std::min(test.size(), 1 + (insecure_randbool())); - int beg = insecure_randrange(test.size() + 1 - del); + if (InsecureRandBits(3) == 4) { + int del = std::min(test.size(), 1 + (InsecureRandBool())); + int beg = InsecureRandRange(test.size() + 1 - del); test.erase(beg, beg + del); } - if (insecure_randbits(4) == 5) { - test.push_back(insecure_rand()); + if (InsecureRandBits(4) == 5) { + test.push_back(InsecureRand32()); } - if (test.size() > 0 && insecure_randbits(4) == 6) { + if (test.size() > 0 && InsecureRandBits(4) == 6) { test.pop_back(); } - if (insecure_randbits(5) == 7) { + if (InsecureRandBits(5) == 7) { int values[4]; - int num = 1 + (insecure_randbits(2)); + int num = 1 + (InsecureRandBits(2)); for (int k = 0; k < num; k++) { - values[k] = insecure_rand(); + values[k] = InsecureRand32(); } - test.insert_range(insecure_randrange(test.size() + 1), values, values + num); + test.insert_range(InsecureRandRange(test.size() + 1), values, values + num); } - if (insecure_randbits(5) == 8) { - int del = std::min(test.size(), 1 + (insecure_randbits(2))); - int beg = insecure_randrange(test.size() + 1 - del); + if (InsecureRandBits(5) == 8) { + int del = std::min(test.size(), 1 + (InsecureRandBits(2))); + int beg = InsecureRandRange(test.size() + 1 - del); test.erase(beg, beg + del); } - if (insecure_randbits(5) == 9) { - test.reserve(insecure_randbits(5)); + if (InsecureRandBits(5) == 9) { + test.reserve(InsecureRandBits(5)); } - if (insecure_randbits(6) == 10) { + if (InsecureRandBits(6) == 10) { test.shrink_to_fit(); } if (test.size() > 0) { - test.update(insecure_randrange(test.size()), insecure_rand()); + test.update(InsecureRandRange(test.size()), InsecureRand32()); } - if (insecure_randbits(10) == 11) { + if (InsecureRandBits(10) == 11) { test.clear(); } - if (insecure_randbits(9) == 12) { - test.assign(insecure_randbits(5), insecure_rand()); + if (InsecureRandBits(9) == 12) { + test.assign(InsecureRandBits(5), InsecureRand32()); } - if (insecure_randbits(3) == 3) { + if (InsecureRandBits(3) == 3) { test.swap(); } - if (insecure_randbits(4) == 8) { + if (InsecureRandBits(4) == 8) { test.copy(); } - if (insecure_randbits(5) == 18) { + if (InsecureRandBits(5) == 18) { test.move(); } } diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index c2813286adf..eb671cb879b 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -89,30 +89,30 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un void static RandomScript(CScript &script) { static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR}; script = CScript(); - int ops = (insecure_randrange(10)); + int ops = (InsecureRandRange(10)); for (int i=0; inTimeMax >= test_time); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 129c4a82ecb..b85f61132e8 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -67,7 +67,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha RegisterAllCoreRPCCommands(tableRPC); ClearDatadirCache(); - pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(insecure_randrange(100000))); + pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000))); fs::create_directories(pathTemp); ForceSetArg("-datadir", pathTemp.string()); mempool.setSanityCheck(1.0); diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index eecd8635e77..3a62b14fe96 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -18,7 +18,7 @@ extern uint256 insecure_rand_seed; extern FastRandomContext insecure_rand_ctx; -static inline void seed_insecure_rand(bool fDeterministic = false) +static inline void SeedInsecureRand(bool fDeterministic = false) { if (fDeterministic) { insecure_rand_seed = uint256(); @@ -28,12 +28,12 @@ static inline void seed_insecure_rand(bool fDeterministic = false) insecure_rand_ctx = FastRandomContext(insecure_rand_seed); } -static inline uint32_t insecure_rand() { return insecure_rand_ctx.rand32(); } -static inline uint256 insecure_rand256() { return insecure_rand_ctx.rand256(); } -static inline uint64_t insecure_randbits(int bits) { return insecure_rand_ctx.randbits(bits); } -static inline uint64_t insecure_randrange(uint64_t range) { return insecure_rand_ctx.randrange(range); } -static inline bool insecure_randbool() { return insecure_rand_ctx.randbool(); } -static inline std::vector insecure_randbytes(size_t len) { return insecure_rand_ctx.randbytes(len); } +static inline uint32_t InsecureRand32() { return insecure_rand_ctx.rand32(); } +static inline uint256 InsecureRand256() { return insecure_rand_ctx.rand256(); } +static inline uint64_t InsecureRandBits(int bits) { return insecure_rand_ctx.randbits(bits); } +static inline uint64_t InsecureRandRange(uint64_t range) { return insecure_rand_ctx.randrange(range); } +static inline bool InsecureRandBool() { return insecure_rand_ctx.randbool(); } +static inline std::vector InsecureRandBytes(size_t len) { return insecure_rand_ctx.randbytes(len); } /** Basic testing setup. * This just configures logging and chain parameters. diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 89f7025d064..d33c4f1cb14 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(util_IsHex) BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) { - seed_insecure_rand(true); + SeedInsecureRand(true); for (int mod=2;mod<11;mod++) { int mask = 1; @@ -256,7 +256,7 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) for (int i = 0; i < 10000; i++) { uint32_t rval; do{ - rval=insecure_rand()&mask; + rval=InsecureRand32()&mask; }while(rval>=(uint32_t)mod); count += rval==0; } diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 051d905c0c5..d8b4675b47b 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -81,7 +81,7 @@ class VersionBitsTester VersionBitsTester& TestStateSinceHeight(int height) { for (int i = 0; i < CHECKERS; i++) { - if (insecure_randbits(i) == 0) { + if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? NULL : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num)); } } @@ -91,7 +91,7 @@ class VersionBitsTester VersionBitsTester& TestDefined() { for (int i = 0; i < CHECKERS; i++) { - if (insecure_randbits(i) == 0) { + if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num)); } } @@ -101,7 +101,7 @@ class VersionBitsTester VersionBitsTester& TestStarted() { for (int i = 0; i < CHECKERS; i++) { - if (insecure_randbits(i) == 0) { + if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num)); } } @@ -111,7 +111,7 @@ class VersionBitsTester VersionBitsTester& TestLockedIn() { for (int i = 0; i < CHECKERS; i++) { - if (insecure_randbits(i) == 0) { + if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num)); } } @@ -121,7 +121,7 @@ class VersionBitsTester VersionBitsTester& TestActive() { for (int i = 0; i < CHECKERS; i++) { - if (insecure_randbits(i) == 0) { + if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num)); } } @@ -131,7 +131,7 @@ class VersionBitsTester VersionBitsTester& TestFailed() { for (int i = 0; i < CHECKERS; i++) { - if (insecure_randbits(i) == 0) { + if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num)); } } diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp index eec916c9d90..cbd74b6f960 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/crypto_tests.cpp @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(passphrase) { std::string hash(GetRandHash().ToString()); std::vector vchSalt(8); GetRandBytes(&vchSalt[0], vchSalt.size()); - uint32_t rounds = insecure_rand(); + uint32_t rounds = InsecureRand32(); if (rounds > 30000) rounds = 30000; TestCrypter::TestPassphrase(vchSalt, SecureString(hash.begin(), hash.end()), rounds); From cbbcc6faf9f12557c9e632f102a8032515dfee47 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Thu, 27 Jul 2017 15:34:09 +0300 Subject: [PATCH 30/60] Check if sys/random.h is required for getentropy on OSX. Cherry-picked from: ee2d10ad0c0e04d0b9da4535a6fff265ac2501e5 --- configure.ac | 8 ++++++++ src/random.cpp | 14 +++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 06d8bd48a88..aff8b9536d1 100644 --- a/configure.ac +++ b/configure.ac @@ -675,6 +675,14 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [ AC_MSG_RESULT(no)] ) +AC_MSG_CHECKING(for getentropy via random.h) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], + [[ getentropy(nullptr, 32) ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY_RAND, 1,[Define this symbol if the BSD getentropy system call is available with sys/random.h]) ], + [ AC_MSG_RESULT(no)] +) + AC_MSG_CHECKING(for sysctl KERN_ARND) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include ]], diff --git a/src/random.cpp b/src/random.cpp index 9a2c905b7c2..5f0ed21ad9a 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -26,9 +26,12 @@ #include #include #endif -#ifdef HAVE_GETENTROPY +#if defined(HAVE_GETENTROPY) || (defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX)) #include #endif +#if defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) +#include +#endif #ifdef HAVE_SYSCTL_ARND #include #endif @@ -165,6 +168,15 @@ void GetOSRand(unsigned char *ent32) if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { RandFailure(); } +#elif defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) + // We need a fallback for OSX < 10.12 + if (&getentropy != NULL) { + if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { + RandFailure(); + } + } else { + GetDevURandom(ent32); + } #elif defined(HAVE_SYSCTL_ARND) /* FreeBSD and similar. It is possible for the call to return less * bytes than requested, so need to read in a loop. From 2f497fc639f718819ddf415fcb0f9f6e99a1c6ac Mon Sep 17 00:00:00 2001 From: fanquake Date: Fri, 19 May 2023 10:14:35 +0100 Subject: [PATCH 31/60] random: getentropy on macOS does not need unistd.h Remove it. Make this change, so in a future commit, we can combine #ifdefs, and avoid duplicate includes once we switch to using getrandom directly. Also remove the comment about macOS 10.12. We already require macOS > 10.15, so it is redundant. Cherry-picked from: c13c97dbf846cf0e6a5581ac414ef96a215b0dc6 --- configure.ac | 4 ++-- src/random.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index aff8b9536d1..bfdd4154a86 100644 --- a/configure.ac +++ b/configure.ac @@ -675,8 +675,8 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [ AC_MSG_RESULT(no)] ) -AC_MSG_CHECKING(for getentropy via random.h) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +AC_MSG_CHECKING([for getentropy via sys/random.h]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ getentropy(nullptr, 32) ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY_RAND, 1,[Define this symbol if the BSD getentropy system call is available with sys/random.h]) ], diff --git a/src/random.cpp b/src/random.cpp index 5f0ed21ad9a..ac9cd02f853 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -26,7 +26,7 @@ #include #include #endif -#if defined(HAVE_GETENTROPY) || (defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX)) +#if defined(HAVE_GETENTROPY) #include #endif #if defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) @@ -170,7 +170,7 @@ void GetOSRand(unsigned char *ent32) } #elif defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) // We need a fallback for OSX < 10.12 - if (&getentropy != NULL) { + if (&getentropy != nullptr) { if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { RandFailure(); } From 16f0ffef194d2ab34c32997969b2ce26557e635b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 2 May 2017 18:21:33 -0700 Subject: [PATCH 32/60] Use hardware timestamps in RNG seeding Cherry-picked from: f544094d5e1ee627ebd34542aead7bf1241fae19 --- src/random.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index ac9cd02f853..4ab677dad3b 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -47,15 +47,22 @@ static void RandFailure() static inline int64_t GetPerformanceCounter() { - int64_t nCounter = 0; -#ifdef WIN32 - QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); + // Read the hardware time stamp counter when available. + // See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information. +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + return __rdtsc(); +#elif !defined(_MSC_VER) && defined(__i386__) + uint64_t r = 0; + __asm__ volatile ("rdtsc" : "=A"(r)); // Constrain the r variable to the eax:edx pair. + return r; +#elif !defined(_MSC_VER) && (defined(__x86_64__) || defined(__amd64__)) + uint64_t r1 = 0, r2 = 0; + __asm__ volatile ("rdtsc" : "=a"(r1), "=d"(r2)); // Constrain r1 to rax and r2 to rdx. + return (r2 << 32) | r1; #else - timeval t; - gettimeofday(&t, NULL); - nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec); + // Fall back to using C++11 clock (usually microsecond or nanosecond precision) + return std::chrono::high_resolution_clock::now().time_since_epoch().count(); #endif - return nCounter; } void RandAddSeed() From 631e3a98af79f3591b5a5c84a2eb60e06a66394c Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 5 May 2017 11:32:06 -0700 Subject: [PATCH 33/60] Test that GetPerformanceCounter() increments Cherry-picked from: 33f853d8d88917b145e2793bf8eb1b4e3790e7e7 --- src/random.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/random.cpp b/src/random.cpp index 4ab677dad3b..f8c3c918a91 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -294,6 +294,8 @@ FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false) bool Random_SanityCheck() { + uint64_t start = GetPerformanceCounter(); + /* This does not measure the quality of randomness, but it does test that * OSRandom() overwrites all 32 bytes of the output given a maximum * number of tries. @@ -320,7 +322,14 @@ bool Random_SanityCheck() tries += 1; } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); - return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */ + if (num_overwritten != NUM_OS_RANDOM_BYTES) return false; /* If this failed, bailed out after too many tries */ + + // Check that GetPerformanceCounter increases at least during a GetOSRand() call + 1ms sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + uint64_t stop = GetPerformanceCounter(); + if (stop == start) return false; + + return true; } FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) From d45bace443f05c1d7492a58603320319c6460369 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 5 May 2017 11:45:37 -0700 Subject: [PATCH 34/60] Use sanity check timestamps as entropy Cherry-picked from: 2c0a6f157da3c6bb3b0a1e77f003caf0d9cb9d6c --- src/random.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/random.cpp b/src/random.cpp index f8c3c918a91..4cbf51bb7ed 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -329,6 +329,10 @@ bool Random_SanityCheck() uint64_t stop = GetPerformanceCounter(); if (stop == start) return false; + // We called GetPerformanceCounter. Use it as entropy. + RAND_add((const unsigned char*)&start, sizeof(start), 1); + RAND_add((const unsigned char*)&stop, sizeof(stop), 1); + return true; } From 3e75d69e61cc51f4207e074a74de99c562ed345b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 9 May 2017 15:13:00 -0700 Subject: [PATCH 35/60] Use rdrand as entropy source on supported platforms Cherry-picked from: cb24c8539d1098d1a61605b452ecfa11a693320d --- src/init.cpp | 1 + src/random.cpp | 68 +++++++++++++++++++++++++++++++++++++++ src/random.h | 3 ++ src/test/test_bitcoin.cpp | 1 + 4 files changed, 73 insertions(+) diff --git a/src/init.cpp b/src/init.cpp index 03f3f470d3f..3243bc9be38 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1187,6 +1187,7 @@ bool AppInitSanityChecks() // ********************************************************* Step 4: sanity checks // Initialize elliptic curve code + RandomInit(); ECC_Start(); globalVerifyHandle.reset(new ECCVerifyHandle()); diff --git a/src/random.cpp b/src/random.cpp index 4cbf51bb7ed..1dd325cca37 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -65,6 +65,64 @@ static inline int64_t GetPerformanceCounter() #endif } + +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +static std::atomic hwrand_initialized{false}; +static bool rdrand_supported = false; +static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; +static void RDRandInit() +{ + //! When calling cpuid function #1, ecx register will have this set if RDRAND is available. + // Avoid clobbering ebx, as that is used for PIC on x86. + uint32_t eax, tmp, ecx, edx; + __asm__ ("mov %%ebx, %1; cpuid; mov %1, %%ebx": "=a"(eax), "=g"(tmp), "=c"(ecx), "=d"(edx) : "a"(1)); + if (ecx & CPUID_F1_ECX_RDRAND) { + LogPrintf("Using RdRand as entropy source\n"); + rdrand_supported = true; + } + hwrand_initialized.store(true); +} +#else +static void RDRandInit() {} +#endif + +static bool GetHWRand(unsigned char* ent32) { +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) + assert(hwrand_initialized.load(std::memory_order_relaxed)); + if (rdrand_supported) { + uint8_t ok; + // Not all assemblers support the rdrand instruction, write it in hex. +#ifdef __i386__ + for (int iter = 0; iter < 4; ++iter) { + uint32_t r1, r2; + __asm__ volatile (".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax + ".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx + "setc %2" : + "=a"(r1), "=d"(r2), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE32(ent32 + 8 * iter, r1); + WriteLE32(ent32 + 8 * iter + 4, r2); + } +#else + uint64_t r1, r2, r3, r4; + __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax + "0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx + "0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx + "0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx + "setc %4" : + "=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE64(ent32, r1); + WriteLE64(ent32 + 8, r2); + WriteLE64(ent32 + 16, r3); + WriteLE64(ent32 + 24, r4); +#endif + return true; + } +#endif + return false; +} + void RandAddSeed() { // Seed with CPU performance counter @@ -227,6 +285,11 @@ void GetStrongRandBytes(unsigned char* out, int num) GetOSRand(buf); hasher.Write(buf, 32); + // Third source: HW RNG, if available. + if (GetHWRand(buf)) { + hasher.Write(buf, 32); + } + // Produce output hasher.Finalize(buf); memcpy(out, buf, num); @@ -344,3 +407,8 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete uint256 seed; rng.SetKey(seed.begin(), 32); } + +void RandomInit() +{ + RDRandInit(); +} diff --git a/src/random.h b/src/random.h index f8e2e0dc555..b35cf51a166 100644 --- a/src/random.h +++ b/src/random.h @@ -133,4 +133,7 @@ void GetOSRand(unsigned char *ent32); */ bool Random_SanityCheck(); +/** Initialize the RNG. */ +void RandomInit(); + #endif // BITCOIN_RANDOM_H diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index b85f61132e8..a3459726caa 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -43,6 +43,7 @@ static const int COINBASE_MATURITY = 60*4; // 4 hours of blocks BasicTestingSetup::BasicTestingSetup(const std::string& chainName) { + RandomInit(); ECC_Start(); SetupEnvironment(); SetupNetworking(); From 543962df2afa263d11157076f152ceefa0fa9bdf Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 23 Jun 2017 14:18:44 -0700 Subject: [PATCH 36/60] Initialize randomness in benchmarks Call RandomInit() in bench_bitcoin to initialize the RNG so that it does not cause an assertion error. Cherry-picked from: 5155d1101eb4fc9d4d797b583bb29f71807bd10b --- src/bench/bench_bitcoin.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index c4e3fd5d034..af00bd4a003 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -5,10 +5,13 @@ #include "bench.h" // for BenchRunner #include "key.h" // for ECC_Start, ECC_Stop #include "util.h" // for SetupEnvironment, fPrintToDebugLog +#include "validation.h" +#include "random.h" int main(int argc, char** argv) { + RandomInit(); ECC_Start(); SetupEnvironment(); fPrintToDebugLog = false; // don't want to write to debug.log file From 6973e5e83dd4e58f396a91337bb744e10b8612ba Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Fri, 16 Jun 2017 14:43:35 -0400 Subject: [PATCH 37/60] random: fix crash on some 64bit platforms rbx needs to be stashed in a 64bit register on 64bit platforms. With this crash in particular, it was holding a stack canary which was not properly restored after the cpuid. Split out the x86+PIC case so that x86_64 doesn't have to worry about it. Cherry-picked from: 9af207c810b5c4b09ccbdec75582b3e34e32e8cc --- src/random.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 1dd325cca37..d2074f36de1 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -72,10 +72,16 @@ static bool rdrand_supported = false; static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; static void RDRandInit() { - //! When calling cpuid function #1, ecx register will have this set if RDRAND is available. + uint32_t eax, ecx, edx; +#if defined(__i386__) && ( defined(__PIC__) || defined(__PIE__)) // Avoid clobbering ebx, as that is used for PIC on x86. - uint32_t eax, tmp, ecx, edx; + uint32_t tmp; __asm__ ("mov %%ebx, %1; cpuid; mov %1, %%ebx": "=a"(eax), "=g"(tmp), "=c"(ecx), "=d"(edx) : "a"(1)); +#else + uint32_t ebx; + __asm__ ("cpuid": "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); +#endif + //! When calling cpuid function #1, ecx register will have this set if RDRAND is available. if (ecx & CPUID_F1_ECX_RDRAND) { LogPrintf("Using RdRand as entropy source\n"); rdrand_supported = true; From 681d662ae7d080b20eb3907316423b2ba96b9033 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 13 Jul 2017 16:43:05 -0700 Subject: [PATCH 38/60] Use cpuid intrinsics instead of asm code Cherry-picked from: a9e82f6512662054f64ed2bde590b2bb0831fc9d --- src/random.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index d2074f36de1..59bfc7ecb5f 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -36,6 +36,12 @@ #include #endif +#include + +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#include +#endif + #include #include @@ -72,17 +78,8 @@ static bool rdrand_supported = false; static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; static void RDRandInit() { - uint32_t eax, ecx, edx; -#if defined(__i386__) && ( defined(__PIC__) || defined(__PIE__)) - // Avoid clobbering ebx, as that is used for PIC on x86. - uint32_t tmp; - __asm__ ("mov %%ebx, %1; cpuid; mov %1, %%ebx": "=a"(eax), "=g"(tmp), "=c"(ecx), "=d"(edx) : "a"(1)); -#else - uint32_t ebx; - __asm__ ("cpuid": "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); -#endif - //! When calling cpuid function #1, ecx register will have this set if RDRAND is available. - if (ecx & CPUID_F1_ECX_RDRAND) { + uint32_t eax, ebx, ecx, edx; + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { LogPrintf("Using RdRand as entropy source\n"); rdrand_supported = true; } From 8512ba56158522334e1850eb0a2d8edf3e1bbe7c Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 14 Jul 2017 12:17:33 -0700 Subject: [PATCH 39/60] Clarify entropy source Cherry-picked from: 674848fe1c43fb88870cf5ba16fca4e2524da793 --- src/random.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/random.cpp b/src/random.cpp index 59bfc7ecb5f..a7a80eec6b9 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -80,7 +80,7 @@ static void RDRandInit() { uint32_t eax, ebx, ecx, edx; if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { - LogPrintf("Using RdRand as entropy source\n"); + LogPrintf("Using RdRand as an additional entropy source\n"); rdrand_supported = true; } hwrand_initialized.store(true); From 2d34354c19eb63d77956373285e3ffb532df909a Mon Sep 17 00:00:00 2001 From: Dag Robole Date: Sat, 15 Jul 2017 21:34:52 +0200 Subject: [PATCH 40/60] Fix resource leak Cherry-picked from: a8ae0b252a2007568e77f5aca1c7fa3ec5941b72 --- src/random.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/random.cpp b/src/random.cpp index a7a80eec6b9..45b3077f804 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -188,6 +188,7 @@ void GetDevURandom(unsigned char *ent32) do { ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { + close(f); RandFailure(); } have += n; From 5d6cdff8ded701f8e8e0c15b05d78840d39028d3 Mon Sep 17 00:00:00 2001 From: Aaron Clauson Date: Fri, 10 Nov 2017 07:06:49 +1100 Subject: [PATCH 41/60] Minimal code changes to allow msvc compilation. Cherry-picked from: fbf327b13868861c2877c5754caf5a9816f2603c --- src/bench/base58.cpp | 14 ++++++++------ src/bench/checkqueue.cpp | 2 +- src/chainparams.cpp | 5 +++-- src/compat.h | 10 ++++++++++ src/random.h | 2 +- src/support/cleanse.cpp | 34 ++++++++++++++++++++++++++++++++-- src/test/checkqueue_tests.cpp | 2 +- 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp index 3319c179bf5..70850fe74fb 100644 --- a/src/bench/base58.cpp +++ b/src/bench/base58.cpp @@ -7,20 +7,22 @@ #include "validation.h" #include "base58.h" +#include #include #include static void Base58Encode(benchmark::State& state) { - unsigned char buff[32] = { - 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147, - 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90, - 200, 24 + static const std::array buff = { + { + 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147, + 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90, + 200, 24 + } }; - unsigned char* b = buff; while (state.KeepRunning()) { - EncodeBase58(b, b + 32); + EncodeBase58(buff.data(), buff.data() + buff.size()); } } diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp index 88a2a570f93..79082290e25 100644 --- a/src/bench/checkqueue.cpp +++ b/src/bench/checkqueue.cpp @@ -19,7 +19,7 @@ static const int MIN_CORES = 2; static const size_t BATCHES = 101; static const size_t BATCH_SIZE = 30; static const int PREVECTOR_SIZE = 28; -static const int QUEUE_BATCH_SIZE = 128; +static const unsigned int QUEUE_BATCH_SIZE = 128; static void CCheckQueueSpeed(benchmark::State& state) { struct FakeJobNoWork { diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 5f8ebaa7af1..3991e245e6f 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -459,8 +459,9 @@ class CRegTestParams : public CChainParams { fMineBlocksOnDemand = true; checkpointData = (CCheckpointData){ - boost::assign::map_list_of - ( 0, uint256S("0x3d2160a3b5dc4a9d62e7e66a295f70313ac808440ef7400d6c0772171ce973a5")) + { + {0, uint256S("0x3d2160a3b5dc4a9d62e7e66a295f70313ac808440ef7400d6c0772171ce973a5")}, + } }; chainTxData = ChainTxData{ diff --git a/src/compat.h b/src/compat.h index d4dc3ba9923..6a2cbc5dcb1 100644 --- a/src/compat.h +++ b/src/compat.h @@ -32,6 +32,7 @@ #include #include #include +#include #else #include #include @@ -74,6 +75,15 @@ typedef u_int SOCKET; #else #define MAX_PATH 1024 #endif +#ifdef _MSC_VER +#if !defined(ssize_t) +#ifdef _WIN64 +typedef int64_t ssize_t; +#else +typedef int32_t ssize_t; +#endif +#endif +#endif // As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall, it is defined as 0 #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) diff --git a/src/random.h b/src/random.h index b35cf51a166..6503d8359f0 100644 --- a/src/random.h +++ b/src/random.h @@ -121,7 +121,7 @@ class FastRandomContext { * sure that the underlying OS APIs for all platforms support the number. * (many cap out at 256 bytes). */ -static const ssize_t NUM_OS_RANDOM_BYTES = 32; +static const int NUM_OS_RANDOM_BYTES = 32; /** Get 32 bytes of system entropy. Do not use this in application code: use * GetStrongRandBytes instead. diff --git a/src/support/cleanse.cpp b/src/support/cleanse.cpp index a2141b24498..eb6e0a73a20 100644 --- a/src/support/cleanse.cpp +++ b/src/support/cleanse.cpp @@ -5,9 +5,39 @@ #include "cleanse.h" -#include +#include +#if defined(_MSC_VER) +#include // For SecureZeroMemory. +#endif + +/* Compilers have a bad habit of removing "superfluous" memset calls that + * are trying to zero memory. For example, when memset()ing a buffer and + * then free()ing it, the compiler might decide that the memset is + * unobservable and thus can be removed. + * + * Previously we used OpenSSL which tried to stop this by a) implementing + * memset in assembly on x86 and b) putting the function in its own file + * for other platforms. + * + * This change removes those tricks in favour of using asm directives to + * scare the compiler away. As best as our compiler folks can tell, this is + * sufficient and will continue to be so. + * + * Adam Langley + * Commit: ad1907fe73334d6c696c8539646c21b11178f20f + * BoringSSL (LICENSE: ISC) + */ void memory_cleanse(void *ptr, size_t len) { - OPENSSL_cleanse(ptr, len); + std::memset(ptr, 0, len); + + /* As best as we can tell, this is sufficient to break any optimisations that + might try to eliminate "superfluous" memsets. If there's an easy way to + detect memset_s, it would be better to use that. */ +#if defined(_MSC_VER) + SecureZeroMemory(ptr, len); +#else + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#endif } diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 36b8768cd36..34d9bdb6348 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -24,7 +24,7 @@ // otherwise. BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup) -static const int QUEUE_BATCH_SIZE = 128; +static const unsigned int QUEUE_BATCH_SIZE = 128; struct FakeCheck { bool operator()() From a74d965765ed0f4d4e4191dd1b123d2e3dc31c49 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 20 Mar 2018 19:10:39 -0700 Subject: [PATCH 42/60] Make FastRandomContext support standard C++11 RNG interface This makes it possible to plug it into the various standard C++11 random distribution algorithms and other functions like std::shuffle. Cherry-picked from: 1ec1602a4549f6b68586cead8eff701bceb624f5 --- src/random.h | 7 +++++++ src/test/random_tests.cpp | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/random.h b/src/random.h index 6503d8359f0..b14028a431a 100644 --- a/src/random.h +++ b/src/random.h @@ -11,6 +11,7 @@ #include "uint256.h" #include +#include /* Seed OpenSSL PRNG with additional entropy data */ void RandAddSeed(); @@ -114,6 +115,12 @@ class FastRandomContext { /** Generate a random boolean. */ bool randbool() { return randbits(1); } + + // Compatibility with the C++11 UniformRandomBitGenerator concept + typedef uint64_t result_type; + static constexpr uint64_t min() { return 0; } + static constexpr uint64_t max() { return std::numeric_limits::max(); } + inline uint64_t operator()() { return rand64(); } }; /* Number of random bytes returned by GetOSRand. diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 132e190051e..1f5c99552a3 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -8,6 +8,9 @@ #include +#include +#include + BOOST_FIXTURE_TEST_SUITE(random_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(osrandom_tests) @@ -57,4 +60,23 @@ BOOST_AUTO_TEST_CASE(fastrandom_randbits) } } +/** Does-it-compile test for compatibility with standard C++11 RNG interface. */ +BOOST_AUTO_TEST_CASE(stdrandom_test) +{ + FastRandomContext ctx; + std::uniform_int_distribution distribution(3, 9); + for (int i = 0; i < 100; ++i) { + int x = distribution(ctx); + BOOST_CHECK(x >= 3); + BOOST_CHECK(x <= 9); + + std::vector test{1,2,3,4,5,6,7,8,9,10}; + std::shuffle(test.begin(), test.end(), ctx); + for (int j = 1; j <= 10; ++j) { + BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end()); + } + } + +} + BOOST_AUTO_TEST_SUITE_END() From a5b036bdca95053e923afee7980989fd26f6634f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 19 Dec 2018 01:50:36 -0800 Subject: [PATCH 43/60] Don't log RandAddSeedPerfmon details These are hard to deal with, as in a follow-up this function can get called before the logging infrastructure is initialized. Cherry-picked from: 2d1cc5093949f8ea9487a68724162c8b39035ad8 --- src/random.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 45b3077f804..f0facb73820 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -163,13 +163,13 @@ static void RandAddSeedPerfmon() if (ret == ERROR_SUCCESS) { RAND_add(vData.data(), nSize, nSize / 100.0); memory_cleanse(vData.data(), nSize); - LogPrint("rand", "%s: %lu bytes\n", __func__, nSize); } else { - static bool warned = false; // Warn only once - if (!warned) { - LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret); - warned = true; - } + // Performance data is only a best-effort attempt at improving the + // situation when the OS randomness (and other sources) aren't + // adequate. As a result, failure to read it is isn't considered critical, + // so we don't call RandFailure(). + // TODO: Add logging when the logger is made functional before global + // constructors have been invoked. } #endif } From 7a97961adedda3606359b052cc1954454052d2a2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 17 Dec 2018 16:48:21 -0800 Subject: [PATCH 44/60] Automatically initialize RNG on first use. Cherry-picked from: 05fde14e3afe6f7156ebb6df6cd0e3ae12635b89 --- src/random.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/random.h | 7 ++++- 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index f0facb73820..5e7d35c9231 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -71,7 +71,6 @@ static inline int64_t GetPerformanceCounter() #endif } - #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) static std::atomic hwrand_initialized{false}; static bool rdrand_supported = false; @@ -80,13 +79,24 @@ static void RDRandInit() { uint32_t eax, ebx, ecx, edx; if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { - LogPrintf("Using RdRand as an additional entropy source\n"); rdrand_supported = true; } hwrand_initialized.store(true); } + +static void RDRandReport() +{ + assert(hwrand_initialized.load(std::memory_order_relaxed)); + if (rdrand_supported) { + // This must be done in a separate function, as HWRandInit() may be indirectly called + // from global constructors, before logging is initialized. + LogPrintf("Using RdRand as an additional entropy source\n"); + } +} + #else static void RDRandInit() {} +static void RDRandReport() {} #endif static bool GetHWRand(unsigned char* ent32) { @@ -274,8 +284,64 @@ void GetRandBytes(unsigned char* buf, int num) } } +namespace { +struct RNGState { + Mutex m_mutex; + unsigned char m_state[32] = {0}; + uint64_t m_counter = 0; + + explicit RNGState() { + RDRandInit(); + } +}; + +RNGState& GetRNGState() +{ + // This C++11 idiom relies on the guarantee that static variable are initialized + // on first call, even when multiple parallel calls are permitted. + static std::unique_ptr g_rng{new RNGState()}; + return *g_rng; +} +} + +static void AddDataToRng(void* data, size_t len); + +void RandAddSeedSleep() +{ + int64_t nPerfCounter1 = GetPerformanceCounter(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + int64_t nPerfCounter2 = GetPerformanceCounter(); + + // Combine with and update state + AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1)); + AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2)); + + memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1)); + memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); +} + +static void AddDataToRng(void* data, size_t len) { + RNGState& rng = GetRNGState(); + + CSHA512 hasher; + hasher.Write((const unsigned char*)&len, sizeof(len)); + hasher.Write((const unsigned char*)data, len); + unsigned char buf[64]; + { + std::unique_lock lock(rng.m_mutex); + hasher.Write(rng.m_state, sizeof(rng.m_state)); + hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter)); + ++rng.m_counter; + hasher.Finalize(buf); + memcpy(rng.m_state, buf + 32, 32); + } + memory_cleanse(buf, 64); +} + void GetStrongRandBytes(unsigned char* out, int num) { + RNGState& rng = GetRNGState(); + assert(num <= 32); CSHA512 hasher; unsigned char buf[64]; @@ -294,6 +360,16 @@ void GetStrongRandBytes(unsigned char* out, int num) hasher.Write(buf, 32); } + // Combine with and update state + { + std::unique_lock lock(rng.m_mutex); + hasher.Write(rng.m_state, sizeof(rng.m_state)); + hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter)); + ++rng.m_counter; + hasher.Finalize(buf); + memcpy(rng.m_state, buf + 32, 32); + } + // Produce output hasher.Finalize(buf); memcpy(out, buf, num); @@ -414,5 +490,8 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete void RandomInit() { - RDRandInit(); + // Invoke RNG code to trigger initialization (if not already performed) + GetRNGState(); + + RDRandReport(); } diff --git a/src/random.h b/src/random.h index b14028a431a..3e5d17f341e 100644 --- a/src/random.h +++ b/src/random.h @@ -140,7 +140,12 @@ void GetOSRand(unsigned char *ent32); */ bool Random_SanityCheck(); -/** Initialize the RNG. */ +/** + * Initialize global RNG state and log any CPU features that are used. + * + * Calling this function is optional. RNG state will be initialized when first + * needed if it is not called. + */ void RandomInit(); #endif // BITCOIN_RANDOM_H From 549026ccf2cff3791a9acd33f93b92f61d8d331b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 16 Jan 2019 15:15:21 -0800 Subject: [PATCH 45/60] Rename some hardware RNG related functions Cherry-picked from: d3f54d1c82b131d817b20cd9daa75f9d3c9475e1 --- src/random.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 5e7d35c9231..dafb2443b2b 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -75,7 +75,7 @@ static inline int64_t GetPerformanceCounter() static std::atomic hwrand_initialized{false}; static bool rdrand_supported = false; static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; -static void RDRandInit() +static void InitHardwareRand() { uint32_t eax, ebx, ecx, edx; if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { @@ -84,7 +84,7 @@ static void RDRandInit() hwrand_initialized.store(true); } -static void RDRandReport() +static void ReportHardwareRand() { assert(hwrand_initialized.load(std::memory_order_relaxed)); if (rdrand_supported) { @@ -95,11 +95,16 @@ static void RDRandReport() } #else -static void RDRandInit() {} -static void RDRandReport() {} +/* Access to other hardware random number generators could be added here later, + * assuming it is sufficiently fast (in the order of a few hundred CPU cycles). + * Slower sources should probably be invoked separately, and/or only from + * RandAddSeedSleep (which is called during idle background operation). + */ +static void InitHardwareRand() {} +static void ReportHardwareRand() {} #endif -static bool GetHWRand(unsigned char* ent32) { +static bool GetHardwareRand(unsigned char* ent32) { #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) assert(hwrand_initialized.load(std::memory_order_relaxed)); if (rdrand_supported) { @@ -291,7 +296,7 @@ struct RNGState { uint64_t m_counter = 0; explicit RNGState() { - RDRandInit(); + InitHardwareRand(); } }; @@ -356,7 +361,7 @@ void GetStrongRandBytes(unsigned char* out, int num) hasher.Write(buf, 32); // Third source: HW RNG, if available. - if (GetHWRand(buf)) { + if (GetHardwareRand(buf)) { hasher.Write(buf, 32); } @@ -493,5 +498,5 @@ void RandomInit() // Invoke RNG code to trigger initialization (if not already performed) GetRNGState(); - RDRandReport(); + ReportHardwareRand(); } From 1d8f3cb8e57968b72f19b3a94832d0668e192dc8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 17 Dec 2018 17:03:30 -0800 Subject: [PATCH 46/60] Add thread safety annotations to RNG state Cherry-picked from: aae8b9bf0f4fd2b801ee72cf191588c8b3a67c3c --- src/random.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index dafb2443b2b..ba53ba77e78 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -292,10 +292,11 @@ void GetRandBytes(unsigned char* buf, int num) namespace { struct RNGState { Mutex m_mutex; - unsigned char m_state[32] = {0}; - uint64_t m_counter = 0; + unsigned char m_state[32] GUARDED_BY(m_mutex) = {0}; + uint64_t m_counter GUARDED_BY(m_mutex) = 0; - explicit RNGState() { + RNGState() + { InitHardwareRand(); } }; @@ -333,7 +334,7 @@ static void AddDataToRng(void* data, size_t len) { hasher.Write((const unsigned char*)data, len); unsigned char buf[64]; { - std::unique_lock lock(rng.m_mutex); + LOCK(rng.m_mutex); hasher.Write(rng.m_state, sizeof(rng.m_state)); hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter)); ++rng.m_counter; @@ -367,7 +368,7 @@ void GetStrongRandBytes(unsigned char* out, int num) // Combine with and update state { - std::unique_lock lock(rng.m_mutex); + LOCK(rng.m_mutex); hasher.Write(rng.m_state, sizeof(rng.m_state)); hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter)); ++rng.m_counter; From a9455f9feb794369f415e7567283a46f12d8f92f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 27 Mar 2024 19:10:27 -0700 Subject: [PATCH 47/60] Abstract out seeding/extracting entropy into RNGState::MixExtract Cherry-picked from: 2ccc3d3aa346e96206281a391bc29874cf5ee7f4 --- src/crypto/sha512.h | 2 +- src/random.cpp | 60 +++++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h index 98746e9d279..185544bcedf 100644 --- a/src/crypto/sha512.h +++ b/src/crypto/sha512.h @@ -18,7 +18,7 @@ class CSHA512 uint64_t bytes; public: - static const size_t OUTPUT_SIZE = 64; + static constexpr size_t OUTPUT_SIZE = 64; CSHA512(); CSHA512& Write(const unsigned char* data, size_t len); diff --git a/src/random.cpp b/src/random.cpp index ba53ba77e78..b0466acb11b 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -299,6 +299,34 @@ struct RNGState { { InitHardwareRand(); } + + /** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. */ + void MixExtract(unsigned char* out, size_t num, CSHA512&& hasher) + { + assert(num <= 32); + unsigned char buf[64]; + static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, "Buffer needs to have hasher's output size"); + { + LOCK(m_mutex); + // Write the current state of the RNG into the hasher + hasher.Write(m_state, 32); + // Write a new counter number into the state + hasher.Write((const unsigned char*)&m_counter, sizeof(m_counter)); + ++m_counter; + // Finalize the hasher + hasher.Finalize(buf); + // Store the last 32 bytes of the hash output as new RNG state. + memcpy(m_state, buf + 32, 32); + } + // If desired, copy (up to) the first 32 bytes of the hash output as output. + if (num) { + assert(out != nullptr); + memcpy(out, buf, num); + } + // Best effort cleanup of internal state + hasher.Reset(); + memory_cleanse(buf, 64); + } }; RNGState& GetRNGState() @@ -310,38 +338,29 @@ RNGState& GetRNGState() } } -static void AddDataToRng(void* data, size_t len); +static void AddDataToRng(void* data, size_t len, RNGState& rng); void RandAddSeedSleep() { + RNGState& rng = GetRNGState(); + int64_t nPerfCounter1 = GetPerformanceCounter(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); int64_t nPerfCounter2 = GetPerformanceCounter(); // Combine with and update state - AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1)); - AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2)); + AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1), rng); + AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2), rng); memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1)); memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); } -static void AddDataToRng(void* data, size_t len) { - RNGState& rng = GetRNGState(); - +static void AddDataToRng(void* data, size_t len, RNGState& rng) { CSHA512 hasher; hasher.Write((const unsigned char*)&len, sizeof(len)); hasher.Write((const unsigned char*)data, len); - unsigned char buf[64]; - { - LOCK(rng.m_mutex); - hasher.Write(rng.m_state, sizeof(rng.m_state)); - hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter)); - ++rng.m_counter; - hasher.Finalize(buf); - memcpy(rng.m_state, buf + 32, 32); - } - memory_cleanse(buf, 64); + rng.MixExtract(nullptr, 0, std::move(hasher)); } void GetStrongRandBytes(unsigned char* out, int num) @@ -367,14 +386,7 @@ void GetStrongRandBytes(unsigned char* out, int num) } // Combine with and update state - { - LOCK(rng.m_mutex); - hasher.Write(rng.m_state, sizeof(rng.m_state)); - hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter)); - ++rng.m_counter; - hasher.Finalize(buf); - memcpy(rng.m_state, buf + 32, 32); - } + rng.MixExtract(out, num, std::move(hasher)); // Produce output hasher.Finalize(buf); From 60c0e07daa891d85e20a33746ed15fcd12fa3412 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 27 Mar 2024 19:38:27 -0700 Subject: [PATCH 48/60] Integrate util/system's CInit into RNGState This guarantees that OpenSSL is initialized properly whenever randomness is used, even when that randomness is invoked from global constructors. Note that this patch uses Mutex directly, rather than CCriticalSection. This is because the lock-detection code is not necessarily initialized during global constructors. Cherry-picked from: 16e40a8b562ad849a5f5e8b21ceb375e46038243 --- src/random.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++-- src/util.cpp | 51 -------------------------------------------------- 2 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index b0466acb11b..986f395b43f 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -12,7 +12,7 @@ #include #endif #include "util.h" // for LogPrint() -#include "sync.h" // for LOCK +#include "sync.h" // for WAIT_LOCK #include "utiltime.h" // for GetTime() #include @@ -42,8 +42,11 @@ #include #endif +#include + #include #include +#include static void RandFailure() { @@ -289,15 +292,46 @@ void GetRandBytes(unsigned char* buf, int num) } } +void LockingCallbackOpenSSL(int mode, int i, const char* file, int line); + namespace { + struct RNGState { Mutex m_mutex; unsigned char m_state[32] GUARDED_BY(m_mutex) = {0}; uint64_t m_counter GUARDED_BY(m_mutex) = 0; + std::unique_ptr m_mutex_openssl; RNGState() { InitHardwareRand(); + + // Init OpenSSL library multithreading support + m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]); + CRYPTO_set_locking_callback(LockingCallbackOpenSSL); + + // OpenSSL can optionally load a config file which lists optional loadable modules and engines. + // We don't use them so we don't require the config. However some of our libs may call functions + // which attempt to load the config file, possibly resulting in an exit() or crash if it is missing + // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be + // that the config appears to have been loaded and there are no modules/engines available. + OPENSSL_no_config(); + +#ifdef WIN32 + // Seed OpenSSL PRNG with current contents of the screen + RAND_screen(); +#endif + + // Seed OpenSSL PRNG with performance counter + RandAddSeed(); + } + + ~RNGState() + { + // Securely erase the memory used by the OpenSSL PRNG + RAND_cleanup(); + // Shutdown OpenSSL library multithreading support + CRYPTO_set_locking_callback(nullptr); } /** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. */ @@ -307,7 +341,7 @@ struct RNGState { unsigned char buf[64]; static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, "Buffer needs to have hasher's output size"); { - LOCK(m_mutex); + WAIT_LOCK(m_mutex, lock); // Write the current state of the RNG into the hasher hasher.Write(m_state, 32); // Write a new counter number into the state @@ -338,6 +372,17 @@ RNGState& GetRNGState() } } +void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS +{ + RNGState& rng = GetRNGState(); + + if (mode & CRYPTO_LOCK) { + rng.m_mutex_openssl[i].lock(); + } else { + rng.m_mutex_openssl[i].unlock(); + } +} + static void AddDataToRng(void* data, size_t len, RNGState& rng); void RandAddSeedSleep() diff --git a/src/util.cpp b/src/util.cpp index 0f7a4db7a46..c66cf584475 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -123,57 +123,6 @@ bool fLogIPs = DEFAULT_LOGIPS; std::atomic fReopenDebugLog(false); CTranslationInterface translationInterface; -/** Init OpenSSL library multithreading support */ -static CCriticalSection** ppmutexOpenSSL; -void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS -{ - if (mode & CRYPTO_LOCK) { - ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]); - } else { - LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]); - } -} - -// Init -class CInit -{ -public: - CInit() - { - // Init OpenSSL library multithreading support - ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*)); - for (int i = 0; i < CRYPTO_num_locks(); i++) - ppmutexOpenSSL[i] = new CCriticalSection(); - CRYPTO_set_locking_callback(locking_callback); - - // OpenSSL can optionally load a config file which lists optional loadable modules and engines. - // We don't use them so we don't require the config. However some of our libs may call functions - // which attempt to load the config file, possibly resulting in an exit() or crash if it is missing - // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be - // that the config appears to have been loaded and there are no modules/engines available. - OPENSSL_no_config(); - -#ifdef WIN32 - // Seed OpenSSL PRNG with current contents of the screen - RAND_screen(); -#endif - - // Seed OpenSSL PRNG with performance counter - RandAddSeed(); - } - ~CInit() - { - // Securely erase the memory used by the PRNG - RAND_cleanup(); - // Shutdown OpenSSL library multithreading support - CRYPTO_set_locking_callback(NULL); - for (int i = 0; i < CRYPTO_num_locks(); i++) - delete ppmutexOpenSSL[i]; - OPENSSL_free(ppmutexOpenSSL); - } -} -instance_of_cinit; - /** * LogPrintf() has been broken a couple of times now * by well-meaning people adding mutexes in the most straightforward way. From bd9b28802cc7446ab5cdd355e426260d184cd1fd Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 2 Apr 2024 17:42:35 -0700 Subject: [PATCH 49/60] Switch all RNG code to the built-in PRNG. It includes the following policy changes: * All GetRand* functions seed the stack pointer and rdrand result (in addition to the performance counter) * The periodic entropy added by the idle scheduler now seeds stack pointer, rdrand and perfmon data (once every 10 minutes) in addition to just a sleep timing. * The entropy added when calling GetStrongRandBytes no longer includes the once-per-10-minutes perfmon data on windows (it is moved to the idle scheduler instead, where latency matters less). Other changes: * OpenSSL is no longer seeded directly anywhere. Instead, any generated randomness through our own RNG is fed back to OpenSSL (after an additional hashing step to prevent leaking our RNG state). * Seeding that was previously done directly in RandAddSeedSleep is now moved to SeedSleep(), which is indirectly invoked through ProcRand from RandAddSeedSleep. * Seeding that was previously done directly in GetStrongRandBytes() is now moved to SeedSlow(), which is indirectly invoked through ProcRand from GetStrongRandBytes(). Cherry-picked from: 9d7032e4f066777c97c58b1394884716e213790a --- src/qt/utilitydialog.cpp | 2 +- src/random.cpp | 190 ++++++++++++++++++++++++--------------- src/random.h | 28 ++++-- src/scheduler.cpp | 6 ++ 4 files changed, 148 insertions(+), 78 deletions(-) diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index f9eee737068..d1dc34ff544 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -215,7 +215,7 @@ void PaperWalletDialog::setClientModel(ClientModel *_clientModel) void PaperWalletDialog::setModel(WalletModel *model) { - RandAddSeed(); + RandAddSeedSleep(); this->model = model; this->on_getNewAddress_clicked(); } diff --git a/src/random.cpp b/src/random.cpp index 986f395b43f..54bfef68324 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -12,7 +12,7 @@ #include #endif #include "util.h" // for LogPrint() -#include "sync.h" // for WAIT_LOCK +#include "sync.h" // for LOCK #include "utiltime.h" // for GetTime() #include @@ -144,18 +144,8 @@ static bool GetHardwareRand(unsigned char* ent32) { return false; } -void RandAddSeed() +static void RandAddSeedPerfmon(CSHA512& hasher) { - // Seed with CPU performance counter - int64_t nCounter = GetPerformanceCounter(); - RAND_add(&nCounter, sizeof(nCounter), 1.5); - memory_cleanse((void*)&nCounter, sizeof(nCounter)); -} - -static void RandAddSeedPerfmon() -{ - RandAddSeed(); - #ifdef WIN32 // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // Seed with the entire set of perfmon data @@ -179,7 +169,7 @@ static void RandAddSeedPerfmon() } RegCloseKey(HKEY_PERFORMANCE_DATA); if (ret == ERROR_SUCCESS) { - RAND_add(vData.data(), nSize, nSize / 100.0); + hasher.Write(vData.data(), nSize); memory_cleanse(vData.data(), nSize); } else { // Performance data is only a best-effort attempt at improving the @@ -285,13 +275,6 @@ void GetOSRand(unsigned char *ent32) #endif } -void GetRandBytes(unsigned char* buf, int num) -{ - if (RAND_bytes(buf, num) != 1) { - RandFailure(); - } -} - void LockingCallbackOpenSSL(int mode, int i, const char* file, int line); namespace { @@ -300,6 +283,7 @@ struct RNGState { Mutex m_mutex; unsigned char m_state[32] GUARDED_BY(m_mutex) = {0}; uint64_t m_counter GUARDED_BY(m_mutex) = 0; + bool m_strongly_seeded GUARDED_BY(m_mutex) = false; std::unique_ptr m_mutex_openssl; RNGState() @@ -316,14 +300,6 @@ struct RNGState { // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be // that the config appears to have been loaded and there are no modules/engines available. OPENSSL_no_config(); - -#ifdef WIN32 - // Seed OpenSSL PRNG with current contents of the screen - RAND_screen(); -#endif - - // Seed OpenSSL PRNG with performance counter - RandAddSeed(); } ~RNGState() @@ -334,14 +310,19 @@ struct RNGState { CRYPTO_set_locking_callback(nullptr); } - /** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. */ - void MixExtract(unsigned char* out, size_t num, CSHA512&& hasher) + /** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. + * + * If this function has never been called with strong_seed = true, false is returned. + */ + bool MixExtract(unsigned char* out, size_t num, CSHA512&& hasher, bool strong_seed) { assert(num <= 32); unsigned char buf[64]; static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, "Buffer needs to have hasher's output size"); + bool ret; { - WAIT_LOCK(m_mutex, lock); + LOCK(m_mutex); + ret = (m_strongly_seeded |= strong_seed); // Write the current state of the RNG into the hasher hasher.Write(m_state, 32); // Write a new counter number into the state @@ -360,6 +341,7 @@ struct RNGState { // Best effort cleanup of internal state hasher.Reset(); memory_cleanse(buf, 64); + return ret; } }; @@ -383,62 +365,128 @@ void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THRE } } -static void AddDataToRng(void* data, size_t len, RNGState& rng); +static void SeedTimestamp(CSHA512& hasher) +{ + int64_t perfcounter = GetPerformanceCounter(); + hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter)); +} -void RandAddSeedSleep() +static void SeedFast(CSHA512& hasher) { - RNGState& rng = GetRNGState(); + unsigned char buffer[32]; - int64_t nPerfCounter1 = GetPerformanceCounter(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - int64_t nPerfCounter2 = GetPerformanceCounter(); + // Stack pointer to indirectly commit to thread/callstack + const unsigned char* ptr = buffer; + hasher.Write((const unsigned char*)&ptr, sizeof(ptr)); - // Combine with and update state - AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1), rng); - AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2), rng); + // Hardware randomness is very fast when available; use it always. + bool have_hw_rand = GetHardwareRand(buffer); + if (have_hw_rand) hasher.Write(buffer, sizeof(buffer)); - memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1)); - memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); + // High-precision timestamp + SeedTimestamp(hasher); } -static void AddDataToRng(void* data, size_t len, RNGState& rng) { - CSHA512 hasher; - hasher.Write((const unsigned char*)&len, sizeof(len)); - hasher.Write((const unsigned char*)data, len); - rng.MixExtract(nullptr, 0, std::move(hasher)); +static void SeedSlow(CSHA512& hasher) +{ + unsigned char buffer[32]; + + // Everything that the 'fast' seeder includes + SeedFast(hasher); + + // OS randomness + GetOSRand(buffer); + hasher.Write(buffer, sizeof(buffer)); + + // OpenSSL RNG (for now) + RAND_bytes(buffer, sizeof(buffer)); + hasher.Write(buffer, sizeof(buffer)); + + // High-precision timestamp. + // + // Note that we also commit to a timestamp in the Fast seeder, so we indirectly commit to a + // benchmark of all the entropy gathering sources in this function). + SeedTimestamp(hasher); } -void GetStrongRandBytes(unsigned char* out, int num) +static void SeedSleep(CSHA512& hasher) { - RNGState& rng = GetRNGState(); + // Everything that the 'fast' seeder includes + SeedFast(hasher); - assert(num <= 32); - CSHA512 hasher; - unsigned char buf[64]; + // High-precision timestamp + SeedTimestamp(hasher); - // First source: OpenSSL's RNG - RandAddSeedPerfmon(); - GetRandBytes(buf, 32); - hasher.Write(buf, 32); + // Sleep for 1ms + MilliSleep(1); - // Second source: OS RNG - GetOSRand(buf); - hasher.Write(buf, 32); + // High-precision timestamp after sleeping (as we commit to both the time before and after, this measures the delay) + SeedTimestamp(hasher); - // Third source: HW RNG, if available. - if (GetHardwareRand(buf)) { - hasher.Write(buf, 32); + // Windows performance monitor data (once every 10 minutes) + RandAddSeedPerfmon(hasher); +} + +static void SeedStartup(CSHA512& hasher) +{ +#ifdef WIN32 + RAND_screen(); +#endif + + // Everything that the 'slow' seeder includes. + SeedSlow(hasher); + + // Windows performance monitor data. + RandAddSeedPerfmon(hasher); +} + +enum class RNGLevel { + FAST, //!< Automatically called by GetRandBytes + SLOW, //!< Automatically called by GetStrongRandBytes + SLEEP, //!< Called by RandAddSeedSleep() +}; + +static void ProcRand(unsigned char* out, int num, RNGLevel level) +{ + // Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available). + RNGState& rng = GetRNGState(); + + assert(num <= 32); + + CSHA512 hasher; + switch (level) { + case RNGLevel::FAST: + SeedFast(hasher); + break; + case RNGLevel::SLOW: + SeedSlow(hasher); + break; + case RNGLevel::SLEEP: + SeedSleep(hasher); + break; } // Combine with and update state - rng.MixExtract(out, num, std::move(hasher)); + if (!rng.MixExtract(out, num, std::move(hasher), false)) { + // On the first invocation, also seed with SeedStartup(). + CSHA512 startup_hasher; + SeedStartup(startup_hasher); + rng.MixExtract(out, num, std::move(startup_hasher), true); + } - // Produce output - hasher.Finalize(buf); - memcpy(out, buf, num); - memory_cleanse(buf, 64); + // For anything but the 'fast' level, feed the resulting RNG output (after an additional hashing step) back into OpenSSL. + if (level != RNGLevel::FAST) { + unsigned char buf[64]; + CSHA512().Write(out, num).Finalize(buf); + RAND_add(buf, sizeof(buf), num); + memory_cleanse(buf, 64); + } } +void GetRandBytes(unsigned char* buf, int num) { ProcRand(buf, num, RNGLevel::FAST); } +void GetStrongRandBytes(unsigned char* buf, int num) { ProcRand(buf, num, RNGLevel::SLOW); } +void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); } + uint64_t GetRand(uint64_t nMax) { if (nMax == 0) @@ -536,8 +584,10 @@ bool Random_SanityCheck() if (stop == start) return false; // We called GetPerformanceCounter. Use it as entropy. - RAND_add((const unsigned char*)&start, sizeof(start), 1); - RAND_add((const unsigned char*)&stop, sizeof(stop), 1); + CSHA512 to_add; + to_add.Write((const unsigned char*)&start, sizeof(start)); + to_add.Write((const unsigned char*)&stop, sizeof(stop)); + GetRNGState().MixExtract(nullptr, 0, std::move(to_add), false); return true; } @@ -554,7 +604,7 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete void RandomInit() { // Invoke RNG code to trigger initialization (if not already performed) - GetRNGState(); + ProcRand(nullptr, 0, RNGLevel::FAST); ReportHardwareRand(); } diff --git a/src/random.h b/src/random.h index 3e5d17f341e..333e5f7a283 100644 --- a/src/random.h +++ b/src/random.h @@ -13,11 +13,13 @@ #include #include -/* Seed OpenSSL PRNG with additional entropy data */ -void RandAddSeed(); - /** - * Functions to gather random data via the OpenSSL PRNG + * Generate random data via the internal PRNG. + * + * These functions are designed to be fast (sub microsecond), but do not necessarily + * meaningfully add entropy to the PRNG state. + * + * Thread-safe. */ void GetRandBytes(unsigned char* buf, int num); uint64_t GetRand(uint64_t nMax); @@ -25,14 +27,26 @@ int GetRandInt(int nMax); uint256 GetRandHash(); /** - * Function to gather random data from multiple sources, failing whenever any - * of those source fail to provide a result. + * Gather entropy from various sources, feed it into the internal PRNG, and + * generate random data using it. + * + * This function will cause failure whenever the OS RNG fails. + * + * Thread-safe. */ void GetStrongRandBytes(unsigned char* buf, int num); +/** + * Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG state. + * + * Thread-safe. + */ +void RandAddSeedSleep(); + /** * Fast randomness source. This is seeded once with secure random data, but - * is completely deterministic and insecure after that. + * is completely deterministic and does not gather more entropy after that. + * * This class is not thread-safe. */ class FastRandomContext { diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 5949f11ecb5..9ca530a8564 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -5,6 +5,7 @@ #include "scheduler.h" +#include "random.h" #include "reverselock.h" #include @@ -38,6 +39,11 @@ void CScheduler::serviceQueue() // is called. while (!shouldStop()) { try { + if (!shouldStop() && taskQueue.empty()) { + reverse_lock > rlock(lock); + // Use this chance to get more entropy + RandAddSeedSleep(); + } while (!shouldStop() && taskQueue.empty()) { // Wait until there is something to do. newTaskScheduled.wait(lock); From cf9c48fc391ec26a0a9072bc2e31873a84453927 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 17 Dec 2018 16:22:22 -0800 Subject: [PATCH 50/60] Remove hwrand_initialized. All access to hwrand is now gated by GetRNGState, which initializes the hwrand code. Cherry-picked from: 4ea8e50837a0932b31a241988fd68d6730a2048a --- src/random.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 54bfef68324..55ff8fb346c 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -75,7 +75,6 @@ static inline int64_t GetPerformanceCounter() } #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) -static std::atomic hwrand_initialized{false}; static bool rdrand_supported = false; static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; static void InitHardwareRand() @@ -84,12 +83,10 @@ static void InitHardwareRand() if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { rdrand_supported = true; } - hwrand_initialized.store(true); } static void ReportHardwareRand() { - assert(hwrand_initialized.load(std::memory_order_relaxed)); if (rdrand_supported) { // This must be done in a separate function, as HWRandInit() may be indirectly called // from global constructors, before logging is initialized. @@ -109,7 +106,6 @@ static void ReportHardwareRand() {} static bool GetHardwareRand(unsigned char* ent32) { #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) - assert(hwrand_initialized.load(std::memory_order_relaxed)); if (rdrand_supported) { uint8_t ok; // Not all assemblers support the rdrand instruction, write it in hex. From 1ac7940ec4bdfe9dc1890c1c4658c2c6d3399ef4 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 17 Dec 2018 15:11:33 -0800 Subject: [PATCH 51/60] Sprinkle some sweet noexcepts over the RNG code Cherry-picked from: a1f252eda87356fa329c838a7bf569808489648f --- src/random.cpp | 49 ++++++++++++++++++++++++++++++++----------------- src/random.h | 28 ++++++++++++++-------------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 55ff8fb346c..1fa8c99716a 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -54,7 +54,7 @@ static void RandFailure() abort(); } -static inline int64_t GetPerformanceCounter() +static inline int64_t GetPerformanceCounter() noexcept { // Read the hardware time stamp counter when available. // See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information. @@ -104,7 +104,7 @@ static void InitHardwareRand() {} static void ReportHardwareRand() {} #endif -static bool GetHardwareRand(unsigned char* ent32) { +static bool GetHardwareRand(unsigned char* ent32) noexcept { #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) if (rdrand_supported) { uint8_t ok; @@ -282,7 +282,7 @@ struct RNGState { bool m_strongly_seeded GUARDED_BY(m_mutex) = false; std::unique_ptr m_mutex_openssl; - RNGState() + RNGState() noexcept { InitHardwareRand(); @@ -310,7 +310,7 @@ struct RNGState { * * If this function has never been called with strong_seed = true, false is returned. */ - bool MixExtract(unsigned char* out, size_t num, CSHA512&& hasher, bool strong_seed) + bool MixExtract(unsigned char* out, size_t num, CSHA512&& hasher, bool strong_seed) noexcept { assert(num <= 32); unsigned char buf[64]; @@ -341,7 +341,7 @@ struct RNGState { } }; -RNGState& GetRNGState() +RNGState& GetRNGState() noexcept { // This C++11 idiom relies on the guarantee that static variable are initialized // on first call, even when multiple parallel calls are permitted. @@ -361,13 +361,28 @@ void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THRE } } -static void SeedTimestamp(CSHA512& hasher) +/* A note on the use of noexcept in the seeding functions below: + * + * None of the RNG code should ever throw any exception, with the sole exception + * of MilliSleep in SeedSleep, which can (and does) support interruptions which + * cause a boost::thread_interrupted to be thrown. + * + * This means that SeedSleep, and all functions that invoke it are throwing. + * However, we know that GetRandBytes() and GetStrongRandBytes() never trigger + * this sleeping logic, so they are noexcept. The same is true for all the + * GetRand*() functions that use GetRandBytes() indirectly. + * + * TODO: After moving away from interruptible boost-based thread management, + * everything can become noexcept here. + */ + +static void SeedTimestamp(CSHA512& hasher) noexcept { int64_t perfcounter = GetPerformanceCounter(); hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter)); } -static void SeedFast(CSHA512& hasher) +static void SeedFast(CSHA512& hasher) noexcept { unsigned char buffer[32]; @@ -383,7 +398,7 @@ static void SeedFast(CSHA512& hasher) SeedTimestamp(hasher); } -static void SeedSlow(CSHA512& hasher) +static void SeedSlow(CSHA512& hasher) noexcept { unsigned char buffer[32]; @@ -423,7 +438,7 @@ static void SeedSleep(CSHA512& hasher) RandAddSeedPerfmon(hasher); } -static void SeedStartup(CSHA512& hasher) +static void SeedStartup(CSHA512& hasher) noexcept { #ifdef WIN32 RAND_screen(); @@ -479,11 +494,11 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) } } -void GetRandBytes(unsigned char* buf, int num) { ProcRand(buf, num, RNGLevel::FAST); } -void GetStrongRandBytes(unsigned char* buf, int num) { ProcRand(buf, num, RNGLevel::SLOW); } +void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); } +void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); } void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); } -uint64_t GetRand(uint64_t nMax) +uint64_t GetRand(uint64_t nMax) noexcept { if (nMax == 0) return 0; @@ -498,12 +513,12 @@ uint64_t GetRand(uint64_t nMax) return (nRand % nMax); } -int GetRandInt(int nMax) +int GetRandInt(int nMax) noexcept { return GetRand(nMax); } -uint256 GetRandHash() +uint256 GetRandHash() noexcept { uint256 hash; GetRandBytes((unsigned char*)&hash, sizeof(hash)); @@ -517,7 +532,7 @@ void FastRandomContext::RandomSeed() requires_seed = false; } -uint256 FastRandomContext::rand256() +uint256 FastRandomContext::rand256() noexcept { if (bytebuf_size < 32) { FillByteBuffer(); @@ -537,7 +552,7 @@ std::vector FastRandomContext::randbytes(size_t len) return ret; } -FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0) +FastRandomContext::FastRandomContext(const uint256& seed) noexcept : requires_seed(false), bytebuf_size(0), bitbuf_size(0) { rng.SetKey(seed.begin(), 32); } @@ -588,7 +603,7 @@ bool Random_SanityCheck() return true; } -FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) +FastRandomContext::FastRandomContext(bool fDeterministic) noexcept : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) { if (!fDeterministic) { return; diff --git a/src/random.h b/src/random.h index 333e5f7a283..00851113001 100644 --- a/src/random.h +++ b/src/random.h @@ -21,10 +21,10 @@ * * Thread-safe. */ -void GetRandBytes(unsigned char* buf, int num); -uint64_t GetRand(uint64_t nMax); -int GetRandInt(int nMax); -uint256 GetRandHash(); +void GetRandBytes(unsigned char* buf, int num) noexcept; +uint64_t GetRand(uint64_t nMax) noexcept; +int GetRandInt(int nMax) noexcept; +uint256 GetRandHash() noexcept; /** * Gather entropy from various sources, feed it into the internal PRNG, and @@ -34,7 +34,7 @@ uint256 GetRandHash(); * * Thread-safe. */ -void GetStrongRandBytes(unsigned char* buf, int num); +void GetStrongRandBytes(unsigned char* buf, int num) noexcept; /** * Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG state. @@ -78,13 +78,13 @@ class FastRandomContext { } public: - explicit FastRandomContext(bool fDeterministic = false); + explicit FastRandomContext(bool fDeterministic = false) noexcept; /** Initialize with explicit seed (only for testing) */ - explicit FastRandomContext(const uint256& seed); + explicit FastRandomContext(const uint256& seed) noexcept; /** Generate a random 64-bit integer. */ - uint64_t rand64() + uint64_t rand64() noexcept { if (bytebuf_size < 8) FillByteBuffer(); uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size); @@ -93,7 +93,7 @@ class FastRandomContext { } /** Generate a random (bits)-bit integer. */ - uint64_t randbits(int bits) { + uint64_t randbits(int bits) noexcept { if (bits == 0) { return 0; } else if (bits > 32) { @@ -108,7 +108,7 @@ class FastRandomContext { } /** Generate a random integer in the range [0..range). */ - uint64_t randrange(uint64_t range) + uint64_t randrange(uint64_t range) noexcept { --range; int bits = CountBits(range); @@ -122,19 +122,19 @@ class FastRandomContext { std::vector randbytes(size_t len); /** Generate a random 32-bit integer. */ - uint32_t rand32() { return randbits(32); } + uint32_t rand32() noexcept { return randbits(32); } /** generate a random uint256. */ - uint256 rand256(); + uint256 rand256() noexcept; /** Generate a random boolean. */ - bool randbool() { return randbits(1); } + bool randbool() noexcept { return randbits(1); } // Compatibility with the C++11 UniformRandomBitGenerator concept typedef uint64_t result_type; static constexpr uint64_t min() { return 0; } static constexpr uint64_t max() { return std::numeric_limits::max(); } - inline uint64_t operator()() { return rand64(); } + inline uint64_t operator()() noexcept { return rand64(); } }; /* Number of random bytes returned by GetOSRand. From fac9b1af96a6868dc0140de1bf91fd2dc9e692ff Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 4 Jan 2019 02:00:44 -0800 Subject: [PATCH 52/60] DRY: Implement GetRand using FastRandomContext::randrange Cherry-picked from: 152146e782d401aa1ce7d989d62306aabc85f22e --- src/random.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 1fa8c99716a..917b1644f89 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -500,17 +500,7 @@ void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); } uint64_t GetRand(uint64_t nMax) noexcept { - if (nMax == 0) - return 0; - - // The range of the random source must be a multiple of the modulus - // to give every possible output value an equal possibility - uint64_t nRange = (std::numeric_limits::max() / nMax) * nMax; - uint64_t nRand = 0; - do { - GetRandBytes((unsigned char*)&nRand, sizeof(nRand)); - } while (nRand >= nRange); - return (nRand % nMax); + return FastRandomContext().randrange(nMax); } int GetRandInt(int nMax) noexcept From 756303c2ad92288cc1bb478f3fe2bd6873d0cca6 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 10 Jan 2019 18:19:50 -0800 Subject: [PATCH 53/60] Encapsulate RNGState better Cherry-picked from: cddb31bb0a132afa50b5350196cf26f0064fe3e2 --- src/random.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 917b1644f89..9e1d2736f03 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -275,13 +275,14 @@ void LockingCallbackOpenSSL(int mode, int i, const char* file, int line); namespace { -struct RNGState { +class RNGState { Mutex m_mutex; unsigned char m_state[32] GUARDED_BY(m_mutex) = {0}; uint64_t m_counter GUARDED_BY(m_mutex) = 0; bool m_strongly_seeded GUARDED_BY(m_mutex) = false; std::unique_ptr m_mutex_openssl; +public: RNGState() noexcept { InitHardwareRand(); @@ -339,6 +340,8 @@ struct RNGState { memory_cleanse(buf, 64); return ret; } + + Mutex& GetOpenSSLMutex(int i) { return m_mutex_openssl[i]; } }; RNGState& GetRNGState() noexcept @@ -355,9 +358,9 @@ void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THRE RNGState& rng = GetRNGState(); if (mode & CRYPTO_LOCK) { - rng.m_mutex_openssl[i].lock(); + rng.GetOpenSSLMutex(i).lock(); } else { - rng.m_mutex_openssl[i].unlock(); + rng.GetOpenSSLMutex(i).unlock(); } } From 0c6be8fcbe278835396cb76b1b809fcebcd708a4 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 10 Jan 2019 18:34:17 -0800 Subject: [PATCH 54/60] Use secure allocator for RNG state Cherry-picked from: f2e60ca98530e0a865ff6c6fd3c5633aec11a515 --- src/random.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 9e1d2736f03..7370fbd577f 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -18,6 +18,8 @@ #include #include +#include + #ifndef WIN32 #include #endif @@ -348,8 +350,8 @@ RNGState& GetRNGState() noexcept { // This C++11 idiom relies on the guarantee that static variable are initialized // on first call, even when multiple parallel calls are permitted. - static std::unique_ptr g_rng{new RNGState()}; - return *g_rng; + static std::vector> g_rng(1); + return g_rng[0]; } } From 7f6b2e5916d07233612a999c85b18b93fd2c5516 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 13 Jan 2019 10:51:17 -0800 Subject: [PATCH 55/60] Document RNG design in random.h Cherry-picked from: 223de8d94d6522f795ec3c2e7db27469f24aa68c --- src/random.cpp | 8 ++++++++ src/random.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/random.cpp b/src/random.cpp index 7370fbd577f..8ae09c46e43 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -279,6 +279,14 @@ namespace { class RNGState { Mutex m_mutex; + /* The RNG state consists of 256 bits of entropy, taken from the output of + * one operation's SHA512 output, and fed as input to the next one. + * Carrying 256 bits of entropy should be sufficient to guarantee + * unpredictability as long as any entropy source was ever unpredictable + * to an attacker. To protect against situations where an attacker might + * observe the RNG's state, fresh entropy is always mixed when + * GetStrongRandBytes is called. + */ unsigned char m_state[32] GUARDED_BY(m_mutex) = {0}; uint64_t m_counter GUARDED_BY(m_mutex) = 0; bool m_strongly_seeded GUARDED_BY(m_mutex) = false; diff --git a/src/random.h b/src/random.h index 00851113001..44c0943789d 100644 --- a/src/random.h +++ b/src/random.h @@ -13,6 +13,49 @@ #include #include +/** + * Overall design of the RNG and entropy sources. + * + * We maintain a single global 256-bit RNG state for all high-quality randomness. + * The following (classes of) functions interact with that state by mixing in new + * entropy, and optionally extracting random output from it: + * + * - The GetRand*() class of functions, as well as construction of FastRandomContext objects, + * perform 'fast' seeding, consisting of mixing in: + * - A stack pointer (indirectly committing to calling thread and call stack) + * - A high-precision timestamp (rdtsc when available, c++ high_resolution_clock otherwise) + * - Hardware RNG (rdrand) when available. + * These entropy sources are very fast, and only designed to protect against situations + * where a VM state restore/copy results in multiple systems with the same randomness. + * FastRandomContext on the other hand does not protect against this once created, but + * is even faster (and acceptable to use inside tight loops). + * + * - The GetStrongRand*() class of function perform 'slow' seeding, including everything + * that fast seeding includes, but additionally: + * - OS entropy (/dev/urandom, getrandom(), ...). The application will terminate if + * this entropy source fails. + * - Bytes from OpenSSL's RNG (which itself may be seeded from various sources) + * - Another high-precision timestamp (indirectly committing to a benchmark of all the + * previous sources). + * These entropy sources are slower, but designed to make sure the RNG state contains + * fresh data that is unpredictable to attackers. + * + * - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally: + * - A high-precision timestamp before and after sleeping 1ms. + * - (On Windows) Once every 10 minutes, performance monitoring data from the OS. + * These just exploit the fact the system is idle to improve the quality of the RNG + * slightly. + * + * On first use of the RNG (regardless of what function is called first), all entropy + * sources used in the 'slow' seeder are included, but also: + * - (On Windows) Performance monitoring data from the OS. + * - (On Windows) Through OpenSSL, the screen contents. + * + * When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and + * (up to) the first 32 bytes of H are produced as output, while the last 32 bytes + * become the new RNG state. +*/ + /** * Generate random data via the internal PRNG. * From 284b3737d6f49dfbc32f9bd60820d1099de0dbc6 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 17 Dec 2018 15:50:31 -0800 Subject: [PATCH 56/60] Add hash strengthening to the RNG Once every minute, this will feed the RNG state through repeated SHA512 for 10ms. The timings of that operation are used as entropy source as well. Cherry-picked from: 1d207bc46f995ad3b5ae89bb504affaca09d10b1 --- src/random.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 8ae09c46e43..044888cfd6f 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -142,6 +142,34 @@ static bool GetHardwareRand(unsigned char* ent32) noexcept { return false; } +/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */ +static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept +{ + CSHA512 inner_hasher; + inner_hasher.Write(seed, sizeof(seed)); + + // Hash loop + unsigned char buffer[64]; + int64_t stop = GetTimeMicros() + microseconds; + do { + for (int i = 0; i < 1000; ++i) { + inner_hasher.Finalize(buffer); + inner_hasher.Reset(); + inner_hasher.Write(buffer, sizeof(buffer)); + } + // Benchmark operation and feed it into outer hasher. + int64_t perf = GetPerformanceCounter(); + hasher.Write((const unsigned char*)&perf, sizeof(perf)); + } while (GetTimeMicros() < stop); + + // Produce output from inner state and feed it to outer hasher. + inner_hasher.Finalize(buffer); + hasher.Write(buffer, sizeof(buffer)); + // Try to clean up. + inner_hasher.Reset(); + memory_cleanse(buffer, sizeof(buffer)); +} + static void RandAddSeedPerfmon(CSHA512& hasher) { #ifdef WIN32 @@ -433,7 +461,23 @@ static void SeedSlow(CSHA512& hasher) noexcept SeedTimestamp(hasher); } -static void SeedSleep(CSHA512& hasher) +/** Extract entropy from rng, strengthen it, and feed it into hasher. */ +static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept +{ + static std::atomic last_strengthen{0}; + int64_t last_time = last_strengthen.load(); + int64_t current_time = GetTimeMicros(); + if (current_time > last_time + 60000000) { // Only run once a minute + // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher. + unsigned char strengthen_seed[32]; + rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false); + // Strengthen it for 10ms (100ms on first run), and feed it into hasher. + Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher); + last_strengthen = current_time; + } +} + +static void SeedSleep(CSHA512& hasher, RNGState& rng) { // Everything that the 'fast' seeder includes SeedFast(hasher); @@ -449,9 +493,12 @@ static void SeedSleep(CSHA512& hasher) // Windows performance monitor data (once every 10 minutes) RandAddSeedPerfmon(hasher); + + // Strengthen every minute + SeedStrengthen(hasher, rng); } -static void SeedStartup(CSHA512& hasher) noexcept +static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept { #ifdef WIN32 RAND_screen(); @@ -462,6 +509,9 @@ static void SeedStartup(CSHA512& hasher) noexcept // Windows performance monitor data. RandAddSeedPerfmon(hasher); + + // Strengthen + SeedStrengthen(hasher, rng); } enum class RNGLevel { @@ -486,7 +536,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) SeedSlow(hasher); break; case RNGLevel::SLEEP: - SeedSleep(hasher); + SeedSleep(hasher, rng); break; } @@ -494,7 +544,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) if (!rng.MixExtract(out, num, std::move(hasher), false)) { // On the first invocation, also seed with SeedStartup(). CSHA512 startup_hasher; - SeedStartup(startup_hasher); + SeedStartup(startup_hasher, rng); rng.MixExtract(out, num, std::move(startup_hasher), true); } From ebc728cc139f881276bc7395a2bdb74ff0c46e45 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 21 Jan 2019 13:25:14 -0800 Subject: [PATCH 57/60] Document strenghtening Cherry-picked from: 3cb9ce85d0c6d01217babf0df7efc2eabde1b12f --- src/random.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/random.h b/src/random.h index 44c0943789d..90d39a4bc3d 100644 --- a/src/random.h +++ b/src/random.h @@ -43,6 +43,7 @@ * - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally: * - A high-precision timestamp before and after sleeping 1ms. * - (On Windows) Once every 10 minutes, performance monitoring data from the OS. + - - Once every minute, strengthen the entropy for 10 ms using repeated SHA512. * These just exploit the fact the system is idle to improve the quality of the RNG * slightly. * @@ -50,6 +51,7 @@ * sources used in the 'slow' seeder are included, but also: * - (On Windows) Performance monitoring data from the OS. * - (On Windows) Through OpenSSL, the screen contents. + * - Strengthen the entropy for 100 ms using repeated SHA512. * * When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and * (up to) the first 32 bytes of H are produced as output, while the last 32 bytes From aa110649c521dacb43335ed40418e96923872163 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 1 Mar 2019 17:03:58 +0100 Subject: [PATCH 58/60] Add ChaCha20 encryption option (XOR) Cherry-picked from: 2bc2b8b49ad87376d98591b14403dc3b46e3166b --- src/crypto/chacha20.cpp | 132 +++++++++++++++++++++++++++++++++++++- src/crypto/chacha20.h | 18 ++++-- src/random.cpp | 2 +- src/random.h | 2 +- src/test/crypto_tests.cpp | 47 +++++++++++--- 5 files changed, 185 insertions(+), 16 deletions(-) diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp index 816ae870e13..7c9c9c82c90 100644 --- a/src/crypto/chacha20.cpp +++ b/src/crypto/chacha20.cpp @@ -71,7 +71,7 @@ void ChaCha20::Seek(uint64_t pos) input[13] = pos >> 32; } -void ChaCha20::Output(unsigned char* c, size_t bytes) +void ChaCha20::Keystream(unsigned char* c, size_t bytes) { uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; @@ -178,3 +178,133 @@ void ChaCha20::Output(unsigned char* c, size_t bytes) c += 64; } } + +void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes) +{ + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + unsigned char *ctarget = nullptr; + unsigned char tmp[64]; + unsigned int i; + + if (!bytes) return; + + j0 = input[0]; + j1 = input[1]; + j2 = input[2]; + j3 = input[3]; + j4 = input[4]; + j5 = input[5]; + j6 = input[6]; + j7 = input[7]; + j8 = input[8]; + j9 = input[9]; + j10 = input[10]; + j11 = input[11]; + j12 = input[12]; + j13 = input[13]; + j14 = input[14]; + j15 = input[15]; + + for (;;) { + if (bytes < 64) { + // if m has fewer than 64 bytes available, copy m to tmp and + // read from tmp instead + for (i = 0;i < bytes;++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 += j0; + x1 += j1; + x2 += j2; + x3 += j3; + x4 += j4; + x5 += j5; + x6 += j6; + x7 += j7; + x8 += j8; + x9 += j9; + x10 += j10; + x11 += j11; + x12 += j12; + x13 += j13; + x14 += j14; + x15 += j15; + + x0 ^= ReadLE32(m + 0); + x1 ^= ReadLE32(m + 4); + x2 ^= ReadLE32(m + 8); + x3 ^= ReadLE32(m + 12); + x4 ^= ReadLE32(m + 16); + x5 ^= ReadLE32(m + 20); + x6 ^= ReadLE32(m + 24); + x7 ^= ReadLE32(m + 28); + x8 ^= ReadLE32(m + 32); + x9 ^= ReadLE32(m + 36); + x10 ^= ReadLE32(m + 40); + x11 ^= ReadLE32(m + 44); + x12 ^= ReadLE32(m + 48); + x13 ^= ReadLE32(m + 52); + x14 ^= ReadLE32(m + 56); + x15 ^= ReadLE32(m + 60); + + ++j12; + if (!j12) ++j13; + + WriteLE32(c + 0, x0); + WriteLE32(c + 4, x1); + WriteLE32(c + 8, x2); + WriteLE32(c + 12, x3); + WriteLE32(c + 16, x4); + WriteLE32(c + 20, x5); + WriteLE32(c + 24, x6); + WriteLE32(c + 28, x7); + WriteLE32(c + 32, x8); + WriteLE32(c + 36, x9); + WriteLE32(c + 40, x10); + WriteLE32(c + 44, x11); + WriteLE32(c + 48, x12); + WriteLE32(c + 52, x13); + WriteLE32(c + 56, x14); + WriteLE32(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + input[12] = j12; + input[13] = j13; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h index a305977bcd5..5a4674f4a8a 100644 --- a/src/crypto/chacha20.h +++ b/src/crypto/chacha20.h @@ -8,7 +8,8 @@ #include #include -/** A PRNG class for ChaCha20. */ +/** A class for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein + https://cr.yp.to/chacha/chacha-20080128.pdf */ class ChaCha20 { private: @@ -17,10 +18,17 @@ class ChaCha20 public: ChaCha20(); ChaCha20(const unsigned char* key, size_t keylen); - void SetKey(const unsigned char* key, size_t keylen); - void SetIV(uint64_t iv); - void Seek(uint64_t pos); - void Output(unsigned char* output, size_t bytes); + void SetKey(const unsigned char* key, size_t keylen); //!< set key with flexible keylength; 256bit recommended */ + void SetIV(uint64_t iv); // set the 64bit nonce + void Seek(uint64_t pos); // set the 64bit block counter + + /** outputs the keystream of size into */ + void Keystream(unsigned char* c, size_t bytes); + + /** enciphers the message of length and write the enciphered representation into + * Used for encryption and decryption (XOR) + */ + void Crypt(const unsigned char* input, unsigned char* output, size_t bytes); }; #endif // BITCOIN_CRYPTO_CHACHA20_H diff --git a/src/random.cpp b/src/random.cpp index 044888cfd6f..97c0b6d8220 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -600,7 +600,7 @@ std::vector FastRandomContext::randbytes(size_t len) { std::vector ret(len); if (len > 0) { - rng.Output(&ret[0], len); + rng.Keystream(&ret[0], len); } return ret; } diff --git a/src/random.h b/src/random.h index 90d39a4bc3d..7c76de82db3 100644 --- a/src/random.h +++ b/src/random.h @@ -112,7 +112,7 @@ class FastRandomContext { if (requires_seed) { RandomSeed(); } - rng.Output(bytebuf, sizeof(bytebuf)); + rng.Keystream(bytebuf, sizeof(bytebuf)); bytebuf_size = sizeof(bytebuf); } diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index a4f0e17c997..f07422db3e0 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -188,17 +188,36 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad } } -void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout) +static void TestChaCha20(const std::string &hex_message, const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout) { std::vector key = ParseHex(hexkey); + std::vector m = ParseHex(hex_message); ChaCha20 rng(key.data(), key.size()); rng.SetIV(nonce); rng.Seek(seek); std::vector out = ParseHex(hexout); std::vector outres; outres.resize(out.size()); - rng.Output(outres.data(), outres.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 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); + } } std::string LongTestString(void) { @@ -457,25 +476,37 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) { BOOST_AUTO_TEST_CASE(chacha20_testvector) { // Test vector from RFC 7539 - TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, + + // test encryption + TestChaCha20("4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756" + "c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e" + "20776f756c642062652069742e", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, + "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d" + "624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74" + "a35be6b40b8eedf2785e42874d" + ); + + // test keystream output + TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb" "a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a" "832c89c167eacd901d7e2bf363"); // Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 - TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0, + TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 0, 0, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b" "8f41518a11cc387b669b2ee6586"); - TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0, + TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000001", 0, 0, "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79" "2b1c43fea817e9ad275ae546963"); - TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0, + TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0, "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770" "62eb7a0433e445f41e3"); - TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0, + TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 1, 0, "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4" "97a0b466e7d6bbdb0041b2f586b"); - TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0, + TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0, "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b" "e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1" "18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5" From 2112bbb918edac0d3d5062be7f1d85d1bc319e24 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 5 Mar 2019 10:50:50 +0100 Subject: [PATCH 59/60] Add ChaCha20 bench Cherry-picked from: 2dfe2751713c814aea53b5a7563eb74ad1baea00 --- src/Makefile.bench.include | 1 + src/bench/chacha20.cpp | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/bench/chacha20.cpp diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index e603216a2b7..9800ad412c3 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -17,6 +17,7 @@ bench_bench_dogecoin_SOURCES = \ bench/checkqueue.cpp \ bench/Examples.cpp \ bench/rollingbloom.cpp \ + bench/chacha20.cpp \ bench/crypto_hash.cpp \ bench/ccoins_caching.cpp \ bench/mempool_eviction.cpp \ diff --git a/src/bench/chacha20.cpp b/src/bench/chacha20.cpp new file mode 100644 index 00000000000..7c25adf9cba --- /dev/null +++ b/src/bench/chacha20.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include + +/* Number of bytes to process per iteration */ +static const uint64_t BUFFER_SIZE_TINY = 64; +static const uint64_t BUFFER_SIZE_SMALL = 256; +static const uint64_t BUFFER_SIZE_LARGE = 1024*1024; + +static void CHACHA20(benchmark::State& state, size_t buffersize) +{ + std::vector key(32,0); + ChaCha20 ctx(key.data(), key.size()); + ctx.SetIV(0); + ctx.Seek(0); + std::vector in(buffersize,0); + std::vector out(buffersize,0); + while (state.KeepRunning()) { + ctx.Crypt(in.data(), out.data(), in.size()); + } +} + +static void CHACHA20_64BYTES(benchmark::State& state) +{ + CHACHA20(state, BUFFER_SIZE_TINY); +} + +static void CHACHA20_256BYTES(benchmark::State& state) +{ + CHACHA20(state, BUFFER_SIZE_SMALL); +} + +static void CHACHA20_1MB(benchmark::State& state) +{ + CHACHA20(state, BUFFER_SIZE_LARGE); +} + +BENCHMARK(CHACHA20_64BYTES); +BENCHMARK(CHACHA20_256BYTES); +BENCHMARK(CHACHA20_1MB); From 4ebd97bf7f7cf0e142714d411fb8cc3262dac848 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 22 Apr 2022 12:28:28 -0400 Subject: [PATCH 60/60] Unroll the ChaCha20 inner loop for performance Cherry-picked from: 81c09ee45caecf8d9daf6766b94cebf54f3f08cd --- src/crypto/chacha20.cpp | 48 ++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp index 7c9c9c82c90..cf3522d208c 100644 --- a/src/crypto/chacha20.cpp +++ b/src/crypto/chacha20.cpp @@ -18,6 +18,8 @@ constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | ( a += b; d = rotl32(d ^ a, 8); \ c += d; b = rotl32(b ^ c, 7); +#define REPEAT10(a) do { {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; } while(0) + static const unsigned char sigma[] = "expand 32-byte k"; static const unsigned char tau[] = "expand 16-byte k"; @@ -119,16 +121,19 @@ void ChaCha20::Keystream(unsigned char* c, size_t bytes) x13 = j13; x14 = j14; x15 = j15; - for (i = 20;i > 0;i -= 2) { - QUARTERROUND( x0, x4, x8,x12) - QUARTERROUND( x1, x5, x9,x13) - QUARTERROUND( x2, x6,x10,x14) - QUARTERROUND( x3, x7,x11,x15) - QUARTERROUND( x0, x5,x10,x15) - QUARTERROUND( x1, x6,x11,x12) - QUARTERROUND( x2, x7, x8,x13) - QUARTERROUND( x3, x4, x9,x14) - } + + // The 20 inner ChaCha20 rounds are unrolled here for performance. + REPEAT10( + QUARTERROUND( x0, x4, x8,x12); + QUARTERROUND( x1, x5, x9,x13); + QUARTERROUND( x2, x6,x10,x14); + QUARTERROUND( x3, x7,x11,x15); + QUARTERROUND( x0, x5,x10,x15); + QUARTERROUND( x1, x6,x11,x12); + QUARTERROUND( x2, x7, x8,x13); + QUARTERROUND( x3, x4, x9,x14); + ); + x0 += j0; x1 += j1; x2 += j2; @@ -231,16 +236,19 @@ void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes) x13 = j13; x14 = j14; x15 = j15; - for (i = 20;i > 0;i -= 2) { - QUARTERROUND( x0, x4, x8,x12) - QUARTERROUND( x1, x5, x9,x13) - QUARTERROUND( x2, x6,x10,x14) - QUARTERROUND( x3, x7,x11,x15) - QUARTERROUND( x0, x5,x10,x15) - QUARTERROUND( x1, x6,x11,x12) - QUARTERROUND( x2, x7, x8,x13) - QUARTERROUND( x3, x4, x9,x14) - } + + // The 20 inner ChaCha20 rounds are unrolled here for performance. + REPEAT10( + QUARTERROUND( x0, x4, x8,x12); + QUARTERROUND( x1, x5, x9,x13); + QUARTERROUND( x2, x6,x10,x14); + QUARTERROUND( x3, x7,x11,x15); + QUARTERROUND( x0, x5,x10,x15); + QUARTERROUND( x1, x6,x11,x12); + QUARTERROUND( x2, x7, x8,x13); + QUARTERROUND( x3, x4, x9,x14); + ); + x0 += j0; x1 += j1; x2 += j2;