Browse Source

Whisper is functional from the JS API.

cl-refactor
Gav Wood 10 years ago
parent
commit
6181b6d2c7
  1. 3
      alethzero/MainWin.cpp
  2. 139
      libqethereum/QEthereum.cpp
  3. 22
      libqethereum/QEthereum.h
  4. 6
      libwhisper/Common.cpp
  5. 6
      libwhisper/Common.h
  6. 4
      libwhisper/Interface.h
  7. 7
      libwhisper/Message.cpp
  8. 14
      libwhisper/Message.h
  9. 2
      libwhisper/WhisperHost.cpp
  10. 3
      third/MainWin.cpp

3
alethzero/MainWin.cpp

@ -151,6 +151,7 @@ Main::Main(QWidget *parent) :
connect(ui->webView, &QWebView::loadFinished, [=]()
{
m_ethereum->poll();
m_whisper->poll();
});
connect(ui->webView, &QWebView::titleChanged, [=]()
@ -1001,6 +1002,8 @@ void Main::timerEvent(QTimerEvent*)
if (m_ethereum)
m_ethereum->poll();
if (m_whisper)
m_whisper->poll();
for (auto const& i: m_handlers)
if (ethereum()->checkWatch(i.first))

139
libqethereum/QEthereum.cpp

@ -5,6 +5,8 @@
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libethereum/EthereumHost.h>
#include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h>
#include "QEthereum.h"
using namespace std;
using namespace dev;
@ -21,7 +23,7 @@ dev::bytes toBytes(QString const& _s)
else
{
// Binary
cwarn << "THIS FUNCTIONALITY IS DEPRECATED. DO NOT ASSUME ASCII/BINARY-STRINGS WILL BE ACCEPTED. USE eth.fromAscii().";
cwarn << "'" << _s.toStdString() << "': Unrecognised format for number/hash. USE eth.fromAscii() if you mean to convert from ASCII.";
return asBytes(_s);
}
}
@ -558,37 +560,148 @@ void QWhisper::faceDieing()
}
void QWhisper::send(QString /*dev::Address*/ _dest, QString /*ev::KeyPair*/ _from, QString /*dev::h256 const&*/ _topic, QString /*dev::bytes const&*/ _payload)
static shh::Message toMessage(QString _json)
{
(void)_dest;
(void)_from;
(void)_topic;
(void)_payload;
shh::Message ret;
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if (f.contains("from"))
ret.setFrom(toPublic(f["from"].toString()));
if (f.contains("to"))
ret.setTo(toPublic(f["to"].toString()));
if (f.contains("payload"))
ret.setPayload(toBytes(f["payload"].toString()));
return ret;
}
unsigned QWhisper::newWatch(QString _json)
static shh::Envelope toSealed(QString _json, shh::Message const& _m, Secret _from)
{
(void)_json;
return 0;
unsigned ttl = 50;
unsigned workToProve = 50;
shh::BuildTopic bt;
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if (f.contains("ttl"))
ttl = f["ttl"].toInt();
if (f.contains("workToProve"))
workToProve = f["workToProve"].toInt();
if (f.contains("topic"))
{
if (f["topic"].isString())
bt.shift(asBytes(padded(f["topic"].toString(), 32)));
else if (f["topic"].isArray())
for (auto i: f["topic"].toArray())
bt.shift(asBytes(padded(i.toString(), 32)));
}
return _m.seal(_from, bt, workToProve, ttl);
}
QString QWhisper::watchMessages(unsigned _w)
void QWhisper::doPost(QString _json)
{
(void)_w;
return "";
shh::Message m = toMessage(_json);
Secret from;
if (m.from() && m_ids.count(m.from()))
{
cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here.";
// TODO: insert validification hook here.
from = m_ids[m.from()];
}
face()->inject(toSealed(_json, m, from));
}
static pair<shh::TopicMask, Public> toWatch(QString _json)
{
shh::BuildTopicMask bt(shh::BuildTopicMask::Empty);
Public to;
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
if (f.contains("to"))
to = toPublic(f["to"].toString());
if (f.contains("topic"))
{
if (f["topic"].isString())
bt.shift(asBytes(padded(f["topic"].toString(), 32)));
else if (f["topic"].isArray())
for (auto i: f["topic"].toArray())
if (i.isString())
bt.shift(asBytes(padded(i.toString(), 32)));
else
bt.shift();
}
return make_pair(bt.toTopicMask(), to);
}
// _json contains
// topic: the topic as an array of components, some may be null.
// to: specifies the id to which the message is encrypted. null if broadcast.
unsigned QWhisper::newWatch(QString _json)
{
auto w = toWatch(_json);
auto ret = face()->installWatch(w.first);
m_watches.insert(make_pair(ret, w.second));
return ret;
}
void QWhisper::killWatch(unsigned _w)
{
(void)_w;
face()->uninstallWatch(_w);
m_watches.erase(_w);
}
void QWhisper::clearWatches()
{
for (auto i: m_watches)
face()->uninstallWatch(i.first);
m_watches.clear();
}
QString QWhisper::newIdentity()
{
KeyPair kp = KeyPair::create();
m_ids[kp.pub()] = kp.sec();
return toQJS(kp.pub());
}
static QString toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m)
{
QJsonObject v;
v["hash"] = toQJS(_h);
v["expiry"] = (int)_e.expiry();
v["sent"] = (int)_e.sent();
v["ttl"] = (int)_e.ttl();
v["workProved"] = (int)_e.workProved();
v["topic"] = toQJS(_e.topic());
v["payload"] = toQJS(_m.payload());
v["from"] = toQJS(_m.from());
v["to"] = toQJS(_m.to());
return QString::fromUtf8(QJsonDocument(v).toJson());
}
void QWhisper::poll()
{
for (auto const& w: m_watches)
if (!w.second || m_ids.count(w.second))
for (h256 const& h: face()->checkWatch(w.first))
{
auto e = face()->envelope(h);
shh::Message m;
if (w.second)
{
cwarn << "Silently decrypting message from identity" << w.second.abridged() << ": User validation hook goes here.";
m = e.open(m_ids[w.second]);
}
else
m = e.open();
emit watchChanged(w.first, toJson(h, e, m));
}
}
// extra bits needed to link on VS

22
libqethereum/QEthereum.h

@ -59,8 +59,9 @@ template <unsigned N> dev::FixedHash<N> toFixed(QString const& _s)
template <unsigned N> inline boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> toInt(QString const& _s);
inline dev::Address toAddress(QString const& _s) { return toFixed<20>(_s); }
inline dev::Secret toSecret(QString const& _s) { return toFixed<32>(_s); }
inline dev::Address toAddress(QString const& _s) { return toFixed<sizeof(dev::Address)>(_s); }
inline dev::Public toPublic(QString const& _s) { return toFixed<sizeof(dev::Public)>(_s); }
inline dev::Secret toSecret(QString const& _s) { return toFixed<sizeof(dev::Secret)>(_s); }
inline dev::u256 toU256(QString const& _s) { return toInt<32>(_s); }
template <unsigned S> QString toQJS(dev::FixedHash<S> const& _h) { return QString::fromStdString("0x" + toHex(_h.ref())); }
@ -225,12 +226,12 @@ public:
Q_INVOKABLE QWhisper* self() { return this; }
/// Basic message send.
Q_INVOKABLE void send(QString /*dev::Address*/ _dest, QString /*ev::KeyPair*/ _from, QString /*dev::h256 const&*/ _topic, QString /*dev::bytes const&*/ _payload);
Q_INVOKABLE void doPost(QString _json);
// Watches interface
Q_INVOKABLE QString newIdentity();
// Watches interface
Q_INVOKABLE unsigned newWatch(QString _json);
Q_INVOKABLE QString watchMessages(unsigned _w);
Q_INVOKABLE void killWatch(unsigned _w);
void clearWatches();
@ -240,11 +241,13 @@ public slots:
void poll();
signals:
void watchChanged(unsigned _w);
void watchChanged(unsigned _w, QString _envelopeJson);
private:
std::weak_ptr<dev::shh::Interface> m_face;
std::vector<unsigned> m_watches;
std::map<unsigned, dev::Public> m_watches;
std::map<dev::Public, dev::Secret> m_ids;
};
// TODO: add p2p object
@ -269,8 +272,9 @@ private:
if (_shh) \
{ \
_frame->addToJavaScriptWindowObject("_web3_dot_shh", _shh, QWebFrame::ScriptOwnership); \
_frame->evaluateJavaScript("_web3_dot_shh.makeWatch = function(a) { var ww = _web3_dot_shh.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_shh.killWatch(w); }; ret.changed = function(f) { _web3_dot_shh.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(_web3_dot_shh.watchMessages(this.w)) }; return ret; }"); \
_frame->evaluateJavaScript("_web3_dot_shh.watch = function(a) { return _web3_dot_shh.makeWatch(JSON.stringify(a)) }"); \
_frame->evaluateJavaScript("_web3_dot_shh.makeWatch = function(json) { var ww = _web3_dot_shh.newWatch(json); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_shh.killWatch(w); }; ret.arrived = function(f) { _web3_dot_shh.watchChanged.connect(function(nw, envelope) { if (nw == ww) f(JSON.parse(envelope)) }); }; return ret; }"); \
_frame->evaluateJavaScript("_web3_dot_shh.watch = function(filter) { return _web3_dot_shh.makeWatch(JSON.stringify(filter)) }"); \
_frame->evaluateJavaScript("_web3_dot_shh.post = function(message) { return _web3_dot_shh.doPost(JSON.stringify(message)) }"); \
_frame->evaluateJavaScript("web3.shh = _web3_dot_shh"); \
} \
}

6
libwhisper/Common.cpp

@ -43,6 +43,7 @@ h256 TopicFilter::sha3() const
TopicMask BuildTopicMask::toTopicMask() const
{
TopicMask ret;
if (m_parts.size())
for (auto i = 0; i < 32; ++i)
{
ret.first[i] = m_parts[i * m_parts.size() / 32][i];
@ -50,3 +51,8 @@ TopicMask BuildTopicMask::toTopicMask() const
}
return ret;
}
/*
web3.shh.watch({}, function(m) { env.note("New message:\n"+JSON.stringify(m)); })
k = web3.shh.newIdentity()
web3.shh.post({from: k, topic: web3.fromAscii("test"), payload: web3.fromAscii("Hello world!")})
*/

6
libwhisper/Common.h

@ -64,6 +64,7 @@ using Topic = h256;
class BuildTopic
{
public:
BuildTopic() {}
template <class T> BuildTopic(T const& _t) { shift(_t); }
template <class T> BuildTopic& shift(T const& _r) { return shiftBytes(RLPStream().append(_r).out()); }
@ -75,8 +76,6 @@ public:
Topic toTopic() const { Topic ret; for (auto i = 0; i < 32; ++i) ret[i] = m_parts[i * m_parts.size() / 32][i]; return ret; }
protected:
BuildTopic() {}
BuildTopic& shiftBytes(bytes const& _b);
h256s m_parts;
@ -105,7 +104,10 @@ private:
class BuildTopicMask: BuildTopic
{
public:
enum EmptyType { Empty };
BuildTopicMask() { shift(); }
BuildTopicMask(EmptyType) {}
template <class T> BuildTopicMask(T const& _t) { shift(_t); }
template <class T> BuildTopicMask& shift(T const& _r) { BuildTopic::shift(_r); return *this; }

4
libwhisper/Interface.h

@ -79,9 +79,9 @@ public:
virtual Envelope envelope(h256 _m) const = 0;
void post(bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_to, _topic, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _to, _topic, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); }
};
struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; };

7
libwhisper/Message.cpp

@ -60,7 +60,7 @@ void Message::populate(bytes const& _data)
m_payload = bytesConstRef(&_data).cropped(1).toBytes();
}
Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigned _workToProve)
Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigned _workToProve) const
{
Envelope ret(time(0) + _ttl, _ttl, _topic);
@ -91,11 +91,6 @@ Message Envelope::open(Secret const& _s) const
return Message(*this, _s);
}
Message Envelope::open() const
{
return Message(*this);
}
unsigned Envelope::workProved() const
{
h256 d[2];

14
libwhisper/Message.h

@ -65,8 +65,7 @@ public:
Topic const& topic() const { return m_topic; }
bytes const& data() const { return m_data; }
Message open(Secret const& _s) const;
Message open() const;
Message open(Secret const& _s = Secret()) const;
unsigned workProved() const;
void proveWork(unsigned _ms);
@ -102,16 +101,19 @@ public:
Public to() const { return m_to; }
bytes const& payload() const { return m_payload; }
void setFrom(Public _from) { m_from = _from; }
void setTo(Public _to) { m_to = _to; }
void setPayload(bytes const& _payload) { m_payload = _payload; }
void setPayload(bytes&& _payload) { swap(m_payload, _payload); }
operator bool() const { return !!m_payload.size() || m_from || m_to; }
/// Turn this message into a ditributable Envelope.
Envelope seal(Secret _from, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50);
Envelope seal(Secret _from, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const;
// Overloads for skipping _from or specifying _to.
Envelope seal(Topic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope seal(Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope seal(Secret _from, Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
Envelope seal(Topic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Secret _from, Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
private:
void populate(bytes const& _data);

2
libwhisper/WhisperHost.cpp

@ -62,7 +62,7 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
m_messages[h] = _m;
}
if (_p)
// if (_p)
{
Guard l(m_filterLock);
for (auto const& f: m_filters)

3
third/MainWin.cpp

@ -120,6 +120,7 @@ Main::Main(QWidget *parent) :
connect(ui->webView, &QWebView::loadFinished, [=]()
{
m_ethereum->poll();
m_whisper->poll();
});
connect(ui->webView, &QWebView::titleChanged, [=]()
@ -522,6 +523,8 @@ void Main::timerEvent(QTimerEvent*)
if (m_ethereum)
m_ethereum->poll();
if (m_whisper)
m_whisper->poll();
for (auto const& i: m_handlers)
if (ethereum()->checkWatch(i.first))

Loading…
Cancel
Save