Marek Kotewicz
10 years ago
258 changed files with 14372 additions and 3618 deletions
Before Width: | Height: | Size: 171 KiB |
@ -0,0 +1,49 @@ |
|||
# Find rocksdb |
|||
# |
|||
# Find the rocksdb includes and library |
|||
# |
|||
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH |
|||
# |
|||
# This module defines |
|||
# ROCKSDB_INCLUDE_DIRS, where to find header, etc. |
|||
# ROCKSDB_LIBRARIES, the libraries needed to use rocksdb. |
|||
# ROCKSDB_FOUND, If false, do not try to use rocksdb. |
|||
|
|||
# only look in default directories |
|||
find_path( |
|||
ROCKSDB_INCLUDE_DIR |
|||
NAMES rocksdb/db.h |
|||
DOC "rocksdb include dir" |
|||
) |
|||
|
|||
find_library( |
|||
ROCKSDB_LIBRARY |
|||
NAMES rocksdb |
|||
DOC "rocksdb library" |
|||
) |
|||
|
|||
set(ROCKSDB_INCLUDE_DIRS ${ROCKSDB_INCLUDE_DIR}) |
|||
set(ROCKSDB_LIBRARIES ${ROCKSDB_LIBRARY}) |
|||
|
|||
# debug library on windows |
|||
# same naming convention as in qt (appending debug library with d) |
|||
# boost is using the same "hack" as us with "optimized" and "debug" |
|||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") |
|||
|
|||
find_library( |
|||
ROCKSDB_LIBRARY_DEBUG |
|||
NAMES rocksdbd |
|||
DOC "rocksdb debug library" |
|||
) |
|||
|
|||
set(ROCKSDB_LIBRARIES optimized ${ROCKSDB_LIBRARIES} debug ${ROCKSDB_LIBRARY_DEBUG}) |
|||
|
|||
endif() |
|||
|
|||
# handle the QUIETLY and REQUIRED arguments and set ROCKSDB_FOUND to TRUE |
|||
# if all listed variables are TRUE, hide their existence from configuration view |
|||
include(FindPackageHandleStandardArgs) |
|||
find_package_handle_standard_args(rocksdb DEFAULT_MSG |
|||
ROCKSDB_INCLUDE_DIR ROCKSDB_LIBRARY) |
|||
mark_as_advanced (ROCKSDB_INCLUDE_DIR ROCKSDB_LIBRARY) |
|||
|
@ -0,0 +1,63 @@ |
|||
#!/bin/bash |
|||
|
|||
CPP_ETHEREUM_PATH=$(pwd) |
|||
BUILD_DIR=$CPP_ETHEREUM_PATH/build |
|||
TEST_MODE="" |
|||
|
|||
for i in "$@" |
|||
do |
|||
case $i in |
|||
-builddir) |
|||
shift |
|||
((i++)) |
|||
BUILD_DIR=${!i} |
|||
shift |
|||
;; |
|||
--all) |
|||
TEST_MODE="--all" |
|||
shift |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
which $BUILD_DIR/test/testeth >/dev/null 2>&1 |
|||
if [ $? != 0 ] |
|||
then |
|||
echo "You need to compile and build ethereum with cmake -DPROFILING option to the build dir!" |
|||
exit; |
|||
fi |
|||
|
|||
OUTPUT_DIR=$BUILD_DIR/test/coverage |
|||
if which lcov >/dev/null; then |
|||
if which genhtml >/dev/null; then |
|||
echo Cleaning previous report... |
|||
if [ -d "$OUTPUT_DIR" ]; then |
|||
rm -r $OUTPUT_DIR |
|||
fi |
|||
mkdir $OUTPUT_DIR |
|||
lcov --directory $BUILD_DIR --zerocounters |
|||
lcov --capture --initial --directory $BUILD_DIR --output-file $OUTPUT_DIR/coverage_base.info |
|||
|
|||
echo Running testeth... |
|||
$CPP_ETHEREUM_PATH/build/test/testeth $TEST_MODE |
|||
$CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit $TEST_MODE |
|||
$CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit $TEST_MODE |
|||
|
|||
echo Prepearing coverage info... |
|||
lcov --capture --directory $BUILD_DIR --output-file $OUTPUT_DIR/coverage_test.info |
|||
lcov --add-tracefile $OUTPUT_DIR/coverage_base.info --add-tracefile $OUTPUT_DIR/coverage_test.info --output-file $OUTPUT_DIR/coverage_all.info |
|||
lcov --extract $OUTPUT_DIR/coverage_all.info *cpp-ethereum/* --output-file $OUTPUT_DIR/coverage_export.info |
|||
genhtml $OUTPUT_DIR/coverage_export.info --output-directory $OUTPUT_DIR/testeth |
|||
else |
|||
echo genhtml not found |
|||
exit; |
|||
fi |
|||
else |
|||
echo lcov not found |
|||
exit; |
|||
fi |
|||
|
|||
echo "Coverage info should be located at: $OUTPUT_DIR/testeth" |
|||
echo "Opening index..." |
|||
|
|||
xdg-open $OUTPUT_DIR/testeth/index.html & |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,36 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file DB.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#pragma warning(push) |
|||
#pragma warning(disable: 4100 4267) |
|||
#if ETH_ROCKSDB || !ETH_TRUE |
|||
#include <rocksdb/db.h> |
|||
#include <rocksdb/write_batch.h> |
|||
namespace ldb = rocksdb; |
|||
#else |
|||
#include <leveldb/db.h> |
|||
#include <leveldb/write_batch.h> |
|||
namespace ldb = leveldb; |
|||
#endif |
|||
#pragma warning(pop) |
|||
#define DEV_LDB 1 |
@ -0,0 +1,35 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Exceptions.h
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2016 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <libdevcore/Exceptions.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace crypto |
|||
{ |
|||
|
|||
/// Rare malfunction of cryptographic functions.
|
|||
DEV_SIMPLE_EXCEPTION(CryptoException); |
|||
|
|||
} |
|||
} |
@ -0,0 +1,800 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file BlockChainSync.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "BlockChainSync.h" |
|||
|
|||
#include <chrono> |
|||
#include <libdevcore/Common.h> |
|||
#include <libp2p/Host.h> |
|||
#include <libp2p/Session.h> |
|||
#include <libethcore/Exceptions.h> |
|||
#include <libethcore/Params.h> |
|||
#include "BlockChain.h" |
|||
#include "BlockQueue.h" |
|||
#include "EthereumPeer.h" |
|||
#include "EthereumHost.h" |
|||
#include "DownloadMan.h" |
|||
|
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
using namespace p2p; |
|||
|
|||
unsigned const c_chainReorgSize = 30000; |
|||
|
|||
BlockChainSync::BlockChainSync(EthereumHost& _host): |
|||
m_host(_host) |
|||
{ |
|||
m_bqRoomAvailable = host().bq().onRoomAvailable([this]() |
|||
{ |
|||
RecursiveGuard l(x_sync); |
|||
continueSync(); |
|||
}); |
|||
} |
|||
|
|||
BlockChainSync::~BlockChainSync() |
|||
{ |
|||
RecursiveGuard l(x_sync); |
|||
abortSync(); |
|||
} |
|||
|
|||
DownloadMan const& BlockChainSync::downloadMan() const |
|||
{ |
|||
return host().downloadMan(); |
|||
} |
|||
|
|||
DownloadMan& BlockChainSync::downloadMan() |
|||
{ |
|||
return host().downloadMan(); |
|||
} |
|||
|
|||
void BlockChainSync::abortSync() |
|||
{ |
|||
DEV_INVARIANT_CHECK; |
|||
host().foreachPeer([this](EthereumPeer* _p) { onPeerAborting(_p); return true; }); |
|||
downloadMan().resetToChain(h256s()); |
|||
DEV_INVARIANT_CHECK; |
|||
} |
|||
|
|||
void BlockChainSync::onPeerStatus(EthereumPeer* _peer) |
|||
{ |
|||
RecursiveGuard l(x_sync); |
|||
DEV_INVARIANT_CHECK; |
|||
if (_peer->m_genesisHash != host().chain().genesisHash()) |
|||
_peer->disable("Invalid genesis hash"); |
|||
else if (_peer->m_protocolVersion != host().protocolVersion() && _peer->m_protocolVersion != EthereumHost::c_oldProtocolVersion) |
|||
_peer->disable("Invalid protocol version."); |
|||
else if (_peer->m_networkId != host().networkId()) |
|||
_peer->disable("Invalid network identifier."); |
|||
else if (_peer->session()->info().clientVersion.find("/v0.7.0/") != string::npos) |
|||
_peer->disable("Blacklisted client version."); |
|||
else if (host().isBanned(_peer->session()->id())) |
|||
_peer->disable("Peer banned for previous bad behaviour."); |
|||
else |
|||
{ |
|||
unsigned hashes = estimatedHashes(); |
|||
_peer->m_expectedHashes = hashes; |
|||
onNewPeer(_peer); |
|||
} |
|||
DEV_INVARIANT_CHECK; |
|||
} |
|||
|
|||
unsigned BlockChainSync::estimatedHashes() const |
|||
{ |
|||
BlockInfo block = host().chain().info(); |
|||
time_t lastBlockTime = (block.hash() == host().chain().genesisHash()) ? 1428192000 : (time_t)block.timestamp; |
|||
time_t now = time(0); |
|||
unsigned blockCount = c_chainReorgSize; |
|||
if (lastBlockTime > now) |
|||
clog(NetWarn) << "Clock skew? Latest block is in the future"; |
|||
else |
|||
blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit; |
|||
clog(NetAllDetail) << "Estimated hashes: " << blockCount; |
|||
return blockCount; |
|||
} |
|||
|
|||
void BlockChainSync::requestBlocks(EthereumPeer* _peer) |
|||
{ |
|||
if (host().bq().knownFull()) |
|||
{ |
|||
clog(NetAllDetail) << "Waiting for block queue before downloading blocks"; |
|||
pauseSync(); |
|||
_peer->setIdle(); |
|||
return; |
|||
} |
|||
_peer->requestBlocks(); |
|||
if (_peer->m_asking != Asking::Blocks) //nothing to download
|
|||
{ |
|||
peerDoneBlocks(_peer); |
|||
if (downloadMan().isComplete()) |
|||
completeSync(); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
void BlockChainSync::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) |
|||
{ |
|||
RecursiveGuard l(x_sync); |
|||
DEV_INVARIANT_CHECK; |
|||
unsigned itemCount = _r.itemCount(); |
|||
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks"); |
|||
|
|||
_peer->setIdle(); |
|||
if (m_state != SyncState::Blocks && m_state != SyncState::NewBlocks) |
|||
clog(NetWarn) << "Unexpected Blocks received!"; |
|||
if (m_state == SyncState::Waiting) |
|||
{ |
|||
clog(NetAllDetail) << "Ignored blocks while waiting"; |
|||
return; |
|||
} |
|||
|
|||
if (itemCount == 0) |
|||
{ |
|||
// Got to this peer's latest block - just give up.
|
|||
peerDoneBlocks(_peer); |
|||
if (downloadMan().isComplete()) |
|||
completeSync(); |
|||
return; |
|||
} |
|||
|
|||
unsigned success = 0; |
|||
unsigned future = 0; |
|||
unsigned unknown = 0; |
|||
unsigned got = 0; |
|||
unsigned repeated = 0; |
|||
u256 maxUnknownNumber = 0; |
|||
h256 maxUnknown; |
|||
|
|||
for (unsigned i = 0; i < itemCount; ++i) |
|||
{ |
|||
auto h = BlockInfo::headerHash(_r[i].data()); |
|||
if (_peer->m_sub.noteBlock(h)) |
|||
{ |
|||
_peer->addRating(10); |
|||
switch (host().bq().import(_r[i].data(), host().chain())) |
|||
{ |
|||
case ImportResult::Success: |
|||
success++; |
|||
break; |
|||
|
|||
case ImportResult::Malformed: |
|||
case ImportResult::BadChain: |
|||
_peer->disable("Malformed block received."); |
|||
return; |
|||
|
|||
case ImportResult::FutureTimeKnown: |
|||
future++; |
|||
break; |
|||
case ImportResult::AlreadyInChain: |
|||
case ImportResult::AlreadyKnown: |
|||
got++; |
|||
break; |
|||
|
|||
case ImportResult::FutureTimeUnknown: |
|||
future++; //Fall through
|
|||
|
|||
case ImportResult::UnknownParent: |
|||
{ |
|||
unknown++; |
|||
if (m_state == SyncState::NewBlocks) |
|||
{ |
|||
BlockInfo bi; |
|||
bi.populateFromHeader(_r[i][0]); |
|||
if (bi.number > maxUnknownNumber) |
|||
{ |
|||
maxUnknownNumber = bi.number; |
|||
maxUnknown = h; |
|||
} |
|||
} |
|||
break; |
|||
} |
|||
|
|||
default:; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
_peer->addRating(0); // -1?
|
|||
repeated++; |
|||
} |
|||
} |
|||
|
|||
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received."; |
|||
|
|||
if (host().bq().unknownFull()) |
|||
{ |
|||
clog(NetWarn) << "Too many unknown blocks, restarting sync"; |
|||
restartSync(); |
|||
return; |
|||
} |
|||
|
|||
if (m_state == SyncState::NewBlocks && unknown > 0) |
|||
{ |
|||
completeSync(); |
|||
resetSyncFor(_peer, maxUnknown, std::numeric_limits<u256>::max()); //TODO: proper total difficuty
|
|||
} |
|||
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks) |
|||
{ |
|||
if (downloadMan().isComplete()) |
|||
completeSync(); |
|||
else |
|||
requestBlocks(_peer); // Some of the blocks might have been downloaded by helping peers, proceed anyway
|
|||
} |
|||
DEV_INVARIANT_CHECK; |
|||
} |
|||
|
|||
void BlockChainSync::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) |
|||
{ |
|||
DEV_INVARIANT_CHECK; |
|||
RecursiveGuard l(x_sync); |
|||
auto h = BlockInfo::headerHash(_r[0].data()); |
|||
clog(NetMessageSummary) << "NewBlock: " << h; |
|||
|
|||
if (_r.itemCount() != 2) |
|||
_peer->disable("NewBlock without 2 data fields."); |
|||
else |
|||
{ |
|||
switch (host().bq().import(_r[0].data(), host().chain())) |
|||
{ |
|||
case ImportResult::Success: |
|||
_peer->addRating(100); |
|||
break; |
|||
case ImportResult::FutureTimeKnown: |
|||
//TODO: Rating dependent on how far in future it is.
|
|||
break; |
|||
|
|||
case ImportResult::Malformed: |
|||
case ImportResult::BadChain: |
|||
_peer->disable("Malformed block received."); |
|||
return; |
|||
|
|||
case ImportResult::AlreadyInChain: |
|||
case ImportResult::AlreadyKnown: |
|||
break; |
|||
|
|||
case ImportResult::FutureTimeUnknown: |
|||
case ImportResult::UnknownParent: |
|||
clog(NetMessageSummary) << "Received block with no known parent. Resyncing..."; |
|||
resetSyncFor(_peer, h, _r[1].toInt<u256>()); |
|||
break; |
|||
default:; |
|||
} |
|||
|
|||
DEV_GUARDED(_peer->x_knownBlocks) |
|||
_peer->m_knownBlocks.insert(h); |
|||
} |
|||
DEV_INVARIANT_CHECK; |
|||
} |
|||
|
|||
PV60Sync::PV60Sync(EthereumHost& _host): |
|||
BlockChainSync(_host) |
|||
{ |
|||
resetSync(); |
|||
} |
|||
|
|||
SyncStatus PV60Sync::status() const |
|||
{ |
|||
RecursiveGuard l(x_sync); |
|||
SyncStatus res; |
|||
res.state = m_state; |
|||
if (m_state == SyncState::Hashes) |
|||
{ |
|||
res.hashesTotal = m_estimatedHashes; |
|||
res.hashesReceived = static_cast<unsigned>(m_syncingNeededBlocks.size()); |
|||
res.hashesEstimated = true; |
|||
} |
|||
else if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks || m_state == SyncState::Waiting) |
|||
{ |
|||
res.blocksTotal = downloadMan().chainSize(); |
|||
res.blocksReceived = downloadMan().blocksGot().size(); |
|||
} |
|||
return res; |
|||
} |
|||
|
|||
void PV60Sync::setState(EthereumPeer* _peer, SyncState _s, bool _isSyncing, bool _needHelp) |
|||
{ |
|||
bool changedState = (m_state != _s); |
|||
m_state = _s; |
|||
|
|||
if (_isSyncing != (m_syncer == _peer) || (_isSyncing && changedState)) |
|||
changeSyncer(_isSyncing ? _peer : nullptr, _needHelp); |
|||
else if (_s == SyncState::Idle) |
|||
changeSyncer(nullptr, _needHelp); |
|||
|
|||
assert(!!m_syncer || _s == SyncState::Idle); |
|||
} |
|||
|
|||
void PV60Sync::resetSync() |
|||
{ |
|||
m_syncingLatestHash = h256(); |
|||
m_syncingLastReceivedHash = h256(); |
|||
m_syncingTotalDifficulty = 0; |
|||
m_syncingNeededBlocks.clear(); |
|||
} |
|||
|
|||
void PV60Sync::restartSync() |
|||
{ |
|||
resetSync(); |
|||
host().bq().clear(); |
|||
if (isSyncing()) |
|||
transition(m_syncer, SyncState::Idle); |
|||
} |
|||
|
|||
void PV60Sync::completeSync() |
|||
{ |
|||
if (isSyncing()) |
|||
transition(m_syncer, SyncState::Idle); |
|||
} |
|||
|
|||
void PV60Sync::pauseSync() |
|||
{ |
|||
if (isSyncing()) |
|||
setState(m_syncer, SyncState::Waiting, true); |
|||
} |
|||
|
|||
void PV60Sync::continueSync() |
|||
{ |
|||
transition(m_syncer, SyncState::Blocks); |
|||
} |
|||
|
|||
void PV60Sync::onNewPeer(EthereumPeer* _peer) |
|||
{ |
|||
setNeedsSyncing(_peer, _peer->m_latestHash, _peer->m_totalDifficulty); |
|||
} |
|||
|
|||
void PV60Sync::transition(EthereumPeer* _peer, SyncState _s, bool _force, bool _needHelp) |
|||
{ |
|||
clog(NetMessageSummary) << "Transition!" << EthereumHost::stateName(_s) << "from" << EthereumHost::stateName(m_state) << ", " << (isSyncing(_peer) ? "syncing" : "holding") << (needsSyncing(_peer) ? "& needed" : ""); |
|||
|
|||
if (m_state == SyncState::Idle && _s != SyncState::Idle) |
|||
_peer->m_requireTransactions = true; |
|||
|
|||
RLPStream s; |
|||
if (_s == SyncState::Hashes) |
|||
{ |
|||
if (m_state == SyncState::Idle) |
|||
{ |
|||
if (isSyncing(_peer)) |
|||
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!"; |
|||
|
|||
m_syncingLatestHash = _peer->m_latestHash; |
|||
m_syncingTotalDifficulty = _peer->m_totalDifficulty; |
|||
setState(_peer, _s, true); |
|||
_peer->requestHashes(m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash); |
|||
DEV_INVARIANT_CHECK; |
|||
return; |
|||
} |
|||
else if (m_state == SyncState::Hashes) |
|||
{ |
|||
if (!isSyncing(_peer)) |
|||
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!"; |
|||
|
|||
setState(_peer, _s, true); |
|||
_peer->requestHashes(m_syncingLastReceivedHash); |
|||
DEV_INVARIANT_CHECK; |
|||
return; |
|||
} |
|||
} |
|||
else if (_s == SyncState::Blocks) |
|||
{ |
|||
if (m_state == SyncState::Hashes) |
|||
{ |
|||
if (!isSyncing(_peer)) |
|||
{ |
|||
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!"; |
|||
return; |
|||
} |
|||
if (shouldGrabBlocks(_peer)) |
|||
{ |
|||
clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash << ", was" << host().latestBlockSent() << "]"; |
|||
downloadMan().resetToChain(m_syncingNeededBlocks); |
|||
resetSync(); |
|||
} |
|||
else |
|||
{ |
|||
clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring."; |
|||
resetSync(); |
|||
setState(_peer, SyncState::Idle, false); |
|||
return; |
|||
} |
|||
assert (isSyncing(_peer)); |
|||
} |
|||
// run through into...
|
|||
if (m_state == SyncState::Idle || m_state == SyncState::Hashes || m_state == SyncState::Blocks || m_state == SyncState::Waiting) |
|||
{ |
|||
// Looks like it's the best yet for total difficulty. Set to download.
|
|||
setState(_peer, SyncState::Blocks, isSyncing(_peer), _needHelp); // will kick off other peers to help if available.
|
|||
requestBlocks(_peer); |
|||
DEV_INVARIANT_CHECK; |
|||
return; |
|||
} |
|||
} |
|||
else if (_s == SyncState::NewBlocks) |
|||
{ |
|||
if (m_state != SyncState::Idle && m_state != SyncState::NewBlocks && m_state != SyncState::Waiting) |
|||
clog(NetWarn) << "Bad state: Asking new blocks while syncing!"; |
|||
else |
|||
{ |
|||
setState(_peer, SyncState::NewBlocks, true, _needHelp); |
|||
requestBlocks(_peer); |
|||
DEV_INVARIANT_CHECK; |
|||
return; |
|||
} |
|||
} |
|||
else if (_s == SyncState::Waiting) |
|||
{ |
|||
if (m_state != SyncState::Blocks && m_state != SyncState::NewBlocks && m_state != SyncState::Hashes && m_state != SyncState::Waiting) |
|||
clog(NetWarn) << "Bad state: Entering waiting state while not downloading blocks!"; |
|||
else |
|||
{ |
|||
setState(_peer, SyncState::Waiting, isSyncing(_peer), _needHelp); |
|||
return; |
|||
} |
|||
} |
|||
else if (_s == SyncState::Idle) |
|||
{ |
|||
host().foreachPeer([this](EthereumPeer* _p) { _p->setIdle(); return true; }); |
|||
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks) |
|||
{ |
|||
clog(NetNote) << "Finishing blocks fetch..."; |
|||
|
|||
// a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry.
|
|||
if (isSyncing(_peer)) |
|||
noteDoneBlocks(_peer, _force); |
|||
|
|||
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
|
|||
_peer->m_sub.doneFetch(); |
|||
_peer->setIdle(); |
|||
setState(_peer, SyncState::Idle, false); |
|||
} |
|||
else if (m_state == SyncState::Hashes) |
|||
{ |
|||
clog(NetNote) << "Finishing hashes fetch..."; |
|||
setState(_peer, SyncState::Idle, false); |
|||
} |
|||
// Otherwise it's fine. We don't care if it's Nothing->Nothing.
|
|||
DEV_INVARIANT_CHECK; |
|||
return; |
|||
} |
|||
|
|||
clog(NetWarn) << "Invalid state transition:" << EthereumHost::stateName(_s) << "from" << EthereumHost::stateName(m_state) << ", " << (isSyncing(_peer) ? "syncing" : "holding") << (needsSyncing(_peer) ? "& needed" : ""); |
|||
} |
|||
|
|||
void PV60Sync::resetSyncFor(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td) |
|||
{ |
|||
setNeedsSyncing(_peer, _latestHash, _td); |
|||
} |
|||
|
|||
void PV60Sync::setNeedsSyncing(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td) |
|||
{ |
|||
_peer->m_latestHash = _latestHash; |
|||
_peer->m_totalDifficulty = _td; |
|||
|
|||
if (_peer->m_latestHash) |
|||
noteNeedsSyncing(_peer); |
|||
|
|||
_peer->session()->addNote("sync", string(isSyncing(_peer) ? "ongoing" : "holding") + (needsSyncing(_peer) ? " & needed" : "")); |
|||
} |
|||
|
|||
bool PV60Sync::needsSyncing(EthereumPeer* _peer) const |
|||
{ |
|||
return !!_peer->m_latestHash; |
|||
} |
|||
|
|||
bool PV60Sync::isSyncing(EthereumPeer* _peer) const |
|||
{ |
|||
return m_syncer == _peer; |
|||
} |
|||
|
|||
bool PV60Sync::shouldGrabBlocks(EthereumPeer* _peer) const |
|||
{ |
|||
auto td = _peer->m_totalDifficulty; |
|||
auto lh = _peer->m_latestHash; |
|||
auto ctd = host().chain().details().totalDifficulty; |
|||
|
|||
if (m_syncingNeededBlocks.empty()) |
|||
return false; |
|||
|
|||
clog(NetNote) << "Should grab blocks? " << td << "vs" << ctd << ";" << m_syncingNeededBlocks.size() << " blocks, ends" << m_syncingNeededBlocks.back(); |
|||
|
|||
if (td < ctd || (td == ctd && host().chain().currentHash() == lh)) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void PV60Sync::attemptSync(EthereumPeer* _peer) |
|||
{ |
|||
if (m_state != SyncState::Idle) |
|||
{ |
|||
clog(NetAllDetail) << "Can't sync with this peer - outstanding asks."; |
|||
return; |
|||
} |
|||
|
|||
// if already done this, then ignore.
|
|||
if (!needsSyncing(_peer)) |
|||
{ |
|||
clog(NetAllDetail) << "Already synced with this peer."; |
|||
return; |
|||
} |
|||
|
|||
unsigned n = host().chain().number(); |
|||
u256 td = host().chain().details().totalDifficulty; |
|||
if (host().bq().isActive()) |
|||
td += host().bq().difficulty(); |
|||
|
|||
clog(NetAllDetail) << "Attempt chain-grab? Latest:" << (m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash) << ", number:" << n << ", TD:" << td << " versus " << _peer->m_totalDifficulty; |
|||
if (td >= _peer->m_totalDifficulty) |
|||
{ |
|||
clog(NetAllDetail) << "No. Our chain is better."; |
|||
resetNeedsSyncing(_peer); |
|||
transition(_peer, SyncState::Idle); |
|||
} |
|||
else |
|||
{ |
|||
clog(NetAllDetail) << "Yes. Their chain is better."; |
|||
m_estimatedHashes = _peer->m_expectedHashes - c_chainReorgSize; |
|||
transition(_peer, SyncState::Hashes); |
|||
} |
|||
} |
|||
|
|||
void PV60Sync::noteNeedsSyncing(EthereumPeer* _peer) |
|||
{ |
|||
// if already downloading hash-chain, ignore.
|
|||
if (isSyncing()) |
|||
{ |
|||
clog(NetAllDetail) << "Sync in progress: Just set to help out."; |
|||
if (m_state == SyncState::Blocks) |
|||
requestBlocks(_peer); |
|||
} |
|||
else |
|||
// otherwise check to see if we should be downloading...
|
|||
attemptSync(_peer); |
|||
} |
|||
|
|||
void PV60Sync::changeSyncer(EthereumPeer* _syncer, bool _needHelp) |
|||
{ |
|||
if (_syncer) |
|||
clog(NetAllDetail) << "Changing syncer to" << _syncer->session()->socketId(); |
|||
else |
|||
clog(NetAllDetail) << "Clearing syncer."; |
|||
|
|||
m_syncer = _syncer; |
|||
if (isSyncing()) |
|||
{ |
|||
if (_needHelp && (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)) |
|||
host().foreachPeer([&](EthereumPeer* _p) |
|||
{ |
|||
clog(NetNote) << "Getting help with downloading blocks"; |
|||
if (_p != _syncer && _p->m_asking == Asking::Nothing) |
|||
transition(_p, m_state); |
|||
return true; |
|||
}); |
|||
} |
|||
else |
|||
{ |
|||
// start grabbing next hash chain if there is one.
|
|||
host().foreachPeer([this](EthereumPeer* _p) |
|||
{ |
|||
attemptSync(_p); |
|||
return !isSyncing(); |
|||
}); |
|||
if (!isSyncing()) |
|||
{ |
|||
if (m_state != SyncState::Idle) |
|||
setState(_syncer, SyncState::Idle); |
|||
clog(NetNote) << "No more peers to sync with."; |
|||
} |
|||
} |
|||
assert(!!m_syncer || m_state == SyncState::Idle); |
|||
} |
|||
|
|||
void PV60Sync::peerDoneBlocks(EthereumPeer* _peer) |
|||
{ |
|||
noteDoneBlocks(_peer, false); |
|||
} |
|||
|
|||
void PV60Sync::noteDoneBlocks(EthereumPeer* _peer, bool _clemency) |
|||
{ |
|||
resetNeedsSyncing(_peer); |
|||
if (downloadMan().isComplete()) |
|||
{ |
|||
// Done our chain-get.
|
|||
clog(NetNote) << "Chain download complete."; |
|||
// 1/100th for each useful block hash.
|
|||
_peer->addRating(downloadMan().chainSize() / 100); |
|||
downloadMan().reset(); |
|||
} |
|||
else if (isSyncing(_peer)) |
|||
{ |
|||
if (_clemency) |
|||
clog(NetNote) << "Chain download failed. Aborted while incomplete."; |
|||
else |
|||
{ |
|||
// Done our chain-get.
|
|||
clog(NetWarn) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished."; |
|||
clog(NetWarn) << downloadMan().remaining(); |
|||
clog(NetWarn) << "WOULD BAN."; |
|||
// m_banned.insert(_peer->session()->id()); // We know who you are!
|
|||
// _peer->disable("Peer sent hashes but was unable to provide the blocks.");
|
|||
} |
|||
resetSync(); |
|||
downloadMan().reset(); |
|||
transition(_peer, SyncState::Idle); |
|||
} |
|||
_peer->m_sub.doneFetch(); |
|||
} |
|||
|
|||
void PV60Sync::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) |
|||
{ |
|||
RecursiveGuard l(x_sync); |
|||
DEV_INVARIANT_CHECK; |
|||
_peer->setIdle(); |
|||
if (!isSyncing(_peer)) |
|||
{ |
|||
clog(NetMessageSummary) << "Ignoring hashes since not syncing"; |
|||
return; |
|||
} |
|||
if (_hashes.size() == 0) |
|||
{ |
|||
transition(_peer, SyncState::Blocks); |
|||
return; |
|||
} |
|||
unsigned knowns = 0; |
|||
unsigned unknowns = 0; |
|||
for (unsigned i = 0; i < _hashes.size(); ++i) |
|||
{ |
|||
auto h = _hashes[i]; |
|||
auto status = host().bq().blockStatus(h); |
|||
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host().chain().isKnown(h)) |
|||
{ |
|||
clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download..."; |
|||
assert (isSyncing(_peer)); |
|||
transition(_peer, SyncState::Blocks); |
|||
return; |
|||
} |
|||
else if (status == QueueStatus::Bad) |
|||
{ |
|||
cwarn << "block hash bad!" << h << ". Bailing..."; |
|||
transition(_peer, SyncState::Idle); |
|||
return; |
|||
} |
|||
else if (status == QueueStatus::Unknown) |
|||
{ |
|||
unknowns++; |
|||
m_syncingNeededBlocks.push_back(h); |
|||
} |
|||
else |
|||
knowns++; |
|||
m_syncingLastReceivedHash = h; |
|||
} |
|||
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLastReceivedHash; |
|||
if (m_syncingNeededBlocks.size() > _peer->m_expectedHashes) |
|||
{ |
|||
_peer->disable("Too many hashes"); |
|||
restartSync(); |
|||
return; |
|||
} |
|||
// run through - ask for more.
|
|||
transition(_peer, SyncState::Hashes); |
|||
DEV_INVARIANT_CHECK; |
|||
} |
|||
|
|||
void PV60Sync::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) |
|||
{ |
|||
RecursiveGuard l(x_sync); |
|||
DEV_INVARIANT_CHECK; |
|||
if (isSyncing()) |
|||
{ |
|||
clog(NetMessageSummary) << "Ignoring since we're already downloading."; |
|||
return; |
|||
} |
|||
clog(NetMessageDetail) << "Not syncing and new block hash discovered: syncing without help."; |
|||
unsigned knowns = 0; |
|||
unsigned unknowns = 0; |
|||
for (auto const& h: _hashes) |
|||
{ |
|||
_peer->addRating(1); |
|||
DEV_GUARDED(_peer->x_knownBlocks) |
|||
_peer->m_knownBlocks.insert(h); |
|||
auto status = host().bq().blockStatus(h); |
|||
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host().chain().isKnown(h)) |
|||
knowns++; |
|||
else if (status == QueueStatus::Bad) |
|||
{ |
|||
cwarn << "block hash bad!" << h << ". Bailing..."; |
|||
return; |
|||
} |
|||
else if (status == QueueStatus::Unknown) |
|||
{ |
|||
unknowns++; |
|||
m_syncingNeededBlocks.push_back(h); |
|||
} |
|||
else |
|||
knowns++; |
|||
} |
|||
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns"; |
|||
if (unknowns > 0) |
|||
{ |
|||
clog(NetNote) << "Not syncing and new block hash discovered: syncing without help."; |
|||
downloadMan().resetToChain(m_syncingNeededBlocks); |
|||
resetSync(); |
|||
transition(_peer, SyncState::NewBlocks, false, false); |
|||
} |
|||
DEV_INVARIANT_CHECK; |
|||
} |
|||
|
|||
void PV60Sync::abortSync(EthereumPeer* _peer) |
|||
{ |
|||
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
|
|||
if (isSyncing(_peer)) |
|||
{ |
|||
host().foreachPeer([this](EthereumPeer* _p) { _p->setIdle(); return true; }); |
|||
transition(_peer, SyncState::Idle, true); |
|||
} |
|||
DEV_INVARIANT_CHECK; |
|||
} |
|||
|
|||
void PV60Sync::onPeerAborting(EthereumPeer* _peer) |
|||
{ |
|||
RecursiveGuard l(x_sync); |
|||
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
|
|||
abortSync(_peer); |
|||
DEV_INVARIANT_CHECK; |
|||
} |
|||
|
|||
bool PV60Sync::invariants() const |
|||
{ |
|||
if (m_state == SyncState::Idle && !!m_syncer) |
|||
return false; |
|||
if (m_state != SyncState::Idle && !m_syncer) |
|||
return false; |
|||
if (m_state == SyncState::Hashes) |
|||
{ |
|||
bool hashes = false; |
|||
host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking == Asking::Hashes) hashes = true; return !hashes; }); |
|||
if (!hashes) |
|||
return false; |
|||
if (!m_syncingLatestHash) |
|||
return false; |
|||
if (m_syncingNeededBlocks.empty() != (!m_syncingLastReceivedHash)) |
|||
return false; |
|||
} |
|||
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks) |
|||
{ |
|||
bool blocks = false; |
|||
host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking == Asking::Blocks) blocks = true; return !blocks; }); |
|||
if (!blocks) |
|||
return false; |
|||
if (downloadMan().isComplete()) |
|||
return false; |
|||
} |
|||
if (m_state == SyncState::Idle) |
|||
{ |
|||
bool busy = false; |
|||
host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking != Asking::Nothing && _p->m_asking != Asking::State) busy = true; return !busy; }); |
|||
if (busy) |
|||
return false; |
|||
} |
|||
if (m_state == SyncState::Waiting && !host().bq().isActive()) |
|||
return false; |
|||
return true; |
|||
} |
@ -0,0 +1,278 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file BlockChainSync.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <mutex> |
|||
|
|||
#include <libdevcore/Guards.h> |
|||
#include <libdevcore/RangeMask.h> |
|||
#include <libethcore/Common.h> |
|||
#include <libp2p/Common.h> |
|||
#include "CommonNet.h" |
|||
#include "DownloadMan.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
class RLPStream; |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
class EthereumHost; |
|||
class BlockQueue; |
|||
class EthereumPeer; |
|||
|
|||
/**
|
|||
* @brief Base BlockChain synchronization strategy class. |
|||
* Syncs to peers and keeps up to date. Base class handles blocks downloading but does not contain any details on state transfer logic. |
|||
*/ |
|||
class BlockChainSync: public HasInvariants |
|||
{ |
|||
public: |
|||
BlockChainSync(EthereumHost& _host); |
|||
virtual ~BlockChainSync(); |
|||
void abortSync(); ///< Abort all sync activity
|
|||
|
|||
DownloadMan const& downloadMan() const; |
|||
DownloadMan& downloadMan(); |
|||
|
|||
/// @returns true is Sync is in progress
|
|||
virtual bool isSyncing() const = 0; |
|||
|
|||
/// Called by peer to report status
|
|||
virtual void onPeerStatus(EthereumPeer* _peer); |
|||
|
|||
/// Called by peer once it has new blocks during syn
|
|||
virtual void onPeerBlocks(EthereumPeer* _peer, RLP const& _r); |
|||
|
|||
/// Called by peer once it has new blocks
|
|||
virtual void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r); |
|||
|
|||
/// Called by peer once it has new hashes
|
|||
virtual void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) = 0; |
|||
|
|||
/// Called by peer once it has another sequential block of hashes during sync
|
|||
virtual void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) = 0; |
|||
|
|||
/// Called by peer when it is disconnecting
|
|||
virtual void onPeerAborting(EthereumPeer* _peer) = 0; |
|||
|
|||
/// @returns Synchonization status
|
|||
virtual SyncStatus status() const = 0; |
|||
|
|||
static char const* stateName(SyncState _s) { return s_stateNames[static_cast<int>(_s)]; } |
|||
|
|||
protected: |
|||
//To be implemented in derived classes:
|
|||
/// New valid peer appears
|
|||
virtual void onNewPeer(EthereumPeer* _peer) = 0; |
|||
|
|||
/// Peer done downloading blocks
|
|||
virtual void peerDoneBlocks(EthereumPeer* _peer) = 0; |
|||
|
|||
/// Resume downloading after witing state
|
|||
virtual void continueSync() = 0; |
|||
|
|||
/// Restart sync
|
|||
virtual void restartSync() = 0; |
|||
|
|||
/// Called after all blocks have been donloaded
|
|||
virtual void completeSync() = 0; |
|||
|
|||
/// Enter waiting state
|
|||
virtual void pauseSync() = 0; |
|||
|
|||
/// Restart sync for given peer
|
|||
virtual void resetSyncFor(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td) = 0; |
|||
|
|||
EthereumHost& host() { return m_host; } |
|||
EthereumHost const& host() const { return m_host; } |
|||
|
|||
/// Estimates max number of hashes peers can give us.
|
|||
unsigned estimatedHashes() const; |
|||
|
|||
/// Request blocks from peer if needed
|
|||
void requestBlocks(EthereumPeer* _peer); |
|||
|
|||
protected: |
|||
Handler m_bqRoomAvailable; ///< Triggered once block queue
|
|||
mutable RecursiveMutex x_sync; |
|||
SyncState m_state = SyncState::Idle; ///< Current sync state
|
|||
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
|
|||
|
|||
private: |
|||
static char const* const s_stateNames[static_cast<int>(SyncState::Size)]; |
|||
bool invariants() const override = 0; |
|||
EthereumHost& m_host; |
|||
HashDownloadMan m_hashMan; |
|||
}; |
|||
|
|||
|
|||
/**
|
|||
* @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash downaload is complete |
|||
* Syncs to peers and keeps up to date |
|||
*/ |
|||
|
|||
/**
|
|||
* Transitions: |
|||
* |
|||
* Idle->Hashes |
|||
* Triggered when: |
|||
* * A new peer appears that we can sync to |
|||
* * Transtition to Idle, there are peers we can sync to |
|||
* Effects: |
|||
* * Set chain sync (m_syncingTotalDifficulty, m_syncingLatestHash, m_syncer) |
|||
* * Requests hashes from m_syncer |
|||
* |
|||
* Hashes->Idle |
|||
* Triggered when: |
|||
* * Received too many hashes |
|||
* * Received 0 total hashes from m_syncer |
|||
* * m_syncer aborts |
|||
* Effects: |
|||
* In case of too many hashes sync is reset |
|||
* |
|||
* Hashes->Blocks |
|||
* Triggered when: |
|||
* * Received known hash from m_syncer |
|||
* * Received 0 hashes from m_syncer and m_syncingTotalBlocks not empty |
|||
* Effects: |
|||
* * Set up download manager, clear m_syncingTotalBlocks. Set all peers to help with downloading if they can |
|||
* |
|||
* Blocks->Idle |
|||
* Triggered when: |
|||
* * m_syncer aborts |
|||
* * m_syncer does not have required block |
|||
* * All blocks downloaded |
|||
* * Block qeueue is full with unknown blocks |
|||
* Effects: |
|||
* * Download manager is reset |
|||
* |
|||
* Blocks->Waiting |
|||
* Triggered when: |
|||
* * Block queue is full with known blocks |
|||
* Effects: |
|||
* * Stop requesting blocks from peers |
|||
* |
|||
* Waiting->Blocks |
|||
* Triggered when: |
|||
* * Block queue has space for new blocks |
|||
* Effects: |
|||
* * Continue requesting blocks from peers |
|||
* |
|||
* Idle->NewBlocks |
|||
* Triggered when: |
|||
* * New block hashes arrive |
|||
* Effects: |
|||
* * Set up download manager, clear m_syncingTotalBlocks. Download blocks from a single peer. If downloaded blocks have unknown parents, set the peer to sync |
|||
* |
|||
* NewBlocks->Idle |
|||
* Triggered when: |
|||
* * m_syncer aborts |
|||
* * m_syncer does not have required block |
|||
* * All new blocks downloaded |
|||
* * Block qeueue is full with unknown blocks |
|||
* Effects: |
|||
* * Download manager is reset |
|||
* |
|||
*/ |
|||
class PV60Sync: public BlockChainSync |
|||
{ |
|||
public: |
|||
PV60Sync(EthereumHost& _host); |
|||
|
|||
/// @returns true is Sync is in progress
|
|||
bool isSyncing() const override { return !!m_syncer; } |
|||
|
|||
/// Called by peer once it has new hashes
|
|||
void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) override; |
|||
|
|||
/// Called by peer once it has another sequential block of hashes during sync
|
|||
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) override; |
|||
|
|||
/// Called by peer when it is disconnecting
|
|||
void onPeerAborting(EthereumPeer* _peer) override; |
|||
|
|||
/// @returns Sync status
|
|||
SyncStatus status() const override; |
|||
|
|||
protected: |
|||
void onNewPeer(EthereumPeer* _peer) override; |
|||
void continueSync() override; |
|||
void peerDoneBlocks(EthereumPeer* _peer) override; |
|||
void restartSync() override; |
|||
void completeSync() override; |
|||
void pauseSync() override; |
|||
void resetSyncFor(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td) override; |
|||
|
|||
private: |
|||
/// Transition sync state in a particular direction. @param _peer Peer that is responsible for state tranfer
|
|||
void transition(EthereumPeer* _peer, SyncState _s, bool _force = false, bool _needHelp = true); |
|||
|
|||
/// Reset peer syncing requirements state.
|
|||
void resetNeedsSyncing(EthereumPeer* _peer) { setNeedsSyncing(_peer, h256(), 0); } |
|||
|
|||
/// Update peer syncing requirements state.
|
|||
void setNeedsSyncing(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td); |
|||
|
|||
/// Do we presently need syncing with this peer?
|
|||
bool needsSyncing(EthereumPeer* _peer) const; |
|||
|
|||
/// Check whether the session should bother grabbing blocks from a peer.
|
|||
bool shouldGrabBlocks(EthereumPeer* _peer) const; |
|||
|
|||
/// Attempt to begin syncing with the peer; first check the peer has a more difficlult chain to download, then start asking for hashes, then move to blocks
|
|||
void attemptSync(EthereumPeer* _peer); |
|||
|
|||
/// Update our syncing state
|
|||
void setState(EthereumPeer* _peer, SyncState _s, bool _isSyncing = false, bool _needHelp = false); |
|||
|
|||
/// Check if peer is main syncer
|
|||
bool isSyncing(EthereumPeer* _peer) const; |
|||
|
|||
/// Check if we need (re-)syncing with the peer.
|
|||
void noteNeedsSyncing(EthereumPeer* _who); |
|||
|
|||
/// Set main syncing peer
|
|||
void changeSyncer(EthereumPeer* _syncer, bool _needHelp); |
|||
|
|||
/// Called when peer done downloading blocks
|
|||
void noteDoneBlocks(EthereumPeer* _who, bool _clemency); |
|||
|
|||
/// Abort syncing for peer
|
|||
void abortSync(EthereumPeer* _peer); |
|||
|
|||
/// Reset hash chain syncing
|
|||
void resetSync(); |
|||
|
|||
bool invariants() const override; |
|||
|
|||
h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer.
|
|||
h256 m_syncingLastReceivedHash; ///< Hash most recently received from peer.
|
|||
h256 m_syncingLatestHash; ///< Latest block's hash of the peer we are syncing to, as of the current sync.
|
|||
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty of the peer we aresyncing to, as of the current sync.
|
|||
// TODO: switch to weak_ptr
|
|||
EthereumPeer* m_syncer = nullptr; ///< Peer we are currently syncing with
|
|||
}; |
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue