Merge pull request #4468

e11b2ce Fix large reorgs (Pieter Wuille)
afc32c5 Fix rebuild-chainstate feature and improve its performance (Pieter Wuille)
16d5194 Skip reindexed blocks individually (Pieter Wuille)
ad96e7c Make -reindex cope with out-of-order blocks (Wladimir J. van der Laan)
e17bd58 Rename setBlockIndexValid to setBlockIndexCandidates (Pieter Wuille)
1af838b Add height to "Requesting block" debug (R E Broadley)
1bcee67 Better logging of stalling (R E Broadley)
4c93322 Improve getheaders (sending) logging (R E Broadley)
f244c99 Remove CheckMinWork, as we always know all parent headers (Pieter Wuille)
ad6e601 RPC additions after headers-first (Pieter Wuille)
341735e Headers-first synchronization (Pieter Wuille)
This commit is contained in:
Wladimir J. van der Laan 2014-10-17 12:24:29 +02:00
commit 84d13eef88
No known key found for this signature in database
GPG key ID: 74810B012346C9A6
15 changed files with 479 additions and 501 deletions

View file

@ -10,7 +10,7 @@ touch "$DATADIR/regtest/debug.log"
tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" &
WAITER=$!
PORT=`expr 10000 + $$ % 55536`
"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` &
"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -checkmempool=0 -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` &
BITCOIND=$!
#Install a watchdog.

View file

@ -49,12 +49,29 @@ struct CDiskBlockPos
};
enum BlockStatus {
// Unused.
BLOCK_VALID_UNKNOWN = 0,
BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future
BLOCK_VALID_TREE = 2, // parent found, difficulty matches, timestamp >= median previous, checkpoint
BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root
BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30
BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok
// Parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future
BLOCK_VALID_HEADER = 1,
// All parent headers found, difficulty matches, timestamp >= median previous, checkpoint. Implies all parents
// are also at least TREE.
BLOCK_VALID_TREE = 2,
// Only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids,
// sigops, size, merkle root. Implies all parents are at least TREE but not necessarily TRANSACTIONS. When all
// parent blocks also have TRANSACTIONS, CBlockIndex::nChainTx will be set.
BLOCK_VALID_TRANSACTIONS = 3,
// Outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30.
// Implies all parents are also at least CHAIN.
BLOCK_VALID_CHAIN = 4,
// Scripts & signatures ok. Implies all parents are also at least SCRIPTS.
BLOCK_VALID_SCRIPTS = 5,
// All validity bits.
BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS |
BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS,
@ -103,7 +120,8 @@ public:
// Note: in a potential headers-first mode, this number cannot be relied upon
unsigned int nTx;
// (memory only) Number of transactions in the chain up to and including this block
// (memory only) Number of transactions in the chain up to and including this block.
// This value will be non-zero only if and only if transactions for this block and all its parents are available.
unsigned int nChainTx; // change to 64-bit type when necessary; won't happen before 2030
// Verification status of this block. See enum BlockStatus
@ -146,7 +164,7 @@ public:
SetNull();
}
CBlockIndex(CBlockHeader& block)
CBlockIndex(const CBlockHeader& block)
{
SetNull();

View file

@ -974,7 +974,7 @@ bool AppInit2(boost::thread_group& threadGroup)
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
if (!mapBlockIndex.empty() && chainActive.Genesis() == NULL)
if (!mapBlockIndex.empty() && mapBlockIndex.count(Params().HashGenesisBlock()) == 0)
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
// Initialize the block index (no-op if non-empty database was already loaded)

File diff suppressed because it is too large Load diff

View file

@ -72,9 +72,17 @@ static const int MAX_SCRIPTCHECK_THREADS = 16;
/** -par default (number of script-checking threads, 0 = auto) */
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 128;
/** Timeout in seconds before considering a block download peer unresponsive. */
static const unsigned int BLOCK_DOWNLOAD_TIMEOUT = 60;
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
* less than this number, we reached their tip. Changing this value is a protocol upgrade. */
static const unsigned int MAX_HEADERS_RESULTS = 2000;
/** Size of the "block download window": how far ahead of our current height do we fetch?
* Larger windows tolerate larger download speed differences between peer, but increase the potential
* degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
* harder). We'll probably want to make this a per-peer adaptive value at some point. */
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
/** "reject" message codes **/
static const unsigned char REJECT_MALFORMED = 0x01;
@ -110,6 +118,9 @@ extern bool fIsBareMultisigStd;
extern unsigned int nCoinCacheSize;
extern CFeeRate minRelayTxFee;
// Best header we've seen so far (used for getheaders queries' starting points).
extern CBlockIndex *pindexBestHeader;
// Minimum disk space required - used in CheckDiskSpace()
static const uint64_t nMinDiskSpace = 52428800;
@ -137,8 +148,6 @@ void RegisterNodeSignals(CNodeSignals& nodeSignals);
/** Unregister a network node */
void UnregisterNodeSignals(CNodeSignals& nodeSignals);
void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd);
/** Process an incoming block */
bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL);
/** Check whether enough disk space is available for an incoming block */
@ -193,6 +202,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
struct CNodeStateStats {
int nMisbehavior;
int nSyncHeight;
int nCommonHeight;
std::vector<int> vHeightInFlight;
};
struct CDiskTxPos : public CDiskBlockPos
@ -439,9 +450,6 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
// Apply the effects of this block (with given index) on the UTXO set represented by coins
bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false);
// Add this block to the block index, and if necessary, switch the active block chain to this
bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos);
// Context-independent validity checks
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
@ -449,7 +457,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = t
// Store block on disk
// if dbp is provided, the file is known to already reside on disk
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL);
bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL);
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL);

View file

@ -73,7 +73,6 @@ map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfReachable[NET_MAX] = {};
static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
static CNode* pnodeSync = NULL;
uint64_t nLocalHostNonce = 0;
static std::vector<ListenSocket> vhListenSocket;
CAddrMan addrman;
@ -519,10 +518,6 @@ void CNode::CloseSocketDisconnect()
TRY_LOCK(cs_vRecvMsg, lockRecv);
if (lockRecv)
vRecvMsg.clear();
// if this was the sync node, we'll need a new one
if (this == pnodeSync)
pnodeSync = NULL;
}
void CNode::PushVersion()
@ -615,7 +610,6 @@ void CNode::copyStats(CNodeStats &stats)
X(nSendBytes);
X(nRecvBytes);
X(fWhitelisted);
stats.fSyncNode = (this == pnodeSync);
// It is common for nodes with good ping times to suddenly become lagged,
// due to a new block arriving or other large transfer.
@ -1487,61 +1481,20 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
}
// for now, use a very simple selection metric: the node from which we received
// most recently
static int64_t NodeSyncScore(const CNode *pnode) {
return pnode->nLastRecv;
}
void static StartSync(const vector<CNode*> &vNodes) {
CNode *pnodeNewSync = NULL;
int64_t nBestScore = 0;
int nBestHeight = g_signals.GetHeight().get_value_or(0);
// Iterate over all nodes
BOOST_FOREACH(CNode* pnode, vNodes) {
// check preconditions for allowing a sync
if (!pnode->fClient && !pnode->fOneShot &&
!pnode->fDisconnect && pnode->fSuccessfullyConnected &&
(pnode->nStartingHeight > (nBestHeight - 144)) &&
(pnode->nVersion < NOBLKS_VERSION_START || pnode->nVersion >= NOBLKS_VERSION_END)) {
// if ok, compare node's score with the best so far
int64_t nScore = NodeSyncScore(pnode);
if (pnodeNewSync == NULL || nScore > nBestScore) {
pnodeNewSync = pnode;
nBestScore = nScore;
}
}
}
// if a new sync candidate was found, start sync!
if (pnodeNewSync) {
pnodeNewSync->fStartSync = true;
pnodeSync = pnodeNewSync;
}
}
void ThreadMessageHandler()
{
SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
while (true)
{
bool fHaveSyncNode = false;
vector<CNode*> vNodesCopy;
{
LOCK(cs_vNodes);
vNodesCopy = vNodes;
BOOST_FOREACH(CNode* pnode, vNodesCopy) {
pnode->AddRef();
if (pnode == pnodeSync)
fHaveSyncNode = true;
}
}
if (!fHaveSyncNode)
StartSync(vNodesCopy);
// Poll the connected nodes for messages
CNode* pnodeTrickle = NULL;
if (!vNodesCopy.empty())
@ -2078,10 +2031,7 @@ CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fIn
nSendSize = 0;
nSendOffset = 0;
hashContinue = 0;
pindexLastGetBlocksBegin = 0;
hashLastGetBlocksEnd = 0;
nStartingHeight = -1;
fStartSync = false;
fGetAddr = false;
fRelayTxes = false;
setInventoryKnown.max_size(SendBufferSize() / 1000);

View file

@ -158,7 +158,6 @@ public:
int nStartingHeight;
uint64_t nSendBytes;
uint64_t nRecvBytes;
bool fSyncNode;
bool fWhitelisted;
double dPingTime;
double dPingWait;
@ -276,10 +275,7 @@ protected:
public:
uint256 hashContinue;
CBlockIndex* pindexLastGetBlocksBegin;
uint256 hashLastGetBlocksEnd;
int nStartingHeight;
bool fStartSync;
// flood relay
std::vector<CAddress> vAddrToSend;

View file

@ -98,39 +98,6 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits)
return true;
}
//
// true if nBits is greater than the minimum amount of work that could
// possibly be required deltaTime after minimum work required was nBase
//
bool CheckMinWork(unsigned int nBits, unsigned int nBase, int64_t deltaTime)
{
bool fOverflow = false;
uint256 bnNewBlock;
bnNewBlock.SetCompact(nBits, NULL, &fOverflow);
if (fOverflow)
return false;
const uint256 &bnLimit = Params().ProofOfWorkLimit();
// Testnet has min-difficulty blocks
// after Params().TargetSpacing()*2 time between blocks:
if (Params().AllowMinDifficultyBlocks() && deltaTime > Params().TargetSpacing()*2)
return bnNewBlock <= bnLimit;
uint256 bnResult;
bnResult.SetCompact(nBase);
while (deltaTime > 0 && bnResult < bnLimit)
{
// Maximum 400% adjustment...
bnResult *= 4;
// ... in best-case exactly 4-times-normal target time
deltaTime -= Params().TargetTimespan()*4;
}
if (bnResult > bnLimit)
bnResult = bnLimit;
return bnNewBlock <= bnResult;
}
void UpdateTime(CBlockHeader* pblock, const CBlockIndex* pindexPrev)
{
pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());

View file

@ -16,8 +16,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
/** Check the work is more than the minimum a received block needs, without knowing its direct parent */
bool CheckMinWork(unsigned int nBits, unsigned int nBase, int64_t deltaTime);
void UpdateTime(CBlockHeader* block, const CBlockIndex* pindexPrev);

View file

@ -836,29 +836,6 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Sync Node</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="peerSyncNode">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text">
<string>N/A</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_29">
<property name="text">

View file

@ -611,7 +611,6 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
ui->peerHeight->setText(QString("%1").arg(stats->nodeStats.nStartingHeight));
ui->peerSyncNode->setText(stats->nodeStats.fSyncNode ? tr("Yes") : tr("No"));
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.

View file

@ -445,6 +445,7 @@ Value getblockchaininfo(const Array& params, bool fHelp)
"{\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
" \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
" \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n"
" \"bestblockhash\": \"...\", (string) the hash of the currently best block\n"
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n"
@ -458,6 +459,7 @@ Value getblockchaininfo(const Array& params, bool fHelp)
Object obj;
obj.push_back(Pair("chain", Params().NetworkIDString()));
obj.push_back(Pair("blocks", (int)chainActive.Height()));
obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1));
obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(chainActive.Tip())));

View file

@ -97,7 +97,12 @@ Value getpeerinfo(const Array& params, bool fHelp)
" \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n"
" \"startingheight\": n, (numeric) The starting height (block) of the peer\n"
" \"banscore\": n, (numeric) The ban score\n"
" \"syncnode\": true|false (boolean) if sync node\n"
" \"synced_headers\": n, (numeric) The last header we have in common with this peer\n"
" \"synced_blocks\": n, (numeric) The last block we have in common with this peer\n"
" \"inflight\": [\n"
" n, (numeric) The heights of blocks we're currently asking from this peer\n"
" ...\n"
" ]\n"
" }\n"
" ,...\n"
"]\n"
@ -137,9 +142,14 @@ Value getpeerinfo(const Array& params, bool fHelp)
obj.push_back(Pair("startingheight", stats.nStartingHeight));
if (fStateStats) {
obj.push_back(Pair("banscore", statestats.nMisbehavior));
obj.push_back(Pair("syncheight", statestats.nSyncHeight));
obj.push_back(Pair("synced_headers", statestats.nSyncHeight));
obj.push_back(Pair("synced_blocks", statestats.nCommonHeight));
Array heights;
BOOST_FOREACH(int height, statestats.vHeightInFlight) {
heights.push_back(height);
}
obj.push_back(Pair("inflight", heights));
}
obj.push_back(Pair("syncnode", stats.fSyncNode));
obj.push_back(Pair("whitelisted", stats.fWhitelisted));
ret.push_back(obj);

View file

@ -106,51 +106,6 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
BOOST_CHECK(!CNode::IsBanned(addr));
}
static bool CheckNBits(unsigned int nbits1, int64_t time1, unsigned int nbits2, int64_t time2)\
{
if (time1 > time2)
return CheckNBits(nbits2, time2, nbits1, time1);
int64_t deltaTime = time2-time1;
return CheckMinWork(nbits2, nbits1, deltaTime);
}
BOOST_AUTO_TEST_CASE(DoS_checknbits)
{
using namespace boost::assign; // for 'map_list_of()'
// Timestamps,nBits from the bitcoin block chain.
// These are the block-chain checkpoint blocks
typedef std::map<int64_t, unsigned int> BlockData;
BlockData chainData =
map_list_of(1239852051,486604799)(1262749024,486594666)
(1279305360,469854461)(1280200847,469830746)(1281678674,469809688)
(1296207707,453179945)(1302624061,453036989)(1309640330,437004818)
(1313172719,436789733);
// Make sure CheckNBits considers every combination of block-chain-lock-in-points
// "sane":
BOOST_FOREACH(const BlockData::value_type& i, chainData)
{
BOOST_FOREACH(const BlockData::value_type& j, chainData)
{
BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first));
}
}
// Test a couple of insane combinations:
BlockData::value_type firstcheck = *(chainData.begin());
BlockData::value_type lastcheck = *(chainData.rbegin());
// First checkpoint difficulty at or a while after the last checkpoint time should fail when
// compared to last checkpoint
BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*10, lastcheck.second, lastcheck.first));
BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*60*24*14, lastcheck.second, lastcheck.first));
// ... but OK if enough time passed for difficulty to adjust downward:
BOOST_CHECK(CheckNBits(firstcheck.second, lastcheck.first+60*60*24*365*4, lastcheck.second, lastcheck.first));
}
CTransaction RandomOrphan()
{
std::map<uint256, COrphanTx>::iterator it;

View file

@ -33,8 +33,11 @@ static const int PROTOCOL_VERSION = 70002;
// initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
// In this version, 'getheaders' was introduced.
static const int GETHEADERS_VERSION = 31800;
// disconnect from peers older than this proto version
static const int MIN_PEER_PROTO_VERSION = 209;
static const int MIN_PEER_PROTO_VERSION = GETHEADERS_VERSION;
// nTime field added to CAddress, starting with this version;
// if possible, avoid requesting addresses nodes older than this