javascript_runtime.go 5.26 KB
Newer Older
1
package javascript
obscuren's avatar
obscuren committed
2 3 4

import (
	"fmt"
obscuren's avatar
obscuren committed
5 6 7 8 9
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

10
	"github.com/ethereum/go-ethereum"
obscuren's avatar
obscuren committed
11
	"github.com/ethereum/go-ethereum/chain"
obscuren's avatar
obscuren committed
12
	"github.com/ethereum/go-ethereum/cmd/utils"
13 14
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/event"
obscuren's avatar
obscuren committed
15
	"github.com/ethereum/go-ethereum/logger"
obscuren's avatar
obscuren committed
16
	"github.com/ethereum/go-ethereum/state"
17
	"github.com/ethereum/go-ethereum/xeth"
18
	"github.com/obscuren/otto"
obscuren's avatar
obscuren committed
19 20
)

obscuren's avatar
obscuren committed
21
var jsrelogger = logger.NewLogger("JSRE")
zelig's avatar
zelig committed
22

obscuren's avatar
obscuren committed
23
type JSRE struct {
obscuren's avatar
obscuren committed
24
	ethereum *eth.Ethereum
25
	Vm       *otto.Otto
26
	pipe     *xeth.JSXEth
obscuren's avatar
obscuren committed
27

28
	events event.Subscription
obscuren's avatar
obscuren committed
29 30

	objectCb map[string][]otto.Value
obscuren's avatar
obscuren committed
31 32
}

33 34 35
func (jsre *JSRE) LoadExtFile(path string) {
	result, err := ioutil.ReadFile(path)
	if err == nil {
36
		jsre.Vm.Run(result)
37
	} else {
38
		jsrelogger.Infoln("Could not load file:", path)
39 40 41 42
	}
}

func (jsre *JSRE) LoadIntFile(file string) {
43
	assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
44 45 46
	jsre.LoadExtFile(path.Join(assetPath, file))
}

obscuren's avatar
obscuren committed
47
func NewJSRE(ethereum *eth.Ethereum) *JSRE {
obscuren's avatar
obscuren committed
48 49 50
	re := &JSRE{
		ethereum,
		otto.New(),
51
		xeth.NewJSXEth(ethereum),
Felix Lange's avatar
Felix Lange committed
52
		nil,
obscuren's avatar
obscuren committed
53 54 55
		make(map[string][]otto.Value),
	}

obscuren's avatar
obscuren committed
56
	// Init the JS lib
57
	re.Vm.Run(jsLib)
obscuren's avatar
obscuren committed
58

59 60 61 62
	// Load extra javascript files
	re.LoadIntFile("string.js")
	re.LoadIntFile("big.js")

zelig's avatar
zelig committed
63
	// Subscribe to events
Felix Lange's avatar
Felix Lange committed
64
	mux := ethereum.EventMux()
obscuren's avatar
obscuren committed
65
	re.events = mux.Subscribe(chain.NewBlockEvent{})
zelig's avatar
zelig committed
66

67 68 69
	// We have to make sure that, whoever calls this, calls "Stop"
	go re.mainLoop()

obscuren's avatar
obscuren committed
70
	re.Bind("eth", &JSEthereum{re.pipe, re.Vm, ethereum})
obscuren's avatar
obscuren committed
71

obscuren's avatar
obscuren committed
72
	re.initStdFuncs()
obscuren's avatar
obscuren committed
73

zelig's avatar
zelig committed
74 75
	jsrelogger.Infoln("started")

obscuren's avatar
obscuren committed
76 77
	return re
}
obscuren's avatar
obscuren committed
78

obscuren's avatar
obscuren committed
79
func (self *JSRE) Bind(name string, v interface{}) {
80
	self.Vm.Set(name, v)
obscuren's avatar
obscuren committed
81
}
obscuren's avatar
obscuren committed
82

obscuren's avatar
obscuren committed
83
func (self *JSRE) Run(code string) (otto.Value, error) {
84
	return self.Vm.Run(code)
obscuren's avatar
obscuren committed
85 86
}

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
func (self *JSRE) Require(file string) error {
	if len(filepath.Ext(file)) == 0 {
		file += ".js"
	}

	fh, err := os.Open(file)
	if err != nil {
		return err
	}

	content, _ := ioutil.ReadAll(fh)
	self.Run("exports = {};(function() {" + string(content) + "})();")

	return nil
}

obscuren's avatar
obscuren committed
103
func (self *JSRE) Stop() {
Felix Lange's avatar
Felix Lange committed
104
	self.events.Unsubscribe()
zelig's avatar
zelig committed
105
	jsrelogger.Infoln("stopped")
obscuren's avatar
obscuren committed
106 107 108
}

func (self *JSRE) mainLoop() {
Felix Lange's avatar
Felix Lange committed
109
	for _ = range self.events.Chan() {
obscuren's avatar
obscuren committed
110 111 112
	}
}

obscuren's avatar
obscuren committed
113
func (self *JSRE) initStdFuncs() {
114
	t, _ := self.Vm.Get("eth")
obscuren's avatar
obscuren committed
115
	eth := t.Object()
116 117
	eth.Set("watch", self.watch)
	eth.Set("addPeer", self.addPeer)
obscuren's avatar
obscuren committed
118
	eth.Set("require", self.require)
obscuren's avatar
obscuren committed
119 120
	eth.Set("stopMining", self.stopMining)
	eth.Set("startMining", self.startMining)
obscuren's avatar
obscuren committed
121
	eth.Set("execBlock", self.execBlock)
obscuren's avatar
obscuren committed
122
	eth.Set("dump", self.dump)
123 124 125 126 127 128
}

/*
 * The following methods are natively implemented javascript functions
 */

obscuren's avatar
obscuren committed
129
func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
obscuren's avatar
obscuren committed
130
	var state *state.State
obscuren's avatar
obscuren committed
131 132

	if len(call.ArgumentList) > 0 {
obscuren's avatar
obscuren committed
133
		var block *chain.Block
obscuren's avatar
obscuren committed
134 135
		if call.Argument(0).IsNumber() {
			num, _ := call.Argument(0).ToInteger()
136
			block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))
obscuren's avatar
obscuren committed
137 138
		} else if call.Argument(0).IsString() {
			hash, _ := call.Argument(0).ToString()
139
			block = self.ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(hash))
obscuren's avatar
obscuren committed
140 141 142 143 144 145 146 147 148 149 150 151
		} else {
			fmt.Println("invalid argument for dump. Either hex string or number")
		}

		if block == nil {
			fmt.Println("block not found")

			return otto.UndefinedValue()
		}

		state = block.State()
	} else {
obscuren's avatar
obscuren committed
152
		state = self.ethereum.BlockManager().CurrentState()
obscuren's avatar
obscuren committed
153 154
	}

155
	v, _ := self.Vm.ToValue(state.Dump())
obscuren's avatar
obscuren committed
156

157
	return v
obscuren's avatar
obscuren committed
158 159
}

obscuren's avatar
obscuren committed
160
func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value {
161
	v, _ := self.Vm.ToValue(utils.StopMining(self.ethereum))
obscuren's avatar
obscuren committed
162 163 164 165
	return v
}

func (self *JSRE) startMining(call otto.FunctionCall) otto.Value {
166
	v, _ := self.Vm.ToValue(utils.StartMining(self.ethereum))
obscuren's avatar
obscuren committed
167 168 169
	return v
}

170
// eth.watch
obscuren's avatar
obscuren committed
171
func (self *JSRE) watch(call otto.FunctionCall) otto.Value {
172 173 174 175 176 177 178 179 180 181 182 183 184 185
	addr, _ := call.Argument(0).ToString()
	var storageAddr string
	var cb otto.Value
	var storageCallback bool
	if len(call.ArgumentList) > 2 {
		storageCallback = true
		storageAddr, _ = call.Argument(1).ToString()
		cb = call.Argument(2)
	} else {
		cb = call.Argument(1)
	}

	if storageCallback {
		self.objectCb[addr+storageAddr] = append(self.objectCb[addr+storageAddr], cb)
obscuren's avatar
obscuren committed
186

Felix Lange's avatar
Felix Lange committed
187 188
		// event := "storage:" + string(ethutil.Hex2Bytes(addr)) + ":" + string(ethutil.Hex2Bytes(storageAddr))
		// self.ethereum.EventMux().Subscribe(event, self.changeChan)
189
	} else {
obscuren's avatar
obscuren committed
190
		self.objectCb[addr] = append(self.objectCb[addr], cb)
obscuren's avatar
obscuren committed
191

Felix Lange's avatar
Felix Lange committed
192 193
		// event := "object:" + string(ethutil.Hex2Bytes(addr))
		// self.ethereum.EventMux().Subscribe(event, self.changeChan)
194 195 196 197
	}

	return otto.UndefinedValue()
}
198

199 200 201 202 203 204 205 206 207 208 209 210 211
func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value {
	host, err := call.Argument(0).ToString()
	if err != nil {
		return otto.FalseValue()
	}
	self.ethereum.ConnectToPeer(host)

	return otto.TrueValue()
}

func (self *JSRE) require(call otto.FunctionCall) otto.Value {
	file, err := call.Argument(0).ToString()
	if err != nil {
212
		return otto.UndefinedValue()
213 214 215 216 217 218
	}
	if err := self.Require(file); err != nil {
		fmt.Println("err:", err)
		return otto.UndefinedValue()
	}

219
	t, _ := self.Vm.Get("exports")
220

221
	return t
obscuren's avatar
obscuren committed
222
}
223 224 225 226 227 228 229

func (self *JSRE) execBlock(call otto.FunctionCall) otto.Value {
	hash, err := call.Argument(0).ToString()
	if err != nil {
		return otto.UndefinedValue()
	}

230
	err = utils.BlockDo(self.ethereum, ethutil.Hex2Bytes(hash))
231 232 233 234 235 236 237
	if err != nil {
		fmt.Println(err)
		return otto.FalseValue()
	}

	return otto.TrueValue()
}