diff --git a/alephzero/Main.cpp b/alephzero/Main.cpp new file mode 100644 index 000000000..6408e0159 --- /dev/null +++ b/alephzero/Main.cpp @@ -0,0 +1,17 @@ +#include "Main.h" +#include "ui_Main.h" + +Main::Main(QWidget *parent) : + QDialog(parent), + ui(new Ui::Main) +{ + setWindowFlags(Qt::Window); + ui->setupUi(this); + + ui->transactions->setHtml("Hello world!"); +} + +Main::~Main() +{ + delete ui; +} diff --git a/alephzero/Main.h b/alephzero/Main.h new file mode 100644 index 000000000..9ef27b3b2 --- /dev/null +++ b/alephzero/Main.h @@ -0,0 +1,27 @@ +#ifndef MAIN_H +#define MAIN_H + +#include + +namespace Ui { +class Main; +} + +class Main : public QDialog +{ + Q_OBJECT + +public: + explicit Main(QWidget *parent = 0); + ~Main(); + +private slots: + void on_connect_clicked(); + +private: + Client c; + + Ui::Main *ui; +}; + +#endif // MAIN_H diff --git a/alephzero/Main.ui b/alephzero/Main.ui new file mode 100644 index 000000000..79231dd28 --- /dev/null +++ b/alephzero/Main.ui @@ -0,0 +1,149 @@ + + + Main + + + + 0 + 0 + 787 + 555 + + + + Main + + + true + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + + + Connect + + + + + + + wei + + + + + + + ) + + + (fee + + + + + + + + + + Send + + + + + + + Qt::Horizontal + + + + 205 + 20 + + + + + + + + Mine + + + true + + + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + QFrame::NoFrame + + + + + true + + + + + + + + + 0 + + + + + 0 wei + + + + + + + + + + 0 peers + + + + + + + + + + + + diff --git a/alephzero/alephzero.pro b/alephzero/alephzero.pro new file mode 100644 index 000000000..3e8542f56 --- /dev/null +++ b/alephzero/alephzero.pro @@ -0,0 +1,25 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2014-01-22T11:47:38 +# +#------------------------------------------------- + +QT += core gui widgets + +TARGET = alephzero +TEMPLATE = app + +QMAKE_LIBDIR += ../../cpp-ethereum-build/libethereum ../../secp256k1 ../../cryptopp562 + +LIBS += -lethereum -lsecp256k1 -lleveldb -lcryptopp -lgmp -lboost_system -lboost_filesystem + +SOURCES += main.cpp\ + Main.cpp + +HEADERS += Main.h + +FORMS += Main.ui + +INCLUDEPATH = ../../secp256k1/include ../../cryptopp562 + + diff --git a/alephzero/main.cpp b/alephzero/main.cpp new file mode 100644 index 000000000..af5cf26e1 --- /dev/null +++ b/alephzero/main.cpp @@ -0,0 +1,11 @@ +#include "Main.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + Main w; + w.show(); + + return a.exec(); +} diff --git a/eth/main.cpp b/eth/main.cpp index 37c5fa38c..48068b79c 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -28,9 +28,27 @@ using namespace eth; int main() { + short listenPort = 30303; + string remoteHost; + short remotePort = 30303; + // Our address. - h256 privkey = sha3("123"); - Address us = toAddress(privkey); // TODO: should be loaded from config file/set at command-line. + Address us; // TODO: should be loaded from config file + + for (int i = 1; i < argc; ++i) + { + string arg = argv[i]; + if (arg == "-l" && i + 1 < argc) + listenPort = atoi(argv[++i]); + else if (arg == "-r" && i + 1 < argc) + remoteHost = argv[++i]; + else if (arg == "-p" && i + 1 < argc) + remotePort = atoi(argv[++i]); + else if (arg == "-a" && i + 1 < argc) + us = h256(fromUserHex(argv[++i])); + else + remoteHost = argv[i]; + } BlockChain bc; // Maintains block database. TransactionQueue tq; // Maintains list of incoming transactions not yet on the block chain. @@ -43,7 +61,7 @@ int main() s.sync(bc); s.sync(tq); - PeerServer net(0, 30303); // TODO: Implement - should run in background and send us events when blocks found and allow us to send blocks as required. + PeerServer net(bc, 0, 30303); // TODO: Implement - should run in background and send us events when blocks found and allow us to send blocks as required. while (true) { // Process network events. diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp new file mode 100644 index 000000000..311851213 --- /dev/null +++ b/libethereum/Client.cpp @@ -0,0 +1,121 @@ +/* + 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. + + Foobar 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 Foobar. If not, see . +*/ +/** @file Client.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Common.h" +#include "Client.h" +using namespace std; +using namespace eth; + +Client::Client(std::string const& _dbPath): + m_bc(_dbPath), + m_stateDB(State::openDB(_dbPath)), + m_s(m_stateDB) +{ + Defaults::setDBPath(_dbPath); + + // Synchronise the state according to the block chain - i.e. replay all transactions in block chain, in order. + // In practise this won't need to be done since the State DB will contain the keys for the tries for most recent (and many old) blocks. + // TODO: currently it contains keys for *all* blocks. Make it remove old ones. + s.sync(bc); + s.sync(tq); + + m_work = new thread([&](){ while (m_workState != Deleting) work(); m_workState = Deleted; }); +} + +Client::~Client() +{ + if (m_workState == Active) + m_workState = Deleting; + while (m_workState != Deleted) + usleep(10000); +} + +void Client::transact(Address _dest, u256 _amount, u256 _fee, u256s _data = u256s(), Secret _secret) +{ +} + +BlockChain const& Client::blockChain() const +{ +} + +TransactionQueue const& Client::transactionQueue() const +{ +} + +unsigned Client::peerCount() const +{ +} + +void Client::startNetwork(short _listenPort = 30303, std::string const& _seedHost, short _port = 30303) +{ + if (m_net) + return; + m_net = new PeerServer(m_bc, 0, _listenPort); + if (_seedHost.size()) + m_net->connect(_seedHost, _port); +} + +void Client::stopNetwork() +{ + delete m_net; + m_net = nullptr; +} + +void Client::startMining() +{ + m_doMine = true; +} + +void Client::stopMining() +{ + m_doMine = false; +} + +std::pair Client::miningProgress() const +{ +} + +void Client::work(string const& _seedHost, short _port) +{ + // 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. + m_net->process(m_bc, m_tq); + + // Synchronise state to block chain. + // This should remove any transactions on our queue that are included within our state. + // It also guarantees that the state reflects the longest (valid!) chain on the block chain. + // This might mean reverting to an earlier state and replaying some blocks, or, (worst-case: + // if there are no checkpoints before our fork) reverting to the genesis block and replaying + // all blocks. + m_s.sync(m_bc); // Resynchronise state with block chain & trans + m_s.sync(m_tq); + + if (m_doMine) + { + // Mine for a while. + bytes b = s.mine(100); + + if (b.size()) + // Import block. + bc.attemptImport(b, stateDB); + } +} diff --git a/libethereum/Client.h b/libethereum/Client.h new file mode 100644 index 000000000..5f08f3c07 --- /dev/null +++ b/libethereum/Client.h @@ -0,0 +1,68 @@ +/* + 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. + + Foobar 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 Foobar. If not, see . +*/ +/** @file Client.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include "Common.h" +#include "BlockChain.h" +#include "TransactionQueue.h" +#include "State.h" +#include "PeerNetwork.h" + +namespace eth +{ + +class Client +{ +public: + Client(std::string const& _dbPath); + ~Client(); + + void transact(Address _dest, u256 _amount, u256 _fee, u256s _data = u256s(), Secret _secret); + + BlockChain const& blockChain() const; + TransactionQueue const& transactionQueue() const; + + unsigned peerCount() const; + + void startNetwork(short _listenPort = 30303, std::string const& _seedHost, short _port = 30303); + void stopNetwork(); + + void startMining(); + void stopMining(); + std::pair miningProgress() const; + +private: + void work(); + + BlockChain m_bc; ///< Maintains block database. + 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. + PeerServer* m_net = nullptr; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. + std::thread* m_work; ///< The work thread. + enum { Active = 0, Deleting, Deleted } m_workState = Active; + bool m_doMine = false; ///< Are we supposed to be mining? +}; + +} diff --git a/libethereum/Common.h b/libethereum/Common.h index 1b9b5b6a0..6e16e407c 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -74,6 +74,7 @@ public: FixedHash() { m_data.fill(0); } FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } + explicit FixedHash(bytes const& _b) { memcpy(m_data.data(), _b.data(), min(_b.size(), N)); } operator Arith() const { return fromBigEndian(m_data); } @@ -97,7 +98,7 @@ public: byte* data() { return m_data.data(); } byte const* data() const { return m_data.data(); } - bytes asBytes() const { return bytes(data(), data() + 32); } + bytes asBytes() const { return bytes(data(), data() + N); } private: std::array m_data; diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index 3299710e2..a8736376c 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -20,11 +20,12 @@ */ #include "Common.h" +#include "BlockChain.h" #include "PeerNetwork.h" using namespace std; using namespace eth; -PeerSession::PeerSession(bi::tcp::socket _socket, uint _rNId): m_socket(std::move(_socket)), m_reqNetworkId(_rNId) +PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId): m_server(_s), m_socket(std::move(_socket)), m_reqNetworkId(_rNId) { } @@ -60,6 +61,61 @@ bool PeerSession::interpret(RLP const& _r) case Pong: cout << "Latency: " << chrono::duration_cast(std::chrono::steady_clock::now() - m_ping).count() << " ms" << endl; break; + case GetPeers: + { + std::vector peers = m_server->peers(); + RLPStream s; + prep(s).appendList(2); + s << Peers; + s.appendList(peers.size()); + for (auto i: peers) + s.appendList(2) << i.address().to_v4().to_bytes() << i.port(); + sealAndSend(s); + break; + } + case Peers: + for (auto i: _r[1]) + { + auto ep = bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()); + m_server->m_incomingPeers.push_back(ep); + cout << "New peer: " << ep << endl; + } + break; + case Transactions: + for (auto i: _r[1]) + m_server->m_incomingTransactions.push_back(i.data().toBytes()); + break; + case Blocks: + for (auto i: _r[1]) + m_server->m_incomingBlocks.push_back(i.data().toBytes()); + break; + case GetChain: + { + h256 parent = _r[1].toHash(); + // return 256 block max. + uint count = (uint)min(_r[1].toInt(), 256); + h256 latest = m_server->m_chain->currentHash(); + uint latestNumber = 0; + uint parentNumber = 0; + if (m_server->m_chain->details(parent)) + { + latestNumber = m_server->m_chain->details(latest).number; + parentNumber = m_server->m_chain->details(parent).number; + } + count = min(latestNumber - parentNumber, count); + RLPStream s; + prep(s); + s.appendList(2); + s.append(Blocks); + s.appendList(count); + uint startNumber = m_server->m_chain->details(parent).number + count; + auto h = m_server->m_chain->currentHash(); + for (uint n = latestNumber; h != parent; n--, h = m_server->m_chain->details(h).parent) + if (m_server->m_chain->details(h).number <= startNumber) + s.appendRaw(m_server->m_chain->block(h)); + sealAndSend(s); + break; + } default: break; } @@ -98,9 +154,7 @@ void PeerSession::send(bytes& _msg) { std::shared_ptr buffer = std::make_shared(); swap(*buffer, _msg); - ba::async_write(m_socket, ba::buffer(*buffer), [=](boost::system::error_code ec, std::size_t length) - { - }); + ba::async_write(m_socket, ba::buffer(*buffer), [=](boost::system::error_code ec, std::size_t length) {}); } void PeerSession::disconnect() @@ -153,7 +207,8 @@ void PeerSession::doRead() }); } -PeerServer::PeerServer(uint _networkId, short _port): +PeerServer::PeerServer(BlockChain const& _ch, uint _networkId, short _port): + m_chain(&_ch), m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), m_socket(m_ioService), m_requiredNetworkId(_networkId) @@ -168,6 +223,21 @@ PeerServer::PeerServer(uint _networkId): { } +std::vector PeerServer::peers() +{ + std::vector ret; + bool haveLocal = false; + for (auto i: m_peers) + if (auto j = i.lock()) + { + if (!haveLocal) + ret.push_back(j->m_socket.local_endpoint()); + haveLocal = true; + ret.push_back(j->m_socket.remote_endpoint()); + } + return ret; +} + void PeerServer::doAccept() { cout << "Listening on " << m_acceptor.local_endpoint() << endl; @@ -176,7 +246,7 @@ void PeerServer::doAccept() if (!ec) { std::cout << "Accepted connection from " << m_socket.remote_endpoint() << std::endl; - auto p = std::make_shared(std::move(m_socket), m_requiredNetworkId); + auto p = std::make_shared(this, std::move(m_socket), m_requiredNetworkId); m_peers.push_back(p); p->start(); } @@ -192,7 +262,7 @@ bool PeerServer::connect(string const& _addr, uint _port) { bi::tcp::socket s(m_ioService); boost::asio::connect(s, resolver.resolve({ _addr, toString(_port) })); - auto p = make_shared(std::move(s), m_requiredNetworkId); + auto p = make_shared(this, std::move(s), m_requiredNetworkId); m_peers.push_back(p); cout << "Connected." << endl; p->start(); @@ -205,10 +275,24 @@ bool PeerServer::connect(string const& _addr, uint _port) } } -void PeerServer::process() +void PeerServer::process(BlockChain& _bc, TransactionQueue const& _tq) { m_ioService.poll(); - // TODO: Gather all transactions, blocks & peers. + for (auto i = m_peers.begin(); i != m_peers.end();) + if (auto j = i->lock()) + {} + else + i = m_peers.erase(i); + /* + while (incomingData()) + { + // import new block + bytes const& data = net.incomingData(); + if (!tq.attemptImport(data) && !_bc.attemptImport(data)) + handleMessage(data); + popIncoming(); + } + */ } void PeerServer::pingAll() @@ -217,17 +301,3 @@ void PeerServer::pingAll() if (auto j = i.lock()) j->ping(); } - -void PeerServer::sync(BlockChain& _bc, TransactionQueue const& _tq) -{ -/* - while (incomingData()) - { - // import new block - bytes const& data = net.incomingData(); - if (!tq.attemptImport(data) && !_bc.attemptImport(data)) - handleMessage(data); - popIncoming(); - } -*/ -} diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index 3057cfd90..c09930e0d 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -36,6 +36,7 @@ namespace eth class BlockChain; class TransactionQueue; +class BlockChain; enum PacketType { @@ -50,10 +51,14 @@ enum PacketType GetChain }; +class PeerServer; + class PeerSession: public std::enable_shared_from_this { + friend class PeerServer; + public: - PeerSession(bi::tcp::socket _socket, uint _rNId); + PeerSession(PeerServer* _server, bi::tcp::socket _socket, uint _rNId); ~PeerSession(); void start(); @@ -70,6 +75,7 @@ private: void sealAndSend(RLPStream& _s); void send(bytes& _msg); + PeerServer* m_server; bi::tcp::socket m_socket; std::array m_data; @@ -79,48 +85,58 @@ private: uint m_networkId; uint m_reqNetworkId; - std::vector m_incomingTransactions; - std::vector m_incomingBlocks; - std::vector m_incomingPeers; - std::chrono::steady_clock::time_point m_ping; }; class PeerServer { + friend class PeerSession; + public: /// Start server, listening for connections on the given port. - PeerServer(uint _networkId, short _port); + PeerServer(BlockChain const& _ch, uint _networkId, short _port); /// Start server, but don't listen. PeerServer(uint _networkId); - /// Conduct I/O, polling, syncing, whatever. - /// Ideally all time-consuming I/O is done in a background thread, but you get this call every 100ms or so anyway. - void process(); - /// Connect to a peer explicitly. bool connect(std::string const& _addr = "127.0.0.1", uint _port = 30303); /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. - void sync(BlockChain& _bc, TransactionQueue const&); + /// Conduct I/O, polling, syncing, whatever. + /// Ideally all time-consuming I/O is done in a background thread, but you get this call every 100ms or so anyway. + void process(BlockChain& _bc, TransactionQueue const&); + + /// Get an incoming transaction from the queue. @returns bytes() if nothing waiting. + std::vector const& incomingTransactions() { return m_incomingTransactions; } + + /// Get an incoming transaction from the queue. @returns bytes() if nothing waiting. + void incomingTransactions(std::vector& o_out) { swap(o_out, m_incomingTransactions); m_incomingTransactions.clear(); } /// Get an incoming transaction from the queue. @returns bytes() if nothing waiting. - bytes const& incomingTransaction() { return NullBytes; } + void incomingBlocks(std::vector& o_out) { swap(o_out, m_blocks); m_blocks.clear(); } - /// Remove incoming transaction from the queue. Make sure you've finished with the data from any previous incomingTransaction() calls. - void popIncomingTransaction() {} + /// Get number of peers connected. + unsigned peerCount() const { return m_peers.size(); } + /// Remove incoming transactions from the queue. + void clearIncomingTransactions() {} void pingAll(); private: void doAccept(); + std::vector peers(); + BlockChain const* m_chain = nullptr; ba::io_service m_ioService; bi::tcp::acceptor m_acceptor; bi::tcp::socket m_socket; uint m_requiredNetworkId; std::vector> m_peers; + + std::vector m_incomingTransactions; + std::vector m_incomingBlocks; + std::vector m_incomingPeers; }; diff --git a/libethereum/RLP.h b/libethereum/RLP.h index 2b0f59eec..c91fe36af 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -23,6 +23,8 @@ #pragma once +#include +#include #include #include #include @@ -158,9 +160,10 @@ public: /// Best-effort conversion operators. explicit operator std::string() const { return toString(); } explicit operator RLPs() const { return toList(); } - explicit operator uint() const { return toSlimInt(); } - explicit operator u256() const { return toFatInt(); } - explicit operator bigint() const { return toBigInt(); } + explicit operator byte() const { return toInt(); } + explicit operator uint() const { return toInt(); } + explicit operator u256() const { return toInt(); } + explicit operator bigint() const { return toInt(); } template explicit operator FixedHash<_N>() const { return toHash>(); } /// Converts to bytearray. @returns the empty byte array if not a string. @@ -173,6 +176,7 @@ public: std::string toStringStrict() const { if (!isString()) throw BadCast(); return payload().cropped(0, items()).toString(); } template std::vector toVector() const { std::vector ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; } + template std::array toArray() const { std::array ret; if (itemCount() != N) throw BadCast(); if (isList()) for (uint i = 0; i < N; ++i) ret[i] = (T)operator[](i); return ret; } /// Int conversion flags enum @@ -321,6 +325,7 @@ public: RLPStream& operator<<(std::string const& _s) { return appendString(_s); } RLPStream& operator<<(RLP const& _i) { return appendRaw(_i); } template RLPStream& operator<<(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } + template RLPStream& operator<<(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } /// Read the byte stream. bytes const& out() const { return m_out; } diff --git a/libethereum/vector_ref.h b/libethereum/vector_ref.h index b13c928ab..f9b100ed8 100644 --- a/libethereum/vector_ref.h +++ b/libethereum/vector_ref.h @@ -35,6 +35,7 @@ public: bool contentsEqual(std::vector<_T> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); } std::vector<_T> toVector() const { return std::vector<_T>(m_data, m_data + m_count); } + std::vector toBytes() const { return std::vector((unsigned char const*)m_data, m_data + m_count * sizeof(_T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count); } template operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>((_T2*)m_data, m_count * sizeof(_T) / sizeof(_T2)); } diff --git a/test/peer.cpp b/test/peer.cpp index 64998fd50..14bf790e1 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -20,6 +20,7 @@ * Peer Network test functions. */ +#include #include using namespace std; using namespace eth; @@ -44,7 +45,8 @@ int peerTest(int argc, char** argv) remoteHost = argv[i]; } - PeerServer pn(0, listenPort); + BlockChain ch("/tmp"); + PeerServer pn(ch, 0, listenPort); if (!remoteHost.empty()) pn.connect(remoteHost, remotePort);