javascript_runtime.go 4.54 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 11
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/types"
12
	"github.com/ethereum/go-ethereum/eth"
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
	xeth     *xeth.XEth
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.New(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
	// Load extra javascript files
60
	re.LoadIntFile("bignumber.min.js")
61

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

			return otto.UndefinedValue()
		}

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

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

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

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

obscuren's avatar
obscuren committed
158
func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value {
obscuren's avatar
obscuren committed
159 160 161
	self.xeth.Miner().Stop()

	return otto.TrueValue()
obscuren's avatar
obscuren committed
162 163 164
}

func (self *JSRE) startMining(call otto.FunctionCall) otto.Value {
obscuren's avatar
obscuren committed
165 166
	self.xeth.Miner().Start()
	return otto.TrueValue()
obscuren's avatar
obscuren committed
167 168
}

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()
}