diff --git a/BUILDING.md b/BUILDING.md deleted file mode 100644 index 56174cbf8..000000000 --- a/BUILDING.md +++ /dev/null @@ -1,25 +0,0 @@ -# Building Ethereum - -## Dependencies - -secp256k1 implementation: https://github.com/sipa/secp256k1.git -Expects secp256k1 directory to be in same path as cpp-ethereum. - -(NOTE: secp256k1 requires a development installation of the GMP library, libssl and libcrypto++.) - -libcrypto++, version 5.6.2 or greater (i.e. with SHA3 support). Because it's so recent, it expects this to be built in a directory libcrypto562 in the same path as cpp-ethereum. A recent version of Boost (I use version 1.53) and leveldb (I use version 1.9.0). - -A decent C++11 compiler (I use GNU GCC 4.8.1). CMake, version 2.8 or greater. - -On Ubuntu: - - sudo apt-get install libgmp3-dev libcrypto++-dev libssl-dev libboost-all-dev cmake libleveldb-dev libminiupnpc-dev - -## Building - - mkdir /path/to/cpp-ethereum/../cpp-ethereum-build - cd /path/to/cpp-ethereum-build - cmake -DCMAKE_BUILD_TYPE=Debug /path/to/cpp-ethereum - make - - diff --git a/CMakeLists.txt b/CMakeLists.txt index 79d3285ab..62ed5d6d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,15 +5,32 @@ set(CMAKE_AUTOMOC ON) cmake_policy(SET CMP0015 NEW) -set(ETH_VERSION 0.1.1) - +set(ETH_VERSION 0.1.2) +set(ETH_BUILD_TYPE ${CMAKE_BUILD_TYPE}) +set(ETH_BUILD_PLATFORM ${CMAKE_SYSTEM_NAME}) +if (CMAKE_COMPILER_IS_MINGW) + set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/mingw") +elseif (CMAKE_COMPILER_IS_MSYS) + set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/msys") +elseif (CMAKE_COMPILER_IS_GNUCXX) + set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/g++") +elseif (CMAKE_COMPILER_IS_MSVC) + set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/msvc") +else () + set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/unknown") +endif () # Initialize CXXFLAGS. -set(CMAKE_CXX_FLAGS "-Wall -std=c++11 -DETH_VERSION=${ETH_VERSION}") +set(CMAKE_CXX_FLAGS "-Wall -std=c++11") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") +add_definitions("-DETH_VERSION=${ETH_VERSION}") +add_definitions("-DETH_BUILD_TYPE=${ETH_BUILD_TYPE}") +add_definitions("-DETH_BUILD_PLATFORM=${ETH_BUILD_PLATFORM}") +#set(CMAKE_CXX_FLAGS, "${CMAKE_CXX_FLAGS} -DETH_VERSION=${ETH_VERSION} -DETH_BUILD_TYPE=${ETH_BUILD_TYPE} -DETH_BUILD_PLATFORM=${ETH_BUILD_PLATFORM}") + # Compiler-specific C++11 activation. if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") execute_process( diff --git a/CodingStandards.txt b/CodingStandards.txt index 727e53efb..79403af2f 100644 --- a/CodingStandards.txt +++ b/CodingStandards.txt @@ -46,7 +46,7 @@ a. File comment is always at top, and includes: - Original author, date. - Later maintainers (not contributors - they can be seen through VCS log). - Copyright. -- Licence (e.g. see COPYING). +- License (e.g. see COPYING). b. Never use #ifdef/#define/#endif file guards. Prefer #pragma once as first line below file comment. c. Prefer static const variable to value macros. d. Prefer inline constexpr functions to function macros. diff --git a/README.md b/README.md index bd45e5fb1..a3c7622f6 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,16 @@ Gav Wood, 2014. ## Building -See BUILDING.md +See https://github.com/ethereum/cpp-ethereum/wiki/Building and https://github.com/ethereum/cpp-ethereum/wiki/Compatibility-Info-and-Build-Tips . ## Yet To Do See TODO -## Licence +## License -See LICENCE +See LICENSE ## Contributing Please read CodingStandards.txt thoroughly before making alterations to the code base. Please do *NOT* use an editor that automatically reformats whitespace away from astylerc or the formating guidelines as describled in CodingStandards.txt. - - diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 3723e1e23..67dfa163e 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -91,9 +91,16 @@ + + + &Help + + + + @@ -132,44 +139,77 @@ 2 - - - 4 - + 0 - - - QFrame::NoFrame + + + Qt::Vertical + + + QFrame::NoFrame + + + + + + + + Listen on + + + + + + + 1 + + + 5 + + + + + + + Ideal Peers + + + + + + + 1024 + + + 32767 + + + 30303 + + + + + + + Client Name + + + + + + + Anonymous + + + + + - - - - - - Listen on - - - - - - - 1024 - - - 32767 - - - 30303 - - - - - @@ -207,7 +247,7 @@ 10 - 4 + 1 Qt::Vertical @@ -270,7 +310,7 @@ - 349 + 408 251 @@ -291,26 +331,6 @@ 4 - - - - - 1 - 0 - - - - - - - - Send - - - - - - @@ -318,6 +338,9 @@ + + + @@ -331,6 +354,16 @@ + + + + Data + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + @@ -344,44 +377,45 @@ - - - - - - - - - - 430000000 - - - 100 + + + + + + + Send - - - - - - - Fee + + + + + 1 + 0 + + + + (Create Contract) - - + + - Data + - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + + + @@ -459,6 +493,11 @@ &New Address + + + &About... + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 9e0d3fc0c..273ccd871 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -15,7 +15,8 @@ static void initUnits(QComboBox* _b) _b->setCurrentIndex(6); } -#define ETH_QUOTED(A) #A +#define ADD_QUOTES_HELPER(s) #s +#define ADD_QUOTES(s) ADD_QUOTES_HELPER(s) Main::Main(QWidget *parent) : QMainWindow(parent), @@ -23,10 +24,8 @@ Main::Main(QWidget *parent) : { setWindowFlags(Qt::Window); ui->setupUi(this); - initUnits(ui->valueUnits); - initUnits(ui->feeUnits); g_logPost = [=](std::string const& s, char const*) { ui->log->addItem(QString::fromStdString(s)); }; - m_client = new Client("AlethZero/v" ETH_QUOTED(ETH_VERSION)); + m_client = new Client("AlethZero"); readSettings(); refresh(); @@ -42,7 +41,7 @@ Main::Main(QWidget *parent) : { m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); }); - QNetworkRequest r(QUrl("http://www.ethereum.org/servers.txt")); + QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc2.txt")); r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); m_webCtrl.get(r); srand(time(0)); @@ -50,6 +49,7 @@ Main::Main(QWidget *parent) : on_verbosity_sliderMoved(); + initUnits(ui->valueUnits); statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->blockChain); @@ -62,6 +62,11 @@ Main::~Main() delete ui; } +void Main::on_about_triggered() +{ + QMessageBox::about(this, "About AlethZero PoC-2", "AlethZero/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Eric Lombrozo, Marko Simovic, Subtly and several others."); +} + void Main::writeSettings() { QSettings s("ethereum", "alethzero"); @@ -131,9 +136,8 @@ void Main::refresh() for (pair const& i: m_client->transactionQueue().transactions()) { Transaction t(i.second); - ui->transactionQueue->addItem(QString("%1 (%2 fee) @ %3 <- %4") + ui->transactionQueue->addItem(QString("%1 @ %2 <- %3") .arg(formatBalance(t.value).c_str()) - .arg(formatBalance(t.fee).c_str()) .arg(asHex(t.receiveAddress.asArray()).c_str()) .arg(asHex(t.sender().asArray()).c_str()) ); } @@ -147,9 +151,8 @@ void Main::refresh() for (auto const& i: RLP(bc.block(h))[1]) { Transaction t(i.data()); - ui->transactions->addItem(QString("%1 (%2) @ %3 <- %4") + ui->transactions->addItem(QString("%1 @ %2 <- %3") .arg(formatBalance(t.value).c_str()) - .arg(formatBalance(t.fee).c_str()) .arg(asHex(t.receiveAddress.asArray()).c_str()) .arg(asHex(t.sender().asArray()).c_str()) ); } @@ -168,6 +171,12 @@ void Main::refresh() m_client->unlock(); } +void Main::on_idealPeers_valueChanged() +{ + if (m_client->peerServer()) + m_client->peerServer()->setIdealPeerCount(ui->idealPeers->value()); +} + void Main::on_ourAccounts_doubleClicked() { qApp->clipboard()->setText(ui->ourAccounts->currentItem()->text().section(" @ ", 1)); @@ -183,11 +192,62 @@ void Main::on_accounts_doubleClicked() qApp->clipboard()->setText(ui->accounts->currentItem()->text().section(" @ ", 1)); } +void Main::on_destination_textChanged() +{ + updateFee(); +} + +void Main::on_data_textChanged() +{ + m_data = ui->data->toPlainText().split(QRegExp("[^0-9a-fA-Fx]+"), QString::SkipEmptyParts); + updateFee(); +} + +u256 Main::fee() const +{ + return (ui->destination->text().isEmpty() || !ui->destination->text().toInt()) ? m_client->state().fee(m_data.size()) : m_client->state().fee(); +} + +u256 Main::value() const +{ + return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; +} + +u256 Main::total() const +{ + return value() + fee(); +} + +void Main::updateFee() +{ + ui->fee->setText(QString("(fee: %1)").arg(formatBalance(fee()).c_str())); + auto totalReq = total(); + ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); + + bool ok = false; + for (auto i: m_myKeys) + if (m_client->state().balance(i.address()) >= totalReq) + { + ok = true; + break; + } + ui->send->setEnabled(ok); + QPalette p = ui->total->palette(); + p.setColor(QPalette::WindowText, QColor(ok ? 0x00 : 0x80, 0x00, 0x00)); + ui->total->setPalette(p); +} + void Main::on_net_triggered() { ui->port->setEnabled(!ui->net->isChecked()); + ui->clientName->setEnabled(!ui->net->isChecked()); + string n = "AlethZero/v" ADD_QUOTES(ETH_VERSION); + if (ui->clientName->text().size()) + n += "/" + ui->clientName->text().toStdString(); + n += "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM); + m_client->setClientVersion(n); if (ui->net->isChecked()) - m_client->startNetwork(ui->port->value(), string(), 0, NodeMode::Full, 5, std::string(), ui->upnp->isChecked()); + m_client->startNetwork(ui->port->value(), string(), 0, NodeMode::Full, ui->idealPeers->value(), std::string(), ui->upnp->isChecked()); else m_client->stopNetwork(); } @@ -227,9 +287,7 @@ void Main::on_mine_triggered() void Main::on_send_clicked() { - u256 value = ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; - u256 fee = ui->fee->value() * units()[units().size() - 1 - ui->feeUnits->currentIndex()].first; - u256 totalReq = value + fee; + u256 totalReq = value() + fee(); m_client->lock(); for (auto i: m_myKeys) if (m_client->state().balance(i.address()) >= totalReq) @@ -237,12 +295,11 @@ void Main::on_send_clicked() m_client->unlock(); Secret s = m_myKeys.front().secret(); Address r = Address(fromUserHex(ui->destination->text().toStdString())); - auto ds = ui->data->toPlainText().split(QRegExp("[^0-9a-fA-Fx]+")); u256s data; - data.reserve(ds.size()); - for (QString const& i: ds) + data.reserve(m_data.size()); + for (QString const& i: m_data) data.push_back(u256(i.toStdString())); - m_client->transact(s, r, value, fee, data); + m_client->transact(s, r, value(), data); refresh(); return; } diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 29be9733c..7f9cd16cf 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -32,15 +32,26 @@ private slots: void on_verbosity_sliderMoved(); void on_ourAccounts_doubleClicked(); void on_accounts_doubleClicked(); + void on_destination_textChanged(); + void on_data_textChanged(); + void on_idealPeers_valueChanged(); + void on_value_valueChanged() { updateFee(); } + void on_valueUnits_currentIndexChanged() { updateFee(); } void on_log_doubleClicked(); + void on_about_triggered(); void on_quit_triggered() { close(); } void refresh(); private: + void updateFee(); void readSettings(); void writeSettings(); + eth::u256 fee() const; + eth::u256 total() const; + eth::u256 value() const; + Ui::Main *ui; eth::Client* m_client; @@ -49,6 +60,7 @@ private: QTimer* m_refresh; QStringList m_servers; QVector m_myKeys; + QStringList m_data; QNetworkAccessManager m_webCtrl; }; diff --git a/alethzero/alephzero.pro b/alethzero/alephzero.pro deleted file mode 100644 index 4b58d56ae..000000000 --- a/alethzero/alephzero.pro +++ /dev/null @@ -1,42 +0,0 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2014-01-22T11:47:38 -# -#------------------------------------------------- - -QT += core gui widgets network - - -TARGET = alephzero -TEMPLATE = app - -CONFIG(debug, debug|release): DEFINES += ETH_DEBUG - -QMAKE_CXXFLAGS += -std=c++11 - -#CONFIG += local_cryptopp - -local_cryptopp { - QMAKE_LIBDIR += ../../cryptopp562 - CRYPTOPP_LIBS += -lcryptopp -} - -!local_cryptopp { - CRYPTOPP_LIBS += -lcryptoppeth -} - -INCLUDEPATH += ../../cpp-ethereum -QMAKE_LIBDIR += ../../cpp-ethereum-build/libethereum ../../cpp-ethereum-build/secp256k1 -CONFIG(debug, debug|release): LIBS += -Wl,-rpath,../../cpp-ethereum-build/libethereum -LIBS += -lethereum $${CRYPTOPP_LIBS} -lsecp256k1 -lminiupnpc -lleveldb -lgmp -lboost_filesystem -lboost_system - -SOURCES += main.cpp \ - MainWin.cpp - -HEADERS += \ - MainWin.h - -FORMS += Main.ui - - - diff --git a/eth/main.cpp b/eth/main.cpp index 04e4871b3..6c3b4bed7 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -20,6 +20,8 @@ * Ethereum client. */ +#include +#include #include #include "Defaults.h" #include "Client.h" @@ -30,6 +32,9 @@ using namespace std; using namespace eth; +#define ADD_QUOTES_HELPER(s) #s +#define ADD_QUOTES(s) ADD_QUOTES_HELPER(s) + bytes contents(std::string const& _file) { std::ifstream is(_file, std::ifstream::binary); @@ -163,7 +168,7 @@ int main(int argc, char** argv) remoteHost = argv[i]; } - Client c("Ethereum(++)/v0.1", coinbase, dbPath); + Client c("Ethereum(++)/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM), coinbase, dbPath); if (interactive) { cout << "Ethereum (++)" << endl; @@ -205,20 +210,18 @@ int main(int argc, char** argv) string sechex; string rechex; u256 amount; - u256 fee; - cin >> sechex >> rechex >> amount >> fee; + cin >> sechex >> rechex >> amount; Secret secret = h256(fromUserHex(sechex)); Address dest = h160(fromUserHex(rechex)); - c.transact(secret, dest, amount, fee); + c.transact(secret, dest, amount); } else if (cmd == "send") { string rechex; u256 amount; - u256 fee; - cin >> rechex >> amount >> fee; + cin >> rechex >> amount; Address dest = h160(fromUserHex(rechex)); - c.transact(us.secret(), dest, amount, fee); + c.transact(us.secret(), dest, amount); } else if (cmd == "exit") { @@ -236,7 +239,7 @@ int main(int argc, char** argv) c.stopMining(); else c.startMining(); - usleep(100000); + this_thread::sleep_for(chrono::milliseconds(100)); } } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index c1204b2ce..9e624b2cb 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -124,20 +124,20 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) auto newHash = eth::sha3(_block); + clog(BlockChainChat) << "Attempting import of " << newHash << "..."; + // Check block doesn't already exist first! if (details(newHash)) { -// cout << " Not new." << endl; + clog(BlockChainChat) << " Not new."; throw AlreadyHaveBlock(); } - cnote << "Attempting import of " << newHash << "..."; - // Work out its number as the parent's number + 1 auto pd = details(bi.parentHash); if (!pd) { - cwarn << " Unknown parent " << bi.parentHash; + clog(BlockChainNote) << " Unknown parent " << bi.parentHash; // We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on. throw UnknownParent(); } @@ -177,12 +177,11 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) { m_lastBlockHash = newHash; m_detailsDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); - cnote << " Imported and best."; + clog(BlockChainNote) << " Imported and best."; } else { - cnote << " Imported."; -// cwarn << "Imported block not newest (otd=" << m_details[m_lastBlockHash].totalDifficulty << ", td=" << td << ")"; + clog(BlockChainNote) << " Imported but not best (oTD:" << m_details[m_lastBlockHash].totalDifficulty << ", TD:" << td << ")"; } } diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 61339dbb7..919b8ba54 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -22,6 +22,7 @@ #pragma once #include "Common.h" +#include "AddressState.h" namespace ldb = leveldb; namespace eth @@ -54,6 +55,10 @@ class Overlay; class AlreadyHaveBlock: public std::exception {}; class UnknownParent: public std::exception {}; +struct BlockChainChat: public LogChannel { static const char constexpr* name = "-B-"; static const int verbosity = 7; }; +struct BlockChainNote: public LogChannel { static const char constexpr* name = "=B="; static const int verbosity = 1; }; +struct BlockChainWarn: public LogChannel { static const char constexpr* name = "!B!"; static const int verbosity = 0; }; + /** * @brief Implements the blockchain database. All data this gives is disk-backed. */ @@ -91,6 +96,10 @@ public: h256 genesisHash() const { return m_genesisHash; } + std::vector> interestQueue() { std::vector> ret; swap(ret, m_interestQueue); return ret; } + void pushInterest(Address _a) { m_interest[_a]++; } + void popInterest(Address _a) { if (m_interest[_a] > 1) m_interest[_a]--; else if (m_interest[_a]) m_interest.erase(_a); } + private: void checkConsistency(); @@ -98,6 +107,10 @@ private: mutable std::map m_details; mutable std::map m_cache; + /// The queue of transactions that have happened that we're interested in. + std::map m_interest; + std::vector> m_interestQueue; + ldb::DB* m_db; ldb::DB* m_detailsDB; diff --git a/libethereum/BlockInfo.cpp b/libethereum/BlockInfo.cpp index 3146ffe44..520b2c202 100644 --- a/libethereum/BlockInfo.cpp +++ b/libethereum/BlockInfo.cpp @@ -39,6 +39,13 @@ BlockInfo::BlockInfo(bytesConstRef _block) populate(_block); } +BlockInfo BlockInfo::fromHeader(bytesConstRef _block) +{ + BlockInfo ret; + ret.populateFromHeader(RLP(_block)); + return ret; +} + bytes BlockInfo::createGenesisBlock() { RLPStream block(3); @@ -79,30 +86,36 @@ void BlockInfo::populateGenesis() populate(&genesisBlock); } -void BlockInfo::populate(bytesConstRef _block) +void BlockInfo::populateFromHeader(RLP const& _header) { - RLP root(_block); int field = 0; - RLP header = root[0]; - if (!header.isList()) - throw InvalidBlockFormat(0, header.data()); try { - hash = eth::sha3(_block); - parentHash = header[field = 0].toHash(); - sha3Uncles = header[field = 1].toHash(); - coinbaseAddress = header[field = 2].toHash
(); - stateRoot = header[field = 3].toHash(); - sha3Transactions = header[field = 4].toHash(); - difficulty = header[field = 5].toInt(); - timestamp = header[field = 6].toInt(); - extraData = header[field = 7].toBytes(); - nonce = header[field = 8].toHash(); + parentHash = _header[field = 0].toHash(); + sha3Uncles = _header[field = 1].toHash(); + coinbaseAddress = _header[field = 2].toHash
(); + stateRoot = _header[field = 3].toHash(); + sha3Transactions = _header[field = 4].toHash(); + difficulty = _header[field = 5].toInt(); + timestamp = _header[field = 6].toInt(); + extraData = _header[field = 7].toBytes(); + nonce = _header[field = 8].toHash(); } catch (RLP::BadCast) { - throw InvalidBlockHeaderFormat(field, header[field].data()); + throw InvalidBlockHeaderFormat(field, _header[field].data()); } +} + +void BlockInfo::populate(bytesConstRef _block) +{ + hash = eth::sha3(_block); + + RLP root(_block); + RLP header = root[0]; + if (!header.isList()) + throw InvalidBlockFormat(0, header.data()); + populateFromHeader(header); if (!root[1].isList()) throw InvalidBlockFormat(1, root[1].data()); diff --git a/libethereum/BlockInfo.h b/libethereum/BlockInfo.h index d932f63de..26d5497a9 100644 --- a/libethereum/BlockInfo.h +++ b/libethereum/BlockInfo.h @@ -44,6 +44,8 @@ public: BlockInfo(); explicit BlockInfo(bytesConstRef _block); + static BlockInfo fromHeader(bytesConstRef _block); + explicit operator bool() const { return timestamp != Invalid256; } bool operator==(BlockInfo const& _cmp) const @@ -61,6 +63,7 @@ public: bool operator!=(BlockInfo const& _cmp) const { return !operator==(_cmp); } static BlockInfo const& genesis() { if (!s_genesis) (s_genesis = new BlockInfo)->populateGenesis(); return *s_genesis; } + void populateFromHeader(RLP const& _header); void populate(bytesConstRef _block); void verifyInternals(bytesConstRef _block) const; void verifyParent(BlockInfo const& _parent) const; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index ea9c6b3bc..a3ef271e6 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -21,6 +21,8 @@ #include "Client.h" +#include +#include #include "Common.h" #include "Defaults.h" using namespace std; @@ -30,7 +32,8 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const m_clientVersion(_clientVersion), m_bc(_dbPath), m_stateDB(State::openDB(_dbPath)), - m_s(_us, m_stateDB) + m_s(_us, m_stateDB), + m_mined(_us, m_stateDB) { Defaults::setDBPath(_dbPath); @@ -41,18 +44,18 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const m_s.sync(m_tq); m_changed = true; - static std::string thread_name = "eth"; + static const char* c_threadName = "eth"; #if defined(__APPLE__) static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - m_work = dispatch_queue_create(thread_name.c_str(), DISPATCH_QUEUE_SERIAL); + m_work = dispatch_queue_create(c_threadName, DISPATCH_QUEUE_SERIAL); }); dispatch_async(m_work, ^{ #else m_work = new thread([&](){ - setThreadName(thread_name); + setThreadName(c_threadName); #endif while (m_workState != Deleting) work(); m_workState = Deleted; @@ -64,7 +67,7 @@ Client::~Client() if (m_workState == Active) m_workState = Deleting; while (m_workState != Deleted) - usleep(10000); + this_thread::sleep_for(chrono::milliseconds(10)); } void Client::startNetwork(short _listenPort, std::string const& _seedHost, short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp) @@ -93,6 +96,7 @@ void Client::stopNetwork() void Client::startMining() { m_doMine = true; + m_miningStarted = true; } void Client::stopMining() @@ -100,14 +104,13 @@ void Client::stopMining() m_doMine = false; } -void Client::transact(Secret _secret, Address _dest, u256 _amount, u256 _fee, u256s _data) +void Client::transact(Secret _secret, Address _dest, u256 _amount, u256s _data) { m_lock.lock(); Transaction t; t.nonce = m_s.transactionsFrom(toAddress(_secret)); t.receiveAddress = _dest; t.value = _amount; - t.fee = _fee; t.data = _data; t.sign(_secret); m_tq.attemptImport(t.rlp()); @@ -118,12 +121,14 @@ void Client::transact(Secret _secret, Address _dest, u256 _amount, u256 _fee, u2 void Client::work() { m_lock.lock(); + bool changed = false; + // Process network events. // Synchronise block chain with network. // Will broadcast any of our (new) transactions and blocks, and collect & add any of their (new) transactions and blocks. if (m_net) if (m_net->process(m_bc, m_tq, m_stateDB)) - m_changed = true; + changed = true; // Synchronise state to block chain. // This should remove any transactions on our queue that are included within our state. @@ -133,16 +138,25 @@ void Client::work() // all blocks. // Resynchronise state with block chain & trans if (m_s.sync(m_bc)) - m_changed = true; - if (m_s.sync(m_tq)) - m_changed = true; + { + changed = true; + m_mined = m_s; + } m_lock.unlock(); if (m_doMine) { + if (m_miningStarted) + { + m_mined = m_s; + m_mined.sync(m_tq); + m_mined.commitToMine(m_bc); + } + + m_miningStarted = false; + // Mine for a while. - m_s.commitToMine(m_bc); - MineInfo mineInfo = m_s.mine(100); + MineInfo mineInfo = m_mined.mine(100); m_mineProgress.best = max(m_mineProgress.best, mineInfo.best); m_mineProgress.current = mineInfo.best; m_mineProgress.requirement = mineInfo.requirement; @@ -151,14 +165,17 @@ void Client::work() { // Import block. m_lock.lock(); - m_bc.attemptImport(m_s.blockData(), m_stateDB); + m_bc.attemptImport(m_mined.blockData(), m_stateDB); m_mineProgress.best = 0; m_lock.unlock(); m_changed = true; + m_miningStarted = true; // need to re-commit to mine. } } else - usleep(100000); + this_thread::sleep_for(chrono::milliseconds(100)); + + m_changed = m_changed || changed; } void Client::lock() diff --git a/libethereum/Client.h b/libethereum/Client.h index e75369be9..1ef75c9f5 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -40,6 +40,18 @@ struct MineProgress uint current; }; +class Client; + +class ClientGuard +{ +public: + inline ClientGuard(Client* _c); + inline ~ClientGuard(); + +private: + Client* m_client; +}; + class Client { public: @@ -50,16 +62,16 @@ public: ~Client(); /// Executes the given transaction. - void transact(Secret _secret, Address _dest, u256 _amount, u256 _fee, u256s _data = u256s()); + void transact(Secret _secret, Address _dest, u256 _amount, u256s _data = u256s()); /// Requires transactions involving this address be queued for inspection. void setInterest(Address _dest); /// @returns incoming minable transactions that we wanted to be notified of. Clears the queue. - Transactions pendingQueue(); + Transactions pendingQueue() { ClientGuard g(this); return m_tq.interestQueue(); } /// @returns alterations in state of a mined block that we wanted to be notified of. Clears the queue. - std::vector> minedQueue(); + std::vector> minedQueue() { ClientGuard g(this); return m_bc.interestQueue(); } // Not yet - probably best as using some sort of signals implementation. /// Calls @a _f when a valid transaction is received that involves @a _dest and once per such transaction. @@ -84,6 +96,8 @@ public: /// Get the object representing the transaction queue. TransactionQueue const& transactionQueue() const { return m_tq; } + void setClientVersion(std::string const& _name) { m_clientVersion = _name; } + // Network stuff: /// Get information on the current peer set. @@ -97,6 +111,8 @@ public: void connect(std::string const& _seedHost, short _port = 30303); /// Stop the network subsystem. void stopNetwork(); + /// Get access to the peer server object. This will be null if the network isn't online. + PeerServer* peerServer() const { return m_net; } // Mining stuff: @@ -119,6 +135,7 @@ private: TransactionQueue m_tq; ///< Maintains list of incoming transactions not yet on the block chain. Overlay m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. State m_s; ///< The present state of the client. + State m_mined; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). PeerServer* m_net = nullptr; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. #if defined(__APPLE__) @@ -131,8 +148,19 @@ private: enum { Active = 0, Deleting, Deleted } m_workState = Active; bool m_doMine = false; ///< Are we supposed to be mining? MineProgress m_mineProgress; + mutable bool m_miningStarted = false; mutable bool m_changed; }; +inline ClientGuard::ClientGuard(Client* _c): m_client(_c) +{ + m_client->lock(); +} + +inline ClientGuard::~ClientGuard() +{ + m_client->unlock(); +} + } diff --git a/libethereum/Common.cpp b/libethereum/Common.cpp index bede9f1fb..2041b25bd 100644 --- a/libethereum/Common.cpp +++ b/libethereum/Common.cpp @@ -43,8 +43,8 @@ int eth::g_logVerbosity = 8; map eth::g_logOverride; #if !defined(__APPLE__) -thread_local std::string eth::t_logThreadName = "???"; -static std::string g_mainThreadName = (eth::t_logThreadName = "main"); +thread_local char const* eth::t_logThreadName = "???"; +static char const* g_mainThreadName = (eth::t_logThreadName = "main"); #endif void eth::simpleDebugOut(std::string const& _s, char const*) @@ -190,13 +190,50 @@ Address eth::toAddress(Secret _private) KeyPair KeyPair::create() { + secp256k1_start(); static std::mt19937_64 s_eng(time(0)); std::uniform_int_distribution d(0, 255); - KeyPair ret; - for (uint i = 0; i < 32; ++i) - ret.m_secret[i] = d(s_eng); - ret.m_address = toAddress(ret.m_secret); - return ret; + + for (int i = 0; i < 100; ++i) + { + h256 sec; + for (uint i = 0; i < 32; ++i) + sec[i] = d(s_eng); + + KeyPair ret(sec); + if (ret.address()) + return ret; + } + return KeyPair(); +} + +KeyPair::KeyPair(h256 _sec): + m_secret(_sec) +{ + int ok = secp256k1_ecdsa_seckey_verify(m_secret.data()); + if (!ok) + return; + + byte pubkey[65]; + int pubkeylen = 65; + ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0); + if (!ok || pubkeylen != 65) + return; + + ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); + if (!ok) + return; + + m_secret = m_secret; + memcpy(m_public.data(), &(pubkey[1]), 64); + m_address = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); + +#if ETH_ADDRESS_DEBUG + cout << "---- ADDRESS -------------------------------" << endl; + cout << "SEC: " << m_secret << endl; + cout << "PUB: " << m_public << endl; + cout << "ADR: " << m_address << endl; +#endif } static const vector> g_units = diff --git a/libethereum/Common.h b/libethereum/Common.h index a184a0906..d066dc193 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -127,6 +127,7 @@ inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) return _out; } +using h512 = FixedHash<64>; using h256 = FixedHash<32>; using h160 = FixedHash<20>; using h256s = std::vector; @@ -135,6 +136,7 @@ using h256Set = std::set; using h160Set = std::set; using Secret = h256; +using Public = h512; using Address = h160; using Addresses = h160s; @@ -158,8 +160,8 @@ public: extern std::map g_logOverride; #if !defined(__APPLE__) -extern thread_local std::string t_logThreadName; -inline void setThreadName(std::string const& _n) { t_logThreadName = _n; } +extern thread_local char const* t_logThreadName; +inline void setThreadName(char const* _n) { t_logThreadName = _n; } #endif struct LogChannel { static const char constexpr* name = " "; static const int verbosity = 1; }; @@ -167,7 +169,7 @@ struct LeftChannel: public LogChannel { static const char constexpr* name = "<<< struct RightChannel: public LogChannel { static const char constexpr* name = ">>>"; }; struct WarnChannel: public LogChannel { static const char constexpr* name = "!!!"; static const int verbosity = 0; }; struct NoteChannel: public LogChannel { static const char constexpr* name = "***"; }; -struct DebugChannel: public LogChannel { static const char constexpr* name = "---"; static const int verbosity = 0; }; +struct DebugChannel: public LogChannel { static const char constexpr* name = "---"; static const int verbosity = 7; }; extern int g_logVerbosity; extern std::function g_logPost; @@ -434,15 +436,19 @@ class KeyPair { public: KeyPair() {} - KeyPair(Secret _k): m_secret(_k), m_address(toAddress(_k)) {} + KeyPair(Secret _k); static KeyPair create(); Secret const& secret() const { return m_secret; } + Secret const& sec() const { return m_secret; } + Public const& pub() const { return m_public; } + Address const& address() const { return m_address; } private: Secret m_secret; + Public m_public; Address m_address; }; diff --git a/libethereum/FileSystem.cpp b/libethereum/FileSystem.cpp index c6a7aec1a..0633f3755 100644 --- a/libethereum/FileSystem.cpp +++ b/libethereum/FileSystem.cpp @@ -22,6 +22,7 @@ */ #include "FileSystem.h" +#include "Common.h" #ifdef _WIN32 #include diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index ecc2e6f72..59f855798 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -30,6 +30,7 @@ #endif #include +#include #include "Exceptions.h" #include "Common.h" #include "BlockChain.h" @@ -42,6 +43,8 @@ using namespace eth; #define clogS(X) eth::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " +static const int c_protocolVersion = 3; + static const eth::uint c_maxHashes = 256; ///< Maximum number of hashes GetChain will ever send. static const eth::uint c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send. BUG: if this gets too big (e.g. 2048) stuff starts going wrong. static const eth::uint c_maxBlocksAsk = 2048; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). @@ -94,21 +97,29 @@ bool PeerSession::interpret(RLP const& _r) m_protocolVersion = _r[1].toInt(); m_networkId = _r[2].toInt(); auto clientVersion = _r[3].toString(); - m_caps = _r.itemCount() > 4 ? _r[4].toInt() : 0x07; - m_listenPort = _r.itemCount() > 5 ? _r[5].toInt() : 0; + m_caps = _r[4].toInt(); + m_listenPort = _r[5].toInt(); + m_id = _r[6].toHash(); + + clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << asHex(m_id.ref().cropped(0, 4)) << showbase << hex << m_caps << dec << m_listenPort; - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << showbase << hex << m_caps << dec << m_listenPort; + if (m_server->m_peers.count(m_id) || !m_id) + { + // Already connected. + disconnect(DuplicatePeer); + } + m_server->m_peers[m_id] = shared_from_this(); - if (m_protocolVersion != 1 || m_networkId != m_reqNetworkId) + if (m_protocolVersion != c_protocolVersion || m_networkId != m_reqNetworkId) { - disconnect(); + disconnect(IncompatibleProtocol); return false; } try { m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), (short)m_socket.remote_endpoint().port(), std::chrono::steady_clock::duration()}); } catch (...) { - disconnect(); + disconnect(BadProtocol); return false; } @@ -131,13 +142,30 @@ bool PeerSession::interpret(RLP const& _r) break; } case DisconnectPacket: - clogS(NetMessageSummary) << "Disconnect"; + { + string reason = "Unspecified"; + if (_r.itemCount() > 1 && _r[1].isInt()) + switch (_r[1].toInt()) + { + case DisconnectRequested: reason = "Disconnect was requested."; break; + case TCPError: reason = "Low-level TCP communication error."; break; + case BadProtocol: reason = "Data format error."; break; + case UselessPeer: reason = "We had no use to peer."; break; + case TooManyPeers: reason = "Peer had too many connections."; break; + case DuplicatePeer: reason = "Peer was already connected."; break; + case WrongGenesis: reason = "Disagreement over genesis block."; break; + case IncompatibleProtocol: reason = "Peer protocol versions are incompatible."; break; + case ClientQuit: reason = "Peer is exiting."; break; + } + + clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")"; if (m_socket.is_open()) clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); else clogS(NetNote) << "Remote closed."; m_socket.close(); return false; + } case PingPacket: { // clogS(NetMessageSummary) << "Ping"; @@ -152,14 +180,14 @@ bool PeerSession::interpret(RLP const& _r) case GetPeersPacket: { clogS(NetMessageSummary) << "GetPeers"; - std::vector peers = m_server->potentialPeers(); + auto peers = m_server->potentialPeers(); RLPStream s; prep(s).appendList(peers.size() + 1); s << PeersPacket; for (auto i: peers) { - clogS(NetMessageDetail) << "Sending peer " << i; - s.appendList(2) << i.address().to_v4().to_bytes() << i.port(); + clogS(NetMessageDetail) << "Sending peer " << asHex(i.first.ref().cropped(0, 4)) << i.second; + s.appendList(3) << i.second.address().to_v4().to_bytes() << i.second.port() << i.first; } sealAndSend(s); break; @@ -169,7 +197,16 @@ bool PeerSession::interpret(RLP const& _r) for (unsigned i = 1; i < _r.itemCount(); ++i) { auto ep = bi::tcp::endpoint(bi::address_v4(_r[i][0].toArray()), _r[i][1].toInt()); - clogS(NetAllDetail) << "Checking: " << ep; + Public id; + if (_r[i].itemCount() > 2) + id = _r[i][2].toHash(); + + clogS(NetAllDetail) << "Checking: " << ep << "(" << asHex(id.ref().cropped(0, 4)) << ")"; + + // check that it's not us or one we already know: + if (id && (m_server->m_key.pub() == id || m_server->m_peers.count(id) || m_server->m_incomingPeers.count(id))) + goto CONTINUE; + // check that we're not already connected to addr: if (!ep.port()) goto CONTINUE; @@ -177,16 +214,16 @@ bool PeerSession::interpret(RLP const& _r) if (ep.address() == i && ep.port() == m_server->listenPort()) goto CONTINUE; for (auto i: m_server->m_peers) - if (shared_ptr p = i.lock()) + if (shared_ptr p = i.second.lock()) { clogS(NetAllDetail) << " ...against " << p->endpoint(); if (p->m_socket.is_open() && p->endpoint() == ep) goto CONTINUE; } for (auto i: m_server->m_incomingPeers) - if (i == ep) + if (i.second == ep) goto CONTINUE; - m_server->m_incomingPeers.push_back(ep); + m_server->m_incomingPeers.insert(make_pair(id, ep)); clogS(NetMessageDetail) << "New peer: " << ep; CONTINUE:; } @@ -313,7 +350,7 @@ bool PeerSession::interpret(RLP const& _r) if (noGood == m_server->m_chain->genesisHash()) { clogS(NetWarn) << "Discordance over genesis block! Disconnect."; - disconnect(); + disconnect(WrongGenesis); } else { @@ -410,14 +447,14 @@ void PeerSession::dropped() }catch (...){} m_socket.close(); for (auto i = m_server->m_peers.begin(); i != m_server->m_peers.end(); ++i) - if (i->lock().get() == this) + if (i->second.lock().get() == this) { m_server->m_peers.erase(i); break; } } -void PeerSession::disconnect() +void PeerSession::disconnect(int _reason) { if (m_socket.is_open()) { @@ -425,7 +462,7 @@ void PeerSession::disconnect() { RLPStream s; prep(s); - s.appendList(1) << DisconnectPacket; + s.appendList(1) << DisconnectPacket << _reason; sealAndSend(s); m_disconnect = chrono::steady_clock::now(); } @@ -446,9 +483,7 @@ void PeerSession::start() { RLPStream s; prep(s); - s.appendList(m_server->m_public.port() ? 6 : 5) << HelloPacket << (uint)1 << (uint)m_server->m_requiredNetworkId << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0); - if (m_server->m_public.port()) - s << m_server->m_public.port(); + s.appendList(m_server->m_public.port() ? 6 : 5) << HelloPacket << (uint)c_protocolVersion << (uint)m_server->m_requiredNetworkId << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0) << m_server->m_public.port() << m_server->m_key.pub(); sealAndSend(s); ping(); @@ -516,31 +551,34 @@ PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, m_chain(&_ch), m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), m_socket(m_ioService), + m_key(KeyPair::create()), m_requiredNetworkId(_networkId) { populateAddresses(); determinePublic(_publicAddress, _upnp); ensureAccepting(); - clog(NetNote) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); + clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); } -PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId): +PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m): m_clientVersion(_clientVersion), + m_mode(_m), m_listenPort(-1), m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), m_socket(m_ioService), + m_key(KeyPair::create()), m_requiredNetworkId(_networkId) { // populate addresses. populateAddresses(); - clog(NetNote) << "Genesis: " << m_chain->genesisHash(); + clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); } PeerServer::~PeerServer() { for (auto const& i: m_peers) - if (auto p = i.lock()) - p->disconnect(); + if (auto p = i.second.lock()) + p->disconnect(ClientQuit); delete m_upnp; } @@ -654,17 +692,17 @@ void PeerServer::populateAddresses() #endif } -std::vector PeerServer::potentialPeers() +std::map PeerServer::potentialPeers() { - std::vector ret; + std::map ret; if (!m_public.address().is_unspecified()) - ret.push_back(m_public); + ret.insert(make_pair(m_key.pub(), m_public)); for (auto i: m_peers) - if (auto j = i.lock()) + if (auto j = i.second.lock()) { auto ep = j->endpoint(); - if (ep.port()) - ret.push_back(ep); + if (ep.port() && j->m_id) + ret.insert(make_pair(i.first, ep)); } return ret; } @@ -684,7 +722,6 @@ void PeerServer::ensureAccepting() clog(NetNote) << "Accepted connection from " << m_socket.remote_endpoint(); } catch (...){} auto p = std::make_shared(this, std::move(m_socket), m_requiredNetworkId); - m_peers.push_back(p); p->start(); } catch (std::exception const& _e) @@ -712,7 +749,6 @@ void PeerServer::connect(bi::tcp::endpoint const& _ep) else { auto p = make_shared(this, std::move(*s), m_requiredNetworkId); - m_peers.push_back(p); clog(NetNote) << "Connected to " << p->endpoint(); p->start(); } @@ -733,7 +769,7 @@ bool PeerServer::process(BlockChain& _bc) if (fullProcess) for (auto i = m_peers.begin(); i != m_peers.end();) { - auto p = i->lock(); + auto p = i->second.lock(); if (p && p->m_socket.is_open() && (p->m_disconnect == chrono::steady_clock::time_point::max() || chrono::steady_clock::now() - p->m_disconnect < chrono::seconds(1))) // kill old peers that should be disconnected. ++i; @@ -782,7 +818,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) if (fullProcess) { for (auto j: m_peers) - if (auto p = j.lock()) + if (auto p = j.second.lock()) { bytes b; uint n = 0; @@ -798,7 +834,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) RLPStream ts; PeerSession::prep(ts); ts.appendList(n + 1) << TransactionsPacket; - ts.appendRaw(b).swapOut(b); + ts.appendRaw(b, n).swapOut(b); seal(b); p->send(&b); } @@ -818,7 +854,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) ts.appendRaw(_bc.block(_bc.currentHash())).swapOut(b); seal(b); for (auto j: m_peers) - if (auto p = j.lock()) + if (auto p = j.second.lock()) { if (!p->m_knownBlocks.count(_bc.currentHash())) p->send(&b); @@ -867,7 +903,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) (PeerSession::prep(s).appendList(1) << GetPeersPacket).swapOut(b); seal(b); for (auto const& i: m_peers) - if (auto p = i.lock()) + if (auto p = i.second.lock()) if (p->isOpen()) p->send(&b); m_lastPeersRequest = chrono::steady_clock::now(); @@ -879,8 +915,8 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) break; } - connect(m_incomingPeers.back()); - m_incomingPeers.pop_back(); + connect(m_incomingPeers.begin()->second); + m_incomingPeers.erase(m_incomingPeers.begin()); } } } @@ -900,7 +936,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) shared_ptr worst; unsigned agedPeers = 0; for (auto i: m_peers) - if (auto p = i.lock()) + if (auto p = i.second.lock()) if ((m_mode != NodeMode::PeerServer || p->m_caps != 0x01) && chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. { ++agedPeers; @@ -909,7 +945,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) } if (!worst || agedPeers <= m_idealPeerCount) break; - worst->disconnect(); + worst->disconnect(TooManyPeers); } } @@ -919,10 +955,10 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) std::vector PeerServer::peers() const { const_cast(this)->pingAll(); - usleep(200000); + this_thread::sleep_for(chrono::milliseconds(200)); std::vector ret; for (auto& i: m_peers) - if (auto j = i.lock()) + if (auto j = i.second.lock()) if (j->m_socket.is_open()) ret.push_back(j->m_info); return ret; @@ -931,6 +967,6 @@ std::vector PeerServer::peers() const void PeerServer::pingAll() { for (auto& i: m_peers) - if (auto j = i.lock()) + if (auto j = i.second.lock()) j->ping(); } diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index 66d05e02a..f8a28335a 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -60,6 +61,19 @@ enum PacketType GetTransactionsPacket }; +enum DisconnectReason +{ + DisconnectRequested = 0, + TCPError, + BadProtocol, + UselessPeer, + TooManyPeers, + DuplicatePeer, + WrongGenesis, + IncompatibleProtocol, + ClientQuit +}; + class PeerServer; struct PeerInfo @@ -79,7 +93,7 @@ public: ~PeerSession(); void start(); - void disconnect(); + void disconnect(int _reason); void ping(); @@ -102,6 +116,7 @@ private: bi::tcp::socket m_socket; std::array m_data; PeerInfo m_info; + Public m_id; bytes m_incoming; uint m_protocolVersion; @@ -137,7 +152,7 @@ public: /// Start server, listening for connections on the given port. PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); /// Start server, but don't listen. - PeerServer(std::string const& _clientVersion, uint _networkId); + PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m = NodeMode::Full); ~PeerServer(); /// Connect to a peer explicitly. @@ -172,7 +187,7 @@ private: void populateAddresses(); void determinePublic(std::string const& _publicAddress, bool _upnp); void ensureAccepting(); - std::vector potentialPeers(); + std::map potentialPeers(); std::string m_clientVersion; NodeMode m_mode = NodeMode::Full; @@ -186,13 +201,14 @@ private: UPnP* m_upnp = nullptr; bi::tcp::endpoint m_public; + KeyPair m_key; uint m_requiredNetworkId; - std::vector> m_peers; + std::map> m_peers; std::vector m_incomingTransactions; std::vector m_incomingBlocks; - std::vector m_incomingPeers; + std::multimap m_incomingPeers; h256 m_latestBlockSent; std::set m_transactionsSent; diff --git a/libethereum/RLP.h b/libethereum/RLP.h index 7c3b07afe..86163f0af 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -278,8 +278,7 @@ public: RLPStream& append(bytes const& _s) { return append(bytesConstRef(&_s)); } RLPStream& append(std::string const& _s) { return append(bytesConstRef(_s)); } RLPStream& append(char const* _s) { return append(std::string(_s)); } - RLPStream& append(h160 _s, bool _compact = false) { return append(_s.ref(), _compact); } - RLPStream& append(h256 _s, bool _compact = false) { return append(_s.ref(), _compact); } + template RLPStream& append(FixedHash _s, bool _compact = false) { return append(_s.ref(), _compact); } /// Appends an arbitrary RLP fragment - this *must* be a single item. RLPStream& append(RLP const& _rlp, uint _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index fe1efe576..c4ad37bf4 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -46,20 +46,15 @@ using namespace std; using namespace eth; -u256 const State::c_stepFee = 10000; -u256 const State::c_dataFee = 20000; -u256 const State::c_memoryFee = 30000; -u256 const State::c_extroFee = 40000; -u256 const State::c_cryptoFee = 50000; -u256 const State::c_newContractFee = 60000; -u256 const State::c_txFee = 0; -u256 const State::c_blockReward = 1000000000000; - -#if NDEBUG -u256 const eth::c_genesisDifficulty = (u256)1 << 22; -#else +u256 const c_stepFee = 1; +u256 const c_dataFee = 20; +u256 const c_memoryFee = 5; +u256 const c_extroFee = 40; +u256 const c_cryptoFee = 20; +u256 const c_newContractFee = 100; +u256 const c_txFee = 100; + u256 const eth::c_genesisDifficulty = (u256)1 << 22; -#endif std::map const& eth::genesisState() { @@ -90,8 +85,14 @@ Overlay State::openDB(std::string _path, bool _killExisting) return Overlay(db); } -State::State(Address _coinbaseAddress, Overlay const& _db): m_db(_db), m_state(&m_db), m_ourAddress(_coinbaseAddress) +State::State(Address _coinbaseAddress, Overlay const& _db): + m_db(_db), + m_state(&m_db), + m_ourAddress(_coinbaseAddress) { + m_blockReward = u256(15000000000) * 100000000; + m_fees.setMultiplier(u256(100000) * 1000000000); + secp256k1_start(); // Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly. @@ -106,6 +107,46 @@ State::State(Address _coinbaseAddress, Overlay const& _db): m_db(_db), m_state(& assert(m_state.root() == m_previousBlock.stateRoot); } +State::State(State const& _s): + m_db(_s.m_db), + m_state(&m_db, _s.m_state.root()), + m_transactions(_s.m_transactions), + m_cache(_s.m_cache), + m_previousBlock(_s.m_previousBlock), + m_currentBlock(_s.m_currentBlock), + m_currentNumber(_s.m_currentNumber), + m_ourAddress(_s.m_ourAddress), + m_fees(_s.m_fees), + m_blockReward(_s.m_blockReward) +{ +} + +State& State::operator=(State const& _s) +{ + m_db = _s.m_db; + m_state.open(&m_db, _s.m_state.root()); + m_transactions = _s.m_transactions; + m_cache = _s.m_cache; + m_previousBlock = _s.m_previousBlock; + m_currentBlock = _s.m_currentBlock; + m_currentNumber = _s.m_currentNumber; + m_ourAddress = _s.m_ourAddress; + m_fees = _s.m_fees; + m_blockReward = _s.m_blockReward; + return *this; +} + +void FeeStructure::setMultiplier(u256 _x) +{ + m_stepFee = c_stepFee * _x; + m_dataFee = c_dataFee * _x; + m_memoryFee = c_memoryFee * _x; + m_extroFee = c_extroFee * _x; + m_cryptoFee = c_cryptoFee * _x; + m_newContractFee = c_newContractFee * _x; + m_txFee = c_txFee * _x; +} + void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const { auto it = m_cache.find(_a); @@ -303,7 +344,7 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ Addresses rewarded; for (auto const& i: RLP(_block)[2]) { - BlockInfo uncle(i.data()); + BlockInfo uncle = BlockInfo::fromHeader(i.data()); if (m_previousBlock.parentHash != uncle.parentHash) throw InvalidUncle(); if (_grandParent) @@ -350,6 +391,8 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ // (i.e. all the transactions we executed). void State::commitToMine(BlockChain const& _bc) { + cnote << "Commiting to mine on" << m_previousBlock.hash; + if (m_currentBlock.sha3Transactions != h256() || m_currentBlock.sha3Uncles != h256()) return; @@ -417,7 +460,7 @@ MineInfo State::mine(uint _msTimeout) ret.appendRaw(m_currentUncles); ret.swapOut(m_currentBytes); m_currentBlock.hash = sha3(m_currentBytes); - cout << "*** SUCCESS: Mined " << m_currentBlock.hash << " (parent: " << m_currentBlock.parentHash << ")" << endl; + cnote << "Mined " << m_currentBlock.hash << "(parent: " << m_currentBlock.parentHash << ")"; } else m_currentBytes.clear(); @@ -525,11 +568,11 @@ void State::execute(bytesConstRef _rlp) void State::applyRewards(Addresses const& _uncleAddresses) { - u256 r = c_blockReward; + u256 r = m_blockReward; for (auto const& i: _uncleAddresses) { - addBalance(i, c_blockReward * 4 / 3); - r += c_blockReward / 8; + addBalance(i, m_blockReward * 4 / 3); + r += m_blockReward / 8; } addBalance(m_currentBlock.coinbaseAddress, r); } @@ -544,31 +587,32 @@ void State::executeBare(Transaction const& _t, Address _sender) throw InvalidNonce(nonceReq, _t.nonce); // Not considered invalid - just pointless. - if (balance(_sender) < _t.value + _t.fee) + u256 fee = _t.receiveAddress ? m_fees.m_txFee : (_t.data.size() * m_fees.m_memoryFee + m_fees.m_newContractFee); + if (balance(_sender) < _t.value + fee) throw NotEnoughCash(); - // TODO: check fee is sufficient? - // Increment associated nonce for sender. noteSending(_sender); if (_t.receiveAddress) { - subBalance(_sender, _t.value + _t.fee); + subBalance(_sender, _t.value + fee); addBalance(_t.receiveAddress, _t.value); - addBalance(m_currentBlock.coinbaseAddress, _t.fee); if (isContractAddress(_t.receiveAddress)) { MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction. - execute(_t.receiveAddress, _sender, _t.value, _t.fee, _t.data, &feeAdder.fee); + execute(_t.receiveAddress, _sender, _t.value, _t.data, &feeAdder.fee); } } else { - // Try to make a new contract - if (_t.fee < _t.data.size() * c_memoryFee + c_newContractFee) - throw FeeTooSmall(); +#if ETH_SENDER_PAYS_SETUP + if (balance(_sender) < _t.value + fee) +#else + if (_t.value < fee) +#endif + throw NotEnoughCash(); Address newAddress = low160(_t.sha3()); @@ -584,9 +628,13 @@ void State::executeBare(Transaction const& _t, Address _sender) else mem.at(i) = _t.data[i]; - subBalance(_sender, _t.value + _t.fee); +#if ETH_SENDER_PAYS_SETUP + subBalance(_sender, _t.value + fee); addBalance(newAddress, _t.value); - addBalance(m_currentBlock.coinbaseAddress, _t.fee); +#else + subBalance(_sender, _t.value); + addBalance(newAddress, _t.value - fee); +#endif } } @@ -598,7 +646,7 @@ inline Address asAddress(u256 _item) return left160(h256(_item)); } -void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee) +void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* _totalFee) { std::vector stack; @@ -637,7 +685,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ { stepCount++; - bigint minerFee = stepCount > 16 ? c_stepFee : 0; + bigint minerFee = stepCount > 16 ? m_fees.m_stepFee : 0; bigint voidFee = 0; auto rawInst = mem(curPC); @@ -650,21 +698,21 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ case Instruction::STORE: require(2); if (!mem(stack.back()) && stack[stack.size() - 2]) - voidFee += c_memoryFee; + voidFee += m_fees.m_memoryFee; if (mem(stack.back()) && !stack[stack.size() - 2]) - voidFee -= c_memoryFee; + voidFee -= m_fees.m_memoryFee; // continue on to... case Instruction::LOAD: - minerFee += c_dataFee; + minerFee += m_fees.m_dataFee; break; case Instruction::EXTRO: case Instruction::BALANCE: - minerFee += c_extroFee; + minerFee += m_fees.m_extroFee; break; case Instruction::MKTX: - minerFee += c_txFee; + minerFee += m_fees.m_txFee; break; case Instruction::SHA256: @@ -674,7 +722,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ case Instruction::ECSIGN: case Instruction::ECRECOVER: case Instruction::ECVALID: - minerFee += c_cryptoFee; + minerFee += m_fees.m_cryptoFee; break; default: break; @@ -779,9 +827,6 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ case Instruction::TXVALUE: stack.push_back(_txValue); break; - case Instruction::TXFEE: - stack.push_back(_txFee); - break; case Instruction::TXDATAN: stack.push_back(_txData.size()); break; @@ -1069,8 +1114,6 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ stack.pop_back(); t.value = stack.back(); stack.pop_back(); - t.fee = stack.back(); - stack.pop_back(); auto itemCount = stack.back(); stack.pop_back(); @@ -1092,7 +1135,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ { require(1); Address dest = asAddress(stack.back()); - u256 minusVoidFee = myMemory.size() * c_memoryFee; + u256 minusVoidFee = myMemory.size() * m_fees.m_memoryFee; addBalance(dest, balance(_myAddress) + minusVoidFee); m_cache[_myAddress].kill(); // ...follow through to... diff --git a/libethereum/State.h b/libethereum/State.h index 7949dc0ea..1338e3adc 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -42,6 +42,21 @@ class BlockChain; extern const u256 c_genesisDifficulty; std::map const& genesisState(); +#define ETH_SENDER_PAYS_SETUP 1 + +struct FeeStructure +{ + /// The fee structure. Values yet to be agreed on... + void setMultiplier(u256 _x); ///< The current block multiplier. + u256 m_stepFee; + u256 m_dataFee; + u256 m_memoryFee; + u256 m_extroFee; + u256 m_cryptoFee; + u256 m_newContractFee; + u256 m_txFee; +}; + /** * @brief Model of the current state of the ledger. * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). @@ -53,6 +68,12 @@ public: /// Construct state object. State(Address _coinbaseAddress, Overlay const& _db); + /// Copy state object. + State(State const& _s); + + /// Copy state object. + State& operator=(State const& _s); + /// Set the coinbase address for any transactions we do. /// This causes a complete reset of current block. void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } @@ -146,6 +167,12 @@ public: /// This might throw. u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit); + /// Get the fee associated for a contract created with the given data. + u256 fee(uint _dataCount) const { return m_fees.m_memoryFee * _dataCount + m_fees.m_newContractFee; } + + /// Get the fee associated for a normal transaction. + u256 fee() const { return m_fees.m_txFee; } + private: /// Fee-adder on destruction RAII class. struct MinerFeeAdder @@ -177,7 +204,7 @@ private: void executeBare(Transaction const& _t, Address _sender); /// Execute a contract transaction. - void execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee); + void execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* o_totalFee); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); @@ -199,16 +226,9 @@ private: Address m_ourAddress; ///< Our address (i.e. the address to which fees go). Dagger m_dagger; - - /// The fee structure. Values yet to be agreed on... - static const u256 c_stepFee; - static const u256 c_dataFee; - static const u256 c_memoryFee; - static const u256 c_extroFee; - static const u256 c_cryptoFee; - static const u256 c_newContractFee; - static const u256 c_txFee; - static const u256 c_blockReward; + + FeeStructure m_fees; + u256 m_blockReward; static std::string c_defaultPath; diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 53326cc6d..4044eccf2 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -32,11 +32,10 @@ Transaction::Transaction(bytesConstRef _rlpData) nonce = rlp[0].toInt(); receiveAddress = rlp[1].toHash
(); value = rlp[2].toInt(); - fee = rlp[3].toInt(); - data.reserve(rlp[4].itemCountStrict()); - for (auto const& i: rlp[4]) + data.reserve(rlp[3].itemCountStrict()); + for (auto const& i: rlp[3]) data.push_back(i.toInt()); - vrs = Signature{ rlp[5].toInt(), rlp[6].toInt(), rlp[7].toInt() }; + vrs = Signature{ rlp[4].toInt(), rlp[5].toInt(), rlp[6].toInt() }; } Address Transaction::sender() const @@ -91,8 +90,8 @@ void Transaction::sign(Secret _priv) void Transaction::fillStream(RLPStream& _s, bool _sig) const { - _s.appendList(_sig ? 8 : 5); - _s << nonce << receiveAddress << value << fee << data; + _s.appendList(_sig ? 7 : 4); + _s << nonce << receiveAddress << value << data; if (_sig) _s << vrs.v << vrs.r << vrs.s; } diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 2e077e875..bacd125f8 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -34,7 +34,7 @@ struct Signature u256 s; }; -// [ nonce, receiving_address, value, fee, [ data item 0, data item 1 ... data item n ], v, r, s ] +// [ nonce, receiving_address, value, [ data item 0, data item 1 ... data item n ], v, r, s ] struct Transaction { Transaction() {} @@ -44,7 +44,6 @@ struct Transaction u256 nonce; Address receiveAddress; u256 value; - u256 fee; u256s data; Signature vrs; diff --git a/libethereum/UPnP.cpp b/libethereum/UPnP.cpp index 5b92d5373..c70177421 100644 --- a/libethereum/UPnP.cpp +++ b/libethereum/UPnP.cpp @@ -117,7 +117,7 @@ int UPnP::addRedirect(char const* addr, int port) // Failed - now try (random external, port internal) and cycle up to 10 times. for (uint i = 0; i < 10; ++i) { - port = random() % 65535 - 1024 + 1024; + port = rand() % 65535 - 1024 + 1024; sprintf(port_str, "%d", port); if (!UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, NULL, port_str, addr, "ethereum", "TCP", NULL, NULL)) return port; diff --git a/test/crypto.cpp b/test/crypto.cpp index 71fc41292..215d772ee 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -41,7 +41,6 @@ int cryptoTest() Transaction t; t.nonce = 0; - t.fee = 0; t.value = 1; // 1 wei. t.receiveAddress = toAddress(sha3("123")); diff --git a/test/peer.cpp b/test/peer.cpp index 5a440a88f..c2d1de17c 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -20,6 +20,8 @@ * Peer Network test functions. */ +#include +#include #include #include using namespace std; @@ -53,7 +55,7 @@ int peerTest(int argc, char** argv) for (int i = 0; ; ++i) { - usleep(100000); + this_thread::sleep_for(chrono::milliseconds(100)); pn.process(ch); if (!(i % 10)) pn.pingAll(); diff --git a/test/state.cpp b/test/state.cpp index 25663b851..64bd37acc 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -62,7 +62,6 @@ int stateTest() { Transaction t; t.nonce = s.transactionsFrom(myMiner.address()); - t.fee = 0; t.value = 1000; // 1e3 wei. t.receiveAddress = me.address(); t.sign(myMiner.secret());