Added whisper interface for xeth, added examples, updated RPC
* Added RPC methods for whisper * Added whisper example
This commit is contained in:
parent
c48644490f
commit
c03d403437
@ -1,6 +1,6 @@
|
|||||||
<!doctype>
|
<!doctype>
|
||||||
<html>
|
<html>
|
||||||
|
<title>JevCoin</title>
|
||||||
<head>
|
<head>
|
||||||
<script type="text/javascript" src="../ext/bignumber.min.js"></script>
|
<script type="text/javascript" src="../ext/bignumber.min.js"></script>
|
||||||
<script type="text/javascript" src="../ext/ethereum.js/dist/ethereum.js"></script>
|
<script type="text/javascript" src="../ext/ethereum.js/dist/ethereum.js"></script>
|
||||||
|
42
cmd/mist/assets/examples/whisper.html
Normal file
42
cmd/mist/assets/examples/whisper.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!doctype>
|
||||||
|
<html>
|
||||||
|
<title>Whisper test</title>
|
||||||
|
<head>
|
||||||
|
<script type="text/javascript" src="../ext/bignumber.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../ext/ethereum.js/dist/ethereum.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Whisper test</h1>
|
||||||
|
|
||||||
|
<button onclick="test()">Send</button>
|
||||||
|
|
||||||
|
<table width="100%" id="table">
|
||||||
|
<tr>
|
||||||
|
<td>ID</td>
|
||||||
|
<td id="id"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var web3 = require('web3');
|
||||||
|
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080'));
|
||||||
|
|
||||||
|
var shh = web3.shh;
|
||||||
|
|
||||||
|
var id = shh.newIdentity();
|
||||||
|
document.querySelector("#id").innerHTML = id;
|
||||||
|
|
||||||
|
shh.watch({topics: ["test"]}).arrived(function(message) {
|
||||||
|
document.querySelector("#table").innerHTML += "<tr><td colspan='2'>"+JSON.stringify(message)+"</td></tr>";
|
||||||
|
});
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
shh.post({topics: ["test"], payload: web3.fromAscii("test it")})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
@ -110,7 +110,7 @@ ApplicationWindow {
|
|||||||
function newBrowserTab(url) {
|
function newBrowserTab(url) {
|
||||||
var window = addPlugin("./views/browser.qml", {noAdd: true, close: true, section: "apps", active: true});
|
var window = addPlugin("./views/browser.qml", {noAdd: true, close: true, section: "apps", active: true});
|
||||||
window.view.url = url;
|
window.view.url = url;
|
||||||
window.menuItem.title = "Browser Tab";
|
window.menuItem.title = "Mist";
|
||||||
activeView(window.view, window.menuItem);
|
activeView(window.view, window.menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ Rectangle {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "#00000000"
|
color: "#00000000"
|
||||||
|
|
||||||
property var title: "DApps"
|
property var title: ""
|
||||||
property var iconSource: "../browser.png"
|
property var iconSource: "../browser.png"
|
||||||
property var menuItem
|
property var menuItem
|
||||||
property var hideUrl: true
|
property var hideUrl: true
|
||||||
@ -154,6 +154,9 @@ Rectangle {
|
|||||||
|
|
||||||
onLoadingChanged: {
|
onLoadingChanged: {
|
||||||
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
|
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("bignumber.min.js"));
|
||||||
webview.runJavaScript(eth.readFile("ethereum.js/dist/ethereum.js"));
|
webview.runJavaScript(eth.readFile("ethereum.js/dist/ethereum.js"));
|
||||||
}
|
}
|
||||||
|
@ -251,3 +251,12 @@ func (a *DbArgs) requirements() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WhisperMessageArgs struct {
|
||||||
|
Payload string
|
||||||
|
To string
|
||||||
|
From string
|
||||||
|
Topics []string
|
||||||
|
Priority uint32
|
||||||
|
Ttl uint32
|
||||||
|
}
|
||||||
|
@ -21,6 +21,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -270,3 +272,45 @@ func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) {
|
|||||||
rpclogger.DebugDetailf("%T %v", args, args)
|
rpclogger.DebugDetailf("%T %v", args, args)
|
||||||
return &args, nil
|
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
|
||||||
|
}
|
||||||
|
@ -44,18 +44,22 @@ type EthereumApi struct {
|
|||||||
xeth *xeth.XEth
|
xeth *xeth.XEth
|
||||||
filterManager *filter.FilterManager
|
filterManager *filter.FilterManager
|
||||||
|
|
||||||
mut sync.RWMutex
|
logMut sync.RWMutex
|
||||||
logs map[int]state.Logs
|
logs map[int]state.Logs
|
||||||
|
|
||||||
|
messagesMut sync.RWMutex
|
||||||
|
messages map[int][]xeth.WhisperMessage
|
||||||
|
|
||||||
db ethutil.Database
|
db ethutil.Database
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEthereumApi(xeth *xeth.XEth) *EthereumApi {
|
func NewEthereumApi(eth *xeth.XEth) *EthereumApi {
|
||||||
db, _ := ethdb.NewLDBDatabase("dapps")
|
db, _ := ethdb.NewLDBDatabase("dapps")
|
||||||
api := &EthereumApi{
|
api := &EthereumApi{
|
||||||
xeth: xeth,
|
xeth: eth,
|
||||||
filterManager: filter.NewFilterManager(xeth.Backend().EventMux()),
|
filterManager: filter.NewFilterManager(eth.Backend().EventMux()),
|
||||||
logs: make(map[int]state.Logs),
|
logs: make(map[int]state.Logs),
|
||||||
|
messages: make(map[int][]xeth.WhisperMessage),
|
||||||
db: db,
|
db: db,
|
||||||
}
|
}
|
||||||
go api.filterManager.Start()
|
go api.filterManager.Start()
|
||||||
@ -67,8 +71,8 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro
|
|||||||
var id int
|
var id int
|
||||||
filter := core.NewFilter(self.xeth.Backend())
|
filter := core.NewFilter(self.xeth.Backend())
|
||||||
filter.LogsCallback = func(logs state.Logs) {
|
filter.LogsCallback = func(logs state.Logs) {
|
||||||
self.mut.Lock()
|
self.logMut.Lock()
|
||||||
defer self.mut.Unlock()
|
defer self.logMut.Unlock()
|
||||||
|
|
||||||
self.logs[id] = append(self.logs[id], logs...)
|
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 {
|
func (self *EthereumApi) FilterChanged(id int, reply *interface{}) error {
|
||||||
self.mut.RLock()
|
self.logMut.RLock()
|
||||||
defer self.mut.RUnlock()
|
defer self.logMut.RUnlock()
|
||||||
|
|
||||||
*reply = toLogs(self.logs[id])
|
*reply = toLogs(self.logs[id])
|
||||||
|
|
||||||
@ -257,6 +261,44 @@ func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error {
|
|||||||
return nil
|
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 {
|
func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
|
||||||
// Spec at https://github.com/ethereum/wiki/wiki/Generic-ON-RPC
|
// Spec at https://github.com/ethereum/wiki/wiki/Generic-ON-RPC
|
||||||
rpclogger.DebugDetailf("%T %s", req.Params, req.Params)
|
rpclogger.DebugDetailf("%T %s", req.Params, req.Params)
|
||||||
@ -354,6 +396,26 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p.DbGet(args, reply)
|
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:
|
default:
|
||||||
return NewErrorResponse(fmt.Sprintf("%v %s", ErrorNotImplemented, req.Method))
|
return NewErrorResponse(fmt.Sprintf("%v %s", ErrorNotImplemented, req.Method))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// QWhisper package. This package is temporarily on hold until QML DApp dev will reemerge.
|
||||||
package qwhisper
|
package qwhisper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
116
xeth/whisper.go
Normal file
116
xeth/whisper.go
Normal file
@ -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())),
|
||||||
|
}
|
||||||
|
}
|
12
xeth/xeth.go
12
xeth/xeth.go
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/whisper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pipelogger = logger.NewLogger("XETH")
|
var pipelogger = logger.NewLogger("XETH")
|
||||||
@ -33,6 +34,7 @@ type Backend interface {
|
|||||||
ClientIdentity() p2p.ClientIdentity
|
ClientIdentity() p2p.ClientIdentity
|
||||||
Db() ethutil.Database
|
Db() ethutil.Database
|
||||||
EventMux() *event.TypeMux
|
EventMux() *event.TypeMux
|
||||||
|
Whisper() *whisper.Whisper
|
||||||
}
|
}
|
||||||
|
|
||||||
type XEth struct {
|
type XEth struct {
|
||||||
@ -40,6 +42,7 @@ type XEth struct {
|
|||||||
blockProcessor *core.BlockProcessor
|
blockProcessor *core.BlockProcessor
|
||||||
chainManager *core.ChainManager
|
chainManager *core.ChainManager
|
||||||
state *State
|
state *State
|
||||||
|
whisper *Whisper
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(eth Backend) *XEth {
|
func New(eth Backend) *XEth {
|
||||||
@ -47,17 +50,16 @@ func New(eth Backend) *XEth {
|
|||||||
eth: eth,
|
eth: eth,
|
||||||
blockProcessor: eth.BlockProcessor(),
|
blockProcessor: eth.BlockProcessor(),
|
||||||
chainManager: eth.ChainManager(),
|
chainManager: eth.ChainManager(),
|
||||||
|
whisper: NewWhisper(eth.Whisper()),
|
||||||
}
|
}
|
||||||
xeth.state = NewState(xeth)
|
xeth.state = NewState(xeth)
|
||||||
|
|
||||||
return xeth
|
return xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) Backend() Backend {
|
func (self *XEth) Backend() Backend { return self.eth }
|
||||||
return self.eth
|
func (self *XEth) State() *State { return self.state }
|
||||||
}
|
func (self *XEth) Whisper() *Whisper { return self.whisper }
|
||||||
|
|
||||||
func (self *XEth) State() *State { return self.state }
|
|
||||||
|
|
||||||
func (self *XEth) BlockByHash(strHash string) *Block {
|
func (self *XEth) BlockByHash(strHash string) *Block {
|
||||||
hash := fromHex(strHash)
|
hash := fromHex(strHash)
|
||||||
|
Loading…
Reference in New Issue
Block a user