WebSocket interface

Web sockets handlers fully implemented. Filter handlers have yet to be
implemented.
This commit is contained in:
obscuren 2014-09-30 23:26:16 +02:00
parent 41ae6f298e
commit 6db40ecb22
7 changed files with 226 additions and 55 deletions

@ -10,36 +10,41 @@ import (
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
) )
var Identifier string var (
var KeyRing string Identifier string
var DiffTool bool KeyRing string
var DiffType string DiffTool bool
var KeyStore string DiffType string
var StartRpc bool KeyStore string
var RpcPort int StartRpc bool
var UseUPnP bool StartWebSockets bool
var OutboundPort string RpcPort int
var ShowGenesis bool UseUPnP bool
var AddPeer string OutboundPort string
var MaxPeer int ShowGenesis bool
var GenAddr bool AddPeer string
var UseSeed bool MaxPeer int
var SecretFile string GenAddr bool
var ExportDir string UseSeed bool
var NonInteractive bool SecretFile string
var Datadir string ExportDir string
var LogFile string NonInteractive bool
var ConfigFile string Datadir string
var DebugFile string LogFile string
var LogLevel int ConfigFile string
var Dump bool DebugFile string
var DumpHash string LogLevel int
var DumpNumber int Dump bool
DumpHash string
DumpNumber int
)
// flags specific to cli client // flags specific to cli client
var StartMining bool var (
var StartJsConsole bool StartMining bool
var InputFile string StartJsConsole bool
InputFile string
)
func defaultDataDir() string { func defaultDataDir() string {
usr, _ := user.Current() usr, _ := user.Current()
@ -62,6 +67,7 @@ func Init() {
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers") flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server") flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)") flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.BoolVar(&UseSeed, "seed", true, "seed peers") flag.BoolVar(&UseSeed, "seed", true, "seed peers")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")

@ -103,6 +103,10 @@ func main() {
utils.StartRpc(ethereum, RpcPort) utils.StartRpc(ethereum, RpcPort)
} }
if StartWebSockets {
utils.StartWebSockets(ethereum)
}
utils.StartEthereum(ethereum, UseSeed) utils.StartEthereum(ethereum, UseSeed)
// this blocks the thread // this blocks the thread

@ -1,9 +1,8 @@
// The magic return variable. The magic return variable will be set during the execution of the QML call. // The magic return variable. The magic return variable will be set during the execution of the QML call.
(function(window) { (function(window) {
function message(type, data) { var Promise = window.Promise;
document.title = JSON.stringify({type: type, data: data}); if(typeof(Promise) === "undefined") {
var Promise = Q.Promise;
return window.____returnData;
} }
function isPromise(o) { function isPromise(o) {
@ -446,6 +445,7 @@
} }
}); });
var g_seed = 1; var g_seed = 1;
function postData(data, cb) { function postData(data, cb) {
data._seed = g_seed; data._seed = g_seed;
@ -459,24 +459,6 @@
g_seed++; g_seed++;
navigator.qt.postMessage(JSON.stringify(data)); window._messagingAdapter.call(this, JSON.stringify(data))
}
navigator.qt.onmessage = function(ev) {
var data = JSON.parse(ev.data)
if(data._event !== undefined) {
eth.trigger(data._event, data.data);
} else {
if(data._seed) {
var cb = eth._callbacks[data._seed];
if(cb) {
cb.call(this, data.data)
// Remove the "trigger" callback
delete eth._callbacks[ev._seed];
}
}
}
} }
})(this); })(this);

@ -1,3 +0,0 @@
if(typeof(Promise) === "undefined") {
window.Promise = Q.Promise;
}

@ -0,0 +1,21 @@
window._messagingAdapter = function(data) {
navigator.qt.postMessage(data);
};
navigator.qt.onmessage = function(ev) {
var data = JSON.parse(ev.data)
if(data._event !== undefined) {
eth.trigger(data._event, data.data);
} else {
if(data._seed) {
var cb = eth._callbacks[data._seed];
if(cb) {
cb.call(this, data.data)
// Remove the "trigger" callback
delete eth._callbacks[ev._seed];
}
}
}
}

@ -164,7 +164,7 @@ import "../ext/qml_messaging.js" as Messaging
experimental.preferences.javascriptEnabled: true experimental.preferences.javascriptEnabled: true
experimental.preferences.navigatorQtObjectEnabled: true experimental.preferences.navigatorQtObjectEnabled: true
experimental.preferences.developerExtrasEnabled: true experimental.preferences.developerExtrasEnabled: true
experimental.userScripts: ["../ext/q.js", "../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"] experimental.userScripts: ["../ext/qt_messaging_adapter.js", "../ext/q.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"]
experimental.onMessageReceived: { experimental.onMessageReceived: {
console.log("[onMessageReceived]: ", message.data) console.log("[onMessageReceived]: ", message.data)
// TODO move to messaging.js // TODO move to messaging.js

161
utils/websockets.go Normal file

@ -0,0 +1,161 @@
package utils
import (
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/websocket"
)
func args(v ...interface{}) []interface{} {
return v
}
type WebSocketServer struct {
ethereum *eth.Ethereum
filterCallbacks map[int][]int
}
func NewWebSocketServer(eth *eth.Ethereum) *WebSocketServer {
return &WebSocketServer{eth, make(map[int][]int)}
}
func (self *WebSocketServer) Serv() {
pipe := ethpipe.NewJSPipe(self.ethereum)
wsServ := websocket.NewServer("/eth", ":40404")
wsServ.MessageFunc(func(c *websocket.Client, msg *websocket.Message) {
switch msg.Call {
case "compile":
data := ethutil.NewValue(msg.Args)
bcode, err := ethutil.Compile(data.Get(0).Str(), false)
if err != nil {
c.Write(args(nil, err.Error()), msg.Seed)
}
code := ethutil.Bytes2Hex(bcode)
c.Write(args(code, nil), msg.Seed)
case "getBlockByNumber":
args := msg.Arguments()
block := pipe.BlockByNumber(int32(args.Get(0).Uint()))
c.Write(block, msg.Seed)
case "getKey":
c.Write(pipe.Key().PrivateKey, msg.Seed)
case "transact":
if mp, ok := msg.Args[0].(map[string]interface{}); ok {
object := mapToTxParams(mp)
c.Write(
args(pipe.Transact(object["from"], object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])),
msg.Seed,
)
}
case "getCoinBase":
c.Write(pipe.CoinBase(), msg.Seed)
case "getIsListening":
c.Write(pipe.IsListening(), msg.Seed)
case "getIsMining":
c.Write(pipe.IsMining(), msg.Seed)
case "getPeerCoint":
c.Write(pipe.PeerCount(), msg.Seed)
case "getCountAt":
args := msg.Arguments()
c.Write(pipe.TxCountAt(args.Get(0).Str()), msg.Seed)
case "getCodeAt":
args := msg.Arguments()
c.Write(len(pipe.CodeAt(args.Get(0).Str())), msg.Seed)
case "getBlockByHash":
args := msg.Arguments()
c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Seed)
case "getStorageAt":
args := msg.Arguments()
c.Write(pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str()), msg.Seed)
case "getBalanceAt":
args := msg.Arguments()
c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Seed)
case "getSecretToAddress":
args := msg.Arguments()
c.Write(pipe.SecretToAddress(args.Get(0).Str()), msg.Seed)
case "newFilter":
case "newFilterString":
case "messages":
// TODO
}
})
wsServ.Listen()
}
func StartWebSockets(eth *eth.Ethereum) {
sock := NewWebSocketServer(eth)
go sock.Serv()
}
// TODO This is starting to become a generic method. Move to utils
func mapToTxParams(object map[string]interface{}) map[string]string {
// Default values
if object["from"] == nil {
object["from"] = ""
}
if object["to"] == nil {
object["to"] = ""
}
if object["value"] == nil {
object["value"] = ""
}
if object["gas"] == nil {
object["gas"] = ""
}
if object["gasPrice"] == nil {
object["gasPrice"] = ""
}
var dataStr string
var data []string
if str, ok := object["data"].(string); ok {
data = []string{str}
}
for _, str := range data {
if ethutil.IsHex(str) {
str = str[2:]
if len(str) != 64 {
str = ethutil.LeftPadString(str, 64)
}
} else {
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
}
dataStr += str
}
object["data"] = dataStr
conv := make(map[string]string)
for key, value := range object {
if v, ok := value.(string); ok {
conv[key] = v
}
}
return conv
}