Browse Source
Conflicts: README.md libethereum/Instruction.cpp libethereum/Instruction.h libethereum/PeerNetwork.cppcl-refactor
109 changed files with 7628 additions and 3789 deletions
@ -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, libboost-thread-dev, libcryptoppeth-dev, libboost-filesystem-dev, libboost-mpi-dev, libboost-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} |
|||
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} |
|||
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}) |
|||
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/alethzero/usr/bin |
|||
mv $(CURDIR)/debian/libethereum-dev/usr/bin/alethzero $(CURDIR)/debian/alethzero/usr/bin |
|||
|
@ -0,0 +1,48 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
|
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
include_directories(../libethereum) |
|||
link_directories(../libethereum) |
|||
|
|||
add_executable(eth ${SRC_LIST}) |
|||
|
|||
if (${TARGET_PLATFORM} STREQUAL "w64") |
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") |
|||
target_link_libraries(eth gcc) |
|||
target_link_libraries(eth gdi32) |
|||
target_link_libraries(eth ws2_32) |
|||
target_link_libraries(eth mswsock) |
|||
target_link_libraries(eth shlwapi) |
|||
target_link_libraries(eth iphlpapi) |
|||
target_link_libraries(eth cryptopp) |
|||
target_link_libraries(eth ncurses) |
|||
target_link_libraries(eth form) |
|||
target_link_libraries(eth boost_system-mt-s) |
|||
target_link_libraries(eth boost_filesystem-mt-s) |
|||
target_link_libraries(eth boost_thread_win32-mt-s) |
|||
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) |
|||
elseif (UNIX) |
|||
target_link_libraries(eth ncurses) |
|||
target_link_libraries(eth form) |
|||
else () |
|||
target_link_libraries(eth ${CRYPTOPP_LIBRARIES}) |
|||
target_link_libraries(eth boost_system) |
|||
target_link_libraries(eth boost_filesystem) |
|||
target_link_libraries(eth ncurses) |
|||
target_link_libraries(eth form) |
|||
find_package(Threads REQUIRED) |
|||
target_link_libraries(eth ${CMAKE_THREAD_LIBS_INIT}) |
|||
endif () |
|||
|
|||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") |
|||
include_directories(/usr/local/include) |
|||
endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") |
|||
|
|||
target_link_libraries(eth ethereum) |
|||
target_link_libraries(eth miniupnpc) |
|||
target_link_libraries(eth leveldb) |
|||
target_link_libraries(eth gmp) |
|||
|
|||
install( TARGETS eth DESTINATION bin ) |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,95 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Common.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "CommonData.h" |
|||
|
|||
#include <random> |
|||
#include "Exceptions.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
std::string eth::escaped(std::string const& _s, bool _all) |
|||
{ |
|||
std::string ret; |
|||
ret.reserve(_s.size()); |
|||
ret.push_back('"'); |
|||
for (auto i: _s) |
|||
if (i == '"' && !_all) |
|||
ret += "\\\""; |
|||
else if (i == '\\' && !_all) |
|||
ret += "\\\\"; |
|||
else if (i < ' ' || _all) |
|||
{ |
|||
ret += "\\x"; |
|||
ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); |
|||
ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); |
|||
} |
|||
else |
|||
ret.push_back(i); |
|||
ret.push_back('"'); |
|||
return ret; |
|||
} |
|||
|
|||
std::string eth::randomWord() |
|||
{ |
|||
static std::mt19937_64 s_eng(0); |
|||
std::string ret(std::uniform_int_distribution<int>(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) |
|||
{ |
|||
if (_s.size() % 2 || _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,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 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.
|
|||
|
|||
/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero.
|
|||
template <class _T> |
|||
inline uint bytesRequired(_T _i) |
|||
{ |
|||
uint i = 0; |
|||
for (; _i != 0; ++i, _i >>= 8) {} |
|||
return i; |
|||
} |
|||
|
|||
/// 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,189 @@ |
|||
/*
|
|||
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" |
|||
#include "CryptoHeaders.h" |
|||
#include "Exceptions.h" |
|||
#include <random> |
|||
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,36 @@ |
|||
/*
|
|||
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 CryptoHeaders.h
|
|||
* @author Tim Hughes <tim@twistedfury.com> |
|||
* @date 2014 |
|||
*/ |
|||
#pragma once |
|||
|
|||
// need to leave this one disabled
|
|||
#pragma GCC diagnostic ignored "-Wunused-function" |
|||
|
|||
#pragma warning(push) |
|||
#pragma warning(disable:4100 4244) |
|||
#pragma GCC diagnostic push |
|||
#pragma GCC diagnostic ignored "-Wconversion" |
|||
#pragma GCC diagnostic ignored "-Wunused-parameter" |
|||
#include <sha.h> |
|||
#include <sha3.h> |
|||
#include <ripemd.h> |
|||
#include <secp256k1.h> |
|||
#pragma warning(pop) |
|||
#pragma GCC diagnostic pop |
@ -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 {}; |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,54 @@ |
|||
/*
|
|||
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"); |
|||
|
|||
// foward declare without all of Windows.h
|
|||
#ifdef _WIN32 |
|||
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString); |
|||
#endif |
|||
|
|||
void eth::simpleDebugOut(std::string const& _s, char const*) |
|||
{ |
|||
cout << _s << endl << flush; |
|||
|
|||
// helpful to use OutputDebugString on windows
|
|||
#ifdef _WIN32 |
|||
{ |
|||
OutputDebugStringA(_s.data()); |
|||
OutputDebugStringA("\n"); |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
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,562 @@ |
|||
/*
|
|||
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, unsigned int _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, BlockChain const& _ch, unsigned int _networkId, NodeMode _m, string const& _publicAddress, bool _upnp): |
|||
m_clientVersion(_clientVersion), |
|||
m_mode(_m), |
|||
m_listenPort(0), |
|||
m_chain(&_ch), |
|||
m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), |
|||
m_socket(m_ioService), |
|||
m_key(KeyPair::create()), |
|||
m_networkId(_networkId) |
|||
{ |
|||
m_listenPort = m_acceptor.local_endpoint().port(); |
|||
|
|||
// populate addresses.
|
|||
populateAddresses(); |
|||
determinePublic(_publicAddress, _upnp); |
|||
ensureAccepting(); |
|||
clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); |
|||
} |
|||
|
|||
PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m): |
|||
m_clientVersion(_clientVersion), |
|||
m_mode(_m), |
|||
m_listenPort(0), |
|||
m_chain(&_ch), |
|||
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 11; |
|||
} |
|||
|
|||
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,136 @@ |
|||
/*
|
|||
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, unsigned int _networkId, unsigned short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); |
|||
/// Start server, listening for connections on a system-assigned port.
|
|||
PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); |
|||
/// Start server, but don't listen.
|
|||
PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _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,587 @@ |
|||
/*
|
|||
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)); |
|||
if (m_socket.is_open()) |
|||
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()); |
|||
if (m_socket.is_open()) |
|||
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,74 +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 Making BuildInfo... |
|||
mkdir build |
|||
cd build |
|||
cmake .. |
|||
cd .. |
|||
cp build/BuildInfo.h . |
|||
rm -rf build |
|||
|
|||
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 |
|||
shasum $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,32 @@ |
|||
/*
|
|||
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 JsonSpiritHeaders.h
|
|||
* @author Tim Hughes <tim@twistedfury.com> |
|||
* @date 2014 |
|||
*/ |
|||
#pragma once |
|||
|
|||
#pragma warning(push) |
|||
#pragma warning(disable: 4100) |
|||
#pragma GCC diagnostic push |
|||
#pragma GCC diagnostic ignored "-Wconversion" |
|||
#pragma GCC diagnostic ignored "-Wunused-parameter" |
|||
#include "../json_spirit/json_spirit_reader_template.h" |
|||
#include "../json_spirit/json_spirit_writer_template.h" |
|||
#pragma GCC diagnostic pop |
|||
#pragma warning(pop) |
|||
|
@ -0,0 +1,49 @@ |
|||
/*
|
|||
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 TestHelper.cpp
|
|||
* @author Marko Simovic <markobarko@gmail.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include <thread> |
|||
#include <chrono> |
|||
#include <Client.h> |
|||
#include "TestHelper.h" |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
void mine(Client& c, int numBlocks) |
|||
{ |
|||
auto startBlock = c.blockChain().details().number; |
|||
|
|||
c.startMining(); |
|||
while(c.blockChain().details().number < startBlock + numBlocks) |
|||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
|||
c.stopMining(); |
|||
} |
|||
|
|||
void connectClients(Client& c1, Client& c2) |
|||
{ |
|||
short c1Port = 20000; |
|||
short c2Port = 21000; |
|||
c1.startNetwork(c1Port); |
|||
c2.startNetwork(c2Port); |
|||
c2.connect("127.0.0.1", c1Port); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,30 @@ |
|||
/*
|
|||
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 TestHelper.h
|
|||
* @author Marko Simovic <markobarko@gmail.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
void mine(Client& c, int numBlocks); |
|||
void connectClients(Client& c1, Client& c2); |
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
/*
|
|||
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 boostTest.cpp
|
|||
* @author Marko Simovic <markobarko@gmail.com> |
|||
* @date 2014 |
|||
* Stub for generating main boost.test module. |
|||
*/ |
|||
|
|||
#define BOOST_TEST_MODULE EthereumTests |
|||
#include <boost/test/included/unit_test.hpp> |
@ -0,0 +1,56 @@ |
|||
/*
|
|||
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 fork.cpp
|
|||
* @author Marko Simovic <markobarko@gmail.com> |
|||
* @date 2014 |
|||
* Tests for different forking behavior |
|||
*/ |
|||
|
|||
#include <boost/test/unit_test.hpp> |
|||
#include <boost/filesystem/operations.hpp> |
|||
#include <Client.h> |
|||
#include <BlockChain.h> |
|||
#include <PeerServer.h> |
|||
#include "TestHelper.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
BOOST_AUTO_TEST_CASE(simple_chain_fork) |
|||
{ |
|||
//start a client and mine a short chain
|
|||
Client c1("TestClient1", KeyPair::create().address(), |
|||
(boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); |
|||
mine(c1, 4); |
|||
|
|||
//start another client and mine a longer chain
|
|||
Client c2("TestClient2", KeyPair::create().address(), |
|||
(boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); |
|||
mine(c2, 6); |
|||
|
|||
//connect the two clients up to resolve chain
|
|||
c1.startNetwork(20000); |
|||
c2.startNetwork(21000); |
|||
c2.connect("127.0.0.1", 20000); |
|||
|
|||
//mine an extra block to cement it
|
|||
mine(c1, 1); |
|||
|
|||
//check the balances are where they should be
|
|||
//c1's chain should have been clobbered by c2
|
|||
BOOST_REQUIRE(c1.state().balance(c1.address()) == 0); |
|||
BOOST_REQUIRE(c2.state().balance(c2.address()) > 0); |
|||
} |
@ -0,0 +1,51 @@ |
|||
/*
|
|||
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 network.cpp
|
|||
* @author Marko Simovic <markobarko@gmail.com> |
|||
* @date 2014 |
|||
* Basic networking tests |
|||
*/ |
|||
|
|||
#include <boost/test/unit_test.hpp> |
|||
#include <boost/filesystem/operations.hpp> |
|||
#include <Client.h> |
|||
#include <BlockChain.h> |
|||
#include <PeerServer.h> |
|||
#include "TestHelper.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
BOOST_AUTO_TEST_CASE(listen_port_busy) |
|||
{ |
|||
short port = 20000; |
|||
|
|||
//make use of the port ahead of our client
|
|||
ba::io_service ioService; |
|||
bi::tcp::endpoint endPoint(bi::tcp::v4(), port); |
|||
bi::tcp::acceptor acceptor(ioService, endPoint); |
|||
acceptor.listen(10); |
|||
|
|||
//prepare client and try to listen on same, used, port
|
|||
Client c1("TestClient1", KeyPair::create().address(), |
|||
(boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); |
|||
|
|||
c1.startNetwork(port); |
|||
|
|||
BOOST_REQUIRE(c1.haveNetwork()); |
|||
BOOST_REQUIRE(c1.peerServer()->listenPort() != 0); |
|||
BOOST_REQUIRE(c1.peerServer()->listenPort() != port); |
|||
} |
@ -0,0 +1,117 @@ |
|||
/*
|
|||
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 txTest.cpp
|
|||
* @author Marko Simovic <markobarko@gmail.com> |
|||
* @date 2014 |
|||
* Simple peer transaction send test. |
|||
*/ |
|||
|
|||
#include <boost/test/unit_test.hpp> |
|||
#include <boost/filesystem/operations.hpp> |
|||
#include <Client.h> |
|||
#include <BlockChain.h> |
|||
#include <PeerServer.h> |
|||
#include "TestHelper.h" |
|||
using namespace std; |
|||
using namespace eth; |
|||
|
|||
BOOST_AUTO_TEST_CASE(mine_local_simple_tx) |
|||
{ |
|||
KeyPair kp1 = KeyPair::create(); |
|||
KeyPair kp2 = KeyPair::create(); |
|||
|
|||
Client c1("TestClient1", kp1.address(), (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); |
|||
|
|||
//mine some blocks so that client 1 has a balance
|
|||
mine(c1, 1); |
|||
auto c1bal = c1.state().balance(kp1.address()); |
|||
BOOST_REQUIRE(c1bal > 0); |
|||
|
|||
//send c2 some eth from c1
|
|||
auto txAmount = c1bal / 2u; |
|||
auto gasPrice = 10 * szabo; |
|||
auto gas = eth::c_callGas; |
|||
c1.transact(kp1.secret(), txAmount, kp2.address(), bytes(), gas, gasPrice); |
|||
|
|||
//mine some more to include the transaction on chain
|
|||
mine(c1, 1); |
|||
auto c2bal = c1.state().balance(kp2.address()); |
|||
BOOST_REQUIRE(c2bal > 0); |
|||
BOOST_REQUIRE(c2bal == txAmount); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(mine_and_send_to_peer) |
|||
{ |
|||
KeyPair kp1 = KeyPair::create(); |
|||
KeyPair kp2 = KeyPair::create(); |
|||
|
|||
Client c1("TestClient1", kp1.address(), (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); |
|||
Client c2("TestClient2", kp2.address(), (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); |
|||
|
|||
connectClients(c1, c2); |
|||
|
|||
//mine some blocks so that client 1 has a balance
|
|||
mine(c1, 1); |
|||
auto c1bal = c1.state().balance(kp1.address()); |
|||
BOOST_REQUIRE(c1bal > 0); |
|||
|
|||
//send c2 some eth from c1
|
|||
auto txAmount = c1bal / 2u; |
|||
auto gasPrice = 10 * szabo; |
|||
auto gas = eth::c_callGas; |
|||
c1.transact(kp1.secret(), txAmount, kp2.address(), bytes(), gas, gasPrice); |
|||
|
|||
//mine some more to include the transaction on chain
|
|||
mine(c1, 1); |
|||
auto c2bal = c2.state().balance(kp2.address()); |
|||
BOOST_REQUIRE(c2bal > 0); |
|||
BOOST_REQUIRE(c2bal == txAmount); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(mine_and_send_to_peer_fee_check) |
|||
{ |
|||
KeyPair kp1 = KeyPair::create(); |
|||
KeyPair kp2 = KeyPair::create(); |
|||
|
|||
Client c1("TestClient1", kp1.address(), (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); |
|||
Client c2("TestClient2", kp2.address(), (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); |
|||
|
|||
connectClients(c1, c2); |
|||
|
|||
//mine some blocks so that client 1 has a balance
|
|||
mine(c1, 1); |
|||
|
|||
auto c1StartBalance = c1.state().balance(kp1.address()); |
|||
auto c2StartBalance = c2.state().balance(kp2.address()); |
|||
BOOST_REQUIRE(c1StartBalance > 0); |
|||
BOOST_REQUIRE(c2StartBalance == 0); |
|||
|
|||
//send c2 some eth from c1
|
|||
auto txAmount = c1StartBalance / 2u; |
|||
auto gasPrice = 10 * szabo; |
|||
auto gas = eth::c_callGas; |
|||
c1.transact(kp1.secret(), txAmount, c2.address(), bytes(), gas, gasPrice); |
|||
|
|||
//mine some more, this time with second client (so he can get fees from first client's tx)
|
|||
mine(c2, 1); |
|||
|
|||
auto c1EndBalance = c1.state().balance(kp1.address()); |
|||
auto c2EndBalance = c2.state().balance(kp2.address()); |
|||
BOOST_REQUIRE(c1EndBalance > 0); |
|||
BOOST_REQUIRE(c1EndBalance == c1StartBalance - txAmount - gasPrice * gas); |
|||
BOOST_REQUIRE(c2EndBalance > 0); |
|||
} |
@ -0,0 +1,115 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
|
|||
if ("${TARGET_PLATFORM}" STREQUAL "w64") |
|||
cmake_policy(SET CMP0020 NEW) |
|||
endif () |
|||
|
|||
|
|||
set(CMAKE_INCLUDE_CURRENT_DIR ON) |
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
include_directories(..) |
|||
link_directories(../libethereum) |
|||
|
|||
# Find Qt5 for Apple and update src_list for windows |
|||
if (APPLE) |
|||
# homebrew defaults to qt4 and installs qt5 as 'keg-only' |
|||
# which places it into /usr/local/opt insteadof /usr/local. |
|||
|
|||
set(CMAKE_PREFIX_PATH /usr/local/opt/qt5) |
|||
include_directories(/usr/local/opt/qt5/include /usr/local/include) |
|||
elseif (${TARGET_PLATFORM} STREQUAL "w64") |
|||
set(SRC_LIST ${SRC_LIST} ../windows/qt_plugin_import.cpp) |
|||
elseif (UNIX) |
|||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ";$ENV{QTDIR}/lib/cmake") |
|||
endif () |
|||
|
|||
|
|||
find_package(Qt5Widgets REQUIRED) |
|||
find_package(Qt5Gui REQUIRED) |
|||
find_package(Qt5Quick REQUIRED) |
|||
find_package(Qt5Qml REQUIRED) |
|||
find_package(Qt5Network REQUIRED) |
|||
qt5_wrap_ui(ui_Main.h Main.ui) |
|||
qt5_add_resources(RESOURCE_ADDED Resources.qrc) |
|||
|
|||
# Set name of binary and add_executable() |
|||
if (APPLE) |
|||
set(EXECUTEABLE Walleth) |
|||
set(CMAKE_INSTALL_PREFIX ./) |
|||
set(BIN_INSTALL_DIR ".") |
|||
set(DOC_INSTALL_DIR ".") |
|||
|
|||
set(PROJECT_VERSION "${ETH_VERSION}") |
|||
set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") |
|||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") |
|||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") |
|||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") |
|||
set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") |
|||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") |
|||
set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTEABLE}) |
|||
include(BundleUtilities) |
|||
|
|||
add_executable(${EXECUTEABLE} MACOSX_BUNDLE Main.ui ${RESOURCE_ADDED} ${SRC_LIST}) |
|||
else () |
|||
set(EXECUTEABLE walleth) |
|||
add_executable(${EXECUTEABLE} Main.ui ${RESOURCE_ADDED} ${SRC_LIST}) |
|||
endif () |
|||
|
|||
qt5_use_modules(${EXECUTEABLE} Core Gui Widgets Network Quick Qml) |
|||
target_link_libraries(${EXECUTEABLE} ethereum secp256k1 ${CRYPTOPP_LIBRARIES}) |
|||
|
|||
if (APPLE) |
|||
if (${ADDFRAMEWORKS}) |
|||
set_target_properties(${EXECUTEABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in") |
|||
endif () |
|||
|
|||
SET_SOURCE_FILES_PROPERTIES(${EXECUTEABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) |
|||
|
|||
# This is a workaround for when the build-type defaults to Debug, and when a multi-config generator like xcode is used, where the type |
|||
# will not be set but defaults to release. |
|||
set(generator_lowercase "${CMAKE_GENERATOR}") |
|||
string(TOLOWER "${CMAKE_GENERATOR}" generator_lowercase) |
|||
if (generator_lowercase STREQUAL "xcode") |
|||
# TODO: Not sure how to resolve this. Possibly \${TARGET_BUILD_DIR} |
|||
set(binary_build_dir "${CMAKE_CURRENT_BINARY_DIR}/Debug") |
|||
else () |
|||
set(binary_build_dir "${CMAKE_CURRENT_BINARY_DIR}") |
|||
endif () |
|||
|
|||
set(APPS ${binary_build_dir}/${EXECUTEABLE}.app) |
|||
|
|||
# This tool and the next will automatically looked at the linked libraries in order to determine what dependencies are required. Thus, target_link_libaries only needs to add ethereum and secp256k1 (above) |
|||
install(CODE " |
|||
include(BundleUtilities) |
|||
set(BU_CHMOD_BUNDLE_ITEMS 1) |
|||
fixup_bundle(\"${APPS}\" \"${BUNDLELIBS}\" \"../libethereum ../secp256k1\") |
|||
" COMPONENT RUNTIME ) |
|||
|
|||
if (${ADDFRAMEWORKS}) |
|||
add_custom_target(addframeworks ALL |
|||
COMMAND /usr/local/opt/qt5/bin/macdeployqt ${binary_build_dir}/${EXECUTEABLE}.app |
|||
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} |
|||
DEPENDS ${PROJECT_NAME} |
|||
) |
|||
endif () |
|||
|
|||
elseif (${TARGET_PLATFORM} STREQUAL "w64") |
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-keep-inline-dllexport -static-libgcc -static-libstdc++ -static") |
|||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-s -Wl,-subsystem,windows -mthreads -L/usr/x86_64-w64-mingw32/plugins/platforms") |
|||
target_link_libraries(${EXECUTEABLE} gcc) |
|||
target_link_libraries(${EXECUTEABLE} mingw32 qtmain mswsock iphlpapi qwindows shlwapi Qt5PlatformSupport gdi32 comdlg32 oleaut32 imm32 winmm ole32 uuid ws2_32) |
|||
target_link_libraries(${EXECUTEABLE} boost_system-mt-s) |
|||
target_link_libraries(${EXECUTEABLE} boost_filesystem-mt-s) |
|||
target_link_libraries(${EXECUTEABLE} boost_thread_win32-mt-s) |
|||
target_link_libraries(${EXECUTEABLE} Qt5PlatformSupport) |
|||
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) |
|||
elseif (UNIX) |
|||
else () |
|||
target_link_libraries(${EXECUTEABLE} boost_system) |
|||
target_link_libraries(${EXECUTEABLE} boost_filesystem) |
|||
find_package(Threads REQUIRED) |
|||
target_link_libraries(${EXECUTEABLE} ${CMAKE_THREAD_LIBS_INIT}) |
|||
install( TARGETS ${EXECUTEABLE} RUNTIME DESTINATION bin ) |
|||
endif () |
|||
|
@ -0,0 +1,168 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>Main</class> |
|||
<widget class="QMainWindow" name="Main"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>562</width> |
|||
<height>488</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Walleth</string> |
|||
</property> |
|||
<property name="dockNestingEnabled"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="dockOptions"> |
|||
<set>QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs</set> |
|||
</property> |
|||
<property name="sizeGripEnabled" stdset="0"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<widget class="QWidget" name="centralwidget"> |
|||
<layout class="QVBoxLayout" name="verticalLayout"> |
|||
<property name="leftMargin"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="topMargin"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="rightMargin"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="bottomMargin"> |
|||
<number>0</number> |
|||
</property> |
|||
<item> |
|||
<layout class="QHBoxLayout" name="fullDisplay"> |
|||
<item> |
|||
<widget class="QLabel" name="balance"> |
|||
<property name="text"> |
|||
<string>0 wei</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QLabel" name="peerCount"> |
|||
<property name="text"> |
|||
<string>0 peers</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QLabel" name="blockCount"> |
|||
<property name="text"> |
|||
<string>1 block</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<widget class="QMenuBar" name="menubar"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>562</width> |
|||
<height>20</height> |
|||
</rect> |
|||
</property> |
|||
<widget class="QMenu" name="menu_File"> |
|||
<property name="title"> |
|||
<string>&File</string> |
|||
</property> |
|||
<addaction name="quit"/> |
|||
</widget> |
|||
<widget class="QMenu" name="menu_Network"> |
|||
<property name="title"> |
|||
<string>&Network</string> |
|||
</property> |
|||
<addaction name="upnp"/> |
|||
<addaction name="net"/> |
|||
<addaction name="connect"/> |
|||
</widget> |
|||
<widget class="QMenu" name="menu_Tools"> |
|||
<property name="title"> |
|||
<string>T&ools</string> |
|||
</property> |
|||
<addaction name="mine"/> |
|||
<addaction name="create"/> |
|||
<addaction name="preview"/> |
|||
</widget> |
|||
<widget class="QMenu" name="menu_Help"> |
|||
<property name="title"> |
|||
<string>&Help</string> |
|||
</property> |
|||
<addaction name="about"/> |
|||
</widget> |
|||
<addaction name="menu_File"/> |
|||
<addaction name="menu_Network"/> |
|||
<addaction name="menu_Tools"/> |
|||
<addaction name="menu_Help"/> |
|||
</widget> |
|||
<widget class="QStatusBar" name="statusbar"/> |
|||
<action name="quit"> |
|||
<property name="text"> |
|||
<string>&Quit</string> |
|||
</property> |
|||
</action> |
|||
<action name="upnp"> |
|||
<property name="checkable"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="checked"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Use &UPnP</string> |
|||
</property> |
|||
</action> |
|||
<action name="connect"> |
|||
<property name="text"> |
|||
<string>&Connect to Peer...</string> |
|||
</property> |
|||
</action> |
|||
<action name="net"> |
|||
<property name="checkable"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Enable &Network</string> |
|||
</property> |
|||
</action> |
|||
<action name="mine"> |
|||
<property name="checkable"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="text"> |
|||
<string>&Mine</string> |
|||
</property> |
|||
</action> |
|||
<action name="create"> |
|||
<property name="text"> |
|||
<string>&New Address</string> |
|||
</property> |
|||
</action> |
|||
<action name="about"> |
|||
<property name="text"> |
|||
<string>&About...</string> |
|||
</property> |
|||
</action> |
|||
<action name="preview"> |
|||
<property name="checkable"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="text"> |
|||
<string>&Preview</string> |
|||
</property> |
|||
</action> |
|||
</widget> |
|||
<layoutdefault spacing="6" margin="11"/> |
|||
<resources/> |
|||
<connections/> |
|||
</ui> |
@ -0,0 +1,448 @@ |
|||
#include <QtNetwork/QNetworkReply> |
|||
#include <QtQuick/QQuickView> |
|||
//#include <QtQml/QQmlContext>
|
|||
//#include <QtQml/QQmlEngine>
|
|||
#include <QtQml/QtQml> |
|||
#include <QtWidgets/QMessageBox> |
|||
#include <QtWidgets/QInputDialog> |
|||
#include <QtGui/QClipboard> |
|||
#include <QtCore/QtCore> |
|||
#include <libethereum/Dagger.h> |
|||
#include <libethereum/Client.h> |
|||
#include <libethereum/Instruction.h> |
|||
#include <libethereum/FileSystem.h> |
|||
#include <libethereum/PeerServer.h> |
|||
#include "BuildInfo.h" |
|||
#include "MainWin.h" |
|||
#include "ui_Main.h" |
|||
using namespace std; |
|||
|
|||
// types
|
|||
using eth::bytes; |
|||
using eth::bytesConstRef; |
|||
using eth::h160; |
|||
using eth::h256; |
|||
using eth::u160; |
|||
using eth::u256; |
|||
using eth::u256s; |
|||
using eth::Address; |
|||
using eth::BlockInfo; |
|||
using eth::Client; |
|||
using eth::Instruction; |
|||
using eth::KeyPair; |
|||
using eth::NodeMode; |
|||
using eth::PeerInfo; |
|||
using eth::RLP; |
|||
using eth::Secret; |
|||
using eth::Transaction; |
|||
|
|||
// functions
|
|||
using eth::toHex; |
|||
using eth::assemble; |
|||
using eth::compileLisp; |
|||
using eth::disassemble; |
|||
using eth::formatBalance; |
|||
using eth::fromHex; |
|||
using eth::right160; |
|||
using eth::simpleDebugOut; |
|||
using eth::toLog2; |
|||
using eth::toString; |
|||
using eth::units; |
|||
|
|||
// vars
|
|||
using eth::g_logPost; |
|||
using eth::g_logVerbosity; |
|||
using eth::c_instructionInfo; |
|||
|
|||
// Horrible global for the mainwindow. Needed for the QEthereums to find the Main window which acts as multiplexer for now.
|
|||
// Can get rid of this once we've sorted out ITC for signalling & multiplexed querying.
|
|||
Main* g_main = nullptr; |
|||
|
|||
QEthereum::QEthereum(QObject* _p): QObject(_p) |
|||
{ |
|||
connect(g_main, SIGNAL(changed()), SIGNAL(changed())); |
|||
} |
|||
|
|||
QEthereum::~QEthereum() |
|||
{ |
|||
} |
|||
|
|||
Client* QEthereum::client() const |
|||
{ |
|||
return g_main->client(); |
|||
} |
|||
|
|||
Address QEthereum::coinbase() const |
|||
{ |
|||
return client()->address(); |
|||
} |
|||
|
|||
void QEthereum::setCoinbase(Address _a) |
|||
{ |
|||
if (client()->address() != _a) |
|||
{ |
|||
client()->setAddress(_a); |
|||
changed(); |
|||
} |
|||
} |
|||
|
|||
QAccount::QAccount(QObject*) |
|||
{ |
|||
} |
|||
|
|||
QAccount::~QAccount() |
|||
{ |
|||
} |
|||
|
|||
void QAccount::setEthereum(QEthereum* _eth) |
|||
{ |
|||
if (m_eth == _eth) |
|||
return; |
|||
if (m_eth) |
|||
disconnect(m_eth, SIGNAL(changed()), this, SIGNAL(changed())); |
|||
m_eth = _eth; |
|||
if (m_eth) |
|||
connect(m_eth, SIGNAL(changed()), this, SIGNAL(changed())); |
|||
ethChanged(); |
|||
changed(); |
|||
} |
|||
|
|||
u256 QAccount::balance() const |
|||
{ |
|||
if (m_eth) |
|||
return m_eth->balanceAt(m_address); |
|||
return 0; |
|||
} |
|||
|
|||
double QAccount::txCount() const |
|||
{ |
|||
if (m_eth) |
|||
return m_eth->txCountAt(m_address); |
|||
return 0; |
|||
} |
|||
|
|||
bool QAccount::isContract() const |
|||
{ |
|||
if (m_eth) |
|||
return m_eth->isContractAt(m_address); |
|||
return 0; |
|||
} |
|||
|
|||
u256 QEthereum::balanceAt(Address _a) const |
|||
{ |
|||
return client()->postState().balance(_a); |
|||
} |
|||
|
|||
bool QEthereum::isContractAt(Address _a) const |
|||
{ |
|||
return client()->postState().isContractAddress(_a); |
|||
} |
|||
|
|||
bool QEthereum::isMining() const |
|||
{ |
|||
return client()->isMining(); |
|||
} |
|||
|
|||
bool QEthereum::isListening() const |
|||
{ |
|||
return client()->haveNetwork(); |
|||
} |
|||
|
|||
void QEthereum::setMining(bool _l) |
|||
{ |
|||
if (_l) |
|||
client()->startMining(); |
|||
else |
|||
client()->stopMining(); |
|||
} |
|||
|
|||
void QEthereum::setListening(bool _l) |
|||
{ |
|||
if (_l) |
|||
client()->startNetwork(); |
|||
else |
|||
client()->stopNetwork(); |
|||
} |
|||
|
|||
double QEthereum::txCountAt(Address _a) const |
|||
{ |
|||
return (double)client()->postState().transactionsFrom(_a); |
|||
} |
|||
|
|||
unsigned QEthereum::peerCount() const |
|||
{ |
|||
return (unsigned)client()->peerCount(); |
|||
} |
|||
|
|||
void QEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _code, QByteArray _init) |
|||
{ |
|||
client()->transact(_secret, _amount, bytes(_code.data(), _code.data() + _code.size()), bytes(_init.data(), _init.data() + _init.size()), _gas, _gasPrice); |
|||
} |
|||
|
|||
void QEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data) |
|||
{ |
|||
client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice); |
|||
} |
|||
|
|||
Main::Main(QWidget *parent) : |
|||
QMainWindow(parent), |
|||
ui(new Ui::Main) |
|||
{ |
|||
setWindowFlags(Qt::Window); |
|||
ui->setupUi(this); |
|||
setWindowIcon(QIcon(":/Ethereum.png")); |
|||
|
|||
g_main = this; |
|||
|
|||
m_client.reset(new Client("Walleth", Address(), eth::getDataDir() + "/Walleth")); |
|||
|
|||
qRegisterMetaType<eth::u256>("eth::u256"); |
|||
qRegisterMetaType<eth::KeyPair>("eth::KeyPair"); |
|||
qRegisterMetaType<eth::Secret>("eth::Secret"); |
|||
qRegisterMetaType<eth::Address>("eth::Address"); |
|||
qRegisterMetaType<QAccount*>("QAccount*"); |
|||
qRegisterMetaType<QEthereum*>("QEthereum*"); |
|||
|
|||
qmlRegisterType<QEthereum>("org.ethereum", 1, 0, "Ethereum"); |
|||
qmlRegisterType<QAccount>("org.ethereum", 1, 0, "Account"); |
|||
qmlRegisterSingletonType<U256Helper>("org.ethereum", 1, 0, "Balance", QEthereum::constructU256Helper); |
|||
qmlRegisterSingletonType<KeyHelper>("org.ethereum", 1, 0, "Key", QEthereum::constructKeyHelper); |
|||
|
|||
/*
|
|||
ui->librariesView->setModel(m_libraryMan); |
|||
ui->graphsView->setModel(m_graphMan); |
|||
*/ |
|||
|
|||
m_view = new QQuickView(); |
|||
|
|||
// QQmlContext* context = m_view->rootContext();
|
|||
// context->setContextProperty("u256", new U256Helper(this));
|
|||
|
|||
m_view->setSource(QUrl("qrc:/Simple.qml")); |
|||
|
|||
QWidget* w = QWidget::createWindowContainer(m_view); |
|||
m_view->setResizeMode(QQuickView::SizeRootObjectToView); |
|||
ui->fullDisplay->insertWidget(0, w); |
|||
m_view->create(); |
|||
|
|||
// m_timelinesItem = m_view->rootObject()->findChild<TimelinesItem*>("timelines");
|
|||
|
|||
readSettings(); |
|||
refresh(); |
|||
|
|||
m_refreshNetwork = new QTimer(this); |
|||
connect(m_refreshNetwork, SIGNAL(timeout()), SLOT(refreshNetwork())); |
|||
m_refreshNetwork->start(1000); |
|||
|
|||
connect(this, SIGNAL(changed()), SLOT(refresh())); |
|||
|
|||
connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r) |
|||
{ |
|||
m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); |
|||
if (m_servers.size()) |
|||
{ |
|||
ui->net->setChecked(true); |
|||
on_net_triggered(true); |
|||
} |
|||
}); |
|||
QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1) + ".txt")); |
|||
r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); |
|||
m_webCtrl.get(r); |
|||
srand(time(0)); |
|||
|
|||
startTimer(200); |
|||
|
|||
statusBar()->addPermanentWidget(ui->balance); |
|||
statusBar()->addPermanentWidget(ui->peerCount); |
|||
statusBar()->addPermanentWidget(ui->blockCount); |
|||
} |
|||
|
|||
Main::~Main() |
|||
{ |
|||
writeSettings(); |
|||
} |
|||
|
|||
void Main::timerEvent(QTimerEvent *) |
|||
{ |
|||
if (m_client->changed()) |
|||
changed(); |
|||
} |
|||
|
|||
void Main::on_about_triggered() |
|||
{ |
|||
QMessageBox::about(this, "About Walleth PoC-" + QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1), "Walleth/v" ETH_QUOTED(ETH_VERSION) "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM) " - " ETH_QUOTED(ETH_COMMIT_HASH) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Tim Hughes, Eric Lombrozo, Marko Simovic, Alex Leverington and several others."); |
|||
} |
|||
|
|||
void Main::writeSettings() |
|||
{ |
|||
QSettings s("ethereum", "walleth"); |
|||
QByteArray b; |
|||
b.resize(sizeof(Secret) * m_myKeys.size()); |
|||
auto p = b.data(); |
|||
for (auto i: m_myKeys) |
|||
{ |
|||
memcpy(p, &(i.secret()), sizeof(Secret)); |
|||
p += sizeof(Secret); |
|||
} |
|||
s.setValue("address", b); |
|||
|
|||
s.setValue("upnp", ui->upnp->isChecked()); |
|||
s.setValue("clientName", m_clientName); |
|||
s.setValue("idealPeers", m_idealPeers); |
|||
s.setValue("port", m_port); |
|||
|
|||
if (client()->peerServer()) |
|||
{ |
|||
bytes d = client()->peerServer()->savePeers(); |
|||
m_peers = QByteArray((char*)d.data(), (int)d.size()); |
|||
|
|||
} |
|||
s.setValue("peers", m_peers); |
|||
|
|||
s.setValue("geometry", saveGeometry()); |
|||
s.setValue("windowState", saveState()); |
|||
} |
|||
|
|||
void Main::readSettings() |
|||
{ |
|||
QSettings s("ethereum", "walleth"); |
|||
|
|||
restoreGeometry(s.value("geometry").toByteArray()); |
|||
restoreState(s.value("windowState").toByteArray()); |
|||
|
|||
QByteArray b = s.value("address").toByteArray(); |
|||
if (b.isEmpty()) |
|||
m_myKeys.append(KeyPair::create()); |
|||
else |
|||
{ |
|||
h256 k; |
|||
for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) |
|||
{ |
|||
memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); |
|||
m_myKeys.append(KeyPair(k)); |
|||
} |
|||
} |
|||
//m_eth->setAddress(m_myKeys.last().address());
|
|||
m_peers = s.value("peers").toByteArray(); |
|||
ui->upnp->setChecked(s.value("upnp", true).toBool()); |
|||
m_clientName = s.value("clientName", "").toString(); |
|||
m_idealPeers = s.value("idealPeers", 5).toInt(); |
|||
m_port = s.value("port", 30303).toInt(); |
|||
} |
|||
|
|||
void Main::refreshNetwork() |
|||
{ |
|||
auto ps = client()->peers(); |
|||
ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); |
|||
} |
|||
|
|||
eth::State const& Main::state() const |
|||
{ |
|||
return ui->preview->isChecked() ? client()->postState() : client()->state(); |
|||
} |
|||
|
|||
void Main::refresh() |
|||
{ |
|||
eth::ClientGuard l(client()); |
|||
auto const& st = state(); |
|||
|
|||
auto d = client()->blockChain().details(); |
|||
auto diff = BlockInfo(client()->blockChain().block()).difficulty; |
|||
ui->blockCount->setText(QString("#%1 @%3 T%2").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff))); |
|||
|
|||
m_keysChanged = false; |
|||
u256 totalBalance = 0; |
|||
for (auto i: m_myKeys) |
|||
{ |
|||
u256 b = st.balance(i.address()); |
|||
totalBalance += b; |
|||
} |
|||
ui->balance->setText(QString::fromStdString(formatBalance(totalBalance))); |
|||
} |
|||
|
|||
void Main::on_net_triggered(bool _auto) |
|||
{ |
|||
string n = "Walleth/v" ETH_QUOTED(ETH_VERSION); |
|||
if (m_clientName.size()) |
|||
n += "/" + m_clientName.toStdString(); |
|||
n += "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM); |
|||
client()->setClientVersion(n); |
|||
if (ui->net->isChecked()) |
|||
{ |
|||
if (_auto) |
|||
{ |
|||
QString s = m_servers[rand() % m_servers.size()]; |
|||
client()->startNetwork(m_port, s.section(':', 0, 0).toStdString(), s.section(':', 1).toInt(), NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked()); |
|||
} |
|||
else |
|||
client()->startNetwork(m_port, string(), 0, NodeMode::Full, m_idealPeers, std::string(), ui->upnp->isChecked()); |
|||
if (m_peers.size()) |
|||
client()->peerServer()->restorePeers(bytesConstRef((byte*)m_peers.data(), m_peers.size())); |
|||
} |
|||
else |
|||
client()->stopNetwork(); |
|||
} |
|||
|
|||
void Main::on_connect_triggered() |
|||
{ |
|||
if (!ui->net->isChecked()) |
|||
{ |
|||
ui->net->setChecked(true); |
|||
on_net_triggered(); |
|||
} |
|||
bool ok = false; |
|||
QString s = QInputDialog::getItem(this, "Connect to a Network Peer", "Enter a peer to which a connection may be made:", m_servers, m_servers.count() ? rand() % m_servers.count() : 0, true, &ok); |
|||
if (ok && s.contains(":")) |
|||
{ |
|||
string host = s.section(":", 0, 0).toStdString(); |
|||
unsigned short port = s.section(":", 1).toInt(); |
|||
client()->connect(host, port); |
|||
} |
|||
} |
|||
|
|||
void Main::on_mine_triggered() |
|||
{ |
|||
if (ui->mine->isChecked()) |
|||
{ |
|||
client()->setAddress(m_myKeys.last().address()); |
|||
client()->startMining(); |
|||
} |
|||
else |
|||
client()->stopMining(); |
|||
} |
|||
|
|||
void Main::on_create_triggered() |
|||
{ |
|||
m_myKeys.append(KeyPair::create()); |
|||
m_keysChanged = true; |
|||
} |
|||
|
|||
// extra bits needed to link on VS
|
|||
#ifdef _MSC_VER |
|||
|
|||
// include moc file, ofuscated to hide from automoc
|
|||
#include\ |
|||
"moc_MainWin.cpp" |
|||
|
|||
// specify library dependencies, it's easier to do here than in the project since we can control the "d" debug suffix
|
|||
#ifdef _DEBUG |
|||
#define QTLIB(x) x"d.lib" |
|||
#else |
|||
#define QTLIB(x) x".lib" |
|||
#endif |
|||
|
|||
#pragma comment(lib, QTLIB("Qt5PlatformSupport")) |
|||
#pragma comment(lib, QTLIB("Qt5Core")) |
|||
#pragma comment(lib, QTLIB("Qt5GUI")) |
|||
#pragma comment(lib, QTLIB("Qt5Widgets")) |
|||
#pragma comment(lib, QTLIB("Qt5Network")) |
|||
#pragma comment(lib, QTLIB("Qt5Quick")) |
|||
#pragma comment(lib, QTLIB("Qt5Declarative")) |
|||
#pragma comment(lib, QTLIB("Qt5Qml")) |
|||
#pragma comment(lib, QTLIB("qwindows")) |
|||
#pragma comment(lib, "Imm32.lib") |
|||
#pragma comment(lib, "opengl32.lib") |
|||
#pragma comment(lib, "winmm.lib") |
|||
|
|||
|
|||
#endif |
@ -0,0 +1,224 @@ |
|||
#ifndef MAIN_H |
|||
#define MAIN_H |
|||
|
|||
#include <QtNetwork/QNetworkAccessManager> |
|||
#include <QtCore/QAbstractListModel> |
|||
#include <QtCore/QMutex> |
|||
#include <QtWidgets/QMainWindow> |
|||
#include <libethereum/CommonEth.h> |
|||
|
|||
namespace Ui { |
|||
class Main; |
|||
} |
|||
|
|||
namespace eth { |
|||
class Client; |
|||
class State; |
|||
} |
|||
|
|||
class QQuickView; |
|||
class QQmlEngine; |
|||
class QJSEngine; |
|||
|
|||
class QEthereum; |
|||
class QAccount; |
|||
|
|||
Q_DECLARE_METATYPE(eth::u256) |
|||
Q_DECLARE_METATYPE(eth::Address) |
|||
Q_DECLARE_METATYPE(eth::Secret) |
|||
Q_DECLARE_METATYPE(eth::KeyPair) |
|||
Q_DECLARE_METATYPE(QEthereum*) |
|||
Q_DECLARE_METATYPE(QAccount*) |
|||
|
|||
class U256Helper: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
U256Helper(QObject* _p = nullptr): QObject(_p) {} |
|||
|
|||
Q_INVOKABLE eth::u256 add(eth::u256 _a, eth::u256 _b) const { return _a + _b; } |
|||
Q_INVOKABLE eth::u256 sub(eth::u256 _a, eth::u256 _b) const { return _a - _b; } |
|||
Q_INVOKABLE eth::u256 mul(eth::u256 _a, int _b) const { return _a * _b; } |
|||
Q_INVOKABLE eth::u256 mul(int _a, eth::u256 _b) const { return _a * _b; } |
|||
Q_INVOKABLE eth::u256 div(eth::u256 _a, int _b) const { return _a / _b; } |
|||
|
|||
Q_INVOKABLE eth::u256 wei(double _s) const { return (eth::u256)_s; } |
|||
Q_INVOKABLE eth::u256 szabo(double _s) const { return (eth::u256)(_s * (double)eth::szabo); } |
|||
Q_INVOKABLE eth::u256 finney(double _s) const { return (eth::u256)(_s * (double)eth::finney); } |
|||
Q_INVOKABLE eth::u256 ether(double _s) const { return (eth::u256)(_s * (double)eth::ether); } |
|||
Q_INVOKABLE eth::u256 wei(unsigned _s) const { return (eth::u256)_s; } |
|||
Q_INVOKABLE eth::u256 szabo(unsigned _s) const { return (eth::u256)(_s * eth::szabo); } |
|||
Q_INVOKABLE eth::u256 finney(unsigned _s) const { return (eth::u256)(_s * eth::finney); } |
|||
Q_INVOKABLE eth::u256 ether(unsigned _s) const { return (eth::u256)(_s * eth::ether); } |
|||
Q_INVOKABLE double toWei(eth::u256 _t) const { return (double)_t; } |
|||
Q_INVOKABLE double toSzabo(eth::u256 _t) const { return toWei(_t) / (double)eth::szabo; } |
|||
Q_INVOKABLE double toFinney(eth::u256 _t) const { return toWei(_t) / (double)eth::finney; } |
|||
Q_INVOKABLE double toEther(eth::u256 _t) const { return toWei(_t) / (double)eth::ether; } |
|||
|
|||
Q_INVOKABLE double value(eth::u256 _t) const { return (double)_t; } |
|||
|
|||
Q_INVOKABLE QString stringOf(eth::u256 _t) const { return QString::fromStdString(eth::formatBalance(_t)); } |
|||
}; |
|||
|
|||
class KeyHelper: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
KeyHelper(QObject* _p = nullptr): QObject(_p) {} |
|||
|
|||
Q_INVOKABLE eth::KeyPair create() const { return eth::KeyPair::create(); } |
|||
Q_INVOKABLE eth::Address address(eth::KeyPair _p) const { return _p.address(); } |
|||
Q_INVOKABLE eth::Secret secret(eth::KeyPair _p) const { return _p.secret(); } |
|||
Q_INVOKABLE eth::KeyPair keypair(eth::Secret _k) const { return eth::KeyPair(_k); } |
|||
|
|||
Q_INVOKABLE bool isNull(eth::Address _a) const { return !_a; } |
|||
|
|||
Q_INVOKABLE eth::Address addressOf(QString _s) const { return eth::Address(_s.toStdString()); } |
|||
Q_INVOKABLE QString stringOf(eth::Address _a) const { return QString::fromStdString(eth::toHex(_a.asArray())); } |
|||
Q_INVOKABLE QString toAbridged(eth::Address _a) const { return QString::fromStdString(_a.abridged()); } |
|||
}; |
|||
|
|||
class QAccount: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
QAccount(QObject* _p = nullptr); |
|||
virtual ~QAccount(); |
|||
|
|||
Q_INVOKABLE QEthereum* ethereum() const { return m_eth; } |
|||
Q_INVOKABLE eth::u256 balance() const; |
|||
Q_INVOKABLE double txCount() const; |
|||
Q_INVOKABLE bool isContract() const; |
|||
|
|||
// TODO: past transactions models.
|
|||
|
|||
public slots: |
|||
void setEthereum(QEthereum* _eth); |
|||
|
|||
signals: |
|||
void changed(); |
|||
void ethChanged(); |
|||
|
|||
private: |
|||
QEthereum* m_eth = nullptr; |
|||
eth::Address m_address; |
|||
|
|||
Q_PROPERTY(eth::u256 balance READ balance NOTIFY changed STORED false) |
|||
Q_PROPERTY(double txCount READ txCount NOTIFY changed STORED false) |
|||
Q_PROPERTY(bool isContract READ isContract NOTIFY changed STORED false) |
|||
Q_PROPERTY(eth::Address address MEMBER m_address NOTIFY changed) |
|||
Q_PROPERTY(QEthereum* ethereum READ ethereum WRITE setEthereum NOTIFY ethChanged) |
|||
}; |
|||
|
|||
class QEthereum: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
QEthereum(QObject* _p = nullptr); |
|||
virtual ~QEthereum(); |
|||
|
|||
eth::Client* client() const; |
|||
|
|||
static QObject* constructU256Helper(QQmlEngine*, QJSEngine*) { return new U256Helper; } |
|||
static QObject* constructKeyHelper(QQmlEngine*, QJSEngine*) { return new KeyHelper; } |
|||
|
|||
Q_INVOKABLE eth::Address coinbase() const; |
|||
|
|||
Q_INVOKABLE bool isListening() const; |
|||
Q_INVOKABLE bool isMining() const; |
|||
|
|||
Q_INVOKABLE eth::u256 balanceAt(eth::Address _a) const; |
|||
Q_INVOKABLE double txCountAt(eth::Address _a) const; |
|||
Q_INVOKABLE bool isContractAt(eth::Address _a) const; |
|||
|
|||
Q_INVOKABLE unsigned peerCount() const; |
|||
|
|||
Q_INVOKABLE QEthereum* self() { return this; } |
|||
|
|||
public slots: |
|||
void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _data); |
|||
void transact(eth::Secret _secret, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _code, QByteArray _init); |
|||
void setCoinbase(eth::Address); |
|||
void setMining(bool _l); |
|||
|
|||
void setListening(bool _l); |
|||
|
|||
signals: |
|||
void changed(); |
|||
// void netChanged();
|
|||
// void miningChanged();
|
|||
|
|||
private: |
|||
Q_PROPERTY(eth::Address coinbase READ coinbase WRITE setCoinbase NOTIFY changed) |
|||
Q_PROPERTY(bool listening READ isListening WRITE setListening) |
|||
Q_PROPERTY(bool mining READ isMining WRITE setMining) |
|||
}; |
|||
|
|||
class Main : public QMainWindow |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
explicit Main(QWidget *parent = 0); |
|||
~Main(); |
|||
|
|||
eth::Client* client() const { return m_client.get(); } |
|||
|
|||
private slots: |
|||
void on_connect_triggered(); |
|||
void on_mine_triggered(); |
|||
void on_create_triggered(); |
|||
void on_net_triggered(bool _auto = false); |
|||
void on_about_triggered(); |
|||
void on_preview_triggered() { refresh(); } |
|||
void on_quit_triggered() { close(); } |
|||
|
|||
void refresh(); |
|||
void refreshNetwork(); |
|||
|
|||
signals: |
|||
void changed(); |
|||
|
|||
protected: |
|||
virtual void timerEvent(QTimerEvent *); |
|||
|
|||
private: |
|||
/* QString pretty(eth::Address _a) const;
|
|||
QString render(eth::Address _a) const; |
|||
eth::Address fromString(QString const& _a) const; |
|||
*/ |
|||
eth::State const& state() const; |
|||
|
|||
void updateFee(); |
|||
void readSettings(); |
|||
void writeSettings(); |
|||
|
|||
eth::u256 fee() const; |
|||
eth::u256 total() const; |
|||
eth::u256 value() const; |
|||
|
|||
std::unique_ptr<Ui::Main> ui; |
|||
|
|||
QByteArray m_peers; |
|||
QMutex m_guiLock; |
|||
QTimer* m_refresh; |
|||
QTimer* m_refreshNetwork; |
|||
QVector<eth::KeyPair> m_myKeys; |
|||
bool m_keysChanged = false; |
|||
int m_port; |
|||
int m_idealPeers; |
|||
QString m_clientName; |
|||
QStringList m_servers; |
|||
|
|||
QQuickView* m_view; |
|||
|
|||
QNetworkAccessManager m_webCtrl; |
|||
|
|||
std::unique_ptr<eth::Client> m_client; |
|||
}; |
|||
|
|||
#endif // MAIN_H
|
@ -0,0 +1,5 @@ |
|||
<RCC> |
|||
<qresource prefix="/"> |
|||
<file>Simple.qml</file> |
|||
</qresource> |
|||
</RCC> |
@ -0,0 +1,38 @@ |
|||
import QtQml 2.2 |
|||
import QtQuick 2.1 |
|||
import QtQuick.Controls 1.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import Qt.labs.settings 1.0 |
|||
import org.ethereum 1.0 |
|||
|
|||
Item { |
|||
id: main |
|||
anchors.fill: parent |
|||
anchors.margins: 9 |
|||
|
|||
// Qt.application.name: "Walleth" |
|||
// Qt.application.organization: "Ethereum" |
|||
// Qt.application.domain: "org.ethereum" |
|||
|
|||
Ethereum { |
|||
id: eth |
|||
} |
|||
|
|||
Account { |
|||
id: myAccount |
|||
address: Key.addressOf("84fc4ba9373c30bfe32d8c5a502854e7f1175878") |
|||
ethereum: eth |
|||
// TODO: state: eth.latest // could be eth.pending |
|||
// will provide balance, txCount, isContract, incomingTransactions (list model), outgoingTransactions (list model). |
|||
// transaction lists' items will provide value, from, to, final balance. |
|||
} |
|||
|
|||
// KeyPair provides makeTransaction(recvAddress, value, data (array)) |
|||
|
|||
Text { |
|||
text: "Balance: " + Balance.stringOf(myAccount.balance) + " [" + myAccount.txCount + "]" + "\nAccount: " + Key.stringOf(myAccount.address) |
|||
Layout.minimumHeight: 30 |
|||
Layout.fillHeight: true |
|||
Layout.fillWidth: true |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
#include "MainWin.h" |
|||
#include <QtWidgets/QApplication> |
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
QApplication a(argc, argv); |
|||
Main w; |
|||
w.show(); |
|||
|
|||
return a.exec(); |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue