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>
|
||||
<html>
|
||||
|
||||
<title>JevCoin</title>
|
||||
<head>
|
||||
<script type="text/javascript" src="../ext/bignumber.min.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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// QWhisper package. This package is temporarily on hold until QML DApp dev will reemerge.
|
||||
package qwhisper
|
||||
|
||||
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/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)
|
||||
|
Loading…
Reference in New Issue
Block a user