javascript_runtime.go 4.62 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"

obscuren's avatar
obscuren committed
10
	"github.com/ethereum/go-ethereum/cmd/utils"
obscuren's avatar
obscuren committed
11 12
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/types"
13
	"github.com/ethereum/go-ethereum/eth"
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
	xeth     *xeth.XEth
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.New(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
	// Load extra javascript files
61
	re.LoadIntFile("bignumber.min.js")
62

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(core.NewBlockEvent{})
zelig's avatar
zelig committed
66

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

70
	re.Bind("eth", &JSEthereum{re.xeth, 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
	eth.Set("connect", self.connect)
obscuren's avatar
obscuren committed
117
	eth.Set("require", self.require)
obscuren's avatar
obscuren committed
118 119
	eth.Set("stopMining", self.stopMining)
	eth.Set("startMining", self.startMining)
obscuren's avatar
obscuren committed
120
	eth.Set("dump", self.dump)
obscuren's avatar
obscuren committed
121
	eth.Set("export", self.export)
122 123 124 125 126 127
}

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

obscuren's avatar
obscuren committed
128
func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
129
	var block *types.Block
obscuren's avatar
obscuren committed
130 131 132 133

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

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

			return otto.UndefinedValue()
		}

	} else {
149
		block = self.ethereum.ChainManager().CurrentBlock()
obscuren's avatar
obscuren committed
150 151
	}

152
	statedb := state.New(block.Root(), self.ethereum.Db())
153 154

	v, _ := self.Vm.ToValue(statedb.RawDump())
obscuren's avatar
obscuren committed
155

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

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

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

169
func (self *JSRE) connect(call otto.FunctionCall) otto.Value {
170
	nodeURL, err := call.Argument(0).ToString()
171 172 173
	if err != nil {
		return otto.FalseValue()
	}
174
	if err := self.ethereum.SuggestPeer(nodeURL); err != nil {
175 176
		return otto.FalseValue()
	}
177 178 179 180 181 182
	return otto.TrueValue()
}

func (self *JSRE) require(call otto.FunctionCall) otto.Value {
	file, err := call.Argument(0).ToString()
	if err != nil {
183
		return otto.UndefinedValue()
184 185 186 187 188 189
	}
	if err := self.Require(file); err != nil {
		fmt.Println("err:", err)
		return otto.UndefinedValue()
	}

190
	t, _ := self.Vm.Get("exports")
191

192
	return t
obscuren's avatar
obscuren committed
193
}
194

195 196 197
func (self *JSRE) export(call otto.FunctionCall) otto.Value {
	if len(call.ArgumentList) == 0 {
		fmt.Println("err: require file name")
198 199 200
		return otto.FalseValue()
	}

obscuren's avatar
obscuren committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	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()
}