Ethereum  PoC-8
The C++ Implementation of Ethereum
WarpCapability.cpp
Go to the documentation of this file.
1 /*
2  This file is part of cpp-ethereum.
3 
4  cpp-ethereum is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  cpp-ethereum is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #include "WarpCapability.h"
19 #include "BlockChain.h"
20 #include "SnapshotStorage.h"
21 
22 #include <boost/fiber/all.hpp>
23 #include <chrono>
24 
25 namespace dev
26 {
27 namespace eth
28 {
29 namespace
30 {
31 static size_t const c_freePeerBufferSize = 32;
32 static int const c_backroundWorkPeriodMs = 1000;
33 
34 bool validateManifest(RLP const& _manifestRlp)
35 {
36  if (!_manifestRlp.isList() || _manifestRlp.itemCount() != 1)
37  return false;
38 
39  RLP const manifest = _manifestRlp[0];
40 
41  u256 const version = manifest[0].toInt<u256>();
42  return version == 2;
43 }
44 
45 h256 snapshotBlockHash(RLP const& _manifestRlp)
46 {
47  RLP const manifest = _manifestRlp[0];
48  return manifest[5].toHash<h256>();
49 }
50 
51 class WarpPeerObserver : public WarpPeerObserverFace
52 {
53 public:
54  WarpPeerObserver(WarpCapability& _host, BlockChain const& _blockChain,
55  boost::filesystem::path const& _snapshotPath)
56  : m_host(_host),
57  m_hostProtocolVersion(_host.protocolVersion()),
58  m_hostNetworkId(_host.networkId()),
59  m_hostGenesisHash(_blockChain.genesisHash()),
60  m_daoForkBlock(_blockChain.sealEngine()->chainParams().daoHardforkBlock),
61  m_freePeers(c_freePeerBufferSize),
62  m_snapshotDir(_snapshotPath)
63  {}
64  ~WarpPeerObserver()
65  {
66  if (m_downloadFiber)
67  m_downloadFiber->join();
68  }
69 
70  void onPeerStatus(NodeID const& _peerID) override
71  {
72  boost::fibers::fiber checkPeerFiber(&WarpPeerObserver::validatePeer, this, _peerID);
73  checkPeerFiber.detach();
74 
75  // start the downloading fiber in the thread handling network messages
76  if (!m_downloadFiber)
77  m_downloadFiber.reset(
78  new boost::fibers::fiber(&WarpPeerObserver::downloadChunks, this));
79 
80  boost::this_fiber::yield();
81  }
82 
83  void onPeerManifest(NodeID const& _peerID, RLP const& _r) override
84  {
85  m_manifests[_peerID].set_value(_r.data().toBytes());
86  boost::this_fiber::yield();
87  }
88 
89  void onPeerBlockHeaders(NodeID const& _peerID, RLP const& _r) override
90  {
91  m_daoForkHeaders[_peerID].set_value(_r.data().toBytes());
92  boost::this_fiber::yield();
93  }
94 
95  void onPeerData(NodeID const& _peerID, RLP const& _r) override
96  {
97  if (!_r.isList() || _r.itemCount() != 1)
98  return;
99 
100  RLP const data = _r[0];
101 
102  h256 const hash = sha3(data.toBytesConstRef());
103 
104  auto it = m_requestedChunks.find(_peerID);
105  if (it == m_requestedChunks.end())
106  return;
107 
108  h256 const askedHash = it->second;
109  m_requestedChunks.erase(it);
110 
111  if (hash == askedHash)
112  {
113  // TODO handle writeFile failure
114  writeFile((boost::filesystem::path(m_snapshotDir) / toHex(hash)).string(),
115  data.toBytesConstRef());
116 
117  LOG(m_logger) << "Saved chunk " << hash << " Chunks left: " << m_neededChunks.size()
118  << " Requested chunks: " << m_requestedChunks.size();
119  if (m_neededChunks.empty() && m_requestedChunks.empty())
120  LOG(m_logger) << "Snapshot download complete!";
121  }
122  else
123  m_neededChunks.push_back(askedHash);
124 
125  m_freePeers.push(_peerID);
126  boost::this_fiber::yield();
127  }
128 
129  void onPeerDisconnect(NodeID const& _peerID, Asking _asking) override
130  {
131  if (_asking == Asking::WarpManifest)
132  {
133  auto it = m_manifests.find(_peerID);
134  if (it != m_manifests.end())
135  it->second.set_exception(std::make_exception_ptr(FailedToDownloadManifest()));
136  }
137  else if (_asking == Asking::BlockHeaders)
138  {
139  auto it = m_daoForkHeaders.find(_peerID);
140  if (it != m_daoForkHeaders.end())
141  it->second.set_exception(
142  std::make_exception_ptr(FailedToDownloadDaoForkBlockHeader()));
143  }
144  else if (_asking == Asking::WarpData)
145  {
146  auto it = m_requestedChunks.find(_peerID);
147  if (it != m_requestedChunks.end())
148  {
149  m_neededChunks.push_back(it->second);
150  m_requestedChunks.erase(it);
151  }
152  }
153  boost::this_fiber::yield();
154  }
155 
156 private:
157  void validatePeer(NodeID _peerID)
158  {
159  if (!m_host.validateStatus(
160  _peerID, m_hostGenesisHash, {m_hostProtocolVersion}, m_hostNetworkId))
161  return;
162 
163  m_host.requestManifest(_peerID);
164 
165  bytes const manifestBytes = waitForManifestResponse(_peerID);
166  if (manifestBytes.empty())
167  return;
168 
169  RLP manifestRlp(manifestBytes);
170  if (!validateManifest(manifestRlp))
171  {
172  // TODO try disconnecting instead of disabling; disabled peer still occupies the peer slot
173  m_host.disablePeer(_peerID, "Invalid snapshot manifest.");
174  return;
175  }
176 
177  u256 const snapshotHash = snapshotBlockHash(manifestRlp);
178  if (m_syncingSnapshotHash)
179  {
180  if (snapshotHash == m_syncingSnapshotHash)
181  m_freePeers.push(_peerID);
182  else
183  m_host.disablePeer(_peerID, "Another snapshot.");
184  }
185  else
186  {
187  if (m_daoForkBlock)
188  {
189  m_host.requestBlockHeaders(_peerID, m_daoForkBlock, 1, 0, false);
190 
191  bytes const headerBytes = waitForDaoForkBlockResponse(_peerID);
192  if (headerBytes.empty())
193  return;
194 
195  RLP headerRlp(headerBytes);
196  if (!verifyDaoChallengeResponse(headerRlp))
197  {
198  m_host.disablePeer(_peerID, "Peer from another fork.");
199  return;
200  }
201  }
202 
203  m_syncingSnapshotHash = snapshotHash;
204  m_manifest.set_value(manifestBytes);
205  m_freePeers.push(_peerID);
206  }
207  }
208 
209  bytes waitForManifestResponse(NodeID const& _peerID)
210  {
211  try
212  {
213  bytes const result = m_manifests[_peerID].get_future().get();
214  m_manifests.erase(_peerID);
215  return result;
216  }
217  catch (Exception const&)
218  {
219  m_manifests.erase(_peerID);
220  }
221  return bytes{};
222  }
223 
224  bytes waitForDaoForkBlockResponse(NodeID const& _peerID)
225  {
226  try
227  {
228  bytes const result = m_daoForkHeaders[_peerID].get_future().get();
229  m_daoForkHeaders.erase(_peerID);
230  return result;
231  }
232  catch (Exception const&)
233  {
234  m_daoForkHeaders.erase(_peerID);
235  }
236  return bytes{};
237  }
238 
239  bool verifyDaoChallengeResponse(RLP const& _r)
240  {
241  if (_r.itemCount() != 1)
242  return false;
243 
244  BlockHeader info(_r[0].data(), HeaderData);
245  return info.number() == m_daoForkBlock &&
246  info.extraData() == fromHex("0x64616f2d686172642d666f726b");
247  }
248 
249  void downloadChunks()
250  {
251  bytes const manifestBytes = m_manifest.get_future().get();
252 
253  RLP manifestRlp(manifestBytes);
254  RLP manifest(manifestRlp[0]);
255 
256  u256 const version = manifest[0].toInt<u256>();
257  h256s const stateHashes = manifest[1].toVector<h256>();
258  h256s const blockHashes = manifest[2].toVector<h256>();
259  h256 const stateRoot = manifest[3].toHash<h256>();
260  u256 const blockNumber = manifest[4].toInt<u256>();
261  h256 const blockHash = manifest[5].toHash<h256>();
262 
263  LOG(m_logger) << "MANIFEST: "
264  << "version " << version << " state root " << stateRoot << " block number "
265  << blockNumber << " block hash " << blockHash;
266 
267  // TODO handle writeFile failure
268  writeFile((boost::filesystem::path(m_snapshotDir) / "MANIFEST").string(), manifest.data());
269 
270  m_neededChunks.assign(stateHashes.begin(), stateHashes.end());
271  m_neededChunks.insert(m_neededChunks.end(), blockHashes.begin(), blockHashes.end());
272 
273  while (!m_neededChunks.empty())
274  {
275  h256 const chunkHash(m_neededChunks.front());
276 
277  NodeID peerID;
278  do
279  {
280  peerID = m_freePeers.value_pop();
281  } while (!m_host.requestData(peerID, chunkHash));
282 
283  LOG(m_logger) << "Requested chunk " << chunkHash;
284 
285  m_requestedChunks[peerID] = chunkHash;
286  m_neededChunks.pop_front();
287  }
288  }
289 
290  WarpCapability& m_host;
291  unsigned const m_hostProtocolVersion;
292  u256 const m_hostNetworkId;
293  h256 const m_hostGenesisHash;
294  unsigned const m_daoForkBlock;
295  boost::fibers::promise<bytes> m_manifest;
296  h256 m_syncingSnapshotHash;
297  std::deque<h256> m_neededChunks;
298  boost::fibers::buffered_channel<NodeID> m_freePeers;
299  boost::filesystem::path const m_snapshotDir;
300  std::map<NodeID, boost::fibers::promise<bytes>> m_manifests;
301  std::map<NodeID, boost::fibers::promise<bytes>> m_daoForkHeaders;
302  std::map<NodeID, h256> m_requestedChunks;
303 
304  std::unique_ptr<boost::fibers::fiber> m_downloadFiber;
305 
306  Logger m_logger{createLogger(VerbosityInfo, "snap")};
307 };
308 
309 } // namespace
310 
311 
312 WarpCapability::WarpCapability(std::shared_ptr<p2p::CapabilityHostFace> _host,
313  BlockChain const& _blockChain, u256 const& _networkId,
314  boost::filesystem::path const& _snapshotDownloadPath,
315  std::shared_ptr<SnapshotStorageFace> _snapshotStorage)
316  : m_host(std::move(_host)),
317  m_blockChain(_blockChain),
318  m_networkId(_networkId),
319  m_snapshot(_snapshotStorage),
320  // observer needed only in case we download snapshot
321  m_peerObserver(
322  _snapshotDownloadPath.empty() ? nullptr : createPeerObserver(_snapshotDownloadPath))
323 {
324 }
325 
327 {
328  m_backgroundWorkEnabled = true;
329  m_host->scheduleExecution(c_backroundWorkPeriodMs, [this]() { doBackgroundWork(); });
330 }
331 
333 {
334  m_backgroundWorkEnabled = false;
335 }
336 
337 std::shared_ptr<WarpPeerObserverFace> WarpCapability::createPeerObserver(
338  boost::filesystem::path const& _snapshotDownloadPath)
339 {
340  return std::make_shared<WarpPeerObserver>(*this, m_blockChain, _snapshotDownloadPath);
341 }
342 
343 void WarpCapability::doBackgroundWork()
344 {
345  for (auto const& peer : m_peers)
346  {
347  time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
348  auto const& status = peer.second;
349  if (now - status.m_lastAsk > 10 && status.m_asking != Asking::Nothing)
350  {
351  // timeout
352  m_host->disconnect(peer.first, p2p::PingTimeout);
353  }
354  }
355 
356  if (m_backgroundWorkEnabled)
357  m_host->scheduleExecution(c_backroundWorkPeriodMs, [this]() { doBackgroundWork(); });
358 }
359 
360 void WarpCapability::onConnect(NodeID const& _peerID, u256 const& /* _peerCapabilityVersion */)
361 {
362  m_peers.emplace(_peerID, WarpPeerStatus{});
363 
364  u256 snapshotBlockNumber;
365  h256 snapshotBlockHash;
366  if (m_snapshot)
367  {
368  bytes const snapshotManifest(m_snapshot->readManifest());
369  RLP manifest(snapshotManifest);
370  if (manifest.itemCount() != 6)
371  BOOST_THROW_EXCEPTION(InvalidSnapshotManifest());
372  snapshotBlockNumber = manifest[4].toInt<u256>(RLP::VeryStrict);
373  snapshotBlockHash = manifest[5].toHash<h256>(RLP::VeryStrict);
374  }
375 
376  requestStatus(_peerID, c_WarpProtocolVersion, m_networkId,
377  m_blockChain.details().totalDifficulty, m_blockChain.currentHash(),
378  m_blockChain.genesisHash(), snapshotBlockHash, snapshotBlockNumber);
379 }
380 
381 bool WarpCapability::interpretCapabilityPacket(NodeID const& _peerID, unsigned _id, RLP const& _r)
382 {
383  auto& peerStatus = m_peers[_peerID];
384  peerStatus.m_lastAsk = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
385 
386  try
387  {
388  switch (_id)
389  {
390  case WarpStatusPacket:
391  {
392  if (_r.itemCount() < 7)
393  BOOST_THROW_EXCEPTION(InvalidWarpStatusPacket());
394 
395  // Packet layout:
396  // [ version:P, state_hashes : [hash_1:B_32, hash_2 : B_32, ...], block_hashes :
397  // [hash_1:B_32, hash_2 : B_32, ...],
398  // state_root : B_32, block_number : P, block_hash : B_32 ]
399  peerStatus.m_protocolVersion = _r[0].toInt<unsigned>();
400  peerStatus.m_networkId = _r[1].toInt<u256>();
401  peerStatus.m_totalDifficulty = _r[2].toInt<u256>();
402  peerStatus.m_latestHash = _r[3].toHash<h256>();
403  peerStatus.m_genesisHash = _r[4].toHash<h256>();
404  peerStatus.m_snapshotHash = _r[5].toHash<h256>();
405  peerStatus.m_snapshotNumber = _r[6].toInt<u256>();
406 
407  cnetlog << "Status: "
408  << " protocol version " << peerStatus.m_protocolVersion << " networkId "
409  << peerStatus.m_networkId << " genesis hash " << peerStatus.m_genesisHash
410  << " total difficulty " << peerStatus.m_totalDifficulty << " latest hash "
411  << peerStatus.m_latestHash << " snapshot hash " << peerStatus.m_snapshotHash
412  << " snapshot number " << peerStatus.m_snapshotNumber;
413  setIdle(_peerID);
414  m_peerObserver->onPeerStatus(_peerID);
415  break;
416  }
417  case GetSnapshotManifest:
418  {
419  if (!m_snapshot)
420  return false;
421 
422  RLPStream s;
423  m_host->prep(_peerID, name(), s, SnapshotManifest, 1)
424  .appendRaw(m_snapshot->readManifest());
425  m_host->sealAndSend(_peerID, s);
426  break;
427  }
428  case GetSnapshotData:
429  {
430  if (!m_snapshot)
431  return false;
432 
433  const h256 chunkHash = _r[0].toHash<h256>(RLP::VeryStrict);
434 
435  RLPStream s;
436  m_host->prep(_peerID, name(), s, SnapshotData, 1)
437  .append(m_snapshot->readCompressedChunk(chunkHash));
438  m_host->sealAndSend(_peerID, s);
439  break;
440  }
442  {
443  // TODO We are being asked DAO fork block sometimes, need to be able to answer this
444  RLPStream s;
445  m_host->prep(_peerID, name(), s, BlockHeadersPacket);
446  m_host->sealAndSend(_peerID, s);
447  break;
448  }
449  case BlockHeadersPacket:
450  {
451  setIdle(_peerID);
452  m_peerObserver->onPeerBlockHeaders(_peerID, _r);
453  break;
454  }
455  case SnapshotManifest:
456  {
457  setIdle(_peerID);
458  m_peerObserver->onPeerManifest(_peerID, _r);
459  break;
460  }
461  case SnapshotData:
462  {
463  setIdle(_peerID);
464  m_peerObserver->onPeerData(_peerID, _r);
465  break;
466  }
467  default:
468  return false;
469  }
470  }
471  catch (Exception const&)
472  {
473  cnetlog << "Warp Peer causing an Exception: "
474  << boost::current_exception_diagnostic_information() << " " << _r;
475  }
476  catch (std::exception const& _e)
477  {
478  cnetlog << "Warp Peer causing an exception: " << _e.what() << " " << _r;
479  }
480 
481  return true;
482 }
483 
485 {
486  m_peerObserver->onPeerDisconnect(_peerID, m_peers[_peerID].m_asking);
487  m_peers.erase(_peerID);
488 }
489 
490 
491 void WarpCapability::requestStatus(NodeID const& _peerID, unsigned _hostProtocolVersion,
492  u256 const& _hostNetworkId, u256 const& _chainTotalDifficulty, h256 const& _chainCurrentHash,
493  h256 const& _chainGenesisHash, h256 const& _snapshotBlockHash, u256 const& _snapshotBlockNumber)
494 {
495  RLPStream s;
496  m_host->prep(_peerID, name(), s, WarpStatusPacket, 7)
497  << _hostProtocolVersion << _hostNetworkId << _chainTotalDifficulty << _chainCurrentHash
498  << _chainGenesisHash << _snapshotBlockHash << _snapshotBlockNumber;
499  m_host->sealAndSend(_peerID, s);
500 }
501 
502 
504  NodeID const& _peerID, unsigned _startNumber, unsigned _count, unsigned _skip, bool _reverse)
505 {
506  auto itPeerStatus = m_peers.find(_peerID);
507  if (itPeerStatus == m_peers.end())
508  return;
509 
510  assert(itPeerStatus->second.m_asking == Asking::Nothing);
511  setAsking(_peerID, Asking::BlockHeaders);
512  RLPStream s;
513  m_host->prep(_peerID, name(), s, GetBlockHeadersPacket, 4)
514  << _startNumber << _count << _skip << (_reverse ? 1 : 0);
515  m_host->sealAndSend(_peerID, s);
516 }
517 
519 {
520  auto itPeerStatus = m_peers.find(_peerID);
521  if (itPeerStatus == m_peers.end())
522  return;
523 
524  assert(itPeerStatus->second.m_asking == Asking::Nothing);
525  setAsking(_peerID, Asking::WarpManifest);
526  RLPStream s;
527  m_host->prep(_peerID, name(), s, GetSnapshotManifest);
528  m_host->sealAndSend(_peerID, s);
529 }
530 
531 bool WarpCapability::requestData(NodeID const& _peerID, h256 const& _chunkHash)
532 {
533  auto itPeerStatus = m_peers.find(_peerID);
534  if (itPeerStatus == m_peers.end())
535  return false;
536 
537  assert(itPeerStatus->second.m_asking == Asking::Nothing);
538  setAsking(_peerID, Asking::WarpData);
539  RLPStream s;
540 
541  m_host->prep(_peerID, name(), s, GetSnapshotData, 1) << _chunkHash;
542  m_host->sealAndSend(_peerID, s);
543  return true;
544 }
545 
546 void WarpCapability::setAsking(NodeID const& _peerID, Asking _a)
547 {
548  auto itPeerStatus = m_peers.find(_peerID);
549  if (itPeerStatus == m_peers.end())
550  return;
551 
552  auto& peerStatus = itPeerStatus->second;
553 
554  peerStatus.m_asking = _a;
555  peerStatus.m_lastAsk = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
556 }
557 
559 bool WarpCapability::validateStatus(NodeID const& _peerID, h256 const& _genesisHash,
560  std::vector<unsigned> const& _protocolVersions, u256 const& _networkId)
561 {
562  auto itPeerStatus = m_peers.find(_peerID);
563  if (itPeerStatus == m_peers.end())
564  return false; // Expired
565 
566  auto const& peerStatus = itPeerStatus->second;
567 
568  if (peerStatus.m_genesisHash != _genesisHash)
569  {
570  disablePeer(_peerID, "Invalid genesis hash");
571  return false;
572  }
573  if (find(_protocolVersions.begin(), _protocolVersions.end(), peerStatus.m_protocolVersion) ==
574  _protocolVersions.end())
575  {
576  disablePeer(_peerID, "Invalid protocol version.");
577  return false;
578  }
579  if (peerStatus.m_networkId != _networkId)
580  {
581  disablePeer(_peerID, "Invalid network identifier.");
582  return false;
583  }
584  if (peerStatus.m_asking != Asking::State && peerStatus.m_asking != Asking::Nothing)
585  {
586  disablePeer(_peerID, "Peer banned for unexpected status message.");
587  return false;
588  }
589 
590  return true;
591 }
592 
593 void WarpCapability::disablePeer(NodeID const& _peerID, std::string const& _problem)
594 {
595  m_host->disableCapability(_peerID, name(), _problem);
596 }
597 
598 } // namespace eth
599 } // namespace dev
_T toInt(int _flags=Strict) const
Converts to int of type given; if isData(), decodes as big-endian bytestream.
Definition: RLP.h:257
void disablePeer(NodeID const &_peerID, std::string const &_problem)
Definition: Address.cpp:20
bool interpretCapabilityPacket(NodeID const &_peerID, unsigned _id, RLP const &) override
WarpCapability(std::shared_ptr< p2p::CapabilityHostFace > _host, BlockChain const &_blockChain, u256 const &_networkId, boost::filesystem::path const &_snapshotDownloadPath, std::shared_ptr< SnapshotStorageFace > _snapshotStorage)
Implements the blockchain database. All data this gives is disk-backed. .
Definition: BlockChain.h:104
BlockDetails details(h256 const &_hash) const
Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
Definition: BlockChain.h:157
Definition: FixedHash.h:390
void requestBlockHeaders(NodeID const &_peerID, unsigned _startNumber, unsigned _count, unsigned _skip, bool _reverse)
bool sha3(bytesConstRef _input, bytesRef o_output) noexcept
Definition: SHA3.cpp:28
void requestManifest(NodeID const &_peerID)
bytes fromHex(std::string const &_s, WhenError _throw=WhenError::DontThrow)
Definition: CommonData.cpp:81
Base class for all exceptions.
Definition: Exceptions.h:38
p2p::NodeID NodeID
Definition: CommonNet.h:105
void onConnect(NodeID const &_peerID, u256 const &_peerCapabilityVersion) override
unsigned const c_WarpProtocolVersion
void onDisconnect(NodeID const &_peerID) override
Logger createLogger(int _severity, std::string const &_channel)
Definition: Log.h:125
std::vector< byte > bytes
Definition: Common.h:72
void requestStatus(NodeID const &_peerID, unsigned _hostProtocolVersion, u256 const &_hostNetworkId, u256 const &_chainTotalDifficulty, h256 const &_chainCurrentHash, h256 const &_chainGenesisHash, h256 const &_snapshotBlockHash, u256 const &_snapshotBlockNumber)
bool validateStatus(NodeID const &_peerID, h256 const &_genesisHash, std::vector< unsigned > const &_protocolVersions, u256 const &_networkId)
Validates whether peer is able to communicate with the host, disables peer if not.
FixedHash< 32 > h256
Definition: FixedHash.h:354
h256 genesisHash() const
Get the hash of the genesis block. Thread-safe.
Definition: BlockChain.h:231
h256 currentHash() const
Get a given block (RLP format). Thread-safe.
Definition: BlockChain.h:228
std::string name() const override
boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void > > u256
Definition: Common.h:121
#define LOG
Definition: Log.h:63
size_t itemCount() const
Definition: RLP.h:101
std::string toHex(Iterator _it, Iterator _end, std::string const &_prefix)
Definition: CommonData.h:46
std::vector< h256 > h256s
Definition: FixedHash.h:359
void writeFile(boost::filesystem::path const &_file, bytesConstRef _data, bool _writeDeleteRename)
Definition: CommonIO.cpp:125
Class for writing to an RLP bytestream.
Definition: RLP.h:369
_N toHash(int _flags=Strict) const
Definition: RLP.h:288
Definition: RLP.h:47
bool requestData(NodeID const &_peerID, h256 const &_chunkHash)
boost::log::sources::severity_channel_logger<> Logger
Definition: Log.h:124