229 changed files with 9654 additions and 4804 deletions
@ -0,0 +1,83 @@ |
|||
/*
|
|||
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 DownloadView.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "DownloadView.h" |
|||
|
|||
#include <QtWidgets> |
|||
#include <QtCore> |
|||
#include <libethereum/DownloadMan.h> |
|||
#include "Grapher.h" |
|||
|
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
DownloadView::DownloadView(QWidget* _p): QWidget(_p) |
|||
{ |
|||
} |
|||
|
|||
void DownloadView::paintEvent(QPaintEvent*) |
|||
{ |
|||
QPainter p(this); |
|||
|
|||
p.fillRect(rect(), Qt::white); |
|||
if (!m_man || m_man->chain().empty() || !m_man->subCount()) |
|||
return; |
|||
|
|||
double ratio = (double)rect().width() / rect().height(); |
|||
if (ratio < 1) |
|||
ratio = 1 / ratio; |
|||
double n = min(rect().width(), rect().height()) / ceil(sqrt(m_man->chain().size() / ratio)); |
|||
|
|||
// QSizeF area(rect().width() / floor(rect().width() / n), rect().height() / floor(rect().height() / n));
|
|||
QSizeF area(n, n); |
|||
QPointF pos(0, 0); |
|||
|
|||
auto const& bg = m_man->blocksGot(); |
|||
|
|||
for (unsigned i = bg.all().first, ei = bg.all().second; i < ei; ++i) |
|||
{ |
|||
int s = -2; |
|||
if (bg.contains(i)) |
|||
s = -1; |
|||
else |
|||
{ |
|||
unsigned h = 0; |
|||
m_man->foreachSub([&](DownloadSub const& sub) |
|||
{ |
|||
if (sub.asked().contains(i)) |
|||
s = h; |
|||
h++; |
|||
}); |
|||
} |
|||
unsigned dh = 360 / m_man->subCount(); |
|||
if (s == -2) |
|||
p.fillRect(QRectF(QPointF(pos) + QPointF(3 * area.width() / 8, 3 * area.height() / 8), area / 4), Qt::black); |
|||
else if (s == -1) |
|||
p.fillRect(QRectF(QPointF(pos) + QPointF(1 * area.width() / 8, 1 * area.height() / 8), area * 3 / 4), Qt::black); |
|||
else |
|||
p.fillRect(QRectF(QPointF(pos) + QPointF(1 * area.width() / 8, 1 * area.height() / 8), area * 3 / 4), QColor::fromHsv(s * dh, 64, 128)); |
|||
|
|||
pos.setX(pos.x() + n); |
|||
if (pos.x() >= rect().width() - n) |
|||
pos = QPoint(0, pos.y() + n); |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,282 @@ |
|||
/*
|
|||
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 Common.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "CommonData.h" |
|||
|
|||
#include <random> |
|||
#include "Exceptions.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
|
|||
std::string dev::escaped(std::string const& _s, bool _all) |
|||
{ |
|||
std::string ret; |
|||
ret.reserve(_s.size()); |
|||
ret.push_back('"'); |
|||
for (auto i: _s) |
|||
if (i == '"' && !_all) |
|||
ret += "\\\""; |
|||
else if (i == '\\' && !_all) |
|||
ret += "\\\\"; |
|||
else if (i < ' ' || _all) |
|||
{ |
|||
ret += "\\x"; |
|||
ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); |
|||
ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); |
|||
} |
|||
else |
|||
ret.push_back(i); |
|||
ret.push_back('"'); |
|||
return ret; |
|||
} |
|||
|
|||
std::string dev::randomWord() |
|||
{ |
|||
static std::mt19937_64 s_eng(0); |
|||
std::string ret(std::uniform_int_distribution<int>(1, 5)(s_eng), ' '); |
|||
char const n[] = "qwertyuiop";//asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
|
|||
std::uniform_int_distribution<int> d(0, sizeof(n) - 2); |
|||
for (char& c: ret) |
|||
c = n[d(s_eng)]; |
|||
return ret; |
|||
} |
|||
|
|||
int dev::fromHex(char _i) |
|||
{ |
|||
if (_i >= '0' && _i <= '9') |
|||
return _i - '0'; |
|||
if (_i >= 'a' && _i <= 'f') |
|||
return _i - 'a' + 10; |
|||
if (_i >= 'A' && _i <= 'F') |
|||
return _i - 'A' + 10; |
|||
throw BadHexCharacter(); |
|||
} |
|||
|
|||
bytes dev::fromHex(std::string const& _s) |
|||
{ |
|||
unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; |
|||
std::vector<uint8_t> ret; |
|||
ret.reserve((_s.size() - s + 1) / 2); |
|||
|
|||
if (_s.size() % 2) |
|||
try |
|||
{ |
|||
ret.push_back(fromHex(_s[s++])); |
|||
} |
|||
catch (...){ ret.push_back(0); } |
|||
for (unsigned i = s; i < _s.size(); i += 2) |
|||
try |
|||
{ |
|||
ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); |
|||
} |
|||
catch (...){ ret.push_back(0); } |
|||
return ret; |
|||
} |
|||
|
|||
bytes dev::asNibbles(std::string const& _s) |
|||
{ |
|||
std::vector<uint8_t> ret; |
|||
ret.reserve(_s.size() * 2); |
|||
for (auto i: _s) |
|||
{ |
|||
ret.push_back(i / 16); |
|||
ret.push_back(i % 16); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
#if 0 |
|||
|
|||
/* Following code is copyright 2012-2014 Luke Dashjr
|
|||
* |
|||
* This program is free software; you can redistribute it and/or modify it |
|||
* under the terms of the standard MIT license. See COPYING for more details. |
|||
*/ |
|||
|
|||
#include <cstdbool> |
|||
#include <cstddef> |
|||
#include <cstdint> |
|||
#include <cstring> |
|||
|
|||
static const int8_t b58digits_map[] = { |
|||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1, |
|||
-1, 9,10,11,12,13,14,15, 16,-1,17,18,19,20,21,-1, |
|||
22,23,24,25,26,27,28,29, 30,31,32,-1,-1,-1,-1,-1, |
|||
-1,33,34,35,36,37,38,39, 40,41,42,43,-1,44,45,46, |
|||
47,48,49,50,51,52,53,54, 55,56,57,-1,-1,-1,-1,-1, |
|||
}; |
|||
|
|||
bool b58tobin(void *bin, size_t *binszp, const char *b58, size_t b58sz) |
|||
{ |
|||
size_t binsz = *binszp; |
|||
const unsigned char *b58u = (void*)b58; |
|||
unsigned char *binu = bin; |
|||
size_t outisz = (binsz + 3) / 4; |
|||
uint32_t outi[outisz]; |
|||
uint64_t t; |
|||
uint32_t c; |
|||
size_t i, j; |
|||
uint8_t bytesleft = binsz % 4; |
|||
uint32_t zeromask = bytesleft ? (0xffffffff << (bytesleft * 8)) : 0; |
|||
unsigned zerocount = 0; |
|||
|
|||
if (!b58sz) |
|||
b58sz = strlen(b58); |
|||
|
|||
memset(outi, 0, outisz * sizeof(*outi)); |
|||
|
|||
// Leading zeros, just count
|
|||
for (i = 0; i < b58sz && !b58digits_map[b58u[i]]; ++i) |
|||
++zerocount; |
|||
|
|||
for ( ; i < b58sz; ++i) |
|||
{ |
|||
if (b58u[i] & 0x80) |
|||
// High-bit set on invalid digit
|
|||
return false; |
|||
if (b58digits_map[b58u[i]] == -1) |
|||
// Invalid base58 digit
|
|||
return false; |
|||
c = (unsigned)b58digits_map[b58u[i]]; |
|||
for (j = outisz; j--; ) |
|||
{ |
|||
t = ((uint64_t)outi[j]) * 58 + c; |
|||
c = (t & 0x3f00000000) >> 32; |
|||
outi[j] = t & 0xffffffff; |
|||
} |
|||
if (c) |
|||
// Output number too big (carry to the next int32)
|
|||
return false; |
|||
if (outi[0] & zeromask) |
|||
// Output number too big (last int32 filled too far)
|
|||
return false; |
|||
} |
|||
|
|||
j = 0; |
|||
switch (bytesleft) { |
|||
case 3: |
|||
*(binu++) = (outi[0] & 0xff0000) >> 16; |
|||
case 2: |
|||
*(binu++) = (outi[0] & 0xff00) >> 8; |
|||
case 1: |
|||
*(binu++) = (outi[0] & 0xff); |
|||
++j; |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
for (; j < outisz; ++j) |
|||
{ |
|||
*(binu++) = (outi[j] >> 0x18) & 0xff; |
|||
*(binu++) = (outi[j] >> 0x10) & 0xff; |
|||
*(binu++) = (outi[j] >> 8) & 0xff; |
|||
*(binu++) = (outi[j] >> 0) & 0xff; |
|||
} |
|||
|
|||
// Count canonical base58 byte count
|
|||
binu = bin; |
|||
for (i = 0; i < binsz; ++i) |
|||
{ |
|||
if (binu[i]) |
|||
break; |
|||
--*binszp; |
|||
} |
|||
*binszp += zerocount; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
static |
|||
bool my_dblsha256(void *hash, const void *data, size_t datasz) |
|||
{ |
|||
uint8_t buf[0x20]; |
|||
return b58_sha256_impl(buf, data, datasz) && b58_sha256_impl(hash, buf, sizeof(buf)); |
|||
} |
|||
|
|||
int b58check(const void *bin, size_t binsz, const char *base58str, size_t b58sz) |
|||
{ |
|||
unsigned char buf[32]; |
|||
const uint8_t *binc = bin; |
|||
unsigned i; |
|||
if (binsz < 4) |
|||
return -4; |
|||
if (!my_dblsha256(buf, bin, binsz - 4)) |
|||
return -2; |
|||
if (memcmp(&binc[binsz - 4], buf, 4)) |
|||
return -1; |
|||
|
|||
// Check number of zeros is correct AFTER verifying checksum (to avoid possibility of accessing base58str beyond the end)
|
|||
for (i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) |
|||
{} // Just finding the end of zeros, nothing to do in loop
|
|||
if (binc[i] == '\0' || base58str[i] == '1') |
|||
return -3; |
|||
|
|||
return binc[0]; |
|||
} |
|||
|
|||
static const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; |
|||
|
|||
bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) |
|||
{ |
|||
const uint8_t *bin = data; |
|||
int carry; |
|||
size_t i, j, high, zcount = 0; |
|||
size_t size; |
|||
|
|||
while (zcount < binsz && !bin[zcount]) |
|||
++zcount; |
|||
|
|||
size = (binsz - zcount) * 138 / 100 + 1; |
|||
uint8_t buf[size]; |
|||
memset(buf, 0, size); |
|||
|
|||
for (i = zcount, high = size - 1; i < binsz; ++i, high = j) |
|||
{ |
|||
for (carry = bin[i], j = size - 1; (j > high) || carry; --j) |
|||
{ |
|||
carry += 256 * buf[j]; |
|||
buf[j] = carry % 58; |
|||
carry /= 58; |
|||
} |
|||
} |
|||
|
|||
for (j = 0; j < size && !buf[j]; ++j); |
|||
|
|||
if (*b58sz <= zcount + size - j) |
|||
{ |
|||
*b58sz = zcount + size - j + 1; |
|||
return false; |
|||
} |
|||
|
|||
if (zcount) |
|||
memset(b58, '1', zcount); |
|||
for (i = zcount; j < size; ++i, ++j) |
|||
b58[i] = b58digits_ordered[buf[j]]; |
|||
b58[i] = '\0'; |
|||
*b58sz = i + 1; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,22 @@ |
|||
/*
|
|||
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 RangeMask.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "RangeMask.h" |
@ -0,0 +1,212 @@ |
|||
/*
|
|||
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 EthereumHost.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <utility> |
|||
#include <vector> |
|||
#include <iostream> |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
class RLPStream; |
|||
|
|||
using UnsignedRange = std::pair<unsigned, unsigned>; |
|||
using UnsignedRanges = std::vector<UnsignedRange>; |
|||
|
|||
template <class T> |
|||
class RangeMask |
|||
{ |
|||
template <class U> friend std::ostream& operator<<(std::ostream& _out, RangeMask<U> const& _r); |
|||
|
|||
public: |
|||
using Range = std::pair<T, T>; |
|||
using Ranges = std::vector<Range>; |
|||
|
|||
RangeMask() {} |
|||
RangeMask(T _begin, T _end): m_all(_begin, _end) {} |
|||
RangeMask(Range const& _c): m_all(_c) {} |
|||
|
|||
RangeMask operator+(RangeMask const& _m) const { return RangeMask(*this) += _m; } |
|||
|
|||
RangeMask lowest(T _items) const |
|||
{ |
|||
RangeMask ret(m_all); |
|||
for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i) |
|||
_items -= (ret.m_ranges[i->first] = std::min(i->first + _items, i->second)) - i->first; |
|||
return ret; |
|||
} |
|||
|
|||
RangeMask operator~() const |
|||
{ |
|||
RangeMask ret(m_all); |
|||
T last = m_all.first; |
|||
for (auto i: m_ranges) |
|||
{ |
|||
if (i.first != last) |
|||
ret.m_ranges[last] = i.first; |
|||
last = i.second; |
|||
} |
|||
if (last != m_all.second) |
|||
ret.m_ranges[last] = m_all.second; |
|||
return ret; |
|||
} |
|||
|
|||
RangeMask& operator+=(RangeMask const& _m) |
|||
{ |
|||
for (auto const& i: _m.m_ranges) |
|||
operator+=(i); |
|||
return *this; |
|||
} |
|||
RangeMask& operator+=(UnsignedRange const& _m) |
|||
{ |
|||
for (auto i = _m.first; i < _m.second;) |
|||
{ |
|||
// for each number, we find the element equal or next lower. this, if any, must contain the value.
|
|||
auto uit = m_ranges.upper_bound(i); |
|||
auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit); |
|||
if (it == m_ranges.end() || it->second < i) |
|||
// lower range is too low to merge.
|
|||
// if the next higher range is too high.
|
|||
if (uit == m_ranges.end() || uit->first > _m.second) |
|||
{ |
|||
// just create a new range
|
|||
m_ranges[i] = _m.second; |
|||
break; |
|||
} |
|||
else |
|||
{ |
|||
if (uit->first == i) |
|||
// move i to end of range
|
|||
i = uit->second; |
|||
else |
|||
{ |
|||
// merge with the next higher range
|
|||
// move i to end of range
|
|||
i = m_ranges[i] = uit->second; |
|||
i = uit->second; |
|||
m_ranges.erase(uit); |
|||
} |
|||
} |
|||
else if (it->second == i) |
|||
{ |
|||
// if the next higher range is too high.
|
|||
if (uit == m_ranges.end() || uit->first > _m.second) |
|||
{ |
|||
// merge with the next lower range
|
|||
m_ranges[it->first] = _m.second; |
|||
break; |
|||
} |
|||
else |
|||
{ |
|||
// merge with both next lower & next higher.
|
|||
i = m_ranges[it->first] = uit->second; |
|||
m_ranges.erase(uit); |
|||
} |
|||
} |
|||
else |
|||
i = it->second; |
|||
} |
|||
return *this; |
|||
} |
|||
|
|||
RangeMask& operator+=(T _i) |
|||
{ |
|||
return operator+=(Range(_i, _i + 1)); |
|||
} |
|||
|
|||
bool contains(T _i) const |
|||
{ |
|||
auto it = m_ranges.upper_bound(_i); |
|||
if (it == m_ranges.begin()) |
|||
return false; |
|||
return (--it)->second > _i; |
|||
} |
|||
|
|||
bool empty() const |
|||
{ |
|||
return m_ranges.empty(); |
|||
} |
|||
|
|||
bool full() const |
|||
{ |
|||
return m_ranges.size() == 1 && m_ranges.begin()->first == m_all.first && m_ranges.begin()->second == m_all.second; |
|||
} |
|||
|
|||
void clear() |
|||
{ |
|||
m_ranges.clear(); |
|||
} |
|||
|
|||
std::pair<T, T> const& all() const { return m_all; } |
|||
|
|||
class const_iterator |
|||
{ |
|||
friend class RangeMask; |
|||
|
|||
public: |
|||
const_iterator() {} |
|||
|
|||
T operator*() const { return m_value; } |
|||
const_iterator& operator++() { if (m_owner) m_value = m_owner->next(m_value); return *this; } |
|||
const_iterator operator++(int) { auto ret = *this; if (m_owner) m_value = m_owner->next(m_value); return ret; } |
|||
|
|||
bool operator==(const_iterator const& _i) const { return m_owner == _i.m_owner && m_value == _i.m_value; } |
|||
bool operator!=(const_iterator const& _i) const { return !operator==(_i); } |
|||
bool operator<(const_iterator const& _i) const { return m_value < _i.m_value; } |
|||
|
|||
private: |
|||
const_iterator(RangeMask const& _m, bool _end): m_owner(&_m), m_value(_m.m_ranges.empty() || _end ? _m.m_all.second : _m.m_ranges.begin()->first) {} |
|||
|
|||
RangeMask const* m_owner = nullptr; |
|||
T m_value = 0; |
|||
}; |
|||
|
|||
const_iterator begin() const { return const_iterator(*this, false); } |
|||
const_iterator end() const { return const_iterator(*this, true); } |
|||
T next(T _t) const |
|||
{ |
|||
_t++; |
|||
// find next item in range at least _t
|
|||
auto uit = m_ranges.upper_bound(_t); // > _t
|
|||
auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit); |
|||
if (it != m_ranges.end() && it->first <= _t && it->second > _t) |
|||
return _t; |
|||
return uit == m_ranges.end() ? m_all.second : uit->first; |
|||
} |
|||
|
|||
private: |
|||
UnsignedRange m_all; |
|||
std::map<T, T> m_ranges; |
|||
}; |
|||
|
|||
template <class T> inline std::ostream& operator<<(std::ostream& _out, RangeMask<T> const& _r) |
|||
{ |
|||
_out << _r.m_all.first << "{ "; |
|||
for (auto const& i: _r.m_ranges) |
|||
_out << "[" << i.first << ", " << i.second << ") "; |
|||
_out << "}" << _r.m_all.second; |
|||
return _out; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,63 @@ |
|||
/*
|
|||
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 Worker.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "Worker.h" |
|||
|
|||
#include <chrono> |
|||
#include <thread> |
|||
#include "Log.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
|
|||
void Worker::startWorking() |
|||
{ |
|||
cdebug << "startWorking for thread" << m_name; |
|||
Guard l(x_work); |
|||
if (m_work) |
|||
return; |
|||
cdebug << "Spawning" << m_name; |
|||
m_stop = false; |
|||
m_work.reset(new thread([&]() |
|||
{ |
|||
setThreadName(m_name.c_str()); |
|||
while (!m_stop) |
|||
{ |
|||
this_thread::sleep_for(chrono::milliseconds(30)); |
|||
doWork(); |
|||
} |
|||
cdebug << "Finishing up worker thread"; |
|||
doneWorking(); |
|||
})); |
|||
} |
|||
|
|||
void Worker::stopWorking() |
|||
{ |
|||
cdebug << "stopWorking for thread" << m_name; |
|||
Guard l(x_work); |
|||
if (!m_work) |
|||
return; |
|||
cdebug << "Stopping" << m_name; |
|||
m_stop = true; |
|||
m_work->join(); |
|||
m_work.reset(); |
|||
cdebug << "Stopped" << m_name; |
|||
} |
|||
|
@ -0,0 +1,59 @@ |
|||
/*
|
|||
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 Worker.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include <thread> |
|||
#include "Guards.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
class Worker |
|||
{ |
|||
protected: |
|||
Worker(std::string const& _name = "anon"): m_name(_name) {} |
|||
|
|||
/// Move-constructor.
|
|||
Worker(Worker&& _m) { std::swap(m_name, _m.m_name); } |
|||
|
|||
/// Move-assignment.
|
|||
Worker& operator=(Worker&& _m) { std::swap(m_name, _m.m_name); return *this; } |
|||
|
|||
virtual ~Worker() { stopWorking(); } |
|||
|
|||
void setName(std::string _n) { if (!isWorking()) m_name = _n; } |
|||
|
|||
void startWorking(); |
|||
void stopWorking(); |
|||
bool isWorking() const { Guard l(x_work); return !!m_work; } |
|||
virtual void doWork() = 0; |
|||
virtual void doneWorking() {} |
|||
|
|||
private: |
|||
mutable Mutex x_work; ///< Lock for the network existance.
|
|||
std::unique_ptr<std::thread> m_work; ///< The network thread.
|
|||
bool m_stop = false; |
|||
std::string m_name; |
|||
}; |
|||
|
|||
} |
@ -0,0 +1,12 @@ |
|||
#ifdef _MSC_VER |
|||
#include "All.h" |
|||
#include "Common.cpp" |
|||
#include "CommonData.cpp" |
|||
#include "CommonIO.cpp" |
|||
#include "FixedHash.cpp" |
|||
#include "Guards.cpp" |
|||
#include "Log.cpp" |
|||
#include "RangeMask.cpp" |
|||
#include "RLP.cpp" |
|||
#include "Worker.cpp" |
|||
#endif |
@ -0,0 +1,9 @@ |
|||
#pragma once |
|||
|
|||
#include "Common.h" |
|||
#include "FileSystem.h" |
|||
#include "MemoryDB.h" |
|||
#include "OverlayDB.h" |
|||
#include "SHA3.h" |
|||
#include "TrieCommon.h" |
|||
#include "TrieDB.h" |
@ -0,0 +1,56 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
|
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
set(EXECUTABLE devcrypto) |
|||
|
|||
# set(CMAKE_INSTALL_PREFIX ../lib) |
|||
if(ETH_STATIC) |
|||
add_library(${EXECUTABLE} STATIC ${SRC_LIST}) |
|||
else() |
|||
add_library(${EXECUTABLE} SHARED ${SRC_LIST}) |
|||
endif() |
|||
|
|||
file(GLOB HEADERS "*.h") |
|||
|
|||
include_directories(..) |
|||
|
|||
target_link_libraries(${EXECUTABLE} devcore) |
|||
target_link_libraries(${EXECUTABLE} secp256k1) |
|||
target_link_libraries(${EXECUTABLE} gmp) |
|||
target_link_libraries(${EXECUTABLE} ${LEVELDB_LS}) |
|||
target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LS}) |
|||
|
|||
if("${TARGET_PLATFORM}" STREQUAL "w64") |
|||
target_link_libraries(${EXECUTABLE} boost_system-mt-s) |
|||
target_link_libraries(${EXECUTABLE} boost_filesystem-mt-s) |
|||
target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) |
|||
target_link_libraries(${EXECUTABLE} iphlpapi) |
|||
target_link_libraries(${EXECUTABLE} ws2_32) |
|||
target_link_libraries(${EXECUTABLE} mswsock) |
|||
target_link_libraries(${EXECUTABLE} shlwapi) |
|||
elseif (APPLE) |
|||
# Latest mavericks boost libraries only come with -mt |
|||
target_link_libraries(${EXECUTABLE} boost_system-mt) |
|||
target_link_libraries(${EXECUTABLE} boost_filesystem-mt) |
|||
target_link_libraries(${EXECUTABLE} boost_thread-mt) |
|||
find_package(Threads REQUIRED) |
|||
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) |
|||
elseif (UNIX) |
|||
find_package(Boost 1.53 REQUIRED COMPONENTS filesystem) |
|||
target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) |
|||
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARY}) |
|||
target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) |
|||
target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARY}) |
|||
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) |
|||
else () |
|||
target_link_libraries(${EXECUTABLE} boost_system) |
|||
target_link_libraries(${EXECUTABLE} boost_filesystem) |
|||
target_link_libraries(${EXECUTABLE} boost_thread) |
|||
find_package(Threads REQUIRED) |
|||
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) |
|||
endif () |
|||
|
|||
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) |
|||
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) |
|||
|
@ -0,0 +1,110 @@ |
|||
/*
|
|||
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 CommonEth.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "Common.h" |
|||
#include <random> |
|||
#include <secp256k1/secp256k1.h> |
|||
#include "SHA3.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
//#define ETH_ADDRESS_DEBUG 1
|
|||
|
|||
Address dev::toAddress(Secret _private) |
|||
{ |
|||
secp256k1_start(); |
|||
|
|||
byte pubkey[65]; |
|||
int pubkeylen = 65; |
|||
int ok = secp256k1_ecdsa_seckey_verify(_private.data()); |
|||
if (!ok) |
|||
return Address(); |
|||
ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, _private.data(), 0); |
|||
assert(pubkeylen == 65); |
|||
if (!ok) |
|||
return Address(); |
|||
ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); |
|||
if (!ok) |
|||
return Address(); |
|||
auto ret = right160(dev::eth::sha3(bytesConstRef(&(pubkey[1]), 64))); |
|||
#if ETH_ADDRESS_DEBUG |
|||
cout << "---- ADDRESS -------------------------------" << endl; |
|||
cout << "SEC: " << _private << endl; |
|||
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl; |
|||
cout << "ADR: " << ret << endl; |
|||
#endif |
|||
return ret; |
|||
} |
|||
|
|||
KeyPair KeyPair::create() |
|||
{ |
|||
secp256k1_start(); |
|||
static std::mt19937_64 s_eng(time(0)); |
|||
std::uniform_int_distribution<uint16_t> d(0, 255); |
|||
|
|||
for (int i = 0; i < 100; ++i) |
|||
{ |
|||
h256 sec; |
|||
for (unsigned i = 0; i < 32; ++i) |
|||
sec[i] = (byte)d(s_eng); |
|||
|
|||
KeyPair ret(sec); |
|||
if (ret.address()) |
|||
return ret; |
|||
} |
|||
return KeyPair(); |
|||
} |
|||
|
|||
KeyPair::KeyPair(h256 _sec): |
|||
m_secret(_sec) |
|||
{ |
|||
int ok = secp256k1_ecdsa_seckey_verify(m_secret.data()); |
|||
if (!ok) |
|||
return; |
|||
|
|||
byte pubkey[65]; |
|||
int pubkeylen = 65; |
|||
ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0); |
|||
if (!ok || pubkeylen != 65) |
|||
return; |
|||
|
|||
ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); |
|||
if (!ok) |
|||
return; |
|||
|
|||
m_secret = m_secret; |
|||
memcpy(m_public.data(), &(pubkey[1]), 64); |
|||
m_address = right160(dev::eth::sha3(bytesConstRef(&(pubkey[1]), 64))); |
|||
|
|||
#if ETH_ADDRESS_DEBUG |
|||
cout << "---- ADDRESS -------------------------------" << endl; |
|||
cout << "SEC: " << m_secret << endl; |
|||
cout << "PUB: " << m_public << endl; |
|||
cout << "ADR: " << m_address << endl; |
|||
#endif |
|||
} |
|||
|
|||
KeyPair KeyPair::fromEncryptedSeed(bytesConstRef _seed, std::string const& _password) |
|||
{ |
|||
return KeyPair(sha3(aesDecrypt(_seed, _password))); |
|||
} |
|||
|
@ -0,0 +1,89 @@ |
|||
/*
|
|||
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 CommonEth.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
* |
|||
* Ethereum-specific data structures & algorithms. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcore/FixedHash.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
/// A secret key: 32 bytes.
|
|||
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
|
|||
using Secret = h256; |
|||
|
|||
/// A public key: 64 bytes.
|
|||
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
|
|||
using Public = h512; |
|||
|
|||
/// An Ethereum address: 20 bytes.
|
|||
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
|
|||
using Address = h160; |
|||
|
|||
/// A vector of Ethereum addresses.
|
|||
using Addresses = h160s; |
|||
|
|||
/// Convert a private key into the public key equivalent.
|
|||
/// @returns 0 if it's not a valid private key.
|
|||
Address toAddress(h256 _private); |
|||
|
|||
/// Simple class that represents a "key pair".
|
|||
/// All of the data of the class can be regenerated from the secret key (m_secret) alone.
|
|||
/// Actually stores a tuplet of secret, public and address (the right 160-bits of the public).
|
|||
class KeyPair |
|||
{ |
|||
public: |
|||
/// Null constructor.
|
|||
KeyPair() {} |
|||
|
|||
/// Normal constructor - populates object from the given secret key.
|
|||
KeyPair(Secret _k); |
|||
|
|||
/// Create a new, randomly generated object.
|
|||
static KeyPair create(); |
|||
|
|||
/// Create from an encrypted seed.
|
|||
static KeyPair fromEncryptedSeed(bytesConstRef _seed, std::string const& _password); |
|||
|
|||
/// Retrieve the secret key.
|
|||
Secret const& secret() const { return m_secret; } |
|||
/// Retrieve the secret key.
|
|||
Secret const& sec() const { return m_secret; } |
|||
|
|||
/// Retrieve the public key.
|
|||
Public const& pub() const { return m_public; } |
|||
|
|||
/// Retrieve the associated address of the public key.
|
|||
Address const& address() const { return m_address; } |
|||
|
|||
bool operator==(KeyPair const& _c) const { return m_secret == _c.m_secret; } |
|||
bool operator!=(KeyPair const& _c) const { return m_secret != _c.m_secret; } |
|||
|
|||
private: |
|||
Secret m_secret; |
|||
Public m_public; |
|||
Address m_address; |
|||
}; |
|||
|
|||
} |
@ -1,43 +1,46 @@ |
|||
#pragma once |
|||
|
|||
#include <libethential/Exceptions.h> |
|||
#include <libdevcore/Exceptions.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
|
|||
class DatabaseAlreadyOpen: public Exception {}; |
|||
class DatabaseAlreadyOpen: public dev::Exception {}; |
|||
|
|||
class NotEnoughCash: public Exception {}; |
|||
class NotEnoughCash: public dev::Exception {}; |
|||
|
|||
class GasPriceTooLow: public Exception {}; |
|||
class BlockGasLimitReached: public Exception {}; |
|||
class NoSuchContract: public Exception {}; |
|||
class ContractAddressCollision: public Exception {}; |
|||
class FeeTooSmall: public Exception {}; |
|||
class TooMuchGasUsed: public Exception {}; |
|||
class ExtraDataTooBig: public Exception {}; |
|||
class InvalidSignature: public Exception {}; |
|||
class InvalidTransactionFormat: public Exception { public: InvalidTransactionFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid transaction format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; |
|||
class InvalidBlockFormat: public Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; |
|||
class InvalidBlockHeaderFormat: public Exception { public: InvalidBlockHeaderFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block header format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; |
|||
class InvalidUnclesHash: public Exception {}; |
|||
class InvalidUncle: public Exception {}; |
|||
class UncleTooOld: public Exception {}; |
|||
class UncleInChain: public Exception {}; |
|||
class DuplicateUncleNonce: public Exception {}; |
|||
class InvalidStateRoot: public Exception {}; |
|||
class InvalidTransactionsHash: public Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual std::string description() const { return "Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref()); } }; |
|||
class InvalidTransaction: public Exception {}; |
|||
class InvalidDifficulty: public Exception {}; |
|||
class InvalidGasLimit: public Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual std::string description() const { return "Invalid gas limit (provided: " + toString(provided) + " valid:" + toString(valid) + ")"; } }; |
|||
class InvalidMinGasPrice: public Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual std::string description() const { return "Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")"; } }; |
|||
class InvalidTransactionGasUsed: public Exception {}; |
|||
class InvalidTransactionStateRoot: public Exception {}; |
|||
class InvalidTimestamp: public Exception {}; |
|||
class InvalidNonce: public Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual std::string description() const { return "Invalid nonce (r: " + toString(required) + " c:" + toString(candidate) + ")"; } }; |
|||
class InvalidBlockNonce: public Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual std::string description() const { return "Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")"; } }; |
|||
class InvalidParentHash: public Exception {}; |
|||
class InvalidNumber: public Exception {}; |
|||
class InvalidContractAddress: public Exception {}; |
|||
class GasPriceTooLow: public dev::Exception {}; |
|||
class BlockGasLimitReached: public dev::Exception {}; |
|||
class NoSuchContract: public dev::Exception {}; |
|||
class ContractAddressCollision: public dev::Exception {}; |
|||
class FeeTooSmall: public dev::Exception {}; |
|||
class TooMuchGasUsed: public dev::Exception {}; |
|||
class ExtraDataTooBig: public dev::Exception {}; |
|||
class InvalidSignature: public dev::Exception {}; |
|||
class InvalidTransactionFormat: public dev::Exception { public: InvalidTransactionFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid transaction format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; |
|||
class InvalidBlockFormat: public dev::Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; |
|||
class InvalidBlockHeaderFormat: public dev::Exception { public: InvalidBlockHeaderFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block header format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; |
|||
class InvalidUnclesHash: public dev::Exception {}; |
|||
class InvalidUncle: public dev::Exception {}; |
|||
class UncleTooOld: public dev::Exception {}; |
|||
class UncleInChain: public dev::Exception { public: UncleInChain(h256Set _uncles, h256 _block): m_uncles(_uncles), m_block(_block) {} h256Set m_uncles; h256 m_block; virtual std::string description() const { return "Uncle in block already mentioned: Uncles " + toString(m_uncles) + " (" + m_block.abridged() + ")"; } }; |
|||
class DuplicateUncleNonce: public dev::Exception {}; |
|||
class InvalidStateRoot: public dev::Exception {}; |
|||
class InvalidTransactionsHash: public dev::Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual std::string description() const { return "Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref()); } }; |
|||
class InvalidTransaction: public dev::Exception {}; |
|||
class InvalidDifficulty: public dev::Exception {}; |
|||
class InvalidGasLimit: public dev::Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual std::string description() const { return "Invalid gas limit (provided: " + toString(provided) + " valid:" + toString(valid) + ")"; } }; |
|||
class InvalidMinGasPrice: public dev::Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual std::string description() const { return "Invalid minimum gas price (provided: " + toString(provided) + " limit:" + toString(limit) + ")"; } }; |
|||
class InvalidTransactionGasUsed: public dev::Exception {}; |
|||
class InvalidTransactionStateRoot: public dev::Exception {}; |
|||
class InvalidTimestamp: public dev::Exception {}; |
|||
class InvalidNonce: public dev::Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual std::string description() const { return "Invalid nonce (r: " + toString(required) + " c:" + toString(candidate) + ")"; } }; |
|||
class InvalidBlockNonce: public dev::Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual std::string description() const { return "Invalid nonce (h: " + toString(h) + " n:" + toString(n) + " d:" + toString(d) + ")"; } }; |
|||
class InvalidParentHash: public dev::Exception {}; |
|||
class InvalidNumber: public dev::Exception {}; |
|||
class InvalidContractAddress: public dev::Exception {}; |
|||
|
|||
} |
|||
} |
|||
|
@ -1,183 +0,0 @@ |
|||
/*
|
|||
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 UPnP.cpp
|
|||
* @authors: |
|||
* Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "UPnP.h" |
|||
|
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
#if ETH_MINIUPNPC |
|||
#include <miniupnpc/miniwget.h> |
|||
#include <miniupnpc/miniupnpc.h> |
|||
#include <miniupnpc/upnpcommands.h> |
|||
#endif |
|||
#include <libethential/Exceptions.h> |
|||
#include <libethential/Common.h> |
|||
#include <libethential/Log.h> |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
UPnP::UPnP() |
|||
{ |
|||
#if ETH_MINIUPNPC |
|||
m_urls.reset(new UPNPUrls); |
|||
m_data.reset(new IGDdatas); |
|||
|
|||
m_ok = false; |
|||
|
|||
struct UPNPDev* devlist; |
|||
struct UPNPDev* dev; |
|||
char* descXML; |
|||
int descXMLsize = 0; |
|||
int upnperror = 0; |
|||
memset(m_urls.get(), 0, sizeof(struct UPNPUrls)); |
|||
memset(m_data.get(), 0, sizeof(struct IGDdatas)); |
|||
devlist = upnpDiscover(2000, NULL/*multicast interface*/, NULL/*minissdpd socket path*/, 0/*sameport*/, 0/*ipv6*/, &upnperror); |
|||
if (devlist) |
|||
{ |
|||
dev = devlist; |
|||
while (dev) |
|||
{ |
|||
if (strstr (dev->st, "InternetGatewayDevice")) |
|||
break; |
|||
dev = dev->pNext; |
|||
} |
|||
if (!dev) |
|||
dev = devlist; /* defaulting to first device */ |
|||
|
|||
cnote << "UPnP device:" << dev->descURL << "[st:" << dev->st << "]"; |
|||
#if MINIUPNPC_API_VERSION >= 9 |
|||
descXML = (char*)miniwget(dev->descURL, &descXMLsize, 0); |
|||
#else |
|||
descXML = (char*)miniwget(dev->descURL, &descXMLsize); |
|||
#endif |
|||
if (descXML) |
|||
{ |
|||
parserootdesc (descXML, descXMLsize, m_data.get()); |
|||
free (descXML); descXML = 0; |
|||
#if MINIUPNPC_API_VERSION >= 9 |
|||
GetUPNPUrls (m_urls.get(), m_data.get(), dev->descURL, 0); |
|||
#else |
|||
GetUPNPUrls (m_urls.get(), m_data.get(), dev->descURL); |
|||
#endif |
|||
m_ok = true; |
|||
} |
|||
freeUPNPDevlist(devlist); |
|||
} |
|||
else |
|||
#endif |
|||
{ |
|||
cnote << "UPnP device not found."; |
|||
throw NoUPnPDevice(); |
|||
} |
|||
} |
|||
|
|||
UPnP::~UPnP() |
|||
{ |
|||
auto r = m_reg; |
|||
for (auto i: r) |
|||
removeRedirect(i); |
|||
} |
|||
|
|||
string UPnP::externalIP() |
|||
{ |
|||
#if ETH_MINIUPNPC |
|||
char addr[16]; |
|||
if (!UPNP_GetExternalIPAddress(m_urls->controlURL, m_data->first.servicetype, addr)) |
|||
return addr; |
|||
else |
|||
#endif |
|||
return "0.0.0.0"; |
|||
} |
|||
|
|||
int UPnP::addRedirect(char const* _addr, int _port) |
|||
{ |
|||
(void)_addr; |
|||
(void)_port; |
|||
#if ETH_MINIUPNPC |
|||
if (m_urls->controlURL[0] == '\0') |
|||
{ |
|||
cwarn << "UPnP::addRedirect() called without proper initialisation?"; |
|||
return -1; |
|||
} |
|||
|
|||
// Try direct mapping first (port external, port internal).
|
|||
char port_str[16]; |
|||
sprintf(port_str, "%d", _port); |
|||
if (!UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, port_str, port_str, _addr, "ethereum", "TCP", NULL, NULL)) |
|||
return _port; |
|||
|
|||
// Failed - now try (random external, port internal) and cycle up to 10 times.
|
|||
for (uint i = 0; i < 10; ++i) |
|||
{ |
|||
_port = rand() % 65535 - 1024 + 1024; |
|||
sprintf(port_str, "%d", _port); |
|||
if (!UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, NULL, port_str, _addr, "ethereum", "TCP", NULL, NULL)) |
|||
return _port; |
|||
} |
|||
|
|||
// Failed. Try asking the router to give us a free external port.
|
|||
if (UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, port_str, NULL, _addr, "ethereum", "TCP", NULL, NULL)) |
|||
// Failed. Exit.
|
|||
return 0; |
|||
|
|||
// We got mapped, but we don't know which ports we got mapped to. Now to find...
|
|||
unsigned num = 0; |
|||
UPNP_GetPortMappingNumberOfEntries(m_urls->controlURL, m_data->first.servicetype, &num); |
|||
for (unsigned i = 0; i < num; ++i) |
|||
{ |
|||
char extPort[16]; |
|||
char intClient[16]; |
|||
char intPort[6]; |
|||
char protocol[4]; |
|||
char desc[80]; |
|||
char enabled[4]; |
|||
char rHost[64]; |
|||
char duration[16]; |
|||
UPNP_GetGenericPortMappingEntry(m_urls->controlURL, m_data->first.servicetype, toString(i).c_str(), extPort, intClient, intPort, protocol, desc, enabled, rHost, duration); |
|||
if (string("ethereum") == desc) |
|||
{ |
|||
m_reg.insert(atoi(extPort)); |
|||
return atoi(extPort); |
|||
} |
|||
} |
|||
cerr << "ERROR: Mapped port not found." << endl; |
|||
#endif |
|||
return 0; |
|||
} |
|||
|
|||
void UPnP::removeRedirect(int _port) |
|||
{ |
|||
(void)_port; |
|||
#if ETH_MINIUPNPC |
|||
char port_str[16]; |
|||
// int t;
|
|||
printf("TB : upnp_rem_redir (%d)\n", _port); |
|||
if (m_urls->controlURL[0] == '\0') |
|||
{ |
|||
printf("TB : the init was not done !\n"); |
|||
return; |
|||
} |
|||
sprintf(port_str, "%d", _port); |
|||
UPNP_DeletePortMapping(m_urls->controlURL, m_data->first.servicetype, port_str, "TCP", NULL); |
|||
m_reg.erase(_port); |
|||
#endif |
|||
} |
@ -0,0 +1,6 @@ |
|||
#ifdef _MSC_VER |
|||
#include "All.h" |
|||
#include "BlockInfo.cpp" |
|||
#include "CommonEth.cpp" |
|||
#include "Dagger.cpp" |
|||
#endif |
@ -1,104 +0,0 @@ |
|||
/*
|
|||
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 Common.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "CommonData.h" |
|||
|
|||
#include <random> |
|||
#include "Exceptions.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
std::string eth::escaped(std::string const& _s, bool _all) |
|||
{ |
|||
std::string ret; |
|||
ret.reserve(_s.size()); |
|||
ret.push_back('"'); |
|||
for (auto i: _s) |
|||
if (i == '"' && !_all) |
|||
ret += "\\\""; |
|||
else if (i == '\\' && !_all) |
|||
ret += "\\\\"; |
|||
else if (i < ' ' || _all) |
|||
{ |
|||
ret += "\\x"; |
|||
ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); |
|||
ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); |
|||
} |
|||
else |
|||
ret.push_back(i); |
|||
ret.push_back('"'); |
|||
return ret; |
|||
} |
|||
|
|||
std::string eth::randomWord() |
|||
{ |
|||
static std::mt19937_64 s_eng(0); |
|||
std::string ret(std::uniform_int_distribution<int>(1, 5)(s_eng), ' '); |
|||
char const n[] = "qwertyuiop";//asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
|
|||
std::uniform_int_distribution<int> d(0, sizeof(n) - 2); |
|||
for (char& c: ret) |
|||
c = n[d(s_eng)]; |
|||
return ret; |
|||
} |
|||
|
|||
int eth::fromHex(char _i) |
|||
{ |
|||
if (_i >= '0' && _i <= '9') |
|||
return _i - '0'; |
|||
if (_i >= 'a' && _i <= 'f') |
|||
return _i - 'a' + 10; |
|||
if (_i >= 'A' && _i <= 'F') |
|||
return _i - 'A' + 10; |
|||
throw BadHexCharacter(); |
|||
} |
|||
|
|||
bytes eth::fromHex(std::string const& _s) |
|||
{ |
|||
uint s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; |
|||
std::vector<uint8_t> ret; |
|||
ret.reserve((_s.size() - s + 1) / 2); |
|||
|
|||
if (_s.size() % 2) |
|||
try |
|||
{ |
|||
ret.push_back(fromHex(_s[s++])); |
|||
} |
|||
catch (...){ ret.push_back(0); } |
|||
for (uint i = s; i < _s.size(); i += 2) |
|||
try |
|||
{ |
|||
ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); |
|||
} |
|||
catch (...){ ret.push_back(0); } |
|||
return ret; |
|||
} |
|||
|
|||
bytes eth::asNibbles(std::string const& _s) |
|||
{ |
|||
std::vector<uint8_t> ret; |
|||
ret.reserve(_s.size() * 2); |
|||
for (auto i: _s) |
|||
{ |
|||
ret.push_back(i / 16); |
|||
ret.push_back(i % 16); |
|||
} |
|||
return ret; |
|||
} |
@ -0,0 +1,75 @@ |
|||
/*
|
|||
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 DownloadMan.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "DownloadMan.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
DownloadSub::DownloadSub(DownloadMan& _man): m_man(&_man) |
|||
{ |
|||
WriteGuard l(m_man->x_subs); |
|||
m_man->m_subs.insert(this); |
|||
} |
|||
|
|||
DownloadSub::~DownloadSub() |
|||
{ |
|||
if (m_man) |
|||
{ |
|||
WriteGuard l(m_man->x_subs); |
|||
m_man->m_subs.erase(this); |
|||
} |
|||
} |
|||
|
|||
h256Set DownloadSub::nextFetch(unsigned _n) |
|||
{ |
|||
Guard l(m_fetch); |
|||
|
|||
if (m_remaining.size()) |
|||
return m_remaining; |
|||
|
|||
m_asked.clear(); |
|||
m_indices.clear(); |
|||
m_remaining.clear(); |
|||
|
|||
if (!m_man || m_man->chain().empty()) |
|||
return h256Set(); |
|||
|
|||
m_asked = (~(m_man->taken() + m_attempted)).lowest(_n); |
|||
if (m_asked.empty()) |
|||
m_asked = (~(m_man->taken(true) + m_attempted)).lowest(_n); |
|||
m_attempted += m_asked; |
|||
for (auto i: m_asked) |
|||
{ |
|||
auto x = m_man->m_chain[i]; |
|||
m_remaining.insert(x); |
|||
m_indices[x] = i; |
|||
} |
|||
return m_remaining; |
|||
} |
|||
|
|||
void DownloadSub::noteBlock(h256 _hash) |
|||
{ |
|||
Guard l(m_fetch); |
|||
if (m_man && m_indices.count(_hash)) |
|||
m_man->m_blocksGot += m_indices[_hash]; |
|||
m_remaining.erase(_hash); |
|||
} |
@ -0,0 +1,147 @@ |
|||
/*
|
|||
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 DownloadMan.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <vector> |
|||
#include <set> |
|||
#include <libdevcore/Guards.h> |
|||
#include <libdevcore/Worker.h> |
|||
#include <libdevcore/RangeMask.h> |
|||
#include <libdevcore/FixedHash.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
class DownloadMan; |
|||
|
|||
class DownloadSub |
|||
{ |
|||
friend class DownloadMan; |
|||
|
|||
public: |
|||
DownloadSub(DownloadMan& _man); |
|||
~DownloadSub(); |
|||
|
|||
/// Finished last fetch - grab the next bunch of block hashes to download.
|
|||
h256Set nextFetch(unsigned _n); |
|||
|
|||
/// Note that we've received a particular block.
|
|||
void noteBlock(h256 _hash); |
|||
|
|||
/// Nothing doing here.
|
|||
void doneFetch() { resetFetch(); } |
|||
|
|||
RangeMask<unsigned> const& asked() const { return m_asked; } |
|||
RangeMask<unsigned> const& attemped() const { return m_attempted; } |
|||
|
|||
private: |
|||
void resetFetch() // Called by DownloadMan when we need to reset the download.
|
|||
{ |
|||
Guard l(m_fetch); |
|||
m_remaining.clear(); |
|||
m_indices.clear(); |
|||
m_asked.clear(); |
|||
m_attempted.clear(); |
|||
} |
|||
|
|||
DownloadMan* m_man = nullptr; |
|||
|
|||
Mutex m_fetch; |
|||
h256Set m_remaining; |
|||
std::map<h256, unsigned> m_indices; |
|||
RangeMask<unsigned> m_asked; |
|||
RangeMask<unsigned> m_attempted; |
|||
}; |
|||
|
|||
class DownloadMan |
|||
{ |
|||
friend class DownloadSub; |
|||
|
|||
public: |
|||
~DownloadMan() |
|||
{ |
|||
for (auto i: m_subs) |
|||
i->m_man = nullptr; |
|||
} |
|||
|
|||
void resetToChain(h256s const& _chain) |
|||
{ |
|||
{ |
|||
ReadGuard l(x_subs); |
|||
for (auto i: m_subs) |
|||
i->resetFetch(); |
|||
} |
|||
m_chain.clear(); |
|||
m_chain.reserve(_chain.size()); |
|||
for (auto i = _chain.rbegin(); i != _chain.rend(); ++i) |
|||
m_chain.push_back(*i); |
|||
m_blocksGot = RangeMask<unsigned>(0, m_chain.size()); |
|||
} |
|||
|
|||
void reset() |
|||
{ |
|||
{ |
|||
ReadGuard l(x_subs); |
|||
for (auto i: m_subs) |
|||
i->resetFetch(); |
|||
} |
|||
m_chain.clear(); |
|||
m_blocksGot.clear(); |
|||
} |
|||
|
|||
RangeMask<unsigned> taken(bool _desperate = false) const |
|||
{ |
|||
auto ret = m_blocksGot; |
|||
if (!_desperate) |
|||
{ |
|||
ReadGuard l(x_subs); |
|||
for (auto i: m_subs) |
|||
ret += i->m_asked; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
bool isComplete() const |
|||
{ |
|||
return m_blocksGot.full(); |
|||
} |
|||
|
|||
h256s chain() const { return m_chain; } |
|||
void foreachSub(std::function<void(DownloadSub const&)> const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); } |
|||
unsigned subCount() const { ReadGuard l(x_subs); return m_subs.size(); } |
|||
RangeMask<unsigned> blocksGot() const { return m_blocksGot; } |
|||
|
|||
private: |
|||
h256s m_chain; |
|||
RangeMask<unsigned> m_blocksGot; |
|||
|
|||
mutable SharedMutex x_subs; |
|||
std::set<DownloadSub*> m_subs; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
@ -0,0 +1,334 @@ |
|||
/*
|
|||
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 EthereumPeer.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "EthereumPeer.h" |
|||
|
|||
#include <chrono> |
|||
#include <libdevcore/Common.h> |
|||
#include <libethcore/Exceptions.h> |
|||
#include <libp2p/Session.h> |
|||
#include "BlockChain.h" |
|||
#include "EthereumHost.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
using namespace p2p; |
|||
|
|||
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] " |
|||
|
|||
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h): |
|||
Capability(_s, _h), |
|||
m_sub(host()->m_man) |
|||
{ |
|||
setGrabbing(Grabbing::State); |
|||
sendStatus(); |
|||
} |
|||
|
|||
EthereumPeer::~EthereumPeer() |
|||
{ |
|||
giveUpOnFetch(); |
|||
} |
|||
|
|||
EthereumHost* EthereumPeer::host() const |
|||
{ |
|||
return static_cast<EthereumHost*>(Capability::hostCapability()); |
|||
} |
|||
|
|||
void EthereumPeer::sendStatus() |
|||
{ |
|||
RLPStream s; |
|||
prep(s); |
|||
s.appendList(6) << StatusPacket |
|||
<< host()->protocolVersion() |
|||
<< host()->networkId() |
|||
<< host()->m_chain.details().totalDifficulty |
|||
<< host()->m_chain.currentHash() |
|||
<< host()->m_chain.genesisHash(); |
|||
sealAndSend(s); |
|||
} |
|||
|
|||
void EthereumPeer::startInitialSync() |
|||
{ |
|||
// Grab transactions off them.
|
|||
{ |
|||
RLPStream s; |
|||
prep(s).appendList(1); |
|||
s << GetTransactionsPacket; |
|||
sealAndSend(s); |
|||
} |
|||
|
|||
host()->noteHavePeerState(this); |
|||
} |
|||
|
|||
void EthereumPeer::tryGrabbingHashChain() |
|||
{ |
|||
// if already done this, then ignore.
|
|||
if (m_grabbing != Grabbing::State) |
|||
{ |
|||
clogS(NetAllDetail) << "Already synced with this peer."; |
|||
return; |
|||
} |
|||
|
|||
h256 c = host()->m_chain.currentHash(); |
|||
unsigned n = host()->m_chain.number(); |
|||
u256 td = host()->m_chain.details().totalDifficulty; |
|||
|
|||
clogS(NetAllDetail) << "Attempt chain-grab? Latest:" << c.abridged() << ", number:" << n << ", TD:" << td << " versus " << m_totalDifficulty; |
|||
if (td >= m_totalDifficulty) |
|||
{ |
|||
clogS(NetAllDetail) << "No. Our chain is better."; |
|||
m_grabbing = Grabbing::Nothing; |
|||
return; // All good - we have the better chain.
|
|||
} |
|||
|
|||
// Our chain isn't better - grab theirs.
|
|||
{ |
|||
clogS(NetAllDetail) << "Yes. Their chain is better."; |
|||
|
|||
host()->updateGrabbing(Grabbing::Hashes); |
|||
m_grabbing = Grabbing::Hashes; |
|||
RLPStream s; |
|||
prep(s).appendList(3); |
|||
s << GetBlockHashesPacket << m_latestHash << c_maxHashesAsk; |
|||
m_neededBlocks = h256s(1, m_latestHash); |
|||
sealAndSend(s); |
|||
} |
|||
} |
|||
|
|||
void EthereumPeer::giveUpOnFetch() |
|||
{ |
|||
clogS(NetNote) << "GIVE UP FETCH"; |
|||
|
|||
// a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry.
|
|||
if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) |
|||
{ |
|||
host()->noteDoneBlocks(this); |
|||
setGrabbing(Grabbing::Nothing); |
|||
} |
|||
|
|||
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
|
|||
m_sub.doneFetch(); |
|||
} |
|||
|
|||
bool EthereumPeer::interpret(RLP const& _r) |
|||
{ |
|||
switch (_r[0].toInt<unsigned>()) |
|||
{ |
|||
case StatusPacket: |
|||
{ |
|||
m_protocolVersion = _r[1].toInt<unsigned>(); |
|||
m_networkId = _r[2].toInt<u256>(); |
|||
m_totalDifficulty = _r[3].toInt<u256>(); |
|||
m_latestHash = _r[4].toHash<h256>(); |
|||
auto genesisHash = _r[5].toHash<h256>(); |
|||
|
|||
clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged(); |
|||
|
|||
if (genesisHash != host()->m_chain.genesisHash()) |
|||
disable("Invalid genesis hash"); |
|||
if (m_protocolVersion != host()->protocolVersion()) |
|||
disable("Invalid protocol version."); |
|||
if (m_networkId != host()->networkId()) |
|||
disable("Invalid network identifier."); |
|||
|
|||
startInitialSync(); |
|||
break; |
|||
} |
|||
case GetTransactionsPacket: |
|||
{ |
|||
m_requireTransactions = true; |
|||
break; |
|||
} |
|||
case TransactionsPacket: |
|||
{ |
|||
clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; |
|||
addRating(_r.itemCount() - 1); |
|||
lock_guard<recursive_mutex> l(host()->m_incomingLock); |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
host()->addIncomingTransaction(_r[i].data().toBytes()); |
|||
|
|||
lock_guard<mutex> l(x_knownTransactions); |
|||
m_knownTransactions.insert(sha3(_r[i].data())); |
|||
} |
|||
break; |
|||
} |
|||
case GetBlockHashesPacket: |
|||
{ |
|||
h256 later = _r[1].toHash<h256>(); |
|||
unsigned limit = _r[2].toInt<unsigned>(); |
|||
clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")"; |
|||
|
|||
unsigned c = min<unsigned>(host()->m_chain.number(later), limit); |
|||
|
|||
RLPStream s; |
|||
prep(s).appendList(1 + c).append(BlockHashesPacket); |
|||
h256 p = host()->m_chain.details(later).parent; |
|||
for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent) |
|||
s << p; |
|||
sealAndSend(s); |
|||
break; |
|||
} |
|||
case BlockHashesPacket: |
|||
{ |
|||
clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); |
|||
|
|||
if (m_grabbing != Grabbing::Hashes) |
|||
{ |
|||
cwarn << "Peer giving us hashes when we didn't ask for them."; |
|||
break; |
|||
} |
|||
if (_r.itemCount() == 1) |
|||
{ |
|||
host()->noteHaveChain(this); |
|||
return true; |
|||
} |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
auto h = _r[i].toHash<h256>(); |
|||
if (host()->m_chain.isKnown(h)) |
|||
{ |
|||
host()->noteHaveChain(this); |
|||
return true; |
|||
} |
|||
else |
|||
m_neededBlocks.push_back(h); |
|||
} |
|||
// run through - ask for more.
|
|||
RLPStream s; |
|||
prep(s).appendList(3); |
|||
s << GetBlockHashesPacket << m_neededBlocks.back() << c_maxHashesAsk; |
|||
sealAndSend(s); |
|||
break; |
|||
} |
|||
case GetBlocksPacket: |
|||
{ |
|||
clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << "entries)"; |
|||
// return the requested blocks.
|
|||
bytes rlp; |
|||
unsigned n = 0; |
|||
for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i) |
|||
{ |
|||
auto b = host()->m_chain.block(_r[i].toHash<h256>()); |
|||
if (b.size()) |
|||
{ |
|||
rlp += b; |
|||
++n; |
|||
} |
|||
} |
|||
RLPStream s; |
|||
sealAndSend(prep(s).appendList(n + 1).append(BlocksPacket).appendRaw(rlp, n)); |
|||
break; |
|||
} |
|||
case BlocksPacket: |
|||
{ |
|||
clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); |
|||
|
|||
if (_r.itemCount() == 1) |
|||
{ |
|||
// Couldn't get any from last batch - probably got to this peer's latest block - just give up.
|
|||
if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) |
|||
giveUpOnFetch(); |
|||
break; |
|||
} |
|||
|
|||
unsigned used = 0; |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
auto h = BlockInfo::headerHash(_r[i].data()); |
|||
m_sub.noteBlock(h); |
|||
if (host()->noteBlock(h, _r[i].data())) |
|||
used++; |
|||
Guard l(x_knownBlocks); |
|||
m_knownBlocks.insert(h); |
|||
} |
|||
addRating(used); |
|||
unsigned knownParents = 0; |
|||
unsigned unknownParents = 0; |
|||
if (g_logVerbosity >= NetMessageSummary::verbosity) |
|||
{ |
|||
unsigned ic = _r.itemCount(); |
|||
for (unsigned i = 1; i < ic; ++i) |
|||
{ |
|||
auto h = BlockInfo::headerHash(_r[i].data()); |
|||
BlockInfo bi(_r[i].data()); |
|||
Guard l(x_knownBlocks); |
|||
if (!host()->m_chain.details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) |
|||
{ |
|||
unknownParents++; |
|||
clogS(NetAllDetail) << "Unknown parent" << bi.parentHash.abridged() << "of block" << h.abridged(); |
|||
} |
|||
else |
|||
{ |
|||
knownParents++; |
|||
clogS(NetAllDetail) << "Known parent" << bi.parentHash.abridged() << "of block" << h.abridged(); |
|||
} |
|||
} |
|||
} |
|||
clogS(NetMessageSummary) << dec << knownParents << "known parents," << unknownParents << "unknown," << used << "used."; |
|||
if (m_grabbing == Grabbing::Chain || m_grabbing == Grabbing::ChainHelper) |
|||
continueGettingChain(); |
|||
break; |
|||
} |
|||
default: |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
void EthereumPeer::ensureGettingChain() |
|||
{ |
|||
if (m_grabbing == Grabbing::ChainHelper) |
|||
return; // Already asked & waiting for some.
|
|||
|
|||
// Switch to ChainHelper otherwise, unless we're already the Chain grabber.
|
|||
if (m_grabbing != Grabbing::Chain) |
|||
setGrabbing(Grabbing::ChainHelper); |
|||
|
|||
continueGettingChain(); |
|||
} |
|||
|
|||
void EthereumPeer::continueGettingChain() |
|||
{ |
|||
// If we're getting the hashes already, then we shouldn't be asking for the chain.
|
|||
if (m_grabbing == Grabbing::Hashes) |
|||
return; |
|||
|
|||
auto blocks = m_sub.nextFetch(c_maxBlocksAsk); |
|||
|
|||
if (blocks.size()) |
|||
{ |
|||
RLPStream s; |
|||
prep(s); |
|||
s.appendList(blocks.size() + 1) << GetBlocksPacket; |
|||
for (auto const& i: blocks) |
|||
s << i; |
|||
sealAndSend(s); |
|||
} |
|||
else |
|||
giveUpOnFetch(); |
|||
} |
|||
|
|||
void EthereumPeer::setGrabbing(Grabbing _g) |
|||
{ |
|||
m_grabbing = _g; |
|||
session()->addNote("grab", _g == Grabbing::Nothing ? "nothing" : _g == Grabbing::State ? "state" : _g == Grabbing::Hashes ? "hashes" : _g == Grabbing::Chain ? "chain" : _g == Grabbing::ChainHelper ? "chainhelper" : "?"); |
|||
} |
@ -0,0 +1,96 @@ |
|||
/*
|
|||
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 EthereumPeer.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <mutex> |
|||
#include <array> |
|||
#include <set> |
|||
#include <memory> |
|||
#include <utility> |
|||
#include <libdevcore/RLP.h> |
|||
#include <libdevcore/Guards.h> |
|||
#include <libdevcore/RangeMask.h> |
|||
#include <libethcore/CommonEth.h> |
|||
#include <libp2p/Capability.h> |
|||
#include "CommonNet.h" |
|||
#include "DownloadMan.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
|
|||
/**
|
|||
* @brief The EthereumPeer class |
|||
* @todo Document fully. |
|||
*/ |
|||
class EthereumPeer: public p2p::Capability |
|||
{ |
|||
friend class EthereumHost; |
|||
|
|||
public: |
|||
EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h); |
|||
virtual ~EthereumPeer(); |
|||
|
|||
static std::string name() { return "eth"; } |
|||
|
|||
EthereumHost* host() const; |
|||
|
|||
private: |
|||
virtual bool interpret(RLP const& _r); |
|||
|
|||
void sendStatus(); |
|||
void startInitialSync(); |
|||
|
|||
void tryGrabbingHashChain(); |
|||
|
|||
/// Ensure that we are waiting for a bunch of blocks from our peer.
|
|||
void ensureGettingChain(); |
|||
/// Ensure that we are waiting for a bunch of blocks from our peer.
|
|||
void continueGettingChain(); |
|||
|
|||
void giveUpOnFetch(); |
|||
|
|||
void clearKnownTransactions() { std::lock_guard<std::mutex> l(x_knownTransactions); m_knownTransactions.clear(); } |
|||
void setGrabbing(Grabbing _g); |
|||
|
|||
unsigned m_protocolVersion; |
|||
u256 m_networkId; |
|||
|
|||
Grabbing m_grabbing; |
|||
|
|||
h256 m_latestHash; ///< Peer's latest block's hash.
|
|||
u256 m_totalDifficulty; ///< Peer's latest block's total difficulty.
|
|||
h256s m_neededBlocks; ///< The blocks that we should download from this peer.
|
|||
|
|||
bool m_requireTransactions; |
|||
|
|||
Mutex x_knownBlocks; |
|||
std::set<h256> m_knownBlocks; |
|||
std::set<h256> m_knownTransactions; |
|||
std::mutex x_knownTransactions; |
|||
|
|||
DownloadSub m_sub; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,628 +0,0 @@ |
|||
/*
|
|||
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 EthereumSession.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "EthereumSession.h" |
|||
|
|||
#include <chrono> |
|||
#include <libethential/Common.h> |
|||
#include <libethcore/Exceptions.h> |
|||
#include "BlockChain.h" |
|||
#include "EthereumHost.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
#define clogS(X) eth::LogOutputStream<X, true>(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " |
|||
|
|||
EthereumSession::EthereumSession(EthereumHost* _s, bi::tcp::socket _socket, u256 _rNId, bi::address _peerAddress, unsigned short _peerPort): |
|||
m_server(_s), |
|||
m_socket(std::move(_socket)), |
|||
m_reqNetworkId(_rNId), |
|||
m_listenPort(_peerPort), |
|||
m_rating(0) |
|||
{ |
|||
m_disconnect = std::chrono::steady_clock::time_point::max(); |
|||
m_connect = std::chrono::steady_clock::now(); |
|||
m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0)}); |
|||
} |
|||
|
|||
EthereumSession::~EthereumSession() |
|||
{ |
|||
giveUpOnFetch(); |
|||
|
|||
// Read-chain finished for one reason or another.
|
|||
try |
|||
{ |
|||
if (m_socket.is_open()) |
|||
m_socket.close(); |
|||
} |
|||
catch (...){} |
|||
} |
|||
|
|||
string toString(h256s const& _bs) |
|||
{ |
|||
ostringstream out; |
|||
out << "[ "; |
|||
for (auto i: _bs) |
|||
out << i.abridged() << ", "; |
|||
out << "]"; |
|||
return out.str(); |
|||
} |
|||
|
|||
void EthereumSession::giveUpOnFetch() |
|||
{ |
|||
clogS(NetNote) << "GIVE UP FETCH; can't get " << toString(m_askedBlocks); |
|||
if (m_askedBlocks.size()) |
|||
{ |
|||
Guard l (m_server->x_blocksNeeded); |
|||
m_server->m_blocksNeeded.reserve(m_server->m_blocksNeeded.size() + m_askedBlocks.size()); |
|||
for (auto i: m_askedBlocks) |
|||
{ |
|||
m_server->m_blocksOnWay.erase(i); |
|||
m_server->m_blocksNeeded.push_back(i); |
|||
} |
|||
m_askedBlocks.clear(); |
|||
} |
|||
} |
|||
|
|||
bi::tcp::endpoint EthereumSession::endpoint() const |
|||
{ |
|||
if (m_socket.is_open()) |
|||
try |
|||
{ |
|||
return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_listenPort); |
|||
} |
|||
catch (...){} |
|||
|
|||
return bi::tcp::endpoint(); |
|||
} |
|||
|
|||
bool EthereumSession::interpret(RLP const& _r) |
|||
{ |
|||
clogS(NetRight) << _r; |
|||
switch (_r[0].toInt<unsigned>()) |
|||
{ |
|||
case HelloPacket: |
|||
{ |
|||
m_protocolVersion = _r[1].toInt<uint>(); |
|||
m_networkId = _r[2].toInt<u256>(); |
|||
auto clientVersion = _r[3].toString(); |
|||
m_caps = _r[4].toInt<uint>(); |
|||
m_listenPort = _r[5].toInt<unsigned short>(); |
|||
m_id = _r[6].toHash<h512>(); |
|||
m_totalDifficulty = _r[7].toInt<u256>(); |
|||
m_latestHash = _r[8].toHash<h256>(); |
|||
|
|||
clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << m_id.abridged() << showbase << hex << m_caps << dec << m_listenPort; |
|||
|
|||
if (m_server->havePeer(m_id)) |
|||
{ |
|||
// Already connected.
|
|||
cwarn << "Already have peer id" << m_id.abridged();// << "at" << l->endpoint() << "rather than" << endpoint();
|
|||
disconnect(DuplicatePeer); |
|||
return false; |
|||
} |
|||
|
|||
if (m_protocolVersion != EthereumHost::protocolVersion() || m_networkId != m_server->networkId() || !m_id) |
|||
{ |
|||
disconnect(IncompatibleProtocol); |
|||
return false; |
|||
} |
|||
try |
|||
{ m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), m_listenPort, std::chrono::steady_clock::duration()}); } |
|||
catch (...) |
|||
{ |
|||
disconnect(BadProtocol); |
|||
return false; |
|||
} |
|||
|
|||
m_server->registerPeer(shared_from_this()); |
|||
startInitialSync(); |
|||
|
|||
// Grab trsansactions off them.
|
|||
{ |
|||
RLPStream s; |
|||
prep(s).appendList(1); |
|||
s << GetTransactionsPacket; |
|||
sealAndSend(s); |
|||
} |
|||
break; |
|||
} |
|||
case DisconnectPacket: |
|||
{ |
|||
string reason = "Unspecified"; |
|||
if (_r[1].isInt()) |
|||
reason = reasonOf((DisconnectReason)_r[1].toInt<int>()); |
|||
|
|||
clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")"; |
|||
if (m_socket.is_open()) |
|||
clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); |
|||
else |
|||
clogS(NetNote) << "Remote closed."; |
|||
m_socket.close(); |
|||
return false; |
|||
} |
|||
case PingPacket: |
|||
{ |
|||
clogS(NetTriviaSummary) << "Ping"; |
|||
RLPStream s; |
|||
sealAndSend(prep(s).appendList(1) << PongPacket); |
|||
break; |
|||
} |
|||
case PongPacket: |
|||
m_info.lastPing = std::chrono::steady_clock::now() - m_ping; |
|||
clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_info.lastPing).count() << " ms"; |
|||
break; |
|||
case GetPeersPacket: |
|||
{ |
|||
clogS(NetTriviaSummary) << "GetPeers"; |
|||
auto peers = m_server->potentialPeers(); |
|||
RLPStream s; |
|||
prep(s).appendList(peers.size() + 1); |
|||
s << PeersPacket; |
|||
for (auto i: peers) |
|||
{ |
|||
clogS(NetTriviaDetail) << "Sending peer " << i.first.abridged() << i.second; |
|||
s.appendList(3) << bytesConstRef(i.second.address().to_v4().to_bytes().data(), 4) << i.second.port() << i.first; |
|||
} |
|||
sealAndSend(s); |
|||
break; |
|||
} |
|||
case PeersPacket: |
|||
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
bi::address_v4 peerAddress(_r[i][0].toHash<FixedHash<4>>().asArray()); |
|||
auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt<short>()); |
|||
Public id = _r[i][2].toHash<Public>(); |
|||
if (isPrivateAddress(peerAddress)) |
|||
goto CONTINUE; |
|||
|
|||
clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")"; |
|||
|
|||
// check that it's not us or one we already know:
|
|||
if (id && (m_server->m_key.pub() == id || m_server->havePeer(id) || m_server->m_incomingPeers.count(id))) |
|||
goto CONTINUE; |
|||
|
|||
// check that we're not already connected to addr:
|
|||
if (!ep.port()) |
|||
goto CONTINUE; |
|||
for (auto i: m_server->m_addresses) |
|||
if (ep.address() == i && ep.port() == m_server->listenPort()) |
|||
goto CONTINUE; |
|||
for (auto i: m_server->m_incomingPeers) |
|||
if (i.second.first == ep) |
|||
goto CONTINUE; |
|||
m_server->m_incomingPeers[id] = make_pair(ep, 0); |
|||
m_server->m_freePeers.push_back(id); |
|||
m_server->noteNewPeers(); |
|||
clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id << ")"; |
|||
CONTINUE:; |
|||
} |
|||
break; |
|||
case TransactionsPacket: |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << " entries)"; |
|||
m_rating += _r.itemCount() - 1; |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
m_server->m_incomingTransactions.push_back(_r[i].data().toBytes()); |
|||
m_knownTransactions.insert(sha3(_r[i].data())); |
|||
} |
|||
break; |
|||
case GetBlockHashesPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
h256 later = _r[1].toHash<h256>(); |
|||
unsigned limit = _r[2].toInt<unsigned>(); |
|||
clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries, " << later.abridged() << ")"; |
|||
|
|||
unsigned c = min<unsigned>(m_server->m_chain->number(later), limit); |
|||
|
|||
RLPStream s; |
|||
prep(s).appendList(1 + c).append(BlockHashesPacket); |
|||
h256 p = m_server->m_chain->details(later).parent; |
|||
for (unsigned i = 0; i < c; ++i, p = m_server->m_chain->details(p).parent) |
|||
s << p; |
|||
sealAndSend(s); |
|||
break; |
|||
} |
|||
case BlockHashesPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << " entries)"; |
|||
if (_r.itemCount() == 1) |
|||
{ |
|||
m_server->noteHaveChain(shared_from_this()); |
|||
return true; |
|||
} |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
auto h = _r[i].toHash<h256>(); |
|||
if (m_server->m_chain->details(h)) |
|||
{ |
|||
m_server->noteHaveChain(shared_from_this()); |
|||
return true; |
|||
} |
|||
else |
|||
m_neededBlocks.push_back(h); |
|||
} |
|||
// run through - ask for more.
|
|||
RLPStream s; |
|||
prep(s).appendList(3); |
|||
s << GetBlockHashesPacket << m_neededBlocks.back() << c_maxHashesAsk; |
|||
sealAndSend(s); |
|||
break; |
|||
} |
|||
case GetBlocksPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << " entries)"; |
|||
// TODO: return the requested blocks.
|
|||
bytes rlp; |
|||
unsigned n = 0; |
|||
for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i) |
|||
{ |
|||
auto b = m_server->m_chain->block(_r[i].toHash<h256>()); |
|||
if (b.size()) |
|||
{ |
|||
rlp += b; |
|||
++n; |
|||
} |
|||
} |
|||
RLPStream s; |
|||
sealAndSend(prep(s).appendList(n + 1).append(BlocksPacket).appendRaw(rlp, n)); |
|||
break; |
|||
} |
|||
case BlocksPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << " entries)"; |
|||
|
|||
if (_r.itemCount() == 1) |
|||
{ |
|||
// Couldn't get any from last batch - probably got to this peer's latest block - just give up.
|
|||
giveUpOnFetch(); |
|||
break; |
|||
} |
|||
|
|||
unsigned used = 0; |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
auto h = sha3(_r[i].data()); |
|||
if (m_server->noteBlock(h, _r[i].data())) |
|||
used++; |
|||
m_askedBlocks.erase(h); |
|||
m_knownBlocks.insert(h); |
|||
} |
|||
m_rating += used; |
|||
unsigned knownParents = 0; |
|||
unsigned unknownParents = 0; |
|||
if (g_logVerbosity >= NetMessageSummary::verbosity) |
|||
{ |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
auto h = sha3(_r[i].data()); |
|||
BlockInfo bi(_r[i].data()); |
|||
if (!m_server->m_chain->details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) |
|||
{ |
|||
unknownParents++; |
|||
clogS(NetAllDetail) << "Unknown parent " << bi.parentHash << " of block " << h; |
|||
} |
|||
else |
|||
{ |
|||
knownParents++; |
|||
clogS(NetAllDetail) << "Known parent " << bi.parentHash << " of block " << h; |
|||
} |
|||
} |
|||
} |
|||
clogS(NetMessageSummary) << dec << knownParents << " known parents, " << unknownParents << "unknown, " << used << "used."; |
|||
ensureGettingChain(); |
|||
break; |
|||
} |
|||
case GetTransactionsPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
m_requireTransactions = true; |
|||
break; |
|||
} |
|||
default: |
|||
break; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
void EthereumSession::ensureGettingChain() |
|||
{ |
|||
if (!m_askedBlocks.size()) |
|||
m_askedBlocks = m_server->neededBlocks(); |
|||
|
|||
if (m_askedBlocks.size()) |
|||
{ |
|||
RLPStream s; |
|||
prep(s); |
|||
s.appendList(m_askedBlocks.size() + 1) << GetBlocksPacket; |
|||
for (auto i: m_askedBlocks) |
|||
s << i; |
|||
sealAndSend(s); |
|||
} |
|||
else |
|||
clogS(NetMessageSummary) << "No blocks left to get."; |
|||
} |
|||
|
|||
void EthereumSession::ping() |
|||
{ |
|||
RLPStream s; |
|||
sealAndSend(prep(s).appendList(1) << PingPacket); |
|||
m_ping = std::chrono::steady_clock::now(); |
|||
} |
|||
|
|||
void EthereumSession::getPeers() |
|||
{ |
|||
RLPStream s; |
|||
sealAndSend(prep(s).appendList(1) << GetPeersPacket); |
|||
} |
|||
|
|||
RLPStream& EthereumSession::prep(RLPStream& _s) |
|||
{ |
|||
return _s.appendRaw(bytes(8, 0)); |
|||
} |
|||
|
|||
void EthereumSession::sealAndSend(RLPStream& _s) |
|||
{ |
|||
bytes b; |
|||
_s.swapOut(b); |
|||
m_server->seal(b); |
|||
sendDestroy(b); |
|||
} |
|||
|
|||
bool EthereumSession::checkPacket(bytesConstRef _msg) |
|||
{ |
|||
if (_msg.size() < 8) |
|||
return false; |
|||
if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91)) |
|||
return false; |
|||
uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7]; |
|||
if (_msg.size() != len + 8) |
|||
return false; |
|||
RLP r(_msg.cropped(8)); |
|||
if (r.actualSize() != len) |
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
void EthereumSession::sendDestroy(bytes& _msg) |
|||
{ |
|||
clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); |
|||
|
|||
if (!checkPacket(bytesConstRef(&_msg))) |
|||
{ |
|||
cwarn << "INVALID PACKET CONSTRUCTED!"; |
|||
} |
|||
|
|||
bytes buffer = bytes(std::move(_msg)); |
|||
writeImpl(buffer); |
|||
} |
|||
|
|||
void EthereumSession::send(bytesConstRef _msg) |
|||
{ |
|||
clogS(NetLeft) << RLP(_msg.cropped(8)); |
|||
|
|||
if (!checkPacket(_msg)) |
|||
{ |
|||
cwarn << "INVALID PACKET CONSTRUCTED!"; |
|||
} |
|||
|
|||
bytes buffer = bytes(_msg.toBytes()); |
|||
writeImpl(buffer); |
|||
} |
|||
|
|||
void EthereumSession::writeImpl(bytes& _buffer) |
|||
{ |
|||
// cerr << (void*)this << " writeImpl" << endl;
|
|||
if (!m_socket.is_open()) |
|||
return; |
|||
|
|||
lock_guard<recursive_mutex> l(m_writeLock); |
|||
m_writeQueue.push_back(_buffer); |
|||
if (m_writeQueue.size() == 1) |
|||
write(); |
|||
} |
|||
|
|||
void EthereumSession::write() |
|||
{ |
|||
// cerr << (void*)this << " write" << endl;
|
|||
lock_guard<recursive_mutex> l(m_writeLock); |
|||
if (m_writeQueue.empty()) |
|||
return; |
|||
|
|||
const bytes& bytes = m_writeQueue[0]; |
|||
auto self(shared_from_this()); |
|||
ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/) |
|||
{ |
|||
// cerr << (void*)this << " write.callback" << endl;
|
|||
|
|||
// must check queue, as write callback can occur following dropped()
|
|||
if (ec) |
|||
{ |
|||
cwarn << "Error sending: " << ec.message(); |
|||
dropped(); |
|||
} |
|||
else |
|||
{ |
|||
m_writeQueue.pop_front(); |
|||
write(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
void EthereumSession::dropped() |
|||
{ |
|||
// cerr << (void*)this << " dropped" << endl;
|
|||
if (m_socket.is_open()) |
|||
try |
|||
{ |
|||
clogS(NetConnect) << "Closing " << m_socket.remote_endpoint(); |
|||
m_socket.close(); |
|||
} |
|||
catch (...) {} |
|||
} |
|||
|
|||
void EthereumSession::disconnect(int _reason) |
|||
{ |
|||
clogS(NetConnect) << "Disconnecting (reason:" << reasonOf((DisconnectReason)_reason) << ")"; |
|||
if (m_socket.is_open()) |
|||
{ |
|||
if (m_disconnect == chrono::steady_clock::time_point::max()) |
|||
{ |
|||
RLPStream s; |
|||
prep(s); |
|||
s.appendList(2) << DisconnectPacket << _reason; |
|||
sealAndSend(s); |
|||
m_disconnect = chrono::steady_clock::now(); |
|||
} |
|||
else |
|||
dropped(); |
|||
} |
|||
} |
|||
|
|||
void EthereumSession::start() |
|||
{ |
|||
RLPStream s; |
|||
prep(s); |
|||
s.appendList(9) << HelloPacket |
|||
<< (uint)EthereumHost::protocolVersion() |
|||
<< m_server->networkId() |
|||
<< m_server->m_clientVersion |
|||
<< (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0) |
|||
<< m_server->m_public.port() |
|||
<< m_server->m_key.pub() |
|||
<< m_server->m_chain->details().totalDifficulty |
|||
<< m_server->m_chain->currentHash(); |
|||
sealAndSend(s); |
|||
ping(); |
|||
getPeers(); |
|||
|
|||
doRead(); |
|||
} |
|||
|
|||
void EthereumSession::startInitialSync() |
|||
{ |
|||
h256 c = m_server->m_chain->currentHash(); |
|||
uint n = m_server->m_chain->number(); |
|||
u256 td = max(m_server->m_chain->details().totalDifficulty, m_server->m_totalDifficultyOfNeeded); |
|||
|
|||
clogS(NetAllDetail) << "Initial sync. Latest:" << c.abridged() << ", number:" << n << ", TD: max(" << m_server->m_chain->details().totalDifficulty << "," << m_server->m_totalDifficultyOfNeeded << ") versus " << m_totalDifficulty; |
|||
if (td > m_totalDifficulty) |
|||
return; // All good - we have the better chain.
|
|||
|
|||
// Our chain isn't better - grab theirs.
|
|||
RLPStream s; |
|||
prep(s).appendList(3); |
|||
s << GetBlockHashesPacket << m_latestHash << c_maxHashesAsk; |
|||
m_neededBlocks = h256s(1, m_latestHash); |
|||
sealAndSend(s); |
|||
} |
|||
|
|||
void EthereumSession::doRead() |
|||
{ |
|||
// ignore packets received while waiting to disconnect
|
|||
if (chrono::steady_clock::now() - m_disconnect > chrono::seconds(0)) |
|||
return; |
|||
|
|||
auto self(shared_from_this()); |
|||
m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) |
|||
{ |
|||
// If error is end of file, ignore
|
|||
if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) |
|||
{ |
|||
// got here with length of 1241...
|
|||
cwarn << "Error reading: " << ec.message(); |
|||
dropped(); |
|||
} |
|||
else if (ec && length == 0) |
|||
{ |
|||
return; |
|||
} |
|||
else |
|||
{ |
|||
try |
|||
{ |
|||
m_incoming.resize(m_incoming.size() + length); |
|||
memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); |
|||
while (m_incoming.size() > 8) |
|||
{ |
|||
if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) |
|||
{ |
|||
cwarn << "INVALID SYNCHRONISATION TOKEN; expected = 22400891; received = " << toHex(bytesConstRef(m_incoming.data(), 4)); |
|||
disconnect(BadProtocol); |
|||
return; |
|||
} |
|||
else |
|||
{ |
|||
uint32_t len = fromBigEndian<uint32_t>(bytesConstRef(m_incoming.data() + 4, 4)); |
|||
uint32_t tlen = len + 8; |
|||
if (m_incoming.size() < tlen) |
|||
break; |
|||
|
|||
// enough has come in.
|
|||
auto data = bytesConstRef(m_incoming.data(), tlen); |
|||
if (!checkPacket(data)) |
|||
{ |
|||
cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; |
|||
cwarn << "INVALID MESSAGE RECEIVED"; |
|||
disconnect(BadProtocol); |
|||
return; |
|||
} |
|||
else |
|||
{ |
|||
RLP r(data.cropped(8)); |
|||
if (!interpret(r)) |
|||
{ |
|||
// error
|
|||
dropped(); |
|||
return; |
|||
} |
|||
} |
|||
memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); |
|||
m_incoming.resize(m_incoming.size() - tlen); |
|||
} |
|||
} |
|||
doRead(); |
|||
} |
|||
catch (Exception const& _e) |
|||
{ |
|||
clogS(NetWarn) << "ERROR: " << _e.description(); |
|||
dropped(); |
|||
} |
|||
catch (std::exception const& _e) |
|||
{ |
|||
clogS(NetWarn) << "ERROR: " << _e.what(); |
|||
dropped(); |
|||
} |
|||
} |
|||
}); |
|||
} |
@ -1,116 +0,0 @@ |
|||
/*
|
|||
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 EthereumSession.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <mutex> |
|||
#include <array> |
|||
#include <set> |
|||
#include <memory> |
|||
#include <utility> |
|||
#include <libethential/RLP.h> |
|||
#include <libethcore/CommonEth.h> |
|||
#include "CommonNet.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
/**
|
|||
* @brief The EthereumSession class |
|||
* @todo Document fully. |
|||
*/ |
|||
class EthereumSession: public std::enable_shared_from_this<EthereumSession> |
|||
{ |
|||
friend class EthereumHost; |
|||
|
|||
public: |
|||
EthereumSession(EthereumHost* _server, bi::tcp::socket _socket, u256 _rNId, bi::address _peerAddress, unsigned short _peerPort = 0); |
|||
~EthereumSession(); |
|||
|
|||
void start(); |
|||
void disconnect(int _reason); |
|||
|
|||
void ping(); |
|||
|
|||
bool isOpen() const { return m_socket.is_open(); } |
|||
|
|||
bi::tcp::endpoint endpoint() const; ///< for other peers to connect to.
|
|||
|
|||
private: |
|||
void startInitialSync(); |
|||
void getPeers(); |
|||
|
|||
/// Ensure that we are waiting for a bunch of blocks from our peer.
|
|||
void ensureGettingChain(); |
|||
|
|||
void giveUpOnFetch(); |
|||
|
|||
void dropped(); |
|||
void doRead(); |
|||
void doWrite(std::size_t length); |
|||
bool interpret(RLP const& _r); |
|||
|
|||
/// @returns true iff the _msg forms a valid message for sending or receiving on the network.
|
|||
static bool checkPacket(bytesConstRef _msg); |
|||
|
|||
static RLPStream& prep(RLPStream& _s); |
|||
void sealAndSend(RLPStream& _s); |
|||
void sendDestroy(bytes& _msg); |
|||
void send(bytesConstRef _msg); |
|||
void writeImpl(bytes& _buffer); |
|||
void write(); |
|||
EthereumHost* m_server; |
|||
|
|||
std::recursive_mutex m_writeLock; |
|||
std::deque<bytes> m_writeQueue; |
|||
|
|||
bi::tcp::socket m_socket; |
|||
std::array<byte, 65536> m_data; |
|||
PeerInfo m_info; |
|||
Public m_id; |
|||
|
|||
bytes m_incoming; |
|||
uint m_protocolVersion; |
|||
u256 m_networkId; |
|||
u256 m_reqNetworkId; |
|||
unsigned short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers.
|
|||
uint m_caps; |
|||
|
|||
h256 m_latestHash; ///< Peer's latest block's hash.
|
|||
u256 m_totalDifficulty; ///< Peer's latest block's total difficulty.
|
|||
h256s m_neededBlocks; ///< The blocks that we should download from this peer.
|
|||
|
|||
h256Set m_askedBlocks; ///< The blocks for which we sent the last GetBlocks for but haven't received a corresponding Blocks.
|
|||
|
|||
std::chrono::steady_clock::time_point m_ping; |
|||
std::chrono::steady_clock::time_point m_connect; |
|||
std::chrono::steady_clock::time_point m_disconnect; |
|||
|
|||
uint m_rating; |
|||
bool m_requireTransactions; |
|||
|
|||
std::set<h256> m_knownBlocks; |
|||
std::set<h256> m_knownTransactions; |
|||
|
|||
bool m_willBeDeleted = false; ///< True if we already posted a deleter on the strand.
|
|||
}; |
|||
|
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue