Fabcoin Core  0.16.2
P2P Digital Currency
EthereumPeer.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 */
22 #include "EthereumPeer.h"
23 
24 #include <chrono>
25 #include <libdevcore/Common.h>
26 #include <libethcore/Exceptions.h>
27 #include <libp2p/Session.h>
28 #include <libp2p/Host.h>
29 #include "EthereumHost.h"
30 
31 using namespace std;
32 using namespace dev;
33 using namespace dev::eth;
34 using namespace p2p;
35 
36 static const unsigned c_maxIncomingNewHashes = 1024;
37 static const unsigned c_maxHeadersToSend = 1024;
38 
39 static string toString(Asking _a)
40 {
41  switch (_a)
42  {
43  case Asking::BlockHeaders: return "BlockHeaders";
44  case Asking::BlockBodies: return "BlockBodies";
45  case Asking::NodeData: return "NodeData";
46  case Asking::Receipts: return "Receipts";
47  case Asking::Nothing: return "Nothing";
48  case Asking::State: return "State";
49  }
50  return "?";
51 }
52 
53 EthereumPeer::EthereumPeer(std::shared_ptr<SessionFace> _s, HostCapabilityFace* _h, unsigned _i, CapDesc const& _cap, uint16_t _capID):
54  Capability(_s, _h, _i, _capID),
55  m_peerCapabilityVersion(_cap.second)
56 {
57  session()->addNote("manners", isRude() ? "RUDE" : "nice");
58 }
59 
61 {
63  {
64  clog(NetAllDetail) << "Peer aborting while being asked for " << ::toString(m_asking);
65  setRude();
66  }
67  abortSync();
68 }
69 
70 void EthereumPeer::init(unsigned _hostProtocolVersion, u256 _hostNetworkId, u256 _chainTotalDifficulty, h256 _chainCurrentHash, h256 _chainGenesisHash, shared_ptr<EthereumHostDataFace> _hostData, shared_ptr<EthereumPeerObserverFace> _observer)
71 {
72  m_hostData = _hostData;
73  m_observer = _observer;
74  m_hostProtocolVersion = _hostProtocolVersion;
75  requestStatus(_hostNetworkId, _chainTotalDifficulty, _chainCurrentHash, _chainGenesisHash);
76 }
77 
79 {
80  auto s = session();
81  if (s)
82  return s->repMan().isRude(*s, name());
83  return false;
84 }
85 
86 unsigned EthereumPeer::askOverride() const
87 {
88  std::string static const badGeth = "Geth/v0.9.27";
89  auto s = session();
90  if (!s)
91  return c_maxBlocksAsk;
92  if (s->info().clientVersion.substr(0, badGeth.size()) == badGeth)
93  return 1;
94  bytes const& d = s->repMan().data(*s, name());
95  return d.empty() ? c_maxBlocksAsk : RLP(d).toInt<unsigned>(RLP::LaissezFaire);
96 }
97 
99 {
100  auto s = session();
101  if (!s)
102  return;
103  auto old = askOverride();
104  s->repMan().setData(*s, name(), rlp(askOverride() / 2 + 1));
105  cnote << "Rude behaviour; askOverride now" << askOverride() << ", was" << old;
106  s->repMan().noteRude(*s, name());
107  session()->addNote("manners", "RUDE");
108 }
109 
111 {
112  if (m_observer)
113  m_observer->onPeerAborting();
114 }
115 
116 
117 /*
118  * Possible asking/syncing states for two peers:
119  */
120 
122 {
124 }
125 
126 void EthereumPeer::requestStatus(u256 _hostNetworkId, u256 _chainTotalDifficulty, h256 _chainCurrentHash, h256 _chainGenesisHash)
127 {
130  m_requireTransactions = true;
131  RLPStream s;
133  prep(s, StatusPacket, 5)
135  << _hostNetworkId
136  << _chainTotalDifficulty
137  << _chainCurrentHash
138  << _chainGenesisHash;
139  sealAndSend(s);
140 }
141 
142 void EthereumPeer::requestBlockHeaders(unsigned _startNumber, unsigned _count, unsigned _skip, bool _reverse)
143 {
144  if (m_asking != Asking::Nothing)
145  {
146  clog(NetWarn) << "Asking headers while requesting " << ::toString(m_asking);
147  }
149  RLPStream s;
150  prep(s, GetBlockHeadersPacket, 4) << _startNumber << _count << _skip << (_reverse ? 1 : 0);
151  clog(NetMessageDetail) << "Requesting " << _count << " block headers starting from " << _startNumber << (_reverse ? " in reverse" : "");
152  m_lastAskedHeaders = _count;
153  sealAndSend(s);
154 }
155 
156 void EthereumPeer::requestBlockHeaders(h256 const& _startHash, unsigned _count, unsigned _skip, bool _reverse)
157 {
158  if (m_asking != Asking::Nothing)
159  {
160  clog(NetWarn) << "Asking headers while requesting " << ::toString(m_asking);
161  }
163  RLPStream s;
164  prep(s, GetBlockHeadersPacket, 4) << _startHash << _count << _skip << (_reverse ? 1 : 0);
165  clog(NetMessageDetail) << "Requesting " << _count << " block headers starting from " << _startHash << (_reverse ? " in reverse" : "");
166  m_lastAskedHeaders = _count;
167  sealAndSend(s);
168 }
169 
170 
172 {
174 }
175 
177 {
179 }
180 
182 {
184 }
185 
186 void EthereumPeer::requestByHashes(h256s const& _hashes, Asking _asking, SubprotocolPacketType _packetType)
187 {
188  if (m_asking != Asking::Nothing)
189  {
190  clog(NetWarn) << "Asking "<< ::toString(_asking) << " while requesting " << ::toString(m_asking);
191  }
192  setAsking(_asking);
193  if (_hashes.size())
194  {
195  RLPStream s;
196  prep(s, _packetType, _hashes.size());
197  for (auto const& i: _hashes)
198  s << i;
199  sealAndSend(s);
200  }
201  else
202  setIdle();
203 }
204 
206 {
207  m_asking = _a;
208  m_lastAsk = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
209 
210  auto s = session();
211  if (s)
212  {
213  s->addNote("ask", ::toString(_a));
214  s->addNote("sync", string(isCriticalSyncing() ? "ONGOING" : "holding") + (needsSyncing() ? " & needed" : ""));
215  }
216 }
217 
219 {
220  auto s = session();
221  time_t now = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
222  if (s && (now - m_lastAsk > 10 && m_asking != Asking::Nothing))
223  // timeout
224  s->disconnect(PingTimeout);
225 }
226 
228 {
229  return m_asking != Asking::Nothing;
230 }
231 
233 {
235 }
236 
237 bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
238 {
240 
241  m_lastAsk = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
242  try
243  {
244  switch (_id)
245  {
246  case StatusPacket:
247  {
248  m_protocolVersion = _r[0].toInt<unsigned>();
249  m_networkId = _r[1].toInt<u256>();
250  m_totalDifficulty = _r[2].toInt<u256>();
251  m_latestHash = _r[3].toHash<h256>();
252  m_genesisHash = _r[4].toHash<h256>();
255 
256  clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << m_genesisHash << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
257  setIdle();
258  m_observer->onPeerStatus(dynamic_pointer_cast<EthereumPeer>(dynamic_pointer_cast<EthereumPeer>(shared_from_this())));
259  break;
260  }
261  case TransactionsPacket:
262  {
263  m_observer->onPeerTransactions(dynamic_pointer_cast<EthereumPeer>(dynamic_pointer_cast<EthereumPeer>(shared_from_this())), _r);
264  break;
265  }
267  {
270  const auto blockId = _r[0];
271  const auto maxHeaders = _r[1].toInt<u256>();
272  const auto skip = _r[2].toInt<u256>();
273  const auto reverse = _r[3].toInt<bool>();
274 
275  auto numHeadersToSend = maxHeaders <= c_maxHeadersToSend ? static_cast<unsigned>(maxHeaders) : c_maxHeadersToSend;
276 
277  if (skip > std::numeric_limits<unsigned>::max() - 1)
278  {
279  clog(NetAllDetail) << "Requested block skip is too big: " << skip;
280  break;
281  }
282 
283  pair<bytes, unsigned> const rlpAndItemCount = m_hostData->blockHeaders(blockId, numHeadersToSend, skip, reverse);
284 
285  RLPStream s;
286  prep(s, BlockHeadersPacket, rlpAndItemCount.second).appendRaw(rlpAndItemCount.first, rlpAndItemCount.second);
287  sealAndSend(s);
288  addRating(0);
289  break;
290  }
291  case BlockHeadersPacket:
292  {
294  clog(NetImpolite) << "Peer giving us block headers when we didn't ask for them.";
295  else
296  {
297  setIdle();
298  m_observer->onPeerBlockHeaders(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), _r);
299  }
300  break;
301  }
303  {
304  unsigned count = static_cast<unsigned>(_r.itemCount());
305  clog(NetMessageSummary) << "GetBlockBodies (" << dec << count << "entries)";
306 
307  if (!count)
308  {
309  clog(NetImpolite) << "Zero-entry GetBlockBodies: Not replying.";
310  addRating(-10);
311  break;
312  }
313 
314  pair<bytes, unsigned> const rlpAndItemCount = m_hostData->blockBodies(_r);
315 
316  addRating(0);
317  RLPStream s;
318  prep(s, BlockBodiesPacket, rlpAndItemCount.second).appendRaw(rlpAndItemCount.first, rlpAndItemCount.second);
319  sealAndSend(s);
320  break;
321  }
322  case BlockBodiesPacket:
323  {
325  clog(NetImpolite) << "Peer giving us block bodies when we didn't ask for them.";
326  else
327  {
328  setIdle();
329  m_observer->onPeerBlockBodies(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), _r);
330  }
331  break;
332  }
333  case NewBlockPacket:
334  {
335  m_observer->onPeerNewBlock(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), _r);
336  break;
337  }
339  {
340  unsigned itemCount = _r.itemCount();
341 
342  clog(NetMessageSummary) << "BlockHashes (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreHashes");
343 
344  if (itemCount > c_maxIncomingNewHashes)
345  {
346  disable("Too many new hashes");
347  break;
348  }
349 
350  vector<pair<h256, u256>> hashes(itemCount);
351  for (unsigned i = 0; i < itemCount; ++i)
352  hashes[i] = std::make_pair(_r[i][0].toHash<h256>(), _r[i][1].toInt<u256>());
353 
354  m_observer->onPeerNewHashes(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), hashes);
355  break;
356  }
357  case GetNodeDataPacket:
358  {
359  unsigned count = static_cast<unsigned>(_r.itemCount());
360  if (!count)
361  {
362  clog(NetImpolite) << "Zero-entry GetNodeData: Not replying.";
363  addRating(-10);
364  break;
365  }
366  clog(NetMessageSummary) << "GetNodeData (" << dec << count << " entries)";
367 
368  strings const data = m_hostData->nodeData(_r);
369 
370  addRating(0);
371  RLPStream s;
372  prep(s, NodeDataPacket, data.size());
373  for (auto const& element: data)
374  s.append(element);
375  sealAndSend(s);
376  break;
377  }
378  case GetReceiptsPacket:
379  {
380  unsigned count = static_cast<unsigned>(_r.itemCount());
381  if (!count)
382  {
383  clog(NetImpolite) << "Zero-entry GetReceipts: Not replying.";
384  addRating(-10);
385  break;
386  }
387  clog(NetMessageSummary) << "GetReceipts (" << dec << count << " entries)";
388 
389  pair<bytes, unsigned> const rlpAndItemCount = m_hostData->receipts(_r);
390 
391  addRating(0);
392  RLPStream s;
393  prep(s, ReceiptsPacket, rlpAndItemCount.second).appendRaw(rlpAndItemCount.first, rlpAndItemCount.second);
394  sealAndSend(s);
395  break;
396  }
397  case NodeDataPacket:
398  {
399  if (m_asking != Asking::NodeData)
400  clog(NetImpolite) << "Peer giving us node data when we didn't ask for them.";
401  else
402  {
403  setIdle();
404  m_observer->onPeerNodeData(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), _r);
405  }
406  break;
407  }
408  case ReceiptsPacket:
409  {
410  if (m_asking != Asking::Receipts)
411  clog(NetImpolite) << "Peer giving us receipts when we didn't ask for them.";
412  else
413  {
414  setIdle();
415  m_observer->onPeerReceipts(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), _r);
416  }
417  break;
418  }
419  default:
420  return false;
421  }
422  }
423  catch (Exception const&)
424  {
425  clog(NetWarn) << "Peer causing an Exception:" << boost::current_exception_diagnostic_information() << _r;
426  }
427  catch (std::exception const& _e)
428  {
429  clog(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
430  }
431 
432  return true;
433 }
unsigned m_lastAskedHeaders
Number of hashes asked.
Definition: EthereumPeer.h:193
Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c Origi...
Definition: Arith256.cpp:15
void tick()
Runs period checks to check up on the peer.
u256 const m_peerCapabilityVersion
Protocol version this peer supports received as capability.
Definition: EthereumPeer.h:184
void disable(std::string const &_problem)
Definition: Capability.cpp:37
u256 m_totalDifficulty
Peer&#39;s latest block&#39;s total difficulty.
Definition: EthereumPeer.h:181
bytes rlp(_T _t)
Export a single item in RLP format, returning a byte array.
Definition: RLP.h:467
virtual bool interpret(unsigned _id, RLP const &_r)
Interpret an incoming message.
std::pair< std::string, u256 > CapDesc
Definition: Common.h:142
size_t itemCount() const
Definition: RLP.h:118
bool needsSyncing() const
Do we presently need syncing with this peer?
Definition: EthereumPeer.h:155
RLPStream & append(unsigned _s)
Append given datum to the byte stream.
Definition: RLP.h:395
void abortSync()
Abort the sync operation.
std::shared_ptr< SessionFace > session() const
Definition: Capability.h:48
size_t count
Definition: ExecStats.cpp:37
Asking m_asking
What, if anything, we last asked the other peer for.
Definition: EthereumPeer.h:175
bool isCriticalSyncing() const
Are we presently in a critical part of the syncing process with this peer?
std::vector< std::string > strings
Definition: Common.h:147
std::hash for asio::adress
Definition: Common.h:323
assert(len-trim+(2 *lenIndices)<=WIDTH)
std::string toString(string32 const &_s)
Make normal string from fixed-length string.
Definition: CommonData.cpp:141
bool isConversing() const
Are we presently in the process of communicating with this peer?
std::atomic< time_t > m_lastAsk
When we asked for it. Allows a time out.
Definition: EthereumPeer.h:177
unsigned m_hostProtocolVersion
Definition: EthereumPeer.h:166
void requestStatus(u256 _hostNetworkId, u256 _chainTotalDifficulty, h256 _chainCurrentHash, h256 _chainGenesisHash)
Request status. Called from constructor.
void init(unsigned _hostProtocolVersion, u256 _hostNetworkId, u256 _chainTotalDifficulty, h256 _chainCurrentHash, h256 _chainGenesisHash, std::shared_ptr< EthereumHostDataFace > _hostData, std::shared_ptr< EthereumPeerObserverFace > _observer)
void requestBlockHeaders(h256 const &_startHash, unsigned _count, unsigned _skip, bool _reverse)
Request hashes for given parent hash.
void addRating(int _r)
Definition: Capability.cpp:55
_N toHash(int _flags=Strict) const
Definition: RLP.h:298
u256 m_networkId
Peer&#39;s network id.
Definition: EthereumPeer.h:172
Base class for all exceptions.
Definition: Exceptions.h:39
static unsigned const c_oldProtocolVersion
Definition: EthereumHost.h:93
ExecStats::duration max
Definition: ExecStats.cpp:36
void requestBlockBodies(h256s const &_blocks)
Request specified blocks from peer.
unsigned m_protocolVersion
Peer&#39;s protocol version.
Definition: EthereumPeer.h:169
std::vector< byte > bytes
Definition: Common.h:75
boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void >> u256
Definition: Common.h:125
void setRude()
Set that it&#39;s a rude node.
h256 m_latestHash
These are determined through either a Status message or from NewBlock.
Definition: EthereumPeer.h:180
RLPStream & prep(RLPStream &_s, unsigned _id, unsigned _args=0)
Definition: Capability.cpp:43
void setIdle()
Abort sync and reset fetch.
SubprotocolPacketType
Definition: CommonNet.h:60
void sealAndSend(RLPStream &_s)
Definition: Capability.cpp:48
std::shared_ptr< EthereumPeerObserverFace > m_observer
Definition: EthereumPeer.h:195
void requestReceipts(h256s const &_blocks)
Request receipts for specified blocks from peer.
#define cnote
Definition: Log.h:303
static std::string name()
What is our name?
Definition: EthereumPeer.h:96
void setAsking(Asking _g)
Update our asking state.
bool m_requireTransactions
Have we received a GetTransactions packet that we haven&#39;t yet answered?
Definition: EthereumPeer.h:186
std::shared_ptr< EthereumHostDataFace > m_hostData
Definition: EthereumPeer.h:196
#define clog(X)
Definition: Log.h:295
virtual ~EthereumPeer()
Basic destructor.
#define d(i)
Definition: sha.cpp:732
unsigned askOverride() const
Figure out the amount of blocks we should be asking for.
std::vector< h256 > h256s
Definition: FixedHash.h:345
_T toInt(int _flags=Strict) const
Converts to int of type given; if isString(), decodes as big-endian bytestream.
Definition: RLP.h:275
Class for writing to an RLP bytestream.
Definition: RLP.h:383
void requestNodeData(h256s const &_hashes)
Request values for specified keys from peer.
RLPStream & appendRaw(bytesConstRef _rlp, size_t _itemCount=1)
Appends raw (pre-serialised) RLP data. Use with caution.
Definition: RLP.cpp:230
bool isRude() const
Check if this node is rude.
uint8_t const * data
Definition: sha3.h:19
Class for interpreting Recursive Linear-Prefix Data.
Definition: RLP.h:64
h256 m_genesisHash
Peer&#39;s genesis hash.
Definition: EthereumPeer.h:182
void requestByHashes(h256s const &_hashes, Asking _asking, SubprotocolPacketType _packetType)