diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html
index 572f6959dd..edeabe5e86 100644
--- a/cmd/mist/assets/examples/coin.html
+++ b/cmd/mist/assets/examples/coin.html
@@ -1,6 +1,6 @@
-
+
JevCoin
diff --git a/cmd/mist/assets/examples/whisper.html b/cmd/mist/assets/examples/whisper.html
new file mode 100644
index 0000000000..51d7004de4
--- /dev/null
+++ b/cmd/mist/assets/examples/whisper.html
@@ -0,0 +1,42 @@
+
+
+Whisper test
+
+
+
+
+
+
+Whisper test
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml
index 1a8885e586..da1f4a0c6e 100644
--- a/cmd/mist/assets/qml/main.qml
+++ b/cmd/mist/assets/qml/main.qml
@@ -110,7 +110,7 @@ ApplicationWindow {
function newBrowserTab(url) {
var window = addPlugin("./views/browser.qml", {noAdd: true, close: true, section: "apps", active: true});
window.view.url = url;
- window.menuItem.title = "Browser Tab";
+ window.menuItem.title = "Mist";
activeView(window.view, window.menuItem);
}
diff --git a/cmd/mist/assets/qml/views/browser.qml b/cmd/mist/assets/qml/views/browser.qml
index d6a7622784..277a5b7ebf 100644
--- a/cmd/mist/assets/qml/views/browser.qml
+++ b/cmd/mist/assets/qml/views/browser.qml
@@ -11,7 +11,7 @@ Rectangle {
anchors.fill: parent
color: "#00000000"
- property var title: "DApps"
+ property var title: ""
property var iconSource: "../browser.png"
property var menuItem
property var hideUrl: true
@@ -154,6 +154,9 @@ Rectangle {
onLoadingChanged: {
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
+ webview.runJavaScript("document.title", function(pageTitle) {
+ menuItem.title = pageTitle;
+ });
webview.runJavaScript(eth.readFile("bignumber.min.js"));
webview.runJavaScript(eth.readFile("ethereum.js/dist/ethereum.js"));
}
diff --git a/rpc/args.go b/rpc/args.go
index aaa017c4ec..75eef873d5 100644
--- a/rpc/args.go
+++ b/rpc/args.go
@@ -251,3 +251,12 @@ func (a *DbArgs) requirements() error {
}
return nil
}
+
+type WhisperMessageArgs struct {
+ Payload string
+ To string
+ From string
+ Topics []string
+ Priority uint32
+ Ttl uint32
+}
diff --git a/rpc/message.go b/rpc/message.go
index 5045adb8fc..9193029219 100644
--- a/rpc/message.go
+++ b/rpc/message.go
@@ -21,6 +21,8 @@ import (
"encoding/json"
"errors"
"fmt"
+
+ "github.com/ethereum/go-ethereum/xeth"
)
const (
@@ -270,3 +272,45 @@ func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) {
rpclogger.DebugDetailf("%T %v", args, args)
return &args, nil
}
+
+func (req *RpcRequest) ToWhisperFilterArgs() (*xeth.Options, error) {
+ if len(req.Params) < 1 {
+ return nil, NewErrorResponse(ErrorArguments)
+ }
+
+ var args xeth.Options
+ err := json.Unmarshal(req.Params[0], &args)
+ if err != nil {
+ return nil, NewErrorResponseWithError(ErrorDecodeArgs, err)
+ }
+ rpclogger.DebugDetailf("%T %v", args, args)
+ return &args, nil
+}
+
+func (req *RpcRequest) ToWhisperChangedArgs() (int, error) {
+ if len(req.Params) < 1 {
+ return 0, NewErrorResponse(ErrorArguments)
+ }
+
+ var id int
+ err := json.Unmarshal(req.Params[0], &id)
+ if err != nil {
+ return 0, NewErrorResponse(ErrorDecodeArgs)
+ }
+ rpclogger.DebugDetailf("%T %v", id, id)
+ return id, nil
+}
+
+func (req *RpcRequest) ToWhisperPostArgs() (*WhisperMessageArgs, error) {
+ if len(req.Params) < 1 {
+ return nil, NewErrorResponse(ErrorArguments)
+ }
+
+ var args WhisperMessageArgs
+ err := json.Unmarshal(req.Params[0], &args)
+ if err != nil {
+ return nil, err
+ }
+ rpclogger.DebugDetailf("%T %v", args, args)
+ return &args, nil
+}
diff --git a/rpc/packages.go b/rpc/packages.go
index aa51aad42e..8344d6a46f 100644
--- a/rpc/packages.go
+++ b/rpc/packages.go
@@ -44,18 +44,22 @@ type EthereumApi struct {
xeth *xeth.XEth
filterManager *filter.FilterManager
- mut sync.RWMutex
- logs map[int]state.Logs
+ logMut sync.RWMutex
+ logs map[int]state.Logs
+
+ messagesMut sync.RWMutex
+ messages map[int][]xeth.WhisperMessage
db ethutil.Database
}
-func NewEthereumApi(xeth *xeth.XEth) *EthereumApi {
+func NewEthereumApi(eth *xeth.XEth) *EthereumApi {
db, _ := ethdb.NewLDBDatabase("dapps")
api := &EthereumApi{
- xeth: xeth,
- filterManager: filter.NewFilterManager(xeth.Backend().EventMux()),
+ xeth: eth,
+ filterManager: filter.NewFilterManager(eth.Backend().EventMux()),
logs: make(map[int]state.Logs),
+ messages: make(map[int][]xeth.WhisperMessage),
db: db,
}
go api.filterManager.Start()
@@ -67,8 +71,8 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro
var id int
filter := core.NewFilter(self.xeth.Backend())
filter.LogsCallback = func(logs state.Logs) {
- self.mut.Lock()
- defer self.mut.Unlock()
+ self.logMut.Lock()
+ defer self.logMut.Unlock()
self.logs[id] = append(self.logs[id], logs...)
}
@@ -79,8 +83,8 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro
}
func (self *EthereumApi) FilterChanged(id int, reply *interface{}) error {
- self.mut.RLock()
- defer self.mut.RUnlock()
+ self.logMut.RLock()
+ defer self.logMut.RUnlock()
*reply = toLogs(self.logs[id])
@@ -257,6 +261,44 @@ func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error {
return nil
}
+func (p *EthereumApi) NewWhisperIdentity(reply *interface{}) error {
+ *reply = p.xeth.Whisper().NewIdentity()
+ return nil
+}
+
+func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) error {
+ var id int
+ args.Fn = func(msg xeth.WhisperMessage) {
+ p.messagesMut.Lock()
+ defer p.messagesMut.Unlock()
+ p.messages[id] = append(p.messages[id], msg)
+ }
+ id = p.xeth.Whisper().Watch(args)
+ *reply = id
+ return nil
+}
+
+func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error {
+ self.messagesMut.RLock()
+ defer self.messagesMut.RUnlock()
+
+ *reply = self.messages[id]
+
+ self.messages[id] = nil // empty the messages
+
+ return nil
+}
+
+func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error {
+ err := p.xeth.Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
+ if err != nil {
+ return err
+ }
+
+ *reply = true
+ return nil
+}
+
func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
// Spec at https://github.com/ethereum/wiki/wiki/Generic-ON-RPC
rpclogger.DebugDetailf("%T %s", req.Params, req.Params)
@@ -354,6 +396,26 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
return err
}
return p.DbGet(args, reply)
+ case "shh_newIdentity":
+ return p.NewWhisperIdentity(reply)
+ case "shh_newFilter":
+ args, err := req.ToWhisperFilterArgs()
+ if err != nil {
+ return err
+ }
+ return p.NewWhisperFilter(args, reply)
+ case "shh_changed":
+ args, err := req.ToWhisperChangedArgs()
+ if err != nil {
+ return err
+ }
+ return p.MessagesChanged(args, reply)
+ case "shh_post":
+ args, err := req.ToWhisperPostArgs()
+ if err != nil {
+ return nil
+ }
+ return p.WhisperPost(args, reply)
default:
return NewErrorResponse(fmt.Sprintf("%v %s", ErrorNotImplemented, req.Method))
}
diff --git a/ui/qt/qwhisper/whisper.go b/ui/qt/qwhisper/whisper.go
index 22671f1b5d..98bfc69b0f 100644
--- a/ui/qt/qwhisper/whisper.go
+++ b/ui/qt/qwhisper/whisper.go
@@ -1,3 +1,4 @@
+// QWhisper package. This package is temporarily on hold until QML DApp dev will reemerge.
package qwhisper
import (
diff --git a/xeth/whisper.go b/xeth/whisper.go
new file mode 100644
index 0000000000..31201271b9
--- /dev/null
+++ b/xeth/whisper.go
@@ -0,0 +1,116 @@
+package xeth
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/whisper"
+)
+
+var qlogger = logger.NewLogger("XSHH")
+
+type Whisper struct {
+ *whisper.Whisper
+}
+
+func NewWhisper(w *whisper.Whisper) *Whisper {
+ return &Whisper{w}
+}
+
+func (self *Whisper) Post(payload string, to, from string, topics []string, priority, ttl uint32) error {
+ if priority == 0 {
+ priority = 1000
+ }
+
+ if ttl == 0 {
+ ttl = 100
+ }
+
+ pk := crypto.ToECDSAPub(fromHex(from))
+ if key := self.Whisper.GetIdentity(pk); key != nil || len(from) == 0 {
+ msg := whisper.NewMessage(fromHex(payload))
+ envelope, err := msg.Seal(time.Duration(priority*100000), whisper.Opts{
+ Ttl: time.Duration(ttl) * time.Second,
+ To: crypto.ToECDSAPub(fromHex(to)),
+ From: key,
+ Topics: whisper.TopicsFromString(topics...),
+ })
+
+ if err != nil {
+ return err
+ }
+
+ if err := self.Whisper.Send(envelope); err != nil {
+ return err
+ }
+ } else {
+ return errors.New("unmatched pub / priv for seal")
+ }
+
+ return nil
+}
+
+func (self *Whisper) NewIdentity() string {
+ key := self.Whisper.NewIdentity()
+
+ return toHex(crypto.FromECDSAPub(&key.PublicKey))
+}
+
+func (self *Whisper) HasIdentity(key string) bool {
+ return self.Whisper.HasIdentity(crypto.ToECDSAPub(fromHex(key)))
+}
+
+func (self *Whisper) Watch(opts *Options) int {
+ filter := whisper.Filter{
+ To: crypto.ToECDSA(fromHex(opts.To)),
+ From: crypto.ToECDSAPub(fromHex(opts.From)),
+ Topics: whisper.TopicsFromString(opts.Topics...),
+ }
+
+ var i int
+ filter.Fn = func(msg *whisper.Message) {
+ opts.Fn(NewWhisperMessage(msg))
+ }
+ fmt.Println("new filter", filter)
+
+ i = self.Whisper.Watch(filter)
+
+ return i
+}
+
+func (self *Whisper) Messages(id int) (messages []WhisperMessage) {
+ msgs := self.Whisper.Messages(id)
+ messages = make([]WhisperMessage, len(msgs))
+ for i, message := range msgs {
+ messages[i] = NewWhisperMessage(message)
+ }
+
+ return
+}
+
+type Options struct {
+ To string
+ From string
+ Topics []string
+ Fn func(msg WhisperMessage)
+}
+
+type WhisperMessage struct {
+ ref *whisper.Message
+ Flags int32 `json:"flags"`
+ Payload string `json:"payload"`
+ From string `json:"from"`
+}
+
+func NewWhisperMessage(msg *whisper.Message) WhisperMessage {
+ return WhisperMessage{
+ ref: msg,
+ Flags: int32(msg.Flags),
+ Payload: "0x" + ethutil.Bytes2Hex(msg.Payload),
+ From: "0x" + ethutil.Bytes2Hex(crypto.FromECDSAPub(msg.Recover())),
+ }
+}
diff --git a/xeth/xeth.go b/xeth/xeth.go
index 4cfa104d0e..a65fa13315 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -16,6 +16,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/state"
+ "github.com/ethereum/go-ethereum/whisper"
)
var pipelogger = logger.NewLogger("XETH")
@@ -33,6 +34,7 @@ type Backend interface {
ClientIdentity() p2p.ClientIdentity
Db() ethutil.Database
EventMux() *event.TypeMux
+ Whisper() *whisper.Whisper
}
type XEth struct {
@@ -40,6 +42,7 @@ type XEth struct {
blockProcessor *core.BlockProcessor
chainManager *core.ChainManager
state *State
+ whisper *Whisper
}
func New(eth Backend) *XEth {
@@ -47,17 +50,16 @@ func New(eth Backend) *XEth {
eth: eth,
blockProcessor: eth.BlockProcessor(),
chainManager: eth.ChainManager(),
+ whisper: NewWhisper(eth.Whisper()),
}
xeth.state = NewState(xeth)
return xeth
}
-func (self *XEth) Backend() Backend {
- return self.eth
-}
-
-func (self *XEth) State() *State { return self.state }
+func (self *XEth) Backend() Backend { return self.eth }
+func (self *XEth) State() *State { return self.state }
+func (self *XEth) Whisper() *Whisper { return self.whisper }
func (self *XEth) BlockByHash(strHash string) *Block {
hash := fromHex(strHash)