diff --git a/abi/main.cpp b/abi/main.cpp index e895152cc..cdae81f1e 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -43,8 +43,8 @@ void help() << " -h,--help Print this help message and exit." << endl << " -V,--version Show the version and exit." << endl << "Input options:" << endl - << " -p,--prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl - << " -P,--no-prefix Require no input format to be prefixed." << endl + << " -f,--format-prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl + << " -F,--no-format-prefix Require no input format to be prefixed." << endl << " -t,--typing Require all arguments to be typed e.g. b32: (bytes32), u64: (uint64), b[]: (byte[]), i: (int256)." << endl << " -T,--no-typing Require no arguments to be typed." << endl << "Output options:" << endl @@ -53,8 +53,6 @@ void help() << " -x,--hex Display all data as hex." << endl << " -b,--binary Display all data as binary." << endl << " -p,--prefix Prefix by a base identifier." << endl - << " -z,--no-zeroes Remove any leading zeroes from the data." << endl - << " -n,--no-nulls Remove any trailing nulls from the data." << endl ; exit(0); } @@ -90,7 +88,9 @@ enum class Format { Binary, Hex, - Decimal + Decimal, + Open, + Close }; struct InvalidUserString: public Exception {}; @@ -115,6 +115,12 @@ static const map<Base, string> s_bases = { Base::Fixed, "fixed" } }; +struct EncodingPrefs +{ + Encoding e = Encoding::Auto; + bool prefix = true; +}; + struct ABIType { Base base = Base::Unknown; @@ -204,39 +210,72 @@ struct ABIType bool isBytes() const { return base == Base::Bytes && !size; } - string render(bytes const& _data) const + string render(bytes const& _data, EncodingPrefs _e) const { - if (base == Base::Uint) - return toString(fromBigEndian<u256>(_data)); - else if (base == Base::Int) - return toString((s256)fromBigEndian<u256>(_data)); + if (base == Base::Uint || base == Base::Int) + { + if (_e.e == Encoding::Hex) + return (_e.prefix ? "0x" : "") + toHex(toCompactBigEndian(fromBigEndian<bigint>(bytesConstRef(&_data).cropped(32 - size / 8)))); + else + { + bigint i = fromBigEndian<bigint>(bytesConstRef(&_data).cropped(32 - size / 8)); + if (base == Base::Int && i > (bigint(1) << (size - 1))) + i -= (bigint(1) << size); + return toString(i); + } + } else if (base == Base::Address) - return toString(Address(h256(_data))); + { + Address a = Address(h256(_data), Address::AlignRight); + return _e.e == Encoding::Binary ? asString(a.asBytes()) : ((_e.prefix ? "0x" : "") + toString(a)); + } + else if (isBytes()) + { + return _e.e == Encoding::Binary ? asString(_data) : ((_e.prefix ? "0x" : "") + toHex(_data)); + } + else if (base == Base::Bytes) + { + bytesConstRef b(&_data); + b = b.cropped(0, size); + return _e.e == Encoding::Binary ? asString(b) : ((_e.prefix ? "0x" : "") + toHex(b)); + } + else + throw InvalidFormat(); + } + + bytes unrender(bytes const& _data, Format _f) const + { + if (isBytes()) + { + auto ret = _data; + while (ret.size() % 32 != 0) + ret.push_back(0); + return ret; + } else - return toHex(_data); + return aligned(_data, _f, 32); } void noteHexInput(unsigned _nibbles) { if (base == Base::Unknown) { if (_nibbles == 40) base = Base::Address; else { base = Base::Bytes; size = _nibbles / 2; } } } void noteBinaryInput() { if (base == Base::Unknown) { base = Base::Bytes; size = 32; } } void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } } -}; -bytes aligned(bytes const& _b, ABIType _t, Format _f, unsigned _length) -{ - (void)_t; - bytes ret = _b; - while (ret.size() < _length) - if (_f == Format::Binary) - ret.push_back(0); - else - ret.insert(ret.begin(), 0); - while (ret.size() > _length) - if (_f == Format::Binary) - ret.pop_back(); - else - ret.erase(ret.begin()); - return ret; -} + bytes aligned(bytes const& _b, Format _f, unsigned _length) const + { + bytes ret = _b; + while (ret.size() < _length) + if (_f == Format::Binary) + ret.push_back(0); + else + ret.insert(ret.begin(), 0); + while (ret.size() > _length) + if (_f == Format::Binary) + ret.pop_back(); + else + ret.erase(ret.begin()); + return ret; + } +}; tuple<bytes, ABIType, Format> fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing) { @@ -269,25 +308,36 @@ tuple<bytes, ABIType, Format> fromUser(std::string const& _arg, Tristate _prefix type.noteBinaryInput(); return make_tuple(asBytes(val.substr(1)), type, Format::Binary); } + if (val == "[") + return make_tuple(bytes(), type, Format::Open); + if (val == "]") + return make_tuple(bytes(), type, Format::Close); } if (_prefix != Tristate::True) { - if (_arg.find_first_not_of("0123456789") == string::npos) + if (val.find_first_not_of("0123456789") == string::npos) { type.noteDecimalInput(); return make_tuple(toCompactBigEndian(bigint(val)), type, Format::Decimal); } - if (_arg.find_first_not_of("0123456789abcdefABCDEF") == string::npos) + if (val.find_first_not_of("0123456789abcdefABCDEF") == string::npos) { type.noteHexInput(val.size()); return make_tuple(fromHex(val), type, Format::Hex); } + if (val == "[") + return make_tuple(bytes(), type, Format::Open); + if (val == "]") + return make_tuple(bytes(), type, Format::Close); type.noteBinaryInput(); - return make_tuple(asBytes(_arg), type, Format::Binary); + return make_tuple(asBytes(val), type, Format::Binary); } throw InvalidUserString(); } +struct ExpectedOpen: public Exception {}; +struct ExpectedClose: public Exception {}; + struct ABIMethod { string name; @@ -355,77 +405,107 @@ struct ABIMethod bytes encode(vector<pair<bytes, Format>> const& _params) const { - // ALL WRONG!!!! - // INARITIES SHOULD BE HEIRARCHICAL! - bytes ret = name.empty() ? bytes() : id().asBytes(); + bytes suffix; + + // int int[] int + // example: 42 [ 1 2 3 ] 69 + // int[2][][3] + // example: [ [ [ 1 2 3 ] [ 4 5 6 ] ] [ ] ] + unsigned pi = 0; - vector<unsigned> inArity; - for (ABIType const& i: ins) - { - unsigned arity = 1; - for (auto j: i.dims) - if (j == -1) - { - ret += aligned(_params[pi].first, ABIType(), Format::Decimal, 32); - arity *= fromBigEndian<unsigned>(_params[pi].first); - pi++; - } - else - arity *= j; - if (i.isBytes()) - for (unsigned i = 0; i < arity; ++i) - inArity.push_back(arity); - } - unsigned ii = 0; - for (ABIType const& i: ins) + for (ABIType const& a: ins) { - for (unsigned j = 0; j < inArity[ii]; ++j) - { - if (i.base == Base::Bytes && !i.size) + auto put = [&]() { + if (a.isBytes()) + ret += h256(u256(_params[pi].first.size())).asBytes(); + suffix += a.unrender(_params[pi].first, _params[pi].second); + pi++; + }; + function<void(vector<int>, unsigned)> putDim = [&](vector<int> addr, unsigned q) { + if (addr.size() == a.dims.size()) + put(); + else { - ret += _params[pi].first; - while (ret.size() % 32 != 0) - ret.push_back(0); + if (_params[pi].second != Format::Open) + throw ExpectedOpen(); + int l = a.dims[addr.size()]; + if (l == -1) + { + // read ahead in params and discover the arity. + unsigned depth = 0; + l = 0; + for (unsigned pi2 = pi + 1; depth || _params[pi2].second != Format::Close;) + { + if (_params[pi2].second == Format::Open) + ++depth; + if (_params[pi2].second == Format::Close) + --depth; + if (!depth) + ++l; + if (++pi2 == _params.size()) + throw ExpectedClose(); + } + ret += h256(u256(l)).asBytes(); + } + q *= l; + for (addr.push_back(0); addr.back() < l; ++addr.back()) + putDim(addr, q); + if (_params[pi].second != Format::Close) + throw ExpectedClose(); } - else - ret += aligned(_params[pi].first, i, _params[pi].second, 32); - ++pi; - } - ++ii; + }; + putDim(vector<int>(), 1); } - return ret; + return ret + suffix; } - string decode(bytes const& _data, int _index = -1) + string decode(bytes const& _data, int _index, EncodingPrefs _ep) { stringstream out; if (_index == -1) out << "["; unsigned di = 0; - vector<ABIType> souts; vector<unsigned> catDims; - for (ABIType a: outs) + for (ABIType const& a: outs) { - unsigned q = 1; - for (auto& i: a.dims) - { - for (unsigned j = 0; j < q; ++j) - if (i == -1) + auto put = [&]() { + if (a.isBytes()) + { + catDims.push_back(fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32))); + di += 32; + } + }; + function<void(vector<int>, unsigned)> putDim = [&](vector<int> addr, unsigned q) { + if (addr.size() == a.dims.size()) + put(); + else + { + out << "["; + int l = a.dims[addr.size()]; + if (l == -1) { - catDims.push_back(fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32))); + l = fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32)); + catDims.push_back(l); di += 32; } - q *= i; - } - if (a.isBytes()) - souts.push_back(a); + q *= l; + for (addr.push_back(0); addr.back() < l; ++addr.back()) + putDim(addr, q); + out << "]"; + } + }; + putDim(vector<int>(), 1); } - for (ABIType const& a: souts) + unsigned d = 0; + for (ABIType const& a: outs) { auto put = [&]() { - out << a.render(bytesConstRef(&_data).cropped(di, 32).toBytes()) << ", "; - di += 32; + unsigned l = 32; + if (a.isBytes()) + l = (catDims[d++] + 31 / 32) * 32; + out << a.render(bytesConstRef(&_data).cropped(di, l).toBytes(), _ep) << ", "; + di += l; }; function<void(vector<int>)> putDim = [&](vector<int> addr) { if (addr.size() == a.dims.size()) @@ -433,9 +513,11 @@ struct ABIMethod else { out << "["; - auto d = addr; addr.push_back(0); - for (addr.back() = 0; addr.back() < a.dims[addr.size() - 1]; ++addr.back()) + int l = a.dims[addr.size() - 1]; + if (l == -1) + l = catDims[d++]; + for (addr.back() = 0; addr.back() < l; ++addr.back()) { if (addr.back()) out << ", "; @@ -543,10 +625,9 @@ int main(int argc, char** argv) Mode mode = Mode::Encode; string abiFile; string method; - Tristate prefix = Tristate::Mu; + Tristate formatPrefix = Tristate::Mu; Tristate typePrefix = Tristate::Mu; - bool clearZeroes = false; - bool clearNulls = false; + EncodingPrefs prefs; bool verbose = false; int outputIndex = -1; vector<pair<bytes, Format>> params; @@ -566,25 +647,23 @@ int main(int argc, char** argv) else if ((arg == "-i" || arg == "--index") && argc > i) outputIndex = atoi(argv[++i]); else if (arg == "-p" || arg == "--prefix") - prefix = Tristate::True; - else if (arg == "-P" || arg == "--no-prefix") - prefix = Tristate::False; + prefs.prefix = true; + else if (arg == "-f" || arg == "--format-prefix") + formatPrefix = Tristate::True; + else if (arg == "-F" || arg == "--no-format-prefix") + formatPrefix = Tristate::False; else if (arg == "-t" || arg == "--typing") typePrefix = Tristate::True; else if (arg == "-T" || arg == "--no-typing") typePrefix = Tristate::False; - else if (arg == "-z" || arg == "--no-zeroes") - clearZeroes = true; - else if (arg == "-n" || arg == "--no-nulls") - clearNulls = true; else if (arg == "-v" || arg == "--verbose") verbose = true; else if (arg == "-x" || arg == "--hex") - encoding = Encoding::Hex; + prefs.e = Encoding::Hex; else if (arg == "-d" || arg == "--decimal" || arg == "--dec") - encoding = Encoding::Decimal; + prefs.e = Encoding::Decimal; else if (arg == "-b" || arg == "--binary" || arg == "--bin") - encoding = Encoding::Binary; + prefs.e = Encoding::Binary; else if (arg == "-v" || arg == "--verbose") version(); else if (arg == "-V" || arg == "--version") @@ -593,7 +672,7 @@ int main(int argc, char** argv) method = arg; else { - auto u = fromUser(arg, prefix, typePrefix); + auto u = fromUser(arg, formatPrefix, typePrefix); args.push_back(get<1>(u)); params.push_back(make_pair(get<0>(u), get<2>(u))); } @@ -648,14 +727,8 @@ int main(int argc, char** argv) string encoded; for (int i = cin.get(); i != -1; i = cin.get()) encoded.push_back((char)i); - cout << m.decode(fromHex(encoded)); + cout << m.decode(fromHex(encoded), outputIndex, prefs); } - - // TODO: read abi to determine output format. - (void)encoding; - (void)clearZeroes; - (void)clearNulls; - (void)outputIndex; } return 0; diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 49c847a16..c81c86222 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -19,6 +19,7 @@ find_package (Qt5WebEngine QUIET) find_package (Qt5WebEngineWidgets QUIET) qt5_wrap_ui(ui_Main.h Main.ui) +qt5_wrap_ui(ui_Connect.h Connect.ui) qt5_wrap_ui(ui_Debugger.h Debugger.ui) qt5_wrap_ui(ui_Transact.h Transact.ui) @@ -32,8 +33,8 @@ endif () # eth_add_executable is defined in cmake/EthExecutableHelper.cmake eth_add_executable(${EXECUTABLE} - ICON alethzero - UI_RESOURCES alethzero.icns Main.ui Debugger.ui Transact.ui + ICON alethzero + UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui WIN_RESOURCES alethzero.rc ) diff --git a/alethzero/Connect.cpp b/alethzero/Connect.cpp new file mode 100644 index 000000000..32fa74f38 --- /dev/null +++ b/alethzero/Connect.cpp @@ -0,0 +1,63 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file Connect.cpp + * @author Alex Leverington <nessence@gmail.com> + * @date 2015 + */ + +#include "Connect.h" + +#include <libp2p/Host.h> +#include "ui_Connect.h" + +Connect::Connect(QWidget *parent) : + QDialog(parent), + ui(new Ui::Connect) +{ + ui->setupUi(this); +} + +Connect::~Connect() +{ + delete ui; +} + +void Connect::setEnvironment(QStringList const& _nodes) +{ + ui->host->addItems(_nodes); +} + +void Connect::reset() +{ + ui->nodeId->clear(); + ui->required->setChecked(false); +} + +QString Connect::host() +{ + return ui->host->currentText(); +} + +QString Connect::nodeId() +{ + return ui->nodeId->text(); +} + +bool Connect::required() +{ + return ui->required->isChecked(); +} diff --git a/alethzero/Connect.h b/alethzero/Connect.h new file mode 100644 index 000000000..8209a78af --- /dev/null +++ b/alethzero/Connect.h @@ -0,0 +1,55 @@ +/* + 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 Connect.h + * @author Alex Leverington <nessence@gmail.com> + * @date 2015 + */ + +#pragma once + +#include <QDialog> +#include <QList> + +namespace Ui { class Connect; } +namespace dev { namespace p2p { class Host; } } + +class Connect : public QDialog +{ + Q_OBJECT + +public: + explicit Connect(QWidget* _parent = 0); + ~Connect(); + + /// Populate host chooser with default host entries. + void setEnvironment(QStringList const& _nodes); + + /// Clear dialogue inputs. + void reset(); + + /// @returns the host string, as chosen or entered by the user. Assumed to be "hostOrIP:port" (:port is optional). + QString host(); + + /// @returns the identity of the node, as entered by the user. Assumed to be a 64-character hex string. + QString nodeId(); + + /// @returns true if Required is checked by the user, indicating that the host is a required Peer. + bool required(); + +private: + Ui::Connect* ui; +}; diff --git a/alethzero/Connect.ui b/alethzero/Connect.ui new file mode 100644 index 000000000..9a0522e5f --- /dev/null +++ b/alethzero/Connect.ui @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Connect</class> + <widget class="QDialog" name="Connect"> + <property name="windowModality"> + <enum>Qt::WindowModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>343</width> + <height>178</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>343</width> + <height>178</height> + </size> + </property> + <property name="windowTitle"> + <string>Connect to Peer</string> + </property> + <property name="modal"> + <bool>true</bool> + </property> + <layout class="QFormLayout" name="formLayout_2"> + <item row="1" column="0"> + <layout class="QFormLayout" name="formLayout"> + <item row="1" column="0" colspan="2"> + <widget class="QComboBox" name="host"> + <property name="minimumSize"> + <size> + <width>311</width> + <height>0</height> + </size> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QLineEdit" name="nodeId"> + <property name="placeholderText"> + <string>Node Id</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="QCheckBox" name="required"> + <property name="text"> + <string>Required (Always Connect to this Peer)</string> + </property> + <property name="tristate"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="formLabel"> + <property name="text"> + <string>Enter a peer to which a connection may be made:</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Connect</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Connect</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/alethzero/Debugger.cpp b/alethzero/Debugger.cpp index 93d6b1f6a..371630456 100644 --- a/alethzero/Debugger.cpp +++ b/alethzero/Debugger.cpp @@ -67,7 +67,8 @@ void Debugger::populate(dev::eth::Executive& _executive, dev::eth::Transaction c bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction) { try { - if (_executive.setup(_transaction)) + _executive.initialize(_transaction); + if (_executive.execute()) return false; } catch (...) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 34f240fbf..b59c9ce2d 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -118,7 +118,7 @@ <x>0</x> <y>0</y> <width>1617</width> - <height>24</height> + <height>22</height> </rect> </property> <widget class="QMenu" name="menu_File"> @@ -134,8 +134,7 @@ <addaction name="go"/> <addaction name="separator"/> <addaction name="upnp"/> - <addaction name="usePast"/> - <addaction name="localNetworking"/> + <addaction name="dropPeers"/> <addaction name="net"/> <addaction name="connect"/> </widget> @@ -284,7 +283,27 @@ </widget> <widget class="QWidget" name="layoutWidget"> <layout class="QGridLayout" name="gridLayout_2"> - <item row="1" column="2"> + <item row="3" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>&Listen on</string> + </property> + <property name="buddy"> + <cstring>port</cstring> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2"> + <widget class="QSpinBox" name="idealPeers"> + <property name="minimum"> + <number>1</number> + </property> + <property name="value"> + <number>5</number> + </property> + </widget> + </item> + <item row="3" column="2"> <widget class="QSpinBox" name="port"> <property name="minimum"> <number>1024</number> @@ -297,8 +316,8 @@ </property> </widget> </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="forceAddress"> + <item row="3" column="1"> + <widget class="QLineEdit" name="listenIP"> <property name="inputMask"> <string/> </property> @@ -310,16 +329,6 @@ </property> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>&Listen on</string> - </property> - <property name="buddy"> - <cstring>port</cstring> - </property> - </widget> - </item> <item row="0" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> @@ -330,20 +339,17 @@ </property> </widget> </item> - <item row="0" column="1" colspan="2"> - <widget class="QSpinBox" name="idealPeers"> - <property name="minimum"> - <number>1</number> - </property> - <property name="value"> - <number>5</number> + <item row="5" column="1"> + <widget class="QLineEdit" name="forcePublicIP"> + <property name="placeholderText"> + <string>Automatic</string> </property> </widget> </item> - <item row="2" column="1" colspan="2"> - <widget class="QLineEdit" name="clientName"> - <property name="placeholderText"> - <string>Anonymous</string> + <item row="5" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Public IP</string> </property> </widget> </item> @@ -357,6 +363,13 @@ </property> </widget> </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="clientName"> + <property name="placeholderText"> + <string>Anonymous</string> + </property> + </widget> + </item> </layout> </widget> </widget> @@ -1443,12 +1456,12 @@ font-size: 14pt</string> <string>Show &Anonymous Accounts</string> </property> </action> - <action name="usePast"> + <action name="dropPeers"> <property name="checkable"> <bool>true</bool> </property> <property name="text"> - <string>Use &Past Peers</string> + <string>&Drop Past Peers</string> </property> </action> <action name="loadJS"> @@ -1694,9 +1707,8 @@ font-size: 14pt</string> <tabstop>tabWidget</tabstop> <tabstop>urlEdit</tabstop> <tabstop>idealPeers</tabstop> - <tabstop>forceAddress</tabstop> + <tabstop>listenIP</tabstop> <tabstop>port</tabstop> - <tabstop>clientName</tabstop> <tabstop>transactionQueue</tabstop> <tabstop>pendingInfo</tabstop> <tabstop>blockChainFilter</tabstop> diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 36b6176f4..0de7e2cb8 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -28,6 +28,7 @@ //pragma GCC diagnostic ignored "-Werror=pedantic" #include <QtNetwork/QNetworkReply> #include <QtWidgets/QFileDialog> +#include <QtWidgets/QDialog> #include <QtWidgets/QMessageBox> #include <QtWidgets/QInputDialog> #include <QtWebEngine/QtWebEngine> @@ -141,7 +142,7 @@ Main::Main(QWidget *parent) : #endif #if ETH_DEBUG - m_servers.append("localhost:30300"); + m_servers.append("127.0.0.1:30300"); #endif m_servers.append(QString::fromStdString(Host::pocHost() + ":30303")); @@ -251,7 +252,30 @@ void Main::addNewId(QString _ids) NetworkPreferences Main::netPrefs() const { - return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); + auto listenIP = ui->listenIP->text().toStdString(); + try + { + listenIP = bi::address::from_string(listenIP).to_string(); + } + catch (...) + { + listenIP.clear(); + } + + auto publicIP = ui->forcePublicIP->text().toStdString(); + try + { + publicIP = bi::address::from_string(publicIP).to_string(); + } + catch (...) + { + publicIP.clear(); + } + + if (isPublicAddress(publicIP)) + return NetworkPreferences(publicIP, listenIP, ui->port->value(), ui->upnp->isChecked()); + else + return NetworkPreferences(listenIP, ui->port->value(), ui->upnp->isChecked()); } void Main::onKeysChanged() @@ -675,9 +699,7 @@ void Main::writeSettings() } s.setValue("upnp", ui->upnp->isChecked()); - s.setValue("forceAddress", ui->forceAddress->text()); - s.setValue("usePast", ui->usePast->isChecked()); - s.setValue("localNetworking", ui->localNetworking->isChecked()); + s.setValue("forceAddress", ui->forcePublicIP->text()); s.setValue("forceMining", ui->forceMining->isChecked()); s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("natSpec", ui->natSpec->isChecked()); @@ -685,6 +707,7 @@ void Main::writeSettings() s.setValue("showAllAccounts", ui->showAllAccounts->isChecked()); s.setValue("clientName", ui->clientName->text()); s.setValue("idealPeers", ui->idealPeers->value()); + s.setValue("listenIP", ui->listenIP->text()); s.setValue("port", ui->port->value()); s.setValue("url", ui->urlEdit->text()); s.setValue("privateChain", m_privateChain); @@ -744,9 +767,8 @@ void Main::readSettings(bool _skipGeometry) } ui->upnp->setChecked(s.value("upnp", true).toBool()); - ui->forceAddress->setText(s.value("forceAddress", "").toString()); - ui->usePast->setChecked(s.value("usePast", true).toBool()); - ui->localNetworking->setChecked(s.value("localNetworking", true).toBool()); + ui->forcePublicIP->setText(s.value("forceAddress", "").toString()); + ui->dropPeers->setChecked(false); ui->forceMining->setChecked(s.value("forceMining", false).toBool()); on_forceMining_triggered(); ui->paranoia->setChecked(s.value("paranoia", false).toBool()); @@ -757,6 +779,7 @@ void Main::readSettings(bool _skipGeometry) if (ui->clientName->text().isEmpty()) ui->clientName->setText(QInputDialog::getText(nullptr, "Enter identity", "Enter a name that will identify you on the peer network")); ui->idealPeers->setValue(s.value("idealPeers", ui->idealPeers->value()).toInt()); + ui->listenIP->setText(s.value("listenIP", "").toString()); ui->port->setValue(s.value("port", ui->port->value()).toInt()); ui->nameReg->setText(s.value("nameReg", "").toString()); m_privateChain = s.value("privateChain", "").toString(); @@ -1099,7 +1122,7 @@ void Main::refreshBlockChain() blocks.insert(bc.numberHash(b)); } else if (f.toLongLong() <= bc.number()) - blocks.insert(bc.numberHash(u256(f.toLongLong()))); + blocks.insert(bc.numberHash((unsigned)f.toLongLong())); else if (f.size() == 40) { Address h(f.toStdString()); @@ -1362,7 +1385,7 @@ void Main::on_transactionQueue_currentItemChanged() if (!!receipt.bloom()) s << "<div>Log Bloom: " << receipt.bloom() << "</div>"; else - s << "<div>Log Bloom: <i>Uneventful</i></div>"; + s << "<div>Log Bloom: <b><i>Uneventful</i></b></div>"; auto r = receipt.rlp(); s << "<div>Receipt: " << toString(RLP(r)) << "</div>"; s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>"; @@ -1461,7 +1484,7 @@ void Main::on_blocks_currentItemChanged() if (!!info.logBloom) s << "<div>Log Bloom: " << info.logBloom << "</div>"; else - s << "<div>Log Bloom: <i>Uneventful</i></div>"; + s << "<div>Log Bloom: <b><i>Uneventful</i></b></div>"; s << "<div>Transactions: <b>" << block[1].itemCount() << "</b> @<b>" << info.transactionsRoot << "</b>" << "</div>"; s << "<div>Uncles: <b>" << block[2].itemCount() << "</b> @<b>" << info.sha3Uncles << "</b>" << "</div>"; for (auto u: block[2]) @@ -1743,11 +1766,8 @@ void Main::on_net_triggered() if (ui->net->isChecked()) { web3()->setIdealPeerCount(ui->idealPeers->value()); - web3()->setNetworkPreferences(netPrefs()); + web3()->setNetworkPreferences(netPrefs(), ui->dropPeers->isChecked()); ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : h256()); - // TODO: p2p -// if (m_networkConfig.size()/* && ui->usePast->isChecked()*/) -// web3()->restoreNetwork(bytesConstRef((byte*)m_networkConfig.data(), m_networkConfig.size())); web3()->startNetwork(); ui->downloadView->setDownloadMan(ethereum()->downloadMan()); } @@ -1765,13 +1785,25 @@ void Main::on_connect_triggered() 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(":")) + + m_connect.setEnvironment(m_servers); + if (m_connect.exec() == QDialog::Accepted) { - string host = s.section(":", 0, 0).toStdString(); - unsigned short port = s.section(":", 1).toInt(); - web3()->connect(host, port); + bool required = m_connect.required(); + string host(m_connect.host().toStdString()); + NodeId nodeID; + try + { + nodeID = NodeId(fromHex(m_connect.nodeId().toStdString())); + } + catch (BadHexCharacter&) {} + + m_connect.reset(); + + if (required) + web3()->requirePeer(nodeID, host); + else + web3()->addNode(nodeID, host); } } @@ -1882,7 +1914,7 @@ void Main::on_go_triggered() ui->net->setChecked(true); on_net_triggered(); } - web3()->connect(Host::pocHost()); + web3()->addNode(p2p::NodeId(), Host::pocHost()); } QString Main::prettyU256(dev::u256 _n) const diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index ba89b455a..fccbc855d 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -40,6 +40,7 @@ #include "Context.h" #include "Transact.h" #include "NatspecHandler.h" +#include "Connect.h" namespace Ui { class Main; @@ -256,4 +257,6 @@ private: std::unique_ptr<DappHost> m_dappHost; DappLoader* m_dappLoader; QWebEnginePage* m_webPage; + + Connect m_connect; }; diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 534f18a69..6f52548c7 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -316,7 +316,7 @@ void Transact::rejigData() return; } else - gasNeeded = min<qint64>((qint64)ethereum()->gasLimitRemaining(), (qint64)((b - value()) / gasPrice())); + gasNeeded = (qint64)min<bigint>(ethereum()->gasLimitRemaining(), ((b - value()) / gasPrice())); // Dry-run execution to determine gas requirement and any execution errors Address to; @@ -326,7 +326,7 @@ void Transact::rejigData() else { to = m_context->fromString(ui->destination->currentText()); - er = ethereum()->call(s, value(), to, m_data, ethereum()->gasLimitRemaining(), gasPrice()); + er = ethereum()->call(s, value(), to, m_data, gasNeeded, gasPrice()); } gasNeeded = (qint64)(er.gasUsed + er.gasRefunded); htmlInfo = QString("<div class=\"info\"><span class=\"icon\">INFO</span> Gas required: %1 total = %2 base, %3 exec [%4 refunded later]</div>").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo; diff --git a/eth/main.cpp b/eth/main.cpp index e0cf76193..2a065cc8c 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -121,8 +121,9 @@ void help() << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl #endif << " -K,--kill-blockchain First kill the blockchain." << endl - << " -l,--listen <port> Listen on the given port for incoming connected (default: 30303)." << endl - << " -L,--local-networking Use peers whose addresses are local." << endl + << " --listen-ip <port> Listen on the given port for incoming connections (default: 30303)." << endl + << " -l,--listen <ip> Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl + << " -u,--public-ip <ip> Force public ip to given (default: auto)." << endl << " -m,--mining <on/off/number> Enable mining, optionally for a specified number of blocks (Default: off)" << endl << " -n,--upnp <on/off> Use upnp for NAT (default: on)." << endl << " -o,--mode <full/peer> Start a full node or a peer node (Default: full)." << endl @@ -131,7 +132,6 @@ void help() << " -r,--remote <host> Connect to remote host (default: none)." << endl << " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl << " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl - << " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl << " -V,--version Show the version and exit." << endl @@ -199,7 +199,9 @@ enum class NodeMode int main(int argc, char** argv) { + string listenIP; unsigned short listenPort = 30303; + string publicIP; string remoteHost; unsigned short remotePort = 30303; string dbPath; @@ -211,10 +213,8 @@ int main(int argc, char** argv) #if ETH_JSONRPC int jsonrpc = -1; #endif - string publicIP; bool bootstrap = false; bool upnp = true; - bool useLocal = false; bool forceMining = false; bool killChain = false; bool jit = false; @@ -250,7 +250,9 @@ int main(int argc, char** argv) for (int i = 1; i < argc; ++i) { string arg = argv[i]; - if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) + if (arg == "--listen-ip" && i + 1 < argc) + listenIP = argv[++i]; + else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) listenPort = (short)atoi(argv[++i]); else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) publicIP = argv[++i]; @@ -271,8 +273,6 @@ int main(int argc, char** argv) return -1; } } - else if (arg == "-L" || arg == "--local-networking") - useLocal = true; else if (arg == "-K" || arg == "--kill-blockchain") killChain = true; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) @@ -421,7 +421,7 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); - NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); + auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); std::string clientImplString = "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( @@ -449,9 +449,9 @@ int main(int argc, char** argv) web3.startNetwork(); if (bootstrap) - web3.connect(Host::pocHost()); + web3.addNode(p2p::NodeId(), Host::pocHost()); if (remoteHost.size()) - web3.connect(remoteHost, remotePort); + web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); #if ETH_JSONRPC shared_ptr<WebThreeStubServer> jsonrpcServer; @@ -511,7 +511,7 @@ int main(int argc, char** argv) string addr; unsigned port; iss >> addr >> port; - web3.connect(addr, (short)port); + web3.addNode(p2p::NodeId(), addr + ":" + toString(port ? port : p2p::c_defaultIPPort)); } else if (cmd == "netstop") { @@ -835,11 +835,8 @@ int main(int argc, char** argv) Executive e(state, c->blockChain(), 0); Transaction t = state.pending()[index]; state = state.fromPending(index); - bytes r = t.rlp(); try { - e.setup(&r); - OnOpFunc oof; if (format == "pretty") oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) @@ -872,7 +869,9 @@ int main(int argc, char** argv) f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl; }; - e.go(oof); + e.initialize(t); + if (!e.execute()) + e.go(oof); e.finalize(); } catch(Exception const& _e) diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 1c7a4cd61..6468e250f 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -42,14 +42,15 @@ enum class WhenError }; /// 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. +/// @param _w specifies the width of the first 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; + unsigned ii = 0; for (auto i: _data) - ret << std::hex << std::setfill('0') << std::setw(_w) << (int)(typename std::make_unsigned<decltype(i)>::type)i; + ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned<decltype(i)>::type)i; return ret.str(); } @@ -74,6 +75,13 @@ inline std::string asString(bytes const& _b) return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); } +/// Converts byte array ref 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(bytesConstRef _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) { diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index f5469ada8..456365299 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -141,6 +141,7 @@ public: return ret; } + /// @returns a random valued object. static FixedHash random() { return random(s_fixedHashEngine); } /// A generic std::hash compatible function object. diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 1e6f0acef..79a2fdea3 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -119,9 +119,21 @@ bool Ethasher::verify(BlockInfo const& _header) boundary.data()); #if ETH_DEBUG - // should be equivalent to: auto result = eval(_header); - assert((result.mixHash == _header.mixHash && result.value <= boundary) == ret); + if ((result.value <= boundary && result.mixHash == _header.mixHash) != ret) + { + cwarn << "Assertion failure coming: evaluated result gives different outcome to ethash_quick_check_difficulty"; + cwarn << "headerHash:" << _header.headerHash(WithoutNonce); + cwarn << "nonce:" << _header.nonce; + cwarn << "mixHash:" << _header.mixHash; + cwarn << "difficulty:" << _header.difficulty; + cwarn << "boundary:" << boundary; + cwarn << "result.value:" << result.value; + cwarn << "result.mixHash:" << result.mixHash; + } + assert((result.value <= boundary) == ret); + if (result.value <= boundary) + assert(result.mixHash == _header.mixHash); #endif return ret; diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index df6a08817..db2718fc4 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -38,6 +38,7 @@ using errinfo_difficulty = boost::error_info<struct tag_difficulty, u256>; using BadFieldError = boost::tuple<errinfo_field, errinfo_data>; struct DatabaseAlreadyOpen: virtual dev::Exception {}; +struct NotEnoughAvailableSpace: virtual dev::Exception {}; struct NotEnoughCash: virtual dev::Exception {}; struct GasPriceTooLow: virtual dev::Exception {}; struct BlockGasLimitReached: virtual dev::Exception {}; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index f80680f38..508531f9d 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -63,7 +63,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) return _out; } -ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub) +ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) { #if ALL_COMPILERS_ARE_CPP11_COMPLIANT static thread_local h256 h = _h ^ sha3(h256(u256(_sub))); @@ -131,10 +131,19 @@ void BlockChain::open(std::string _path, bool _killExisting) o.create_if_missing = true; ldb::DB::Open(o, _path + "/blocks", &m_blocksDB); ldb::DB::Open(o, _path + "/details", &m_extrasDB); - if (!m_blocksDB) - BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); - if (!m_extrasDB) - BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); + if (!m_blocksDB || !m_extrasDB) + { + if (boost::filesystem::space(_path + "/blocks").available < 1024) + { + cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing."; + BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace()); + } + else + { + cwarn << "Database already open. You appear to have another instance of ethereum running. Bailing."; + BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); + } + } if (!details(m_genesisHash)) { @@ -184,6 +193,19 @@ inline string toString(h256s const& _bs) return out.str(); } +LastHashes BlockChain::lastHashes(unsigned _n) const +{ + Guard l(x_lastLastHashes); + if (m_lastLastHashesNumber != _n || m_lastLastHashes.empty()) + { + m_lastLastHashes.resize(256); + for (unsigned i = 0; i < 256; ++i) + m_lastLastHashes[i] = _n >= i ? numberHash(_n - i) : h256(); + m_lastLastHashesNumber = _n; + } + return m_lastLastHashes; +} + h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) { _bq.tick(*this); @@ -412,6 +434,9 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) WriteGuard l(x_lastBlockHash); m_lastBlockHash = newHash; } + + noteCanonChanged(); + m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret); StructuredLogger::chainNewHead( @@ -428,7 +453,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) return ret; } -h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, bool _post) const +h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const { // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); if (!_from || !_to) @@ -438,38 +463,40 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo unsigned fn = details(_from).number; unsigned tn = details(_to).number; // cdebug << "treeRoute" << fn << "..." << tn; + h256 from = _from; while (fn > tn) { if (_pre) - ret.push_back(_from); - _from = details(_from).parent; + ret.push_back(from); + from = details(from).parent; fn--; // cdebug << "from:" << fn << _from.abridged(); } + h256 to = _to; while (fn < tn) { if (_post) - back.push_back(_to); - _to = details(_to).parent; + back.push_back(to); + to = details(to).parent; tn--; // cdebug << "to:" << tn << _to.abridged(); } - while (_from != _to) + while (from != to) { - assert(_from); - assert(_to); - _from = details(_from).parent; - _to = details(_to).parent; + assert(from); + assert(to); + from = details(from).parent; + to = details(to).parent; if (_pre) - ret.push_back(_from); + ret.push_back(from); if (_post) - back.push_back(_to); + back.push_back(to); fn--; tn--; // cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); } if (o_common) - *o_common = _from; + *o_common = from; ret.reserve(ret.size() + back.size()); for (auto it = back.cbegin(); it != back.cend(); ++it) ret.push_back(*it); @@ -677,7 +704,7 @@ vector<unsigned> BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earlie return ret; } -h256Set BlockChain::allUnclesFrom(h256 _parent) const +h256Set BlockChain::allUnclesFrom(h256 const& _parent) const { // Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). h256Set ret; @@ -692,7 +719,7 @@ h256Set BlockChain::allUnclesFrom(h256 _parent) const return ret; } -bool BlockChain::isKnown(h256 _hash) const +bool BlockChain::isKnown(h256 const& _hash) const { if (_hash == m_genesisHash) return true; @@ -706,7 +733,7 @@ bool BlockChain::isKnown(h256 _hash) const return !!d.size(); } -bytes BlockChain::block(h256 _hash) const +bytes BlockChain::block(h256 const& _hash) const { if (_hash == m_genesisHash) return m_genesisBlock; diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 235a39267..03c0fdcfd 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -30,9 +30,10 @@ #include <chrono> #include <libdevcore/Log.h> #include <libdevcore/Exceptions.h> +#include <libdevcore/Guards.h> #include <libethcore/Common.h> #include <libethcore/BlockInfo.h> -#include <libdevcore/Guards.h> +#include <libevm/ExtVMFace.h> #include "BlockDetails.h" #include "Account.h" #include "Transaction.h" @@ -61,7 +62,7 @@ struct BlockChainNote: public LogChannel { static const char* name() { return "= // TODO: Move all this Genesis stuff into Genesis.h/.cpp std::map<Address, Account> const& genesisState(); -ldb::Slice toSlice(h256 _h, unsigned _sub = 0); +ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map<h256, bytes>; using TransactionHashes = h256s; @@ -105,37 +106,42 @@ public: h256s import(bytes const& _block, OverlayDB const& _stateDB); /// Returns true if the given block is known (though not necessarily a part of the canon chain). - bool isKnown(h256 _hash) const; + bool isKnown(h256 const& _hash) const; /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. - BlockInfo info(h256 _hash) const { return BlockInfo(block(_hash)); } + BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash)); } BlockInfo info() const { return BlockInfo(block()); } /// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe. - bytes block(h256 _hash) const; + bytes block(h256 const& _hash) const; bytes block() const { return block(currentHash()); } /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. - BlockDetails details(h256 _hash) const { return queryExtras<BlockDetails, ExtraDetails>(_hash, m_details, x_details, NullBlockDetails); } + BlockDetails details(h256 const& _hash) const { return queryExtras<BlockDetails, ExtraDetails>(_hash, m_details, x_details, NullBlockDetails); } BlockDetails details() const { return details(currentHash()); } /// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe. - BlockLogBlooms logBlooms(h256 _hash) const { return queryExtras<BlockLogBlooms, ExtraLogBlooms>(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); } + BlockLogBlooms logBlooms(h256 const& _hash) const { return queryExtras<BlockLogBlooms, ExtraLogBlooms>(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); } BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); } /// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe. - BlockReceipts receipts(h256 _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); } + BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); } BlockReceipts receipts() const { return receipts(currentHash()); } /// Get a list of transaction hashes for a given block. Thread-safe. - TransactionHashes transactionHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } + TransactionHashes transactionHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); } /// Get a list of uncle hashes for a given block. Thread-safe. - UncleHashes uncleHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; } + UncleHashes uncleHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; } UncleHashes uncleHashes() const { return uncleHashes(currentHash()); } - h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras<BlockHash, ExtraBlockHash>(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; } + /// Get the hash for a given block's number. + h256 numberHash(unsigned _i) const { if (!_i) return genesisHash(); return queryExtras<BlockHash, ExtraBlockHash>(h256(u256(_i)), m_blockHashes, x_blockHashes, NullBlockHash).value; } + + /// Get the last N hashes for a given block. (N is determined by the LastHashes type.) + LastHashes lastHashes() const { return lastHashes(number()); } + LastHashes lastHashes(unsigned _i) const; /** Get the block blooms for a number of blocks. Thread-safe. * @returns the object pertaining to the blocks: @@ -158,15 +164,15 @@ public: std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const; /// Get a transaction from its hash. Thread-safe. - bytes transaction(h256 _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } - std::pair<h256, unsigned> transactionLocation(h256 _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair<h256, unsigned>(h256(), 0); return std::make_pair(ta.blockHash, ta.index); } + bytes transaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } + std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair<h256, unsigned>(h256(), 0); return std::make_pair(ta.blockHash, ta.index); } /// Get a block's transaction (RLP format) for the given block hash (or the most recent mined if none given) & index. Thread-safe. - bytes transaction(h256 _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); } + bytes transaction(h256 const& _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); } bytes transaction(unsigned _i) const { return transaction(currentHash(), _i); } /// Get a number for the given hash (or the most recent mined if none given). Thread-safe. - unsigned number(h256 _hash) const { return details(_hash).number; } + unsigned number(h256 const& _hash) const { return details(_hash).number; } unsigned number() const { return number(currentHash()); } /// Get a given block (RLP format). Thread-safe. @@ -178,7 +184,7 @@ public: /// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). /// @returns set including the header-hash of every parent (including @a _parent) up to and including generation +5 /// togther with all their quoted uncles. - h256Set allUnclesFrom(h256 _parent) const; + h256Set allUnclesFrom(h256 const& _parent) const; /** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent. @@ -194,7 +200,7 @@ public: * treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g * @endcode */ - h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; + h256s treeRoute(h256 const& _from, h256 const& _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; struct Statistics { @@ -219,7 +225,7 @@ private: void open(std::string _path, bool _killExisting = false); void close(); - template<class T, unsigned N> T queryExtras(h256 _h, std::map<h256, T>& _m, boost::shared_mutex& _x, T const& _n) const + template<class T, unsigned N> T queryExtras(h256 const& _h, std::map<h256, T>& _m, boost::shared_mutex& _x, T const& _n) const { { ReadGuard l(_x); @@ -268,6 +274,11 @@ private: void noteUsed(h256 const& _h, unsigned _extra = (unsigned)-1) const; std::chrono::system_clock::time_point m_lastCollection; + void noteCanonChanged() const { Guard l(x_lastLastHashes); m_lastLastHashes.clear(); } + mutable Mutex x_lastLastHashes; + mutable LastHashes m_lastLastHashes; + mutable unsigned m_lastLastHashesNumber = (unsigned)-1; + void updateStats() const; mutable Statistics m_lastStats; diff --git a/libethereum/Client.h b/libethereum/Client.h index 5fb30b8f0..1091bba58 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -110,7 +110,7 @@ public: private: u256 m_weiPerRef; u256 m_refsPerBlock; - u256 m_gasPerBlock = 1000000; + u256 m_gasPerBlock = 3141592; std::array<u256, 9> m_octiles; }; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index df30595b6..b45b9cf27 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -73,7 +73,7 @@ ExecutionResult ClientBase::call(Secret _secret, u256 _value, Address _dest, byt State temp = asOf(_blockNumber); u256 n = temp.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted); } catch (...) { @@ -92,7 +92,7 @@ ExecutionResult ClientBase::create(Secret _secret, u256 _value, bytes const& _da // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _data, n, _secret); - ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted); } catch (...) { diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 7dfc51b47..5bc42540f 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -178,7 +178,7 @@ void EthereumHost::maintainTransactions() for (auto const& i: m_tq.transactions()) if (ep->m_requireTransactions || (!m_transactionsSent.count(i.first) && !ep->m_knownTransactions.count(i.first))) { - b += i.second; + b += i.second.rlp(); ++n; m_transactionsSent.insert(i.first); } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index c574fa650..dfd526bef 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -35,7 +35,7 @@ using namespace dev::eth; Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level): m_s(_s), - m_lastHashes(_s.getLastHashes(_bc, (unsigned)_s.info().number - 1)), + m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)), m_depth(_level) {} @@ -55,12 +55,33 @@ void Executive::accrueSubState(SubState& _parentContext) _parentContext += m_ext->sub; } -bool Executive::setup(bytesConstRef _rlp) +void Executive::initialize(Transaction const& _transaction) { - // Entry point for a user-executed transaction. + m_t = _transaction; + + // Avoid transactions that would take us beyond the block gas limit. + u256 startGasUsed = m_s.gasUsed(); + if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit) + { + clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas(); + m_excepted = TransactionException::BlockGasLimitReached; + BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas())); + } + + // Check gas cost is enough. + m_gasRequired = Interface::txGas(m_t.data()); + if (m_t.gas() < m_gasRequired) + { + clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_gasRequired << " Got" << m_t.gas(); + m_excepted = TransactionException::OutOfGas; + BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)m_gasRequired, (bigint)m_t.gas())); + } + + // Avoid invalid transactions. + u256 nonceReq; try { - m_t = Transaction(_rlp, CheckSignature::Sender); + nonceReq = m_s.transactionsFrom(m_t.sender()); } catch (...) { @@ -68,15 +89,6 @@ bool Executive::setup(bytesConstRef _rlp) m_excepted = TransactionException::InvalidSignature; throw; } - return setup(); -} - -bool Executive::setup() -{ - // Entry point for a user-executed transaction. - - // Avoid invalid transactions. - auto nonceReq = m_s.transactionsFrom(m_t.sender()); if (m_t.nonce() != nonceReq) { clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce(); @@ -84,46 +96,32 @@ bool Executive::setup() BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce())); } - // Check gas cost is enough. - auto gasRequired = Interface::txGas(m_t.data()); - - if (m_t.gas() < gasRequired) - { - clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasRequired << " Got" << m_t.gas(); - m_excepted = TransactionException::OutOfGas; - BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)gasRequired, (bigint)m_t.gas())); - } - - bigint gasCost = (bigint)m_t.gas() * m_t.gasPrice(); - bigint totalCost = m_t.value() + gasCost; - // Avoid unaffordable transactions. - if (m_s.balance(m_t.sender()) < totalCost) + m_gasCost = (bigint)m_t.gas() * m_t.gasPrice(); + m_totalCost = m_t.value() + m_gasCost; + if (m_s.balance(m_t.sender()) < m_totalCost) { - clog(StateDetail) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender()); + clog(StateDetail) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender()); m_excepted = TransactionException::NotEnoughCash; - BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender()))); + BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender()))); } +} - u256 startGasUsed = m_s.gasUsed(); - if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit) - { - clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas(); - m_excepted = TransactionException::BlockGasLimitReached; - BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas())); - } +bool Executive::execute() +{ + // Entry point for a user-executed transaction. // Increment associated nonce for sender. m_s.noteSending(m_t.sender()); // Pay... - clog(StateDetail) << "Paying" << formatBalance(u256(gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; - m_s.subBalance(m_t.sender(), gasCost); + clog(StateDetail) << "Paying" << formatBalance(u256(m_gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; + m_s.subBalance(m_t.sender(), m_gasCost); if (m_t.isCreation()) - return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasRequired, &m_t.data(), m_t.sender()); + return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_gasRequired, &m_t.data(), m_t.sender()); else - return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasRequired, m_t.sender()); + return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_gasRequired, m_t.sender()); } bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index eb0c27ad2..3445ad407 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -41,10 +41,21 @@ struct VMTraceChannel: public LogChannel { static const char* name() { return "E * @brief Message-call/contract-creation executor; useful for executing transactions. * * Two ways of using this class - either as a transaction executive or a CALL/CREATE executive. - * In the first use, after construction, begin with setup() and end with finalize(). Call go() - * after setup() only if it returns false. + * + * In the first use, after construction, begin with initialize(), then execute() and end with finalize(). Call go() + * after execute() only if it returns false. + * * In the second use, after construction, begin with call() or create() and end with * accrueSubState(). Call go() after call()/create() only if it returns false. + * + * Example: + * @code + * Executive e(state, blockchain, 0); + * e.initialize(transaction); + * if (!e.execute()) + * e.go(); + * e.finalize(); + * @endcode */ class Executive { @@ -59,17 +70,17 @@ public: Executive(Executive const&) = delete; void operator=(Executive) = delete; - /// Set up the executive for evaluating a transaction. You must call finalize() following this. - /// @returns true iff go() must be called (and thus a VM execution in required). - bool setup(bytesConstRef _transaction); - /// Set up the executive for evaluating a transaction. You must call finalize() following this. - /// @returns true iff go() must be called (and thus a VM execution in required). - bool setup(Transaction const& _transaction) { m_t = _transaction; return setup(); } - /// Finalise a transaction previously set up with setup(). - /// @warning Only valid after setup(), and possibly go(). + /// Initializes the executive for evaluating a transaction. You must call finalize() at some point following this. + void initialize(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckSignature::None)); } + void initialize(Transaction const& _transaction); + /// Finalise a transaction previously set up with initialize(). + /// @warning Only valid after initialize() and execute(), and possibly go(). void finalize(); - /// @returns the transaction from setup(). - /// @warning Only valid after setup(). + /// Begins execution of a transaction. You must call finalize() following this. + /// @returns true if the transaction is done, false if go() must be called. + bool execute(); + /// @returns the transaction from initialize(). + /// @warning Only valid after initialize(). Transaction const& t() const { return m_t; } /// @returns the log entries created by this operation. /// @warning Only valid after finalise(). @@ -107,8 +118,6 @@ public: ExecutionResult executionResult() const; private: - bool setup(); - State& m_s; ///< The state to which this operation/transaction is applied. LastHashes m_lastHashes; std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required. @@ -125,6 +134,10 @@ private: Transaction m_t; ///< The original transaction. Set by setup(). LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). + + bigint m_gasRequired; + bigint m_gasCost; + bigint m_totalCost; }; } diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 47fdb7250..529ddc093 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -71,11 +71,13 @@ public: virtual void flushTransactions() = 0; /// Makes the given call. Nothing is recorded into the state. - virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = 0) = 0; + virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) = 0; + ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) { return call(_secret, _value, _dest, _data, _gas, _gasPrice, m_default); } /// Does the given creation. Nothing is recorded into the state. /// @returns the pair of the Address of the created contract together with its code. - virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = 0) = 0; + virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) = 0; + ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) { return create(_secret, _value, _data, _gas, _gasPrice, m_default); } // [STATE-QUERY API] diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 486abde12..378c60fa3 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -60,7 +60,18 @@ OverlayDB State::openDB(std::string _path, bool _killExisting) ldb::DB* db = nullptr; ldb::DB::Open(o, _path + "/state", &db); if (!db) - BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); + { + if (boost::filesystem::space(_path + "/state").available < 1024) + { + cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing."; + BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace()); + } + else + { + cwarn << "Database already open. You appear to have another instance of ethereum running. Bailing."; + BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); + } + } cnote << "Opened state DB."; return OverlayDB(db); @@ -388,8 +399,7 @@ bool State::cull(TransactionQueue& _tq) const { try { - Transaction t(i.second, CheckSignature::Sender); - if (t.nonce() <= transactionsFrom(t.sender())) + if (i.second.nonce() <= transactionsFrom(i.second.sender())) { _tq.drop(i.first); ret = true; @@ -411,7 +421,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga TransactionReceipts ret; auto ts = _tq.transactions(); - auto lh = getLastHashes(_bc, _bc.number()); + LastHashes lh; for (int goodTxs = 1; goodTxs;) { @@ -421,12 +431,11 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga { try { - Transaction t(i.second, CheckSignature::Sender); - if (t.gasPrice() >= _gp.ask(*this)) + if (i.second.gasPrice() >= _gp.ask(*this)) { - // don't have it yet! Execute it now. - uncommitToMine(); // boost::timer t; + if (lh.empty()) + lh = _bc.lastHashes(); execute(lh, i.second); ret.push_back(m_receipts.back()); _tq.noteGood(i); @@ -434,6 +443,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga // cnote << "TX took:" << t.elapsed() * 1000; } } +#if ETH_DEBUG catch (InvalidNonce const& in) { bigint const* req = boost::get_error_info<errinfo_required>(in); @@ -449,13 +459,19 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga else _tq.setFuture(i); } + catch (BlockGasLimitReached const& e) + { + _tq.setFuture(i); + } +#endif catch (Exception const& _e) { // Something else went wrong - drop it. _tq.drop(i.first); if (o_transactionQueueChanged) *o_transactionQueueChanged = true; - cwarn << "Sync went wrong\n" << diagnostic_information(_e); + cnote << "Dropping invalid transaction:"; + cnote << diagnostic_information(_e); } catch (std::exception const&) { @@ -463,6 +479,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga _tq.drop(i.first); if (o_transactionQueueChanged) *o_transactionQueueChanged = true; + cnote << "Transaction caused low-level exception :("; } } } @@ -498,7 +515,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) GenericTrieDB<MemoryDB> receiptsTrie(&rm); receiptsTrie.init(); - LastHashes lh = getLastHashes(_bc, (unsigned)m_previousBlock.number); + LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number); RLP rlp(_block); // All ok with the block generally. Play back the transactions now... @@ -509,7 +526,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) k << i; transactionsTrie.insert(&k.out(), tr.data()); - execute(lh, tr.data()); + execute(lh, Transaction(tr.data(), CheckSignature::Sender)); RLPStream receiptrlp; m_receipts.back().streamRLP(receiptrlp); @@ -1040,56 +1057,34 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const return true; } -LastHashes State::getLastHashes(BlockChain const& _bc, unsigned _n) const -{ - LastHashes ret; - ret.resize(256); - if (eth::c_protocolVersion > 49) - { - ret[0] = _bc.numberHash(_n); - for (unsigned i = 1; i < 256; ++i) - ret[i] = ret[i - 1] ? _bc.details(ret[i - 1]).parent : h256(); - } - return ret; -} - -ExecutionResult State::execute(BlockChain const& _bc, bytes const& _rlp, Permanence _p) -{ - return execute(getLastHashes(_bc, _bc.number()), &_rlp, _p); -} - -ExecutionResult State::execute(BlockChain const& _bc, bytesConstRef _rlp, Permanence _p) +ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p) { - return execute(getLastHashes(_bc, _bc.number()), _rlp, _p); -} - -// TODO: maintain node overlay revisions for stateroots -> each commit gives a stateroot + OverlayDB; allow overlay copying for rewind operations. -ExecutionResult State::execute(LastHashes const& _lh, bytesConstRef _rlp, Permanence _p) -{ -#ifndef ETH_RELEASE - commit(); // get an updated hash -#endif - +#if ETH_PARANOIA paranoia("start of execution.", true); - State old(*this); -#if ETH_PARANOIA auto h = rootHash(); #endif + // Create and initialize the executive. This will throw fairly cheaply and quickly if the + // transaction is bad in any way. Executive e(*this, _lh, 0); - e.setup(_rlp); + e.initialize(_t); - u256 startGasUsed = gasUsed(); + // Uncommitting is a non-trivial operation - only do it once we've verified as much of the + // transaction as possible. + uncommitToMine(); + // OK - transaction looks valid - execute. + u256 startGasUsed = gasUsed(); #if ETH_PARANOIA ctrace << "Executing" << e.t() << "on" << h; ctrace << toHex(e.t().rlp()); #endif + if (!e.execute()) #if ETH_VMTRACE - e.go(e.simpleTrace()); + e.go(e.simpleTrace()); #else - e.go(); + e.go(); #endif e.finalize(); diff --git a/libethereum/State.h b/libethereum/State.h index ee88f443e..5ed76cc27 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -188,15 +188,9 @@ public: /// Like sync but only operate on _tq, killing the invalid/old ones. bool cull(TransactionQueue& _tq) const; - /// Returns the last few block hashes of the current chain. - LastHashes getLastHashes(BlockChain const& _bc, unsigned _n) const; - /// Execute a given transaction. /// This will append @a _t to the transaction list and change the state accordingly. - ExecutionResult execute(BlockChain const& _bc, bytes const& _rlp, Permanence _p = Permanence::Committed); - ExecutionResult execute(BlockChain const& _bc, bytesConstRef _rlp, Permanence _p = Permanence::Committed); - ExecutionResult execute(LastHashes const& _lh, bytes const& _rlp, Permanence _p = Permanence::Committed) { return execute(_lh, &_rlp, _p); } - ExecutionResult execute(LastHashes const& _lh, bytesConstRef _rlp, Permanence _p = Permanence::Committed); + ExecutionResult execute(LastHashes const& _lh, Transaction const& _t, Permanence _p = Permanence::Committed); /// Get the remaining gas limit in this block. u256 gasLimitRemaining() const { return m_currentBlock.gasLimit - gasUsed(); } diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 5701fc4a5..803d320ee 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -46,7 +46,7 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP) UpgradeGuard ul(l); // If valid, append to blocks. - m_current[h] = _transactionRLP.toBytes(); + m_current[h] = t; m_known.insert(h); } catch (Exception const& _e) @@ -63,20 +63,20 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP) return true; } -void TransactionQueue::setFuture(std::pair<h256, bytes> const& _t) +void TransactionQueue::setFuture(std::pair<h256, Transaction> const& _t) { WriteGuard l(m_lock); if (m_current.count(_t.first)) { m_current.erase(_t.first); - m_unknown.insert(make_pair(Transaction(_t.second, CheckSignature::Sender).sender(), _t)); + m_unknown.insert(make_pair(_t.second.sender(), _t)); } } -void TransactionQueue::noteGood(std::pair<h256, bytes> const& _t) +void TransactionQueue::noteGood(std::pair<h256, Transaction> const& _t) { WriteGuard l(m_lock); - auto r = m_unknown.equal_range(Transaction(_t.second, CheckSignature::Sender).sender()); + auto r = m_unknown.equal_range(_t.second.sender()); for (auto it = r.first; it != r.second; ++it) m_current.insert(it->second); m_unknown.erase(r.first, r.second); diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index b58944e4f..b104b98ca 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -25,6 +25,7 @@ #include <libdevcore/Common.h> #include "libethcore/Common.h" #include <libdevcore/Guards.h> +#include "Transaction.h" namespace dev { @@ -46,19 +47,19 @@ public: void drop(h256 _txHash); - std::map<h256, bytes> transactions() const { ReadGuard l(m_lock); return m_current; } + std::map<h256, Transaction> transactions() const { ReadGuard l(m_lock); return m_current; } std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); } - void setFuture(std::pair<h256, bytes> const& _t); - void noteGood(std::pair<h256, bytes> const& _t); + void setFuture(std::pair<h256, Transaction> const& _t); + void noteGood(std::pair<h256, Transaction> const& _t); void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } private: mutable boost::shared_mutex m_lock; ///< General lock. std::set<h256> m_known; ///< Hashes of transactions in both sets. - std::map<h256, bytes> m_current; ///< Map of SHA3(tx) to tx. - std::multimap<Address, std::pair<h256, bytes>> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. + std::map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx. + std::multimap<Address, std::pair<h256, Transaction>> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. }; } diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index b995723c3..95c96e346 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -25,10 +25,11 @@ using namespace dev; using namespace dev::p2p; const unsigned dev::p2p::c_protocolVersion = 3; +const unsigned dev::p2p::c_defaultIPPort = 30303; bool p2p::isPublicAddress(std::string const& _addressToCheck) { - return isPublicAddress(bi::address::from_string(_addressToCheck)); + return _addressToCheck.empty() ? false : isPublicAddress(bi::address::from_string(_addressToCheck)); } bool p2p::isPublicAddress(bi::address const& _addressToCheck) @@ -67,7 +68,7 @@ bool p2p::isPrivateAddress(bi::address const& _addressToCheck) bool p2p::isPrivateAddress(std::string const& _addressToCheck) { - return isPrivateAddress(bi::address::from_string(_addressToCheck)); + return _addressToCheck.empty() ? false : isPrivateAddress(bi::address::from_string(_addressToCheck)); } // Helper function to determine if an address is localhost @@ -86,7 +87,7 @@ bool p2p::isLocalHostAddress(bi::address const& _addressToCheck) bool p2p::isLocalHostAddress(std::string const& _addressToCheck) { - return isLocalHostAddress(bi::address::from_string(_addressToCheck)); + return _addressToCheck.empty() ? false : isLocalHostAddress(bi::address::from_string(_addressToCheck)); } std::string p2p::reasonOf(DisconnectReason _r) diff --git a/libp2p/Common.h b/libp2p/Common.h index 3303c9c07..c9aee9a0e 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -50,7 +50,8 @@ namespace p2p /// Peer network protocol version. extern const unsigned c_protocolVersion; - +extern const unsigned c_defaultIPPort; + using NodeId = h512; bool isPrivateAddress(bi::address const& _addressToCheck); diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 41c2fbb77..ab39c5c25 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -66,10 +66,6 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, byte m_alias(networkAlias(_restoreNetwork)), m_lastPing(chrono::steady_clock::time_point::min()) { - for (auto address: m_ifAddresses) - if (address.is_v4()) - clog(NetNote) << "IP Address: " << address << " = " << (isPrivateAddress(address) ? "[LOCAL]" : "[PEER]"); - clog(NetNote) << "Id:" << id(); } @@ -287,67 +283,50 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) } } -void Host::determinePublic(string const& _publicAddress, bool _upnp) +void Host::determinePublic() { - m_peerAddresses.clear(); - - // no point continuing if there are no interface addresses or valid listen port - if (!m_ifAddresses.size() || m_listenPort < 1) - return; - - // populate interfaces we'll listen on (eth listens on all interfaces); ignores local - for (auto addr: m_ifAddresses) - if ((m_netPrefs.localNetworking || !isPrivateAddress(addr)) && !isLocalHostAddress(addr)) - m_peerAddresses.insert(addr); - - // if user supplied address is a public address then we use it - // if user supplied address is private, and localnetworking is enabled, we use it - bi::address reqPublicAddr(bi::address(_publicAddress.empty() ? bi::address() : bi::address::from_string(_publicAddress))); - bi::tcp::endpoint reqPublic(reqPublicAddr, m_listenPort); - bool isprivate = isPrivateAddress(reqPublicAddr); - bool ispublic = !isprivate && !isLocalHostAddress(reqPublicAddr); - if (!reqPublicAddr.is_unspecified() && (ispublic || (isprivate && m_netPrefs.localNetworking))) + // set m_tcpPublic := listenIP (if public) > public > upnp > unspecified address. + + auto ifAddresses = Network::getInterfaceAddresses(); + auto laddr = m_netPrefs.listenIPAddress.empty() ? bi::address() : bi::address::from_string(m_netPrefs.listenIPAddress); + auto lset = !laddr.is_unspecified(); + auto paddr = m_netPrefs.publicIPAddress.empty() ? bi::address() : bi::address::from_string(m_netPrefs.publicIPAddress); + auto pset = !paddr.is_unspecified(); + + bool listenIsPublic = lset && isPublicAddress(laddr); + bool publicIsHost = !lset && pset && ifAddresses.count(paddr); + + bi::tcp::endpoint ep(bi::address(), m_netPrefs.listenPort); + if (m_netPrefs.traverseNAT && listenIsPublic) { - if (!m_peerAddresses.count(reqPublicAddr)) - m_peerAddresses.insert(reqPublicAddr); - m_tcpPublic = reqPublic; - return; + clog(NetNote) << "Listen address set to Public address:" << laddr << ". UPnP disabled."; + ep.address(laddr); } - - // if address wasn't provided, then use first public ipv4 address found - for (auto addr: m_peerAddresses) - if (addr.is_v4() && !isPrivateAddress(addr)) - { - m_tcpPublic = bi::tcp::endpoint(*m_peerAddresses.begin(), m_listenPort); - return; - } - - // or find address via upnp - if (_upnp) + else if (m_netPrefs.traverseNAT && publicIsHost) + { + clog(NetNote) << "Public address set to Host configured address:" << paddr << ". UPnP disabled."; + ep.address(paddr); + } + else if (m_netPrefs.traverseNAT) { - bi::address upnpifaddr; - bi::tcp::endpoint upnpep = Network::traverseNAT(m_ifAddresses, m_listenPort, upnpifaddr); - if (!upnpep.address().is_unspecified() && !upnpifaddr.is_unspecified()) + bi::address natIFAddr; + ep = Network::traverseNAT(lset && ifAddresses.count(laddr) ? std::set<bi::address>({laddr}) : ifAddresses, m_netPrefs.listenPort, natIFAddr); + + if (lset && natIFAddr != laddr) + // if listen address is set, Host will use it, even if upnp returns different + clog(NetWarn) << "Listen address" << laddr << "differs from local address" << natIFAddr << "returned by UPnP!"; + + if (pset && ep.address() != paddr) { - if (!m_peerAddresses.count(upnpep.address())) - m_peerAddresses.insert(upnpep.address()); - m_tcpPublic = upnpep; - return; + // if public address is set, Host will advertise it, even if upnp returns different + clog(NetWarn) << "Specified public address" << paddr << "differs from external address" << ep.address() << "returned by UPnP!"; + ep.address(paddr); } } + else if (pset) + ep.address(paddr); - // or if no address provided, use private ipv4 address if local networking is enabled - if (reqPublicAddr.is_unspecified()) - if (m_netPrefs.localNetworking) - for (auto addr: m_peerAddresses) - if (addr.is_v4() && isPrivateAddress(addr)) - { - m_tcpPublic = bi::tcp::endpoint(addr, m_listenPort); - return; - } - - // otherwise address is unspecified - m_tcpPublic = bi::tcp::endpoint(bi::address(), m_listenPort); + m_tcpPublic = ep; } void Host::runAcceptor() @@ -401,7 +380,7 @@ string Host::pocHost() return "poc-" + strs[1] + ".ethdev.com"; } -void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPeerPort, unsigned short _udpNodePort) +void Host::addNode(NodeId const& _node, bi::address const& _addr, unsigned short _udpNodePort, unsigned short _tcpPeerPort) { // TODO: p2p clean this up (bring tested acceptor code over from network branch) while (isWorking() && !m_run) @@ -417,24 +396,59 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short cwarn << "Private port being recorded - setting to 0"; _tcpPeerPort = 0; } + + if (m_nodeTable) + m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(_addr, _udpNodePort), bi::tcp::endpoint(_addr, _tcpPeerPort)))); +} - boost::system::error_code ec; - bi::address addr = bi::address::from_string(_addr, ec); - if (ec) +void Host::requirePeer(NodeId const& _n, bi::address const& _udpAddr, unsigned short _udpPort, bi::address const& _tcpAddr, unsigned short _tcpPort) +{ + auto naddr = _udpAddr; + auto paddr = _tcpAddr.is_unspecified() ? naddr : _tcpAddr; + auto udp = bi::udp::endpoint(naddr, _udpPort); + auto tcp = bi::tcp::endpoint(paddr, _tcpPort ? _tcpPort : _udpPort); + Node node(_n, NodeIPEndpoint(udp, tcp)); + if (_n) { - bi::tcp::resolver *r = new bi::tcp::resolver(m_ioService); - r->async_resolve({_addr, toString(_tcpPeerPort)}, [=](boost::system::error_code const& _ec, bi::tcp::resolver::iterator _epIt) + // add or replace peer + shared_ptr<Peer> p; { - if (!_ec) + RecursiveGuard l(x_sessions); + if (m_peers.count(_n)) + p = m_peers[_n]; + else { - bi::tcp::endpoint tcp = *_epIt; - if (m_nodeTable) m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(tcp.address(), _udpNodePort), tcp))); + p.reset(new Peer()); + p->id = _n; + p->required = true; + m_peers[_n] = p; } - delete r; + p->endpoint.udp = node.endpoint.udp; + p->endpoint.tcp = node.endpoint.tcp; + } + connect(p); + } + else if (m_nodeTable) + { + shared_ptr<boost::asio::deadline_timer> t(new boost::asio::deadline_timer(m_ioService)); + m_timers.push_back(t); + + m_nodeTable->addNode(node); + t->expires_from_now(boost::posix_time::milliseconds(600)); + t->async_wait([this, _n](boost::system::error_code const& _ec) + { + if (!_ec && m_nodeTable) + if (auto n = m_nodeTable->node(_n)) + requirePeer(n.id, n.endpoint.udp.address(), n.endpoint.udp.port(), n.endpoint.tcp.address(), n.endpoint.tcp.port()); }); } - else - if (m_nodeTable) m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(addr, _udpNodePort), bi::tcp::endpoint(addr, _tcpPeerPort)))); +} + +void Host::relinquishPeer(NodeId const& _node) +{ + Guard l(x_requiredPeers); + if (m_requiredPeers.count(_node)) + m_requiredPeers.erase(_node); } void Host::connect(std::shared_ptr<Peer> const& _p) @@ -485,6 +499,9 @@ void Host::connect(std::shared_ptr<Peer> const& _p) Guard l(x_connecting); m_connecting.push_back(handshake); } + + // preempt setting failedAttempts; this value is cleared upon success + _p->m_failedAttempts++; handshake->start(); } @@ -541,6 +558,13 @@ void Host::run(boost::system::error_code const&) Guard l(x_connecting); m_connecting.remove_if([](std::weak_ptr<RLPXHandshake> h){ return h.lock(); }); } + { + Guard l(x_timers); + m_timers.remove_if([](std::shared_ptr<boost::asio::deadline_timer> t) + { + return t->expires_from_now().total_milliseconds() > 0; + }); + } for (auto p: m_sessions) if (auto pp = p.second.lock()) @@ -597,21 +621,21 @@ void Host::startedWorking() h.second->onStarting(); // try to open acceptor (todo: ipv6) - m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort); + m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs); // determine public IP, but only if we're able to listen for connections // todo: GUI when listen is unavailable in UI if (m_listenPort) { - determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); + determinePublic(); if (m_listenPort > 0) runAcceptor(); } else - clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "Listen port is invalid or unavailable. Node Table using default port (30303)."; + clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "TCP Listen port is invalid or unavailable."; - m_nodeTable.reset(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort() > 0 ? listenPort() : 30303)); + m_nodeTable.reset(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort())); m_nodeTable->setEventHandler(new HostNodeTableHandler(*this)); restoreNetwork(&m_restoreNetwork); @@ -720,6 +744,9 @@ void Host::restoreNetwork(bytesConstRef _b) if (!isStarted()) BOOST_THROW_EXCEPTION(NetworkStartRequired()); + if (m_dropPeers) + return; + RecursiveGuard l(x_sessions); RLP r(_b); if (r.itemCount() > 0 && r[0].isInt() && r[0].toInt<unsigned>() == dev::p2p::c_protocolVersion) diff --git a/libp2p/Host.h b/libp2p/Host.h index 0feda364f..c161f8437 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -70,9 +70,7 @@ private: * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. * - * @todo cleanup startPeerSession * @todo determinePublic: ipv6, udp - * @todo handle conflict if addNode/requireNode called and Node already exists w/conflicting tcp or udp port * @todo per-session keepalive/ping instead of broadcast; set ping-timeout via median-latency */ class Host: public Worker @@ -105,8 +103,14 @@ public: template <class T> std::shared_ptr<T> cap() const { try { return std::static_pointer_cast<T>(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } } /// Add node as a peer candidate. Node is added if discovery ping is successful and table has capacity. - void addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPort, unsigned short _udpPort); + void addNode(NodeId const& _node, bi::address const& _addr, unsigned short _udpPort, unsigned short _tcpPort); + + /// Create Peer and attempt keeping peer connected. + void requirePeer(NodeId const& _node, bi::address const& _udpAddr, unsigned short _udpPort, bi::address const& _tcpAddr = bi::address(), unsigned short _tcpPort = 0); + /// Note peer as no longer being required. + void relinquishPeer(NodeId const& _node); + /// Set ideal number of peers. void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } @@ -117,10 +121,10 @@ public: size_t peerCount() const; /// Get the address we're listening on currently. - std::string listenAddress() const { return m_tcpPublic.address().to_string(); } + std::string listenAddress() const { return m_netPrefs.listenIPAddress.empty() ? "0.0.0.0" : m_netPrefs.listenIPAddress; } /// Get the port we're listening on currently. - unsigned short listenPort() const { return m_tcpPublic.port(); } + unsigned short listenPort() const { return m_netPrefs.listenPort; } /// Serialise the set of known peers. bytes saveNetwork() const; @@ -128,7 +132,7 @@ public: // TODO: P2P this should be combined with peers into a HostStat object of some kind; coalesce data, as it's only used for status information. Peers getPeers() const { RecursiveGuard l(x_sessions); Peers ret; for (auto const& i: m_peers) ret.push_back(*i.second); return ret; } - void setNetworkPreferences(NetworkPreferences const& _p) { auto had = isStarted(); if (had) stop(); m_netPrefs = _p; if (had) start(); } + void setNetworkPreferences(NetworkPreferences const& _p, bool _dropPeers = false) { m_dropPeers = _dropPeers; auto had = isStarted(); if (had) stop(); m_netPrefs = _p; if (had) start(); } /// Start network. @threadsafe void start(); @@ -154,8 +158,8 @@ protected: private: bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } - /// Populate m_peerAddresses with available public addresses. - void determinePublic(std::string const& _publicAddress, bool _upnp); + /// Determines and sets m_tcpPublic to publicly advertised address. + void determinePublic(); void connect(std::shared_ptr<Peer> const& _p); @@ -192,7 +196,7 @@ private: NetworkPreferences m_netPrefs; ///< Network settings. /// Interface addresses (private, public) - std::vector<bi::address> m_ifAddresses; ///< Interface addresses. + std::set<bi::address> m_ifAddresses; ///< Interface addresses. int m_listenPort = -1; ///< What port are we listening on. -1 means binding failed or acceptor hasn't been initialized. @@ -211,6 +215,10 @@ private: /// Shared storage of Peer objects. Peers are created or destroyed on demand by the Host. Active sessions maintain a shared_ptr to a Peer; std::map<NodeId, std::shared_ptr<Peer>> m_peers; + + /// Peers we try to connect regardless of p2p network. + std::set<NodeId> m_requiredPeers; + Mutex x_requiredPeers; /// The nodes to which we are currently connected. Used by host to service peer requests and keepAlivePeers and for shutdown. (see run()) /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. @@ -222,12 +230,15 @@ private: unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. - std::set<bi::address> m_peerAddresses; ///< Public addresses that peers (can) know us by. - std::map<CapDesc, std::shared_ptr<HostCapabilityFace>> m_capabilities; ///< Each of the capabilities we support. + + /// Deadline timers used for isolated network events. GC'd by run. + std::list<std::shared_ptr<boost::asio::deadline_timer>> m_timers; + Mutex x_timers; std::chrono::steady_clock::time_point m_lastPing; ///< Time we sent the last ping to all peers. bool m_accepting = false; + bool m_dropPeers = false; }; } diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index bdd1f0108..74bc8bd45 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -27,6 +27,7 @@ #endif #include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/split.hpp> #include <libdevcore/Common.h> #include <libdevcore/Assertions.h> @@ -40,9 +41,9 @@ using namespace std; using namespace dev; using namespace dev::p2p; -std::vector<bi::address> Network::getInterfaceAddresses() +std::set<bi::address> Network::getInterfaceAddresses() { - std::vector<bi::address> addresses; + std::set<bi::address> addresses; #ifdef _WIN32 WSAData wsaData; @@ -72,7 +73,7 @@ std::vector<bi::address> Network::getInterfaceAddresses() char *addrStr = inet_ntoa(addr); bi::address address(bi::address::from_string(addrStr)); if (!isLocalHostAddress(address)) - addresses.push_back(address.to_v4()); + addresses.insert(address.to_v4()); } WSACleanup(); @@ -91,7 +92,7 @@ std::vector<bi::address> Network::getInterfaceAddresses() in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); if (!isLocalHostAddress(address)) - addresses.push_back(address); + addresses.insert(address); } else if (ifa->ifa_addr->sa_family == AF_INET6) { @@ -101,7 +102,7 @@ std::vector<bi::address> Network::getInterfaceAddresses() memcpy(&bytes[0], addr.s6_addr, 16); boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); if (!isLocalHostAddress(address)) - addresses.push_back(address); + addresses.insert(address); } } @@ -113,13 +114,39 @@ std::vector<bi::address> Network::getInterfaceAddresses() return std::move(addresses); } -int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort) +int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, NetworkPreferences const& _netPrefs) { int retport = -1; - for (unsigned i = 0; i < 2; ++i) + if (_netPrefs.listenIPAddress.empty()) + for (unsigned i = 0; i < 2; ++i) + { + // try to connect w/listenPort, else attempt net-allocated port + bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _netPrefs.listenPort); + try + { + _acceptor.open(endpoint.protocol()); + _acceptor.set_option(ba::socket_base::reuse_address(true)); + _acceptor.bind(endpoint); + _acceptor.listen(); + retport = _acceptor.local_endpoint().port(); + break; + } + catch (...) + { + if (i) + { + // both attempts failed + cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); + } + + // first attempt failed + _acceptor.close(); + continue; + } + } + else { - // try to connect w/listenPort, else attempt net-allocated port - bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort); + bi::tcp::endpoint endpoint(bi::address::from_string(_netPrefs.listenIPAddress), _netPrefs.listenPort); try { _acceptor.open(endpoint.protocol()); @@ -127,25 +154,18 @@ int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort _acceptor.bind(endpoint); _acceptor.listen(); retport = _acceptor.local_endpoint().port(); - break; } catch (...) { - if (i) - { - // both attempts failed - cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); - } - - // first attempt failed - _acceptor.close(); - continue; + clog(NetWarn) << "Couldn't start accepting connections on host. Failed to accept socket.\n" << boost::current_exception_diagnostic_information(); } + assert(retport == _netPrefs.listenPort); + return retport; } return retport; } -bi::tcp::endpoint Network::traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) +bi::tcp::endpoint Network::traverseNAT(std::set<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpInterfaceAddr) { asserts(_listenPort != 0); @@ -157,26 +177,26 @@ bi::tcp::endpoint Network::traverseNAT(std::vector<bi::address> const& _ifAddres // let m_upnp continue as null - we handle it properly. catch (...) {} - bi::tcp::endpoint upnpep; + bi::tcp::endpoint upnpEP; if (upnp && upnp->isValid()) { - bi::address paddr; + bi::address pAddr; int extPort = 0; for (auto const& addr: _ifAddresses) if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) { - paddr = addr; + pAddr = addr; break; } - auto eip = upnp->externalIP(); - bi::address eipaddr(bi::address::from_string(eip)); - if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) + auto eIP = upnp->externalIP(); + bi::address eIPAddr(bi::address::from_string(eIP)); + if (extPort && eIP != string("0.0.0.0") && !isPrivateAddress(eIPAddr)) { clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << "."; - clog(NetNote) << "External addr:" << eip; - o_upnpifaddr = paddr; - upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort); + clog(NetNote) << "External addr:" << eIP; + o_upnpInterfaceAddr = pAddr; + upnpEP = bi::tcp::endpoint(eIPAddr, (unsigned short)extPort); } else clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place)."; @@ -185,5 +205,33 @@ bi::tcp::endpoint Network::traverseNAT(std::vector<bi::address> const& _ifAddres delete upnp; } - return upnpep; + return upnpEP; } + +bi::tcp::endpoint Network::resolveHost(string const& _addr) +{ + static boost::asio::io_service s_resolverIoService; + + vector<string> split; + boost::split(split, _addr, boost::is_any_of(":")); + unsigned port = split.size() > 1 ? stoi(split[1]) : dev::p2p::c_defaultIPPort; + + bi::tcp::endpoint ep(bi::address(), port); + boost::system::error_code ec; + bi::address address = bi::address::from_string(split[0], ec); + if (!ec) + ep.address(address); + else + { + boost::system::error_code ec; + // resolve returns an iterator (host can resolve to multiple addresses) + bi::tcp::resolver r(s_resolverIoService); + auto it = r.resolve({split[0], toString(port)}, ec); + if (ec) + clog(NetWarn) << "Error resolving host address " << _addr << ":" << ec.message(); + else + ep = *it; + } + return ep; +} + diff --git a/libp2p/Network.h b/libp2p/Network.h index aeeabf329..d02ce3cbe 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -39,12 +39,19 @@ namespace p2p struct NetworkPreferences { - NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {} + // Default Network Preferences + NetworkPreferences(unsigned short lp = 30303): listenPort(lp) {} + + // Network Preferences with specific Listen IP + NetworkPreferences(std::string const& l, unsigned short lp = 30303, bool u = true): publicIPAddress(), listenIPAddress(l), listenPort(lp), traverseNAT(u) {} + + // Network Preferences with intended Public IP + NetworkPreferences(std::string const& publicIP, std::string const& l = std::string(), unsigned short lp = 30303, bool u = true): publicIPAddress(publicIP), listenIPAddress(l), listenPort(lp), traverseNAT(u) { if (!publicIPAddress.empty() && !isPublicAddress(publicIPAddress)) BOOST_THROW_EXCEPTION(InvalidPublicIPAddress()); } + std::string publicIPAddress; + std::string listenIPAddress; unsigned short listenPort = 30303; - std::string publicIP; - bool upnp = true; - bool localNetworking = false; + bool traverseNAT = true; }; /** @@ -55,14 +62,17 @@ class Network { public: /// @returns public and private interface addresses - static std::vector<bi::address> getInterfaceAddresses(); + static std::set<bi::address> getInterfaceAddresses(); /// Try to bind and listen on _listenPort, else attempt net-allocated port. - static int tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort); + static int tcp4Listen(bi::tcp::acceptor& _acceptor, NetworkPreferences const& _netPrefs); /// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port. - static bi::tcp::endpoint traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); + static bi::tcp::endpoint traverseNAT(std::set<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpInterfaceAddr); + + /// Resolve "host:port" string as TCP endpoint. Returns unspecified endpoint on failure. + static bi::tcp::endpoint resolveHost(std::string const& _host); }; - + } } diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index c230c5441..407b99942 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -101,6 +101,7 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node) shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, NodeIPEndpoint(_node.endpoint.udp, _node.endpoint.tcp))); m_nodes[_node.id] = ret; ret->cullEndpoint(); + clog(NodeTableConnect) << "addNode pending for" << m_node.endpoint.udp << m_node.endpoint.tcp; PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); p.sign(m_secret); m_socketPointer->send(p); @@ -450,6 +451,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes return; // unsolicited pong; don't note node as active } + clog(NodeTableConnect) << "PONG from " << nodeid.abridged() << _from; break; } @@ -550,31 +552,16 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) clog(NodeTableEvent) << "refreshing buckets"; bool connected = m_socketPointer->isOpen(); - bool refreshed = false; if (connected) { - Guard l(x_state); - for (auto& d: m_state) - if (chrono::steady_clock::now() - d.modified > c_bucketRefresh) - { - d.touch(); - while (!d.nodes.empty()) - { - auto n = d.nodes.front(); - if (auto p = n.lock()) - { - refreshed = true; - ping(p.get()); - break; - } - d.nodes.pop_front(); - } - } + NodeId randNodeId; + crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(0, h256::size)); + crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(h256::size, h256::size)); + discover(randNodeId); } - unsigned nextRefresh = connected ? (refreshed ? 200 : c_bucketRefresh.count()*1000) : 10000; auto runcb = [this](boost::system::error_code const& error) { doRefreshBuckets(error); }; - m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(nextRefresh)); + m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(c_bucketRefresh.count())); m_bucketRefreshTimer.async_wait(runcb); } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 6a167930d..9ff28b7f1 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -195,7 +195,7 @@ private: /* todo: replace boost::posix_time; change constants to upper camelcase */ boost::posix_time::milliseconds const c_evictionCheckInterval = boost::posix_time::milliseconds(75); ///< Interval at which eviction timeouts are checked. std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). - std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] + std::chrono::milliseconds const c_bucketRefresh = std::chrono::milliseconds(112500); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] struct NodeBucket { diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 8774b6578..bfa2eaeb6 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -47,7 +47,6 @@ namespace p2p * those peers. Modifying these properties via a storage backend alleviates * Host of the responsibility. (&& remove save/restoreNetwork) * @todo reimplement recording of historical session information on per-transport basis - * @todo rebuild nodetable when localNetworking is enabled/disabled * @todo move attributes into protected */ class Peer: public Node diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index d7bfca34e..dac588149 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -216,12 +216,8 @@ bool Session::interpret(PacketType _t, RLP const& _r) NodeId id = _r[i][2].toHash<NodeId>(); clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")"; -// clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")" << isPrivateAddress(peerAddress) << this->id().abridged() << isPrivateAddress(endpoint().address()) << m_server->m_peers.count(id) << (m_server->m_peers.count(id) ? isPrivateAddress(m_server->m_peers.at(id)->address.address()) : -1); - // todo: draft spec: ignore if dist(us,item) - dist(us,them) > 1 - - // TODO: isPrivate - if (!m_server->m_netPrefs.localNetworking && isPrivateAddress(peerAddress)) + if (!isPublicAddress(peerAddress)) goto CONTINUE; // Private address. Ignore. if (!id) @@ -241,7 +237,7 @@ bool Session::interpret(PacketType _t, RLP const& _r) // OK passed all our checks. Assume it's good. addRating(1000); - m_server->addNode(id, ep.address().to_string(), ep.port(), ep.port()); + m_server->addNode(id, ep.address(), ep.port(), ep.port()); clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")"; CONTINUE:; LAMEPEER:; diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 68160d053..374f986b0 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -203,7 +203,10 @@ void UDPSocket<Handler, MaxDatagramSize>::doRead() auto self(UDPSocket<Handler, MaxDatagramSize>::shared_from_this()); m_socket.async_receive_from(boost::asio::buffer(m_recvData), m_recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) { - if (_ec) + // ASIO Safety: It is possible that ASIO will call lambda w/o an error + // and after the socket has been disconnected. Checking m_closed + // guarantees that m_host will not be called after disconnect(). + if (_ec || m_closed) return disconnectWithError(_ec); assert(_len); @@ -222,7 +225,7 @@ void UDPSocket<Handler, MaxDatagramSize>::doWrite() auto self(UDPSocket<Handler, MaxDatagramSize>::shared_from_this()); m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.endpoint(), [this, self](boost::system::error_code _ec, std::size_t) { - if (_ec) + if (_ec || m_closed) return disconnectWithError(_ec); else { diff --git a/libtestutils/CMakeLists.txt b/libtestutils/CMakeLists.txt index 202b9823c..4ae52e0c9 100644 --- a/libtestutils/CMakeLists.txt +++ b/libtestutils/CMakeLists.txt @@ -18,6 +18,11 @@ set(EXECUTABLE testutils) file(GLOB HEADERS "*.h") +if (NOT JSONRPC) + list(REMOVE_ITEM SRC_LIST "./FixedWebThreeServer.cpp") + list(REMOVE_ITEM HEADERS "./FixedWebThreeServer.h") +endif() + if (ETH_STATIC) add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) else() diff --git a/libtestutils/Common.cpp b/libtestutils/Common.cpp index f15a2da12..86f96f667 100644 --- a/libtestutils/Common.cpp +++ b/libtestutils/Common.cpp @@ -74,7 +74,7 @@ std::string dev::test::toTestFilePath(std::string const& _filename) std::string dev::test::getRandomPath() { std::stringstream stream; - stream << getDataDir() << "/EthereumTests/" << randomNumber(); + stream << getDataDir("EthereumTests") << "/" << randomNumber(); return stream.str(); } diff --git a/libtestutils/TransientDirectory.cpp b/libtestutils/TransientDirectory.cpp index 48beca7e3..694784e25 100644 --- a/libtestutils/TransientDirectory.cpp +++ b/libtestutils/TransientDirectory.cpp @@ -22,12 +22,16 @@ #include <boost/filesystem.hpp> #include <libdevcore/Exceptions.h> #include "TransientDirectory.h" - using namespace std; using namespace dev; using namespace dev::test; -TransientDirectory::TransientDirectory(std::string const& _path) : m_path(_path) +TransientDirectory::TransientDirectory(): + TransientDirectory((boost::filesystem::temp_directory_path() / "eth_transient" / toString(FixedHash<4>::random())).string()) +{} + +TransientDirectory::TransientDirectory(std::string const& _path): + m_path(_path) { // we never ever want to delete a directory (including all its contents) that we did not create ourselves. if (boost::filesystem::exists(m_path)) diff --git a/libtestutils/TransientDirectory.h b/libtestutils/TransientDirectory.h index 328b4c92b..21a338e59 100644 --- a/libtestutils/TransientDirectory.h +++ b/libtestutils/TransientDirectory.h @@ -37,7 +37,8 @@ namespace test class TransientDirectory { public: - TransientDirectory(std::string const& _path = getRandomPath()); + TransientDirectory(); + TransientDirectory(std::string const& _path); ~TransientDirectory(); std::string const& path() const { return m_path; } diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp index 6c6414741..b2c6765b5 100644 --- a/libwebthree/WebThree.cpp +++ b/libwebthree/WebThree.cpp @@ -27,7 +27,6 @@ #include <boost/filesystem.hpp> #include <libdevcore/Log.h> -#include <libp2p/Host.h> #include <libethereum/Defaults.h> #include <libethereum/EthereumHost.h> #include <libwhisper/WhisperHost.h> @@ -73,12 +72,12 @@ WebThreeDirect::~WebThreeDirect() m_ethereum.reset(); } -void WebThreeDirect::setNetworkPreferences(p2p::NetworkPreferences const& _n) +void WebThreeDirect::setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers) { auto had = haveNetwork(); if (had) stopNetwork(); - m_net.setNetworkPreferences(_n); + m_net.setNetworkPreferences(_n, _dropPeers); if (had) startNetwork(); } @@ -103,7 +102,14 @@ bytes WebThreeDirect::saveNetwork() return m_net.saveNetwork(); } -void WebThreeDirect::connect(std::string const& _seedHost, unsigned short _port) +void WebThreeDirect::addNode(NodeId const& _node, bi::tcp::endpoint const& _host) { - m_net.addNode(NodeId(), _seedHost, _port, _port); + m_net.addNode(_node, _host.address(), _host.port(), _host.port()); } + +void WebThreeDirect::requirePeer(NodeId const& _node, bi::tcp::endpoint const& _host) +{ + m_net.requirePeer(_node, _host.address(), _host.port()); +} + + diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index 242639af4..a0e5cc666 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -63,9 +63,12 @@ public: /// Same as peers().size(), but more efficient. virtual size_t peerCount() const = 0; - /// Connect to a particular peer. - virtual void connect(std::string const& _seedHost, unsigned short _port) = 0; - + /// Add node to connect to. + virtual void addNode(p2p::NodeId const& _node, bi::tcp::endpoint const& _hostEndpoint) = 0; + + /// Require connection to peer. + virtual void requirePeer(p2p::NodeId const& _node, bi::tcp::endpoint const& _endpoint) = 0; + /// Save peers virtual dev::bytes saveNetwork() = 0; @@ -74,7 +77,7 @@ public: virtual bool haveNetwork() const = 0; - virtual void setNetworkPreferences(p2p::NetworkPreferences const& _n) = 0; + virtual void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers) = 0; virtual p2p::NodeId id() const = 0; @@ -137,22 +140,34 @@ public: /// Same as peers().size(), but more efficient. size_t peerCount() const override; - - /// Connect to a particular peer. - void connect(std::string const& _seedHost, unsigned short _port = 30303) override; + + /// Add node to connect to. + virtual void addNode(p2p::NodeId const& _node, bi::tcp::endpoint const& _hostEndpoint) override; + + /// Add node to connect to. + void addNode(p2p::NodeId const& _node, std::string const& _hostString) { addNode(_node, p2p::Network::resolveHost(_hostString)); } + + /// Add node to connect to. + void addNode(bi::tcp::endpoint const& _endpoint) { addNode(p2p::NodeId(), _endpoint); } + + /// Add node to connect to. + void addNode(std::string const& _hostString) { addNode(p2p::NodeId(), _hostString); } + + /// Require connection to peer. + void requirePeer(p2p::NodeId const& _node, bi::tcp::endpoint const& _endpoint) override; + + /// Require connection to peer. + void requirePeer(p2p::NodeId const& _node, std::string const& _hostString) { requirePeer(_node, p2p::Network::resolveHost(_hostString)); } /// Save peers dev::bytes saveNetwork() override; -// /// Restore peers -// void restoreNetwork(bytesConstRef _saved) override; - /// Sets the ideal number of peers. void setIdealPeerCount(size_t _n) override; bool haveNetwork() const override { return m_net.isStarted(); } - void setNetworkPreferences(p2p::NetworkPreferences const& _n) override; + void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers = false) override; p2p::NodeId id() const override { return m_net.id(); } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 19d946d01..7729c0ffe 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -112,7 +112,8 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c State execState = _state; Executive execution(execState, lastHashes, 0); - execution.setup(&rlp); + execution.initialize(&rlp); + execution.execute(); std::vector<MachineState> machineStates; std::vector<unsigned> levels; std::vector<MachineCode> codes; @@ -184,7 +185,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c // execute on a state if (!_call) { - _state.execute(lastHashes, rlp); + _state.execute(lastHashes, _t); if (_t.isCreation() && _state.code(d.contractAddress).empty()) BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); // collect watches diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 87ad95bcd..2733238b8 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -143,7 +143,7 @@ Item { } onProjectSaved: { - if (projectModel.appIsClosing) + if (projectModel.appIsClosing || projectModel.projectIsClosing) return; for (var i = 0; i < editorListModel.count; i++) { diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 6c859a9f9..d7a07eab2 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -35,6 +35,7 @@ Item { readonly property string projectFileName: ".mix" property bool appIsClosing: false + property bool projectIsClosing: false property string projectPath: "" property string projectTitle: "" property string currentDocumentId: "" @@ -122,13 +123,17 @@ Item { icon: StandardIcon.Question property var callBack; onYes: { + projectIsClosing = true; projectModel.saveAll(); + unsavedFiles = []; ProjectModelCode.doCloseProject(); if (callBack) callBack(); } onRejected: {} onNo: { + projectIsClosing = true; + unsavedFiles = []; ProjectModelCode.doCloseProject(); if (callBack) callBack(); diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index e1c1f6b0b..43736a89a 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -44,7 +44,7 @@ Item { target: projectModel onProjectSaved: { - if (projectModel.appIsClosing) + if (projectModel.appIsClosing || projectModel.projectIsClosing) return; if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) projectModel.stateListModel.debugDefaultState(); diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index c33984d36..a5f68ad66 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -19,9 +19,9 @@ Item { function setText(text, mode) { currentText = text; - if(mode !== undefined) + if (mode !== undefined) currentMode = mode; - if (initialized) { + if (initialized && editorBrowser) { editorBrowser.runJavaScript("setTextBase64(\"" + Qt.btoa(text) + "\")"); editorBrowser.runJavaScript("setMode(\"" + currentMode + "\")"); } @@ -29,7 +29,8 @@ Item { } function setFocus() { - editorBrowser.forceActiveFocus(); + if (editorBrowser) + editorBrowser.forceActiveFocus(); } function getText() { @@ -37,19 +38,19 @@ Item { } function syncClipboard() { - if (Qt.platform.os == "osx") { + if (Qt.platform.os == "osx" && editorBrowser) { var text = clipboard.text; editorBrowser.runJavaScript("setClipboardBase64(\"" + Qt.btoa(text) + "\")"); } } function highlightExecution(location) { - if (initialized) + if (initialized && editorBrowser) editorBrowser.runJavaScript("highlightExecution(" + location.start + "," + location.end + ")"); } function showWarning(content) { - if (initialized) + if (initialized && editorBrowser) editorBrowser.runJavaScript("showWarning('" + content + "')"); } @@ -58,12 +59,12 @@ Item { } function toggleBreakpoint() { - if (initialized) + if (initialized && editorBrowser) editorBrowser.runJavaScript("toggleBreakpoint()"); } function changeGeneration() { - if (initialized) + if (initialized && editorBrowser) editorBrowser.runJavaScript("changeGeneration()", function(result) {}); } @@ -84,9 +85,15 @@ Item { console.log("editor: " + sourceID + ":" + lineNumber + ":" + message); } + Component.onDestruction: + { + codeModel.onCompilationComplete.disconnect(compilationComplete); + codeModel.onCompilationError.disconnect(compilationError); + } + onLoadingChanged: { - if (!loading && !unloaded && editorBrowser) { + if (!loading && editorBrowser) { initialized = true; setText(currentText, currentMode); runJavaScript("getTextChanged()", function(result) { }); @@ -94,27 +101,31 @@ Item { syncClipboard(); if (currentMode === "solidity") { - codeModel.onCompilationComplete.connect(function(){ - if (editorBrowser) - editorBrowser.runJavaScript("compilationComplete()", function(result) { }); - }); - - codeModel.onCompilationError.connect(function(error){ - if (editorBrowser) - { - var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); - if (errorInfo.line && errorInfo.column) - editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); - else - editorBrowser.runJavaScript("compilationComplete()", function(result) { }); - } - }); + codeModel.onCompilationComplete.connect(compilationComplete); + codeModel.onCompilationError.connect(compilationError); } parent.changeGeneration(); - loadComplete(); } } + + function compilationComplete() + { + if (editorBrowser) + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + } + + function compilationError(error) + { + if (!editorBrowser || !error) + return; + var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); + if (errorInfo.line && errorInfo.column) + editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + else + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + } + Timer { id: pollTimer @@ -122,10 +133,10 @@ Item { running: false repeat: true onTriggered: { - if (unloaded) + if (!editorBrowser) return; editorBrowser.runJavaScript("getTextChanged()", function(result) { - if (result === true) { + if (result === true && editorBrowser) { editorBrowser.runJavaScript("getText()" , function(textValue) { currentText = textValue; editorTextChanged(); @@ -133,7 +144,7 @@ Item { } }); editorBrowser.runJavaScript("getBreakpointsChanged()", function(result) { - if (result === true) { + if (result === true && editorBrowser) { editorBrowser.runJavaScript("getBreakpoints()" , function(bp) { if (currentBreakpoints !== bp) { currentBreakpoints = bp; diff --git a/mix/qml/html/cm/errorannotation.js b/mix/qml/html/cm/errorannotation.js index 03e36c927..a9f5c289f 100644 --- a/mix/qml/html/cm/errorannotation.js +++ b/mix/qml/html/cm/errorannotation.js @@ -23,12 +23,15 @@ ErrorAnnotation.prototype.init = function() ErrorAnnotation.prototype.open = function() { - var node = document.createElement("div"); - node.id = "annotation" - node.innerHTML = this.content; - node.className = "CodeMirror-errorannotation-context"; - this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: false }); - this.opened = true; + if (this.errorMark.find()) + { + var node = document.createElement("div"); + node.id = "annotation" + node.innerHTML = this.content; + node.className = "CodeMirror-errorannotation-context"; + this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: false }); + this.opened = true; + } } ErrorAnnotation.prototype.close = function() diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 625722c56..177115f83 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -51,6 +51,7 @@ function closeProject(callBack) { } else { + projectIsClosing = true; doCloseProject(); if (callBack) callBack(); @@ -96,45 +97,45 @@ function saveProjectFile() function loadProject(path) { closeProject(function() { - console.log("Loading project at " + path); - var projectFile = path + projectFileName; - var json = fileIo.readFile(projectFile); - var projectData = JSON.parse(json); - if (projectData.deploymentDir) - projectModel.deploymentDir = projectData.deploymentDir - if (projectData.packageHash) - deploymentDialog.packageHash = projectData.packageHash - if (projectData.packageBase64) - deploymentDialog.packageBase64 = projectData.packageBase64 - if (projectData.applicationUrlEth) - deploymentDialog.applicationUrlEth = projectData.applicationUrlEth - if (projectData.applicationUrlHttp) - deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp - if (!projectData.title) { - var parts = path.split("/"); - projectData.title = parts[parts.length - 2]; - } - deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : []; - projectTitle = projectData.title; - projectPath = path; - if (!projectData.files) - projectData.files = []; - - for(var i = 0; i < projectData.files.length; i++) { - addFile(projectData.files[i]); - } - projectSettings.lastProjectPath = path; - projectLoading(projectData); - projectLoaded() - - //TODO: move this to codemodel - var contractSources = {}; - for (var d = 0; d < listModel.count; d++) { - var doc = listModel.get(d); - if (doc.isContract) - contractSources[doc.documentId] = fileIo.readFile(doc.path); - } - codeModel.reset(contractSources); + console.log("Loading project at " + path); + var projectFile = path + projectFileName; + var json = fileIo.readFile(projectFile); + var projectData = JSON.parse(json); + if (projectData.deploymentDir) + projectModel.deploymentDir = projectData.deploymentDir + if (projectData.packageHash) + deploymentDialog.packageHash = projectData.packageHash + if (projectData.packageBase64) + deploymentDialog.packageBase64 = projectData.packageBase64 + if (projectData.applicationUrlEth) + deploymentDialog.applicationUrlEth = projectData.applicationUrlEth + if (projectData.applicationUrlHttp) + deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp + if (!projectData.title) { + var parts = path.split("/"); + projectData.title = parts[parts.length - 2]; + } + deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : []; + projectTitle = projectData.title; + projectPath = path; + if (!projectData.files) + projectData.files = []; + + for(var i = 0; i < projectData.files.length; i++) { + addFile(projectData.files[i]); + } + projectSettings.lastProjectPath = path; + projectLoading(projectData); + projectLoaded() + + //TODO: move this to codemodel + var contractSources = {}; + for (var d = 0; d < listModel.count; d++) { + var doc = listModel.get(d); + if (doc.isContract) + contractSources[doc.documentId] = fileIo.readFile(doc.path); + } + codeModel.reset(contractSources); }); } diff --git a/mix/test/qml/TestTransactionDebug.qml b/mix/test/qml/TestTransactionDebug.qml deleted file mode 100644 index 5544d4639..000000000 --- a/mix/test/qml/TestTransactionDebug.qml +++ /dev/null @@ -1,386 +0,0 @@ -import QtQuick 2.2 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Dialogs 1.1 -import QtQuick.Layouts 1.1 -import QtQuick.Window 2.1 -import QtQuick.PrivateWidgets 1.1 -import Qt.labs.settings 1.0 -import org.ethereum.qml.QEther 1.0 -import org.ethereum.qml.CodeModel 1.0 -import org.ethereum.qml.ClientModel 1.0 -import org.ethereum.qml.FileIo 1.0 -import org.ethereum.qml.Clipboard 1.0 - -ApplicationWindow { - - id: mainApplication - signal loaded; - visible: true - width: 1200 - height: 800 - minimumWidth: 400 - minimumHeight: 300 - title: qsTr("Mix") - - CodeModel { - id: codeModel - } - - ClientModel { - id: clientModel - codeModel: codeModel - } - - ProjectModel { - id: projectModel - } - - FileIo { - id: fileIo - } - - Clipboard { - id: clipboard - } - - Connections { - target: mainApplication - onClosing: - { - mainApplication.close(); - close.accepted = false; - } - } - - Component.onCompleted: { - loaded(); - } - - function close() { - projectModel.appIsClosing = true; - if (projectModel.projectPath !== "") - projectModel.closeProject(function() { Qt.quit(); }) - else - Qt.quit(); - } - - menuBar: MenuBar { - Menu { - title: qsTr("File") - MenuItem { action: createProjectAction } - MenuItem { action: openProjectAction } - MenuSeparator {} - MenuItem { action: saveAllFilesAction } - MenuItem { action: saveCurrentDocument } - MenuSeparator {} - MenuItem { action: addExistingFileAction } - MenuItem { action: addNewJsFileAction } - MenuItem { action: addNewHtmlFileAction } - MenuItem { action: addNewCssFileAction } - MenuSeparator {} - MenuItem { action: addNewContractAction } - MenuItem { action: closeProjectAction } - MenuSeparator {} - MenuItem { action: exitAppAction } - } - Menu { - title: qsTr("Deploy") - MenuItem { action: mineAction } - MenuSeparator {} - MenuItem { action: editStatesAction } - MenuSeparator {} - MenuItem { action: deployViaRpcAction } - MenuSeparator {} - MenuItem { action: toggleRunOnLoadAction } - } - Menu { - title: qsTr("Debug") - MenuItem { action: debugRunAction } - MenuSeparator {} - MenuItem { action: toggleAssemblyDebuggingAction } - } - Menu { - title: qsTr("Windows") - MenuItem { action: openNextDocumentAction } - MenuItem { action: openPrevDocumentAction } - MenuSeparator {} - MenuItem { action: toggleProjectNavigatorAction } - MenuItem { action: showHideRightPanelAction } - MenuItem { action: toggleTransactionLogAction } - MenuItem { action: toggleWebPreviewAction } - MenuItem { action: toggleWebPreviewOrientationAction } - //MenuItem { action: toggleCallsInLog } - } - } - - MainContent { - id: mainContent; - anchors.fill: parent - } - - ModalDialog { - objectName: "dialog" - id: dialog - } - - AlertMessageDialog { - objectName: "alertMessageDialog" - id: messageDialog - } - - Settings { - id: mainWindowSettings - property alias mainWidth: mainApplication.width - property alias mainHeight: mainApplication.height - property alias mainX: mainApplication.x - property alias mainY: mainApplication.y - } - - Action { - id: exitAppAction - text: qsTr("Exit") - shortcut: "Ctrl+Q" - onTriggered: - { - mainApplication.close(); - } - } - - Action { - id: mineAction - text: qsTr("New Block") - shortcut: "Ctrl+M" - onTriggered: clientModel.mine(); - enabled: codeModel.hasContract && !clientModel.running && !clientModel.mining - } - - StateList { - id: stateList - } - - Action { - id: editStatesAction - text: qsTr("Edit States") - shortcut: "Ctrl+Alt+E" - onTriggered: stateList.show(); - } - - Connections { - target: projectModel.stateListModel - - function updateRunLabel() - { - debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\""; - } - - onDefaultStateChanged: updateRunLabel() - onStateListModelReady: updateRunLabel() - } - - Action { - id: debugRunAction - text: qsTr("Deploy") - shortcut: "F5" - onTriggered: mainContent.startQuickDebugging() - enabled: codeModel.hasContract && !clientModel.running - } - - Action { - id: toggleAssemblyDebuggingAction - text: qsTr("Show VM Code") - shortcut: "Ctrl+Alt+V" - onTriggered: mainContent.rightPane.assemblyMode = !mainContent.rightPane.assemblyMode; - checked: mainContent.rightPane.assemblyMode; - enabled: true - } - - Action { - id: toggleWebPreviewAction - text: qsTr("Show Web View") - shortcut: "F2" - checkable: true - checked: mainContent.webViewVisible - onTriggered: mainContent.toggleWebPreview(); - } - - Action { - id: toggleTransactionLogAction - text: qsTr("Show States and Transactions") - shortcut: "Alt+1" - checkable: true - checked: mainContent.rightPane.transactionLog.visible - onTriggered: mainContent.rightPane.transactionLog.visible = !mainContent.rightPane.transactionLog.visible - } - - Action { - id: toggleProjectNavigatorAction - text: qsTr("Show Project Navigator") - shortcut: "Alt+0" - checkable: true - checked: mainContent.projectViewVisible - onTriggered: mainContent.toggleProjectView(); - } - - Action { - id: toggleWebPreviewOrientationAction - text: qsTr("Horizontal Web View") - shortcut: "" - checkable: true - checked: mainContent.webViewHorizontal - onTriggered: mainContent.toggleWebPreviewOrientation(); - } - - Action { - id: toggleRunOnLoadAction - text: qsTr("Load State on Startup") - shortcut: "" - checkable: true - checked: mainContent.runOnProjectLoad - onTriggered: mainContent.runOnProjectLoad = !mainContent.runOnProjectLoad - } - - Action { - id: showHideRightPanelAction - text: qsTr("Show Right View") - shortcut: "F7" - checkable: true - checked: mainContent.rightViewVisible - onTriggered: mainContent.toggleRightView(); - } - - Action { - id: createProjectAction - text: qsTr("&New Project") - shortcut: "Ctrl+N" - enabled: true; - onTriggered: projectModel.createProject(); - } - - Action { - id: openProjectAction - text: qsTr("&Open Project") - shortcut: "Ctrl+O" - enabled: true; - onTriggered: openProjectFileDialog.open() - } - - FileDialog { - id: openProjectFileDialog - visible: false - title: qsTr("Open a Project") - selectFolder: true - onAccepted: { - var path = openProjectFileDialog.fileUrl.toString(); - path += "/"; - projectModel.loadProject(path); - } - } - - Action { - id: addNewJsFileAction - text: qsTr("New JavaScript File") - shortcut: "Ctrl+Alt+J" - enabled: !projectModel.isEmpty - onTriggered: projectModel.newJsFile(); - } - - Action { - id: addNewHtmlFileAction - text: qsTr("New HTML File") - shortcut: "Ctrl+Alt+H" - enabled: !projectModel.isEmpty - onTriggered: projectModel.newHtmlFile(); - } - - Action { - id: addNewCssFileAction - text: qsTr("New CSS File") - shortcut: "Ctrl+Alt+S" - enabled: !projectModel.isEmpty - onTriggered: projectModel.newCssFile(); - } - - Action { - id: addNewContractAction - text: qsTr("New Contract") - shortcut: "Ctrl+Alt+C" - enabled: !projectModel.isEmpty - onTriggered: projectModel.newContract(); - } - - Action { - id: addExistingFileAction - text: qsTr("Add Existing File") - shortcut: "Ctrl+Alt+A" - enabled: !projectModel.isEmpty - onTriggered: addExistingFileDialog.open() - } - - FileDialog { - id: addExistingFileDialog - visible: false - title: qsTr("Add a File") - selectFolder: false - onAccepted: { - var paths = addExistingFileDialog.fileUrls; - projectModel.addExistingFiles(paths); - } - } - - Action { - id: saveAllFilesAction - text: qsTr("Save All") - shortcut: "Ctrl+Shift+A" - enabled: !projectModel.isEmpty - onTriggered: projectModel.saveAll(); - } - - Action { - id: saveCurrentDocument - text: qsTr("Save Current Document") - shortcut: "Ctrl+S" - enabled: !projectModel.isEmpty - onTriggered: projectModel.saveCurrentDocument(); - } - - Action { - id: closeProjectAction - text: qsTr("Close Project") - shortcut: "Ctrl+W" - enabled: !projectModel.isEmpty - onTriggered: projectModel.closeProject(); - } - - Action { - id: openNextDocumentAction - text: qsTr("Next Document") - shortcut: "Ctrl+Tab" - enabled: !projectModel.isEmpty - onTriggered: projectModel.openNextDocument(); - } - - Action { - id: openPrevDocumentAction - text: qsTr("Previous Document") - shortcut: "Ctrl+Shift+Tab" - enabled: !projectModel.isEmpty - onTriggered: projectModel.openPrevDocument(); - } - - Action { - id: toggleBreakpointAction - text: qsTr("Toggle Breakpoint") - shortcut: "F9" - enabled: mainContent.codeEditor.editingContract(); - onTriggered: mainContent.toggleBreakpoint(); - } - - Action { - id: deployViaRpcAction - text: qsTr("Deploy to Network") - shortcut: "Ctrl+Shift+D" - enabled: !projectModel.isEmpty && codeModel.hasContract - onTriggered: projectModel.deployProject(); - } -} diff --git a/neth/main.cpp b/neth/main.cpp index a844da92d..e7dde3cc6 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -84,8 +84,9 @@ void help() << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl #endif << " -K,--kill-blockchain First kill the blockchain." << endl - << " -l,--listen <port> Listen on the given port for incoming connected (default: 30303)." << endl - << " -L,--local-networking Use peers whose addresses are local." << endl + << " --listen-ip <ip> Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl + << " -l,--listen <port> Listen on the given port for incoming connections (default: 30303)." << endl + << " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl << " -m,--mining <on/off> Enable mining (default: off)" << endl << " -n,--upnp <on/off> Use upnp for NAT (default: on)." << endl << " -o,--mode <full/peer> Start a full node or a peer node (Default: full)." << endl @@ -94,7 +95,6 @@ void help() << " -r,--remote <host> Connect to remote host (default: none)." << endl << " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl << " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl - << " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl << " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl << " -x,--peers <number> Attempt to connect to given number of peers (default: 5)." << endl << " -V,--version Show the version and exit." << endl @@ -321,7 +321,9 @@ enum class NodeMode int main(int argc, char** argv) { + string listenIP; unsigned short listenPort = 30303; + string publicIP; string remoteHost; unsigned short remotePort = 30303; string dbPath; @@ -332,10 +334,8 @@ int main(int argc, char** argv) #if ETH_JSONRPC int jsonrpc = 8080; #endif - string publicIP; bool bootstrap = false; bool upnp = true; - bool useLocal = false; bool forceMining = false; bool killChain = false; bool jit = false; @@ -371,7 +371,9 @@ int main(int argc, char** argv) for (int i = 1; i < argc; ++i) { string arg = argv[i]; - if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) + if (arg == "--listen-ip" && i + 1 < argc) + listenIP = argv[++i]; + else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) listenPort = (short)atoi(argv[++i]); else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) publicIP = argv[++i]; @@ -392,8 +394,6 @@ int main(int argc, char** argv) return -1; } } - else if (arg == "-L" || arg == "--local-networking") - useLocal = true; else if (arg == "-K" || arg == "--kill-blockchain") killChain = true; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) @@ -535,6 +535,8 @@ int main(int argc, char** argv) } } + + if (!clientName.empty()) clientName += "/"; @@ -542,7 +544,7 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); - NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); + auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); std::string clientImplString = "NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( @@ -570,9 +572,9 @@ int main(int argc, char** argv) web3.startNetwork(); if (bootstrap) - web3.connect(Host::pocHost()); + web3.addNode(p2p::NodeId(), Host::pocHost()); if (remoteHost.size()) - web3.connect(remoteHost, remotePort); + web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); if (c && mining) c->startMining(); @@ -687,7 +689,9 @@ int main(int argc, char** argv) { unsigned port; iss >> port; - web3.setNetworkPreferences(NetworkPreferences((short)port, publicIP, upnp)); + if (port) + netPrefs.listenPort = port; + web3.setNetworkPreferences(netPrefs); web3.startNetwork(); } else if (cmd == "connect") @@ -695,7 +699,7 @@ int main(int argc, char** argv) string addr; unsigned port; iss >> addr >> port; - web3.connect(addr, (short)port); + web3.addNode(p2p::NodeId(), addr + ":" + toString(port ? port : p2p::c_defaultIPPort)); } else if (cmd == "netstop") { diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 50ca22c54..17e6c3588 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -20,7 +20,9 @@ * block test functions. */ +#include <boost/filesystem.hpp> #include <libdevcrypto/FileSystem.h> +#include <libtestutils/TransientDirectory.h> #include <libethereum/CanonBlockChain.h> #include "TestHelper.h" @@ -35,8 +37,8 @@ bytes createBlockRLPFromFields(mObject& _tObj); void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj); BlockInfo constructBlock(mObject& _o); void updatePoW(BlockInfo& _bi); -void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi); -RLPStream createFullBlockFromHeader(const BlockInfo& _bi, const bytes& _txs = RLPEmptyList, const bytes& _uncles = RLPEmptyList); +void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi); +RLPStream createFullBlockFromHeader(BlockInfo const& _bi, bytes const& _txs = RLPEmptyList, bytes const& _uncles = RLPEmptyList); void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) { @@ -75,7 +77,8 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) o["genesisRLP"] = "0x" + toHex(rlpGenesisBlock.out()); // construct blockchain - BlockChain bc(rlpGenesisBlock.out(), string(), true); + TransientDirectory td; + BlockChain bc(rlpGenesisBlock.out(), td.path(), true); if (_fillin) { @@ -182,18 +185,17 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) Transactions txList; for (auto const& txi: txs.transactions()) { - Transaction tx(txi.second, CheckSignature::Sender); - txList.push_back(tx); + txList.push_back(txi.second); mObject txObject; - txObject["nonce"] = toString(tx.nonce()); - txObject["data"] = "0x" + toHex(tx.data()); - txObject["gasLimit"] = toString(tx.gas()); - txObject["gasPrice"] = toString(tx.gasPrice()); - txObject["r"] = "0x" + toString(tx.signature().r); - txObject["s"] = "0x" + toString(tx.signature().s); - txObject["v"] = to_string(tx.signature().v + 27); - txObject["to"] = tx.isCreation() ? "" : toString(tx.receiveAddress()); - txObject["value"] = toString(tx.value()); + txObject["nonce"] = toString(txi.second.nonce()); + txObject["data"] = "0x" + toHex(txi.second.data()); + txObject["gasLimit"] = toString(txi.second.gas()); + txObject["gasPrice"] = toString(txi.second.gasPrice()); + txObject["r"] = "0x" + toString(txi.second.signature().r); + txObject["s"] = "0x" + toString(txi.second.signature().s); + txObject["v"] = to_string(txi.second.signature().v + 27); + txObject["to"] = txi.second.isCreation() ? "" : toString(txi.second.receiveAddress()); + txObject["value"] = toString(txi.second.value()); txArray.push_back(txObject); } @@ -242,6 +244,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) if (sha3(RLP(state.blockData())[2].data()) != sha3(RLP(block2.out())[2].data())) cnote << "uncle list mismatch\n" << RLP(state.blockData())[2].data() << "\n" << RLP(block2.out())[2].data(); + try { state.sync(bc); @@ -293,7 +296,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) BOOST_CHECK(blObj.count("uncleHeaders") == 0); continue; } - catch(...) + catch (...) { cnote << "state sync or block import did throw an exception\n"; BOOST_CHECK(blObj.count("blockHeader") == 0); @@ -389,7 +392,6 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) BOOST_CHECK_MESSAGE(txsFromField[i] == txsFromRlp[i], "transactions from rlp and transaction from field do not match"); BOOST_CHECK_MESSAGE(txsFromField[i].rlp() == txsFromRlp[i].rlp(), "transactions rlp do not match"); - } // check uncle list @@ -489,12 +491,12 @@ bytes createBlockRLPFromFields(mObject& _tObj) return rlpStream.out(); } -void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj) +void overwriteBlockHeader(BlockInfo& _currentBlockHeader, mObject& _blObj) { if (_blObj["blockHeader"].get_obj().size() != 14) { - BlockInfo tmp = _current_BlockHeader; + BlockInfo tmp = _currentBlockHeader; if (_blObj["blockHeader"].get_obj().count("parentHash")) tmp.parentHash = h256(_blObj["blockHeader"].get_obj()["parentHash"].get_str()); @@ -540,16 +542,16 @@ void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj) // find new valid nonce - if (tmp != _current_BlockHeader) + if (tmp != _currentBlockHeader) { - _current_BlockHeader = tmp; + _currentBlockHeader = tmp; ProofOfWork pow; std::pair<MineInfo, Ethash::Proof> ret; - while (!ProofOfWork::verify(_current_BlockHeader)) + while (!ProofOfWork::verify(_currentBlockHeader)) { - ret = pow.mine(_current_BlockHeader, 1000, true, true); - Ethash::assignResult(ret.second, _current_BlockHeader); + ret = pow.mine(_currentBlockHeader, 1000, true, true); + Ethash::assignResult(ret.second, _currentBlockHeader); } } } @@ -558,13 +560,12 @@ void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj) // take the blockheader as is const bytes c_blockRLP = createBlockRLPFromFields(_blObj["blockHeader"].get_obj()); const RLP c_bRLP(c_blockRLP); - _current_BlockHeader.populateFromHeader(c_bRLP, IgnoreNonce); + _currentBlockHeader.populateFromHeader(c_bRLP, IgnoreNonce); } } BlockInfo constructBlock(mObject& _o) { - BlockInfo ret; try { @@ -601,7 +602,7 @@ void updatePoW(BlockInfo& _bi) _bi.hash = _bi.headerHash(WithNonce); } -void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi) +void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi) { _o["parentHash"] = toString(_bi.parentHash); _o["uncleHash"] = toString(_bi.sha3Uncles); @@ -621,7 +622,7 @@ void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi) _o["hash"] = toString(_bi.hash); } -RLPStream createFullBlockFromHeader(const BlockInfo& _bi,const bytes& _txs, const bytes& _uncles ) +RLPStream createFullBlockFromHeader(BlockInfo const& _bi, bytes const& _txs, bytes const& _uncles) { RLPStream rlpStream; _bi.streamRLP(rlpStream, WithNonce); @@ -633,8 +634,8 @@ RLPStream createFullBlockFromHeader(const BlockInfo& _bi,const bytes& _txs, cons return ret; } -} }// Namespace Close +} }// Namespace Close BOOST_AUTO_TEST_SUITE(BlockChainTests) diff --git a/test/checkRandomStateTest.cpp b/test/checkRandomStateTest.cpp index a4d390b16..49aca852f 100644 --- a/test/checkRandomStateTest.cpp +++ b/test/checkRandomStateTest.cpp @@ -83,12 +83,11 @@ bool doStateTest(mValue& _v) ImportTest importer(o, false); eth::State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { - output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/createRandomStateTest.cpp b/test/createRandomStateTest.cpp index f422d1717..5758598b9 100644 --- a/test/createRandomStateTest.cpp +++ b/test/createRandomStateTest.cpp @@ -183,12 +183,11 @@ void doStateTests(json_spirit::mValue& _v) test::ImportTest importer(o, true); eth::State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { - output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/peer.cpp b/test/peer.cpp index bfb4680d3..48431504f 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -35,8 +35,8 @@ BOOST_AUTO_TEST_CASE(host) auto oldLogVerbosity = g_logVerbosity; g_logVerbosity = 10; - NetworkPreferences host1prefs(30301, "127.0.0.1", false, true); - NetworkPreferences host2prefs(30302, "127.0.0.1", false, true); + NetworkPreferences host1prefs("127.0.0.1", 30301, false); + NetworkPreferences host2prefs("127.0.0.1", 30302, false); Host host1("Test", host1prefs); host1.start(); @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(host) auto node2 = host2.id(); host2.start(); - host1.addNode(node2, "127.0.0.1", host2prefs.listenPort, host2prefs.listenPort); + host1.addNode(node2, bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort); this_thread::sleep_for(chrono::seconds(3)); @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(save_nodes) std::list<Host*> hosts; for (auto i:{0,1,2,3,4,5}) { - Host* h = new Host("Test", NetworkPreferences(30300 + i, "127.0.0.1", false, true)); + Host* h = new Host("Test", NetworkPreferences("127.0.0.1", 30300 + i, false)); h->setIdealPeerCount(10); // starting host is required so listenport is available h->start(); @@ -73,11 +73,11 @@ BOOST_AUTO_TEST_CASE(save_nodes) Host& host = *hosts.front(); for (auto const& h: hosts) - host.addNode(h->id(), "127.0.0.1", h->listenPort(), h->listenPort()); + host.addNode(h->id(), bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort()); Host& host2 = *hosts.back(); for (auto const& h: hosts) - host2.addNode(h->id(), "127.0.0.1", h->listenPort(), h->listenPort()); + host2.addNode(h->id(), bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort()); this_thread::sleep_for(chrono::milliseconds(2000)); bytes firstHostNetwork(host.saveNetwork()); @@ -122,7 +122,7 @@ int peerTest(int argc, char** argv) Host ph("Test", NetworkPreferences(listenPort)); if (!remoteHost.empty() && !remoteAlias) - ph.addNode(remoteAlias, remoteHost, remotePort, remotePort); + ph.addNode(remoteAlias, bi::address::from_string(remoteHost), remotePort, remotePort); this_thread::sleep_for(chrono::milliseconds(200)); diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 86062a90b..2451aa381 100644 --- a/test/solidityExecutionFramework.h +++ b/test/solidityExecutionFramework.h @@ -142,7 +142,8 @@ protected: try { // this will throw since the transaction is invalid, but it should nevertheless store the transaction - executive.setup(&transactionRLP); + executive.initialize(&transactionRLP); + executive.execute(); } catch (...) {} if (_isCreation) diff --git a/test/state.cpp b/test/state.cpp index 4ab59f7a1..7c586ec7d 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -57,13 +57,12 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) ImportTest importer(o, _fillin); State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { Listener::ExecTimeGuard guard{i.first}; - output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp index 5b7b0415e..384d85344 100644 --- a/test/stateOriginal.cpp +++ b/test/stateOriginal.cpp @@ -79,13 +79,9 @@ BOOST_AUTO_TEST_CASE(Complex) cout << s; // Inject a transaction to transfer funds from miner to me. - bytes tx; - { - Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); - assert(t.sender() == myMiner.address()); - tx = t.rlp(); - } - s.execute(bc, tx); + Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); + assert(t.sender() == myMiner.address()); + s.execute(bc.lastHashes(), t); cout << s;