Gav Wood
11 years ago
74 changed files with 3096 additions and 2560 deletions
@ -0,0 +1,55 @@ |
|||
CURRENT_SOURCE_DIR=$1 |
|||
CURRENT_BINARY_DIR=$2 |
|||
BUILD_TYPE=$3 |
|||
BUILD_PLATFORM=$4 |
|||
|
|||
echo "Current source dir: $CURRENT_SOURCE_DIR" |
|||
echo "Current binary dir: $CURRENT_BINARY_DIR" |
|||
echo "Build type: $BUILD_TYPE" |
|||
echo "Build platform: $BUILD_PLATFORM" |
|||
|
|||
if [[ -e "$CURRENT_SOURCE_DIR/BuildInfo.h" ]] |
|||
then |
|||
echo "Using existing BuildInfo.h" |
|||
cp $CURRENT_SOURCE_DIR/BuildInfo.h $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
else |
|||
if [[ -e "$CURRENT_SOURCE_DIR/.git" ]] |
|||
then |
|||
ETH_COMMIT_HASH=$(git --git-dir=$CURRENT_SOURCE_DIR/.git --work-tree=$CURRENT_SOURCE_DIR rev-parse HEAD) |
|||
ETH_LOCAL_CHANGES=$(git --git-dir=$CURRENT_SOURCE_DIR/.git --work-tree=$CURRENT_SOURCE_DIR diff --shortstat) |
|||
if [[ -z "$ETH_LOCAL_CHANGES" ]] |
|||
then |
|||
ETH_CLEAN_REPO=1 |
|||
else |
|||
ETH_CLEAN_REPO=0 |
|||
fi |
|||
|
|||
echo "Commit hash: ${ETH_COMMIT_HASH} (Clean: ${ETH_CLEAN_REPO} - ${ETH_LOCAL_CHANGES})" |
|||
else |
|||
echo "Unknown repo." |
|||
ETH_COMMIT_HASH=0 |
|||
ETH_CLEAN_REPO=1 |
|||
fi |
|||
|
|||
echo "// This file was automatically generated by cmake" > $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
echo "" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
echo "#pragma once" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
echo "" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
echo "#define ETH_COMMIT_HASH $ETH_COMMIT_HASH" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
echo "#define ETH_CLEAN_REPO $ETH_CLEAN_REPO" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
echo "#define ETH_BUILD_TYPE $BUILD_TYPE" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
echo "#define ETH_BUILD_PLATFORM $BUILD_PLATFORM" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
fi |
|||
|
|||
if [[ -e "$CURRENT_BINARY_DIR/BuildInfo.h" ]] |
|||
then |
|||
DIFF=$(diff $CURRENT_BINARY_DIR/BuildInfo.h $CURRENT_BINARY_DIR/BuildInfo.h.tmp) |
|||
if [[ -z "$DIFF" ]] |
|||
then |
|||
rm $CURRENT_BINARY_DIR/BuildInfo.h.tmp |
|||
else |
|||
mv $CURRENT_BINARY_DIR/BuildInfo.h.tmp $CURRENT_BINARY_DIR/BuildInfo.h |
|||
fi |
|||
else |
|||
mv $CURRENT_BINARY_DIR/BuildInfo.h.tmp $CURRENT_BINARY_DIR/BuildInfo.h |
|||
fi |
@ -1,94 +0,0 @@ |
|||
### UP FOR GRABS |
|||
|
|||
BUG: Nonce sometimes jumps one. |
|||
|
|||
Tests |
|||
- Use standard tests. |
|||
|
|||
Crypto stuff: |
|||
- kFromMessage |
|||
- Check all the tweak instructions. |
|||
|
|||
Network: |
|||
- *** Exponential backoff on bad connection. |
|||
- *** Handle exception when no network. |
|||
- *** Only download blocks from one peer at once. |
|||
- Download in parallel, but stripe. |
|||
- NotInChain will be very bad for new peers - it'll run through until the genesis. |
|||
- Check how many it has first. |
|||
- Crypto on network - use id as public key? |
|||
- Make work with IPv6 |
|||
- Peers rated. |
|||
- Useful/useless - new blocks/transactions or useful peers? |
|||
- Solid communications? |
|||
- Strategy for peer suggestion? |
|||
- Ignore transactions with future nonces until address's nonce changes. |
|||
|
|||
Cleanups & caching |
|||
- All caches should flush unused data (I'm looking at you, BlockChain) to avoid memory overload. |
|||
- State DB should keep only last few N blocks worth of nodes (except for restore points - configurable, defaults to every 30000th block - all blocks that are restore points should be stored so their stateRoots are known good). |
|||
|
|||
THREAD-SAFETY |
|||
- BlockChain |
|||
- TransactionQueue |
|||
- State |
|||
|
|||
General: |
|||
- Better logging. |
|||
- Colours. |
|||
- Move over to new system. |
|||
- Remove block chain on protocol change (i.e. store protocol with block chain). |
|||
|
|||
Robustness |
|||
- Remove aborts |
|||
- Recover from all exceptions. |
|||
- Especially RLP & other I/O. |
|||
- RLP should never assert; only throw. |
|||
- Store version alongside BC DB. |
|||
- Better handling of corrupt blocks. |
|||
- Kill DB & restart. |
|||
- Avoid transactions with future invalid nonces until additional transactions are processed. |
|||
|
|||
GUI |
|||
- Make address/block chain list model-based, JIT populated. |
|||
- Make everything else model-based |
|||
- Qt/QML class. |
|||
- Turn on/off debug channels. |
|||
|
|||
|
|||
### Marko |
|||
|
|||
Ubuntu builds |
|||
- Raring (branch, local, x64 only :-( ) |
|||
- Quantal (branch) (Launchpad) |
|||
- Saucy (master) (Launchpad) |
|||
|
|||
### Alex |
|||
|
|||
Mac build. |
|||
Mac build instructions. |
|||
|
|||
### Eric |
|||
|
|||
Windows XC build. |
|||
Windows XC build instructions. |
|||
|
|||
### Tim/Harv |
|||
|
|||
Windows MSVC build. |
|||
Windows MSVC build instructions. |
|||
|
|||
LATER: |
|||
|
|||
Trie on DB. |
|||
- Move the restore point stuff into block restore points |
|||
- i.e. keep all nodes from last 127 blocks with counter, at 128, kill but keep every (60*24*7)th or so i.e. one per week as a restore point. |
|||
- maybe allow this to be configured. |
|||
|
|||
|
|||
### TIM |
|||
|
|||
Stateful Miner class. |
|||
|
|||
Better Mod-Exp. |
|||
|
@ -1,6 +0,0 @@ |
|||
cpp-ethereum for Debian |
|||
----------------------- |
|||
|
|||
<possible notes regarding this package - if none, delete this file> |
|||
|
|||
-- Gav <Gav Wood <i@gavwood.com>> Mon, 03 Feb 2014 14:50:20 +0000 |
@ -1,9 +0,0 @@ |
|||
cpp-ethereum for Debian |
|||
----------------------- |
|||
|
|||
<this file describes information about the source package, see Debian policy |
|||
manual section 4.14. You WILL either need to modify or delete this file> |
|||
|
|||
|
|||
|
|||
|
@ -1,23 +0,0 @@ |
|||
cpp-ethereum (0.1.0-1) saucy; urgency=low |
|||
|
|||
* Various improvements moving towards PoC-2. |
|||
|
|||
-- Ethereum (Ethereum Project) <ethereum@gavwood.com> Fri, 07 Feb 2014 00:48:26 +0000 |
|||
|
|||
cpp-ethereum (0.1-3) saucy; urgency=low |
|||
|
|||
* Packaging stuff. |
|||
|
|||
-- Ethereum (Ethereum Project) <ethereum@gavwood.com> Thu, 06 Feb 2014 14:13:00 +0000 |
|||
|
|||
cpp-ethereum (0.1-2) saucy; urgency=low |
|||
|
|||
* Fix Qt dep. |
|||
|
|||
-- Gav Wood <ethereum@gavwood.com> Mon, 06 Feb 2014 11:35:20 +0000 |
|||
|
|||
cpp-ethereum (0.1-1) saucy; urgency=low |
|||
|
|||
* Initial release. |
|||
|
|||
-- Gav Wood <ethereum@gavwood.com> Mon, 03 Feb 2014 14:50:20 +0000 |
@ -1 +0,0 @@ |
|||
8 |
@ -1,29 +0,0 @@ |
|||
Source: cpp-ethereum |
|||
Section: science |
|||
Priority: extra |
|||
Maintainer: Ethereum (Ethereum Project) <ethereum@gavwood.com> |
|||
Build-Depends: debhelper (>= 8.0.0), cmake, libgmp-dev, libcryptoppeth-dev, libboost-filesystem1.53-dev, libboost-mpi1.53-dev, libboost1.53-dev, libleveldb-dev, libminiupnpc-dev, qtbase5-dev, qt5-default |
|||
Standards-Version: 3.9.4 |
|||
Homepage: http://ethereum.org |
|||
#Vcs-Git: git://git.debian.org/collab-maint/cpp-ethereum.git |
|||
#Vcs-Browser: http://git.debian.org/?p=collab-maint/cpp-ethereum.git;a=summary |
|||
|
|||
Package: libethereum |
|||
Section: libdevel |
|||
Architecture: any |
|||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgmp, libcryptoppeth, libboost-filesystem1.53.0, libboost-mpi1.53.0, libleveldb, libminiupnpc, secp256k1eth |
|||
Description: The future of computational social contracts. |
|||
A long description of libethereum. |
|||
|
|||
Package: libethereum-dev |
|||
Section: libdevel |
|||
Architecture: any |
|||
Depends: libethereum (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, libgmp, libcryptoppeth, libboost-filesystem1.53.0, libboost-mpi1.53.0, libleveldb, libminiupnpc |
|||
Description: The future of computational social contracts. Dev Files. |
|||
A long description of libethereum. Dev Files. |
|||
|
|||
Package: alethzero |
|||
Architecture: any |
|||
Depends: ${shlibs:Depends}, ${misc:Depends}, libethereum (= ${binary:Version}), qtbase5 |
|||
Description: The Qt-based Ethereum GUI. |
|||
A long description of alethzero. |
@ -1,24 +0,0 @@ |
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |
|||
Upstream-Name: cpp-ethereum |
|||
Source: github.com/ethereum/cpp-ethereum |
|||
|
|||
Files: * |
|||
Copyright: 2014 Gav Wood <i@gavwood.com> |
|||
License: MIT |
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
@ -1,3 +0,0 @@ |
|||
CodingStandards.txt |
|||
README.md |
|||
TODO |
@ -1,36 +0,0 @@ |
|||
#!/usr/bin/make -f |
|||
# -*- makefile -*- |
|||
# Sample debian/rules that uses debhelper. |
|||
# |
|||
# This file was originally written by Joey Hess and Craig Small. |
|||
# As a special exception, when this file is copied by dh-make into a |
|||
# dh-make output file, you may use that output file without restriction. |
|||
# This special exception was added by Craig Small in version 0.37 of dh-make. |
|||
# |
|||
# Modified to make a template file for a multi-binary package with separated |
|||
# build-arch and build-indep targets by Bill Allombert 2001 |
|||
|
|||
# Uncomment this to turn on verbose mode. |
|||
#export DH_VERBOSE=1 |
|||
|
|||
# This has to be exported to make some magic below work. |
|||
export DH_OPTIONS |
|||
|
|||
|
|||
%: |
|||
dh $@ |
|||
|
|||
override_dh_auto_configure: |
|||
cmake -DCMAKE_INSTALL_PREFIX=$(CURDIR)/debian/libethereum-dev/usr . |
|||
|
|||
override_dh_auto_build: |
|||
$(MAKE) -j8 |
|||
|
|||
override_dh_auto_install: |
|||
$(MAKE) install |
|||
# Move the libs over to the non-dev package. |
|||
mkdir -p $(CURDIR)/debian/libethereum/usr |
|||
mv $(CURDIR)/debian/libethereum-dev/usr/lib $(CURDIR)/debian/libethereum/usr/lib |
|||
mkdir -p $(CURDIR)/debian/alephzero/usr/bin |
|||
mv $(CURDIR)/debian/libethereum-dev/usr/bin/alethzero $(CURDIR)/debian/alethzero/usr/lib |
|||
|
@ -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 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 < ' ' || i > 127 || _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>(4, 10)(s_eng), ' '); |
|||
char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; |
|||
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) |
|||
{ |
|||
assert(_s.size() % 2 == 0); |
|||
if (_s.size() < 2) |
|||
return bytes(); |
|||
uint s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; |
|||
std::vector<uint8_t> ret; |
|||
ret.reserve((_s.size() - s) / 2); |
|||
for (uint i = s; i < _s.size(); i += 2) |
|||
ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); |
|||
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,186 @@ |
|||
/*
|
|||
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.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
* |
|||
* Shared algorithms and data types. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
#include <algorithm> |
|||
#include <type_traits> |
|||
#include <cstring> |
|||
#include <string> |
|||
#include "Common.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
// String conversion functions, mainly to/from hex/nibble/byte representations.
|
|||
|
|||
/// Convert a series of bytes to the corresponding string of hex duplets.
|
|||
/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte.
|
|||
/// @example toHex("A\x69") == "4169"
|
|||
template <class _T> |
|||
std::string toHex(_T const& _data, int _w = 2) |
|||
{ |
|||
std::ostringstream ret; |
|||
for (auto i: _data) |
|||
ret << std::hex << std::setfill('0') << std::setw(_w) << (int)(typename std::make_unsigned<decltype(i)>::type)i; |
|||
return ret.str(); |
|||
} |
|||
|
|||
/// Converts a (printable) ASCII hex character into the correspnding integer value.
|
|||
/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5
|
|||
int fromHex(char _i); |
|||
|
|||
/// Converts a (printable) ASCII hex string into the corresponding byte stream.
|
|||
/// @example fromHex("41626261") == asBytes("Abba")
|
|||
bytes fromHex(std::string const& _s); |
|||
|
|||
/// Converts byte array to a string containing the same (binary) data. Unless
|
|||
/// the byte array happens to contain ASCII data, this won't be printable.
|
|||
inline std::string asString(bytes const& _b) |
|||
{ |
|||
return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); |
|||
} |
|||
|
|||
/// Converts a string to a byte array containing the string's (byte) data.
|
|||
inline bytes asBytes(std::string const& _b) |
|||
{ |
|||
return bytes((byte const*)_b.data(), (byte const*)(_b.data() + _b.size())); |
|||
} |
|||
|
|||
/// Converts a string into the big-endian base-16 stream of integers (NOT ASCII).
|
|||
/// @example asNibbles("A")[0] == 4 && asNibbles("A")[1] == 1
|
|||
bytes asNibbles(std::string const& _s); |
|||
|
|||
|
|||
// Big-endian to/from host endian conversion functions.
|
|||
|
|||
/// Converts a templated integer value to the big-endian byte-stream represented on a templated collection.
|
|||
/// The size of the collection object will be unchanged. If it is too small, it will not represent the
|
|||
/// value properly, if too big then the additional elements will be zeroed out.
|
|||
/// @a _Out will typically be either std::string or bytes.
|
|||
/// @a _T will typically by uint, u160, u256 or bigint.
|
|||
template <class _T, class _Out> |
|||
inline void toBigEndian(_T _val, _Out& o_out) |
|||
{ |
|||
for (auto i = o_out.size(); i-- != 0; _val >>= 8) |
|||
o_out[i] = (typename _Out::value_type)(uint8_t)_val; |
|||
} |
|||
|
|||
/// Converts a big-endian byte-stream represented on a templated collection to a templated integer value.
|
|||
/// @a _In will typically be either std::string or bytes.
|
|||
/// @a _T will typically by uint, u160, u256 or bigint.
|
|||
template <class _T, class _In> |
|||
inline _T fromBigEndian(_In const& _bytes) |
|||
{ |
|||
_T ret = 0; |
|||
for (auto i: _bytes) |
|||
ret = (ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i; |
|||
return ret; |
|||
} |
|||
|
|||
/// Convenience functions for toBigEndian
|
|||
inline std::string toBigEndianString(u256 _val) { std::string ret(32, '\0'); toBigEndian(_val, ret); return ret; } |
|||
inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toBigEndian(_val, ret); return ret; } |
|||
inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } |
|||
inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; } |
|||
|
|||
/// Convenience function for toBigEndian.
|
|||
/// @returns a string just big enough to represent @a _val.
|
|||
template <class _T> |
|||
inline std::string toCompactBigEndianString(_T _val) |
|||
{ |
|||
int i = 0; |
|||
for (_T v = _val; v; ++i, v >>= 8) {} |
|||
std::string ret(i, '\0'); |
|||
toBigEndian(_val, ret); |
|||
return ret; |
|||
} |
|||
|
|||
|
|||
// Algorithms for string and string-like collections.
|
|||
|
|||
/// Escapes a string into the C-string representation.
|
|||
/// @p _all if true will escape all characters, not just the unprintable ones.
|
|||
std::string escaped(std::string const& _s, bool _all = true); |
|||
|
|||
/// Determines the length of the common prefix of the two collections given.
|
|||
/// @returns the number of elements both @a _t and @a _u share, in order, at the beginning.
|
|||
/// @example commonPrefix("Hello world!", "Hello, world!") == 5
|
|||
template <class _T, class _U> |
|||
uint commonPrefix(_T const& _t, _U const& _u) |
|||
{ |
|||
uint s = std::min<uint>(_t.size(), _u.size()); |
|||
for (uint i = 0;; ++i) |
|||
if (i == s || _t[i] != _u[i]) |
|||
return i; |
|||
return s; |
|||
} |
|||
|
|||
/// Creates a random, printable, word.
|
|||
std::string randomWord(); |
|||
|
|||
|
|||
// General datatype convenience functions.
|
|||
|
|||
/// Trims a given number of elements from the front of a collection.
|
|||
/// Only works for POD element types.
|
|||
template <class _T> |
|||
void trimFront(_T& _t, uint _elements) |
|||
{ |
|||
static_assert(std::is_pod<typename _T::value_type>::value, ""); |
|||
memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0])); |
|||
_t.resize(_t.size() - _elements); |
|||
} |
|||
|
|||
/// Pushes an element on to the front of a collection.
|
|||
/// Only works for POD element types.
|
|||
template <class _T, class _U> |
|||
void pushFront(_T& _t, _U _e) |
|||
{ |
|||
static_assert(std::is_pod<typename _T::value_type>::value, ""); |
|||
_t.push_back(_e); |
|||
memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e)); |
|||
_t[0] = _e; |
|||
} |
|||
|
|||
/// Concatenate two vectors of elements. _T must be POD.
|
|||
template <class _T> |
|||
inline std::vector<_T>& operator+=(std::vector<typename std::enable_if<std::is_pod<_T>::value, _T>::type>& _a, std::vector<_T> const& _b) |
|||
{ |
|||
auto s = _a.size(); |
|||
_a.resize(_a.size() + _b.size()); |
|||
memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(_T)); |
|||
return _a; |
|||
|
|||
} |
|||
|
|||
/// Concatenate two vectors of elements. _T must be POD.
|
|||
template <class _T> |
|||
inline std::vector<_T> operator+(std::vector<typename std::enable_if<std::is_pod<_T>::value, _T>::type> const& _a, std::vector<_T> const& _b) |
|||
{ |
|||
std::vector<_T> ret(_a); |
|||
return ret += _b; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,200 @@ |
|||
/*
|
|||
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 "CommonEth.h" |
|||
|
|||
#if WIN32 |
|||
#pragma warning(push) |
|||
#pragma warning(disable:4244) |
|||
#else |
|||
#pragma GCC diagnostic ignored "-Wunused-function" |
|||
#endif |
|||
#include <secp256k1.h> |
|||
#include <sha3.h> |
|||
#if WIN32 |
|||
#pragma warning(pop) |
|||
#else |
|||
#endif |
|||
#include "Exceptions.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
//#define ETH_ADDRESS_DEBUG 1
|
|||
|
|||
static const vector<pair<u256, string>> g_units = |
|||
{ |
|||
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Uether"}, |
|||
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Vether"}, |
|||
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Dether"}, |
|||
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Nether"}, |
|||
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Yether"}, |
|||
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Zether"}, |
|||
{((u256(1000000000) * 1000000000) * 1000000000) * 1000000000, "Eether"}, |
|||
{((u256(1000000000) * 1000000000) * 1000000000) * 1000000, "Pether"}, |
|||
{((u256(1000000000) * 1000000000) * 1000000000) * 1000, "Tether"}, |
|||
{(u256(1000000000) * 1000000000) * 1000000000, "Gether"}, |
|||
{(u256(1000000000) * 1000000000) * 1000000, "Mether"}, |
|||
{(u256(1000000000) * 1000000000) * 1000, "Kether"}, |
|||
{u256(1000000000) * 1000000000, "ether"}, |
|||
{u256(1000000000) * 1000000, "finney"}, |
|||
{u256(1000000000) * 1000, "szabo"}, |
|||
{u256(1000000000), "Gwei"}, |
|||
{u256(1000000), "Mwei"}, |
|||
{u256(1000), "Kwei"}, |
|||
{u256(1), "wei"} |
|||
}; |
|||
|
|||
vector<pair<u256, string>> const& eth::units() |
|||
{ |
|||
return g_units; |
|||
} |
|||
|
|||
std::string eth::formatBalance(u256 _b) |
|||
{ |
|||
ostringstream ret; |
|||
if (_b > g_units[0].first * 10000) |
|||
{ |
|||
ret << (_b / g_units[0].first) << " " << g_units[0].second; |
|||
return ret.str(); |
|||
} |
|||
ret << setprecision(5); |
|||
for (auto const& i: g_units) |
|||
if (i.first != 1 && _b >= i.first * 100) |
|||
{ |
|||
ret << (double(_b / (i.first / 1000)) / 1000.0) << " " << i.second; |
|||
return ret.str(); |
|||
} |
|||
ret << _b << " wei"; |
|||
return ret.str(); |
|||
} |
|||
|
|||
Address eth::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(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(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 |
|||
} |
|||
|
|||
std::string eth::sha3(std::string const& _input, bool _hex) |
|||
{ |
|||
if (!_hex) |
|||
{ |
|||
string ret(32, '\0'); |
|||
sha3(bytesConstRef((byte const*)_input.data(), _input.size()), bytesRef((byte*)ret.data(), 32)); |
|||
return ret; |
|||
} |
|||
|
|||
uint8_t buf[32]; |
|||
sha3(bytesConstRef((byte const*)_input.data(), _input.size()), bytesRef((byte*)&(buf[0]), 32)); |
|||
std::string ret(64, '\0'); |
|||
for (unsigned int i = 0; i < 32; i++) |
|||
sprintf((char*)(ret.data())+i*2, "%02x", buf[i]); |
|||
return ret; |
|||
} |
|||
|
|||
void eth::sha3(bytesConstRef _input, bytesRef _output) |
|||
{ |
|||
CryptoPP::SHA3_256 ctx; |
|||
ctx.Update((byte*)_input.data(), _input.size()); |
|||
assert(_output.size() >= 32); |
|||
ctx.Final(_output.data()); |
|||
} |
|||
|
|||
bytes eth::sha3Bytes(bytesConstRef _input) |
|||
{ |
|||
bytes ret(32); |
|||
sha3(_input, &ret); |
|||
return ret; |
|||
} |
|||
|
|||
h256 eth::sha3(bytesConstRef _input) |
|||
{ |
|||
h256 ret; |
|||
sha3(_input, bytesRef(&ret[0], 32)); |
|||
return ret; |
|||
} |
@ -0,0 +1,137 @@ |
|||
/*
|
|||
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 "Common.h" |
|||
#include "FixedHash.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
/// 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; |
|||
|
|||
/// User-friendly string representation of the amount _b in wei.
|
|||
std::string formatBalance(u256 _b); |
|||
|
|||
/// Get information concerning the currency denominations.
|
|||
std::vector<std::pair<u256, std::string>> const& units(); |
|||
|
|||
// The various denominations; here for ease of use where needed within code.
|
|||
static const u256 Uether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; |
|||
static const u256 Vether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; |
|||
static const u256 Dether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; |
|||
static const u256 Nether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; |
|||
static const u256 Yether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; |
|||
static const u256 Zether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; |
|||
static const u256 Eether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000000; |
|||
static const u256 Pether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000; |
|||
static const u256 Tether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000; |
|||
static const u256 Gether = (u256(1000000000) * 1000000000) * 1000000000; |
|||
static const u256 Mether = (u256(1000000000) * 1000000000) * 1000000; |
|||
static const u256 Kether = (u256(1000000000) * 1000000000) * 1000; |
|||
static const u256 ether = u256(1000000000) * 1000000000; |
|||
static const u256 finney = u256(1000000000) * 1000000; |
|||
static const u256 szabo = u256(1000000000) * 1000; |
|||
static const u256 Gwei = u256(1000000000); |
|||
static const u256 Mwei = u256(1000000); |
|||
static const u256 Kwei = u256(1000); |
|||
static const u256 wei = u256(1); |
|||
|
|||
/// 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(); |
|||
|
|||
/// 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; } |
|||
|
|||
private: |
|||
Secret m_secret; |
|||
Public m_public; |
|||
Address m_address; |
|||
}; |
|||
|
|||
|
|||
// SHA-3 convenience routines.
|
|||
|
|||
/// Calculate SHA3-256 hash of the given input and load it into the given output.
|
|||
void sha3(bytesConstRef _input, bytesRef _output); |
|||
|
|||
/// Calculate SHA3-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data.
|
|||
std::string sha3(std::string const& _input, bool _isNibbles); |
|||
|
|||
/// Calculate SHA3-256 hash of the given input, returning as a byte array.
|
|||
bytes sha3Bytes(bytesConstRef _input); |
|||
|
|||
/// Calculate SHA3-256 hash of the given input (presented as a binary string), returning as a byte array.
|
|||
inline bytes sha3Bytes(std::string const& _input) { return sha3Bytes((std::string*)&_input); } |
|||
|
|||
/// Calculate SHA3-256 hash of the given input, returning as a byte array.
|
|||
inline bytes sha3Bytes(bytes const& _input) { return sha3Bytes((bytes*)&_input); } |
|||
|
|||
/// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash.
|
|||
h256 sha3(bytesConstRef _input); |
|||
|
|||
/// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash.
|
|||
inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef((bytes*)&_input)); } |
|||
|
|||
/// Calculate SHA3-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash.
|
|||
inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); } |
|||
|
|||
} |
@ -0,0 +1,48 @@ |
|||
/*
|
|||
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 CommonIO.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "Common.h" |
|||
|
|||
#include <fstream> |
|||
#include "Exceptions.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
bytes eth::contents(std::string const& _file) |
|||
{ |
|||
std::ifstream is(_file, std::ifstream::binary); |
|||
if (!is) |
|||
return bytes(); |
|||
// get length of file:
|
|||
is.seekg (0, is.end); |
|||
streamoff length = is.tellg(); |
|||
is.seekg (0, is.beg); |
|||
bytes ret(length); |
|||
is.read((char*)ret.data(), length); |
|||
is.close(); |
|||
return ret; |
|||
} |
|||
|
|||
void eth::writeFile(std::string const& _file, bytes const& _data) |
|||
{ |
|||
ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); |
|||
} |
|||
|
@ -0,0 +1,223 @@ |
|||
/*
|
|||
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 CommonIO.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
* |
|||
* File & stream I/O routines. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <set> |
|||
#include <unordered_map> |
|||
#include <unordered_set> |
|||
#include <array> |
|||
#include <list> |
|||
#include <memory> |
|||
#include <vector> |
|||
#include <array> |
|||
#include <sstream> |
|||
#include <string> |
|||
#include <iostream> |
|||
#include "Common.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
/// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes.
|
|||
bytes contents(std::string const& _file); |
|||
|
|||
/// Write the given binary data into the given file, replacing the file if it pre-exists.
|
|||
void writeFile(std::string const& _file, bytes const& _data); |
|||
|
|||
/// Converts arbitrary value to string representation using std::stringstream.
|
|||
template <class _T> |
|||
std::string toString(_T const& _t) |
|||
{ |
|||
std::ostringstream o; |
|||
o << _t; |
|||
return o.str(); |
|||
} |
|||
|
|||
// Stream I/O functions.
|
|||
// Provides templated stream I/O for all STL collections so they can be shifted on to any iostream-like interface.
|
|||
|
|||
template <class S, class T> struct StreamOut { static S& bypass(S& _out, T const& _t) { _out << _t; return _out; } }; |
|||
template <class S> struct StreamOut<S, uint8_t> { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } }; |
|||
|
|||
template <class S, class T> |
|||
inline S& streamout(S& _out, std::vector<T> const& _e) |
|||
{ |
|||
_out << "["; |
|||
if (!_e.empty()) |
|||
{ |
|||
StreamOut<S, T>::bypass(_out, _e.front()); |
|||
for (auto i = ++_e.begin(); i != _e.end(); ++i) |
|||
StreamOut<S, T>::bypass(_out << ",", *i); |
|||
} |
|||
_out << "]"; |
|||
return _out; |
|||
} |
|||
|
|||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::vector<T> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T, unsigned Z> |
|||
inline S& streamout(S& _out, std::array<T, Z> const& _e) |
|||
{ |
|||
_out << "["; |
|||
if (!_e.empty()) |
|||
{ |
|||
StreamOut<S, T>::bypass(_out, _e.front()); |
|||
auto i = _e.begin(); |
|||
for (++i; i != _e.end(); ++i) |
|||
StreamOut<S, T>::bypass(_out << ",", *i); |
|||
} |
|||
_out << "]"; |
|||
return _out; |
|||
} |
|||
template <class T, unsigned Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T, unsigned long Z> |
|||
inline S& streamout(S& _out, std::array<T, Z> const& _e) |
|||
{ |
|||
_out << "["; |
|||
if (!_e.empty()) |
|||
{ |
|||
StreamOut<S, T>::bypass(_out, _e.front()); |
|||
auto i = _e.begin(); |
|||
for (++i; i != _e.end(); ++i) |
|||
StreamOut<S, T>::bypass(_out << ",", *i); |
|||
} |
|||
_out << "]"; |
|||
return _out; |
|||
} |
|||
template <class T, unsigned long Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T> |
|||
inline S& streamout(S& _out, std::list<T> const& _e) |
|||
{ |
|||
_out << "["; |
|||
if (!_e.empty()) |
|||
{ |
|||
_out << _e.front(); |
|||
for (auto i = ++_e.begin(); i != _e.end(); ++i) |
|||
_out << "," << *i; |
|||
} |
|||
_out << "]"; |
|||
return _out; |
|||
} |
|||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::list<T> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T, class U> |
|||
inline S& streamout(S& _out, std::pair<T, U> const& _e) |
|||
{ |
|||
_out << "(" << _e.first << "," << _e.second << ")"; |
|||
return _out; |
|||
} |
|||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::pair<T, U> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T1, class T2, class T3> |
|||
inline S& streamout(S& _out, std::tuple<T1, T2, T3> const& _t) |
|||
{ |
|||
_out << "(" << std::get<0>(_t) << "," << std::get<1>(_t) << "," << std::get<2>(_t) << ")"; |
|||
return _out; |
|||
} |
|||
template <class T1, class T2, class T3> inline std::ostream& operator<<(std::ostream& _out, std::tuple<T1, T2, T3> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T, class U> |
|||
S& streamout(S& _out, std::map<T, U> const& _v) |
|||
{ |
|||
if (_v.empty()) |
|||
return _out << "{}"; |
|||
int i = 0; |
|||
for (auto p: _v) |
|||
_out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; |
|||
return _out << " }"; |
|||
} |
|||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::map<T, U> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T, class U> |
|||
S& streamout(S& _out, std::unordered_map<T, U> const& _v) |
|||
{ |
|||
if (_v.empty()) |
|||
return _out << "{}"; |
|||
int i = 0; |
|||
for (auto p: _v) |
|||
_out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; |
|||
return _out << " }"; |
|||
} |
|||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::unordered_map<T, U> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T> |
|||
S& streamout(S& _out, std::set<T> const& _v) |
|||
{ |
|||
if (_v.empty()) |
|||
return _out << "{}"; |
|||
int i = 0; |
|||
for (auto p: _v) |
|||
_out << (!(i++) ? "{ " : ", ") << p; |
|||
return _out << " }"; |
|||
} |
|||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::set<T> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T> |
|||
S& streamout(S& _out, std::unordered_set<T> const& _v) |
|||
{ |
|||
if (_v.empty()) |
|||
return _out << "{}"; |
|||
int i = 0; |
|||
for (auto p: _v) |
|||
_out << (!(i++) ? "{ " : ", ") << p; |
|||
return _out << " }"; |
|||
} |
|||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::unordered_set<T> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T> |
|||
S& streamout(S& _out, std::multiset<T> const& _v) |
|||
{ |
|||
if (_v.empty()) |
|||
return _out << "{}"; |
|||
int i = 0; |
|||
for (auto p: _v) |
|||
_out << (!(i++) ? "{ " : ", ") << p; |
|||
return _out << " }"; |
|||
} |
|||
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::multiset<T> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class S, class T, class U> |
|||
S& streamout(S& _out, std::multimap<T, U> const& _v) |
|||
{ |
|||
if (_v.empty()) |
|||
return _out << "{}"; |
|||
T l; |
|||
int i = 0; |
|||
for (auto p: _v) |
|||
if (!(i++)) |
|||
_out << "{ " << (l = p.first) << " => " << p.second; |
|||
else if (l == p.first) |
|||
_out << ", " << p.second; |
|||
else |
|||
_out << "; " << (l = p.first) << " => " << p.second; |
|||
return _out << " }"; |
|||
} |
|||
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::multimap<T, U> const& _e) { streamout(_out, _e); return _out; } |
|||
|
|||
template <class _S, class _T> _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p) { if (_p) _out << "@" << (*_p); else _out << "nullptr"; return _out; } |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
/*
|
|||
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 FixedHash.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "FixedHash.h" |
|||
|
|||
using namespace std; |
|||
using namespace eth; |
@ -0,0 +1,195 @@ |
|||
/*
|
|||
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 FixedHash.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
* |
|||
* The FixedHash fixed-size "hash" container type. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <algorithm> |
|||
#include "CommonData.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
/// Fixed-size raw-byte array container type, with an API optimised for storing hashes.
|
|||
/// Transparently converts to/from the corresponding arithmetic type; this will
|
|||
/// assume the data contained in the hash is big-endian.
|
|||
template <unsigned N> |
|||
class FixedHash |
|||
{ |
|||
/// The corresponding arithmetic type.
|
|||
using Arith = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; |
|||
|
|||
public: |
|||
/// The size of the container.
|
|||
enum { size = N }; |
|||
|
|||
/// A dummy flag to avoid accidental construction from pointer.
|
|||
enum ConstructFromPointerType { ConstructFromPointer }; |
|||
|
|||
/// Method to convert from a string.
|
|||
enum ConstructFromStringType { FromHex, FromBinary }; |
|||
|
|||
/// Construct an empty hash.
|
|||
FixedHash() { m_data.fill(0); } |
|||
|
|||
/// Convert from the corresponding arithmetic type.
|
|||
FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } |
|||
|
|||
/// Explicitly construct, copying from a byte array.
|
|||
explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<uint>(_b.size(), N)); } |
|||
|
|||
/// Explicitly construct, copying from a bytes in memory with given pointer.
|
|||
explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } |
|||
|
|||
/// Explicitly construct, copying from a string.
|
|||
explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex): FixedHash(_t == FromHex ? fromHex(_s) : eth::asBytes(_s)) {} |
|||
|
|||
/// Convert to arithmetic type.
|
|||
operator Arith() const { return fromBigEndian<Arith>(m_data); } |
|||
|
|||
/// @returns true iff this is the empty hash.
|
|||
operator bool() const { return ((Arith)*this) != 0; } |
|||
|
|||
// The obvious comparison operators.
|
|||
bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } |
|||
bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; } |
|||
bool operator<(FixedHash const& _c) const { return m_data < _c.m_data; } |
|||
|
|||
// The obvious binary operators.
|
|||
FixedHash& operator^=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } |
|||
FixedHash operator^(FixedHash const& _c) const { return FixedHash(*this) ^= _c; } |
|||
FixedHash& operator|=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] |= _c.m_data[i]; return *this; } |
|||
FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } |
|||
FixedHash& operator&=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } |
|||
FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } |
|||
FixedHash& operator~() { for (auto i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } |
|||
|
|||
/// @returns a particular byte from the hash.
|
|||
byte& operator[](unsigned _i) { return m_data[_i]; } |
|||
/// @returns a particular byte from the hash.
|
|||
byte operator[](unsigned _i) const { return m_data[_i]; } |
|||
|
|||
/// @returns an abridged version of the hash as a user-readable hex string.
|
|||
std::string abridged() const { return toHex(ref().cropped(0, 4)) + ".."; } |
|||
|
|||
/// @returns a mutable byte vector_ref to the object's data.
|
|||
bytesRef ref() { return bytesRef(m_data.data(), N); } |
|||
|
|||
/// @returns a constant byte vector_ref to the object's data.
|
|||
bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); } |
|||
|
|||
/// @returns a mutable byte pointer to the object's data.
|
|||
byte* data() { return m_data.data(); } |
|||
|
|||
/// @returns a constant byte pointer to the object's data.
|
|||
byte const* data() const { return m_data.data(); } |
|||
|
|||
/// @returns a copy of the object's data as a byte vector.
|
|||
bytes asBytes() const { return bytes(data(), data() + N); } |
|||
|
|||
/// @returns a mutable reference to the object's data as an STL array.
|
|||
std::array<byte, N>& asArray() { return m_data; } |
|||
|
|||
/// @returns a constant reference to the object's data as an STL array.
|
|||
std::array<byte, N> const& asArray() const { return m_data; } |
|||
|
|||
/// A generic std::hash compatible function object.
|
|||
struct hash |
|||
{ |
|||
/// Make a hash of the object's data.
|
|||
size_t operator()(FixedHash const& value) const |
|||
{ |
|||
size_t h = 0; |
|||
for (auto i: value.m_data) |
|||
h = (h << 5 - h) + i; |
|||
return h; |
|||
} |
|||
}; |
|||
|
|||
private: |
|||
std::array<byte, N> m_data; ///< The binary data.
|
|||
}; |
|||
|
|||
|
|||
/// Fast equality operator for h256.
|
|||
template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const |
|||
{ |
|||
const uint64_t* hash1 = (const uint64_t*)this->data(); |
|||
const uint64_t* hash2 = (const uint64_t*)_other.data(); |
|||
return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]); |
|||
} |
|||
|
|||
/// Fast std::hash compatible hash function object for h256.
|
|||
template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const |
|||
{ |
|||
const uint64_t*data = (const uint64_t*)value.data(); |
|||
uint64_t hash = data[0]; |
|||
hash ^= data[1]; |
|||
hash ^= data[2]; |
|||
hash ^= data[3]; |
|||
return (size_t)hash; |
|||
} |
|||
|
|||
/// Stream I/O for the FixedHash class.
|
|||
template <unsigned N> |
|||
inline std::ostream& operator<<(std::ostream& _out, FixedHash<N> const& _h) |
|||
{ |
|||
_out << std::noshowbase << std::hex << std::setfill('0'); |
|||
for (unsigned i = 0; i < N; ++i) |
|||
_out << std::setw(2) << (int)_h[i]; |
|||
_out << std::dec; |
|||
return _out; |
|||
} |
|||
|
|||
// Common types of FixedHash.
|
|||
using h512 = FixedHash<64>; |
|||
using h256 = FixedHash<32>; |
|||
using h160 = FixedHash<20>; |
|||
using h256s = std::vector<h256>; |
|||
using h160s = std::vector<h160>; |
|||
using h256Set = std::set<h256>; |
|||
using h160Set = std::set<h160>; |
|||
|
|||
/// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes.
|
|||
inline h160 right160(h256 const& _t) |
|||
{ |
|||
h160 ret; |
|||
memcpy(ret.data(), _t.data() + 12, 20); |
|||
return ret; |
|||
} |
|||
|
|||
/// Convert the given value into h160 (160-bit unsigned integer) using the left 20 bytes.
|
|||
inline h160 left160(h256 const& _t) |
|||
{ |
|||
h160 ret; |
|||
memcpy(&ret[0], _t.data(), 20); |
|||
return ret; |
|||
} |
|||
|
|||
} |
|||
|
|||
namespace std |
|||
{ |
|||
/// Forward std::hash<eth::h256> to eth::h256::hash.
|
|||
template<> struct hash<eth::h256>: eth::h256::hash {}; |
|||
} |
@ -0,0 +1,41 @@ |
|||
/*
|
|||
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 Log.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "Log.h" |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
// Logging
|
|||
int eth::g_logVerbosity = 8; |
|||
map<type_info const*, bool> eth::g_logOverride; |
|||
|
|||
ThreadLocalLogName eth::t_logThreadName("main"); |
|||
|
|||
void eth::simpleDebugOut(std::string const& _s, char const*) |
|||
{ |
|||
cout << _s << endl << flush; |
|||
} |
|||
|
|||
std::function<void(std::string const&, char const*)> eth::g_logPost = simpleDebugOut; |
|||
|
@ -0,0 +1,134 @@ |
|||
/*
|
|||
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 Log.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
* |
|||
* The logging subsystem. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <ctime> |
|||
#include <chrono> |
|||
#include <boost/thread.hpp> |
|||
#include "vector_ref.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
/// The null output stream. Used when logging is disabled.
|
|||
class NullOutputStream |
|||
{ |
|||
public: |
|||
template <class T> NullOutputStream& operator<<(T const&) { return *this; } |
|||
}; |
|||
|
|||
/// A simple log-output function that prints log messages to stdout.
|
|||
void simpleDebugOut(std::string const&, char const* ); |
|||
|
|||
/// The logging system's current verbosity.
|
|||
extern int g_logVerbosity; |
|||
|
|||
/// The current method that the logging system uses to output the log messages. Defaults to simpleDebugOut().
|
|||
extern std::function<void(std::string const&, char const*)> g_logPost; |
|||
|
|||
/// Map of Log Channel types to bool, false forces the channel to be disabled, true forces it to be enabled.
|
|||
/// If a channel has no entry, then it will output as long as its verbosity (LogChannel::verbosity) is less than
|
|||
/// or equal to the currently output verbosity (g_logVerbosity).
|
|||
extern std::map<std::type_info const*, bool> g_logOverride; |
|||
|
|||
/// Associate a name with each thread for nice logging.
|
|||
struct ThreadLocalLogName |
|||
{ |
|||
ThreadLocalLogName(std::string _name) { m_name.reset(new std::string(_name)); }; |
|||
boost::thread_specific_ptr<std::string> m_name; |
|||
}; |
|||
|
|||
/// The current thread's name.
|
|||
extern ThreadLocalLogName t_logThreadName; |
|||
|
|||
/// Set the current thread's log name.
|
|||
inline void setThreadName(char const* _n) { t_logThreadName.m_name.reset(new std::string(_n)); } |
|||
|
|||
/// The default logging channels. Each has an associated verbosity and three-letter prefix (name() ).
|
|||
/// Channels should inherit from LogChannel and define name() and verbosity.
|
|||
struct LogChannel { static const char* name() { return " "; } static const int verbosity = 1; }; |
|||
struct LeftChannel: public LogChannel { static const char* name() { return "<<<"; } }; |
|||
struct RightChannel: public LogChannel { static const char* name() { return ">>>"; } }; |
|||
struct WarnChannel: public LogChannel { static const char* name() { return "!!!"; } static const int verbosity = 0; }; |
|||
struct NoteChannel: public LogChannel { static const char* name() { return "***"; } }; |
|||
struct DebugChannel: public LogChannel { static const char* name() { return "---"; } static const int verbosity = 0; }; |
|||
|
|||
/// Logging class, iostream-like, that can be shifted to.
|
|||
template <class Id, bool _AutoSpacing = true> |
|||
class LogOutputStream |
|||
{ |
|||
public: |
|||
/// Construct a new object.
|
|||
/// If _term is true the the prefix info is terminated with a ']' character; if not it ends only with a '|' character.
|
|||
LogOutputStream(bool _term = true) |
|||
{ |
|||
std::type_info const* i = &typeid(Id); |
|||
auto it = g_logOverride.find(i); |
|||
if ((it != g_logOverride.end() && it->second == true) || (it == g_logOverride.end() && Id::verbosity <= g_logVerbosity)) |
|||
{ |
|||
time_t rawTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
|||
char buf[24]; |
|||
if (strftime(buf, 24, "%X", localtime(&rawTime)) == 0) |
|||
buf[0] = '\0'; // empty if case strftime fails
|
|||
m_sstr << Id::name() << " [ " << buf << " | " << *(t_logThreadName.m_name.get()) << (_term ? " ] " : ""); |
|||
} |
|||
} |
|||
|
|||
/// Destructor. Posts the accrued log entry to the g_logPost function.
|
|||
~LogOutputStream() { if (Id::verbosity <= g_logVerbosity) g_logPost(m_sstr.str(), Id::name()); } |
|||
|
|||
/// Shift arbitrary data to the log. Spaces will be added between items as required.
|
|||
template <class T> LogOutputStream& operator<<(T const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && m_sstr.str().size() && m_sstr.str().back() != ' ') m_sstr << " "; m_sstr << _t; } return *this; } |
|||
|
|||
private: |
|||
std::stringstream m_sstr; ///< The accrued log entry.
|
|||
}; |
|||
|
|||
// Simple cout-like stream objects for accessing common log channels.
|
|||
// Dirties the global namespace, but oh so convenient...
|
|||
#define cnote eth::LogOutputStream<eth::NoteChannel, true>() |
|||
#define cwarn eth::LogOutputStream<eth::WarnChannel, true>() |
|||
|
|||
// Null stream-like objects.
|
|||
#define ndebug if (true) {} else eth::NullOutputStream() |
|||
#define nlog(X) if (true) {} else eth::NullOutputStream() |
|||
#define nslog(X) if (true) {} else eth::NullOutputStream() |
|||
|
|||
// Kill debugging log channel when we're in release mode.
|
|||
#if NDEBUG |
|||
#define cdebug ndebug |
|||
#else |
|||
#define cdebug eth::LogOutputStream<eth::DebugChannel, true>() |
|||
#endif |
|||
|
|||
// Kill all logs when when NLOG is defined.
|
|||
#if NLOG |
|||
#define clog(X) nlog(X) |
|||
#define cslog(X) nslog(X) |
|||
#else |
|||
#define clog(X) eth::LogOutputStream<X, true>() |
|||
#define cslog(X) eth::LogOutputStream<X, false>() |
|||
#endif |
|||
|
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,542 @@ |
|||
/*
|
|||
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 PeerNetwork.cpp
|
|||
* @authors: |
|||
* Gav Wood <i@gavwood.com> |
|||
* Eric Lombrozo <elombrozo@gmail.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "PeerServer.h" |
|||
|
|||
#include <sys/types.h> |
|||
#ifdef _WIN32 |
|||
// winsock is already included
|
|||
// #include <winsock.h>
|
|||
#else |
|||
#include <ifaddrs.h> |
|||
#endif |
|||
|
|||
#include <set> |
|||
#include <chrono> |
|||
#include <thread> |
|||
#include "Exceptions.h" |
|||
#include "Common.h" |
|||
#include "BlockChain.h" |
|||
#include "BlockInfo.h" |
|||
#include "TransactionQueue.h" |
|||
#include "UPnP.h" |
|||
#include "PeerSession.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
// Addresses we will skip during network interface discovery
|
|||
// Use a vector as the list is small
|
|||
// Why this and not names?
|
|||
// Under MacOSX loopback (127.0.0.1) can be named lo0 and br0 are bridges (0.0.0.0)
|
|||
static const set<bi::address> c_rejectAddresses = { |
|||
{bi::address_v4::from_string("127.0.0.1")}, |
|||
{bi::address_v6::from_string("::1")}, |
|||
{bi::address_v4::from_string("0.0.0.0")}, |
|||
{bi::address_v6::from_string("::")} |
|||
}; |
|||
|
|||
PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, unsigned short _port, NodeMode _m, string const& _publicAddress, bool _upnp): |
|||
m_clientVersion(_clientVersion), |
|||
m_mode(_m), |
|||
m_listenPort(_port), |
|||
m_chain(&_ch), |
|||
m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), |
|||
m_socket(m_ioService), |
|||
m_key(KeyPair::create()), |
|||
m_networkId(_networkId) |
|||
{ |
|||
populateAddresses(); |
|||
determinePublic(_publicAddress, _upnp); |
|||
ensureAccepting(); |
|||
clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); |
|||
} |
|||
|
|||
PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m): |
|||
m_clientVersion(_clientVersion), |
|||
m_mode(_m), |
|||
m_listenPort(0), |
|||
m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), |
|||
m_socket(m_ioService), |
|||
m_key(KeyPair::create()), |
|||
m_networkId(_networkId) |
|||
{ |
|||
// populate addresses.
|
|||
populateAddresses(); |
|||
clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); |
|||
} |
|||
|
|||
PeerServer::~PeerServer() |
|||
{ |
|||
for (auto const& i: m_peers) |
|||
if (auto p = i.second.lock()) |
|||
p->disconnect(ClientQuit); |
|||
delete m_upnp; |
|||
} |
|||
|
|||
unsigned PeerServer::protocolVersion() |
|||
{ |
|||
return 8; |
|||
} |
|||
|
|||
void PeerServer::determinePublic(string const& _publicAddress, bool _upnp) |
|||
{ |
|||
if (_upnp) |
|||
try |
|||
{ |
|||
m_upnp = new UPnP; |
|||
} |
|||
catch (NoUPnPDevice) {} // let m_upnp continue as null - we handle it properly.
|
|||
|
|||
bi::tcp::resolver r(m_ioService); |
|||
if (m_upnp && m_upnp->isValid() && m_peerAddresses.size()) |
|||
{ |
|||
clog(NetNote) << "External addr: " << m_upnp->externalIP(); |
|||
int p = m_upnp->addRedirect(m_peerAddresses[0].to_string().c_str(), m_listenPort); |
|||
if (p) |
|||
clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << "."; |
|||
else |
|||
{ |
|||
// couldn't map
|
|||
clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place). Assuming " << m_listenPort << " is local & external port."; |
|||
p = m_listenPort; |
|||
} |
|||
|
|||
auto eip = m_upnp->externalIP(); |
|||
if (eip == string("0.0.0.0") && _publicAddress.empty()) |
|||
m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); |
|||
else |
|||
{ |
|||
m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p); |
|||
m_addresses.push_back(m_public.address().to_v4()); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// No UPnP - fallback on given public address or, if empty, the assumed peer address.
|
|||
m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress) |
|||
: m_peerAddresses.size() ? m_peerAddresses[0] |
|||
: bi::address(), m_listenPort); |
|||
m_addresses.push_back(m_public.address().to_v4()); |
|||
} |
|||
} |
|||
|
|||
void PeerServer::populateAddresses() |
|||
{ |
|||
#ifdef _WIN32 |
|||
WSAData wsaData; |
|||
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) |
|||
throw NoNetworking(); |
|||
|
|||
char ac[80]; |
|||
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) |
|||
{ |
|||
clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; |
|||
WSACleanup(); |
|||
throw NoNetworking(); |
|||
} |
|||
|
|||
struct hostent* phe = gethostbyname(ac); |
|||
if (phe == 0) |
|||
{ |
|||
clog(NetWarn) << "Bad host lookup."; |
|||
WSACleanup(); |
|||
throw NoNetworking(); |
|||
} |
|||
|
|||
for (int i = 0; phe->h_addr_list[i] != 0; ++i) |
|||
{ |
|||
struct in_addr addr; |
|||
memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); |
|||
char *addrStr = inet_ntoa(addr); |
|||
bi::address ad(bi::address::from_string(addrStr)); |
|||
m_addresses.push_back(ad.to_v4()); |
|||
bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); |
|||
if (!isLocal) |
|||
m_peerAddresses.push_back(ad.to_v4()); |
|||
clog(NetNote) << "Address: " << ac << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); |
|||
} |
|||
|
|||
WSACleanup(); |
|||
#else |
|||
ifaddrs* ifaddr; |
|||
if (getifaddrs(&ifaddr) == -1) |
|||
throw NoNetworking(); |
|||
|
|||
bi::tcp::resolver r(m_ioService); |
|||
|
|||
for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) |
|||
{ |
|||
if (!ifa->ifa_addr) |
|||
continue; |
|||
if (ifa->ifa_addr->sa_family == AF_INET) |
|||
{ |
|||
char host[NI_MAXHOST]; |
|||
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) |
|||
continue; |
|||
// TODO: Make exception safe when no internet.
|
|||
auto it = r.resolve({host, "30303"}); |
|||
bi::tcp::endpoint ep = it->endpoint(); |
|||
bi::address ad = ep.address(); |
|||
m_addresses.push_back(ad.to_v4()); |
|||
bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); |
|||
if (!isLocal) |
|||
m_peerAddresses.push_back(ad.to_v4()); |
|||
clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); |
|||
} |
|||
} |
|||
|
|||
freeifaddrs(ifaddr); |
|||
#endif |
|||
} |
|||
|
|||
std::map<Public, bi::tcp::endpoint> PeerServer::potentialPeers() |
|||
{ |
|||
std::map<Public, bi::tcp::endpoint> ret; |
|||
if (!m_public.address().is_unspecified()) |
|||
ret.insert(make_pair(m_key.pub(), m_public)); |
|||
for (auto i: m_peers) |
|||
if (auto j = i.second.lock()) |
|||
{ |
|||
auto ep = j->endpoint(); |
|||
// Skip peers with a listen port of zero or are on a private network
|
|||
bool peerOnNet = (j->m_listenPort != 0 && !isPrivateAddress(ep.address())); |
|||
if (peerOnNet && ep.port() && j->m_id) |
|||
ret.insert(make_pair(i.first, ep)); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
void PeerServer::ensureAccepting() |
|||
{ |
|||
if (m_accepting == false) |
|||
{ |
|||
clog(NetNote) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; |
|||
m_accepting = true; |
|||
m_acceptor.async_accept(m_socket, [=](boost::system::error_code ec) |
|||
{ |
|||
if (!ec) |
|||
try |
|||
{ |
|||
try { |
|||
clog(NetNote) << "Accepted connection from " << m_socket.remote_endpoint(); |
|||
} catch (...){} |
|||
bi::address remoteAddress = m_socket.remote_endpoint().address(); |
|||
// Port defaults to 0 - we let the hello tell us which port the peer listens to
|
|||
auto p = std::make_shared<PeerSession>(this, std::move(m_socket), m_networkId, remoteAddress); |
|||
p->start(); |
|||
} |
|||
catch (std::exception const& _e) |
|||
{ |
|||
clog(NetWarn) << "ERROR: " << _e.what(); |
|||
} |
|||
m_accepting = false; |
|||
if (ec.value() != 1 && (m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2)) |
|||
ensureAccepting(); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
void PeerServer::connect(std::string const& _addr, unsigned short _port) noexcept |
|||
{ |
|||
try |
|||
{ |
|||
connect(bi::tcp::endpoint(bi::address::from_string(_addr), _port)); |
|||
} |
|||
catch (exception const& e) |
|||
{ |
|||
// Couldn't connect
|
|||
clog(NetNote) << "Bad host " << _addr << " (" << e.what() << ")"; |
|||
} |
|||
} |
|||
|
|||
void PeerServer::connect(bi::tcp::endpoint const& _ep) |
|||
{ |
|||
clog(NetNote) << "Attempting connection to " << _ep; |
|||
bi::tcp::socket* s = new bi::tcp::socket(m_ioService); |
|||
s->async_connect(_ep, [=](boost::system::error_code const& ec) |
|||
{ |
|||
if (ec) |
|||
{ |
|||
clog(NetNote) << "Connection refused to " << _ep << " (" << ec.message() << ")"; |
|||
for (auto i = m_incomingPeers.begin(); i != m_incomingPeers.end(); ++i) |
|||
if (i->second.first == _ep && i->second.second < 3) |
|||
{ |
|||
m_freePeers.push_back(i->first); |
|||
goto OK; |
|||
} |
|||
// for-else
|
|||
clog(NetNote) << "Giving up."; |
|||
OK:; |
|||
} |
|||
else |
|||
{ |
|||
auto p = make_shared<PeerSession>(this, std::move(*s), m_networkId, _ep.address(), _ep.port()); |
|||
clog(NetNote) << "Connected to " << _ep; |
|||
p->start(); |
|||
} |
|||
delete s; |
|||
}); |
|||
} |
|||
|
|||
bool PeerServer::sync() |
|||
{ |
|||
bool ret = false; |
|||
if (isInitialised()) |
|||
for (auto i = m_peers.begin(); i != m_peers.end();) |
|||
{ |
|||
auto p = i->second.lock(); |
|||
if (p && p->m_socket.is_open() && |
|||
(p->m_disconnect == chrono::steady_clock::time_point::max() || chrono::steady_clock::now() - p->m_disconnect < chrono::seconds(1))) // kill old peers that should be disconnected.
|
|||
++i; |
|||
else |
|||
{ |
|||
i = m_peers.erase(i); |
|||
ret = true; |
|||
} |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
bool PeerServer::ensureInitialised(BlockChain& _bc, TransactionQueue& _tq) |
|||
{ |
|||
if (m_latestBlockSent == h256()) |
|||
{ |
|||
// First time - just initialise.
|
|||
m_latestBlockSent = _bc.currentHash(); |
|||
clog(NetNote) << "Initialising: latest=" << m_latestBlockSent; |
|||
|
|||
for (auto const& i: _tq.transactions()) |
|||
m_transactionsSent.insert(i.first); |
|||
m_lastPeersRequest = chrono::steady_clock::time_point::min(); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) |
|||
{ |
|||
bool ret = ensureInitialised(_bc, _tq); |
|||
|
|||
if (sync()) |
|||
ret = true; |
|||
|
|||
if (m_mode == NodeMode::Full) |
|||
{ |
|||
for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) |
|||
if (_tq.import(*it)) |
|||
{}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce...
|
|||
else |
|||
m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on.
|
|||
m_incomingTransactions.clear(); |
|||
|
|||
auto h = _bc.currentHash(); |
|||
bool resendAll = (h != m_latestBlockSent); |
|||
|
|||
// Send any new transactions.
|
|||
for (auto j: m_peers) |
|||
if (auto p = j.second.lock()) |
|||
{ |
|||
bytes b; |
|||
uint n = 0; |
|||
for (auto const& i: _tq.transactions()) |
|||
if ((!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) || p->m_requireTransactions || resendAll) |
|||
{ |
|||
b += i.second; |
|||
++n; |
|||
m_transactionsSent.insert(i.first); |
|||
} |
|||
if (n) |
|||
{ |
|||
RLPStream ts; |
|||
PeerSession::prep(ts); |
|||
ts.appendList(n + 1) << TransactionsPacket; |
|||
ts.appendRaw(b, n).swapOut(b); |
|||
seal(b); |
|||
p->send(&b); |
|||
} |
|||
p->m_knownTransactions.clear(); |
|||
p->m_requireTransactions = false; |
|||
} |
|||
|
|||
// Send any new blocks.
|
|||
if (h != m_latestBlockSent) |
|||
{ |
|||
// TODO: find where they diverge and send complete new branch.
|
|||
RLPStream ts; |
|||
PeerSession::prep(ts); |
|||
ts.appendList(2) << BlocksPacket; |
|||
bytes b; |
|||
ts.appendRaw(_bc.block(_bc.currentHash())).swapOut(b); |
|||
seal(b); |
|||
for (auto j: m_peers) |
|||
if (auto p = j.second.lock()) |
|||
{ |
|||
if (!p->m_knownBlocks.count(_bc.currentHash())) |
|||
p->send(&b); |
|||
p->m_knownBlocks.clear(); |
|||
} |
|||
} |
|||
m_latestBlockSent = h; |
|||
|
|||
for (int accepted = 1, n = 0; accepted; ++n) |
|||
{ |
|||
accepted = 0; |
|||
|
|||
if (m_incomingBlocks.size()) |
|||
for (auto it = prev(m_incomingBlocks.end());; --it) |
|||
{ |
|||
try |
|||
{ |
|||
_bc.import(*it, _o); |
|||
it = m_incomingBlocks.erase(it); |
|||
++accepted; |
|||
ret = true; |
|||
} |
|||
catch (UnknownParent) |
|||
{ |
|||
// Don't (yet) know its parent. Leave it for later.
|
|||
m_unknownParentBlocks.push_back(*it); |
|||
it = m_incomingBlocks.erase(it); |
|||
} |
|||
catch (...) |
|||
{ |
|||
// Some other error - erase it.
|
|||
it = m_incomingBlocks.erase(it); |
|||
} |
|||
|
|||
if (it == m_incomingBlocks.begin()) |
|||
break; |
|||
} |
|||
if (!n && accepted) |
|||
{ |
|||
for (auto i: m_unknownParentBlocks) |
|||
m_incomingBlocks.push_back(i); |
|||
m_unknownParentBlocks.clear(); |
|||
} |
|||
} |
|||
|
|||
// Connect to additional peers
|
|||
while (m_peers.size() < m_idealPeerCount) |
|||
{ |
|||
if (m_freePeers.empty()) |
|||
{ |
|||
if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10)) |
|||
{ |
|||
RLPStream s; |
|||
bytes b; |
|||
(PeerSession::prep(s).appendList(1) << GetPeersPacket).swapOut(b); |
|||
seal(b); |
|||
for (auto const& i: m_peers) |
|||
if (auto p = i.second.lock()) |
|||
if (p->isOpen()) |
|||
p->send(&b); |
|||
m_lastPeersRequest = chrono::steady_clock::now(); |
|||
} |
|||
|
|||
|
|||
if (!m_accepting) |
|||
ensureAccepting(); |
|||
|
|||
break; |
|||
} |
|||
|
|||
auto x = time(0) % m_freePeers.size(); |
|||
m_incomingPeers[m_freePeers[x]].second++; |
|||
connect(m_incomingPeers[m_freePeers[x]].first); |
|||
m_freePeers.erase(m_freePeers.begin() + x); |
|||
} |
|||
} |
|||
|
|||
// platform for consensus of social contract.
|
|||
// restricts your freedom but does so fairly. and that's the value proposition.
|
|||
// guarantees that everyone else respect the rules of the system. (i.e. obeys laws).
|
|||
|
|||
// We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there.
|
|||
for (uint old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2) |
|||
while (m_peers.size() > m_idealPeerCount) |
|||
{ |
|||
// look for worst peer to kick off
|
|||
// first work out how many are old enough to kick off.
|
|||
shared_ptr<PeerSession> worst; |
|||
unsigned agedPeers = 0; |
|||
for (auto i: m_peers) |
|||
if (auto p = i.second.lock()) |
|||
if ((m_mode != NodeMode::PeerServer || p->m_caps != 0x01) && chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers.
|
|||
{ |
|||
++agedPeers; |
|||
if ((!worst || p->m_rating < worst->m_rating || (p->m_rating == worst->m_rating && p->m_connect > worst->m_connect))) // kill older ones
|
|||
worst = p; |
|||
} |
|||
if (!worst || agedPeers <= m_idealPeerCount) |
|||
break; |
|||
worst->disconnect(TooManyPeers); |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
std::vector<PeerInfo> PeerServer::peers() const |
|||
{ |
|||
const_cast<PeerServer*>(this)->pingAll(); |
|||
this_thread::sleep_for(chrono::milliseconds(200)); |
|||
std::vector<PeerInfo> ret; |
|||
for (auto& i: m_peers) |
|||
if (auto j = i.second.lock()) |
|||
if (j->m_socket.is_open()) |
|||
ret.push_back(j->m_info); |
|||
return ret; |
|||
} |
|||
|
|||
void PeerServer::pingAll() |
|||
{ |
|||
for (auto& i: m_peers) |
|||
if (auto j = i.second.lock()) |
|||
j->ping(); |
|||
} |
|||
|
|||
bytes PeerServer::savePeers() const |
|||
{ |
|||
RLPStream ret; |
|||
int n = 0; |
|||
for (auto& i: m_peers) |
|||
if (auto p = i.second.lock()) |
|||
if (p->m_socket.is_open() && p->endpoint().port()) |
|||
{ |
|||
ret.appendList(3) << p->endpoint().address().to_v4().to_bytes() << p->endpoint().port() << p->m_id; |
|||
n++; |
|||
} |
|||
return RLPStream(n).appendRaw(ret.out(), n).out(); |
|||
} |
|||
|
|||
void PeerServer::restorePeers(bytesConstRef _b) |
|||
{ |
|||
for (auto i: RLP(_b)) |
|||
{ |
|||
auto k = (Public)i[2]; |
|||
if (!m_incomingPeers.count(k)) |
|||
{ |
|||
m_incomingPeers.insert(make_pair(k, make_pair(bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>()), 0))); |
|||
m_freePeers.push_back(k); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,133 @@ |
|||
/*
|
|||
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 PeerServer.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <vector> |
|||
#include <set> |
|||
#include <memory> |
|||
#include <utility> |
|||
#include <thread> |
|||
#include "PeerNetwork.h" |
|||
#include "CommonEth.h" |
|||
namespace ba = boost::asio; |
|||
namespace bi = boost::asio::ip; |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
class PeerServer |
|||
{ |
|||
friend class PeerSession; |
|||
|
|||
public: |
|||
/// Start server, listening for connections on the given port.
|
|||
PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, unsigned short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); |
|||
/// Start server, but don't listen.
|
|||
PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m = NodeMode::Full); |
|||
~PeerServer(); |
|||
|
|||
static unsigned protocolVersion(); |
|||
unsigned networkId() { return m_networkId; } |
|||
|
|||
/// Connect to a peer explicitly.
|
|||
void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; |
|||
void connect(bi::tcp::endpoint const& _ep); |
|||
|
|||
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
|
|||
bool sync(BlockChain& _bc, TransactionQueue&, Overlay& _o); |
|||
bool sync(); |
|||
|
|||
/// Conduct I/O, polling, syncing, whatever.
|
|||
/// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway.
|
|||
/// This won't touch alter the blockchain.
|
|||
void process() { if (isInitialised()) m_ioService.poll(); } |
|||
|
|||
/// Set ideal number of peers.
|
|||
void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } |
|||
|
|||
void setMode(NodeMode _m) { m_mode = _m; } |
|||
|
|||
/// Get peer information.
|
|||
std::vector<PeerInfo> peers() const; |
|||
|
|||
/// Get number of peers connected; equivalent to, but faster than, peers().size().
|
|||
size_t peerCount() const { return m_peers.size(); } |
|||
|
|||
/// Ping the peers, to update the latency information.
|
|||
void pingAll(); |
|||
|
|||
/// Get the port we're listening on currently.
|
|||
unsigned short listenPort() const { return m_public.port(); } |
|||
|
|||
bytes savePeers() const; |
|||
void restorePeers(bytesConstRef _b); |
|||
|
|||
private: |
|||
void seal(bytes& _b); |
|||
void populateAddresses(); |
|||
void determinePublic(std::string const& _publicAddress, bool _upnp); |
|||
void ensureAccepting(); |
|||
|
|||
/// Check to see if the network peer-state initialisation has happened.
|
|||
bool isInitialised() const { return m_latestBlockSent; } |
|||
/// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first.
|
|||
bool ensureInitialised(BlockChain& _bc, TransactionQueue& _tq); |
|||
|
|||
std::map<Public, bi::tcp::endpoint> potentialPeers(); |
|||
|
|||
std::string m_clientVersion; |
|||
NodeMode m_mode = NodeMode::Full; |
|||
|
|||
unsigned short m_listenPort; |
|||
|
|||
BlockChain const* m_chain = nullptr; |
|||
ba::io_service m_ioService; |
|||
bi::tcp::acceptor m_acceptor; |
|||
bi::tcp::socket m_socket; |
|||
|
|||
UPnP* m_upnp = nullptr; |
|||
bi::tcp::endpoint m_public; |
|||
KeyPair m_key; |
|||
|
|||
unsigned m_networkId; |
|||
std::map<Public, std::weak_ptr<PeerSession>> m_peers; |
|||
|
|||
std::vector<bytes> m_incomingTransactions; |
|||
std::vector<bytes> m_incomingBlocks; |
|||
std::vector<bytes> m_unknownParentBlocks; |
|||
std::vector<Public> m_freePeers; |
|||
std::map<Public, std::pair<bi::tcp::endpoint, unsigned>> m_incomingPeers; |
|||
|
|||
h256 m_latestBlockSent; |
|||
std::set<h256> m_transactionsSent; |
|||
|
|||
std::chrono::steady_clock::time_point m_lastPeersRequest; |
|||
unsigned m_idealPeerCount = 5; |
|||
|
|||
std::vector<bi::address_v4> m_addresses; |
|||
std::vector<bi::address_v4> m_peerAddresses; |
|||
|
|||
bool m_accepting = false; |
|||
}; |
|||
|
|||
} |
@ -0,0 +1,585 @@ |
|||
/*
|
|||
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 PeerSession.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "PeerSession.h" |
|||
|
|||
#include <chrono> |
|||
#include "Exceptions.h" |
|||
#include "Common.h" |
|||
#include "BlockChain.h" |
|||
#include "BlockInfo.h" |
|||
#include "PeerServer.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
#define clogS(X) eth::LogOutputStream<X, true>(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " |
|||
|
|||
static const eth::uint c_maxHashes = 32; ///< Maximum number of hashes GetChain will ever send.
|
|||
static const eth::uint c_maxBlocks = 32; ///< Maximum number of blocks Blocks will ever send. BUG: if this gets too big (e.g. 2048) stuff starts going wrong.
|
|||
static const eth::uint c_maxBlocksAsk = 256; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
|
|||
|
|||
PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _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)}); |
|||
} |
|||
|
|||
PeerSession::~PeerSession() |
|||
{ |
|||
m_socket.close(); |
|||
} |
|||
|
|||
bi::tcp::endpoint PeerSession::endpoint() const |
|||
{ |
|||
if (m_socket.is_open()) |
|||
try { |
|||
return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_listenPort); |
|||
} catch (...){} |
|||
|
|||
return bi::tcp::endpoint(); |
|||
} |
|||
|
|||
// TODO: BUG! 256 -> work out why things start to break with big packet sizes -> g.t. ~370 blocks.
|
|||
|
|||
bool PeerSession::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<uint>(); |
|||
auto clientVersion = _r[3].toString(); |
|||
m_caps = _r[4].toInt<uint>(); |
|||
m_listenPort = _r[5].toInt<unsigned short>(); |
|||
m_id = _r[6].toHash<h512>(); |
|||
|
|||
clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << m_id.abridged() << showbase << hex << m_caps << dec << m_listenPort; |
|||
|
|||
if (m_server->m_peers.count(m_id)) |
|||
if (auto l = m_server->m_peers[m_id].lock()) |
|||
if (l.get() != this && l->isOpen()) |
|||
{ |
|||
// Already connected.
|
|||
cwarn << "Already have peer id" << m_id.abridged() << "at" << l->endpoint() << "rather than" << endpoint(); |
|||
disconnect(DuplicatePeer); |
|||
return false; |
|||
} |
|||
|
|||
if (m_protocolVersion != PeerServer::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->m_peers[m_id] = shared_from_this(); |
|||
|
|||
// Grab their block chain off them.
|
|||
{ |
|||
clogS(NetAllDetail) << "Want chain. Latest:" << m_server->m_latestBlockSent << ", number:" << m_server->m_chain->details(m_server->m_latestBlockSent).number; |
|||
uint count = std::min(c_maxHashes, m_server->m_chain->details(m_server->m_latestBlockSent).number + 1); |
|||
RLPStream s; |
|||
prep(s).appendList(2 + count); |
|||
s << GetChainPacket; |
|||
auto h = m_server->m_latestBlockSent; |
|||
for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) |
|||
{ |
|||
clogS(NetAllDetail) << " " << i << ":" << h; |
|||
s << h; |
|||
} |
|||
|
|||
s << c_maxBlocksAsk; |
|||
sealAndSend(s); |
|||
s.clear(); |
|||
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(NetMessageSummary) << "Ping";
|
|||
RLPStream s; |
|||
sealAndSend(prep(s).appendList(1) << PongPacket); |
|||
break; |
|||
} |
|||
case PongPacket: |
|||
m_info.lastPing = std::chrono::steady_clock::now() - m_ping; |
|||
// clogS(NetMessageSummary) << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_lastPing).count() << " ms";
|
|||
break; |
|||
case GetPeersPacket: |
|||
{ |
|||
clogS(NetMessageSummary) << "GetPeers"; |
|||
auto peers = m_server->potentialPeers(); |
|||
RLPStream s; |
|||
prep(s).appendList(peers.size() + 1); |
|||
s << PeersPacket; |
|||
for (auto i: peers) |
|||
{ |
|||
clogS(NetMessageDetail) << "Sending peer " << toHex(i.first.ref().cropped(0, 4)) << i.second; |
|||
s.appendList(3) << i.second.address().to_v4().to_bytes() << i.second.port() << i.first; |
|||
} |
|||
sealAndSend(s); |
|||
break; |
|||
} |
|||
case PeersPacket: |
|||
clogS(NetMessageSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
bi::address_v4 peerAddress(_r[i][0].toArray<byte, 4>()); |
|||
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 << "(" << toHex(id.ref().cropped(0, 4)) << ")"; |
|||
|
|||
// check that it's not us or one we already know:
|
|||
if (id && (m_server->m_key.pub() == id || m_server->m_peers.count(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_peers) |
|||
if (shared_ptr<PeerSession> p = i.second.lock()) |
|||
{ |
|||
clogS(NetAllDetail) << " ...against " << p->endpoint(); |
|||
if (p->m_socket.is_open() && p->endpoint() == ep) |
|||
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); |
|||
clogS(NetMessageDetail) << "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 BlocksPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << " entries)"; |
|||
unsigned used = 0; |
|||
for (unsigned i = 1; i < _r.itemCount(); ++i) |
|||
{ |
|||
auto h = sha3(_r[i].data()); |
|||
if (!m_server->m_chain->details(h)) |
|||
{ |
|||
m_server->m_incomingBlocks.push_back(_r[i].data().toBytes()); |
|||
m_knownBlocks.insert(h); |
|||
used++; |
|||
} |
|||
} |
|||
m_rating += used; |
|||
if (g_logVerbosity >= 3) |
|||
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)) |
|||
clogS(NetMessageDetail) << "Unknown parent " << bi.parentHash << " of block " << h; |
|||
else |
|||
clogS(NetMessageDetail) << "Known parent " << bi.parentHash << " of block " << h; |
|||
} |
|||
if (used) // we received some - check if there's any more
|
|||
{ |
|||
RLPStream s; |
|||
prep(s).appendList(3); |
|||
s << GetChainPacket; |
|||
s << sha3(_r[1].data()); |
|||
s << c_maxBlocksAsk; |
|||
sealAndSend(s); |
|||
} |
|||
break; |
|||
} |
|||
case GetChainPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
clogS(NetMessageSummary) << "GetChain (" << (_r.itemCount() - 2) << " hashes, " << (_r[_r.itemCount() - 1].toInt<bigint>()) << ")"; |
|||
// ********************************************************************
|
|||
// NEEDS FULL REWRITE!
|
|||
h256s parents; |
|||
parents.reserve(_r.itemCount() - 2); |
|||
for (unsigned i = 1; i < _r.itemCount() - 1; ++i) |
|||
parents.push_back(_r[i].toHash<h256>()); |
|||
if (_r.itemCount() == 2) |
|||
break; |
|||
// return 2048 block max.
|
|||
uint baseCount = (uint)min<bigint>(_r[_r.itemCount() - 1].toInt<bigint>(), c_maxBlocks); |
|||
clogS(NetMessageSummary) << "GetChain (" << baseCount << " max, from " << parents.front() << " to " << parents.back() << ")"; |
|||
for (auto parent: parents) |
|||
{ |
|||
auto h = m_server->m_chain->currentHash(); |
|||
h256 latest = m_server->m_chain->currentHash(); |
|||
uint latestNumber = 0; |
|||
uint parentNumber = 0; |
|||
RLPStream s; |
|||
|
|||
if (m_server->m_chain->details(parent)) |
|||
{ |
|||
latestNumber = m_server->m_chain->details(latest).number; |
|||
parentNumber = m_server->m_chain->details(parent).number; |
|||
uint count = min<uint>(latestNumber - parentNumber, baseCount); |
|||
clogS(NetAllDetail) << "Requires " << dec << (latestNumber - parentNumber) << " blocks from " << latestNumber << " to " << parentNumber; |
|||
clogS(NetAllDetail) << latest << " - " << parent; |
|||
|
|||
prep(s); |
|||
s.appendList(1 + count) << BlocksPacket; |
|||
uint endNumber = m_server->m_chain->details(parent).number; |
|||
uint startNumber = endNumber + count; |
|||
clogS(NetAllDetail) << "Sending " << dec << count << " blocks from " << startNumber << " to " << endNumber; |
|||
|
|||
uint n = latestNumber; |
|||
for (; n > startNumber; n--, h = m_server->m_chain->details(h).parent) {} |
|||
for (uint i = 0; i < count; ++i, --n, h = m_server->m_chain->details(h).parent) |
|||
{ |
|||
if (h == parent || n == endNumber) |
|||
{ |
|||
cwarn << "BUG! Couldn't create the reply for GetChain!"; |
|||
return true; |
|||
} |
|||
clogS(NetAllDetail) << " " << dec << i << " " << h; |
|||
s.appendRaw(m_server->m_chain->block(h)); |
|||
} |
|||
clogS(NetAllDetail) << "Parent: " << h; |
|||
} |
|||
else if (parent != parents.back()) |
|||
continue; |
|||
|
|||
if (h != parent) |
|||
{ |
|||
// not in the blockchain;
|
|||
if (parent == parents.back()) |
|||
{ |
|||
// out of parents...
|
|||
clogS(NetAllDetail) << "GetChain failed; not in chain"; |
|||
// No good - must have been on a different branch.
|
|||
s.clear(); |
|||
prep(s).appendList(2) << NotInChainPacket << parents.back(); |
|||
} |
|||
else |
|||
// still some parents left - try them.
|
|||
continue; |
|||
} |
|||
// send the packet (either Blocks or NotInChain) & exit.
|
|||
sealAndSend(s); |
|||
break; |
|||
// ********************************************************************
|
|||
} |
|||
break; |
|||
} |
|||
case NotInChainPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
h256 noGood = _r[1].toHash<h256>(); |
|||
clogS(NetMessageSummary) << "NotInChain (" << noGood << ")"; |
|||
if (noGood == m_server->m_chain->genesisHash()) |
|||
{ |
|||
clogS(NetWarn) << "Discordance over genesis block! Disconnect."; |
|||
disconnect(WrongGenesis); |
|||
} |
|||
else |
|||
{ |
|||
uint count = std::min(c_maxHashes, m_server->m_chain->details(noGood).number); |
|||
RLPStream s; |
|||
prep(s).appendList(2 + count); |
|||
s << GetChainPacket; |
|||
auto h = m_server->m_chain->details(noGood).parent; |
|||
for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) |
|||
s << h; |
|||
s << c_maxBlocksAsk; |
|||
sealAndSend(s); |
|||
} |
|||
break; |
|||
} |
|||
case GetTransactionsPacket: |
|||
{ |
|||
if (m_server->m_mode == NodeMode::PeerServer) |
|||
break; |
|||
m_requireTransactions = true; |
|||
break; |
|||
} |
|||
default: |
|||
break; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
void PeerSession::ping() |
|||
{ |
|||
RLPStream s; |
|||
sealAndSend(prep(s).appendList(1) << PingPacket); |
|||
m_ping = std::chrono::steady_clock::now(); |
|||
} |
|||
|
|||
RLPStream& PeerSession::prep(RLPStream& _s) |
|||
{ |
|||
return _s.appendRaw(bytes(8, 0)); |
|||
} |
|||
|
|||
void PeerServer::seal(bytes& _b) |
|||
{ |
|||
_b[0] = 0x22; |
|||
_b[1] = 0x40; |
|||
_b[2] = 0x08; |
|||
_b[3] = 0x91; |
|||
uint32_t len = (uint32_t)_b.size() - 8; |
|||
_b[4] = (len >> 24) & 0xff; |
|||
_b[5] = (len >> 16) & 0xff; |
|||
_b[6] = (len >> 8) & 0xff; |
|||
_b[7] = len & 0xff; |
|||
} |
|||
|
|||
void PeerSession::sealAndSend(RLPStream& _s) |
|||
{ |
|||
bytes b; |
|||
_s.swapOut(b); |
|||
m_server->seal(b); |
|||
sendDestroy(b); |
|||
} |
|||
|
|||
bool PeerSession::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 PeerSession::sendDestroy(bytes& _msg) |
|||
{ |
|||
clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); |
|||
|
|||
if (!checkPacket(bytesConstRef(&_msg))) |
|||
{ |
|||
cwarn << "INVALID PACKET CONSTRUCTED!"; |
|||
} |
|||
|
|||
auto self(shared_from_this()); |
|||
bytes* buffer = new bytes(std::move(_msg)); |
|||
ba::async_write(m_socket, ba::buffer(*buffer), [self, buffer](boost::system::error_code ec, std::size_t length) |
|||
{ |
|||
delete buffer; |
|||
if (ec) |
|||
{ |
|||
cwarn << "Error sending: " << ec.message(); |
|||
self->dropped(); |
|||
} |
|||
// cbug << length << " bytes written (EC: " << ec << ")";
|
|||
}); |
|||
} |
|||
|
|||
void PeerSession::send(bytesConstRef _msg) |
|||
{ |
|||
clogS(NetLeft) << RLP(_msg.cropped(8)); |
|||
|
|||
if (!checkPacket(_msg)) |
|||
{ |
|||
cwarn << "INVALID PACKET CONSTRUCTED!"; |
|||
} |
|||
|
|||
auto self(shared_from_this()); |
|||
bytes* buffer = new bytes(_msg.toBytes()); |
|||
ba::async_write(m_socket, ba::buffer(*buffer), [self, buffer](boost::system::error_code ec, std::size_t length) |
|||
{ |
|||
delete buffer; |
|||
if (ec) |
|||
{ |
|||
cwarn << "Error sending: " << ec.message(); |
|||
self->dropped(); |
|||
} |
|||
// cbug << length << " bytes written (EC: " << ec << ")";
|
|||
}); |
|||
} |
|||
|
|||
void PeerSession::dropped() |
|||
{ |
|||
if (m_socket.is_open()) |
|||
try { |
|||
clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); |
|||
m_socket.close(); |
|||
}catch (...){} |
|||
for (auto i = m_server->m_peers.begin(); i != m_server->m_peers.end(); ++i) |
|||
if (i->second.lock().get() == this) |
|||
{ |
|||
m_server->m_peers.erase(i); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
void PeerSession::disconnect(int _reason) |
|||
{ |
|||
clogS(NetNote) << "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 PeerSession::start() |
|||
{ |
|||
RLPStream s; |
|||
prep(s); |
|||
s.appendList(7) << HelloPacket << (uint)PeerServer::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(); |
|||
sealAndSend(s); |
|||
|
|||
ping(); |
|||
|
|||
doRead(); |
|||
} |
|||
|
|||
void PeerSession::doRead() |
|||
{ |
|||
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 (ec) |
|||
{ |
|||
cwarn << "Error reading: " << ec.message(); |
|||
dropped(); |
|||
} |
|||
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) |
|||
{ |
|||
clogS(NetWarn) << "Out of alignment."; |
|||
disconnect(BadProtocol); |
|||
return; |
|||
clogS(NetNote) << "Skipping: " << hex << showbase << (int)m_incoming[0] << dec; |
|||
memmove(m_incoming.data(), m_incoming.data() + 1, m_incoming.size() - 1); |
|||
m_incoming.resize(m_incoming.size() - 1); |
|||
} |
|||
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.
|
|||
// cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl;
|
|||
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(); |
|||
} |
|||
} |
|||
}); |
|||
} |
@ -0,0 +1,90 @@ |
|||
/*
|
|||
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 PeerSession.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <set> |
|||
#include <memory> |
|||
#include <utility> |
|||
#include "RLP.h" |
|||
#include "CommonEth.h" |
|||
#include "PeerNetwork.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
class PeerSession: public std::enable_shared_from_this<PeerSession> |
|||
{ |
|||
friend class PeerServer; |
|||
|
|||
public: |
|||
PeerSession(PeerServer* _server, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort = 0); |
|||
~PeerSession(); |
|||
|
|||
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 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); |
|||
PeerServer* m_server; |
|||
|
|||
bi::tcp::socket m_socket; |
|||
std::array<byte, 65536> m_data; |
|||
PeerInfo m_info; |
|||
Public m_id; |
|||
|
|||
bytes m_incoming; |
|||
uint m_protocolVersion; |
|||
uint m_networkId; |
|||
uint m_reqNetworkId; |
|||
unsigned short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers.
|
|||
uint m_caps; |
|||
|
|||
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; |
|||
}; |
|||
|
|||
} |
@ -1,8 +0,0 @@ |
|||
#!/bin/bash |
|||
|
|||
set -e |
|||
rm -f ../cpp-ethereum_*_source.changes |
|||
debuild -S -sa |
|||
cd .. |
|||
dput -f ppa:ethereum/ethereum cpp-ethereum_*_source.changes |
|||
|
@ -1,65 +0,0 @@ |
|||
#!/bin/bash |
|||
|
|||
dist="saucy" |
|||
version=$(grep "define ETH_VERSION" libethereum/Common.h | cut -d ' ' -f 3) |
|||
branch="$(git branch | grep \* | cut -c 3-)" |
|||
|
|||
if [[ ! "$1" == "" ]]; then |
|||
version=$1 |
|||
fi |
|||
|
|||
if [[ ! "$3" == "" ]]; then |
|||
if [[ ! "$4" == "" ]]; then |
|||
dist=$4 |
|||
fi |
|||
if [[ "$2" == "-i" ]]; then |
|||
# increment current debian release only |
|||
# new version ./release VERSION -i MESSAGE DIST |
|||
debchange -i -p "$3" -D "$dist" |
|||
git commit -a -m "$3" |
|||
else |
|||
# new version ./release VERSION DEB-VERSION MESSAGE DIST |
|||
debchange -v $version-$2 -p "$3" -D "$dist" |
|||
git commit -a -m "$3" |
|||
fi |
|||
fi |
|||
|
|||
opwd=`pwd` |
|||
cd /tmp |
|||
|
|||
echo Checking out... |
|||
git clone $opwd |
|||
cd cpp-ethereum |
|||
git checkout "$branch" |
|||
|
|||
archdir="cpp-ethereum-$version" |
|||
archfile="$archdir.tar.bz2" |
|||
|
|||
echo Cleaning backup files... |
|||
find . | grep \~ | xargs rm -f |
|||
|
|||
echo Cleaning others... |
|||
rm release.sh |
|||
|
|||
echo Cleaning versioning... |
|||
rm -rf .git .gitignore |
|||
|
|||
echo Renaming directory... |
|||
cd .. |
|||
rm -rf $archdir |
|||
mv cpp-ethereum $archdir |
|||
|
|||
echo Creating archive... |
|||
tar c $archdir | bzip2 -- > $archfile |
|||
|
|||
[[ ! "$version" == "" ]] && ln -sf $archfile "cpp-ethereum_$version.orig.tar.bz2" |
|||
|
|||
echo Packaging... |
|||
cd "$archdir" |
|||
./package.sh |
|||
|
|||
echo Cleaning up... |
|||
rm -rf /tmp/$archdir |
|||
mv /tmp/$archfile ~ |
|||
|
|||
echo Done. |
@ -0,0 +1,25 @@ |
|||
|
|||
function os.capture(cmd) |
|||
local f = io.popen(cmd, 'r') |
|||
if (f) then |
|||
local s = f:read('*a') |
|||
if (f:close()) then |
|||
return s |
|||
end |
|||
end |
|||
return nil |
|||
end |
|||
|
|||
hash = (os.capture("git rev-parse HEAD") or "UnknownRevision"):gsub("\n$", "") |
|||
clean = ((os.capture("git diff --name-only") or "0"):gsub("\n$", "") == "") and "1" or "0" |
|||
|
|||
local output = io.open(arg[1], "w") |
|||
if (output) then |
|||
output:write("// This file was automatically generated by buildinfo.lua\n#pragma once\n\n") |
|||
output:write("#define ETH_COMMIT_HASH "..hash.."\n") |
|||
output:write("#define ETH_CLEAN_REPO "..clean.."\n") |
|||
output:close() |
|||
end |
|||
|
|||
|
|||
|
@ -0,0 +1,16 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<ClCompile Include="..\eth\main.cpp" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Filter Include="Windows"> |
|||
<UniqueIdentifier>{ed0eafbf-bbfb-4700-b7c0-9b58049cc681}</UniqueIdentifier> |
|||
</Filter> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<CustomBuild Include="BuildInfo.lua"> |
|||
<Filter>Windows</Filter> |
|||
</CustomBuild> |
|||
</ItemGroup> |
|||
</Project> |
Loading…
Reference in new issue