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