From 6d209e78855a04a9d3dbcd7a9d4df376077c9439 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 9 Sep 2014 15:19:54 +0200 Subject: [PATCH] Add framework for WebThree clients. --- libethereum/Client.cpp | 99 +++++++++++------ libethereum/Client.h | 32 +++--- libethereum/Interface.cpp | 22 ++++ libethereum/Interface.h | 123 +++++++++++++++++++++ libp2p/Host.h | 2 +- libwebthree/Client.cpp | 172 ----------------------------- libwebthree/Client.h | 111 ------------------- libwebthree/WebThree.cpp | 136 +++++++++++++++++++++++ libwebthree/WebThree.h | 220 +++++++++++++++++++++++++++++++++++++ libwhisper/WhisperPeer.cpp | 6 +- libwhisper/WhisperPeer.h | 24 +++- 11 files changed, 609 insertions(+), 338 deletions(-) create mode 100644 libethereum/Interface.cpp create mode 100644 libethereum/Interface.h delete mode 100644 libwebthree/Client.cpp delete mode 100644 libwebthree/Client.h create mode 100644 libwebthree/WebThree.cpp create mode 100644 libwebthree/WebThree.h diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 9e9ae4773..92ff5d1cd 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -68,6 +68,22 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const work(); } +Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId): + m_vc(_dbPath), + m_bc(_dbPath, !m_vc.ok() || _forceClean), + m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)), + m_preMine(Address(), m_stateDB), + m_postMine(Address(), m_stateDB) +{ + m_extHost = _extNet->registerCapability(new EthereumHost(m_bc, _networkId)); + +// setMiningThreads(); + if (_dbPath.size()) + Defaults::setDBPath(_dbPath); + m_vc.setOk(); + work(); +} + Client::~Client() { if (m_work) @@ -118,6 +134,8 @@ void Client::clearPending() appendFromNewPending(m_postMine.bloom(i), changeds); changeds.insert(PendingChangedFilter); m_postMine = m_preMine; + + if (!m_extHost.lock()) { ReadGuard l(x_miners); for (auto& m: m_miners) @@ -214,24 +232,28 @@ void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHo m_workNetState.store(Deleted, std::memory_order_release); })); - try + if (!m_extHost.lock()) { - m_net.reset(new Host(m_clientVersion, _listenPort, _publicIP, _upnp)); - } - catch (std::exception const&) - { - // Probably already have the port open. - cwarn << "Could not initialize with specified/default port. Trying system-assigned port"; - m_net.reset(new Host(m_clientVersion, 0, _publicIP, _upnp)); + try + { + m_net.reset(new Host(m_clientVersion, _listenPort, _publicIP, _upnp)); + } + catch (std::exception const&) + { + // Probably already have the port open. + cwarn << "Could not initialize with specified/default port. Trying system-assigned port"; + m_net.reset(new Host(m_clientVersion, 0, _publicIP, _upnp)); + } + if (_mode == NodeMode::Full) + m_net->registerCapability(new EthereumHost(m_bc, _networkId)); } - if (_mode == NodeMode::Full) - m_net->registerCapability(new EthereumHost(m_bc, _networkId)); } m_net->setIdealPeerCount(_peers); } - if (_seedHost.size()) - connect(_seedHost, _port); + if (!m_extHost.lock()) + if (_seedHost.size()) + connect(_seedHost, _port); ensureWorking(); } @@ -300,6 +322,9 @@ void Client::connect(std::string const& _seedHost, unsigned short _port) void Client::setMiningThreads(unsigned _threads) { + if (m_extHost.lock()) + return; + stopMining(); auto t = _threads ? _threads : thread::hardware_concurrency(); @@ -314,6 +339,8 @@ void Client::setMiningThreads(unsigned _threads) MineProgress Client::miningProgress() const { MineProgress ret; + if (m_extHost.lock()) + return ret; ReadGuard l(x_miners); for (auto& m: m_miners) ret.combine(m.miningProgress()); @@ -323,6 +350,9 @@ MineProgress Client::miningProgress() const std::list Client::miningHistory() { std::list ret; + if (m_extHost.lock()) + return ret; + ReadGuard l(x_miners); if (m_miners.empty()) return ret; @@ -451,6 +481,10 @@ void Client::workNet() } } } + + if (auto h = m_extHost.lock()) + h->sync(m_tq, m_bq); + this_thread::sleep_for(chrono::milliseconds(1)); } @@ -461,26 +495,29 @@ void Client::work() cworkin << "WORK"; h256Set changeds; - ReadGuard l(x_miners); - for (auto& m: m_miners) - if (m.isComplete()) - { - cwork << "CHAIN <== postSTATE"; - h256s hs; - { - WriteGuard l(x_stateDB); - hs = m_bc.attemptImport(m.blockData(), m_stateDB); - } - if (hs.size()) + if (!m_extHost.lock()) + { + ReadGuard l(x_miners); + for (auto& m: m_miners) + if (m.isComplete()) { - for (auto h: hs) - appendFromNewBlock(h, changeds); - changeds.insert(ChainChangedFilter); - //changeds.insert(PendingChangedFilter); // if we mined the new block, then we've probably reset the pending transactions. + cwork << "CHAIN <== postSTATE"; + h256s hs; + { + WriteGuard l(x_stateDB); + hs = m_bc.attemptImport(m.blockData(), m_stateDB); + } + if (hs.size()) + { + for (auto h: hs) + appendFromNewBlock(h, changeds); + changeds.insert(ChainChangedFilter); + //changeds.insert(PendingChangedFilter); // if we mined the new block, then we've probably reset the pending transactions. + } + for (auto& m: m_miners) + m.noteStateChange(); } - for (auto& m: m_miners) - m.noteStateChange(); - } + } // Synchronise state to block chain. // This should remove any transactions on our queue that are included within our state. @@ -529,7 +566,7 @@ void Client::work() rsm = true; } } - if (rsm) + if (!m_extHost.lock() && rsm) { ReadGuard l(x_miners); for (auto& m: m_miners) diff --git a/libethereum/Client.h b/libethereum/Client.h index 76e903b31..13fb4a516 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -39,6 +39,7 @@ #include "PastMessage.h" #include "MessageFilter.h" #include "Miner.h" +#include "Interface.h" namespace dev { @@ -107,14 +108,17 @@ struct WorkChannel: public LogChannel { static const char* name() { return "-W-" /** * @brief Main API hub for interfacing with Ethereum. */ -class Client: public MinerHost +class Client: public MinerHost, public Interface { friend class Miner; public: - /// Constructor. + /// Original Constructor. explicit Client(std::string const& _clientVersion, Address _us = Address(), std::string const& _dbPath = std::string(), bool _forceClean = false); + /// New-style Constructor. + explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0); + /// Destructor. ~Client(); @@ -138,14 +142,11 @@ public: // [NEW API] - int getDefault() const { return m_default; } - void setDefault(int _block) { m_default = _block; } - - u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); } - u256 countAt(Address _a) const { return countAt(_a, m_default); } - u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); } - bytes codeAt(Address _a) const { return codeAt(_a, m_default); } - std::map storageAt(Address _a) const { return storageAt(_a, m_default); } + using Interface::balanceAt; + using Interface::countAt; + using Interface::stateAt; + using Interface::codeAt; + using Interface::storageAt; u256 balanceAt(Address _a, int _block) const; u256 countAt(Address _a, int _block) const; @@ -169,17 +170,14 @@ public: Transactions pending() const { return m_postMine.pending(); } /// Differences between transactions. - StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); } + using Interface::diff; StateDiff diff(unsigned _txi, h256 _block) const; StateDiff diff(unsigned _txi, int _block) const; /// Get a list of all active addresses. - std::vector
addresses() const { return addresses(m_default); } + using Interface::addresses; std::vector
addresses(int _block) const; - /// Get the fee associated for a transaction with the given data. - static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } - /// Get the remaining gas limit in this block. u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); } @@ -309,6 +307,8 @@ private: mutable boost::shared_mutex x_net; ///< Lock for the network existance. std::unique_ptr m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. + std::weak_ptr m_extHost; ///< Our Ethereum Host. Don't do anything if we can't lock. + std::unique_ptr m_work; ///< The work thread. std::atomic m_workState; @@ -321,8 +321,6 @@ private: mutable std::mutex m_filterLock; std::map m_filters; std::map m_watches; - - int m_default = -1; }; class Watch; diff --git a/libethereum/Interface.cpp b/libethereum/Interface.cpp new file mode 100644 index 000000000..a9801cc33 --- /dev/null +++ b/libethereum/Interface.cpp @@ -0,0 +1,22 @@ +/* + 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 . +*/ +/** @file Interface.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Interface.h" diff --git a/libethereum/Interface.h b/libethereum/Interface.h new file mode 100644 index 000000000..beb193d53 --- /dev/null +++ b/libethereum/Interface.h @@ -0,0 +1,123 @@ +/* + 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 . +*/ +/** @file Interface.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "MessageFilter.h" +#include "Transaction.h" +#include "AccountDiff.h" + +namespace dev +{ +namespace eth +{ + +/** + * @brief Main API hub for interfacing with Ethereum. + * @TODO Mining hooks. + */ +class Interface +{ +public: + /// Constructor. + Interface() {} + + /// Destructor. + virtual ~Interface() {} + + // [TRANSACTION API] + + /// Submits the given message-call transaction. + virtual void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; + + /// Submits a new contract-creation transaction. + /// @returns the new contract's address (assuming it all goes through). + virtual Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; + + /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. + virtual void inject(bytesConstRef _rlp) = 0; + + /// Blocks until all pending transactions have been processed. + virtual void flushTransactions() = 0; + + /// Makes the given call. Nothing is recorded into the state. + virtual bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; + + // Informational stuff + + // [NEW API] + + int getDefault() const { return m_default; } + void setDefault(int _block) { m_default = _block; } + + u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); } + u256 countAt(Address _a) const { return countAt(_a, m_default); } + u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); } + bytes codeAt(Address _a) const { return codeAt(_a, m_default); } + std::map storageAt(Address _a) const { return storageAt(_a, m_default); } + + virtual u256 balanceAt(Address _a, int _block) const = 0; + virtual u256 countAt(Address _a, int _block) const = 0; + virtual u256 stateAt(Address _a, u256 _l, int _block) const = 0; + virtual bytes codeAt(Address _a, int _block) const = 0; + virtual std::map storageAt(Address _a, int _block) const = 0; + + virtual unsigned installWatch(MessageFilter const& _filter) = 0; + virtual unsigned installWatch(h256 _filterId) = 0; + virtual void uninstallWatch(unsigned _watchId) = 0; + virtual bool peekWatch(unsigned _watchId) const = 0; + virtual bool checkWatch(unsigned _watchId) = 0; + + virtual PastMessages messages(unsigned _watchId) const = 0; + virtual PastMessages messages(MessageFilter const& _filter) const = 0; + + // [EXTRA API]: + + /// Get a map containing each of the pending transactions. + /// @TODO: Remove in favour of transactions(). + virtual Transactions pending() const = 0; + + /// Differences between transactions. + StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); } + virtual StateDiff diff(unsigned _txi, h256 _block) const = 0; + virtual StateDiff diff(unsigned _txi, int _block) const = 0; + + /// Get a list of all active addresses. + virtual Addresses addresses() const { return addresses(m_default); } + virtual Addresses addresses(int _block) const = 0; + + /// Get the fee associated for a transaction with the given data. + static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } + + /// Get the remaining gas limit in this block. + virtual u256 gasLimitRemaining() const = 0; + +protected: + int m_default = -1; +}; + +} +} diff --git a/libp2p/Host.h b/libp2p/Host.h index a4897602d..d6673a072 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -68,7 +68,7 @@ public: unsigned protocolVersion() const; /// Register a peer-capability; all new peer connections will have this capability. - template void registerCapability(T* _t) { _t->m_host = this; m_capabilities[T::staticName()] = std::shared_ptr(_t); } + template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr(_t); m_capabilities[T::staticName()] = ret; return ret; } bool haveCapability(std::string const& _name) const { return m_capabilities.count(_name); } std::vector caps() const { std::vector ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } diff --git a/libwebthree/Client.cpp b/libwebthree/Client.cpp deleted file mode 100644 index fc5d0511f..000000000 --- a/libwebthree/Client.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - 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 . -*/ -/** @file RawWebThree.cpp - * @author Gav Wood - * @date 2014 - */ - -#include "Client.h" - -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std; -using namespace dev; -using namespace dev::p2p; -using namespace dev::eth; -using namespace dev::shh; - -RawWebThree::RawWebThree(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean): - m_clientVersion(_clientVersion) -{ - if (_dbPath.size()) - Defaults::setDBPath(_dbPath); -} - -RawWebThree::~RawWebThree() -{ - stopNetwork(); -} - -void RawWebThree::startNetwork(unsigned short _listenPort, std::string const& _seedHost, unsigned short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp, u256 _networkId) -{ - static const char* c_threadName = "net"; - - { - UpgradableGuard l(x_net); - if (m_net.get()) - return; - { - UpgradeGuard ul(l); - - if (!m_workNet) - m_workNet.reset(new thread([&]() - { - setThreadName(c_threadName); - m_workNetState.store(Active, std::memory_order_release); - while (m_workNetState.load(std::memory_order_acquire) != Deleting) - workNet(); - m_workNetState.store(Deleted, std::memory_order_release); - })); - - try - { - m_net.reset(new Host(m_clientVersion, _listenPort, _publicIP, _upnp)); - } - catch (std::exception const&) - { - // Probably already have the port open. - cwarn << "Could not initialize with specified/default port. Trying system-assigned port"; - m_net.reset(new Host(m_clientVersion, 0, _publicIP, _upnp)); - } -/* if (_mode == NodeMode::Full) - m_net->registerCapability(new EthereumHost(m_bc, _networkId)); - if (_mode == NodeMode::Full) - m_net->registerCapability(new WhisperHost());*/ - } - m_net->setIdealPeerCount(_peers); - } - - if (_seedHost.size()) - connect(_seedHost, _port); -} - -void RawWebThree::stopNetwork() -{ - UpgradableGuard l(x_net); - - if (m_workNet) - { - if (m_workNetState.load(std::memory_order_acquire) == Active) - m_workNetState.store(Deleting, std::memory_order_release); - while (m_workNetState.load(std::memory_order_acquire) != Deleted) - this_thread::sleep_for(chrono::milliseconds(10)); - m_workNet->join(); - } - if (m_net) - { - UpgradeGuard ul(l); - m_net.reset(nullptr); - m_workNet.reset(nullptr); - } -} - -std::vector RawWebThree::peers() -{ - ReadGuard l(x_net); - return m_net ? m_net->peers() : std::vector(); -} - -size_t RawWebThree::peerCount() const -{ - ReadGuard l(x_net); - return m_net ? m_net->peerCount() : 0; -} - -void RawWebThree::setIdealPeerCount(size_t _n) const -{ - ReadGuard l(x_net); - if (m_net) - return m_net->setIdealPeerCount(_n); -} - -bytes RawWebThree::savePeers() -{ - ReadGuard l(x_net); - if (m_net) - return m_net->savePeers(); - return bytes(); -} - -void RawWebThree::restorePeers(bytesConstRef _saved) -{ - ReadGuard l(x_net); - if (m_net) - return m_net->restorePeers(_saved); -} - -void RawWebThree::connect(std::string const& _seedHost, unsigned short _port) -{ - ReadGuard l(x_net); - if (!m_net.get()) - return; - m_net->connect(_seedHost, _port); -} - -void RawWebThree::workNet() -{ - // 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. - { - ReadGuard l(x_net); - if (m_net) - { - m_net->process(); // must be in guard for now since it uses the blockchain. - - // returns h256Set as block hashes, once for each block that has come in/gone out. -// m_net->cap()->sync(m_tq, m_bq); - } - } - this_thread::sleep_for(chrono::milliseconds(1)); -} - diff --git a/libwebthree/Client.h b/libwebthree/Client.h deleted file mode 100644 index 4c3cc502f..000000000 --- a/libwebthree/Client.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - 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 . -*/ -/** @file Client.h - * @author Gav Wood - * @date 2014 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace dev -{ - -enum WorkState -{ - Active = 0, - Deleting, - Deleted -}; - -enum class NodeMode -{ - PeerServer, - Full -}; - -namespace eth { class Interface; } -namespace shh { class Interface; } -namespace bzz { class Interface; } - -/** - * @brief Main API hub for interfacing with Web 3 components. This doesn't do any local multiplexing, so you can only have one - * running on any given machine for the provided DB path. - */ -class RawWebThree -{ -public: - /// Constructor. - RawWebThree(std::string const& _clientVersion, std::string const& _dbPath = std::string(), bool _forceClean = false); - - /// Destructor. - ~RawWebThree(); - - // The mainline interfaces: - - eth::Interface* ethereum() const; - shh::Interface* whisper() const; - bzz::Interface* swarm() const; - - // Misc stuff: - - void setClientVersion(std::string const& _name) { m_clientVersion = _name; } - - // Network stuff: - - /// Get information on the current peer set. - std::vector peers(); - /// Same as peers().size(), but more efficient. - size_t peerCount() const; - /// Same as peers().size(), but more efficient. - void setIdealPeerCount(size_t _n) const; - - /// Start the network subsystem. - void startNetwork(unsigned short _listenPort = 30303, std::string const& _remoteHost = std::string(), unsigned short _remotePort = 30303, NodeMode _mode = NodeMode::Full, unsigned _peers = 5, std::string const& _publicIP = std::string(), bool _upnp = true, dev::u256 _networkId = 0); - /// Connect to a particular peer. - void connect(std::string const& _seedHost, unsigned short _port = 30303); - /// Stop the network subsystem. - void stopNetwork(); - /// Is the network subsystem up? - bool haveNetwork() { ReadGuard l(x_net); return !!m_net; } - /// Save peers - dev::bytes savePeers(); - /// Restore peers - void restorePeers(bytesConstRef _saved); - -private: - /// Do some work on the network. - void workNet(); - - std::string m_clientVersion; ///< Our end-application client's name/version. - - std::unique_ptr m_workNet; ///< The network thread. - std::atomic m_workNetState; - mutable boost::shared_mutex x_net; ///< Lock for the network existance. - std::unique_ptr m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. -}; - -} diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp new file mode 100644 index 000000000..93f81dd0a --- /dev/null +++ b/libwebthree/WebThree.cpp @@ -0,0 +1,136 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file WebThree.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "WebThree.h" + +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace dev::eth; +using namespace dev::shh; + +WebThreeDirect::WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean, std::set const& _interfaces, unsigned short _listenPort, std::string const& _publicIP, bool _upnp, dev::u256 _networkId, bool _localNetworking): + m_clientVersion(_clientVersion), + m_net(m_clientVersion, _listenPort, _publicIP, _upnp, _localNetworking) +{ + if (_dbPath.size()) + Defaults::setDBPath(_dbPath); + + if (_interfaces.count("eth")) + m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean, _networkId)); + +// if (_interfaces.count("shh")) +// m_whisper = new eth::Whisper(m_net.get()); + + static const char* c_threadName = "net"; + + UpgradableGuard l(x_work); + { + UpgradeGuard ul(l); + + if (!m_work) + m_work.reset(new thread([&]() + { + setThreadName(c_threadName); + m_workState.store(Active, std::memory_order_release); + while (m_workState.load(std::memory_order_acquire) != Deleting) + workNet(); + m_workState.store(Deleted, std::memory_order_release); + })); + + } +} + +WebThreeDirect::~WebThreeDirect() +{ + UpgradableGuard l(x_work); + + if (m_work) + { + if (m_workState.load(std::memory_order_acquire) == Active) + m_workState.store(Deleting, std::memory_order_release); + while (m_workState.load(std::memory_order_acquire) != Deleted) + this_thread::sleep_for(chrono::milliseconds(10)); + m_work->join(); + } + if (m_work) + { + UpgradeGuard ul(l); + m_work.reset(nullptr); + } +} + +std::vector WebThreeDirect::peers() +{ + ReadGuard l(x_work); + return m_net.peers(); +} + +size_t WebThreeDirect::peerCount() const +{ + ReadGuard l(x_work); + return m_net.peerCount(); +} + +void WebThreeDirect::setIdealPeerCount(size_t _n) +{ + ReadGuard l(x_work); + return m_net.setIdealPeerCount(_n); +} + +bytes WebThreeDirect::savePeers() +{ + ReadGuard l(x_work); + return m_net.savePeers(); +} + +void WebThreeDirect::restorePeers(bytesConstRef _saved) +{ + ReadGuard l(x_work); + return m_net.restorePeers(_saved); +} + +void WebThreeDirect::connect(std::string const& _seedHost, unsigned short _port) +{ + ReadGuard l(x_work); + m_net.connect(_seedHost, _port); +} + +void WebThreeDirect::workNet() +{ + // 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. + { + ReadGuard l(x_work); + m_net.process(); // must be in guard for now since it uses the blockchain. + } + this_thread::sleep_for(chrono::milliseconds(1)); +} + diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h new file mode 100644 index 000000000..cf02c3122 --- /dev/null +++ b/libwebthree/WebThree.h @@ -0,0 +1,220 @@ +/* + 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 . +*/ +/** @file Client.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace dev +{ + +class InterfaceNotSupported: public Exception { public: InterfaceNotSupported(std::string _f): m_f(_f) {} virtual std::string description() const { return "Interface " + m_f + " not supported."; } private: std::string m_f; }; + +enum WorkState +{ + Active = 0, + Deleting, + Deleted +}; + +namespace eth { class Interface; } +namespace shh { class Interface; } +namespace bzz { class Interface; } + +/** + * @brief Main API hub for interfacing with Web 3 components. This doesn't do any local multiplexing, so you can only have one + * running on any given machine for the provided DB path. + * + * Keeps a libp2p Host going (administering the work thread with m_workNet). + * + * Encapsulates a bunch of P2P protocols (interfaces), each using the same underlying libp2p Host. + * + * Provides a baseline for the multiplexed multi-protocol session class, WebThree. + */ +class WebThreeDirect +{ +public: + /// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception. + /// ethereum() may be safely static_cast()ed to a eth::Client*. + WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean = false, std::set const& _interfaces = {"eth", "shh"}, unsigned short _listenPort = 30303, std::string const& _publicIP = std::string(), bool _upnp = true, dev::u256 _networkId = 0, bool _localNetworking = false); + + /// Destructor. + ~WebThreeDirect(); + + // The mainline interfaces: + + eth::Client* ethereum() const { if (!m_ethereum) throw InterfaceNotSupported("eth"); return m_ethereum.get(); } + shh::WhisperHost* whisper() const { if (!m_whisper) throw InterfaceNotSupported("shh"); return m_whisper.get(); } + bzz::Interface* swarm() const { throw InterfaceNotSupported("bzz"); } + + // Misc stuff: + + void setClientVersion(std::string const& _name) { m_clientVersion = _name; } + + // Network stuff: + + /// Get information on the current peer set. + std::vector peers(); + + /// Same as peers().size(), but more efficient. + size_t peerCount() const; + + /// Connect to a particular peer. + void connect(std::string const& _seedHost, unsigned short _port = 30303); + + /// Is the network subsystem up? + bool haveNetwork() { return peerCount(); } + + /// Save peers + dev::bytes savePeers(); + + /// Restore peers + void restorePeers(bytesConstRef _saved); + + /// Sets the ideal number of peers. + void setIdealPeerCount(size_t _n); + + /// Start the network subsystem. + void startNetwork() { setIdealPeerCount(5); } + + /// Stop the network subsystem. + void stopNetwork() { setIdealPeerCount(0); } + +private: + /// Do some work on the network. + void workNet(); + + std::string m_clientVersion; ///< Our end-application client's name/version. + + std::unique_ptr m_ethereum; ///< Main interface for Ethereum ("eth") protocol. + std::unique_ptr m_whisper; ///< Main interface for Whisper ("shh") protocol. + + p2p::Host m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. + std::unique_ptr m_work; ///< The network thread. + mutable boost::shared_mutex x_work; ///< Lock for the network existance. + std::atomic m_workState; +}; + + + +// TODO, probably move into libdevrpc: + +class RPCSlave {}; +class RPCMaster {}; + +// TODO, probably move into eth: + +class EthereumSlave: public eth::Interface +{ +public: + EthereumSlave(RPCSlave* _c) {} + + // TODO: implement all of the virtuals with the RLPClient link. +}; + +class EthereumMaster +{ +public: + EthereumMaster(RPCMaster* _m) {} + + // TODO: implement the master-end of whatever the RLPClient link will send over. +}; + +// TODO, probably move into shh: + +class WhisperSlave: public shh::Interface +{ +public: + WhisperSlave(RPCSlave* _c) {} + + // TODO: implement all of the virtuals with the RLPClient link. +}; + +class WhisperMaster +{ +public: + WhisperMaster(RPCMaster* _m) {} + + // TODO: implement the master-end of whatever the RLPClient link will send over. +}; + +/** + * @brief Main API hub for interfacing with Web 3 components. + * + * This does transparent local multiplexing, so you can have as many running on the + * same machine all working from a single DB path. + */ +class WebThree +{ +public: + /// Constructor for public instance. This will be shared across the local machine. + WebThree(); + + /// Destructor. + ~WebThree(); + + // The mainline interfaces. + + eth::Interface* ethereum() const { if (!m_ethereum) throw InterfaceNotSupported("eth"); return m_ethereum; } + shh::Interface* whisper() const { if (!m_whisper) throw InterfaceNotSupported("shh"); return m_whisper; } + bzz::Interface* swarm() const { throw InterfaceNotSupported("bzz"); } + + // Peer network stuff - forward through RPCSlave, probably with P2PNetworkSlave/Master classes like Whisper & Ethereum. + + /// Get information on the current peer set. + std::vector peers(); + + /// Same as peers().size(), but more efficient. + size_t peerCount() const; + + /// Connect to a particular peer. + void connect(std::string const& _seedHost, unsigned short _port = 30303); + + /// Is the network subsystem up? + bool haveNetwork(); + + /// Save peers + dev::bytes savePeers(); + + /// Restore peers + void restorePeers(bytesConstRef _saved); + +private: + EthereumSlave* m_ethereum = nullptr; + WhisperSlave* m_whisper = nullptr; + + // TODO: + RPCSlave m_rpcSlave; +}; + +} diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index a1598010e..a1f9c99b8 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -158,7 +158,7 @@ void WhisperHost::noteChanged(h256 _messageHash, h256 _filter) for (auto& i: m_watches) if (i.second.id == _filter) { - cwatch << "!!!" << i.first << i.second.id; + cwatshh << "!!!" << i.first << i.second.id; i.second.changes.push_back(_messageHash); } } @@ -182,7 +182,7 @@ unsigned WhisperHost::installWatch(h256 _h) { auto ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; m_watches[ret] = ClientWatch(_h); - cwatch << "+++" << ret << _h; + cwatshh << "+++" << ret << _h; return ret; } @@ -200,7 +200,7 @@ unsigned WhisperHost::installWatch(shh::MessageFilter const& _f) void WhisperHost::uninstallWatch(unsigned _i) { - cwatch << "XXX" << _i; + cwatshh << "XXX" << _i; Guard l(m_filterLock); diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index 9163b544d..d428670c3 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -123,7 +123,25 @@ struct ClientWatch h256s changes; }; -class WhisperHost: public HostCapability +class Interface +{ +public: + virtual ~Interface() {} + + virtual void inject(Message const& _m, WhisperPeer* _from = nullptr) = 0; + + virtual unsigned installWatch(MessageFilter const& _filter) = 0; + virtual unsigned installWatch(h256 _filterId) = 0; + virtual void uninstallWatch(unsigned _watchId) = 0; + virtual h256s peekWatch(unsigned _watchId) const = 0; + virtual h256s checkWatch(unsigned _watchId) = 0; + + virtual Message message(h256 _m) const = 0; + + virtual void sendRaw(bytes const& _payload, bytes const& _topic, unsigned _ttl) = 0; +}; + +class WhisperHost: public HostCapability, public Interface { friend class WhisperPeer; @@ -158,8 +176,8 @@ private: std::map m_watches; }; -struct WatchChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; }; -#define cwatch dev::LogOutputStream() +struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; }; +#define cwatshh dev::LogOutputStream() class Watch;