25 #include <libp2p/Host.h> 26 #include <libp2p/Session.h> 34 static unsigned const c_maxSendTransactions = 256;
35 static unsigned const c_maxHeadersToSend = 1024;
36 static unsigned const c_maxIncomingNewHashes = 1024;
37 static int const c_backroundWorkPeriodMs = 1000;
38 static int const c_minBlockBroadcastPeers = 4;
40 char const*
const EthereumCapability::s_stateNames[static_cast<int>(SyncState::Size)] = {
41 "NotSynced",
"Idle",
"Waiting",
"Blocks",
"State"};
49 case Asking::BlockHeaders:
50 return "BlockHeaders";
51 case Asking::BlockBodies:
53 case Asking::NodeData:
55 case Asking::Receipts:
61 case Asking::WarpManifest:
62 return "WarpManifest";
63 case Asking::WarpData:
72 EthereumPeerObserver(shared_ptr<BlockChainSync> _sync,
TransactionQueue& _tq): m_sync(_sync), m_tq(_tq) {}
78 m_sync->onPeerStatus(_peer);
80 catch (FailedInvariant
const&)
83 cwarn <<
"Failed invariant during sync, restarting sync";
84 m_sync->restartSync();
88 void onPeerTransactions(
NodeID const& _peerID,
RLP const& _r)
override 91 LOG(m_logger) <<
"Transactions (" << dec << itemCount <<
" entries)";
92 m_tq.enqueue(_r, _peerID);
95 void onPeerAborting()
override 99 m_sync->onPeerAborting();
103 cwarn <<
"Exception on peer destruciton: " << boost::current_exception_diagnostic_information();
107 void onPeerBlockHeaders(
NodeID const& _peerID,
RLP const& _headers)
override 111 m_sync->onPeerBlockHeaders(_peerID, _headers);
113 catch (FailedInvariant
const&)
116 cwarn <<
"Failed invariant during sync, restarting sync";
117 m_sync->restartSync();
121 void onPeerBlockBodies(
NodeID const& _peerID,
RLP const& _r)
override 125 m_sync->onPeerBlockBodies(_peerID, _r);
127 catch (FailedInvariant
const&)
130 cwarn <<
"Failed invariant during sync, restarting sync";
131 m_sync->restartSync();
135 void onPeerNewHashes(
136 NodeID const& _peerID, std::vector<std::pair<h256, u256>>
const& _hashes)
override 140 m_sync->onPeerNewHashes(_peerID, _hashes);
142 catch (FailedInvariant
const&)
145 cwarn <<
"Failed invariant during sync, restarting sync";
146 m_sync->restartSync();
150 void onPeerNewBlock(
NodeID const& _peerID,
RLP const& _r)
override 154 m_sync->onPeerNewBlock(_peerID, _r);
156 catch (FailedInvariant
const&)
159 cwarn <<
"Failed invariant during sync, restarting sync";
160 m_sync->restartSync();
164 void onPeerNodeData(
NodeID const& ,
RLP const& _r)
override 167 LOG(m_logger) <<
"Node Data (" << dec << itemCount <<
" entries)";
170 void onPeerReceipts(
NodeID const& ,
RLP const& _r)
override 173 LOG(m_logger) <<
"Receipts (" << dec << itemCount <<
" entries)";
177 shared_ptr<BlockChainSync> m_sync;
186 EthereumHostData(
BlockChain const& _chain,
OverlayDB const& _db): m_chain(_chain), m_db(_db) {}
188 pair<bytes, unsigned> blockHeaders(
RLP const& _blockId,
unsigned _maxHeaders,
u256 _skip,
bool _reverse)
const override 190 auto numHeadersToSend = _maxHeaders;
192 auto step = static_cast<unsigned>(_skip) + 1;
193 assert(step > 0 &&
"step must not be 0");
196 if (_blockId.
size() == 32)
199 cnetlog <<
"GetBlockHeaders (block (hash): " << blockHash
200 <<
", maxHeaders: " << _maxHeaders <<
", skip: " << _skip
201 <<
", reverse: " << _reverse <<
")";
203 if (!m_chain.isKnown(blockHash))
207 auto n = m_chain.number(blockHash);
208 if (numHeadersToSend == 0)
210 else if (n != 0 || blockHash == m_chain.genesisHash())
212 auto top = n + uint64_t(step) * numHeadersToSend - 1;
213 auto lastBlock = m_chain.number();
216 numHeadersToSend = (lastBlock - n) / step + 1;
217 top = n + step * (numHeadersToSend - 1);
219 assert(top <= lastBlock &&
"invalid top block calculated");
220 blockHash = m_chain.numberHash(static_cast<unsigned>(top));
229 cnetlog <<
"GetBlockHeaders (" << n <<
" max: " << _maxHeaders <<
" skip: " << _skip
230 << (_reverse ?
" reverse" :
"") <<
")";
234 auto lastBlock = m_chain.number();
235 if (n > lastBlock || numHeadersToSend == 0)
239 bigint top = n + uint64_t(step) * (numHeadersToSend - 1);
242 numHeadersToSend = (lastBlock - static_cast<unsigned>(n)) / step + 1;
243 top = n + step * (numHeadersToSend - 1);
245 assert(top <= lastBlock &&
"invalid top block calculated");
246 blockHash = m_chain.numberHash(static_cast<unsigned>(top));
249 else if (n <= std::numeric_limits<unsigned>::max())
250 blockHash = m_chain.numberHash(static_cast<unsigned>(n));
255 auto nextHash = [
this](
h256 _h,
unsigned _step)
257 static const unsigned c_blockNumberUsageLimit = 1000;
259 const auto lastBlock = m_chain.number();
260 const auto limitBlock = lastBlock > c_blockNumberUsageLimit ? lastBlock - c_blockNumberUsageLimit : 0;
264 auto details = m_chain.details(_h);
265 if (details.number < limitBlock)
273 auto n = m_chain.number(_h);
275 _h = m_chain.numberHash(n - _step);
285 unsigned itemCount = 0;
287 for (
unsigned i = 0; i != numHeadersToSend; ++i)
289 if (!blockHash || !m_chain.isKnown(blockHash))
292 hashes.push_back(blockHash);
295 blockHash = nextHash(blockHash, step);
298 for (
unsigned i = 0; i < hashes.size() &&
rlp.size() < c_maxPayload; ++i)
299 rlp += m_chain.headerData(hashes[_reverse ? i : hashes.size() - 1 - i]);
301 return make_pair(
rlp, itemCount);
304 pair<bytes, unsigned> blockBodies(
RLP const& _blockHashes)
const override 306 unsigned const count = static_cast<unsigned>(_blockHashes.
itemCount());
310 auto numBodiesToSend = std::min(count, c_maxBlocks);
311 for (
unsigned i = 0; i < numBodiesToSend &&
rlp.size() < c_maxPayload; ++i)
314 if (m_chain.isKnown(h))
316 bytes blockBytes = m_chain.block(h);
317 RLP block{blockBytes};
322 auto bodyBytes = body.
out();
323 rlp.insert(
rlp.end(), bodyBytes.begin(), bodyBytes.end());
327 if (count > 20 && n == 0)
328 cnetlog <<
"all " << count <<
" unknown blocks requested; peer on different chain?";
330 cnetlog << n <<
" blocks known and returned; " << (numBodiesToSend - n)
331 <<
" blocks unknown; " << (count > c_maxBlocks ? count - c_maxBlocks : 0)
332 <<
" blocks ignored";
334 return make_pair(
rlp, n);
337 strings nodeData(
RLP const& _dataHashes)
const override 339 unsigned const count = static_cast<unsigned>(_dataHashes.
itemCount());
342 size_t payloadSize = 0;
343 auto numItemsToSend = std::min(count, c_maxNodes);
344 for (
unsigned i = 0; i < numItemsToSend && payloadSize < c_maxPayload; ++i)
347 auto node = m_db.lookup(h);
350 payloadSize += node.length();
351 data.push_back(move(node));
354 cnetlog << data.
size() <<
" nodes known and returned; " << (numItemsToSend - data.size())
355 <<
" unknown; " << (count > c_maxNodes ? count - c_maxNodes : 0) <<
" ignored";
360 pair<bytes, unsigned> receipts(
RLP const& _blockHashes)
const override 362 unsigned const count = static_cast<unsigned>(_blockHashes.
itemCount());
366 auto numItemsToSend = std::min(count, c_maxReceipts);
367 for (
unsigned i = 0; i < numItemsToSend &&
rlp.size() < c_maxPayload; ++i)
370 if (m_chain.isKnown(h))
372 auto const receipts = m_chain.receipts(h);
373 auto receiptsRlpList = receipts.rlp();
374 rlp.insert(
rlp.end(), receiptsRlpList.begin(), receiptsRlpList.end());
378 cnetlog << n <<
" receipt lists known and returned; " << (numItemsToSend - n)
379 <<
" unknown; " << (count > c_maxReceipts ? count - c_maxReceipts : 0)
382 return make_pair(
rlp, n);
392 EthereumCapability::EthereumCapability(shared_ptr<p2p::CapabilityHostFace> _host,
395 : m_host(move(_host)),
400 m_networkId(_networkId),
401 m_hostData(new EthereumHostData(m_chain, m_db))
406 m_peerObserver.reset(
new EthereumPeerObserver(m_sync, m_tq));
409 std::random_device seed;
410 m_urng = std::mt19937_64(seed());
415 m_backgroundWorkEnabled =
true;
416 m_host->scheduleExecution(c_backroundWorkPeriodMs, [
this]() { doBackgroundWork(); });
421 m_backgroundWorkEnabled =
false;
424 bool EthereumCapability::ensureInitialised()
426 if (!m_latestBlockSent)
430 LOG(m_logger) <<
"Initialising: latest=" << m_latestBlockSent;
444 m_host->scheduleExecution(0, [
this]() {
445 m_latestBlockSent =
h256();
446 m_transactionsSent.clear();
452 m_sync->completeSync();
455 void EthereumCapability::doBackgroundWork()
462 if (m_newTransactions)
464 m_newTransactions =
false;
465 maintainTransactions();
474 time_t now = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
475 if (now - m_lastTick >= 1)
478 for (
auto const&
peer : m_peers)
480 time_t now = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
484 m_host->disconnect(
peer.first, p2p::PingTimeout);
488 if (m_backgroundWorkEnabled)
489 m_host->scheduleExecution(c_backroundWorkPeriodMs, [
this]() { doBackgroundWork(); });
492 void EthereumCapability::maintainTransactions()
495 unordered_map<NodeID, std::vector<size_t>> peerTransactions;
498 for (
size_t i = 0; i < ts.size(); ++i)
500 auto const& t = ts[i];
501 bool unsent = !m_transactionsSent.count(t.sha3());
502 auto const peers = selectPeers([&](
EthereumPeer const& _peer) {
506 for (
auto const& p: peers)
507 peerTransactions[p].push_back(i);
509 for (
auto const& t: ts)
510 m_transactionsSent.insert(t.sha3());
513 for (
auto&
peer : m_peers)
517 for (
auto const& i : peerTransactions[
peer.first])
528 m_host->sealAndSend(
peer.first, ts);
529 LOG(m_logger) <<
"Sent " << n <<
" transactions to " <<
peer.first;
535 vector<NodeID> EthereumCapability::selectPeers(
536 std::function<
bool(
EthereumPeer const&)>
const& _predicate)
const 538 vector<NodeID> allowed;
539 for (
auto const&
peer : m_peers)
541 if (_predicate(
peer.second))
542 allowed.push_back(
peer.first);
547 std::pair<std::vector<NodeID>, std::vector<NodeID>> EthereumCapability::randomPartitionPeers(
548 std::vector<NodeID>
const& _peers, std::size_t _number)
const 550 vector<NodeID> part1(_peers);
551 vector<NodeID> part2;
553 if (_number >= _peers.size())
554 return std::make_pair(part1, part2);
556 std::shuffle(part1.begin(), part1.end(), m_urng);
559 std::move(part1.begin() + _number, part1.end(), std::back_inserter(part2));
560 part1.erase(part1.begin() + _number, part1.end());
561 return std::make_pair(move(part1), move(part2));
564 void EthereumCapability::maintainBlocks(
h256 const& _currentHash)
567 auto detailsFrom = m_chain.
details(m_latestBlockSent);
568 auto detailsTo = m_chain.
details(_currentHash);
569 if (detailsFrom.totalDifficulty < detailsTo.totalDifficulty)
571 if (
diff(detailsFrom.number, detailsTo.number) < 20)
574 LOG(m_logger) <<
"Sending new blocks (current is " << _currentHash <<
", was " 575 << m_latestBlockSent <<
")";
577 h256s blocks = get<0>(m_chain.
treeRoute(m_latestBlockSent, _currentHash,
false,
false,
true));
580 auto const peersWithoutBlock = selectPeers(
583 auto const peersToSendNumber =
584 std::max<std::size_t>(c_minBlockBroadcastPeers, std::sqrt(m_peers.size()));
586 std::vector<NodeID> peersToSend;
587 std::vector<NodeID> peersToAnnounce;
588 std::tie(peersToSend, peersToAnnounce) =
589 randomPartitionPeers(peersWithoutBlock, peersToSendNumber);
591 for (
NodeID const& peerID : peersToSend)
592 for (
auto const& b: blocks)
596 .appendRaw(m_chain.
block(b), 1)
599 auto itPeer = m_peers.find(peerID);
600 if (itPeer != m_peers.end())
602 m_host->sealAndSend(peerID, ts);
603 itPeer->second.clearKnownBlocks();
606 if (!peersToSend.empty())
607 LOG(m_logger) <<
"Sent " << blocks.size() <<
" block(s) to " << peersToSend.size()
610 for (
NodeID const& peerID : peersToAnnounce)
614 for (
auto const& b: blocks)
621 auto itPeer = m_peers.find(peerID);
622 if (itPeer != m_peers.end())
624 m_host->sealAndSend(peerID, ts);
625 itPeer->second.clearKnownBlocks();
628 if (!peersToAnnounce.empty())
629 LOG(m_logger) <<
"Announced " << blocks.size() <<
" block(s) to " 630 << peersToAnnounce.size() <<
" peers";
632 m_latestBlockSent = _currentHash;
638 return m_sync->isSyncing();
643 return m_sync->status();
646 void EthereumCapability::onTransactionImported(
649 m_host->scheduleExecution(0, [
this, _ir, _h, _nodeId]() {
650 auto itPeerStatus = m_peers.find(_nodeId);
651 if (itPeerStatus == m_peers.end())
654 auto&
peer = itPeerStatus->second;
660 m_host->updateRating(_nodeId, -100);
664 m_transactionsSent.insert(_h);
665 m_host->updateRating(_nodeId, 0);
668 m_host->updateRating(_nodeId, 100);
677 m_host->addNote(_peerID,
"manners", m_host->isRude(_peerID,
name()) ?
"RUDE" :
"nice");
680 m_peers.emplace(_peerID,
peer);
688 m_peerObserver->onPeerAborting();
690 m_peers.erase(_peerID);
694 NodeID const& _peerID,
unsigned _id,
RLP const& _r)
696 auto&
peer = m_peers[_peerID];
697 peer.
setLastAsk(std::chrono::system_clock::to_time_t(chrono::system_clock::now()));
705 auto const peerProtocolVersion = _r[0].
toInt<
unsigned>();
707 auto const totalDifficulty = _r[2].
toInt<
u256>();
709 auto const genesisHash = _r[4].
toHash<
h256>();
711 LOG(m_logger) <<
"Status: " << peerProtocolVersion <<
" / " <<
networkId <<
" / " 712 << genesisHash <<
", TD: " << totalDifficulty <<
" = " << latestHash;
715 peerProtocolVersion,
networkId, totalDifficulty, latestHash, genesisHash);
717 m_peerObserver->onPeerStatus(
peer);
722 m_peerObserver->onPeerTransactions(_peerID, _r);
729 const auto blockId = _r[0];
730 const auto maxHeaders = _r[1].
toInt<
u256>();
732 const auto reverse = _r[3].
toInt<
bool>();
734 auto numHeadersToSend = maxHeaders <= c_maxHeadersToSend ?
735 static_cast<unsigned>(maxHeaders) :
738 if (skip > std::numeric_limits<unsigned>::max() - 1)
740 cnetdetails <<
"Requested block skip is too big: " << skip;
744 pair<bytes, unsigned>
const rlpAndItemCount =
745 m_hostData->blockHeaders(blockId, numHeadersToSend, skip, reverse);
749 .appendRaw(rlpAndItemCount.first, rlpAndItemCount.second);
750 m_host->sealAndSend(_peerID, s);
751 m_host->updateRating(_peerID, 0);
757 LOG(m_loggerImpolite)
758 <<
"Peer giving us block headers when we didn't ask for them.";
762 m_peerObserver->onPeerBlockHeaders(_peerID, _r);
768 unsigned count = static_cast<unsigned>(_r.
itemCount());
769 cnetlog <<
"GetBlockBodies (" << dec << count <<
" entries)";
773 LOG(m_loggerImpolite) <<
"Zero-entry GetBlockBodies: Not replying.";
774 m_host->updateRating(_peerID, -10);
778 pair<bytes, unsigned>
const rlpAndItemCount = m_hostData->blockBodies(_r);
780 m_host->updateRating(_peerID, 0);
783 .appendRaw(rlpAndItemCount.first, rlpAndItemCount.second);
784 m_host->sealAndSend(_peerID, s);
790 LOG(m_loggerImpolite) <<
"Peer giving us block bodies when we didn't ask for them.";
794 m_peerObserver->onPeerBlockBodies(_peerID, _r);
800 m_peerObserver->onPeerNewBlock(_peerID, _r);
807 cnetlog <<
"BlockHashes (" << dec << itemCount <<
" entries) " 808 << (itemCount ?
"" :
" : NoMoreHashes");
810 if (itemCount > c_maxIncomingNewHashes)
816 vector<pair<h256, u256>> hashes(itemCount);
817 for (
unsigned i = 0; i < itemCount; ++i)
818 hashes[i] = std::make_pair(_r[i][0].toHash<h256>(), _r[i][1].toInt<u256>());
820 m_peerObserver->onPeerNewHashes(_peerID, hashes);
825 unsigned count = static_cast<unsigned>(_r.
itemCount());
828 LOG(m_loggerImpolite) <<
"Zero-entry GetNodeData: Not replying.";
829 m_host->updateRating(_peerID, -10);
832 cnetlog <<
"GetNodeData (" << dec << count <<
" entries)";
834 strings const data = m_hostData->nodeData(_r);
836 m_host->updateRating(_peerID, 0);
839 for (
auto const& element : data)
841 m_host->sealAndSend(_peerID, s);
846 unsigned count = static_cast<unsigned>(_r.
itemCount());
849 LOG(m_loggerImpolite) <<
"Zero-entry GetReceipts: Not replying.";
850 m_host->updateRating(_peerID, -10);
853 cnetlog <<
"GetReceipts (" << dec << count <<
" entries)";
855 pair<bytes, unsigned>
const rlpAndItemCount = m_hostData->receipts(_r);
857 m_host->updateRating(_peerID, 0);
860 .appendRaw(rlpAndItemCount.first, rlpAndItemCount.second);
861 m_host->sealAndSend(_peerID, s);
867 LOG(m_loggerImpolite) <<
"Peer giving us node data when we didn't ask for them.";
871 m_peerObserver->onPeerNodeData(_peerID, _r);
878 LOG(m_loggerImpolite) <<
"Peer giving us receipts when we didn't ask for them.";
882 m_peerObserver->onPeerReceipts(_peerID, _r);
892 cnetlog <<
"Peer causing an Exception: " 893 << boost::current_exception_diagnostic_information() <<
" " << _r;
895 catch (std::exception
const& _e)
897 cnetlog <<
"Peer causing an exception: " << _e.what() <<
" " << _r;
903 void EthereumCapability::setIdle(
NodeID const& _peerID)
908 void EthereumCapability::setAsking(
NodeID const& _peerID,
Asking _a)
910 auto itPeerStatus = m_peers.find(_peerID);
911 if (itPeerStatus == m_peers.end())
914 auto& peerStatus = itPeerStatus->second;
916 peerStatus.setAsking(_a);
917 peerStatus.setLastAsk(std::chrono::system_clock::to_time_t(chrono::system_clock::now()));
919 m_host->addNote(_peerID,
"ask", ::
toString(_a));
920 m_host->addNote(_peerID,
"sync",
921 string(isCriticalSyncing(_peerID) ?
"ONGOING" :
"holding") +
922 (needsSyncing(_peerID) ?
" & needed" :
""));
925 bool EthereumCapability::isCriticalSyncing(
NodeID const& _peerID)
const 927 auto itPeerStatus = m_peers.find(_peerID);
928 if (itPeerStatus == m_peers.end())
931 auto const& peerStatus = itPeerStatus->second;
933 auto const asking = peerStatus.asking();
937 bool EthereumCapability::needsSyncing(
NodeID const& _peerID)
const 939 if (m_host->isRude(_peerID,
name()))
942 auto peerStatus = m_peers.find(_peerID);
943 return (peerStatus != m_peers.end() && peerStatus->second.latestHash());
948 m_host->disableCapability(_peerID,
name(), _problem);
953 return const_cast<EthereumCapability*>(
this)->peer(_peerID);
958 auto peer = m_peers.find(_peerID);
959 if (
peer == m_peers.end())
960 BOOST_THROW_EXCEPTION(PeerDisconnected() <<
errinfo_nodeID(_peerID));
void requestStatus(u256 _hostNetworkId, u256 _chainTotalDifficulty, h256 _chainCurrentHash, h256 _chainGenesPeersh)
_T toInt(int _flags=Strict) const
Converts to int of type given; if isData(), decodes as big-endian bytestream.
void setWaitingForTransactions(bool _value)
void onStopping() override
A queue of Transactions, each stored as RLP. Maintains a transaction queue sorted by nonce diff and g...
Handler< ImportResult, h256 const &, h512 const & > onImport(T const &_t)
Register a handler that will be called once asynchronous verification is comeplte an transaction has ...
void onDisconnect(NodeID const &_nodeID) override
bool isWaitingForTransactions() const
Implements the blockchain database. All data this gives is disk-backed. @threadsafe.
bytes rlp(_T _t)
Export a single item in RLP format, returning a byte array.
BlockDetails details(h256 const &_hash) const
Get the familial details concerning a block (or the most recent mined if none given)....
void setLastAsk(time_t _lastAsk)
RLPStream & append(unsigned _s)
Append given datum to the byte stream.
void completeSync()
Don't sync further - used only in test mode.
bool interpretCapabilityPacket(NodeID const &_peerID, unsigned _id, RLP const &_r) override
void setStatus(unsigned _protocolVersion, u256 const &_networkId, u256 const &_totalDifficulty, h256 const &_latestHash, h256 const &_genesisHash)
void markTransactionAsKnown(h256 const &_hash)
std::vector< std::string > strings
unsigned number(h256 const &_hash) const
Get a number for the given hash (or the most recent mined if none given). Thread-safe.
void onStarting() override
EthereumPeer const & peer(NodeID const &_peerID) const
bool isBlockKnown(h256 const &_hash) const
std::string toString(std::chrono::time_point< T > const &_e, std::string const &_format="%F %T")
void disablePeer(NodeID const &_peerID, std::string const &_problem)
bool isConversing() const
bool sha3(bytesConstRef _input, bytesRef o_output) noexcept
boost::multiprecision::number< boost::multiprecision::cpp_int_backend<> > bigint
bool isKnown(h256 const &_hash, bool _isCurrent=true) const
Returns true if the given block is known (though not necessarily a part of the canon chain).
Base class for all exceptions.
std::tuple< h256s, h256, unsigned > treeRoute(h256 const &_from, h256 const &_to, bool _common=true, bool _pre=true, bool _post=true) const
Logger createLogger(int _severity, std::string const &_channel)
std::vector< byte > bytes
RLPStream & appendList(size_t _items)
Appends a list.
void onConnect(NodeID const &_nodeID, u256 const &_peerCapabilityVersion) override
A queue of blocks. Sits between network or other I/O and the BlockChain. Sorts them ready for blockch...
h256 genesisHash() const
Get the hash of the genesis block. Thread-safe.
h256Hash knownTransactions() const
boost::error_info< struct tag_nodeID, h512 > errinfo_nodeID
h256 currentHash() const
Get a given block (RLP format). Thread-safe.
bytes const & out() const
Read the byte stream.
Base BlockChain synchronization strategy class. Syncs to peers and keeps up to date....
boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void > > u256
SyncStatus status() const
N diff(N const &_a, N const &_b)
std::string name() const override
Transactions topTransactions(unsigned _limit, h256Hash const &_avoid=h256Hash()) const
std::vector< h256 > h256s
Class for writing to an RLP bytestream.
_N toHash(int _flags=Strict) const
RLPStream & appendRaw(bytesConstRef _rlp, size_t _itemCount=1)
Appends raw (pre-serialised) RLP data. Use with caution.
bool isTransactionKnown(h256 const &_hash) const
boost::log::sources::severity_channel_logger<> Logger
bytes block(h256 const &_hash) const
Get a block (RLP format) for the given hash (or the most recent mined if none given)....