Przeglądaj źródła

Added whisper interface for xeth, added examples, updated RPC

* Added RPC methods for whisper
* Added whisper example
obscuren 10 lat temu
rodzic
commit
c03d403437

+ 1 - 1
cmd/mist/assets/examples/coin.html

@@ -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 - 0
cmd/mist/assets/examples/whisper.html

@@ -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>
+
+

+ 1 - 1
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);
 	}
 

+ 4 - 1
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"));
 				}

+ 9 - 0
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
+}

+ 44 - 0
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
+}

+ 71 - 9
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))
 	}

+ 1 - 0
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 (

+ 116 - 0
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())),
+	}
+}

+ 7 - 5
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)