diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index ebafa2dfb..fdefe09e0 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -1,95 +1,95 @@ // 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 #include "bench.h" #include "bloom.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/sha512.h" #include "hash.h" #include "random.h" #include "uint256.h" #include "utiltime.h" /* Number of bytes to hash per iteration */ static const uint64_t BUFFER_SIZE = 1000 * 1000; static void RIPEMD160(benchmark::State &state) { uint8_t hash[CRIPEMD160::OUTPUT_SIZE]; std::vector in(BUFFER_SIZE, 0); while (state.KeepRunning()) CRIPEMD160().Write(in.data(), in.size()).Finalize(hash); } static void SHA1(benchmark::State &state) { uint8_t hash[CSHA1::OUTPUT_SIZE]; std::vector in(BUFFER_SIZE, 0); while (state.KeepRunning()) CSHA1().Write(in.data(), in.size()).Finalize(hash); } static void SHA256(benchmark::State &state) { uint8_t hash[CSHA256::OUTPUT_SIZE]; std::vector in(BUFFER_SIZE, 0); while (state.KeepRunning()) CSHA256().Write(in.data(), in.size()).Finalize(hash); } static void SHA256_32b(benchmark::State &state) { std::vector in(32, 0); while (state.KeepRunning()) { for (int i = 0; i < 1000000; i++) { CSHA256().Write(in.data(), in.size()).Finalize(&in[0]); } } } static void SHA512(benchmark::State &state) { uint8_t hash[CSHA512::OUTPUT_SIZE]; std::vector in(BUFFER_SIZE, 0); while (state.KeepRunning()) CSHA512().Write(in.data(), in.size()).Finalize(hash); } static void SipHash_32b(benchmark::State &state) { uint256 x; while (state.KeepRunning()) { for (int i = 0; i < 1000000; i++) { *((uint64_t *)x.begin()) = SipHashUint256(0, i, x); } } } static void FastRandom_32bit(benchmark::State &state) { FastRandomContext rng(true); - uint32_t x; + uint32_t x = 0; 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; + uint32_t x = 0; while (state.KeepRunning()) { for (int i = 0; i < 1000000; i++) { x += rng.randbool(); } } } BENCHMARK(RIPEMD160); BENCHMARK(SHA1); BENCHMARK(SHA256); BENCHMARK(SHA512); BENCHMARK(SHA256_32b); BENCHMARK(SipHash_32b); BENCHMARK(FastRandom_32bit); BENCHMARK(FastRandom_1bit); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index b68985fdd..97757f836 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -1,899 +1,899 @@ // 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. #include "paymentserver.h" #include "bitcoinunits.h" #include "guiutil.h" #include "optionsmodel.h" #include "chainparams.h" #include "config.h" #include "dstencode.h" #include "policy/policy.h" #include "ui_interface.h" #include "util.h" #include "wallet/wallet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION < 0x050000 #include #else #include #endif const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds // 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/bitcoincash-payment"; const char *BIP71_MIMETYPE_PAYMENTACK = "application/bitcoincash-paymentack"; const char *BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoincash-paymentrequest"; 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 // data directory // static QString ipcServerName() { QString name("BitcoinQt"); // Append a simple hash of the datadir // Note that GetDataDir(true) returns a different path for -testnet versus // main net QString ddir(GUIUtil::boostPathToQString(GetDataDir(true))); name.append(QString::number(qHash(ddir))); return name; } // // We store payment URIs and requests received before the main GUI window is up // and ready to ask the user to send payment. // static QList savedPaymentRequests; static void ReportInvalidCertificate(const QSslCertificate &cert) { #if QT_VERSION < 0x050000 qDebug() << QString("%1: Payment server found an invalid certificate: ") .arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName); #else 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); #endif } // // 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(gArgs.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(); for (const QSslCertificate &cert : certList) { // Don't log nullptr certificates if (cert.isNull()) { continue; } // Not yet active/valid, or expired certificate if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) { ReportInvalidCertificate(cert); continue; } #if QT_VERSION >= 0x050000 // Blacklisted certificate if (cert.isBlacklisted()) { ReportInvalidCertificate(cert); continue; } #endif QByteArray certData = cert.toDer(); const uint8_t *data = (const uint8_t *)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 } static std::string ipcParseURI(const QString &arg, const CChainParams ¶ms, bool useCashAddr) { const QString scheme = GUIUtil::bitcoinURIScheme(params, useCashAddr); if (!arg.startsWith(scheme + ":", Qt::CaseInsensitive)) { return {}; } SendCoinsRecipient r; if (!GUIUtil::parseBitcoinURI(scheme, arg, &r)) { return {}; } return r.address.toStdString(); } static bool ipcCanParseCashAddrURI(const QString &arg, const std::string &network) { auto tempChainParams = CreateChainParams(network); std::string addr = ipcParseURI(arg, *tempChainParams, true); return IsValidDestinationString(addr, *tempChainParams); } static bool ipcCanParseLegacyURI(const QString &arg, const std::string &network) { auto tempChainParams = CreateChainParams(network); std::string addr = ipcParseURI(arg, *tempChainParams, false); return IsValidDestinationString(addr, *tempChainParams); } // // Sending to the server is done synchronously, at startup. // If the server isn't already running, startup continues, and the items in // savedPaymentRequest will be handled when uiReady() is called. // // Warning: ipcSendCommandLine() is called early in init, so don't use "Q_EMIT // message()", but "QMessageBox::"! // void PaymentServer::ipcParseCommandLine(int argc, char *argv[]) { - std::array networks = {&CBaseChainParams::MAIN, - &CBaseChainParams::TESTNET, - &CBaseChainParams::REGTEST}; + std::array networks = { + {&CBaseChainParams::MAIN, &CBaseChainParams::TESTNET, + &CBaseChainParams::REGTEST}}; const std::string *chosenNetwork = nullptr; for (int i = 1; i < argc; i++) { QString arg(argv[i]); if (arg.startsWith("-")) { continue; } const std::string *itemNetwork = nullptr; // Try to parse as a URI for (auto net : networks) { if (ipcCanParseCashAddrURI(arg, *net)) { itemNetwork = net; break; } if (ipcCanParseLegacyURI(arg, *net)) { itemNetwork = net; break; } } if (!itemNetwork && QFile::exists(arg)) { // Filename PaymentRequestPlus request; if (readPaymentRequestFromFile(arg, request)) { for (auto net : networks) { if (*net == request.getDetails().network()) { itemNetwork = net; } } } } if (itemNetwork == nullptr) { // Printing to debug.log is about the best we can do here, the GUI // hasn't started yet so we can't pop up a message box. qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " "file or URI does not exist or is invalid: " << arg; continue; } if (chosenNetwork && chosenNetwork != itemNetwork) { qWarning() << "PaymentServer::ipcSendCommandLine: Payment request " "from network " << QString(itemNetwork->c_str()) << " does not match already chosen network " << QString(chosenNetwork->c_str()); continue; } savedPaymentRequests.append(arg); chosenNetwork = itemNetwork; } if (chosenNetwork) { SelectParams(*chosenNetwork); } } // // Sending to the server is done synchronously, at startup. // If the server isn't already running, startup continues, and the items in // savedPaymentRequest will be handled when uiReady() is called. // bool PaymentServer::ipcSendCommandLine() { bool fResult = false; for (const QString &r : savedPaymentRequests) { QLocalSocket *socket = new QLocalSocket(); socket->connectToServer(ipcServerName(), QIODevice::WriteOnly); if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) { delete socket; socket = nullptr; return false; } QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_0); out << r; out.device()->seek(0); socket->write(block); socket->flush(); socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT); socket->disconnectFromServer(); delete socket; socket = nullptr; fResult = true; } return fResult; } PaymentServer::PaymentServer(QObject *parent, bool startLocalServer) : QObject(parent), saveURIs(true), uriServer(0), netManager(0), optionsModel(0) { // 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; // Install global event filter to catch QFileOpenEvents // on Mac: sent when you click bitcoincash: links // other OSes: helpful when dealing with payment request files if (parent) { parent->installEventFilter(this); } QString name = ipcServerName(); // Clean up old socket leftover from a crash: QLocalServer::removeServer(name); if (startLocalServer) { uriServer = new QLocalServer(this); if (!uriServer->listen(name)) { // constructor is called early in init, so don't use "Q_EMIT // message()" here QMessageBox::critical(0, tr("Payment request error"), tr("Cannot start click-to-pay handler")); } else { connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString))); } } } PaymentServer::~PaymentServer() { google::protobuf::ShutdownProtobufLibrary(); } // // OSX-specific way of handling bitcoincash: URIs and PaymentRequest mime types. // Also used by paymentservertests.cpp and when opening a payment request file // via "Open URI..." menu entry. // bool PaymentServer::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::FileOpen) { QFileOpenEvent *fileEvent = static_cast(event); if (!fileEvent->file().isEmpty()) { handleURIOrFile(fileEvent->file()); } else if (!fileEvent->url().isEmpty()) { handleURIOrFile(fileEvent->url().toString()); } return true; } return QObject::eventFilter(object, event); } void PaymentServer::initNetManager() { if (!optionsModel) { return; } if (netManager != nullptr) { delete netManager; } // netManager is used to fetch paymentrequests given in bitcoincash: 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; for (const QString &s : savedPaymentRequests) { handleURIOrFile(s); } savedPaymentRequests.clear(); } bool PaymentServer::handleURI(const QString &scheme, const QString &s) { if (!s.startsWith(scheme + ":", Qt::CaseInsensitive)) { return false; } #if QT_VERSION < 0x050000 QUrl uri(s); #else QUrlQuery uri((QUrl(s))); #endif if (uri.hasQueryItem("r")) { // payment request URI QByteArray temp; temp.append(uri.queryItemValue("r")); 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 true; } // normal URI SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(scheme, s, &recipient)) { if (!IsValidDestinationString(recipient.address.toStdString())) { Q_EMIT message( tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), CClientUIInterface::MSG_ERROR); } else { Q_EMIT receivedPaymentRequest(recipient); } } else { Q_EMIT message( tr("URI handling"), tr("URI cannot be parsed! This can be caused by an invalid " "Bitcoin address or malformed URI parameters."), CClientUIInterface::ICON_WARNING); } return true; } void PaymentServer::handleURIOrFile(const QString &s) { if (saveURIs) { savedPaymentRequests.append(s); return; } // bitcoincash: CashAddr URI QString schemeCash = GUIUtil::bitcoinURIScheme(Params(), true); if (handleURI(schemeCash, s)) { return; } // bitcoincash: Legacy URI QString schemeLegacy = GUIUtil::bitcoinURIScheme(Params(), false); if (handleURI(schemeLegacy, s)) { return; } // payment request file 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."), CClientUIInterface::ICON_WARNING); } else if (processPaymentRequest(request, recipient)) { Q_EMIT receivedPaymentRequest(recipient); } return; } } void PaymentServer::handleURIConnection() { QLocalSocket *clientConnection = uriServer->nextPendingConnection(); while (clientConnection->bytesAvailable() < (int)sizeof(quint32)) { clientConnection->waitForReadyRead(); } connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater())); QDataStream in(clientConnection); in.setVersion(QDataStream::Qt_4_0); if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) { return; } QString msg; in >> msg; 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 std::pair &sendingTo : sendingTos) { // Extract and check destination addresses CTxDestination dest; if (ExtractDestination(sendingTo.first, dest)) { // Append destination address addresses.append(QString::fromStdString(EncodeDestination(dest))); } 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 Amount 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(Amount(sendingTo.second), sendingTo.first); if (txOut.IsDust(dustRelayFee)) { Q_EMIT message( tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered " "dust).") .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"; } } int length = payment.ByteSize(); 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; for (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) { bool fVerified = requestDetails.network() == Params().NetworkIDString(); if (!fVerified) { qWarning() << QString("PaymentServer::%1: Payment request network " "\"%2\" doesn't match client network \"%3\".") .arg(__func__) .arg(QString::fromStdString(requestDetails.network())) .arg(QString::fromStdString( Params().NetworkIDString())); } 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 Amount requestAmount) { bool fVerified = MoneyRange(Amount(requestAmount)); if (!fVerified) { qWarning() << QString("PaymentServer::%1: Payment request amount out " "of allowed range (%2, allowed 0 - %3).") .arg(__func__) .arg(requestAmount.GetSatoshis()) .arg(MAX_MONEY.GetSatoshis()); } return fVerified; } X509_STORE *PaymentServer::getCertStore() { return certStore.get(); } diff --git a/src/seeder/bitcoin.cpp b/src/seeder/bitcoin.cpp index d0e7cf7e5..46480610c 100644 --- a/src/seeder/bitcoin.cpp +++ b/src/seeder/bitcoin.cpp @@ -1,340 +1,340 @@ #include "bitcoin.h" #include "db.h" #include "hash.h" #include "netbase.h" #include "serialize.h" #include "streams.h" #include "uint256.h" #include // Weither we are on testnet or mainnet. bool fTestNet; // The network magic to use. -CMessageHeader::MessageMagic netMagic = {0xe3, 0xe1, 0xf3, 0xe8}; +CMessageHeader::MessageMagic netMagic = {{0xe3, 0xe1, 0xf3, 0xe8}}; #define BITCOIN_SEED_NONCE 0x0539a019ca550825ULL static const uint32_t allones(-1); class CSeederNode { SOCKET sock; CDataStream vSend; CDataStream vRecv; uint32_t nHeaderStart; uint32_t nMessageStart; int nVersion; std::string strSubVer; int nStartingHeight; std::vector *vAddr; int ban; int64_t doneAfter; CAddress you; int GetTimeout() { return you.IsTor() ? 120 : 30; } void BeginMessage(const char *pszCommand) { if (nHeaderStart != allones) { AbortMessage(); } nHeaderStart = vSend.size(); vSend << CMessageHeader(netMagic, pszCommand, 0); nMessageStart = vSend.size(); // printf("%s: SEND %s\n", ToString(you).c_str(), pszCommand); } void AbortMessage() { if (nHeaderStart == allones) { return; } vSend.resize(nHeaderStart); nHeaderStart = allones; nMessageStart = allones; } void EndMessage() { if (nHeaderStart == allones) { return; } uint32_t nSize = vSend.size() - nMessageStart; memcpy((char *)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize)); if (vSend.GetVersion() >= 209) { uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end()); unsigned int nChecksum = 0; memcpy(&nChecksum, &hash, sizeof(nChecksum)); assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, pchChecksum) + sizeof(nChecksum)); memcpy((char *)&vSend[nHeaderStart] + offsetof(CMessageHeader, pchChecksum), &nChecksum, sizeof(nChecksum)); } nHeaderStart = allones; nMessageStart = allones; } void Send() { if (sock == INVALID_SOCKET) { return; } if (vSend.empty()) { return; } int nBytes = send(sock, &vSend[0], vSend.size(), 0); if (nBytes > 0) { vSend.erase(vSend.begin(), vSend.begin() + nBytes); } else { close(sock); sock = INVALID_SOCKET; } } void PushVersion() { int64_t nTime = time(nullptr); uint64_t nLocalNonce = BITCOIN_SEED_NONCE; int64_t nLocalServices = 0; CService myService; CAddress me(myService, ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH)); BeginMessage("version"); int nBestHeight = GetRequireHeight(); std::string ver = "/bitcoin-cash-seeder:0.15/"; vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight; EndMessage(); } void GotVersion() { // printf("\n%s: version %i\n", ToString(you).c_str(), nVersion); if (vAddr) { BeginMessage("getaddr"); EndMessage(); doneAfter = time(nullptr) + GetTimeout(); } else { doneAfter = time(nullptr) + 1; } } bool ProcessMessage(std::string strCommand, CDataStream &vRecv) { // printf("%s: RECV %s\n", ToString(you).c_str(), // strCommand.c_str()); if (strCommand == "version") { int64_t nTime; CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; uint64_t nServiceInt; vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; you.nServices = ServiceFlags(nServiceInt); if (nVersion == 10300) nVersion = 300; if (nVersion >= 106 && !vRecv.empty()) vRecv >> addrFrom >> nNonce; if (nVersion >= 106 && !vRecv.empty()) vRecv >> strSubVer; if (nVersion >= 209 && !vRecv.empty()) vRecv >> nStartingHeight; if (nVersion >= 209) { BeginMessage("verack"); EndMessage(); } vSend.SetVersion(std::min(nVersion, PROTOCOL_VERSION)); if (nVersion < 209) { this->vRecv.SetVersion(std::min(nVersion, PROTOCOL_VERSION)); GotVersion(); } return false; } if (strCommand == "verack") { this->vRecv.SetVersion(std::min(nVersion, PROTOCOL_VERSION)); GotVersion(); return false; } if (strCommand == "addr" && vAddr) { std::vector vAddrNew; vRecv >> vAddrNew; // printf("%s: got %i addresses\n", ToString(you).c_str(), // (int)vAddrNew.size()); int64_t now = time(nullptr); std::vector::iterator it = vAddrNew.begin(); if (vAddrNew.size() > 1) { if (doneAfter == 0 || doneAfter > now + 1) doneAfter = now + 1; } while (it != vAddrNew.end()) { CAddress &addr = *it; // printf("%s: got address %s\n", ToString(you).c_str(), // addr.ToString().c_str(), (int)(vAddr->size())); it++; if (addr.nTime <= 100000000 || addr.nTime > now + 600) addr.nTime = now - 5 * 86400; if (addr.nTime > now - 604800) vAddr->push_back(addr); // printf("%s: added address %s (#%i)\n", // ToString(you).c_str(), addr.ToString().c_str(), // (int)(vAddr->size())); if (vAddr->size() > 1000) { doneAfter = 1; return true; } } return false; } return false; } bool ProcessMessages() { if (vRecv.empty()) { return false; } do { CDataStream::iterator pstart = std::search( vRecv.begin(), vRecv.end(), BEGIN(netMagic), END(netMagic)); uint32_t nHeaderSize = GetSerializeSize( CMessageHeader(netMagic), vRecv.GetType(), vRecv.GetVersion()); if (vRecv.end() - pstart < nHeaderSize) { if (vRecv.size() > nHeaderSize) { vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); } break; } vRecv.erase(vRecv.begin(), pstart); std::vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); CMessageHeader hdr(netMagic); vRecv >> hdr; if (!hdr.IsValidWithoutConfig(netMagic)) { // printf("%s: BAD (invalid header)\n", ToString(you).c_str()); ban = 100000; return true; } std::string strCommand = hdr.GetCommand(); unsigned int nMessageSize = hdr.nMessageSize; if (nMessageSize > MAX_SIZE) { // printf("%s: BAD (message too large)\n", // ToString(you).c_str()); ban = 100000; return true; } if (nMessageSize > vRecv.size()) { vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); break; } if (vRecv.GetVersion() >= 209) { uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) { continue; } } CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.GetType(), vRecv.GetVersion()); vRecv.ignore(nMessageSize); if (ProcessMessage(strCommand, vMsg)) return true; // printf("%s: done processing %s\n", ToString(you).c_str(), // strCommand.c_str()); } while (1); return false; } public: CSeederNode(const CService &ip, std::vector *vAddrIn) : vSend(SER_NETWORK, 0), vRecv(SER_NETWORK, 0), nHeaderStart(-1), nMessageStart(-1), nVersion(0), vAddr(vAddrIn), ban(0), doneAfter(0), you(ip, ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH)) { if (time(nullptr) > 1329696000) { vSend.SetVersion(209); vRecv.SetVersion(209); } } bool Run() { bool proxyConnectionFailed = false; if (!ConnectSocket(you, sock, nConnectTimeout, &proxyConnectionFailed)) { return false; } PushVersion(); Send(); bool res = true; int64_t now; while (now = time(nullptr), ban == 0 && (doneAfter == 0 || doneAfter > now) && sock != INVALID_SOCKET) { char pchBuf[0x10000]; fd_set set; FD_ZERO(&set); FD_SET(sock, &set); struct timeval wa; if (doneAfter) { wa.tv_sec = doneAfter - now; wa.tv_usec = 0; } else { wa.tv_sec = GetTimeout(); wa.tv_usec = 0; } int ret = select(sock + 1, &set, nullptr, &set, &wa); if (ret != 1) { if (!doneAfter) res = false; break; } int nBytes = recv(sock, pchBuf, sizeof(pchBuf), 0); int nPos = vRecv.size(); if (nBytes > 0) { vRecv.resize(nPos + nBytes); memcpy(&vRecv[nPos], pchBuf, nBytes); } else if (nBytes == 0) { // printf("%s: BAD (connection closed prematurely)\n", // ToString(you).c_str()); res = false; break; } else { // printf("%s: BAD (connection error)\n", // ToString(you).c_str()); res = false; break; } ProcessMessages(); Send(); } if (sock == INVALID_SOCKET) res = false; close(sock); sock = INVALID_SOCKET; return (ban == 0) && res; } int GetBan() { return ban; } int GetClientVersion() { return nVersion; } std::string GetClientSubVersion() { return strSubVer; } int GetStartingHeight() { return nStartingHeight; } }; bool TestNode(const CService &cip, int &ban, int &clientV, std::string &clientSV, int &blocks, std::vector *vAddr) { try { CSeederNode node(cip, vAddr); bool ret = node.Run(); if (!ret) { ban = node.GetBan(); } else { ban = 0; } clientV = node.GetClientVersion(); clientSV = node.GetClientSubVersion(); blocks = node.GetStartingHeight(); // printf("%s: %s!!!\n", cip.ToString().c_str(), ret ? "GOOD" : "BAD"); return ret; } catch (std::ios_base::failure &e) { ban = 0; return false; } } diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp index 3d415cd24..2688eeb82 100644 --- a/src/test/amount_tests.cpp +++ b/src/test/amount_tests.cpp @@ -1,163 +1,163 @@ // 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 "amount.h" #include "test/test_bitcoin.h" #include #include BOOST_FIXTURE_TEST_SUITE(amount_tests, BasicTestingSetup) static void CheckAmounts(int64_t aval, int64_t bval) { Amount a(aval), b(bval); // Equality BOOST_CHECK_EQUAL(a == b, aval == bval); BOOST_CHECK_EQUAL(b == a, aval == bval); BOOST_CHECK_EQUAL(a != b, aval != bval); BOOST_CHECK_EQUAL(b != a, aval != bval); // Comparison BOOST_CHECK_EQUAL(a < b, aval < bval); BOOST_CHECK_EQUAL(b < a, bval < aval); BOOST_CHECK_EQUAL(a > b, aval > bval); BOOST_CHECK_EQUAL(b > a, bval > aval); BOOST_CHECK_EQUAL(a <= b, aval <= bval); BOOST_CHECK_EQUAL(b <= a, bval <= aval); BOOST_CHECK_EQUAL(a >= b, aval >= bval); BOOST_CHECK_EQUAL(b >= a, bval >= aval); // Unary minus BOOST_CHECK_EQUAL(-a, Amount(-aval)); BOOST_CHECK_EQUAL(-b, Amount(-bval)); // Addition and subtraction. BOOST_CHECK_EQUAL(a + b, b + a); BOOST_CHECK_EQUAL(a + b, Amount(aval + bval)); BOOST_CHECK_EQUAL(a - b, -(b - a)); BOOST_CHECK_EQUAL(a - b, Amount(aval - bval)); // Multiplication BOOST_CHECK_EQUAL(aval * b, bval * a); BOOST_CHECK_EQUAL(aval * b, Amount(aval * bval)); // Division if (b != Amount(0)) { BOOST_CHECK_EQUAL(a / b, aval / bval); BOOST_CHECK_EQUAL(a / bval, Amount(a / b)); } if (a != Amount(0)) { BOOST_CHECK_EQUAL(b / a, bval / aval); BOOST_CHECK_EQUAL(b / aval, Amount(b / a)); } // Modulus if (b != Amount(0)) { BOOST_CHECK_EQUAL(a % b, aval % bval); BOOST_CHECK_EQUAL(a % bval, Amount(a % b)); } if (a != Amount(0)) { BOOST_CHECK_EQUAL(b % a, bval % aval); BOOST_CHECK_EQUAL(b % aval, Amount(b % a)); } // OpAssign Amount v(0); v += a; BOOST_CHECK_EQUAL(v, a); v += b; BOOST_CHECK_EQUAL(v, a + b); v += b; BOOST_CHECK_EQUAL(v, a + 2 * b); v -= 2 * a; BOOST_CHECK_EQUAL(v, 2 * b - a); } BOOST_AUTO_TEST_CASE(AmountTests) { - std::array values = {-23, -1, 0, 1, 2, 3, 42, 99999999}; + std::array values = {{-23, -1, 0, 1, 2, 3, 42, 99999999}}; for (int64_t i : values) { for (int64_t j : values) { CheckAmounts(i, j); } } BOOST_CHECK_EQUAL(COIN + COIN, Amount(2 * COIN)); BOOST_CHECK_EQUAL(2 * COIN + COIN, Amount(3 * COIN)); BOOST_CHECK_EQUAL(-1 * COIN + COIN, Amount(0)); BOOST_CHECK_EQUAL(COIN - COIN, Amount(0)); BOOST_CHECK_EQUAL(COIN - 2 * COIN, -1 * COIN); } BOOST_AUTO_TEST_CASE(GetFeeTest) { CFeeRate feeRate; feeRate = CFeeRate(Amount(0)); // Must always return 0 BOOST_CHECK_EQUAL(feeRate.GetFee(0), Amount(0)); BOOST_CHECK_EQUAL(feeRate.GetFee(1e5), Amount(0)); feeRate = CFeeRate(Amount(1000)); // Must always just return the arg BOOST_CHECK_EQUAL(feeRate.GetFee(0), Amount(0)); BOOST_CHECK_EQUAL(feeRate.GetFee(1), Amount(1)); BOOST_CHECK_EQUAL(feeRate.GetFee(121), Amount(121)); BOOST_CHECK_EQUAL(feeRate.GetFee(999), Amount(999)); BOOST_CHECK_EQUAL(feeRate.GetFee(1000), Amount(1000)); BOOST_CHECK_EQUAL(feeRate.GetFee(9000), Amount(9000)); feeRate = CFeeRate(Amount(-1000)); // Must always just return -1 * arg BOOST_CHECK_EQUAL(feeRate.GetFee(0), Amount(0)); BOOST_CHECK_EQUAL(feeRate.GetFee(1), Amount(-1)); BOOST_CHECK_EQUAL(feeRate.GetFee(121), Amount(-121)); BOOST_CHECK_EQUAL(feeRate.GetFee(999), Amount(-999)); BOOST_CHECK_EQUAL(feeRate.GetFee(1000), Amount(-1000)); BOOST_CHECK_EQUAL(feeRate.GetFee(9000), Amount(-9000)); feeRate = CFeeRate(Amount(123)); // Truncates the result, if not integer BOOST_CHECK_EQUAL(feeRate.GetFee(0), Amount(0)); // Special case: returns 1 instead of 0 BOOST_CHECK_EQUAL(feeRate.GetFee(8), Amount(1)); BOOST_CHECK_EQUAL(feeRate.GetFee(9), Amount(1)); BOOST_CHECK_EQUAL(feeRate.GetFee(121), Amount(14)); BOOST_CHECK_EQUAL(feeRate.GetFee(122), Amount(15)); BOOST_CHECK_EQUAL(feeRate.GetFee(999), Amount(122)); BOOST_CHECK_EQUAL(feeRate.GetFee(1000), Amount(123)); BOOST_CHECK_EQUAL(feeRate.GetFee(9000), Amount(1107)); feeRate = CFeeRate(Amount(-123)); // Truncates the result, if not integer BOOST_CHECK_EQUAL(feeRate.GetFee(0), Amount(0)); // Special case: returns -1 instead of 0 BOOST_CHECK_EQUAL(feeRate.GetFee(8), Amount(-1)); BOOST_CHECK_EQUAL(feeRate.GetFee(9), Amount(-1)); // Check full constructor // default value BOOST_CHECK(CFeeRate(Amount(-1), 1000) == CFeeRate(Amount(-1))); BOOST_CHECK(CFeeRate(Amount(0), 1000) == CFeeRate(Amount(0))); BOOST_CHECK(CFeeRate(Amount(1), 1000) == CFeeRate(Amount(1))); // lost precision (can only resolve satoshis per kB) BOOST_CHECK(CFeeRate(Amount(1), 1001) == CFeeRate(Amount(0))); BOOST_CHECK(CFeeRate(Amount(2), 1001) == CFeeRate(Amount(1))); // some more integer checks BOOST_CHECK(CFeeRate(Amount(26), 789) == CFeeRate(Amount(32))); BOOST_CHECK(CFeeRate(Amount(27), 789) == CFeeRate(Amount(34))); // Maximum size in bytes, should not crash CFeeRate(MAX_MONEY, std::numeric_limits::max() >> 1).GetFeePerK(); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/monolith_opcodes.cpp b/src/test/monolith_opcodes.cpp index 2e5362dcc..e39350526 100644 --- a/src/test/monolith_opcodes.cpp +++ b/src/test/monolith_opcodes.cpp @@ -1,789 +1,789 @@ // Copyright (c) 2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "test/test_bitcoin.h" #include "policy/policy.h" #include "script/interpreter.h" #include #include typedef std::vector valtype; typedef std::vector stacktype; -std::array flagset{0, STANDARD_SCRIPT_VERIFY_FLAGS, - MANDATORY_SCRIPT_VERIFY_FLAGS}; +std::array flagset{ + {0, STANDARD_SCRIPT_VERIFY_FLAGS, MANDATORY_SCRIPT_VERIFY_FLAGS}}; BOOST_FIXTURE_TEST_SUITE(monolith_opcodes_tests, BasicTestingSetup) /** * General utility functions to check for script passing/failing. */ static void CheckTestResultForAllFlags(const stacktype &original_stack, const CScript &script, const stacktype &expected) { BaseSignatureChecker sigchecker; for (uint32_t flags : flagset) { ScriptError err = SCRIPT_ERR_OK; stacktype stack{original_stack}; bool r = EvalScript(stack, script, flags | SCRIPT_ENABLE_MONOLITH_OPCODES, sigchecker, &err); BOOST_CHECK(r); BOOST_CHECK(stack == expected); // Make sure that if we do not pass the monolith flag, opcodes are still // disabled. stack = original_stack; r = EvalScript(stack, script, flags, sigchecker, &err); BOOST_CHECK(!r); BOOST_CHECK_EQUAL(err, SCRIPT_ERR_DISABLED_OPCODE); } } static void CheckError(uint32_t flags, const stacktype &original_stack, const CScript &script, ScriptError expected_error) { BaseSignatureChecker sigchecker; ScriptError err = SCRIPT_ERR_OK; stacktype stack{original_stack}; bool r = EvalScript(stack, script, flags | SCRIPT_ENABLE_MONOLITH_OPCODES, sigchecker, &err); BOOST_CHECK(!r); BOOST_CHECK_EQUAL(err, expected_error); // Make sure that if we do not pass the monolith flag, opcodes are still // disabled. stack = original_stack; r = EvalScript(stack, script, flags, sigchecker, &err); BOOST_CHECK(!r); BOOST_CHECK_EQUAL(err, SCRIPT_ERR_DISABLED_OPCODE); } static void CheckErrorForAllFlags(const stacktype &original_stack, const CScript &script, ScriptError expected_error) { for (uint32_t flags : flagset) { CheckError(flags, original_stack, script, expected_error); } } static void CheckOpError(const stacktype &original_stack, opcodetype op, ScriptError expected_error) { CheckErrorForAllFlags(original_stack, CScript() << op, expected_error); } static void CheckAllBitwiseOpErrors(const stacktype &stack, ScriptError expected_error) { CheckOpError(stack, OP_AND, expected_error); CheckOpError(stack, OP_OR, expected_error); CheckOpError(stack, OP_XOR, expected_error); } static void CheckBinaryOp(const valtype &a, const valtype &b, opcodetype op, const valtype &expected) { CheckTestResultForAllFlags({a, b}, CScript() << op, {expected}); } static valtype NegativeValtype(const valtype &v) { valtype r(v); if (r.size() > 0) { r[r.size() - 1] ^= 0x80; } CScriptNum::MinimallyEncode(r); return r; } BOOST_AUTO_TEST_CASE(negative_valtype_test) { // Test zero values BOOST_CHECK(NegativeValtype({}) == valtype{}); BOOST_CHECK(NegativeValtype({0x00}) == valtype{}); BOOST_CHECK(NegativeValtype({0x80}) == valtype{}); BOOST_CHECK(NegativeValtype({0x00, 0x00}) == valtype{}); BOOST_CHECK(NegativeValtype({0x00, 0x80}) == valtype{}); // Non-zero values BOOST_CHECK(NegativeValtype({0x01}) == valtype{0x81}); BOOST_CHECK(NegativeValtype({0x81}) == valtype{0x01}); BOOST_CHECK(NegativeValtype({0x02, 0x01}) == (valtype{0x02, 0x81})); BOOST_CHECK(NegativeValtype({0x02, 0x81}) == (valtype{0x02, 0x01})); BOOST_CHECK(NegativeValtype({0xff, 0x02, 0x01}) == (valtype{0xff, 0x02, 0x81})); BOOST_CHECK(NegativeValtype({0xff, 0x02, 0x81}) == (valtype{0xff, 0x02, 0x01})); BOOST_CHECK(NegativeValtype({0xff, 0xff, 0x02, 0x01}) == (valtype{0xff, 0xff, 0x02, 0x81})); BOOST_CHECK(NegativeValtype({0xff, 0xff, 0x02, 0x81}) == (valtype{0xff, 0xff, 0x02, 0x01})); // Should not be overly-minimized BOOST_CHECK(NegativeValtype({0xff, 0x80}) == (valtype{0xff, 0x00})); BOOST_CHECK(NegativeValtype({0xff, 0x00}) == (valtype{0xff, 0x80})); } /** * Bitwise Opcodes */ static void RunTestForAllBitwiseOpcodes(const valtype &a, const valtype &b, const valtype &expected_and, const valtype &expected_or, const valtype &expected_xor) { // Bitwise ops are commutative, so we check both ways. CheckBinaryOp(a, b, OP_AND, expected_and); CheckBinaryOp(b, a, OP_AND, expected_and); CheckBinaryOp(a, b, OP_OR, expected_or); CheckBinaryOp(b, a, OP_OR, expected_or); CheckBinaryOp(a, b, OP_XOR, expected_xor); CheckBinaryOp(b, a, OP_XOR, expected_xor); } static void RunTestForAllBitwiseOpcodesSizes(const valtype &a, const valtype &b, const valtype &expected_and, const valtype &expected_or, const valtype &expected_xor) { valtype wa, wb, wand, wor, wxor; for (size_t i = 0; i < a.size(); i++) { wa.push_back(a[i]); wb.push_back(b[i]); wand.push_back(expected_and[i]); wor.push_back(expected_or[i]); wxor.push_back(expected_xor[i]); RunTestForAllBitwiseOpcodes(wa, wb, wand, wor, wxor); } } static void TestBitwiseOpcodes(const valtype &a, const valtype &b, const valtype &expected_and, const valtype &expected_or) { valtype expected_xor(expected_and.size()); for (size_t i = 0; i < a.size(); i++) { // A ^ B = (A | B) & ~(A & B) expected_xor[i] = expected_or[i] & ~expected_and[i]; } RunTestForAllBitwiseOpcodesSizes(a, b, expected_and, expected_or, expected_xor); valtype nota(a.size()); valtype notb(b.size()); valtype nand(expected_and.size()); valtype nor(expected_or.size()); for (size_t i = 0; i < a.size(); i++) { nota[i] = ~a[i]; notb[i] = ~b[i]; nand[i] = ~expected_and[i]; nor[i] = ~expected_or[i]; } // ~A & ~B == ~(A | B) // ~A | ~B == ~(A & B) // ~A ^ ~B == A ^ B RunTestForAllBitwiseOpcodesSizes(nota, notb, nor, nand, expected_xor); } BOOST_AUTO_TEST_CASE(bitwise_opcodes_test) { // Check that empty ops works. RunTestForAllBitwiseOpcodes({}, {}, {}, {}, {}); // Run all variations of zeros and ones. valtype allzeros(MAX_SCRIPT_ELEMENT_SIZE, 0); valtype allones(MAX_SCRIPT_ELEMENT_SIZE, 0xff); BOOST_CHECK_EQUAL(allzeros.size(), MAX_SCRIPT_ELEMENT_SIZE); BOOST_CHECK_EQUAL(allones.size(), MAX_SCRIPT_ELEMENT_SIZE); TestBitwiseOpcodes(allzeros, allzeros, allzeros, allzeros); TestBitwiseOpcodes(allzeros, allones, allzeros, allones); TestBitwiseOpcodes(allones, allones, allones, allones); // Let's use two random a and b. valtype a{ 0x34, 0x0e, 0x7e, 0x17, 0x83, 0x66, 0x1a, 0x81, 0x45, 0x8d, 0x26, 0x26, 0xbc, 0xbd, 0x56, 0xe7, 0xf2, 0x1c, 0xec, 0xf6, 0x79, 0x8c, 0x3e, 0x58, 0x0f, 0x86, 0xcf, 0x53, 0xbe, 0x66, 0x8f, 0xa7, 0xbe, 0xf6, 0x30, 0x12, 0x8d, 0x01, 0x00, 0x37, 0x7f, 0x5b, 0x64, 0x50, 0x63, 0x40, 0x6a, 0x44, 0xf5, 0x7e, 0x02, 0xc7, 0xab, 0x45, 0xcf, 0x6a, 0x98, 0x61, 0xe8, 0xb8, 0xc4, 0x9e, 0x11, 0xe8, 0x30, 0x71, 0x07, 0x73, 0xa2, 0x4d, 0xdd, 0xa6, 0x6c, 0xf4, 0x2a, 0x22, 0xa0, 0xac, 0xdc, 0xf4, 0xcc, 0xfb, 0x4d, 0xe3, 0x55, 0xde, 0x44, 0x46, 0x32, 0x36, 0x93, 0xb4, 0xd9, 0xd1, 0x3b, 0x06, 0x09, 0x6a, 0x64, 0xc3, 0x18, 0x58, 0xc4, 0x9f, 0x1b, 0x6a, 0xa3, 0xab, 0x59, 0x37, 0xbd, 0x36, 0x97, 0x35, 0x26, 0x87, 0x63, 0x58, 0x08, 0x6e, 0x5e, 0x46, 0xcf, 0x15, 0x33, 0xfc, 0x46, 0x45, 0x97, 0x61, 0x4b, 0xb8, 0xec, 0xdd, 0x1b, 0x69, 0x6e, 0x8a, 0x27, 0xf9, 0xcd, 0x4b, 0x5c, 0xa4, 0x84, 0x18, 0xd5, 0x23, 0x50, 0xc6, 0x63, 0xbe, 0xca, 0xd3, 0xd0, 0x91, 0x39, 0x16, 0x6a, 0x6e, 0xd6, 0x09, 0x18, 0x52, 0x05, 0x6a, 0xa7, 0xf7, 0x64, 0xa3, 0xf0, 0xba, 0x75, 0xc5, 0x9c, 0xf7, 0xbb, 0x70, 0x68, 0x65, 0x4f, 0xdb, 0xd0, 0x36, 0x14, 0xfb, 0x1a, 0xf6, 0x6e, 0xea, 0x8d, 0xc8, 0xa5, 0xad, 0x61, 0xc6, 0x04, 0x4c, 0xc3, 0xb9, 0x68, 0x8c, 0xa4, 0xe4, 0x04, 0xae, 0xee, 0xca, 0xe7, 0x52, 0xa7, 0xba, 0x16, 0x91, 0x26, 0x9b, 0xae, 0x31, 0xcd, 0x6f, 0x4e, 0x7e, 0x47, 0x60, 0x40, 0xf0, 0xbc, 0xe2, 0x20, 0xaf, 0xc1, 0x4f, 0x26, 0x54, 0x93, 0x37, 0xfc, 0xbf, 0x50, 0xd3, 0xf2, 0x30, 0x70, 0xfc, 0x67, 0x15, 0x82, 0xd3, 0x39, 0x27, 0xa2, 0x4f, 0xce, 0x10, 0xed, 0x11, 0x73, 0xc4, 0x48, 0xe9, 0x65, 0xa1, 0x5e, 0xf2, 0x0c, 0x81, 0x3b, 0x80, 0xe1, 0x9f, 0x53, 0x31, 0x49, 0x73, 0xc8, 0x0a, 0x6e, 0xa4, 0xe1, 0xe1, 0xe2, 0xac, 0xeb, 0x0b, 0xa5, 0x4b, 0xc5, 0x47, 0xf6, 0xf1, 0x15, 0x10, 0x31, 0xf0, 0xcb, 0x6f, 0xed, 0xd3, 0x50, 0x7d, 0xb2, 0x86, 0x87, 0xab, 0x62, 0x5c, 0x4c, 0x4b, 0xb0, 0x0a, 0x20, 0x19, 0xb9, 0x8c, 0x1a, 0xf5, 0xe6, 0x29, 0xa0, 0x8a, 0x55, 0x88, 0xa0, 0xf5, 0xef, 0xe6, 0x50, 0x6d, 0x36, 0x7b, 0x75, 0xe5, 0x14, 0xc8, 0xfb, 0xc6, 0x5b, 0xe7, 0x99, 0x37, 0x62, 0x56, 0xdb, 0x8f, 0x40, 0x43, 0x54, 0x8d, 0x68, 0x19, 0xc2, 0xf5, 0xc0, 0x37, 0xed, 0xee, 0x0e, 0xab, 0x0b, 0x77, 0x29, 0x27, 0xac, 0x07, 0x70, 0xfa, 0xa9, 0x69, 0x28, 0x51, 0xf5, 0x65, 0x58, 0x7a, 0xcc, 0xc9, 0xfe, 0x3c, 0xa0, 0x0d, 0x6e, 0x87, 0x38, 0x36, 0xb7, 0x1a, 0x41, 0x6c, 0x9a, 0x13, 0xfa, 0x86, 0x13, 0xe6, 0xc9, 0xec, 0x9f, 0x50, 0x15, 0xc3, 0x74, 0x4c, 0x29, 0x67, 0x0a, 0xa7, 0x7e, 0x7f, 0x3c, 0xab, 0xe9, 0x44, 0x61, 0x6e, 0x64, 0x50, 0x47, 0x1e, 0x17, 0x23, 0x64, 0x29, 0x9c, 0x9c, 0xef, 0x5b, 0x28, 0xe3, 0x0e, 0xa5, 0x2a, 0x2f, 0x2d, 0xc6, 0x6c, 0xd3, 0xaa, 0x03, 0x48, 0x15, 0x0c, 0x92, 0x80, 0x86, 0x2f, 0xc2, 0xbd, 0x5e, 0x82, 0x61, 0xa1, 0x88, 0xdd, 0x5e, 0xea, 0xef, 0x19, 0xf9, 0x84, 0x66, 0xf7, 0xbb, 0x44, 0xad, 0xf9, 0xf7, 0x2f, 0x2a, 0xd5, 0x37, 0xef, 0x28, 0x3d, 0x1a, 0xdc, 0x6c, 0xf1, 0xcc, 0xca, 0xd5, 0x2b, 0x58, 0x63, 0xc0, 0x34, 0x91, 0x87, 0xd9, 0x36, 0x2f, 0x90, 0xeb, 0xf1, 0xde, 0x8b, 0x8c, 0x20, 0x51, 0x83, 0xfd, 0xf4, 0xfd, 0xe7, 0x40, 0x68, 0xf3, 0x5a, 0x17, 0x80, 0x21, 0xf3, 0xc1, 0x90, 0x3c, 0x75, 0x23, 0x48, 0x1c, 0x98, 0xb5}; valtype b{ 0xd2, 0x9e, 0x99, 0xc9, 0xe7, 0x11, 0x7b, 0x0e, 0x4b, 0x8e, 0x11, 0x08, 0xd1, 0x5c, 0xf4, 0xb8, 0x2c, 0x14, 0x3f, 0x45, 0x75, 0xe9, 0x8a, 0xeb, 0x81, 0xf8, 0xd8, 0xa3, 0x8e, 0x4b, 0x63, 0x0e, 0x7f, 0x1e, 0xfd, 0x84, 0x83, 0x7c, 0x26, 0x1f, 0xf0, 0xc9, 0x37, 0x1c, 0x5f, 0xf5, 0xf3, 0x3d, 0x67, 0x2b, 0x27, 0x30, 0xdb, 0x3e, 0xe7, 0x2f, 0x7b, 0x7d, 0x1c, 0x40, 0x06, 0x2a, 0x72, 0x5a, 0x37, 0x0c, 0xd5, 0xa8, 0xa3, 0x81, 0xd4, 0x73, 0xef, 0x1e, 0x4e, 0x6c, 0xb9, 0x10, 0x3d, 0x04, 0x6e, 0xca, 0xe7, 0xdf, 0x62, 0x7b, 0x64, 0x00, 0x6a, 0xb6, 0xda, 0x02, 0x96, 0x74, 0xa7, 0xc2, 0xbb, 0x28, 0x69, 0xdf, 0xc8, 0x09, 0xff, 0x6c, 0x6f, 0x7a, 0xf8, 0x82, 0x69, 0xf1, 0x59, 0xf8, 0x3d, 0xe0, 0x6d, 0xa5, 0x71, 0xfb, 0x39, 0x2e, 0x17, 0x51, 0xcb, 0x94, 0x2a, 0xd0, 0x4e, 0x02, 0xaf, 0xa5, 0xd5, 0x39, 0x56, 0xda, 0x10, 0x2e, 0xa2, 0x91, 0x0b, 0xd2, 0xca, 0xb1, 0xac, 0x6d, 0xd2, 0xef, 0xad, 0x59, 0x54, 0xbc, 0xd3, 0x44, 0x4c, 0x6c, 0xe2, 0x5c, 0xed, 0xab, 0xc0, 0x04, 0x6d, 0x3e, 0x92, 0xf9, 0x4a, 0xce, 0x76, 0xed, 0x45, 0x50, 0x93, 0x29, 0x17, 0x93, 0x9c, 0xf0, 0xd8, 0x3c, 0xcd, 0xf7, 0x52, 0x9f, 0x27, 0x57, 0x2a, 0xff, 0xe0, 0x33, 0xb6, 0xa4, 0x41, 0xa3, 0x35, 0x0b, 0xab, 0x0c, 0x0b, 0xdd, 0x98, 0x10, 0x1d, 0x97, 0x24, 0x7a, 0x8e, 0xcb, 0xa3, 0x7a, 0xe9, 0xa8, 0x73, 0xf4, 0x4a, 0x4c, 0x6b, 0xb7, 0x31, 0x65, 0xca, 0x5a, 0xc4, 0xd8, 0x3c, 0xe0, 0xad, 0x30, 0x2a, 0x2e, 0x34, 0x2e, 0x40, 0x84, 0xdd, 0x5d, 0x08, 0xed, 0x10, 0x12, 0xca, 0x3f, 0x24, 0x2d, 0x08, 0x5b, 0x86, 0xb6, 0xf4, 0x70, 0x00, 0x5c, 0x9d, 0x30, 0x2a, 0x81, 0xd2, 0x5c, 0xa1, 0x70, 0xcf, 0x99, 0x0f, 0xf5, 0x94, 0xef, 0x54, 0x1d, 0xab, 0x91, 0x24, 0x59, 0x4f, 0xf6, 0xcb, 0xb8, 0x6d, 0x14, 0x21, 0xf1, 0xfb, 0x14, 0x5c, 0x29, 0x4e, 0x6e, 0xb0, 0x4d, 0x64, 0x0c, 0x38, 0xee, 0x19, 0x63, 0x14, 0x9b, 0x3d, 0xb4, 0x19, 0x25, 0x91, 0xe6, 0xde, 0xf4, 0x34, 0x2b, 0x87, 0x99, 0xbd, 0xec, 0x1c, 0xd3, 0x92, 0x34, 0xb7, 0xba, 0xef, 0x00, 0xae, 0xdc, 0xec, 0x9d, 0xd1, 0xfa, 0x83, 0x9f, 0x95, 0x8d, 0xb0, 0xed, 0xc0, 0x67, 0xae, 0xce, 0x15, 0xdb, 0x28, 0x8b, 0x8f, 0xcb, 0xc4, 0x9b, 0x0d, 0x46, 0x67, 0x96, 0xb0, 0x86, 0xb2, 0xdb, 0x3c, 0x89, 0x6e, 0x57, 0xac, 0xcb, 0x34, 0x57, 0x37, 0x80, 0x00, 0x34, 0x78, 0x71, 0xf0, 0x1a, 0x2c, 0x28, 0x87, 0x9f, 0x08, 0x21, 0x7c, 0x0e, 0x7e, 0x29, 0xfb, 0x9a, 0x2c, 0x77, 0x48, 0x2f, 0x88, 0xe2, 0xf0, 0x6a, 0x87, 0x15, 0x0c, 0x4c, 0xbf, 0xcb, 0xdd, 0xee, 0x75, 0xe1, 0xbc, 0x38, 0x31, 0xdc, 0xe9, 0x61, 0x53, 0x1e, 0xc8, 0x4b, 0x80, 0x94, 0x5c, 0x03, 0xdd, 0x4b, 0xae, 0xa8, 0x54, 0xe9, 0x8b, 0x23, 0x20, 0x21, 0xc8, 0x03, 0x83, 0x33, 0x5f, 0x11, 0x37, 0xfc, 0xd5, 0xb3, 0x11, 0x9a, 0x06, 0x0d, 0xbf, 0xcd, 0xc7, 0x22, 0x88, 0xb8, 0xc9, 0x3f, 0xec, 0x7c, 0x11, 0x96, 0x6a, 0xa0, 0x57, 0xdf, 0x5b, 0xde, 0xa2, 0x09, 0x11, 0xd3, 0xfd, 0xbf, 0x84, 0x7a, 0x9d, 0x3a, 0xba, 0x0f, 0x6d, 0x01, 0xad, 0xbc, 0xb9, 0xd8, 0x8a, 0xe4, 0xd6, 0xa2, 0x04, 0x93, 0xe0, 0x02, 0xd2, 0x45, 0x49, 0x14, 0x8e, 0x84, 0x9c, 0x7c, 0x57, 0x1b, 0x05, 0x27, 0xf6, 0x59, 0x83, 0xd1, 0xf4, 0xb6, 0x2f, 0xbe, 0x6e, 0x35, 0x7e, 0x97, 0x10, 0xf5, 0x42, 0x1a, 0xc9, 0x4d, 0xb9, 0x07, 0x71, 0x6d, 0xd1, 0x96, 0xc3, 0x88, 0xb6, 0xe6, 0x0e, 0x8a, 0x8a, 0xd7}; BOOST_CHECK_EQUAL(a.size(), MAX_SCRIPT_ELEMENT_SIZE); BOOST_CHECK_EQUAL(b.size(), MAX_SCRIPT_ELEMENT_SIZE); valtype aandb{ 0x10, 0x0e, 0x18, 0x01, 0x83, 0x00, 0x1a, 0x00, 0x41, 0x8c, 0x00, 0x00, 0x90, 0x1c, 0x54, 0xa0, 0x20, 0x14, 0x2c, 0x44, 0x71, 0x88, 0x0a, 0x48, 0x01, 0x80, 0xc8, 0x03, 0x8e, 0x42, 0x03, 0x06, 0x3e, 0x16, 0x30, 0x00, 0x81, 0x00, 0x00, 0x17, 0x70, 0x49, 0x24, 0x10, 0x43, 0x40, 0x62, 0x04, 0x65, 0x2a, 0x02, 0x00, 0x8b, 0x04, 0xc7, 0x2a, 0x18, 0x61, 0x08, 0x00, 0x04, 0x0a, 0x10, 0x48, 0x30, 0x00, 0x05, 0x20, 0xa2, 0x01, 0xd4, 0x22, 0x6c, 0x14, 0x0a, 0x20, 0xa0, 0x00, 0x1c, 0x04, 0x4c, 0xca, 0x45, 0xc3, 0x40, 0x5a, 0x44, 0x00, 0x22, 0x36, 0x92, 0x00, 0x90, 0x50, 0x23, 0x02, 0x09, 0x28, 0x60, 0xc3, 0x08, 0x08, 0xc4, 0x0c, 0x0b, 0x6a, 0xa0, 0x82, 0x49, 0x31, 0x19, 0x30, 0x15, 0x20, 0x24, 0x85, 0x61, 0x58, 0x08, 0x2e, 0x16, 0x40, 0xcb, 0x14, 0x22, 0xd0, 0x46, 0x00, 0x87, 0x21, 0x41, 0x38, 0x44, 0xd8, 0x10, 0x28, 0x22, 0x80, 0x03, 0xd0, 0xc8, 0x01, 0x0c, 0x24, 0x80, 0x08, 0x85, 0x01, 0x50, 0x84, 0x43, 0x04, 0x48, 0x40, 0xc0, 0x10, 0x29, 0x02, 0x40, 0x04, 0x44, 0x08, 0x10, 0x50, 0x00, 0x4a, 0x26, 0xe5, 0x44, 0x00, 0x90, 0x28, 0x15, 0x81, 0x9c, 0xf0, 0x98, 0x30, 0x48, 0x65, 0x42, 0x9b, 0x00, 0x16, 0x00, 0xfb, 0x00, 0x32, 0x26, 0xa0, 0x01, 0x80, 0x25, 0x09, 0x21, 0x04, 0x00, 0x4c, 0x80, 0x10, 0x08, 0x84, 0x24, 0x60, 0x04, 0x8a, 0xa2, 0x4a, 0xe1, 0x00, 0x23, 0xb0, 0x02, 0x00, 0x22, 0x93, 0x20, 0x21, 0xc8, 0x4a, 0x44, 0x58, 0x04, 0x60, 0x00, 0x30, 0x28, 0x22, 0x20, 0x2e, 0x40, 0x04, 0x04, 0x54, 0x00, 0x25, 0x10, 0x12, 0x40, 0x13, 0x20, 0x20, 0x00, 0x58, 0x06, 0x14, 0x80, 0x50, 0x00, 0x04, 0x80, 0x00, 0x0a, 0x00, 0xc0, 0x10, 0x21, 0x40, 0x48, 0x89, 0x05, 0xa1, 0x14, 0xe2, 0x04, 0x01, 0x2b, 0x80, 0x20, 0x19, 0x43, 0x30, 0x49, 0x30, 0x48, 0x00, 0x20, 0xa0, 0xe1, 0x00, 0x40, 0x28, 0x4a, 0x0a, 0xa0, 0x49, 0x44, 0x04, 0x30, 0xe0, 0x11, 0x00, 0x10, 0x90, 0x09, 0x24, 0x09, 0x01, 0x10, 0x64, 0x92, 0x84, 0x04, 0x2b, 0x02, 0x18, 0x0c, 0x48, 0x10, 0x02, 0x00, 0x10, 0xb1, 0x88, 0x0a, 0x00, 0xa6, 0x08, 0xa0, 0x88, 0x51, 0x88, 0x80, 0x95, 0x85, 0x84, 0x10, 0x6d, 0x00, 0x63, 0x24, 0xc4, 0x14, 0xc8, 0x28, 0x82, 0x0b, 0xc3, 0x80, 0x13, 0x00, 0x46, 0x43, 0x86, 0x00, 0x02, 0x10, 0x89, 0x28, 0x09, 0x42, 0x55, 0x80, 0x03, 0x24, 0x46, 0x06, 0x80, 0x00, 0x34, 0x28, 0x21, 0xa0, 0x02, 0x20, 0x28, 0x81, 0x09, 0x08, 0x01, 0x74, 0x04, 0x58, 0x28, 0xc8, 0x88, 0x2c, 0x34, 0x00, 0x0d, 0x08, 0x82, 0x30, 0x22, 0x87, 0x10, 0x00, 0x4c, 0x9a, 0x03, 0xd8, 0x86, 0x11, 0xe0, 0x88, 0x28, 0x11, 0x50, 0x01, 0x41, 0x50, 0x0c, 0x08, 0x43, 0x00, 0x84, 0x5c, 0x03, 0x1c, 0x0b, 0xa8, 0x00, 0x40, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x21, 0x1c, 0x10, 0x27, 0x58, 0x00, 0xa3, 0x00, 0x80, 0x02, 0x0d, 0x2d, 0xc4, 0x44, 0x02, 0x88, 0x00, 0x48, 0x15, 0x0c, 0x10, 0x00, 0x86, 0x2a, 0x80, 0x15, 0x5e, 0x02, 0x40, 0xa0, 0x08, 0x11, 0x52, 0xe8, 0xaf, 0x00, 0x78, 0x84, 0x22, 0xb2, 0x0b, 0x44, 0x01, 0xa9, 0xb4, 0x29, 0x08, 0x80, 0x24, 0xc6, 0x20, 0x04, 0x12, 0xc0, 0x00, 0xd0, 0x44, 0x48, 0x14, 0x0a, 0x00, 0x00, 0x40, 0x14, 0x11, 0x05, 0x01, 0x36, 0x09, 0x80, 0xc1, 0xf0, 0x96, 0x0b, 0x8c, 0x20, 0x11, 0x02, 0x95, 0x10, 0xf5, 0x42, 0x00, 0x48, 0x41, 0x18, 0x07, 0x00, 0x21, 0xd1, 0x80, 0x80, 0x08, 0x34, 0x22, 0x08, 0x08, 0x88, 0x95}; valtype aorb{ 0xf6, 0x9e, 0xff, 0xdf, 0xe7, 0x77, 0x7b, 0x8f, 0x4f, 0x8f, 0x37, 0x2e, 0xfd, 0xfd, 0xf6, 0xff, 0xfe, 0x1c, 0xff, 0xf7, 0x7d, 0xed, 0xbe, 0xfb, 0x8f, 0xfe, 0xdf, 0xf3, 0xbe, 0x6f, 0xef, 0xaf, 0xff, 0xfe, 0xfd, 0x96, 0x8f, 0x7d, 0x26, 0x3f, 0xff, 0xdb, 0x77, 0x5c, 0x7f, 0xf5, 0xfb, 0x7d, 0xf7, 0x7f, 0x27, 0xf7, 0xfb, 0x7f, 0xef, 0x6f, 0xfb, 0x7d, 0xfc, 0xf8, 0xc6, 0xbe, 0x73, 0xfa, 0x37, 0x7d, 0xd7, 0xfb, 0xa3, 0xcd, 0xdd, 0xf7, 0xef, 0xfe, 0x6e, 0x6e, 0xb9, 0xbc, 0xfd, 0xf4, 0xee, 0xfb, 0xef, 0xff, 0x77, 0xff, 0x64, 0x46, 0x7a, 0xb6, 0xdb, 0xb6, 0xdf, 0xf5, 0xbf, 0xc6, 0xbb, 0x6a, 0x6d, 0xdf, 0xd8, 0x59, 0xff, 0xff, 0x7f, 0x7a, 0xfb, 0xab, 0x79, 0xf7, 0xfd, 0xfe, 0xbf, 0xf5, 0x6f, 0xa7, 0x73, 0xfb, 0x39, 0x6e, 0x5f, 0x57, 0xcf, 0x95, 0x3b, 0xfc, 0x4e, 0x47, 0xbf, 0xe5, 0xdf, 0xb9, 0xfe, 0xdf, 0x1b, 0x6f, 0xee, 0x9b, 0x2f, 0xfb, 0xcf, 0xfb, 0xfc, 0xed, 0xd6, 0xff, 0xfd, 0x7b, 0x54, 0xfe, 0xf3, 0xfe, 0xce, 0xff, 0xf2, 0xdd, 0xfd, 0xbf, 0xea, 0x6e, 0xff, 0x3f, 0x9a, 0xfb, 0x4f, 0xee, 0xf7, 0xff, 0x65, 0xf3, 0xf3, 0xbb, 0x77, 0xd7, 0x9c, 0xf7, 0xfb, 0x7c, 0xed, 0xf7, 0x5f, 0xdf, 0xf7, 0x77, 0x3e, 0xff, 0xfa, 0xf7, 0xfe, 0xee, 0xcd, 0xeb, 0xb5, 0xaf, 0xeb, 0xce, 0x0f, 0xdd, 0xdb, 0xb9, 0x7d, 0x9f, 0xa4, 0xfe, 0x8e, 0xef, 0xef, 0xfa, 0xef, 0xfa, 0xf7, 0xfe, 0x5e, 0xdd, 0x6f, 0xbf, 0xbf, 0x75, 0xcf, 0x7f, 0xce, 0xfe, 0x7f, 0xe0, 0xed, 0xf0, 0xbe, 0xee, 0x34, 0xaf, 0xc1, 0xcf, 0xff, 0x5d, 0x9b, 0xff, 0xfc, 0xbf, 0xda, 0xff, 0xf6, 0x3d, 0x78, 0xff, 0xe7, 0xb7, 0xf6, 0xf3, 0x39, 0x7f, 0xbf, 0x7f, 0xee, 0x91, 0xff, 0x5d, 0xf3, 0xf4, 0xcf, 0xf9, 0x6f, 0xf5, 0xde, 0xff, 0x5c, 0x9d, 0xbb, 0x91, 0xe5, 0xdf, 0x5f, 0xf7, 0xcb, 0xfb, 0xed, 0x1e, 0x6f, 0xf5, 0xfb, 0xf5, 0xfe, 0xad, 0xef, 0x6f, 0xb5, 0x4f, 0xe5, 0x4f, 0xfe, 0xff, 0x1d, 0x73, 0x35, 0xfb, 0xff, 0xff, 0xfd, 0xf7, 0xd1, 0xff, 0xfe, 0xf6, 0xb7, 0xab, 0xe7, 0xdd, 0xfd, 0xef, 0xbc, 0xdb, 0xb2, 0x3d, 0xbf, 0xbe, 0xff, 0xf5, 0xee, 0xfd, 0xec, 0x9f, 0xd5, 0xfa, 0xa3, 0xff, 0xff, 0xef, 0xf0, 0xed, 0xf6, 0x7f, 0xff, 0xef, 0x15, 0xdb, 0xfb, 0xcf, 0xdf, 0xef, 0xdd, 0xbf, 0x6f, 0x56, 0xff, 0x9f, 0xf0, 0xc7, 0xf6, 0xdf, 0x7c, 0x99, 0xee, 0xf7, 0xec, 0xff, 0xfd, 0xff, 0x3f, 0xab, 0x0b, 0x77, 0x79, 0x77, 0xfc, 0x1f, 0x7c, 0xfa, 0xaf, 0xff, 0x28, 0x71, 0xfd, 0x6f, 0x7e, 0x7b, 0xff, 0xdb, 0xfe, 0x7f, 0xe8, 0x2f, 0xee, 0xe7, 0xf8, 0x7e, 0xb7, 0x1f, 0x4d, 0x6c, 0xbf, 0xdb, 0xff, 0xee, 0x77, 0xe7, 0xfd, 0xfc, 0xbf, 0xdc, 0xfd, 0xe3, 0x77, 0x5e, 0xe9, 0x6f, 0x8a, 0xb7, 0x7e, 0x7f, 0xfd, 0xeb, 0xef, 0xec, 0x75, 0xef, 0xef, 0x73, 0x67, 0x3f, 0xdf, 0x23, 0xe7, 0x3b, 0xdf, 0x9d, 0xff, 0xff, 0xfd, 0xf3, 0x1f, 0xbf, 0x2e, 0x2f, 0xbf, 0xcf, 0xef, 0xf3, 0xaa, 0xbb, 0xc9, 0x3f, 0xec, 0xfe, 0x91, 0x96, 0x6f, 0xe2, 0xff, 0xdf, 0xdb, 0xff, 0xa3, 0x89, 0xdd, 0xdf, 0xff, 0xff, 0x9d, 0xfb, 0x9d, 0x7e, 0xff, 0xbf, 0x6d, 0xad, 0xfd, 0xff, 0xbf, 0xfa, 0xdf, 0xf7, 0xff, 0xaa, 0x3d, 0x9b, 0xfc, 0x6e, 0xf3, 0xcd, 0xcb, 0xd5, 0xaf, 0xdc, 0xff, 0xfc, 0x77, 0x9b, 0x87, 0xff, 0xf6, 0x7f, 0x93, 0xfb, 0xf5, 0xfe, 0xaf, 0xbe, 0x6e, 0x75, 0xff, 0xff, 0xf4, 0xfd, 0xe7, 0x5a, 0xe9, 0xff, 0xfb, 0x17, 0xf1, 0x6d, 0xf3, 0xd7, 0xd3, 0xbc, 0xf7, 0xe7, 0x4e, 0x9e, 0x9a, 0xf7}; TestBitwiseOpcodes(a, b, aandb, aorb); // Check errors conditions. // 1. Less than 2 elements on stack. CheckAllBitwiseOpErrors({}, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckAllBitwiseOpErrors({{}}, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckAllBitwiseOpErrors({{0x00}}, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckAllBitwiseOpErrors({{0xab, 0xcd, 0xef}}, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckAllBitwiseOpErrors({a}, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckAllBitwiseOpErrors({b}, SCRIPT_ERR_INVALID_STACK_OPERATION); // 2. Operand of mismatching length CheckAllBitwiseOpErrors({{}, {0x00}}, SCRIPT_ERR_INVALID_OPERAND_SIZE); CheckAllBitwiseOpErrors({{0x00}, {}}, SCRIPT_ERR_INVALID_OPERAND_SIZE); CheckAllBitwiseOpErrors({{0x00}, {0xab, 0xcd, 0xef}}, SCRIPT_ERR_INVALID_OPERAND_SIZE); CheckAllBitwiseOpErrors({{0xab, 0xcd, 0xef}, {0x00}}, SCRIPT_ERR_INVALID_OPERAND_SIZE); CheckAllBitwiseOpErrors({{}, a}, SCRIPT_ERR_INVALID_OPERAND_SIZE); CheckAllBitwiseOpErrors({b, {}}, SCRIPT_ERR_INVALID_OPERAND_SIZE); } /** * String opcodes. */ static void CheckStringOp(const valtype &a, const valtype &b, const valtype &n) { CheckBinaryOp(a, b, OP_CAT, n); // Check concatenation with empty elements. CheckBinaryOp(a, {}, OP_CAT, a); CheckBinaryOp(b, {}, OP_CAT, b); CheckBinaryOp({}, a, OP_CAT, a); CheckBinaryOp({}, b, OP_CAT, b); // Split n into a and b. CheckTestResultForAllFlags({n}, CScript() << a.size() << OP_SPLIT, {a, b}); // Combine split and cat. CheckTestResultForAllFlags({n}, CScript() << a.size() << OP_SPLIT << OP_CAT, {n}); CheckTestResultForAllFlags( {a, b}, CScript() << OP_CAT << a.size() << OP_SPLIT, {a, b}); // Split away empty elements. CheckTestResultForAllFlags({a}, CScript() << 0 << OP_SPLIT, {{}, a}); CheckTestResultForAllFlags({b}, CScript() << 0 << OP_SPLIT, {{}, b}); CheckTestResultForAllFlags({a}, CScript() << a.size() << OP_SPLIT, {a, {}}); CheckTestResultForAllFlags({b}, CScript() << b.size() << OP_SPLIT, {b, {}}); // Out of bound split. CheckErrorForAllFlags({a}, CScript() << (a.size() + 1) << OP_SPLIT, SCRIPT_ERR_INVALID_SPLIT_RANGE); CheckErrorForAllFlags({b}, CScript() << (b.size() + 1) << OP_SPLIT, SCRIPT_ERR_INVALID_SPLIT_RANGE); CheckErrorForAllFlags({n}, CScript() << (n.size() + 1) << OP_SPLIT, SCRIPT_ERR_INVALID_SPLIT_RANGE); CheckErrorForAllFlags({a}, CScript() << (-1) << OP_SPLIT, SCRIPT_ERR_INVALID_SPLIT_RANGE); } BOOST_AUTO_TEST_CASE(string_opcodes_test) { // Check for empty string. CheckStringOp({}, {}, {}); // Check for simple concats. CheckStringOp({0x00}, {0x00}, {0x00, 0x00}); CheckStringOp({0xab}, {0xcd}, {0xab, 0xcd}); CheckStringOp({0xab, 0xcd, 0xef}, {0x12, 0x34, 0x56, 0x78}, {0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78}); const valtype n{ 0x7b, 0x59, 0xf8, 0x07, 0xc6, 0xc0, 0x70, 0xbc, 0x52, 0x7b, 0xf5, 0xaf, 0xf5, 0xdd, 0xeb, 0xdc, 0x41, 0xaa, 0x07, 0xf6, 0x80, 0x8d, 0x5d, 0x4d, 0xbc, 0x91, 0xcd, 0x0a, 0x14, 0x85, 0xd9, 0x98, 0xb6, 0xab, 0x2e, 0x37, 0x76, 0x78, 0x34, 0x8b, 0x2b, 0xfb, 0x59, 0x3b, 0xea, 0x45, 0x46, 0x72, 0x64, 0x64, 0x83, 0x73, 0xc3, 0x1d, 0xca, 0x86, 0x03, 0x91, 0xfc, 0xc0, 0xc4, 0xdf, 0x17, 0x83, 0x22, 0x5d, 0x50, 0xc5, 0x31, 0x45, 0xaf, 0xbc, 0xfd, 0xc8, 0xb9, 0x6a, 0x72, 0x8b, 0x3c, 0x9b, 0x77, 0x02, 0xd6, 0x18, 0x62, 0x02, 0xc9, 0x1c, 0x66, 0x29, 0x5c, 0x66, 0xf3, 0x9a, 0x00, 0xc1, 0x69, 0x47, 0x35, 0x2f, 0xe8, 0x32, 0x2a, 0xb5, 0xc4, 0x9f, 0x3c, 0xbf, 0xc7, 0x1a, 0x2b, 0xb3, 0xa6, 0x9b, 0xde, 0xcf, 0xc5, 0x15, 0x8c, 0xac, 0xd0, 0x7c, 0x38, 0xe4, 0x41, 0xe1, 0x81, 0x4e, 0x65, 0xa5, 0x24, 0x08, 0x5b, 0xa3, 0x19, 0xf3, 0xc2, 0x80, 0x21, 0x01, 0x33, 0xaf, 0x84, 0x53, 0x1a, 0x00, 0x79, 0x7e, 0x1f, 0xd1, 0x62, 0x53, 0x0d, 0x6a, 0x58, 0xde, 0x16, 0x23, 0x70, 0x32, 0x81, 0x25, 0xbd, 0xa3, 0x92, 0xae, 0xfd, 0x7f, 0x47, 0xa2, 0xf2, 0x34, 0x3d, 0xef, 0xc3, 0x71, 0xb1, 0x33, 0x9a, 0xfd, 0x80, 0x4b, 0x96, 0xcb, 0xaa, 0xda, 0x77, 0x50, 0x58, 0xf7, 0x0c, 0xf3, 0x75, 0xdf, 0x51, 0x96, 0x75, 0x9a, 0x78, 0xc3, 0xd3, 0xaf, 0xac, 0xee, 0xf3, 0xcc, 0x79, 0xfb, 0x3f, 0xda, 0x51, 0x94, 0x8f, 0x59, 0x3d, 0xbc, 0xef, 0x17, 0x47, 0xd4, 0x40, 0x80, 0x8a, 0x78, 0x86, 0x6c, 0x9e, 0x38, 0xd2, 0x11, 0xaa, 0x94, 0x79, 0x9b, 0x61, 0xf3, 0xaa, 0xcf, 0x66, 0x7e, 0xa7, 0x11, 0xe9, 0xad, 0x8a, 0xd4, 0x67, 0x23, 0xf9, 0x62, 0x9f, 0x55, 0xc0, 0x5a, 0x0f, 0x0a, 0xfe, 0x28, 0xd8, 0x80, 0xaf, 0x71, 0x97, 0x65, 0x49, 0xb1, 0xd3, 0x9c, 0xee, 0x7e, 0x4b, 0xeb, 0x06, 0x3b, 0xe1, 0x66, 0xf9, 0xa7, 0x77, 0x4f, 0x6a, 0xd1, 0xa0, 0x16, 0xe0, 0xcf, 0xe3, 0x25, 0x65, 0x08, 0x0f, 0x5e, 0x2c, 0x1e, 0x80, 0x35, 0x75, 0x40, 0x9a, 0xd1, 0x14, 0xba, 0xaa, 0xa7, 0xfc, 0x3c, 0xf1, 0xeb, 0x16, 0x8d, 0x59, 0xb4, 0xcf, 0x16, 0x9a, 0xe3, 0xf1, 0x9d, 0x31, 0x97, 0xe5, 0xa4, 0xcc, 0xae, 0x1c, 0xa2, 0xe7, 0x88, 0x44, 0x05, 0x67, 0x28, 0x21, 0x9f, 0x3e, 0xe2, 0xfc, 0x25, 0x8c, 0x63, 0x09, 0xde, 0x39, 0xfa, 0xae, 0x26, 0x9b, 0x43, 0xdf, 0x06, 0x2f, 0xb7, 0xaf, 0xa2, 0x74, 0x1c, 0x17, 0x96, 0x84, 0x26, 0x1a, 0xe2, 0xcd, 0x90, 0xa8, 0xc3, 0xb6, 0xeb, 0x53, 0xee, 0xdd, 0xf9, 0x88, 0xc6, 0x05, 0xb5, 0xd4, 0xa3, 0xf0, 0x36, 0xc7, 0xf1, 0xb3, 0x04, 0x0c, 0xa5, 0xea, 0x22, 0x5b, 0x56, 0x3d, 0x54, 0x0b, 0x69, 0xc2, 0xe1, 0x4f, 0xa8, 0x28, 0x4e, 0xe2, 0x3d, 0x99, 0x9c, 0x3b, 0xdb, 0xf4, 0x92, 0x5a, 0xb9, 0xce, 0xeb, 0x33, 0xb5, 0xae, 0x16, 0x58, 0x79, 0x31, 0x8f, 0x1e, 0x7a, 0x1a, 0xee, 0xbe, 0x9f, 0xea, 0x89, 0xd6, 0x6c, 0x43, 0x76, 0x94, 0x0d, 0x94, 0x50, 0x6d, 0xdd, 0xc2, 0x68, 0x80, 0x3e, 0x38, 0x51, 0x51, 0xd1, 0xd5, 0x4e, 0xf7, 0x65, 0xe5, 0x42, 0x3c, 0xa8, 0x28, 0x19, 0x02, 0xa7, 0xc9, 0x1c, 0x24, 0xa7, 0x91, 0xfe, 0xa1, 0xbc, 0xb9, 0x15, 0xba, 0x49, 0xac, 0xeb, 0x81, 0xf7, 0xc1, 0xfc, 0xf9, 0x51, 0x0d, 0xa1, 0xe8, 0x71, 0x2c, 0x4e, 0x59, 0xc1, 0x3a, 0x2a, 0xcc, 0x61, 0xee, 0xe5, 0x2a, 0x88, 0xf8, 0xec, 0xbd, 0x90, 0xc0, 0x96, 0xe0, 0x93, 0x1f, 0x78, 0xbe, 0x6b, 0xb1, 0x4c, 0x46, 0x2a, 0x86, 0xd9, 0x2d, 0x20, 0x29, 0xb4, 0x44, 0x15, 0xb2, 0x7e}; BOOST_CHECK_EQUAL(n.size(), MAX_SCRIPT_ELEMENT_SIZE); for (size_t i = 0; i <= MAX_SCRIPT_ELEMENT_SIZE; i++) { valtype a(n.begin(), n.begin() + i); valtype b(n.begin() + i, n.end()); CheckStringOp(a, b, n); // One more char and we are oversize. valtype extraA = a; extraA.push_back(0xaf); valtype extraB = b; extraB.push_back(0xad); CheckOpError({extraA, b}, OP_CAT, SCRIPT_ERR_PUSH_SIZE); CheckOpError({a, extraB}, OP_CAT, SCRIPT_ERR_PUSH_SIZE); CheckOpError({extraA, extraB}, OP_CAT, SCRIPT_ERR_PUSH_SIZE); } // Check error conditions. CheckOpError({}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({}, OP_SPLIT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{}}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{}}, OP_SPLIT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{0x00}}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{0x00}}, OP_SPLIT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{0xab, 0xcd, 0xef}}, OP_CAT, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckOpError({{0xab, 0xcd, 0xef}}, OP_SPLIT, SCRIPT_ERR_INVALID_STACK_OPERATION); } /** * Type conversion opcodes. */ static void CheckTypeConversionOp(const valtype &bin, const valtype &num) { // Check BIN2NUM. CheckTestResultForAllFlags({bin}, CScript() << OP_BIN2NUM, {num}); // Check NUM2BIN. Negative 0 is rebuilt as regular zero, so we need a tweak. valtype rebuilt_bin{bin}; if (num.size() == 0 && bin.size() > 0) { rebuilt_bin[rebuilt_bin.size() - 1] &= 0x7f; } CheckTestResultForAllFlags({num}, CScript() << bin.size() << OP_NUM2BIN, {rebuilt_bin}); // Check roundtrip with NUM2BIN. CheckTestResultForAllFlags( {bin}, CScript() << OP_BIN2NUM << bin.size() << OP_NUM2BIN, {rebuilt_bin}); // Grow and shrink back down using NUM2BIN. CheckTestResultForAllFlags({bin}, CScript() << MAX_SCRIPT_ELEMENT_SIZE << OP_NUM2BIN << bin.size() << OP_NUM2BIN, {rebuilt_bin}); CheckTestResultForAllFlags({num}, CScript() << MAX_SCRIPT_ELEMENT_SIZE << OP_NUM2BIN << bin.size() << OP_NUM2BIN, {rebuilt_bin}); // BIN2NUM is indempotent. CheckTestResultForAllFlags({bin}, CScript() << OP_BIN2NUM << OP_BIN2NUM, {num}); } static void CheckBin2NumError(const stacktype &original_stack, ScriptError expected_error) { CheckErrorForAllFlags(original_stack, CScript() << OP_BIN2NUM, expected_error); } static void CheckNum2BinError(const stacktype &original_stack, ScriptError expected_error) { CheckErrorForAllFlags(original_stack, CScript() << OP_NUM2BIN, expected_error); } BOOST_AUTO_TEST_CASE(type_conversion_test) { valtype empty; CheckTypeConversionOp(empty, empty); valtype paddedzero, paddednegzero; for (size_t i = 0; i < MAX_SCRIPT_ELEMENT_SIZE; i++) { CheckTypeConversionOp(paddedzero, empty); paddedzero.push_back(0x00); paddednegzero.push_back(0x80); CheckTypeConversionOp(paddednegzero, empty); paddednegzero[paddednegzero.size() - 1] = 0x00; } // Merge leading byte when sign bit isn't used. std::vector k{0x7f}, negk{0xff}; std::vector kpadded = k, negkpadded = negk; for (size_t i = 0; i < MAX_SCRIPT_ELEMENT_SIZE; i++) { CheckTypeConversionOp(kpadded, k); kpadded.push_back(0x00); CheckTypeConversionOp(negkpadded, negk); negkpadded[negkpadded.size() - 1] &= 0x7f; negkpadded.push_back(0x80); } // Some known values. CheckTypeConversionOp({0xab, 0xcd, 0xef, 0x00}, {0xab, 0xcd, 0xef, 0x00}); CheckTypeConversionOp({0xab, 0xcd, 0x7f, 0x00}, {0xab, 0xcd, 0x7f}); // Reductions CheckTypeConversionOp({0xab, 0xcd, 0xef, 0x42, 0x80}, {0xab, 0xcd, 0xef, 0xc2}); CheckTypeConversionOp({0xab, 0xcd, 0x7f, 0x42, 0x00}, {0xab, 0xcd, 0x7f, 0x42}); // Empty stack is an error. CheckBin2NumError({}, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckNum2BinError({}, SCRIPT_ERR_INVALID_STACK_OPERATION); // NUM2BIN require 2 elements on the stack. CheckNum2BinError({{0x00}}, SCRIPT_ERR_INVALID_STACK_OPERATION); // Values that do not fit in 4 bytes are considered out of range for // BIN2NUM. CheckBin2NumError({{0xab, 0xcd, 0xef, 0xc2, 0x80}}, SCRIPT_ERR_INVALID_NUMBER_RANGE); CheckBin2NumError({{0x00, 0x00, 0x00, 0x80, 0x80}}, SCRIPT_ERR_INVALID_NUMBER_RANGE); // NUM2BIN must not generate oversized push. valtype largezero(MAX_SCRIPT_ELEMENT_SIZE, 0); BOOST_CHECK_EQUAL(largezero.size(), MAX_SCRIPT_ELEMENT_SIZE); CheckTypeConversionOp(largezero, {}); CheckNum2BinError({{}, {0x09, 0x02}}, SCRIPT_ERR_PUSH_SIZE); // Check that the requested encoding is possible. CheckNum2BinError({{0xab, 0xcd, 0xef, 0x80}, {0x03}}, SCRIPT_ERR_IMPOSSIBLE_ENCODING); } /** * Arithmetic Opcodes */ static void CheckDivMod(const valtype &a, const valtype &b, const valtype &divExpected, const valtype &modExpected) { // Negative values for division CheckBinaryOp(a, b, OP_DIV, divExpected); CheckBinaryOp(a, NegativeValtype(b), OP_DIV, NegativeValtype(divExpected)); CheckBinaryOp(NegativeValtype(a), b, OP_DIV, NegativeValtype(divExpected)); CheckBinaryOp(NegativeValtype(a), NegativeValtype(b), OP_DIV, divExpected); // Negative values for modulo CheckBinaryOp(a, b, OP_MOD, modExpected); CheckBinaryOp(a, NegativeValtype(b), OP_MOD, modExpected); CheckBinaryOp(NegativeValtype(a), b, OP_MOD, NegativeValtype(modExpected)); CheckBinaryOp(NegativeValtype(a), NegativeValtype(b), OP_MOD, NegativeValtype(modExpected)); // Div/Mod by zero for (uint32_t flags : flagset) { CheckError(flags, {a, {}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); CheckError(flags, {b, {}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); if (flags & SCRIPT_VERIFY_MINIMALDATA) { CheckError(flags, {a, {0x00}}, CScript() << OP_DIV, SCRIPT_ERR_UNKNOWN_ERROR); CheckError(flags, {a, {0x80}}, CScript() << OP_DIV, SCRIPT_ERR_UNKNOWN_ERROR); CheckError(flags, {a, {0x00, 0x00}}, CScript() << OP_DIV, SCRIPT_ERR_UNKNOWN_ERROR); CheckError(flags, {a, {0x00, 0x80}}, CScript() << OP_DIV, SCRIPT_ERR_UNKNOWN_ERROR); CheckError(flags, {b, {0x00}}, CScript() << OP_DIV, SCRIPT_ERR_UNKNOWN_ERROR); CheckError(flags, {b, {0x80}}, CScript() << OP_DIV, SCRIPT_ERR_UNKNOWN_ERROR); CheckError(flags, {b, {0x00, 0x00}}, CScript() << OP_DIV, SCRIPT_ERR_UNKNOWN_ERROR); CheckError(flags, {b, {0x00, 0x80}}, CScript() << OP_DIV, SCRIPT_ERR_UNKNOWN_ERROR); } else { CheckError(flags, {a, {0x00}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); CheckError(flags, {a, {0x80}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); CheckError(flags, {a, {0x00, 0x00}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); CheckError(flags, {a, {0x00, 0x80}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); CheckError(flags, {b, {0x00}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); CheckError(flags, {b, {0x80}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); CheckError(flags, {b, {0x00, 0x00}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); CheckError(flags, {b, {0x00, 0x80}}, CScript() << OP_DIV, SCRIPT_ERR_DIV_BY_ZERO); } } // Division identities CheckBinaryOp(a, {0x01}, OP_DIV, a); CheckBinaryOp(a, {0x81}, OP_DIV, NegativeValtype(a)); CheckBinaryOp(a, a, OP_DIV, {0x01}); CheckBinaryOp(a, NegativeValtype(a), OP_DIV, {0x81}); CheckBinaryOp(NegativeValtype(a), a, OP_DIV, {0x81}); CheckBinaryOp(b, {0x01}, OP_DIV, b); CheckBinaryOp(b, {0x81}, OP_DIV, NegativeValtype(b)); CheckBinaryOp(b, b, OP_DIV, {0x01}); CheckBinaryOp(b, NegativeValtype(b), OP_DIV, {0x81}); CheckBinaryOp(NegativeValtype(b), b, OP_DIV, {0x81}); // Modulo identities // a % b % b = a % b CheckTestResultForAllFlags( {a, b}, CScript() << OP_MOD << CScriptNum(b, true).getint() << OP_MOD, {modExpected}); } static void CheckDivModError(const stacktype &original_stack, ScriptError expected_error) { CheckOpError(original_stack, OP_DIV, expected_error); CheckOpError(original_stack, OP_MOD, expected_error); } BOOST_AUTO_TEST_CASE(div_and_mod_opcode_tests) { CheckDivModError({}, SCRIPT_ERR_INVALID_STACK_OPERATION); CheckDivModError({{}}, SCRIPT_ERR_INVALID_STACK_OPERATION); // CheckOps not valid numbers CheckDivModError( {{0x01, 0x02, 0x03, 0x04, 0x05}, {0x01, 0x02, 0x03, 0x04, 0x05}}, SCRIPT_ERR_UNKNOWN_ERROR); CheckDivModError({{0x01, 0x02, 0x03, 0x04, 0x05}, {0x01}}, SCRIPT_ERR_UNKNOWN_ERROR); CheckDivModError({{0x01, 0x05}, {0x01, 0x02, 0x03, 0x04, 0x05}}, SCRIPT_ERR_UNKNOWN_ERROR); // 0x185377af / 0x85f41b01 = -4 // 0x185377af % 0x85f41b01 = 0x00830bab // 408123311 / -99883777 = -4 // 408123311 % -99883777 = 8588203 CheckDivMod({0xaf, 0x77, 0x53, 0x18}, {0x01, 0x1b, 0xf4, 0x85}, {0x84}, {0xab, 0x0b, 0x83, 0x00}); // 0x185377af / 0x00001b01 = 0xe69d // 0x185377af % 0x00001b01 = 0x0212 // 408123311 / 6913 = 59037 // 408123311 % 6913 = 530 CheckDivMod({0xaf, 0x77, 0x53, 0x18}, {0x01, 0x1b}, {0x9d, 0xe6, 0x00}, {0x12, 0x02}); // 15/4 = 3 (and negative operands) CheckDivMod({0x0f}, {0x04}, {0x03}, {0x03}); // 15000/4 = 3750 (and negative operands) CheckDivMod({0x98, 0x3a}, {0x04}, {0xa6, 0x0e}, {}); // 15000/4000 = 3 (and negative operands) CheckDivMod({0x98, 0x3a}, {0xa0, 0x0f}, {0x03}, {0xb8, 0x0b}); // 15000000/4000 = 3750 (and negative operands) CheckDivMod({0xc0, 0xe1, 0xe4, 0x00}, {0xa0, 0x0f}, {0xa6, 0x0e}, {}); // 15000000/4 = 3750000 (and negative operands) CheckDivMod({0xc0, 0xe1, 0xe4, 0x00}, {0x04}, {0x70, 0x38, 0x39}, {}); // 56488123 % 321 = 148 (and negative operands) CheckDivMod({0xbb, 0xf0, 0x5d, 0x03}, {0x41, 0x01}, {0x67, 0xaf, 0x02}, {0x94, 0x00}); // 56488123 % 3 = 1 (and negative operands) CheckDivMod({0xbb, 0xf0, 0x5d, 0x03}, {0x03}, {0x3e, 0x50, 0x1f, 0x01}, {0x01}); // 56488123 % 564881230 = 56488123 (and negative operands) CheckDivMod({0xbb, 0xf0, 0x5d, 0x03}, {0x4e, 0x67, 0xab, 0x21}, {}, {0xbb, 0xf0, 0x5d, 0x03}); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp index e868a262a..ae478203d 100644 --- a/src/test/test_bitcoin_fuzzy.cpp +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -1,259 +1,259 @@ // 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. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "addrman.h" #include "chain.h" #include "coins.h" #include "compressor.h" #include "consensus/merkle.h" #include "net.h" #include "primitives/block.h" #include "protocol.h" #include "pubkey.h" #include "script/script.h" #include "streams.h" #include "undo.h" #include "version.h" #include #include #include #include enum TEST_ID { CBLOCK_DESERIALIZE = 0, CTRANSACTION_DESERIALIZE, CBLOCKLOCATOR_DESERIALIZE, CBLOCKMERKLEROOT, CADDRMAN_DESERIALIZE, CBLOCKHEADER_DESERIALIZE, CBANENTRY_DESERIALIZE, CTXUNDO_DESERIALIZE, CBLOCKUNDO_DESERIALIZE, COIN_DESERIALIZE, CNETADDR_DESERIALIZE, CSERVICE_DESERIALIZE, CMESSAGEHEADER_DESERIALIZE, CADDRESS_DESERIALIZE, CINV_DESERIALIZE, CBLOOMFILTER_DESERIALIZE, CDISKBLOCKINDEX_DESERIALIZE, CTXOUTCOMPRESSOR_DESERIALIZE, TEST_ID_END }; bool read_stdin(std::vector &data) { char buffer[1024]; ssize_t length = 0; while ((length = read(STDIN_FILENO, buffer, 1024)) > 0) { data.insert(data.end(), buffer, buffer + length); if (data.size() > (1 << 20)) return false; } return length == 0; } int main(int argc, char **argv) { ECCVerifyHandle globalVerifyHandle; std::vector buffer; if (!read_stdin(buffer)) return 0; if (buffer.size() < sizeof(uint32_t)) return 0; uint32_t test_id = 0xffffffff; memcpy(&test_id, &buffer[0], sizeof(uint32_t)); buffer.erase(buffer.begin(), buffer.begin() + sizeof(uint32_t)); if (test_id >= TEST_ID_END) return 0; CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); try { int nVersion; ds >> nVersion; ds.SetVersion(nVersion); } catch (const std::ios_base::failure &e) { return 0; } switch (test_id) { case CBLOCK_DESERIALIZE: { try { CBlock block; ds >> block; } catch (const std::ios_base::failure &e) { return 0; } break; } case CTRANSACTION_DESERIALIZE: { try { CTransaction tx(deserialize, ds); } catch (const std::ios_base::failure &e) { return 0; } break; } case CBLOCKLOCATOR_DESERIALIZE: { try { CBlockLocator bl; ds >> bl; } catch (const std::ios_base::failure &e) { return 0; } break; } case CBLOCKMERKLEROOT: { try { CBlock block; ds >> block; bool mutated; BlockMerkleRoot(block, &mutated); } catch (const std::ios_base::failure &e) { return 0; } break; } case CADDRMAN_DESERIALIZE: { try { CAddrMan am; ds >> am; } catch (const std::ios_base::failure &e) { return 0; } break; } case CBLOCKHEADER_DESERIALIZE: { try { CBlockHeader bh; ds >> bh; } catch (const std::ios_base::failure &e) { return 0; } break; } case CBANENTRY_DESERIALIZE: { try { CBanEntry be; ds >> be; } catch (const std::ios_base::failure &e) { return 0; } break; } case CTXUNDO_DESERIALIZE: { try { CTxUndo tu; ds >> tu; } catch (const std::ios_base::failure &e) { return 0; } break; } case CBLOCKUNDO_DESERIALIZE: { try { CBlockUndo bu; ds >> bu; } catch (const std::ios_base::failure &e) { return 0; } break; } case COIN_DESERIALIZE: { try { Coin coin; ds >> coin; } catch (const std::ios_base::failure &e) { return 0; } break; } case CNETADDR_DESERIALIZE: { try { CNetAddr na; ds >> na; } catch (const std::ios_base::failure &e) { return 0; } break; } case CSERVICE_DESERIALIZE: { try { CService s; ds >> s; } catch (const std::ios_base::failure &e) { return 0; } break; } case CMESSAGEHEADER_DESERIALIZE: { - CMessageHeader::MessageMagic pchMessageStart = {0x00, 0x00, 0x00, - 0x00}; + CMessageHeader::MessageMagic pchMessageStart = { + {0x00, 0x00, 0x00, 0x00}}; try { CMessageHeader mh(pchMessageStart); ds >> mh; if (!mh.IsValidWithoutConfig(pchMessageStart)) { return 0; } } catch (const std::ios_base::failure &e) { return 0; } break; } case CADDRESS_DESERIALIZE: { try { CAddress a; ds >> a; } catch (const std::ios_base::failure &e) { return 0; } break; } case CINV_DESERIALIZE: { try { CInv i; ds >> i; } catch (const std::ios_base::failure &e) { return 0; } break; } case CBLOOMFILTER_DESERIALIZE: { try { CBloomFilter bf; ds >> bf; } catch (const std::ios_base::failure &e) { return 0; } break; } case CDISKBLOCKINDEX_DESERIALIZE: { try { CDiskBlockIndex dbi; ds >> dbi; } catch (const std::ios_base::failure &e) { return 0; } break; } case CTXOUTCOMPRESSOR_DESERIALIZE: { CTxOut to; CTxOutCompressor toc(to); try { ds >> toc; } catch (const std::ios_base::failure &e) { return 0; } break; } default: return 0; } return 0; } diff --git a/src/txdb.cpp b/src/txdb.cpp index 12e712afe..cfc7b1a29 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -1,458 +1,458 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "txdb.h" #include "chainparams.h" #include "config.h" #include "hash.h" #include "init.h" #include "pow.h" #include "random.h" #include "ui_interface.h" #include "uint256.h" #include "util.h" #include #include static const char DB_COIN = 'C'; static const char DB_COINS = 'c'; static const char DB_BLOCK_FILES = 'f'; static const char DB_TXINDEX = 't'; static const char DB_BLOCK_INDEX = 'b'; static const char DB_BEST_BLOCK = 'B'; static const char DB_HEAD_BLOCKS = 'H'; static const char DB_FLAG = 'F'; static const char DB_REINDEX_FLAG = 'R'; static const char DB_LAST_BLOCK = 'l'; namespace { struct CoinEntry { COutPoint *outpoint; char key; CoinEntry(const COutPoint *ptr) : outpoint(const_cast(ptr)), key(DB_COIN) {} template void Serialize(Stream &s) const { s << key; s << outpoint->GetTxId(); s << VARINT(outpoint->GetN()); } template void Unserialize(Stream &s) { s >> key; uint256 id; s >> id; - uint32_t n; + uint32_t n = 0; s >> VARINT(n); *outpoint = COutPoint(id, n); } }; } // namespace CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true) {} bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const { return db.Read(CoinEntry(&outpoint), coin); } bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const { return db.Exists(CoinEntry(&outpoint)); } uint256 CCoinsViewDB::GetBestBlock() const { uint256 hashBestChain; if (!db.Read(DB_BEST_BLOCK, hashBestChain)) return uint256(); return hashBestChain; } std::vector CCoinsViewDB::GetHeadBlocks() const { std::vector vhashHeadBlocks; if (!db.Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) { return std::vector(); } return vhashHeadBlocks; } bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { CDBBatch batch(db); size_t count = 0; size_t changed = 0; size_t batch_size = (size_t)gArgs.GetArg("-dbbatchsize", nDefaultDbBatchSize); int crash_simulate = gArgs.GetArg("-dbcrashratio", 0); assert(!hashBlock.IsNull()); uint256 old_tip = GetBestBlock(); if (old_tip.IsNull()) { // We may be in the middle of replaying. std::vector old_heads = GetHeadBlocks(); if (old_heads.size() == 2) { assert(old_heads[0] == hashBlock); old_tip = old_heads[1]; } } // In the first batch, mark the database as being in the middle of a // transition from old_tip to hashBlock. // A vector is used for future extensibility, as we may want to support // interrupting after partial writes from multiple independent reorgs. batch.Erase(DB_BEST_BLOCK); batch.Write(DB_HEAD_BLOCKS, std::vector{hashBlock, old_tip}); for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { if (it->second.flags & CCoinsCacheEntry::DIRTY) { CoinEntry entry(&it->first); if (it->second.coin.IsSpent()) { batch.Erase(entry); } else { batch.Write(entry, it->second.coin); } changed++; } count++; CCoinsMap::iterator itOld = it++; mapCoins.erase(itOld); if (batch.SizeEstimate() > batch_size) { LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0)); db.WriteBatch(batch); batch.Clear(); if (crash_simulate) { static FastRandomContext rng; if (rng.randrange(crash_simulate) == 0) { LogPrintf("Simulating a crash. Goodbye.\n"); _Exit(0); } } } } // In the last batch, mark the database as consistent with hashBlock again. batch.Erase(DB_HEAD_BLOCKS); batch.Write(DB_BEST_BLOCK, hashBlock); LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0)); bool ret = db.WriteBatch(batch); LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of " "%u) to coin database...\n", (unsigned int)changed, (unsigned int)count); return ret; } size_t CCoinsViewDB::EstimateSize() const { return db.EstimateSize(DB_COIN, char(DB_COIN + 1)); } CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {} bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { return Read(std::make_pair(DB_BLOCK_FILES, nFile), info); } bool CBlockTreeDB::WriteReindexing(bool fReindexing) { if (fReindexing) return Write(DB_REINDEX_FLAG, '1'); else return Erase(DB_REINDEX_FLAG); } bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { fReindexing = Exists(DB_REINDEX_FLAG); return true; } bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { return Read(DB_LAST_BLOCK, nFile); } CCoinsViewCursor *CCoinsViewDB::Cursor() const { CCoinsViewDBCursor *i = new CCoinsViewDBCursor( const_cast(db).NewIterator(), GetBestBlock()); /** * It seems that there are no "const iterators" for LevelDB. Since we only * need read operations on it, use a const-cast to get around that * restriction. */ i->pcursor->Seek(DB_COIN); // Cache key of first record if (i->pcursor->Valid()) { CoinEntry entry(&i->keyTmp.second); i->pcursor->GetKey(entry); i->keyTmp.first = entry.key; } else { // Make sure Valid() and GetKey() return false i->keyTmp.first = 0; } return i; } bool CCoinsViewDBCursor::GetKey(COutPoint &key) const { // Return cached key if (keyTmp.first == DB_COIN) { key = keyTmp.second; return true; } return false; } bool CCoinsViewDBCursor::GetValue(Coin &coin) const { return pcursor->GetValue(coin); } unsigned int CCoinsViewDBCursor::GetValueSize() const { return pcursor->GetValueSize(); } bool CCoinsViewDBCursor::Valid() const { return keyTmp.first == DB_COIN; } void CCoinsViewDBCursor::Next() { pcursor->Next(); CoinEntry entry(&keyTmp.second); if (!pcursor->Valid() || !pcursor->GetKey(entry)) { // Invalidate cached key after last record so that Valid() and GetKey() // return false keyTmp.first = 0; } else { keyTmp.first = entry.key; } } bool CBlockTreeDB::WriteBatchSync( const std::vector> &fileInfo, int nLastFile, const std::vector &blockinfo) { CDBBatch batch(*this); for (std::vector>::const_iterator it = fileInfo.begin(); it != fileInfo.end(); it++) { batch.Write(std::make_pair(DB_BLOCK_FILES, it->first), *it->second); } batch.Write(DB_LAST_BLOCK, nLastFile); for (std::vector::const_iterator it = blockinfo.begin(); it != blockinfo.end(); it++) { batch.Write(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it)); } return WriteBatch(batch, true); } bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { return Read(std::make_pair(DB_TXINDEX, txid), pos); } bool CBlockTreeDB::WriteTxIndex( const std::vector> &vect) { CDBBatch batch(*this); for (std::vector>::const_iterator it = vect.begin(); it != vect.end(); it++) batch.Write(std::make_pair(DB_TXINDEX, it->first), it->second); return WriteBatch(batch); } bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); } bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { char ch; if (!Read(std::make_pair(DB_FLAG, name), ch)) return false; fValue = ch == '1'; return true; } bool CBlockTreeDB::LoadBlockIndexGuts( std::function insertBlockIndex) { const Config &config = GetConfig(); std::unique_ptr pcursor(NewIterator()); pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256())); // Load mapBlockIndex while (pcursor->Valid()) { boost::this_thread::interruption_point(); std::pair key; if (!pcursor->GetKey(key) || key.first != DB_BLOCK_INDEX) { break; } CDiskBlockIndex diskindex; if (!pcursor->GetValue(diskindex)) { return error("LoadBlockIndex() : failed to read value"); } // Construct block index object CBlockIndex *pindexNew = insertBlockIndex(diskindex.GetBlockHash()); pindexNew->pprev = insertBlockIndex(diskindex.hashPrev); pindexNew->nHeight = diskindex.nHeight; pindexNew->nFile = diskindex.nFile; pindexNew->nDataPos = diskindex.nDataPos; pindexNew->nUndoPos = diskindex.nUndoPos; pindexNew->nVersion = diskindex.nVersion; pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; pindexNew->nTime = diskindex.nTime; pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, config)) { return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); } pcursor->Next(); } return true; } namespace { //! Legacy class to deserialize pre-pertxout database entries without reindex. class CCoins { public: //! whether transaction is a coinbase bool fCoinBase; //! unspent transaction outputs; spent outputs are .IsNull(); spent outputs //! at the end of the array are dropped std::vector vout; //! at which height this transaction was included in the active block chain int nHeight; //! empty constructor CCoins() : fCoinBase(false), vout(0), nHeight(0) {} template void Unserialize(Stream &s) { uint32_t nCode = 0; // version int nVersionDummy; ::Unserialize(s, VARINT(nVersionDummy)); // header code ::Unserialize(s, VARINT(nCode)); fCoinBase = nCode & 1; std::vector vAvail(2, false); vAvail[0] = (nCode & 2) != 0; vAvail[1] = (nCode & 4) != 0; uint32_t nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); // spentness bitmask while (nMaskCode > 0) { uint8_t chAvail = 0; ::Unserialize(s, chAvail); for (unsigned int p = 0; p < 8; p++) { bool f = (chAvail & (1 << p)) != 0; vAvail.push_back(f); } if (chAvail != 0) { nMaskCode--; } } // txouts themself vout.assign(vAvail.size(), CTxOut()); for (size_t i = 0; i < vAvail.size(); i++) { if (vAvail[i]) { ::Unserialize(s, REF(CTxOutCompressor(vout[i]))); } } // coinbase height ::Unserialize(s, VARINT(nHeight)); } }; } // namespace /** * Upgrade the database from older formats. * * Currently implemented: from the per-tx utxo model (0.8..0.14.x) to per-txout. */ bool CCoinsViewDB::Upgrade() { std::unique_ptr pcursor(db.NewIterator()); pcursor->Seek(std::make_pair(DB_COINS, uint256())); if (!pcursor->Valid()) { return true; } int64_t count = 0; LogPrintf("Upgrading utxo-set database...\n"); LogPrintf("[0%%]..."); size_t batch_size = 1 << 24; CDBBatch batch(db); uiInterface.SetProgressBreakAction(StartShutdown); int reportDone = 0; std::pair key; std::pair prev_key = {DB_COINS, uint256()}; while (pcursor->Valid()) { boost::this_thread::interruption_point(); if (ShutdownRequested()) { break; } if (!pcursor->GetKey(key) || key.first != DB_COINS) { break; } if (count++ % 256 == 0) { uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1); int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5); uiInterface.ShowProgress( _("Upgrading UTXO database") + "\n" + _("(press q to shutdown and continue later)") + "\n", percentageDone); if (reportDone < percentageDone / 10) { // report max. every 10% step LogPrintf("[%d%%]...", percentageDone); reportDone = percentageDone / 10; } } CCoins old_coins; if (!pcursor->GetValue(old_coins)) { return error("%s: cannot parse CCoins record", __func__); } TxId id(key.second); for (size_t i = 0; i < old_coins.vout.size(); ++i) { if (!old_coins.vout[i].IsNull() && !old_coins.vout[i].scriptPubKey.IsUnspendable()) { Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase); COutPoint outpoint(id, i); CoinEntry entry(&outpoint); batch.Write(entry, newcoin); } } batch.Erase(key); if (batch.SizeEstimate() > batch_size) { db.WriteBatch(batch); batch.Clear(); db.CompactRange(prev_key, key); prev_key = key; } pcursor->Next(); } db.WriteBatch(batch); db.CompactRange({DB_COINS, uint256()}, key); uiInterface.SetProgressBreakAction(std::function()); LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE"); return !ShutdownRequested(); }