javascript_runtime.go 5.67 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/cmd/utils"
obscuren's avatar
obscuren committed
12 13
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/types"
14 15
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/event"
obscuren's avatar
obscuren committed
16
	"github.com/ethereum/go-ethereum/logger"
obscuren's avatar
obscuren committed
17
	"github.com/ethereum/go-ethereum/state"
18
	"github.com/ethereum/go-ethereum/xeth"
19
	"github.com/obscuren/otto"
obscuren's avatar
obscuren committed
20 21
)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
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
104
func (self *JSRE) Stop() {
Felix Lange's avatar
Felix Lange committed
105
	self.events.Unsubscribe()
zelig's avatar
zelig committed
106
	jsrelogger.Infoln("stopped")
obscuren's avatar
obscuren committed
107 108 109
}

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

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

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

obscuren's avatar
obscuren committed
131
func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
obscuren's avatar
obscuren committed
132
	var state *state.StateDB
obscuren's avatar
obscuren committed
133 134

	if len(call.ArgumentList) > 0 {
135
		var block *types.Block
obscuren's avatar
obscuren committed
136 137
		if call.Argument(0).IsNumber() {
			num, _ := call.Argument(0).ToInteger()
138
			block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))
obscuren's avatar
obscuren committed
139 140
		} else if call.Argument(0).IsString() {
			hash, _ := call.Argument(0).ToString()
141
			block = self.ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(hash))
obscuren's avatar
obscuren committed
142 143 144 145 146 147 148 149 150 151 152 153
		} 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
154
		state = self.ethereum.ChainManager().State()
obscuren's avatar
obscuren committed
155 156
	}

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

159
	return v
obscuren's avatar
obscuren committed
160 161
}

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

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

172
// eth.watch
obscuren's avatar
obscuren committed
173
func (self *JSRE) watch(call otto.FunctionCall) otto.Value {
174 175 176 177 178 179 180 181 182 183 184 185 186 187
	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
188

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

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

	return otto.UndefinedValue()
}
200

201 202 203 204 205 206 207 208 209 210 211 212 213
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 {
214
		return otto.UndefinedValue()
215 216 217 218 219 220
	}
	if err := self.Require(file); err != nil {
		fmt.Println("err:", err)
		return otto.UndefinedValue()
	}

221
	t, _ := self.Vm.Get("exports")
222

223
	return t
obscuren's avatar
obscuren committed
224
}
225 226 227 228 229 230 231

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

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

	return otto.TrueValue()
}
obscuren's avatar
obscuren committed
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256

func (self *JSRE) export(call otto.FunctionCall) otto.Value {
	fn, err := call.Argument(0).ToString()
	if err != nil {
		fmt.Println(err)
		return otto.FalseValue()
	}

	data := self.ethereum.ChainManager().Export()

	if err := ethutil.WriteFile(fn, data); err != nil {
		fmt.Println(err)
		return otto.FalseValue()
	}

	return otto.TrueValue()
}