Browse Source

Add framework for WebThree clients.

cl-refactor
Gav Wood 10 years ago
parent
commit
6d209e7885
  1. 99
      libethereum/Client.cpp
  2. 32
      libethereum/Client.h
  3. 22
      libethereum/Interface.cpp
  4. 123
      libethereum/Interface.h
  5. 2
      libp2p/Host.h
  6. 172
      libwebthree/Client.cpp
  7. 111
      libwebthree/Client.h
  8. 136
      libwebthree/WebThree.cpp
  9. 220
      libwebthree/WebThree.h
  10. 6
      libwhisper/WhisperPeer.cpp
  11. 24
      libwhisper/WhisperPeer.h

99
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<MineInfo> Client::miningHistory()
{
std::list<MineInfo> 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)

32
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<u256, u256> 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<Address> addresses() const { return addresses(m_default); }
using Interface::addresses;
std::vector<Address> 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<p2p::Host> m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required.
std::weak_ptr<EthereumHost> m_extHost; ///< Our Ethereum Host. Don't do anything if we can't lock.
std::unique_ptr<std::thread> m_work; ///< The work thread.
std::atomic<ClientWorkState> m_workState;
@ -321,8 +321,6 @@ private:
mutable std::mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters;
std::map<unsigned, ClientWatch> m_watches;
int m_default = -1;
};
class Watch;

22
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 <http://www.gnu.org/licenses/>.
*/
/** @file Interface.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Interface.h"

123
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 <http://www.gnu.org/licenses/>.
*/
/** @file Interface.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Guards.h>
#include <libdevcrypto/Common.h>
#include <libevm/FeeStructure.h>
#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<u256, u256> 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<u256, u256> 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;
};
}
}

2
libp2p/Host.h

@ -68,7 +68,7 @@ public:
unsigned protocolVersion() const;
/// Register a peer-capability; all new peer connections will have this capability.
template <class T> void registerCapability(T* _t) { _t->m_host = this; m_capabilities[T::staticName()] = std::shared_ptr<HostCapabilityFace>(_t); }
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr<T>(_t); m_capabilities[T::staticName()] = ret; return ret; }
bool haveCapability(std::string const& _name) const { return m_capabilities.count(_name); }
std::vector<std::string> caps() const { std::vector<std::string> ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; }

172
libwebthree/Client.cpp

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @file RawWebThree.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Client.h"
#include <chrono>
#include <thread>
#include <boost/filesystem.hpp>
#include <libdevcore/Log.h>
#include <libp2p/Host.h>
#include <libethereum/Defaults.h>
#include <libethereum/EthereumHost.h>
#include <libwhisper/WhisperPeer.h>
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<PeerInfo> RawWebThree::peers()
{
ReadGuard l(x_net);
return m_net ? m_net->peers() : std::vector<PeerInfo>();
}
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<EthereumHost>()->sync(m_tq, m_bq);
}
}
this_thread::sleep_for(chrono::milliseconds(1));
}

111
libwebthree/Client.h

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @file Client.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <thread>
#include <mutex>
#include <list>
#include <atomic>
#include <boost/utility.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Guards.h>
#include <libp2p/Host.h>
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<p2p::PeerInfo> 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<std::thread> m_workNet; ///< The network thread.
std::atomic<WorkState> m_workNetState;
mutable boost::shared_mutex x_net; ///< Lock for the network existance.
std::unique_ptr<p2p::Host> m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required.
};
}

136
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 <http://www.gnu.org/licenses/>.
*/
/** @file WebThree.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "WebThree.h"
#include <chrono>
#include <thread>
#include <boost/filesystem.hpp>
#include <libdevcore/Log.h>
#include <libp2p/Host.h>
#include <libethereum/Defaults.h>
#include <libethereum/EthereumHost.h>
#include <libwhisper/WhisperPeer.h>
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<std::string> 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<PeerInfo> 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));
}

220
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 <http://www.gnu.org/licenses/>.
*/
/** @file Client.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <thread>
#include <mutex>
#include <list>
#include <atomic>
#include <boost/utility.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Exceptions.h>
#include <libp2p/Host.h>
#include <libwhisper/WhisperPeer.h>
#include <libethereum/Client.h>
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<std::string> 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<p2p::PeerInfo> 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<eth::Client> m_ethereum; ///< Main interface for Ethereum ("eth") protocol.
std::unique_ptr<shh::WhisperHost> 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<std::thread> m_work; ///< The network thread.
mutable boost::shared_mutex x_work; ///< Lock for the network existance.
std::atomic<WorkState> 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<p2p::PeerInfo> 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;
};
}

6
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);

24
libwhisper/WhisperPeer.h

@ -123,7 +123,25 @@ struct ClientWatch
h256s changes;
};
class WhisperHost: public HostCapability<WhisperPeer>
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<WhisperPeer>, public Interface
{
friend class WhisperPeer;
@ -158,8 +176,8 @@ private:
std::map<unsigned, ClientWatch> m_watches;
};
struct WatchChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; };
#define cwatch dev::LogOutputStream<shh::WatchChannel, true>()
struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; };
#define cwatshh dev::LogOutputStream<shh::WatshhChannel, true>()
class Watch;

Loading…
Cancel
Save