aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.qt.include1
-rw-r--r--src/bitcoind.cpp10
-rw-r--r--src/httpserver.cpp28
-rw-r--r--src/net_processing.cpp8
-rw-r--r--src/tinyformat.h14
-rw-r--r--src/validation.cpp82
-rw-r--r--src/wallet/db.cpp5
-rw-r--r--src/wallet/rpcwallet.cpp27
9 files changed, 136 insertions, 43 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 90deff48b0..3e43076878 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -500,10 +500,6 @@ clean-local:
## FIXME: How to get the appropriate modulename_CPPFLAGS in here?
$(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@
-.mm.o:
- $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
- $(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $<
-
check-symbols: $(bin_PROGRAMS)
if GLIBC_BACK_COMPAT
@echo "Checking glibc back compat..."
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index e4b64c1ca7..0767ee1302 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -368,6 +368,7 @@ BITCOIN_QT_INCLUDES = -I$(builddir)/qt -I$(srcdir)/qt -I$(srcdir)/qt/forms \
qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
$(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS)
qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
+qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \
$(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES)
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 543eba0e69..5f88c35dbd 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -120,7 +120,7 @@ bool AppInit(int argc, char* argv[])
for (int i = 1; i < argc; i++) {
if (!IsSwitchChar(argv[i][0])) {
fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
- exit(EXIT_FAILURE);
+ return false;
}
}
@@ -132,17 +132,17 @@ bool AppInit(int argc, char* argv[])
if (!AppInitBasicSetup())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (!AppInitParameterInteraction())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (!AppInitSanityChecks())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (gArgs.GetBoolArg("-daemon", false))
{
@@ -163,7 +163,7 @@ bool AppInit(int argc, char* argv[])
if (!AppInitLockDataDirectory())
{
// If locking the data directory failed, exit immediately
- exit(EXIT_FAILURE);
+ return false;
}
fRet = AppInitMain(threadGroup, scheduler);
}
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 31b6a3705b..f6cbaa20b7 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -24,6 +24,7 @@
#include <event2/thread.h>
#include <event2/buffer.h>
+#include <event2/bufferevent.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>
@@ -239,6 +240,16 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m)
/** HTTP request callback */
static void http_request_cb(struct evhttp_request* req, void* arg)
{
+ // Disable reading to work around a libevent bug, fixed in 2.2.0.
+ if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
+ evhttp_connection* conn = evhttp_request_get_connection(req);
+ if (conn) {
+ bufferevent* bev = evhttp_connection_get_bufferevent(conn);
+ if (bev) {
+ bufferevent_disable(bev, EV_READ);
+ }
+ }
+ }
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
@@ -601,8 +612,21 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
struct evbuffer* evb = evhttp_request_get_output_buffer(req);
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
- HTTPEvent* ev = new HTTPEvent(eventBase, true,
- std::bind(evhttp_send_reply, req, nStatus, (const char*)nullptr, (struct evbuffer *)nullptr));
+ auto req_copy = req;
+ HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
+ evhttp_send_reply(req_copy, nStatus, nullptr, nullptr);
+ // Re-enable reading from the socket. This is the second part of the libevent
+ // workaround above.
+ if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
+ evhttp_connection* conn = evhttp_request_get_connection(req_copy);
+ if (conn) {
+ bufferevent* bev = evhttp_connection_get_bufferevent(conn);
+ if (bev) {
+ bufferevent_enable(bev, EV_READ | EV_WRITE);
+ }
+ }
+ }
+ });
ev->trigger(nullptr);
replySent = true;
req = nullptr; // transferred back to main thread
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index faa0c7620b..35d73a6a2b 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1305,8 +1305,8 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
int nDoS;
if (state.IsInvalid(nDoS)) {
+ LOCK(cs_main);
if (nDoS > 0) {
- LOCK(cs_main);
Misbehaving(pfrom->GetId(), nDoS);
}
if (punish_duplicate_invalid && mapBlockIndex.find(first_invalid_header.GetHash()) != mapBlockIndex.end()) {
@@ -2591,11 +2591,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId());
- // Process all blocks from whitelisted peers, even if not requested,
- // unless we're still syncing with the network.
- // Such an unrequested block may still be processed, subject to the
- // conditions in AcceptBlock().
- bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
+ bool forceProcessing = false;
const uint256 hash(pblock->GetHash());
{
LOCK(cs_main);
diff --git a/src/tinyformat.h b/src/tinyformat.h
index 2e453e56bb..d34cfaa94f 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -495,7 +495,11 @@ namespace detail {
class FormatArg
{
public:
- FormatArg() {}
+ FormatArg()
+ : m_value(nullptr),
+ m_formatImpl(nullptr),
+ m_toIntImpl(nullptr)
+ { }
template<typename T>
explicit FormatArg(const T& value)
@@ -507,11 +511,15 @@ class FormatArg
void format(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc) const
{
+ assert(m_value);
+ assert(m_formatImpl);
m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
}
int toInt() const
{
+ assert(m_value);
+ assert(m_toIntImpl);
return m_toIntImpl(m_value);
}
@@ -712,23 +720,27 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
break;
case 'X':
out.setf(std::ios::uppercase);
+ // Falls through
case 'x': case 'p':
out.setf(std::ios::hex, std::ios::basefield);
intConversion = true;
break;
case 'E':
out.setf(std::ios::uppercase);
+ // Falls through
case 'e':
out.setf(std::ios::scientific, std::ios::floatfield);
out.setf(std::ios::dec, std::ios::basefield);
break;
case 'F':
out.setf(std::ios::uppercase);
+ // Falls through
case 'f':
out.setf(std::ios::fixed, std::ios::floatfield);
break;
case 'G':
out.setf(std::ios::uppercase);
+ // Falls through
case 'g':
out.setf(std::ios::dec, std::ios::basefield);
// As in boost::format, let stream decide float format.
diff --git a/src/validation.cpp b/src/validation.cpp
index 78eb6d7302..83cbcb42cb 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -156,6 +156,26 @@ namespace {
/** chainwork for the last block that preciousblock has been applied to. */
arith_uint256 nLastPreciousChainwork = 0;
+ /** In order to efficiently track invalidity of headers, we keep the set of
+ * blocks which we tried to connect and found to be invalid here (ie which
+ * were set to BLOCK_FAILED_VALID since the last restart). We can then
+ * walk this set and check if a new header is a descendant of something in
+ * this set, preventing us from having to walk mapBlockIndex when we try
+ * to connect a bad block and fail.
+ *
+ * While this is more complicated than marking everything which descends
+ * from an invalid block as invalid at the time we discover it to be
+ * invalid, doing so would require walking all of mapBlockIndex to find all
+ * descendants. Since this case should be very rare, keeping track of all
+ * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
+ * well.
+ *
+ * Because we alreardy walk mapBlockIndex in height-order at startup, we go
+ * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
+ * instead of putting things in this set.
+ */
+ std::set<CBlockIndex*> g_failed_blocks;
+
/** Dirty block index entries. */
std::set<CBlockIndex*> setDirtyBlockIndex;
@@ -1180,6 +1200,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
+ g_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
@@ -2533,17 +2554,18 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
{
AssertLockHeld(cs_main);
- // Mark the block itself as invalid.
- pindex->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(pindex);
- setBlockIndexCandidates.erase(pindex);
+ // We first disconnect backwards and then mark the blocks as invalid.
+ // This prevents a case where pruned nodes may fail to invalidateblock
+ // and be left unable to start as they have no tip candidates (as there
+ // are no blocks that meet the "have data and are not invalid per
+ // nStatus" criteria for inclusion in setBlockIndexCandidates).
+
+ bool pindex_was_in_chain = false;
+ CBlockIndex *invalid_walk_tip = chainActive.Tip();
DisconnectedBlockTransactions disconnectpool;
while (chainActive.Contains(pindex)) {
- CBlockIndex *pindexWalk = chainActive.Tip();
- pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(pindexWalk);
- setBlockIndexCandidates.erase(pindexWalk);
+ pindex_was_in_chain = true;
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
@@ -2554,6 +2576,21 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
}
}
+ // Now mark the blocks we just disconnected as descendants invalid
+ // (note this may not be all descendants).
+ while (pindex_was_in_chain && invalid_walk_tip != pindex) {
+ invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(invalid_walk_tip);
+ setBlockIndexCandidates.erase(invalid_walk_tip);
+ invalid_walk_tip = invalid_walk_tip->pprev;
+ }
+
+ // Mark the block itself as invalid.
+ pindex->nStatus |= BLOCK_FAILED_VALID;
+ setDirtyBlockIndex.insert(pindex);
+ setBlockIndexCandidates.erase(pindex);
+ g_failed_blocks.insert(pindex);
+
// DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
@@ -2591,6 +2628,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr;
}
+ g_failed_blocks.erase(it->second);
}
it++;
}
@@ -3066,6 +3104,21 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
+
+ if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
+ for (const CBlockIndex* failedit : g_failed_blocks) {
+ if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
+ assert(failedit->nStatus & BLOCK_FAILED_VALID);
+ CBlockIndex* invalid_walk = pindexPrev;
+ while (invalid_walk != failedit) {
+ invalid_walk->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(invalid_walk);
+ invalid_walk = invalid_walk->pprev;
+ }
+ return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
+ }
+ }
+ }
}
if (pindex == nullptr)
pindex = AddToBlockIndex(block);
@@ -3117,7 +3170,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
// process an unrequested block if it's new and has enough work to
// advance our tip, and isn't too many blocks ahead.
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
- bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true);
+ bool fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true);
// Blocks that are too out-of-order needlessly limit the effectiveness of
// pruning, because pruning will not delete block files that contain any
// blocks which are too close in height to the tip. Apply this test
@@ -3134,9 +3187,9 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
// and unrequested blocks.
if (fAlreadyHave) return true;
if (!fRequested) { // If we didn't ask for it:
- if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
- if (!fHasMoreWork) return true; // Don't process less-work chains
- if (fTooFarAhead) return true; // Block height is too high
+ if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
+ if (!fHasMoreOrSameWork) return true; // Don't process less-work chains
+ if (fTooFarAhead) return true; // Block height is too high
// Protect against DoS attacks from low-work chains.
// If our tip is behind, a peer could try to send us
@@ -3494,6 +3547,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pindex->nChainTx = pindex->nTx;
}
}
+ if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
+ pindex->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(pindex);
+ }
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr))
setBlockIndexCandidates.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
@@ -3884,6 +3941,7 @@ void UnloadBlockIndex()
nLastBlockFile = 0;
nBlockSequenceId = 1;
setDirtyBlockIndex.clear();
+ g_failed_blocks.clear();
setDirtyFileInfo.clear();
versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 459d289a42..5d48b01c2e 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -705,6 +705,11 @@ bool CWalletDBWrapper::Backup(const std::string& strDest)
pathDest /= strFile;
try {
+ if (fs::equivalent(pathSrc, pathDest)) {
+ LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
+ return false;
+ }
+
fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
LogPrintf("copied %s to %s\n", strFile, pathDest.string());
return true;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index d6989add89..c77cfa9ea9 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1893,19 +1893,20 @@ UniValue listsinceblock(const JSONRPCRequest& request)
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
- if (!request.params[0].isNull()) {
+ if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
uint256 blockId;
blockId.SetHex(request.params[0].get_str());
BlockMap::iterator it = mapBlockIndex.find(blockId);
- if (it != mapBlockIndex.end()) {
- paltindex = pindex = it->second;
- if (chainActive[pindex->nHeight] != pindex) {
- // the block being asked for is a part of a deactivated chain;
- // we don't want to depend on its perceived height in the block
- // chain, we want to instead use the last common ancestor
- pindex = chainActive.FindFork(pindex);
- }
+ if (it == mapBlockIndex.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ paltindex = pindex = it->second;
+ if (chainActive[pindex->nHeight] != pindex) {
+ // the block being asked for is a part of a deactivated chain;
+ // we don't want to depend on its perceived height in the block
+ // chain, we want to instead use the last common ancestor
+ pindex = chainActive.FindFork(pindex);
}
}
@@ -2179,7 +2180,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
+ if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"walletpassphrase \"passphrase\" timeout\n"
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
@@ -2243,7 +2244,7 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
+ if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n"
@@ -2294,7 +2295,7 @@ UniValue walletlock(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) {
+ if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"walletlock\n"
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
@@ -2334,7 +2335,7 @@ UniValue encryptwallet(const JSONRPCRequest& request)
return NullUniValue;
}
- if (!pwallet->IsCrypted() && (request.fHelp || request.params.size() != 1)) {
+ if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"encryptwallet \"passphrase\"\n"
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"