diff --git a/doc/release-notes.md b/doc/release-notes.md index ff2f24c09..978958699 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -28,3 +28,12 @@ Bitcoin-ABC 19.x backports: - Added parameter `include_removed` to `listsinceblock` for better tracking of transactions during a reorg. See `bitcoin-cli help listsinceblock` for more details. - `listsinceblock` will now throw an error if an unknown `blockhash` argument value is passed, instead of returning a list of all wallet transactions since the genesis block. + - Various minor fixes to RPC parameter validation + - Minor wallet performance improvements + - `errors` in getmininginfo rpc commmand has been deprecated. Use `warnings` now instead. + - Added optional `blockhash` parameter to `getrawtransaction` to narrowly + search for a transaction within a given block. New returned field + `in_active_chain` will indicate if that block is part of the active chain. + - `signrawtransaction` RPC is now deprecated. The new RPCs + `signrawtransactionwithkey` and `signrawtransactionwithwallet` should + be used instead. diff --git a/src/addrman.h b/src/addrman.h index 0e767fc2b..007a15b96 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -55,7 +55,7 @@ class CAddrInfo : public CAddress { template inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(*(CAddress *)this); + READWRITE(*static_cast(this)); READWRITE(source); READWRITE(nLastSuccess); READWRITE(nAttempts); diff --git a/src/core_read.cpp b/src/core_read.cpp index 63cbc8cad..e14a5ea7f 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -28,16 +28,16 @@ CScript ParseScript(const std::string &s) { continue; } - const char *name = GetOpName((opcodetype)op); + const char *name = GetOpName(static_cast(op)); if (strcmp(name, "OP_UNKNOWN") == 0) { continue; } std::string strName(name); - mapOpNames[strName] = (opcodetype)op; + mapOpNames[strName] = static_cast(op); // Convenience: OP_ADD and just ADD are both recognized: strName.replace(strName.find("OP_"),3,""); - mapOpNames[strName] = (opcodetype)op; + mapOpNames[strName] = static_cast(op); } } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index baac2e277..71d20ef99 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -513,7 +513,7 @@ struct event_base *EventBase() { static void httpevent_callback_fn(evutil_socket_t, short, void *data) { // Static handler: simply call inner handler - HTTPEvent *self = ((HTTPEvent *)data); + HTTPEvent *self = static_cast(data); self->handler(); if (self->deleteWhenTriggered) delete self; } diff --git a/src/net.cpp b/src/net.cpp index a5a53533d..dbba1baf4 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2109,7 +2109,8 @@ void CConnman::OpenNetworkConnection(const CAddress &addrConnect, return; } if (!pszDest) { - if (IsLocal(addrConnect) || FindNode((CNetAddr)addrConnect) || + if (IsLocal(addrConnect) || + FindNode(static_cast(addrConnect)) || IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort())) { return; } diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 2e126d82a..82058ab1d 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -513,7 +513,8 @@ unsigned short CService::GetPort() const { } bool operator==(const CService &a, const CService &b) { - return (CNetAddr)a == (CNetAddr)b && a.port == b.port; + return static_cast(a) == static_cast(b) && + a.port == b.port; } bool operator<(const CService &a, const CService &b) { diff --git a/src/primitives/block.h b/src/primitives/block.h index 64e4d73fb..41ac6ff1a 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -70,14 +70,14 @@ class CBlock : public CBlockHeader { CBlock(const CBlockHeader &header) { SetNull(); - *((CBlockHeader *)this) = header; + *(static_cast(this)) = header; } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(*(CBlockHeader *)this); + READWRITE(*static_cast(this)); READWRITE(vtx); } diff --git a/src/protocol.h b/src/protocol.h index 3e5c6f40a..3986b57c5 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -395,8 +395,8 @@ class CAddress : public CService { READWRITE(nTime); uint64_t nServicesInt = nServices; READWRITE(nServicesInt); - nServices = (ServiceFlags)nServicesInt; - READWRITE(*(CService *)this); + nServices = static_cast(nServicesInt); + READWRITE(*static_cast(this)); } // TODO: make private (improves encapsulation) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index a41aaf87a..a4bbb36f2 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -660,7 +660,7 @@ void BitcoinGUI::createTrayIconMenu() { #else // Note: On Mac, the dock icon is used to provide the tray's functionality. MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); - dockIconHandler->setMainWindow((QMainWindow *)this); + dockIconHandler->setMainWindow(static_cast(this)); trayIconMenu = dockIconHandler->dockMenu(); #endif @@ -996,14 +996,15 @@ void BitcoinGUI::message(const QString &title, const QString &message, buttons = QMessageBox::Ok; showNormalIfMinimized(); - QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, - buttons, this); + QMessageBox mBox(static_cast(nMBoxIcon), strTitle, + message, buttons, this); int r = mBox.exec(); if (ret != nullptr) { *ret = r == QMessageBox::Ok; } } else - notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message); + notificator->notify(static_cast(nNotifyIcon), + strTitle, message); } void BitcoinGUI::changeEvent(QEvent *e) { diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 4c9956e0d..e4517b7b3 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -169,9 +169,9 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, ui->radioTreeMode->click(); if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder")) - sortView( - settings.value("nCoinControlSortColumn").toInt(), - ((Qt::SortOrder)settings.value("nCoinControlSortOrder").toInt())); + sortView(settings.value("nCoinControlSortColumn").toInt(), + (static_cast( + settings.value("nCoinControlSortOrder").toInt()))); } CoinControlDialog::~CoinControlDialog() { @@ -473,7 +473,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog *dialog) { nPayAmount += amount; if (amount > Amount::zero()) { - CTxOut txout(Amount(amount), (CScript)std::vector(24, 0)); + CTxOut txout(amount, + static_cast(std::vector(24, 0))); txDummy.vout.push_back(txout); if (txout.IsDust(dustRelayFee)) { fDust = true; @@ -566,7 +567,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog *dialog) { // Never create dust outputs; if we would, just add the dust to the // fee. if (nChange > Amount::zero() && nChange < MIN_CHANGE) { - CTxOut txout(nChange, (CScript)std::vector(24, 0)); + CTxOut txout(nChange, + static_cast(std::vector(24, 0))); if (txout.IsDust(dustRelayFee)) { // dust-change will be raised until no dust if (CoinControlDialog::fSubtractFeeFromAmount) { diff --git a/src/qt/coincontroltreewidget.cpp b/src/qt/coincontroltreewidget.cpp index 03638bf60..e479b2286 100644 --- a/src/qt/coincontroltreewidget.cpp +++ b/src/qt/coincontroltreewidget.cpp @@ -23,7 +23,7 @@ void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event) { { event->ignore(); CoinControlDialog *coinControlDialog = - (CoinControlDialog *)this->parentWidget(); + static_cast(this->parentWidget()); coinControlDialog->done(QDialog::Accepted); } else { this->QTreeWidget::keyPressEvent(event); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 424890bd4..0bfbf3ec7 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -376,7 +376,7 @@ void SendCoinsDialog::on_sendButton_clicked() { SEND_CONFIRM_DELAY, this); confirmationDialog.exec(); QMessageBox::StandardButton retval = - (QMessageBox::StandardButton)confirmationDialog.result(); + static_cast(confirmationDialog.result()); if (retval != QMessageBox::Yes) { fNewRecipientAllowed = true; diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 2d18fe891..3a49d1e72 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -32,7 +32,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) float fontFactor = 1.0; float devicePixelRatio = 1.0; #if QT_VERSION > 0x050100 - devicePixelRatio = ((QGuiApplication*)QCoreApplication::instance())->devicePixelRatio(); + devicePixelRatio = static_cast(QCoreApplication::instance())->devicePixelRatio(); #endif // define text to place diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 8c311b6c5..05d7c9481 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -338,8 +338,8 @@ void TransactionView::chooseWatchonly(int idx) { } transactionProxyModel->setWatchOnlyFilter( - (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx) - .toInt()); + static_cast( + watchOnlyWidget->itemData(idx).toInt())); } void TransactionView::changedPrefix(const QString &prefix) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a2356be6e..20a1cf32c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -24,6 +24,7 @@ #include "util.h" #include "utilstrencodings.h" #include "validation.h" +#include "warnings.h" #include // boost::thread::interrupt @@ -1280,6 +1281,7 @@ UniValue getblockchaininfo(const Config &config, obj.pushKV("pruneheight", block->nHeight); } + obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } @@ -1705,20 +1707,22 @@ UniValue getchaintxstats(const Config &config, const JSONRPCRequest &request) { "ends the window.\n" "\nResult:\n" "{\n" - " \"time\": xxxxx, (numeric) The timestamp for the " - "final block in the window in UNIX format.\n" - " \"txcount\": xxxxx, (numeric) The total number of " - "transactions in the chain up to that point.\n" - " \"window_block_count\": xxxxx, (numeric) Size of the window in " - "number of blocks.\n" - " \"window_tx_count\": xxxxx, (numeric) The number of " - "transactions in the window. Only returned if " + " \"time\": xxxxx, (numeric) The " + "timestamp for the final block in the window in UNIX format.\n" + " \"txcount\": xxxxx, (numeric) The total " + "number of transactions in the chain up to that point.\n" + " \"window_final_block_hash\": \"...\", (string) The hash of " + "the final block in the window.\n" + " \"window_block_count\": xxxxx, (numeric) Size of " + "the window in number of blocks.\n" + " \"window_tx_count\": xxxxx, (numeric) The number " + "of transactions in the window. Only returned if " "\"window_block_count\" is > 0.\n" - " \"window_interval\": xxxxx, (numeric) The elapsed time in " - "the window in seconds. Only returned if \"window_block_count\" is " - "> 0.\n" - " \"txrate\": x.xx, (numeric) The average rate of " - "transactions per second in the window. Only returned if " + " \"window_interval\": xxxxx, (numeric) The elapsed " + "time in the window in seconds. Only returned if " + "\"window_block_count\" is > 0.\n" + " \"txrate\": x.xx, (numeric) The average " + "rate of transactions per second in the window. Only returned if " "\"window_interval\" is > 0.\n" "}\n" "\nExamples:\n" + @@ -1732,27 +1736,20 @@ UniValue getchaintxstats(const Config &config, const JSONRPCRequest &request) { int blockcount = 30 * 24 * 60 * 60 / config.GetChainParams().GetConsensus().nPowTargetSpacing; - bool havehash = !request.params[1].isNull(); - uint256 hash; - if (havehash) { - hash = uint256S(request.params[1].get_str()); - } - - { + if (request.params[1].isNull()) { LOCK(cs_main); - if (havehash) { - auto it = mapBlockIndex.find(hash); - if (it == mapBlockIndex.end()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, - "Block not found"); - } - pindex = it->second; - if (!chainActive.Contains(pindex)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "Block is not in main chain"); - } - } else { - pindex = chainActive.Tip(); + pindex = chainActive.Tip(); + } else { + uint256 hash = uint256S(request.params[1].get_str()); + LOCK(cs_main); + auto it = mapBlockIndex.find(hash); + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + pindex = it->second; + if (!chainActive.Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Block is not in main chain"); } } @@ -1780,6 +1777,7 @@ UniValue getchaintxstats(const Config &config, const JSONRPCRequest &request) { UniValue ret(UniValue::VOBJ); ret.pushKV("time", int64_t(pindex->nTime)); ret.pushKV("txcount", int64_t(pindex->nChainTx)); + ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex()); ret.pushKV("window_block_count", blockcount); if (blockcount > 0) { ret.pushKV("window_tx_count", nTxDiff); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 56153322a..85788bddf 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -234,12 +234,16 @@ static UniValue getmininginfo(const Config &config, " \"currentblocktx\": nnn, (numeric) The last block " "transaction\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"errors\": \"...\" (string) Current errors\n" " \"networkhashps\": nnn, (numeric) The network hashes per " "second\n" " \"pooledtx\": n (numeric) The size of the mempool\n" " \"chain\": \"xxxx\", (string) current network name as " "defined in BIP70 (main, test, regtest)\n" + " \"warnings\": \"...\" (string) any network and " + "blockchain warnings\n" + " \"errors\": \"...\" (string) DEPRECATED. Same as " + "warnings. Only shown when bitcoind is started with " + "-deprecatedrpc=getmininginfo\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") + @@ -256,10 +260,14 @@ static UniValue getmininginfo(const Config &config, obj.pushKV("blockprioritypercentage", uint8_t(gArgs.GetArg("-blockprioritypercentage", DEFAULT_BLOCK_PRIORITY_PERCENTAGE))); - obj.pushKV("errors", GetWarnings("statusbar")); obj.pushKV("networkhashps", getnetworkhashps(config, request)); obj.pushKV("pooledtx", uint64_t(g_mempool.size())); obj.pushKV("chain", config.GetChainParams().NetworkIDString()); + if (IsDeprecatedRPCEnabled(gArgs, "getmininginfo")) { + obj.pushKV("errors", GetWarnings("statusbar")); + } else { + obj.pushKV("warnings", GetWarnings("statusbar")); + } return obj; } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index d8b09ed37..f4d6cb81b 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -563,8 +563,8 @@ static UniValue getnetworkinfo(const Config &config, " }\n" " ,...\n" " ]\n" - " \"warnings\": \"...\" " - "(string) any network warnings\n" + " \"warnings\": \"...\" (string) any network " + "and blockchain warnings\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnetworkinfo", "") + diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 43f6075ac..643a11fb5 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -65,13 +65,20 @@ void TxToJSON(const CTransaction &tx, const uint256 hashBlock, static UniValue getrawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || - request.params.size() > 2) { + request.params.size() > 3) { throw std::runtime_error( - "getrawtransaction \"txid\" ( verbose )\n" + "getrawtransaction \"txid\" ( verbose \"blockhash\" )\n" "\nNOTE: By default this function only works for mempool " "transactions. If the -txindex option is\n" - "enabled, it also works for blockchain transactions.\n" + "enabled, it also works for blockchain transactions. If the block " + "which contains the transaction\n" + "is known, its hash can be provided even for nodes without " + "-txindex. Note that if a blockhash is\n" + "provided, only that block will be searched and if the transaction " + "is in the mempool or other\n" + "blocks, or if this node does not have the given block available, " + "the transaction will not be found.\n" "DEPRECATED: for now, it also works for transactions with unspent " "outputs.\n" @@ -83,8 +90,10 @@ static UniValue getrawtransaction(const Config &config, "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" - "2. verbose (bool, optional, default=false) If false, return " - "a string, otherwise return a json object\n" + "2. verbose (bool, optional, default=false) If false, return a " + "string, otherwise return a json object\n" + "3. \"blockhash\" (string, optional) The block in which to look " + "for the transaction\n" "\nResult (if verbose is not set or set to false):\n" "\"data\" (string) The serialized, hex-encoded data for " @@ -92,6 +101,9 @@ static UniValue getrawtransaction(const Config &config, "\nResult (if verbose is set to true):\n" "{\n" + " \"in_active_chain\": b, (bool) Whether specified block is in " + "the active chain or not (only present with explicit \"blockhash\" " + "argument)\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded " "data for 'txid'\n" " \"txid\" : \"id\", (string) The transaction id (same as " @@ -145,40 +157,57 @@ static UniValue getrawtransaction(const Config &config, "\nExamples:\n" + HelpExampleCli("getrawtransaction", "\"mytxid\"") + HelpExampleCli("getrawtransaction", "\"mytxid\" true") + - HelpExampleRpc("getrawtransaction", "\"mytxid\", true")); + HelpExampleRpc("getrawtransaction", "\"mytxid\", true") + + HelpExampleCli("getrawtransaction", + "\"mytxid\" false \"myblockhash\"") + + HelpExampleCli("getrawtransaction", + "\"mytxid\" true \"myblockhash\"")); } LOCK(cs_main); + bool in_active_chain = true; TxId txid = TxId(ParseHashV(request.params[0], "parameter 1")); + CBlockIndex *blockindex = nullptr; // Accept either a bool (true) or a num (>=1) to indicate verbose output. bool fVerbose = false; if (!request.params[1].isNull()) { - if (request.params[1].isNum()) { - if (request.params[1].get_int() != 0) { - fVerbose = true; - } - } else if (request.params[1].isBool()) { - if (request.params[1].isTrue()) { - fVerbose = true; + fVerbose = request.params[1].isNum() + ? (request.params[1].get_int() != 0) + : request.params[1].get_bool(); + } + + if (!request.params[2].isNull()) { + uint256 blockhash = ParseHashV(request.params[2], "parameter 3"); + if (!blockhash.IsNull()) { + BlockMap::iterator it = mapBlockIndex.find(blockhash); + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + "Block hash not found"); } - } else { - throw JSONRPCError( - RPC_TYPE_ERROR, - "Invalid type provided. Verbose parameter must be a boolean."); + blockindex = it->second; + in_active_chain = chainActive.Contains(blockindex); } } CTransactionRef tx; - uint256 hashBlock; - if (!GetTransaction(config, txid, tx, hashBlock, true)) { - throw JSONRPCError( - RPC_INVALID_ADDRESS_OR_KEY, - std::string(fTxIndex ? "No such mempool or blockchain transaction" - : "No such mempool transaction. Use -txindex " - "to enable blockchain transaction queries") + - ". Use gettransaction for wallet transactions."); + uint256 hash_block; + if (!GetTransaction(config, txid, tx, hash_block, true, blockindex)) { + std::string errmsg; + if (blockindex) { + if (!blockindex->nStatus.hasData()) { + throw JSONRPCError(RPC_MISC_ERROR, "Block not available"); + } + errmsg = "No such transaction found in the provided block"; + } else { + errmsg = fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to " + "enable blockchain transaction queries"; + } + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + errmsg + + ". Use gettransaction for wallet transactions."); } if (!fVerbose) { @@ -186,7 +215,10 @@ static UniValue getrawtransaction(const Config &config, } UniValue result(UniValue::VOBJ); - TxToJSON(*tx, hashBlock, result); + if (blockindex) { + result.pushKV("in_active_chain", in_active_chain); + } + TxToJSON(*tx, hash_block, result); return result; } @@ -1079,7 +1111,8 @@ static UniValue signrawtransaction(const Config &config, "[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\"," "\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype " ")\n" - "\nSign inputs for raw transaction (serialized, hex-encoded).\n" + "\nDEPRECATED.Sign inputs for raw transaction (serialized, " + "hex-encoded).\n" "The second optional argument (may be null) is an array of " "previous transaction outputs that\n" "this transaction depends on but may not yet be in the block " @@ -1158,6 +1191,17 @@ static UniValue signrawtransaction(const Config &config, HelpExampleRpc("signrawtransaction", "\"myhex\"")); } + if (!IsDeprecatedRPCEnabled(gArgs, "signrawtransaction")) { + throw JSONRPCError( + RPC_METHOD_DEPRECATED, + "signrawtransaction is deprecated and will be fully removed in " + "v0.20. " + "To use signrawtransaction in v0.19, restart bitcoind with " + "-deprecatedrpc=signrawtransaction.\n" + "Projects should transition to using signrawtransactionwithkey and " + "signrawtransactionwithwallet before upgrading to v0.20"); + } + RPCTypeCheck( request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); @@ -1288,7 +1332,7 @@ static UniValue sendrawtransaction(const Config &config, static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- - { "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose"} }, + { "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose","blockhash"} }, { "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} }, { "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} }, { "rawtransactions", "decodescript", decodescript, {"hexstring"} }, diff --git a/src/script/script.h b/src/script/script.h index 6c50251d6..7bfe1024e 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -558,7 +558,7 @@ class CScript : public CScriptBase { pc += nSize; } - opcodeRet = (opcodetype)opcode; + opcodeRet = static_cast(opcode); return true; } diff --git a/src/secp256k1/include/secp256k1_schnorr.h b/src/secp256k1/include/secp256k1_schnorr.h index afbae8604..60137fc32 100644 --- a/src/secp256k1/include/secp256k1_schnorr.h +++ b/src/secp256k1/include/secp256k1_schnorr.h @@ -48,7 +48,7 @@ SECP256K1_API int secp256k1_schnorr_sign( const unsigned char *seckey, secp256k1_nonce_function noncefp, const void *ndata -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); # ifdef __cplusplus } diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c index 544b43963..e69ec1955 100644 --- a/src/secp256k1/src/bench_sign.c +++ b/src/secp256k1/src/bench_sign.c @@ -8,6 +8,11 @@ #include "util.h" #include "bench.h" +#ifdef ENABLE_MODULE_SCHNORR +#include "include/secp256k1_schnorr.h" +#endif + + typedef struct { secp256k1_context* ctx; unsigned char msg[32]; @@ -44,12 +49,31 @@ static void bench_sign_run(void* arg) { } } +#ifdef ENABLE_MODULE_SCHNORR +static void bench_schnorr_sign_run(void* arg) { + int i,j; + bench_sign *data = (bench_sign*)arg; + + unsigned char sig[64]; + for (i = 0; i < 20000; i++) { + CHECK(secp256k1_schnorr_sign(data->ctx, sig, data->msg, data->key, NULL, NULL)); + for (j = 0; j < 32; j++) { + data->msg[j] = sig[j]; + data->key[j] = sig[j + 32]; + } + } +} +#endif + int main(void) { bench_sign data; data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, 20000); +#ifdef ENABLE_MODULE_SCHNORR + run_benchmark("schnorr_sign", bench_schnorr_sign_run, bench_sign_setup, NULL, &data, 10, 20000); +#endif secp256k1_context_destroy(data.ctx); return 0; diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c index 418defa0a..667614bb7 100644 --- a/src/secp256k1/src/bench_verify.c +++ b/src/secp256k1/src/bench_verify.c @@ -11,6 +11,10 @@ #include "util.h" #include "bench.h" +#ifdef ENABLE_MODULE_SCHNORR +#include "include/secp256k1_schnorr.h" +#endif + #ifdef ENABLE_OPENSSL_TESTS #include #include @@ -79,6 +83,25 @@ static void benchmark_verify_openssl(void* arg) { } #endif +#ifdef ENABLE_MODULE_SCHNORR +static void benchmark_schnorr_verify(void* arg) { + int i; + benchmark_verify_t* data = (benchmark_verify_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_pubkey pubkey; + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->pubkey, data->pubkeylen) == 1); + CHECK(secp256k1_schnorr_verify(data->ctx, data->sig, data->msg, &pubkey) == (i == 0)); + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} +#endif + int main(void) { int i; secp256k1_pubkey pubkey; @@ -106,6 +129,11 @@ int main(void) { run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000); EC_GROUP_free(data.ec_group); #endif +#ifdef ENABLE_MODULE_SCHNORR + CHECK(secp256k1_schnorr_sign(data.ctx, data.sig, data.msg, data.key, NULL, NULL)); + data.siglen = 64; + run_benchmark("schnorr_verify", benchmark_schnorr_verify, NULL, NULL, &data, 10, 20000); +#endif secp256k1_context_destroy(data.ctx); return 0; diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java index c678480e8..c159da36e 100644 --- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java +++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java @@ -414,6 +414,36 @@ public static synchronized boolean randomize(byte[] seed) throws AssertFailExcep } } + /** + * Verifies the given Schnorr signature in native code. + * Calling when enabled == false is undefined (probably library not loaded) + * + * @param data The data which was signed, must be exactly 32 bytes + * @param signature The signature is exactly 64 bytes + * @param pub The public key which did the signing which is the same ECDSA + */ + public static boolean schnorrVerify(byte[] data, byte[] signature, byte[] pub) { + checkArgument(data.length == 32 && signature.length == 64 && (pub.length == 33 || pub.length == 65)); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + 64 + pub.length) { + byteBuff = ByteBuffer.allocateDirect(32 + 64 + pub.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(signature); + byteBuff.put(pub); + + r.lock(); + try { + return secp256k1_schnorr_verify(byteBuff, Secp256k1Context.getContext(), pub.length) == 1; + } finally { + r.unlock(); + } + } + private static native long secp256k1_ctx_clone(long context); private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context); @@ -438,6 +468,8 @@ public static synchronized boolean randomize(byte[] seed) throws AssertFailExcep private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen); + private static native int secp256k1_schnorr_verify(ByteBuffer byteBuff, long context, int pubLen); + private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen); } diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java index 9fc0360dd..e928afa68 100644 --- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java +++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -164,6 +164,156 @@ public static void testRandomize() throws AssertFailException { assertEquals( result, true, "testRandomize"); } + private static class SchnorrTestVector { + String data; + String sig; + String pubKey; + boolean expected; + String comment; + + SchnorrTestVector(String d, String s, String p, boolean e, String c) { + data = d; + sig = s; + pubKey = p; + expected = e; + comment = c; + } + } + + /** + * This tests schnorrVerify() for a valid signature + * It tests the following test vectors + * https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr/test-vectors.csv + */ + public static void testSchnorrVerify() throws AssertFailException{ + SchnorrTestVector[] tests = new SchnorrTestVector[]{ + new SchnorrTestVector( + "0000000000000000000000000000000000000000000000000000000000000000", + "787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF67031A98831859DC34DFFEEDDA86831842CCD0079E1F92AF177F7F22CC1DCED05", + "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + true, + "success" + ), + new SchnorrTestVector( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + "2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + true, + "success" + ), + new SchnorrTestVector( + "5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", + "00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BE00880371D01766935B92D2AB4CD5C8A2A5837EC57FED7660773A05F0DE142380", + "03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B", + true, + "success" + ), + new SchnorrTestVector( + "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703", + "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6302A8DC32E64E86A333F20EF56EAC9BA30B7246D6D25E22ADB8C6BE1AEB08D49D", + "03DEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", + true, + "success" + ), + new SchnorrTestVector( + "0000000000000000000000000000000000000000000000000000000000000000", + "52818579ACA59767E3291D91B76B637BEF062083284992F2D95F564CA6CB4E3530B1DA849C8E8304ADC0CFE870660334B3CFC18E825EF1DB34CFAE3DFC5D8187", + "031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F", + true, + "success" + ), + new SchnorrTestVector( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "570DD4CA83D4E6317B8EE6BAE83467A1BF419D0767122DE409394414B05080DCE9EE5F237CBD108EABAE1E37759AE47F8E4203DA3532EB28DB860F33D62D49BD", + "03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B", + true, + "success" + ), + new SchnorrTestVector( + "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703", + "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6302A8DC32E64E86A333F20EF56EAC9BA30B7246D6D25E22ADB8C6BE1AEB08D49D", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + false, + "public key not on the curve" + ), + new SchnorrTestVector( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + "2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1DFA16AEE06609280A19B67A24E1977E4697712B5FD2943914ECD5F730901B4AB7", + "03EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", + false, + "incorrect R residuosity" + ), + new SchnorrTestVector( + "5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", + "00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BED092F9D860F1776A1F7412AD8A1EB50DACCC222BC8C0E26B2056DF2F273EFDEC", + "03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B", + false, + "negated message hash" + ), + new SchnorrTestVector( + "0000000000000000000000000000000000000000000000000000000000000000", + "787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF68FCE5677CE7A623CB20011225797CE7A8DE1DC6CCD4F754A47DA6C600E59543C", + "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + false, + "negated s value" + ), + new SchnorrTestVector( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + "2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD", + "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + false, + "negated public key" + ), + new SchnorrTestVector( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + "00000000000000000000000000000000000000000000000000000000000000009E9D01AF988B5CEDCE47221BFA9B222721F3FA408915444A4B489021DB55775F", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + false, + "sG - eP is infinite. Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 0" + ), + new SchnorrTestVector( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + "0000000000000000000000000000000000000000000000000000000000000001D37DDF0254351836D84B1BD6A795FD5D523048F298C4214D187FE4892947F728", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + false, + "sG - eP is infinite. Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 1" + ), + new SchnorrTestVector( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + false, + "sig[0:32] is not an X coordinate on the curve" + ), + new SchnorrTestVector( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2F1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + false, + "sig[0:32] is equal to field size" + ), + new SchnorrTestVector( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + "2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + false, + "sig[32:64] is equal to curve order" + ) + }; + int i = 0; + for(SchnorrTestVector test : tests) { + boolean expected = test.expected; + byte[] data = DatatypeConverter.parseHexBinary(test.data); + byte[] sig = DatatypeConverter.parseHexBinary(test.sig); + byte[] pub = DatatypeConverter.parseHexBinary(test.pubKey); + boolean result = NativeSecp256k1.schnorrVerify(data, sig, pub); + + String testMsg = String.join(" ", "testSchnorrVerify", String.valueOf(i++), String.valueOf(expected), test.comment); + + assertEquals(result, expected, testMsg); + } + } + public static void testCreateECDHSecret() throws AssertFailException{ byte[] sec = DatatypeConverter.parseHexBinary("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530"); @@ -212,6 +362,9 @@ public static void main(String[] args) throws AssertFailException{ //Test randomize() testRandomize(); + //Test verifySchnorr() success/fail + testSchnorrVerify(); + //Test ECDH testCreateECDHSecret(); diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c index bcef7b32c..a87a6ee73 100644 --- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c +++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c @@ -5,7 +5,7 @@ #include "include/secp256k1.h" #include "include/secp256k1_ecdh.h" #include "include/secp256k1_recovery.h" - +#include "include/secp256k1_schnorr.h" SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone (JNIEnv* env, jclass classObject, jlong ctx_l) @@ -332,6 +332,27 @@ SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1p return 0; } +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* sigdata = { (unsigned char*) (data + 32) }; + const unsigned char* pubdata = { (unsigned char*) (data + 32 + 64) }; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if( ret ) { + ret = secp256k1_schnorr_verify(ctx, sigdata, data, &pubkey); + } + + (void)classObject; + + return ret; +} + SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) { diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h index fe613c9e9..7ca6e0288 100644 --- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h +++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h @@ -104,6 +104,14 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse (JNIEnv *, jclass, jobject, jlong, jint); +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_schnorr_verify + * Signature: (Ljava/nio/ByteBuffer;JI)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1verify + (JNIEnv *, jclass, jobject, jlong, jint); + /* * Class: org_bitcoin_NativeSecp256k1 * Method: secp256k1_ecdh diff --git a/src/secp256k1/src/modules/schnorr/schnorr_impl.h b/src/secp256k1/src/modules/schnorr/schnorr_impl.h index 5577f9fdd..5e609e517 100644 --- a/src/secp256k1/src/modules/schnorr/schnorr_impl.h +++ b/src/secp256k1/src/modules/schnorr/schnorr_impl.h @@ -37,7 +37,7 @@ * public key point P, * signature: (32-byte r, scalar s) * - * Signature is invalid if s >= order or r >= p. + * Signature is invalid if s >= n or r >= p. * Compute scalar e = Hash(r || compressed(P) || m) mod n. * Option 1 (faster for single verification): * Compute point R = s * G - e * P. diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 310171d93..edbc8df81 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -258,7 +258,7 @@ ["where the pubkey is obtained through key recovery with sig and the wrong sighash."], ["This is to show that FindAndDelete is applied only to non-segwit scripts"], ["To show that the tests are 'correctly wrong', they should pass by modifying OP_CHECKSIG under interpreter.cpp"], -["by replacing (sigversion == SIGVERSION_BASE) with (sigversion != SIGVERSION_BASE)"], +["by replacing (sigversion == SigVersion::BASE) with (sigversion != SigVersion::BASE)"], ["Non-segwit: wrong sighash (without FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"], [[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]], "010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012103b12a1ec8428fc74166926318c15e17408fea82dbb157575e16a8c365f546248f4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH"], @@ -269,7 +269,7 @@ ["Script is 2 CHECKMULTISIGVERIFY DROP"], ["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"], ["Signature is 0 2 "], -["Should pass by replacing (sigversion == SIGVERSION_BASE) with (sigversion != SIGVERSION_BASE) under OP_CHECKMULTISIG"], +["Should pass by replacing (sigversion == SigVersion::BASE) with (sigversion != SigVersion::BASE) under OP_CHECKMULTISIG"], ["Non-segwit: wrong sighash (without FindAndDelete) = 4bc6a53e8e16ef508c19e38bba08831daba85228b0211f323d4cb0999cf2a5e8"], [[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]], "01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596015221023fd5dd42b44769c5653cbc5947ff30ab8871f240ad0c0e7432aefe84b5b4ff3421039d52178dbde360b83f19cf348deb04fa8360e1bf5634577be8e50fafc2b0e4ef4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH"], diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 7b317b671..2ce81b23f 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -136,7 +136,7 @@ TorControlConnection::~TorControlConnection() { } void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) { - TorControlConnection *self = (TorControlConnection *)ctx; + TorControlConnection *self = static_cast(ctx); struct evbuffer *input = bufferevent_get_input(bev); size_t n_read_out = 0; char *line; @@ -187,7 +187,7 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) { void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ctx) { - TorControlConnection *self = (TorControlConnection *)ctx; + TorControlConnection *self = static_cast(ctx); if (what & BEV_EVENT_CONNECTED) { LogPrint(BCLog::TOR, "tor: Successfully connected!\n"); self->connected(*self); @@ -763,7 +763,7 @@ fs::path TorController::GetPrivateKeyFile() { } void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) { - TorController *self = (TorController *)arg; + TorController *self = static_cast(arg); self->Reconnect(); } diff --git a/src/txdb.h b/src/txdb.h index 6022a5143..94a12b368 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -49,7 +49,7 @@ struct CDiskTxPos : public CDiskBlockPos { template inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(*(CDiskBlockPos *)this); + READWRITE(*static_cast(this)); READWRITE(VARINT(nTxOffset)); } diff --git a/src/validation.cpp b/src/validation.cpp index 78b07b9a6..51b043568 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -782,57 +782,58 @@ bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, /** * Return transaction in txOut, and if it was found inside a block, its hash is - * placed in hashBlock. + * placed in hashBlock. If blockIndex is provided, the transaction is fetched + * from the corresponding block. */ bool GetTransaction(const Config &config, const TxId &txid, - CTransactionRef &txOut, uint256 &hashBlock, - bool fAllowSlow) { - CBlockIndex *pindexSlow = nullptr; + CTransactionRef &txOut, uint256 &hashBlock, bool fAllowSlow, + CBlockIndex *blockIndex) { + CBlockIndex *pindexSlow = blockIndex; LOCK(cs_main); - CTransactionRef ptx = g_mempool.get(txid); - if (ptx) { - txOut = ptx; - return true; - } - - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(txid, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, - CLIENT_VERSION); - if (file.IsNull()) { - return error("%s: OpenBlockFile failed", __func__); - } - - CBlockHeader header; - try { - file >> header; - fseek(file.Get(), postx.nTxOffset, SEEK_CUR); - file >> txOut; - } catch (const std::exception &e) { - return error("%s: Deserialize or I/O error - %s", __func__, - e.what()); - } + if (!blockIndex) { + CTransactionRef ptx = g_mempool.get(txid); + if (ptx) { + txOut = ptx; + return true; + } - hashBlock = header.GetHash(); - if (txOut->GetId() != txid) { - return error("%s: txid mismatch", __func__); + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(txid, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, + CLIENT_VERSION); + if (file.IsNull()) { + return error("%s: OpenBlockFile failed", __func__); + } + CBlockHeader header; + try { + file >> header; + fseek(file.Get(), postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (const std::exception &e) { + return error("%s: Deserialize or I/O error - %s", __func__, + e.what()); + } + hashBlock = header.GetHash(); + if (txOut->GetId() != txid) { + return error("%s: txid mismatch", __func__); + } + return true; } - return true; + // transaction not found in index, nothing more can be done + return false; } - // transaction not found in index, nothing more can be done - return false; - } - - // use coin database to locate block that contains transaction, and scan it - if (fAllowSlow) { - const Coin &coin = AccessByTxid(*pcoinsTip, txid); - if (!coin.IsSpent()) { - pindexSlow = chainActive[coin.GetHeight()]; + // use coin database to locate block that contains transaction, and scan + // it + if (fAllowSlow) { + const Coin &coin = AccessByTxid(*pcoinsTip, txid); + if (!coin.IsSpent()) { + pindexSlow = chainActive[coin.GetHeight()]; + } } } @@ -1301,8 +1302,12 @@ bool UndoWriteToDisk(const CBlockUndo &blockundo, CDiskBlockPos &pos, return true; } -bool UndoReadFromDisk(CBlockUndo &blockundo, const CDiskBlockPos &pos, - const uint256 &hashBlock) { +static bool UndoReadFromDisk(CBlockUndo &blockundo, const CBlockIndex *pindex) { + CDiskBlockPos pos = pindex->GetUndoPos(); + if (pos.IsNull()) { + return error("%s: no undo data available", __func__); + } + // Open history file to read CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { @@ -1314,7 +1319,7 @@ bool UndoReadFromDisk(CBlockUndo &blockundo, const CDiskBlockPos &pos, // We need a CHashVerifier as reserializing may lose data CHashVerifier verifier(&filein); try { - verifier << hashBlock; + verifier << pindex->pprev->GetBlockHash(); verifier >> blockundo; filein >> hashChecksum; } catch (const std::exception &e) { @@ -1396,13 +1401,7 @@ static DisconnectResult DisconnectBlock(const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &view) { CBlockUndo blockUndo; - CDiskBlockPos pos = pindex->GetUndoPos(); - if (pos.IsNull()) { - error("DisconnectBlock(): no undo data available"); - return DISCONNECT_FAILED; - } - - if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) { + if (!UndoReadFromDisk(blockUndo, pindex)) { error("DisconnectBlock(): failure reading undo data"); return DISCONNECT_FAILED; } @@ -3278,9 +3277,9 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState &state, return true; } -static bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, - unsigned int nAddSize, unsigned int nHeight, - uint64_t nTime, bool fKnown = false) { +static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, + unsigned int nHeight, uint64_t nTime, + bool fKnown = false) { LOCK(cs_LastBlockFile); unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; @@ -3339,7 +3338,7 @@ static bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, fclose(file); } } else { - return state.Error("out of disk space"); + return error("out of disk space"); } } } @@ -3789,6 +3788,33 @@ bool ProcessNewBlockHeaders(const Config &config, return true; } +/** + * Store block on disk. If dbp is non-nullptr, the file is known to already + * reside on disk. + */ +static CDiskBlockPos SaveBlockToDisk(const CBlock &block, int nHeight, + const CChainParams &chainparams, + const CDiskBlockPos *dbp) { + unsigned int nBlockSize = + ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != nullptr) { + blockPos = *dbp; + } + if (!FindBlockPos(blockPos, nBlockSize + 8, nHeight, block.GetBlockTime(), + dbp != nullptr)) { + error("%s: FindBlockPos failed", __func__); + return CDiskBlockPos(); + } + if (dbp == nullptr) { + if (!WriteBlockToDisk(block, blockPos, chainparams.DiskMagic())) { + AbortNode("Failed to write block"); + return CDiskBlockPos(); + } + } + return blockPos; +} + /** * Store a block on disk. * @@ -3927,29 +3953,18 @@ static bool AcceptBlock(const Config &config, GetMainSignals().NewPoWValidBlock(pindex, pblock); } - int nHeight = pindex->nHeight; const CChainParams &chainparams = config.GetChainParams(); // Write block to history file try { - unsigned int nBlockSize = - ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (dbp != nullptr) { - blockPos = *dbp; - } - - if (!FindBlockPos(state, blockPos, nBlockSize + 8, nHeight, - block.GetBlockTime(), dbp != nullptr)) { - return error("AcceptBlock(): FindBlockPos failed"); - } - - if (dbp == nullptr) { - if (!WriteBlockToDisk(block, blockPos, chainparams.DiskMagic())) { - AbortNode(state, "Failed to write block"); - } + CDiskBlockPos blockPos = + SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp); + if (blockPos.IsNull()) { + state.Error(strprintf( + "%s: Failed to find position to write new block to disk", + __func__)); + return false; } - if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) { return error("AcceptBlock(): ReceivedBlockTransactions failed"); } @@ -4535,10 +4550,8 @@ bool CVerifyDB::VerifyDB(const Config &config, CCoinsView *coinsview, // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { CBlockUndo undo; - CDiskBlockPos pos = pindex->GetUndoPos(); - if (!pos.IsNull()) { - if (!UndoReadFromDisk(undo, pos, - pindex->pprev->GetBlockHash())) { + if (!pindex->GetUndoPos().IsNull()) { + if (!UndoReadFromDisk(undo, pindex)) { return error( "VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); @@ -4870,19 +4883,13 @@ bool LoadGenesisBlock(const CChainParams &chainparams) { // one already on disk) try { CBlock &block = const_cast(chainparams.GenesisBlock()); - // Start new block file - unsigned int nBlockSize = - ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - CValidationState state; - if (!FindBlockPos(state, blockPos, nBlockSize + 8, 0, - block.GetBlockTime())) { - return error("%s: FindBlockPos failed", __func__); - } - if (!WriteBlockToDisk(block, blockPos, chainparams.DiskMagic())) { + CDiskBlockPos blockPos = + SaveBlockToDisk(block, 0, chainparams, nullptr); + if (blockPos.IsNull()) { return error("%s: writing genesis block to disk failed", __func__); } CBlockIndex *pindex = AddToBlockIndex(block); + CValidationState state; if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) { return error("%s: genesis block not accepted", __func__); } diff --git a/src/validation.h b/src/validation.h index 01afec5c0..6f184a74f 100644 --- a/src/validation.h +++ b/src/validation.h @@ -405,7 +405,8 @@ bool IsInitialBlockDownload(); * Retrieve a transaction (from memory pool, or from disk, if possible). */ bool GetTransaction(const Config &config, const TxId &txid, CTransactionRef &tx, - uint256 &hashBlock, bool fAllowSlow = false); + uint256 &hashBlock, bool fAllowSlow = false, + CBlockIndex *blockIndex = nullptr); /** * Find the best known block, and make it the active tip of the block chain. diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d3d9c4186..1925a3051 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -432,7 +432,7 @@ UniValue removeprunedfunds(const Config &config, txIds.push_back(txid); std::vector txIdsOut; - if (pwallet->ZapSelectTx(txIds, txIdsOut) != DB_LOAD_OK) { + if (pwallet->ZapSelectTx(txIds, txIdsOut) != DBErrors::LOAD_OK) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction."); } diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp index dd394ee5d..3164b89cf 100644 --- a/src/wallet/test/accounting_tests.cpp +++ b/src/wallet/test/accounting_tests.cpp @@ -18,7 +18,7 @@ static void GetResults(std::map &results) { std::list aes; results.clear(); - BOOST_CHECK(pwalletMain->ReorderTransactions() == DB_LOAD_OK); + BOOST_CHECK(pwalletMain->ReorderTransactions() == DBErrors::LOAD_OK); pwalletMain->ListAccountCreditDebit("", aes); for (CAccountingEntry &ae : aes) { results[ae.nOrderPos * SATOSHI] = ae; diff --git a/src/wallet/test/walletdb_tests.cpp b/src/wallet/test/walletdb_tests.cpp index c80af9eac..ccb8fcd2c 100644 --- a/src/wallet/test/walletdb_tests.cpp +++ b/src/wallet/test/walletdb_tests.cpp @@ -37,7 +37,7 @@ static std::unique_ptr TmpDB(const fs::path &pathTemp, static std::unique_ptr LoadWallet(CWalletDB *db) { std::unique_ptr wallet(new CWallet(Params())); DBErrors res = db->LoadWallet(wallet.get()); - BOOST_CHECK(res == DB_LOAD_OK); + BOOST_CHECK(res == DBErrors::LOAD_OK); return wallet; } } // namespace diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e41e0a141..7854d615a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -830,11 +830,11 @@ DBErrors CWallet::ReorderTransactions() { if (pwtx) { if (!walletdb.WriteTx(*pwtx)) { - return DB_LOAD_FAIL; + return DBErrors::LOAD_FAIL; } } else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) { - return DB_LOAD_FAIL; + return DBErrors::LOAD_FAIL; } } else { int64_t nOrderPosOff = 0; @@ -854,18 +854,18 @@ DBErrors CWallet::ReorderTransactions() { // Since we're changing the order, write it back. if (pwtx) { if (!walletdb.WriteTx(*pwtx)) { - return DB_LOAD_FAIL; + return DBErrors::LOAD_FAIL; } } else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) { - return DB_LOAD_FAIL; + return DBErrors::LOAD_FAIL; } } } walletdb.WriteOrderPosNext(nOrderPosNext); - return DB_LOAD_OK; + return DBErrors::LOAD_OK; } int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { @@ -3347,7 +3347,7 @@ DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { fFirstRunRet = false; DBErrors nLoadWalletRet = CWalletDB(*dbw, "cr+").LoadWallet(this); - if (nLoadWalletRet == DB_NEED_REWRITE) { + if (nLoadWalletRet == DBErrors::NEED_REWRITE) { if (dbw->Rewrite("\x04pool")) { setInternalKeyPool.clear(); setExternalKeyPool.clear(); @@ -3363,13 +3363,13 @@ DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty() && mapHdPubKeys.empty() && mapKeyMetadata.empty(); - if (nLoadWalletRet != DB_LOAD_OK) { + if (nLoadWalletRet != DBErrors::LOAD_OK) { return nLoadWalletRet; } uiInterface.LoadWallet(this); - return DB_LOAD_OK; + return DBErrors::LOAD_OK; } DBErrors CWallet::ZapSelectTx(std::vector &txIdsIn, @@ -3381,7 +3381,7 @@ DBErrors CWallet::ZapSelectTx(std::vector &txIdsIn, mapWallet.erase(txid); } - if (nZapSelectTxRet == DB_NEED_REWRITE) { + if (nZapSelectTxRet == DBErrors::NEED_REWRITE) { if (dbw->Rewrite("\x04pool")) { setInternalKeyPool.clear(); setExternalKeyPool.clear(); @@ -3392,18 +3392,18 @@ DBErrors CWallet::ZapSelectTx(std::vector &txIdsIn, } } - if (nZapSelectTxRet != DB_LOAD_OK) { + if (nZapSelectTxRet != DBErrors::LOAD_OK) { return nZapSelectTxRet; } MarkDirty(); - return DB_LOAD_OK; + return DBErrors::LOAD_OK; } DBErrors CWallet::ZapWalletTx(std::vector &vWtx) { DBErrors nZapWalletTxRet = CWalletDB(*dbw, "cr+").ZapWalletTx(vWtx); - if (nZapWalletTxRet == DB_NEED_REWRITE) { + if (nZapWalletTxRet == DBErrors::NEED_REWRITE) { if (dbw->Rewrite("\x04pool")) { LOCK(cs_wallet); setInternalKeyPool.clear(); @@ -3415,11 +3415,11 @@ DBErrors CWallet::ZapWalletTx(std::vector &vWtx) { } } - if (nZapWalletTxRet != DB_LOAD_OK) { + if (nZapWalletTxRet != DBErrors::LOAD_OK) { return nZapWalletTxRet; } - return DB_LOAD_OK; + return DBErrors::LOAD_OK; } bool CWallet::SetAddressBook(const CTxDestination &address, @@ -4224,7 +4224,7 @@ CWallet *CWallet::CreateWalletFromFile(const CChainParams &chainParams, std::unique_ptr tempWallet = MakeUnique(chainParams, std::move(dbw)); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); - if (nZapWalletRet != DB_LOAD_OK) { + if (nZapWalletRet != DBErrors::LOAD_OK) { InitError( strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); return nullptr; @@ -4239,25 +4239,25 @@ CWallet *CWallet::CreateWalletFromFile(const CChainParams &chainParams, new CWalletDBWrapper(&bitdb, walletFile)); CWallet *walletInstance = new CWallet(chainParams, std::move(dbw)); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); - if (nLoadWalletRet != DB_LOAD_OK) { - if (nLoadWalletRet == DB_CORRUPT) { + if (nLoadWalletRet != DBErrors::LOAD_OK) { + if (nLoadWalletRet == DBErrors::CORRUPT) { InitError( strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); return nullptr; } - if (nLoadWalletRet == DB_NONCRITICAL_ERROR) { + if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) { InitWarning(strprintf( _("Error reading %s! All keys read correctly, but transaction " "data" " or address book entries might be missing or incorrect."), walletFile)); - } else if (nLoadWalletRet == DB_TOO_NEW) { + } else if (nLoadWalletRet == DBErrors::TOO_NEW) { InitError(strprintf( _("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME))); return nullptr; - } else if (nLoadWalletRet == DB_NEED_REWRITE) { + } else if (nLoadWalletRet == DBErrors::NEED_REWRITE) { InitError(strprintf( _("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 0246ba5a5..85511e3c8 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -346,7 +346,7 @@ class CWalletTx : public CMerkleTx { } } - READWRITE(*(CMerkleTx *)this); + READWRITE(*static_cast(this)); //!< Used to be vtxPrev std::vector vUnused; READWRITE(vUnused); @@ -764,6 +764,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface { fBroadcastTransactions = false; fAbortRescan = false; fScanningWallet = false; + nRelockTime = 0; } std::map mapWallet; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index a66788510..0a6cfcb74 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -548,14 +548,14 @@ bool CWalletDB::IsKeyType(const std::string &strType) { DBErrors CWalletDB::LoadWallet(CWallet *pwallet) { CWalletScanState wss; bool fNoncriticalErrors = false; - DBErrors result = DB_LOAD_OK; + DBErrors result = DBErrors::LOAD_OK; LOCK(pwallet->cs_wallet); try { int nMinVersion = 0; if (batch.Read((std::string) "minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) { - return DB_TOO_NEW; + return DBErrors::TOO_NEW; } pwallet->LoadMinVersion(nMinVersion); } @@ -564,7 +564,7 @@ DBErrors CWalletDB::LoadWallet(CWallet *pwallet) { Dbc *pcursor = batch.GetCursor(); if (!pcursor) { LogPrintf("Error getting wallet database cursor\n"); - return DB_CORRUPT; + return DBErrors::CORRUPT; } while (true) { @@ -578,7 +578,7 @@ DBErrors CWalletDB::LoadWallet(CWallet *pwallet) { if (ret != 0) { LogPrintf("Error reading next record from wallet database\n"); - return DB_CORRUPT; + return DBErrors::CORRUPT; } // Try to be tolerant of single corrupt records: @@ -587,7 +587,7 @@ DBErrors CWalletDB::LoadWallet(CWallet *pwallet) { // losing keys is considered a catastrophic error, anything else // we assume the user can live with: if (IsKeyType(strType) || strType == "defaultkey") { - result = DB_CORRUPT; + result = DBErrors::CORRUPT; } else { // Leave other errors alone, if we try to fix them we might // make things worse. But do warn the user there is @@ -612,16 +612,16 @@ DBErrors CWalletDB::LoadWallet(CWallet *pwallet) { } catch (const boost::thread_interrupted &) { throw; } catch (...) { - result = DB_CORRUPT; + result = DBErrors::CORRUPT; } - if (fNoncriticalErrors && result == DB_LOAD_OK) { - result = DB_NONCRITICAL_ERROR; + if (fNoncriticalErrors && result == DBErrors::LOAD_OK) { + result = DBErrors::NONCRITICAL_ERROR; } // Any wallet corruption at all: skip any rewriting or upgrading, we don't // want to make it worse. - if (result != DB_LOAD_OK) { + if (result != DBErrors::LOAD_OK) { return result; } @@ -642,7 +642,7 @@ DBErrors CWalletDB::LoadWallet(CWallet *pwallet) { // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) { - return DB_NEED_REWRITE; + return DBErrors::NEED_REWRITE; } if (wss.nFileVersion < CLIENT_VERSION) { @@ -666,14 +666,13 @@ DBErrors CWalletDB::LoadWallet(CWallet *pwallet) { DBErrors CWalletDB::FindWalletTx(std::vector &txIds, std::vector &vWtx) { - bool fNoncriticalErrors = false; - DBErrors result = DB_LOAD_OK; + DBErrors result = DBErrors::LOAD_OK; try { int nMinVersion = 0; if (batch.Read((std::string) "minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) { - return DB_TOO_NEW; + return DBErrors::TOO_NEW; } } @@ -681,7 +680,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector &txIds, Dbc *pcursor = batch.GetCursor(); if (!pcursor) { LogPrintf("Error getting wallet database cursor\n"); - return DB_CORRUPT; + return DBErrors::CORRUPT; } while (true) { @@ -695,7 +694,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector &txIds, if (ret != 0) { LogPrintf("Error reading next record from wallet database\n"); - return DB_CORRUPT; + return DBErrors::CORRUPT; } std::string strType; @@ -715,11 +714,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector &txIds, } catch (const boost::thread_interrupted &) { throw; } catch (...) { - result = DB_CORRUPT; - } - - if (fNoncriticalErrors && result == DB_LOAD_OK) { - result = DB_NONCRITICAL_ERROR; + result = DBErrors::CORRUPT; } return result; @@ -731,7 +726,7 @@ DBErrors CWalletDB::ZapSelectTx(std::vector &txIdsIn, std::vector txIds; std::vector vWtx; DBErrors err = FindWalletTx(txIds, vWtx); - if (err != DB_LOAD_OK) { + if (err != DBErrors::LOAD_OK) { return err; } @@ -762,27 +757,27 @@ DBErrors CWalletDB::ZapSelectTx(std::vector &txIdsIn, } if (delerror) { - return DB_CORRUPT; + return DBErrors::CORRUPT; } - return DB_LOAD_OK; + return DBErrors::LOAD_OK; } DBErrors CWalletDB::ZapWalletTx(std::vector &vWtx) { // Build list of wallet TXs. std::vector txIds; DBErrors err = FindWalletTx(txIds, vWtx); - if (err != DB_LOAD_OK) { + if (err != DBErrors::LOAD_OK) { return err; } // Erase each wallet TX. for (const TxId &txid : txIds) { if (!EraseTx(txid)) { - return DB_CORRUPT; + return DBErrors::CORRUPT; } } - return DB_LOAD_OK; + return DBErrors::LOAD_OK; } void MaybeCompactWalletDB() { diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 3b53afc7d..8848c3e82 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -51,13 +51,13 @@ class uint160; class uint256; /** Error statuses for the wallet database */ -enum DBErrors { - DB_LOAD_OK, - DB_CORRUPT, - DB_NONCRITICAL_ERROR, - DB_TOO_NEW, - DB_LOAD_FAIL, - DB_NEED_REWRITE +enum class DBErrors { + LOAD_OK, + CORRUPT, + NONCRITICAL_ERROR, + TOO_NEW, + LOAD_FAIL, + NEED_REWRITE }; /** diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 4e00ec1bf..550c6ce57 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -63,6 +63,7 @@ def _test_getblockchaininfo(self): 'pruned', 'softforks', 'verificationprogress', + 'warnings', ] res = self.nodes[0].getblockchaininfo() # result should have pruneheight and default keys if pruning is enabled @@ -76,6 +77,31 @@ def _test_getblockchaininfo(self): assert_equal(sorted(res.keys()), keys) def _test_getchaintxstats(self): + self.log.info("Test getchaintxstats") + + # Test `getchaintxstats` invalid extra parameters + assert_raises_rpc_error( + -1, 'getchaintxstats', self.nodes[0].getchaintxstats, 0, '', 0) + + # Test `getchaintxstats` invalid `nblocks` + assert_raises_rpc_error( + -1, "JSON value is not an integer as expected", self.nodes[0].getchaintxstats, '') + assert_raises_rpc_error( + -8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, -1) + assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[ + 0].getchaintxstats, self.nodes[0].getblockcount()) + + # Test `getchaintxstats` invalid `blockhash` + assert_raises_rpc_error( + -1, "JSON value is not a string as expected", self.nodes[0].getchaintxstats, blockhash=0) + assert_raises_rpc_error( + -5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0') + blockhash = self.nodes[0].getblockhash(200) + self.nodes[0].invalidateblock(blockhash) + assert_raises_rpc_error( + -8, "Block is not in main chain", self.nodes[0].getchaintxstats, blockhash=blockhash) + self.nodes[0].reconsiderblock(blockhash) + chaintxstats = self.nodes[0].getchaintxstats(1) # 200 txs plus genesis tx assert_equal(chaintxstats['txcount'], 201) @@ -83,31 +109,31 @@ def _test_getchaintxstats(self): # we have to round because of binary math assert_equal(round(chaintxstats['txrate'] * 600, 10), Decimal(1)) - b1 = self.nodes[0].getblock(self.nodes[0].getblockhash(1)) - b200 = self.nodes[0].getblock(self.nodes[0].getblockhash(200)) + b1_hash = self.nodes[0].getblockhash(1) + b1 = self.nodes[0].getblock(b1_hash) + b200_hash = self.nodes[0].getblockhash(200) + b200 = self.nodes[0].getblock(b200_hash) time_diff = b200['mediantime'] - b1['mediantime'] chaintxstats = self.nodes[0].getchaintxstats() assert_equal(chaintxstats['time'], b200['time']) assert_equal(chaintxstats['txcount'], 201) + assert_equal(chaintxstats['window_final_block_hash'], b200_hash) assert_equal(chaintxstats['window_block_count'], 199) assert_equal(chaintxstats['window_tx_count'], 199) assert_equal(chaintxstats['window_interval'], time_diff) assert_equal( round(chaintxstats['txrate'] * time_diff, 10), Decimal(199)) - chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1['hash']) + chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1_hash) assert_equal(chaintxstats['time'], b1['time']) assert_equal(chaintxstats['txcount'], 2) + assert_equal(chaintxstats['window_final_block_hash'], b1_hash) assert_equal(chaintxstats['window_block_count'], 0) assert('window_tx_count' not in chaintxstats) assert('window_interval' not in chaintxstats) assert('txrate' not in chaintxstats) - assert_raises_rpc_error( - -8, "Invalid block count: should be between 0 and the block's height - 1", - self.nodes[0].getchaintxstats, 201) - def _test_gettxoutsetinfo(self): node = self.nodes[0] res = node.gettxoutsetinfo() @@ -239,5 +265,6 @@ def _test_getblock(self): assert_equal( getblockinfo['nextblockhash'], getblockheaderinfo['nextblockhash']) + if __name__ == '__main__': BlockchainTest().main() diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index d2b6169a2..1070a7580 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -62,6 +62,35 @@ def run_test(self): assert_raises_rpc_error( -25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) + ##################################### + # getrawtransaction with block hash # + ##################################### + + # make a tx by sending then generate 2 blocks; block1 has the tx in it + tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1) + block1, block2 = self.nodes[2].generate(2) + self.sync_all() + # We should be able to get the raw transaction by providing the correct block + gottx = self.nodes[0].getrawtransaction(tx, True, block1) + assert_equal(gottx['txid'], tx) + assert_equal(gottx['in_active_chain'], True) + # We should not have the 'in_active_chain' flag when we don't provide a block + gottx = self.nodes[0].getrawtransaction(tx, True) + assert_equal(gottx['txid'], tx) + assert 'in_active_chain' not in gottx + # We should not get the tx if we provide an unrelated block + assert_raises_rpc_error(-5, "No such transaction found", + self.nodes[0].getrawtransaction, tx, True, block2) + # An invalid block hash should raise the correct errors + assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", + self.nodes[0].getrawtransaction, tx, True, True) + assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", + self.nodes[0].getrawtransaction, tx, True, "foobar") + assert_raises_rpc_error(-8, "parameter 3 must be of length 64", + self.nodes[0].getrawtransaction, tx, True, "abcd1234") + assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, + tx, True, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + # # RAW TX MULTISIG TESTS # # @@ -232,15 +261,15 @@ def run_test(self): # 6. invalid parameters - supply txid and string "Flase" assert_raises_rpc_error( - -3, "Invalid type", self.nodes[0].getrawtransaction, txHash, "False") + -1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "False") # 7. invalid parameters - supply txid and empty array assert_raises_rpc_error( - -3, "Invalid type", self.nodes[0].getrawtransaction, txHash, []) + -1, "not a boolean", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict assert_raises_rpc_error( - -3, "Invalid type", self.nodes[0].getrawtransaction, txHash, {}) + -1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {}) # Sanity checks on verbose getrawtransaction output rawTxOutput = self.nodes[0].getrawtransaction(txHash, True) diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py index bb5e1b5ed..df9634f0f 100755 --- a/test/functional/rpc_signrawtransaction.py +++ b/test/functional/rpc_signrawtransaction.py @@ -12,6 +12,7 @@ class SignRawTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 + self.extra_args = [["-deprecatedrpc=signrawtransaction"]] def successful_signing_test(self): """Creates and signs a valid raw transaction with one input. diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py index 692c525df..94332e5b2 100755 --- a/test/functional/wallet_encryption.py +++ b/test/functional/wallet_encryption.py @@ -14,6 +14,7 @@ class WalletEncryptionTest(BitcoinTestFramework): + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -32,9 +33,13 @@ def run_test(self): self.nodes[0].node_encrypt_wallet(passphrase) self.start_node(0) + # Check the encrypted wallet is marked as locked on initialization + assert_equal(self.nodes[0].getwalletinfo()['unlocked_until'], 0) + # Test that the wallet is encrypted - assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", - self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error( + -13, "Please enter the wallet passphrase with walletpassphrase first", + self.nodes[0].dumpprivkey, address) # Check that walletpassphrase works self.nodes[0].walletpassphrase(passphrase, 2) @@ -42,19 +47,25 @@ def run_test(self): # Check that the timeout is right time.sleep(2) - assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", - self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error( + -13, "Please enter the wallet passphrase with walletpassphrase first", + self.nodes[0].dumpprivkey, address) # Test wrong passphrase assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10) - # Test walletlock + # Test walletlock and unlocked_until values + self.nodes[0].setmocktime(1) self.nodes[0].walletpassphrase(passphrase, 84600) assert_equal(privkey, self.nodes[0].dumpprivkey(address)) + assert_equal( + self.nodes[0].getwalletinfo()['unlocked_until'], 1 + 84600) self.nodes[0].walletlock() - assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", - self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error( + -13, "Please enter the wallet passphrase with walletpassphrase first", + self.nodes[0].dumpprivkey, address) + assert_equal(self.nodes[0].getwalletinfo()['unlocked_until'], 0) # Test passphrase changes self.nodes[0].walletpassphrasechange(passphrase, passphrase2) diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 8b8517713..315474d72 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -160,7 +160,7 @@ def test_double_spend(self): 'vout': utxo['vout'], }] txid1 = self.nodes[1].sendrawtransaction( - self.nodes[1].signrawtransaction( + self.nodes[1].signrawtransactionwithwallet( self.nodes[1].createrawtransaction(utxoDicts, recipientDict))['hex']) # send from nodes[2] using utxo to nodes[3] @@ -169,7 +169,7 @@ def test_double_spend(self): self.nodes[2].getnewaddress(): change, } self.nodes[2].sendrawtransaction( - self.nodes[2].signrawtransaction( + self.nodes[2].signrawtransactionwithwallet( self.nodes[2].createrawtransaction(utxoDicts, recipientDict2))['hex']) # generate on both sides @@ -236,7 +236,7 @@ def test_double_send(self): 'txid': utxo['txid'], 'vout': utxo['vout'], }] - signedtxres = self.nodes[2].signrawtransaction( + signedtxres = self.nodes[2].signrawtransactionwithwallet( self.nodes[2].createrawtransaction(utxoDicts, recipientDict)) assert signedtxres['complete']