git ssb

0+

wanderer🌟 / js-primea-hypervisor



Commit 29b4a4a60e83eef1ed70225dae7e9514764a25bf

rename

wanderer committed on 11/22/2016, 4:51:50 PM
Parent: d4ba20b58c01dea031c4dbd86e4a99d066ca1c28

Files changed

index.jschanged
EVMinterface.jsdeleted
EVMimports.jsadded
interfaceAPI.jsdeleted
vm.jsadded
index.jsView
@@ -1,28 +1,27 @@
11 const Vertex = require('./deps/kernelVertex')
22 // The Kernel Exposes this Interface to VM instances it makes
3-const Interface = require('./EVMinterface.js')
4-const InterfaceAPI = require('./interfaceAPI.js')
3+const Imports = require('./EVMimports.js')
4+const VM = require('./vm.js')
55 const Environment = require('./environment.js')
66
77 module.exports = class Kernel {
88 constructor (opts = {}) {
99 this.state = opts.state || new Vertex()
1010 this.parent = opts.parent
1111
12- // if code is bound to this kernel then create the interfaceAPI and the
13- // imports
12+ // if code is bound to this kernel then create the interfaceAPI and the imports
1413 if (opts.code) {
15- this.interfaceAPI = new InterfaceAPI(opts.code)
16- this.imports = buildImports(this.interfaceAPI, opts.interfaces)
14+ this._vm = new VM(opts.code)
15+ this.imports = buildImports(this._vm, opts.interfaces)
1716 }
1817
1918 /**
2019 * Builds a import map with an array of given interfaces
2120 */
22- function buildImports (api, interfaces = [Interface]) {
23- return interfaces.reduce((obj, Interface) => {
24- obj[Interface.name] = new Interface(api).exports
21+ function buildImports (api, imports = [Imports]) {
22+ return imports.reduce((obj, InterfaceConstuctor) => {
23+ obj[InterfaceConstuctor.name] = new InterfaceConstuctor(api).exports
2524 return obj
2625 }, {})
2726 }
2827 }
@@ -32,10 +31,9 @@
3231 * The Kernel Stores all of its state in the Environment. The Interface is used
3332 * to by the VM to retrive infromation from the Environment.
3433 */
3534 async run (environment = new Environment({state: this.state}), imports = this.imports) {
36- await this.interfaceAPI.run(environment, imports)
37- return environment
35+ await this._vm.run(environment, imports)
3836 }
3937
4038 async messageReceiver (message) {
4139 // let the code handle the message if there is code
EVMinterface.jsView
@@ -1,656 +1,0 @@
1-/**
2- * This is the Ethereum interface that is exposed to the WASM instance which
3- * enables to interact with the Ethereum Environment
4- */
5-const fs = require('fs')
6-const path = require('path')
7-const ethUtil = require('ethereumjs-util')
8-const Vertex = require('merkle-trie')
9-const U256 = require('./deps/u256.js')
10-
11-const U128_SIZE_BYTES = 16
12-const ADDRESS_SIZE_BYTES = 20
13-const U256_SIZE_BYTES = 32
14-
15-// The interface exposed to the WebAessembly Core
16-module.exports = class Interface {
17- constructor (kernel) {
18- this.kernel = kernel
19- const shimBin = fs.readFileSync(path.join(__dirname, '/wasm/interface.wasm'))
20- const shimMod = WebAssembly.Module(shimBin)
21- this.shims = WebAssembly.Instance(shimMod, {
22- 'interface': {
23- 'useGas': this._useGas.bind(this),
24- 'getGasLeftHigh': this._getGasLeftHigh.bind(this),
25- 'getGasLeftLow': this._getGasLeftLow.bind(this),
26- 'call': this._call.bind(this)
27- }
28- })
29- }
30-
31- static get name () {
32- return 'ethereum'
33- }
34-
35- get exports () {
36- let exportMethods = [
37- // include all the public methods according to the Ethereum Environment Interface (EEI) r1
38- 'getAddress',
39- 'getBalance',
40- 'getTxOrigin',
41- 'getCaller',
42- 'getCallValue',
43- 'getCallDataSize',
44- 'callDataCopy',
45- 'callDataCopy256',
46- 'getCodeSize',
47- 'codeCopy',
48- 'getExternalCodeSize',
49- 'externalCodeCopy',
50- 'getTxGasPrice',
51- 'getBlockHash',
52- 'getBlockCoinbase',
53- 'getBlockTimestamp',
54- 'getBlockNumber',
55- 'getBlockDifficulty',
56- 'getBlockGasLimit',
57- 'log',
58- 'create',
59- 'callCode',
60- 'callDelegate',
61- 'storageStore',
62- 'storageLoad',
63- 'return',
64- 'selfDestruct'
65- ]
66- let ret = {}
67- exportMethods.forEach((method) => {
68- ret[method] = this[method].bind(this)
69- })
70-
71- // add shims
72- ret.useGas = this.shims.exports.useGas
73- ret.getGasLeft = this.shims.exports.getGasLeft
74- ret.call = this.shims.exports.call
75- return ret
76- }
77-
78- setModule (mod) {
79- this.module = mod
80- }
81-
82- /**
83- * Subtracts an amount to the gas counter
84- * @param {integer} amount the amount to subtract to the gas counter
85- */
86- _useGas (high, low) {
87- this.takeGas(from64bit(high, low))
88- }
89-
90- /**
91- * Returns the current amount of gas
92- * @return {integer}
93- */
94- _getGasLeftHigh () {
95- return Math.floor(this.kernel.environment.gasLeft / 4294967296)
96- }
97-
98- /**
99- * Returns the current amount of gas
100- * @return {integer}
101- */
102- _getGasLeftLow () {
103- return this.kernel.environment.gasLeft
104- }
105-
106- /**
107- * Gets address of currently executing account and loads it into memory at
108- * the given offset.
109- * @param {integer} offset
110- */
111- getAddress (offset) {
112- this.takeGas(2)
113-
114- this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.address.toMemory())
115- }
116-
117- /**
118- * Gets balance of the given account and loads it into memory at the given
119- * offset.
120- * @param {integer} addressOffset the memory offset to laod the address
121- * @param {integer} resultOffset
122- */
123- getBalance (addressOffset, offset, cbIndex) {
124- this.takeGas(20)
125-
126- const path = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'balance']
127- const opPromise = this.kernel.environment.state.root.get(path)
128- .then(vertex => new U256(vertex.value))
129- .catch(() => new U256(0))
130-
131- this.kernel.pushOpsQueue(opPromise, cbIndex, balance => {
132- this.setMemory(offset, U128_SIZE_BYTES, balance.toMemory(U128_SIZE_BYTES))
133- })
134- }
135-
136- /**
137- * Gets the execution's origination address and loads it into memory at the
138- * given offset. This is the sender of original transaction; it is never an
139- * account with non-empty associated code.
140- * @param {integer} offset
141- */
142- getTxOrigin (offset) {
143- this.takeGas(2)
144-
145- this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.origin.toMemory())
146- }
147-
148- /**
149- * Gets caller address and loads it into memory at the given offset. This is
150- * the address of the account that is directly responsible for this execution.
151- * @param {integer} offset
152- */
153- getCaller (offset) {
154- this.takeGas(2)
155-
156- this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.caller.toMemory())
157- }
158-
159- /**
160- * Gets the deposited value by the instruction/transaction responsible for
161- * this execution and loads it into memory at the given location.
162- * @param {integer} offset
163- */
164- getCallValue (offset) {
165- this.takeGas(2)
166-
167- this.setMemory(offset, U128_SIZE_BYTES, this.kernel.environment.callValue.toMemory(U128_SIZE_BYTES))
168- }
169-
170- /**
171- * Get size of input data in current environment. This pertains to the input
172- * data passed with the message call instruction or transaction.
173- * @return {integer}
174- */
175- getCallDataSize () {
176- this.takeGas(2)
177-
178- return this.kernel.environment.callData.length
179- }
180-
181- /**
182- * Copys the input data in current environment to memory. This pertains to
183- * the input data passed with the message call instruction or transaction.
184- * @param {integer} offset the offset in memory to load into
185- * @param {integer} dataOffset the offset in the input data
186- * @param {integer} length the length of data to copy
187- */
188- callDataCopy (offset, dataOffset, length) {
189- this.takeGas(3 + Math.ceil(length / 32) * 3)
190-
191- if (length) {
192- const callData = this.kernel.environment.callData.slice(dataOffset, dataOffset + length)
193- this.setMemory(offset, length, callData)
194- }
195- }
196-
197- /**
198- * Copys the input data in current environment to memory. This pertains to
199- * the input data passed with the message call instruction or transaction.
200- * @param {integer} offset the offset in memory to load into
201- * @param {integer} dataOffset the offset in the input data
202- */
203- callDataCopy256 (offset, dataOffset) {
204- this.takeGas(3)
205- const callData = this.kernel.environment.callData.slice(dataOffset, dataOffset + 32)
206- this.setMemory(offset, U256_SIZE_BYTES, callData)
207- }
208-
209- /**
210- * Gets the size of code running in current environment.
211- * @return {interger}
212- */
213- getCodeSize (cbIndex) {
214- this.takeGas(2)
215-
216- const opPromise = this.kernel.environment.state
217- .get('code')
218- .then(vertex => vertex.value.length)
219-
220- // wait for all the prevouse async ops to finish before running the callback
221- this.kernel.pushOpsQueue(opPromise, cbIndex, length => length)
222- }
223-
224- /**
225- * Copys the code running in current environment to memory.
226- * @param {integer} offset the memory offset
227- * @param {integer} codeOffset the code offset
228- * @param {integer} length the length of code to copy
229- */
230- codeCopy (resultOffset, codeOffset, length, cbIndex) {
231- this.takeGas(3 + Math.ceil(length / 32) * 3)
232-
233- let opPromise
234-
235- if (length) {
236- opPromise = this.kernel.environment.state
237- .get('code')
238- .then(vertex => vertex.value)
239- } else {
240- opPromise = Promise.resolve([])
241- }
242-
243- // wait for all the prevouse async ops to finish before running the callback
244- this.kernel.pushOpsQueue(opPromise, cbIndex, code => {
245- if (code.length) {
246- code = code.slice(codeOffset, codeOffset + length)
247- this.setMemory(resultOffset, length, code)
248- }
249- })
250- }
251-
252- /**
253- * Get size of an account’s code.
254- * @param {integer} addressOffset the offset in memory to load the address from
255- * @return {integer}
256- */
257- getExternalCodeSize (addressOffset, cbOffset) {
258- this.takeGas(20)
259- const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code']
260- const opPromise = this.kernel.environment.state.root
261- .get(address)
262- .then(vertex => vertex.value.length)
263- .catch(() => 0)
264-
265- // wait for all the prevouse async ops to finish before running the callback
266- this.kernel.pushOpsQueue(opPromise, cbOffset, length => length)
267- }
268-
269- /**
270- * Copys the code of an account to memory.
271- * @param {integer} addressOffset the memory offset of the address
272- * @param {integer} resultOffset the memory offset
273- * @param {integer} codeOffset the code offset
274- * @param {integer} length the length of code to copy
275- */
276- externalCodeCopy (addressOffset, resultOffset, codeOffset, length, cbIndex) {
277- this.takeGas(20 + Math.ceil(length / 32) * 3)
278-
279- const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code']
280- let opPromise
281-
282- if (length) {
283- opPromise = this.kernel.environment.state.root
284- .get(address)
285- .then(vertex => vertex.value)
286- .catch(() => [])
287- } else {
288- opPromise = Promise.resolve([])
289- }
290-
291- // wait for all the prevouse async ops to finish before running the callback
292- this.kernel.pushOpsQueue(opPromise, cbIndex, code => {
293- if (code.length) {
294- code = code.slice(codeOffset, codeOffset + length)
295- this.setMemory(resultOffset, length, code)
296- }
297- })
298- }
299-
300- /**
301- * Gets price of gas in current environment.
302- * @return {integer}
303- */
304- getTxGasPrice () {
305- this.takeGas(2)
306-
307- return this.kernel.environment.gasPrice
308- }
309-
310- /**
311- * Gets the hash of one of the 256 most recent complete blocks.
312- * @param {integer} number which block to load
313- * @param {integer} offset the offset to load the hash into
314- */
315- getBlockHash (number, offset, cbOffset) {
316- this.takeGas(20)
317-
318- const diff = this.kernel.environment.block.number - number
319- let opPromise
320-
321- if (diff > 256 || diff <= 0) {
322- opPromise = Promise.resolve(new U256(0))
323- } else {
324- opPromise = this.kernel.environment.getBlockHash(number)
325- }
326-
327- // wait for all the prevouse async ops to finish before running the callback
328- this.kernel.pushOpsQueue(opPromise, cbOffset, hash => {
329- this.setMemory(offset, U256_SIZE_BYTES, hash.toMemory())
330- })
331- }
332-
333- /**
334- * Gets the block’s beneficiary address and loads into memory.
335- * @param offset
336- */
337- getBlockCoinbase (offset) {
338- this.takeGas(2)
339-
340- this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.coinbase.toMemory())
341- }
342-
343- /**
344- * Get the block’s timestamp.
345- * @return {integer}
346- */
347- getBlockTimestamp () {
348- this.takeGas(2)
349-
350- return this.kernel.environment.block.timestamp
351- }
352-
353- /**
354- * Get the block’s number.
355- * @return {integer}
356- */
357- getBlockNumber () {
358- this.takeGas(2)
359-
360- return this.kernel.environment.block.number
361- }
362-
363- /**
364- * Get the block’s difficulty.
365- * @return {integer}
366- */
367- getBlockDifficulty (offset) {
368- this.takeGas(2)
369-
370- this.setMemory(offset, U256_SIZE_BYTES, this.kernel.environment.block.difficulty.toMemory())
371- }
372-
373- /**
374- * Get the block’s gas limit.
375- * @return {integer}
376- */
377- getBlockGasLimit () {
378- this.takeGas(2)
379-
380- return this.kernel.environment.block.gasLimit
381- }
382-
383- /**
384- * Creates a new log in the current environment
385- * @param {integer} dataOffset the offset in memory to load the memory
386- * @param {integer} length the data length
387- * @param {integer} number of topics
388- */
389- log (dataOffset, length, numberOfTopics, topic1, topic2, topic3, topic4) {
390- if (numberOfTopics < 0 || numberOfTopics > 4) {
391- throw new Error('Invalid numberOfTopics')
392- }
393-
394- this.takeGas(375 + length * 8 + numberOfTopics * 375)
395-
396- const data = length ? this.getMemory(dataOffset, length).slice(0) : new Uint8Array([])
397- const topics = []
398-
399- if (numberOfTopics > 0) {
400- topics.push(U256.fromMemory(this.getMemory(topic1, U256_SIZE_BYTES)))
401- }
402-
403- if (numberOfTopics > 1) {
404- topics.push(U256.fromMemory(this.getMemory(topic2, U256_SIZE_BYTES)))
405- }
406-
407- if (numberOfTopics > 2) {
408- topics.push(U256.fromMemory(this.getMemory(topic3, U256_SIZE_BYTES)))
409- }
410-
411- if (numberOfTopics > 3) {
412- topics.push(U256.fromMemory(this.getMemory(topic4, U256_SIZE_BYTES)))
413- }
414-
415- this.kernel.environment.logs.push({
416- data: data,
417- topics: topics
418- })
419- }
420-
421- /**
422- * Creates a new contract with a given value.
423- * @param {integer} valueOffset the offset in memory to the value from
424- * @param {integer} dataOffset the offset to load the code for the new contract from
425- * @param {integer} length the data length
426- * @param (integer} resultOffset the offset to write the new contract address to
427- * @return {integer} Return 1 or 0 depending on if the VM trapped on the message or not
428- */
429- create (valueOffset, dataOffset, length, resultOffset, cbIndex) {
430- this.takeGas(32000)
431-
432- const value = U256.fromMemory(this.getMemory(valueOffset, U128_SIZE_BYTES))
433- // if (length) {
434- // const code = this.getMemory(dataOffset, length).slice(0)
435- // }
436-
437- let opPromise
438-
439- if (value.gt(this.kernel.environment.value)) {
440- opPromise = Promise.resolve(new Buffer(20).fill(0))
441- } else {
442- // todo actully run the code
443- opPromise = Promise.resolve(ethUtil.generateAddress(this.kernel.environment.address, this.kernel.environment.nonce))
444- }
445-
446- // wait for all the prevouse async ops to finish before running the callback
447- this.kernel.pushOpsQueue(opPromise, cbIndex, address => {
448- this.setMemory(resultOffset, ADDRESS_SIZE_BYTES, address)
449- })
450- }
451-
452- /**
453- * Sends a message with arbiatary data to a given address path
454- * @param {integer} addressOffset the offset to load the address path from
455- * @param {integer} valueOffset the offset to load the value from
456- * @param {integer} dataOffset the offset to load data from
457- * @param {integer} dataLength the length of data
458- * @param {integer} resultOffset the offset to store the result data at
459- * @param {integer} resultLength
460- * @param {integer} gas
461- * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not
462- */
463- _call (gasHigh, gasLow, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength, cbIndex) {
464- this.takeGas(40)
465-
466- const gas = from64bit(gasHigh, gasLow)
467- // Load the params from mem
468- const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)]
469- const value = new U256(this.getMemory(valueOffset, U128_SIZE_BYTES))
470-
471- // Special case for non-zero value; why does this exist?
472- if (!value.isZero()) {
473- this.takeGas(9000 - 2300 + gas)
474- this.takeGas(-gas)
475- }
476-
477- let opPromise = this.kernel.environment.state.root.get(address)
478- .catch(() => {
479- // why does this exist?
480- this.takeGas(25000)
481- })
482-
483- // wait for all the prevouse async ops to finish before running the callback
484- this.kernel.pushOpsQueue(opPromise, cbIndex, () => {
485- return 1
486- })
487- }
488-
489- /**
490- * Message-call into this account with an alternative account’s code.
491- * @param {integer} addressOffset the offset to load the address path from
492- * @param {integer} valueOffset the offset to load the value from
493- * @param {integer} dataOffset the offset to load data from
494- * @param {integer} dataLength the length of data
495- * @param {integer} resultOffset the offset to store the result data at
496- * @param {integer} resultLength
497- * @param {integer} gas
498- * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not
499- */
500- callCode (gas, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength, cbIndex) {
501- this.takeGas(40)
502- // Load the params from mem
503- const path = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code']
504- const value = U256.fromMemory(this.getMemory(valueOffset, U128_SIZE_BYTES))
505-
506- // Special case for non-zero value; why does this exist?
507- if (!value.isZero()) {
508- this.takeGas(6700)
509- }
510-
511- // TODO: should be message?
512- const opPromise = this.kernel.environment.state.root.get(path)
513- .catch(() => {
514- // TODO: handle errors
515- // the value was not found
516- return null
517- })
518-
519- this.kernel.pushOpsQueue(opPromise, cbIndex, oldValue => {
520- return 1
521- })
522- }
523-
524- /**
525- * Message-call into this account with an alternative account’s code, but
526- * persisting the current values for sender and value.
527- * @param {integer} gas
528- * @param {integer} addressOffset the offset to load the address path from
529- * @param {integer} valueOffset the offset to load the value from
530- * @param {integer} dataOffset the offset to load data from
531- * @param {integer} dataLength the length of data
532- * @param {integer} resultOffset the offset to store the result data at
533- * @param {integer} resultLength
534- * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not
535- */
536- callDelegate (gas, addressOffset, dataOffset, dataLength, resultOffset, resultLength) {
537- // FIXME: count properly
538- this.takeGas(40)
539-
540- const data = this.getMemory(dataOffset, dataLength).slice(0)
541- const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)]
542- const [errorCode, result] = this.environment.callDelegate(gas, address, data)
543- this.setMemory(resultOffset, resultLength, result)
544- return errorCode
545- }
546-
547- /**
548- * store a value at a given path in long term storage which are both loaded
549- * from Memory
550- * @param {interger} pathOffest the memory offset to load the the path from
551- * @param {interger} valueOffset the memory offset to load the value from
552- */
553- storageStore (pathOffset, valueOffset, cbIndex) {
554- this.takeGas(5000)
555- const path = ['storage', ...this.getMemory(pathOffset, U256_SIZE_BYTES)]
556- // copy the value
557- const value = this.getMemory(valueOffset, U256_SIZE_BYTES).slice(0)
558- const valIsZero = value.every((i) => i === 0)
559- const opPromise = this.kernel.environment.state.get(path)
560- .then(vertex => vertex.value)
561- .catch(() => null)
562-
563- this.kernel.pushOpsQueue(opPromise, cbIndex, oldValue => {
564- if (valIsZero && oldValue) {
565- // delete a value
566- this.kernel.environment.gasRefund += 15000
567- this.kernel.environment.state.del(path)
568- } else {
569- if (!valIsZero && !oldValue) {
570- // creating a new value
571- this.takeGas(15000)
572- }
573- // update
574- this.kernel.environment.state.set(path, new Vertex({
575- value: value
576- }))
577- }
578- })
579- }
580-
581- /**
582- * reterives a value at a given path in long term storage
583- * @param {interger} pathOffest the memory offset to load the the path from
584- * @param {interger} resultOffset the memory offset to load the value from
585- */
586- storageLoad (pathOffset, resultOffset, cbIndex) {
587- this.takeGas(50)
588-
589- // convert the path to an array
590- const path = ['storage', ...this.getMemory(pathOffset, U256_SIZE_BYTES)]
591- // get the value from the state
592- const opPromise = this.kernel.environment.state.get(path)
593- .then(vertex => vertex.value)
594- .catch(() => new Uint8Array(32))
595-
596- this.kernel.pushOpsQueue(opPromise, cbIndex, value => {
597- this.setMemory(resultOffset, U256_SIZE_BYTES, value)
598- })
599- }
600-
601- /**
602- * Halt execution returning output data.
603- * @param {integer} offset the offset of the output data.
604- * @param {integer} length the length of the output data.
605- */
606- return (offset, length) {
607- if (length) {
608- this.kernel.environment.returnValue = this.getMemory(offset, length).slice(0)
609- }
610- }
611-
612- /**
613- * Halt execution and register account for later deletion giving the remaining
614- * balance to an address path
615- * @param {integer} offset the offset to load the address from
616- */
617- selfDestruct (addressOffset) {
618- this.kernel.environment.selfDestruct = true
619- this.kernel.environment.selfDestructAddress = this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)
620- this.kernel.environment.gasRefund += 24000
621- }
622-
623- getMemory (offset, length) {
624- return new Uint8Array(this.kernel.memory, offset, length)
625- }
626-
627- setMemory (offset, length, value) {
628- const memory = new Uint8Array(this.kernel.memory, offset, length)
629- memory.set(value)
630- }
631-
632- /*
633- * Takes gas from the tank. Only needs to check if there's gas left to be taken,
634- * because every caller of this method is trusted.
635- */
636- takeGas (amount) {
637- if (this.kernel.environment.gasLeft < amount) {
638- throw new Error('Ran out of gas')
639- }
640- this.kernel.environment.gasLeft -= amount
641- }
642-}
643-
644-// converts a 64 bit number to a JS number
645-function from64bit (high, low) {
646- if (high < 0) {
647- // convert from a 32-bit two's compliment
648- high = 0x100000000 - high
649- }
650- if (low < 0) {
651- // convert from a 32-bit two's compliment
652- low = 0x100000000 - low
653- }
654- // JS only bitshift 32bits, so instead of high << 32 we have high * 2 ^ 32
655- return (high * 4294967296) + low
656-}
EVMimports.jsView
@@ -1,0 +1,656 @@
1+/**
2+ * This is the Ethereum interface that is exposed to the WASM instance which
3+ * enables to interact with the Ethereum Environment
4+ */
5+const fs = require('fs')
6+const path = require('path')
7+const ethUtil = require('ethereumjs-util')
8+const Vertex = require('merkle-trie')
9+const U256 = require('./deps/u256.js')
10+
11+const U128_SIZE_BYTES = 16
12+const ADDRESS_SIZE_BYTES = 20
13+const U256_SIZE_BYTES = 32
14+
15+// The interface exposed to the WebAessembly Core
16+module.exports = class Interface {
17+ constructor (kernel) {
18+ this.kernel = kernel
19+ const shimBin = fs.readFileSync(path.join(__dirname, '/wasm/interface.wasm'))
20+ const shimMod = WebAssembly.Module(shimBin)
21+ this.shims = WebAssembly.Instance(shimMod, {
22+ 'interface': {
23+ 'useGas': this._useGas.bind(this),
24+ 'getGasLeftHigh': this._getGasLeftHigh.bind(this),
25+ 'getGasLeftLow': this._getGasLeftLow.bind(this),
26+ 'call': this._call.bind(this)
27+ }
28+ })
29+ }
30+
31+ static get name () {
32+ return 'ethereum'
33+ }
34+
35+ get exports () {
36+ let exportMethods = [
37+ // include all the public methods according to the Ethereum Environment Interface (EEI) r1
38+ 'getAddress',
39+ 'getBalance',
40+ 'getTxOrigin',
41+ 'getCaller',
42+ 'getCallValue',
43+ 'getCallDataSize',
44+ 'callDataCopy',
45+ 'callDataCopy256',
46+ 'getCodeSize',
47+ 'codeCopy',
48+ 'getExternalCodeSize',
49+ 'externalCodeCopy',
50+ 'getTxGasPrice',
51+ 'getBlockHash',
52+ 'getBlockCoinbase',
53+ 'getBlockTimestamp',
54+ 'getBlockNumber',
55+ 'getBlockDifficulty',
56+ 'getBlockGasLimit',
57+ 'log',
58+ 'create',
59+ 'callCode',
60+ 'callDelegate',
61+ 'storageStore',
62+ 'storageLoad',
63+ 'return',
64+ 'selfDestruct'
65+ ]
66+ let ret = {}
67+ exportMethods.forEach((method) => {
68+ ret[method] = this[method].bind(this)
69+ })
70+
71+ // add shims
72+ ret.useGas = this.shims.exports.useGas
73+ ret.getGasLeft = this.shims.exports.getGasLeft
74+ ret.call = this.shims.exports.call
75+ return ret
76+ }
77+
78+ setModule (mod) {
79+ this.module = mod
80+ }
81+
82+ /**
83+ * Subtracts an amount to the gas counter
84+ * @param {integer} amount the amount to subtract to the gas counter
85+ */
86+ _useGas (high, low) {
87+ this.takeGas(from64bit(high, low))
88+ }
89+
90+ /**
91+ * Returns the current amount of gas
92+ * @return {integer}
93+ */
94+ _getGasLeftHigh () {
95+ return Math.floor(this.kernel.environment.gasLeft / 4294967296)
96+ }
97+
98+ /**
99+ * Returns the current amount of gas
100+ * @return {integer}
101+ */
102+ _getGasLeftLow () {
103+ return this.kernel.environment.gasLeft
104+ }
105+
106+ /**
107+ * Gets address of currently executing account and loads it into memory at
108+ * the given offset.
109+ * @param {integer} offset
110+ */
111+ getAddress (offset) {
112+ this.takeGas(2)
113+
114+ this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.address.toMemory())
115+ }
116+
117+ /**
118+ * Gets balance of the given account and loads it into memory at the given
119+ * offset.
120+ * @param {integer} addressOffset the memory offset to laod the address
121+ * @param {integer} resultOffset
122+ */
123+ getBalance (addressOffset, offset, cbIndex) {
124+ this.takeGas(20)
125+
126+ const path = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'balance']
127+ const opPromise = this.kernel.environment.state.root.get(path)
128+ .then(vertex => new U256(vertex.value))
129+ .catch(() => new U256(0))
130+
131+ this.kernel.pushOpsQueue(opPromise, cbIndex, balance => {
132+ this.setMemory(offset, U128_SIZE_BYTES, balance.toMemory(U128_SIZE_BYTES))
133+ })
134+ }
135+
136+ /**
137+ * Gets the execution's origination address and loads it into memory at the
138+ * given offset. This is the sender of original transaction; it is never an
139+ * account with non-empty associated code.
140+ * @param {integer} offset
141+ */
142+ getTxOrigin (offset) {
143+ this.takeGas(2)
144+
145+ this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.origin.toMemory())
146+ }
147+
148+ /**
149+ * Gets caller address and loads it into memory at the given offset. This is
150+ * the address of the account that is directly responsible for this execution.
151+ * @param {integer} offset
152+ */
153+ getCaller (offset) {
154+ this.takeGas(2)
155+
156+ this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.caller.toMemory())
157+ }
158+
159+ /**
160+ * Gets the deposited value by the instruction/transaction responsible for
161+ * this execution and loads it into memory at the given location.
162+ * @param {integer} offset
163+ */
164+ getCallValue (offset) {
165+ this.takeGas(2)
166+
167+ this.setMemory(offset, U128_SIZE_BYTES, this.kernel.environment.callValue.toMemory(U128_SIZE_BYTES))
168+ }
169+
170+ /**
171+ * Get size of input data in current environment. This pertains to the input
172+ * data passed with the message call instruction or transaction.
173+ * @return {integer}
174+ */
175+ getCallDataSize () {
176+ this.takeGas(2)
177+
178+ return this.kernel.environment.callData.length
179+ }
180+
181+ /**
182+ * Copys the input data in current environment to memory. This pertains to
183+ * the input data passed with the message call instruction or transaction.
184+ * @param {integer} offset the offset in memory to load into
185+ * @param {integer} dataOffset the offset in the input data
186+ * @param {integer} length the length of data to copy
187+ */
188+ callDataCopy (offset, dataOffset, length) {
189+ this.takeGas(3 + Math.ceil(length / 32) * 3)
190+
191+ if (length) {
192+ const callData = this.kernel.environment.callData.slice(dataOffset, dataOffset + length)
193+ this.setMemory(offset, length, callData)
194+ }
195+ }
196+
197+ /**
198+ * Copys the input data in current environment to memory. This pertains to
199+ * the input data passed with the message call instruction or transaction.
200+ * @param {integer} offset the offset in memory to load into
201+ * @param {integer} dataOffset the offset in the input data
202+ */
203+ callDataCopy256 (offset, dataOffset) {
204+ this.takeGas(3)
205+ const callData = this.kernel.environment.callData.slice(dataOffset, dataOffset + 32)
206+ this.setMemory(offset, U256_SIZE_BYTES, callData)
207+ }
208+
209+ /**
210+ * Gets the size of code running in current environment.
211+ * @return {interger}
212+ */
213+ getCodeSize (cbIndex) {
214+ this.takeGas(2)
215+
216+ const opPromise = this.kernel.environment.state
217+ .get('code')
218+ .then(vertex => vertex.value.length)
219+
220+ // wait for all the prevouse async ops to finish before running the callback
221+ this.kernel.pushOpsQueue(opPromise, cbIndex, length => length)
222+ }
223+
224+ /**
225+ * Copys the code running in current environment to memory.
226+ * @param {integer} offset the memory offset
227+ * @param {integer} codeOffset the code offset
228+ * @param {integer} length the length of code to copy
229+ */
230+ codeCopy (resultOffset, codeOffset, length, cbIndex) {
231+ this.takeGas(3 + Math.ceil(length / 32) * 3)
232+
233+ let opPromise
234+
235+ if (length) {
236+ opPromise = this.kernel.environment.state
237+ .get('code')
238+ .then(vertex => vertex.value)
239+ } else {
240+ opPromise = Promise.resolve([])
241+ }
242+
243+ // wait for all the prevouse async ops to finish before running the callback
244+ this.kernel.pushOpsQueue(opPromise, cbIndex, code => {
245+ if (code.length) {
246+ code = code.slice(codeOffset, codeOffset + length)
247+ this.setMemory(resultOffset, length, code)
248+ }
249+ })
250+ }
251+
252+ /**
253+ * Get size of an account’s code.
254+ * @param {integer} addressOffset the offset in memory to load the address from
255+ * @return {integer}
256+ */
257+ getExternalCodeSize (addressOffset, cbOffset) {
258+ this.takeGas(20)
259+ const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code']
260+ const opPromise = this.kernel.environment.state.root
261+ .get(address)
262+ .then(vertex => vertex.value.length)
263+ .catch(() => 0)
264+
265+ // wait for all the prevouse async ops to finish before running the callback
266+ this.kernel.pushOpsQueue(opPromise, cbOffset, length => length)
267+ }
268+
269+ /**
270+ * Copys the code of an account to memory.
271+ * @param {integer} addressOffset the memory offset of the address
272+ * @param {integer} resultOffset the memory offset
273+ * @param {integer} codeOffset the code offset
274+ * @param {integer} length the length of code to copy
275+ */
276+ externalCodeCopy (addressOffset, resultOffset, codeOffset, length, cbIndex) {
277+ this.takeGas(20 + Math.ceil(length / 32) * 3)
278+
279+ const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code']
280+ let opPromise
281+
282+ if (length) {
283+ opPromise = this.kernel.environment.state.root
284+ .get(address)
285+ .then(vertex => vertex.value)
286+ .catch(() => [])
287+ } else {
288+ opPromise = Promise.resolve([])
289+ }
290+
291+ // wait for all the prevouse async ops to finish before running the callback
292+ this.kernel.pushOpsQueue(opPromise, cbIndex, code => {
293+ if (code.length) {
294+ code = code.slice(codeOffset, codeOffset + length)
295+ this.setMemory(resultOffset, length, code)
296+ }
297+ })
298+ }
299+
300+ /**
301+ * Gets price of gas in current environment.
302+ * @return {integer}
303+ */
304+ getTxGasPrice () {
305+ this.takeGas(2)
306+
307+ return this.kernel.environment.gasPrice
308+ }
309+
310+ /**
311+ * Gets the hash of one of the 256 most recent complete blocks.
312+ * @param {integer} number which block to load
313+ * @param {integer} offset the offset to load the hash into
314+ */
315+ getBlockHash (number, offset, cbOffset) {
316+ this.takeGas(20)
317+
318+ const diff = this.kernel.environment.block.number - number
319+ let opPromise
320+
321+ if (diff > 256 || diff <= 0) {
322+ opPromise = Promise.resolve(new U256(0))
323+ } else {
324+ opPromise = this.kernel.environment.getBlockHash(number)
325+ }
326+
327+ // wait for all the prevouse async ops to finish before running the callback
328+ this.kernel.pushOpsQueue(opPromise, cbOffset, hash => {
329+ this.setMemory(offset, U256_SIZE_BYTES, hash.toMemory())
330+ })
331+ }
332+
333+ /**
334+ * Gets the block’s beneficiary address and loads into memory.
335+ * @param offset
336+ */
337+ getBlockCoinbase (offset) {
338+ this.takeGas(2)
339+
340+ this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.coinbase.toMemory())
341+ }
342+
343+ /**
344+ * Get the block’s timestamp.
345+ * @return {integer}
346+ */
347+ getBlockTimestamp () {
348+ this.takeGas(2)
349+
350+ return this.kernel.environment.block.timestamp
351+ }
352+
353+ /**
354+ * Get the block’s number.
355+ * @return {integer}
356+ */
357+ getBlockNumber () {
358+ this.takeGas(2)
359+
360+ return this.kernel.environment.block.number
361+ }
362+
363+ /**
364+ * Get the block’s difficulty.
365+ * @return {integer}
366+ */
367+ getBlockDifficulty (offset) {
368+ this.takeGas(2)
369+
370+ this.setMemory(offset, U256_SIZE_BYTES, this.kernel.environment.block.difficulty.toMemory())
371+ }
372+
373+ /**
374+ * Get the block’s gas limit.
375+ * @return {integer}
376+ */
377+ getBlockGasLimit () {
378+ this.takeGas(2)
379+
380+ return this.kernel.environment.block.gasLimit
381+ }
382+
383+ /**
384+ * Creates a new log in the current environment
385+ * @param {integer} dataOffset the offset in memory to load the memory
386+ * @param {integer} length the data length
387+ * @param {integer} number of topics
388+ */
389+ log (dataOffset, length, numberOfTopics, topic1, topic2, topic3, topic4) {
390+ if (numberOfTopics < 0 || numberOfTopics > 4) {
391+ throw new Error('Invalid numberOfTopics')
392+ }
393+
394+ this.takeGas(375 + length * 8 + numberOfTopics * 375)
395+
396+ const data = length ? this.getMemory(dataOffset, length).slice(0) : new Uint8Array([])
397+ const topics = []
398+
399+ if (numberOfTopics > 0) {
400+ topics.push(U256.fromMemory(this.getMemory(topic1, U256_SIZE_BYTES)))
401+ }
402+
403+ if (numberOfTopics > 1) {
404+ topics.push(U256.fromMemory(this.getMemory(topic2, U256_SIZE_BYTES)))
405+ }
406+
407+ if (numberOfTopics > 2) {
408+ topics.push(U256.fromMemory(this.getMemory(topic3, U256_SIZE_BYTES)))
409+ }
410+
411+ if (numberOfTopics > 3) {
412+ topics.push(U256.fromMemory(this.getMemory(topic4, U256_SIZE_BYTES)))
413+ }
414+
415+ this.kernel.environment.logs.push({
416+ data: data,
417+ topics: topics
418+ })
419+ }
420+
421+ /**
422+ * Creates a new contract with a given value.
423+ * @param {integer} valueOffset the offset in memory to the value from
424+ * @param {integer} dataOffset the offset to load the code for the new contract from
425+ * @param {integer} length the data length
426+ * @param (integer} resultOffset the offset to write the new contract address to
427+ * @return {integer} Return 1 or 0 depending on if the VM trapped on the message or not
428+ */
429+ create (valueOffset, dataOffset, length, resultOffset, cbIndex) {
430+ this.takeGas(32000)
431+
432+ const value = U256.fromMemory(this.getMemory(valueOffset, U128_SIZE_BYTES))
433+ // if (length) {
434+ // const code = this.getMemory(dataOffset, length).slice(0)
435+ // }
436+
437+ let opPromise
438+
439+ if (value.gt(this.kernel.environment.value)) {
440+ opPromise = Promise.resolve(new Buffer(20).fill(0))
441+ } else {
442+ // todo actully run the code
443+ opPromise = Promise.resolve(ethUtil.generateAddress(this.kernel.environment.address, this.kernel.environment.nonce))
444+ }
445+
446+ // wait for all the prevouse async ops to finish before running the callback
447+ this.kernel.pushOpsQueue(opPromise, cbIndex, address => {
448+ this.setMemory(resultOffset, ADDRESS_SIZE_BYTES, address)
449+ })
450+ }
451+
452+ /**
453+ * Sends a message with arbiatary data to a given address path
454+ * @param {integer} addressOffset the offset to load the address path from
455+ * @param {integer} valueOffset the offset to load the value from
456+ * @param {integer} dataOffset the offset to load data from
457+ * @param {integer} dataLength the length of data
458+ * @param {integer} resultOffset the offset to store the result data at
459+ * @param {integer} resultLength
460+ * @param {integer} gas
461+ * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not
462+ */
463+ _call (gasHigh, gasLow, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength, cbIndex) {
464+ this.takeGas(40)
465+
466+ const gas = from64bit(gasHigh, gasLow)
467+ // Load the params from mem
468+ const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)]
469+ const value = new U256(this.getMemory(valueOffset, U128_SIZE_BYTES))
470+
471+ // Special case for non-zero value; why does this exist?
472+ if (!value.isZero()) {
473+ this.takeGas(9000 - 2300 + gas)
474+ this.takeGas(-gas)
475+ }
476+
477+ let opPromise = this.kernel.environment.state.root.get(address)
478+ .catch(() => {
479+ // why does this exist?
480+ this.takeGas(25000)
481+ })
482+
483+ // wait for all the prevouse async ops to finish before running the callback
484+ this.kernel.pushOpsQueue(opPromise, cbIndex, () => {
485+ return 1
486+ })
487+ }
488+
489+ /**
490+ * Message-call into this account with an alternative account’s code.
491+ * @param {integer} addressOffset the offset to load the address path from
492+ * @param {integer} valueOffset the offset to load the value from
493+ * @param {integer} dataOffset the offset to load data from
494+ * @param {integer} dataLength the length of data
495+ * @param {integer} resultOffset the offset to store the result data at
496+ * @param {integer} resultLength
497+ * @param {integer} gas
498+ * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not
499+ */
500+ callCode (gas, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength, cbIndex) {
501+ this.takeGas(40)
502+ // Load the params from mem
503+ const path = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code']
504+ const value = U256.fromMemory(this.getMemory(valueOffset, U128_SIZE_BYTES))
505+
506+ // Special case for non-zero value; why does this exist?
507+ if (!value.isZero()) {
508+ this.takeGas(6700)
509+ }
510+
511+ // TODO: should be message?
512+ const opPromise = this.kernel.environment.state.root.get(path)
513+ .catch(() => {
514+ // TODO: handle errors
515+ // the value was not found
516+ return null
517+ })
518+
519+ this.kernel.pushOpsQueue(opPromise, cbIndex, oldValue => {
520+ return 1
521+ })
522+ }
523+
524+ /**
525+ * Message-call into this account with an alternative account’s code, but
526+ * persisting the current values for sender and value.
527+ * @param {integer} gas
528+ * @param {integer} addressOffset the offset to load the address path from
529+ * @param {integer} valueOffset the offset to load the value from
530+ * @param {integer} dataOffset the offset to load data from
531+ * @param {integer} dataLength the length of data
532+ * @param {integer} resultOffset the offset to store the result data at
533+ * @param {integer} resultLength
534+ * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not
535+ */
536+ callDelegate (gas, addressOffset, dataOffset, dataLength, resultOffset, resultLength) {
537+ // FIXME: count properly
538+ this.takeGas(40)
539+
540+ const data = this.getMemory(dataOffset, dataLength).slice(0)
541+ const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)]
542+ const [errorCode, result] = this.environment.callDelegate(gas, address, data)
543+ this.setMemory(resultOffset, resultLength, result)
544+ return errorCode
545+ }
546+
547+ /**
548+ * store a value at a given path in long term storage which are both loaded
549+ * from Memory
550+ * @param {interger} pathOffest the memory offset to load the the path from
551+ * @param {interger} valueOffset the memory offset to load the value from
552+ */
553+ storageStore (pathOffset, valueOffset, cbIndex) {
554+ this.takeGas(5000)
555+ const path = ['storage', ...this.getMemory(pathOffset, U256_SIZE_BYTES)]
556+ // copy the value
557+ const value = this.getMemory(valueOffset, U256_SIZE_BYTES).slice(0)
558+ const valIsZero = value.every((i) => i === 0)
559+ const opPromise = this.kernel.environment.state.get(path)
560+ .then(vertex => vertex.value)
561+ .catch(() => null)
562+
563+ this.kernel.pushOpsQueue(opPromise, cbIndex, oldValue => {
564+ if (valIsZero && oldValue) {
565+ // delete a value
566+ this.kernel.environment.gasRefund += 15000
567+ this.kernel.environment.state.del(path)
568+ } else {
569+ if (!valIsZero && !oldValue) {
570+ // creating a new value
571+ this.takeGas(15000)
572+ }
573+ // update
574+ this.kernel.environment.state.set(path, new Vertex({
575+ value: value
576+ }))
577+ }
578+ })
579+ }
580+
581+ /**
582+ * reterives a value at a given path in long term storage
583+ * @param {interger} pathOffest the memory offset to load the the path from
584+ * @param {interger} resultOffset the memory offset to load the value from
585+ */
586+ storageLoad (pathOffset, resultOffset, cbIndex) {
587+ this.takeGas(50)
588+
589+ // convert the path to an array
590+ const path = ['storage', ...this.getMemory(pathOffset, U256_SIZE_BYTES)]
591+ // get the value from the state
592+ const opPromise = this.kernel.environment.state.get(path)
593+ .then(vertex => vertex.value)
594+ .catch(() => new Uint8Array(32))
595+
596+ this.kernel.pushOpsQueue(opPromise, cbIndex, value => {
597+ this.setMemory(resultOffset, U256_SIZE_BYTES, value)
598+ })
599+ }
600+
601+ /**
602+ * Halt execution returning output data.
603+ * @param {integer} offset the offset of the output data.
604+ * @param {integer} length the length of the output data.
605+ */
606+ return (offset, length) {
607+ if (length) {
608+ this.kernel.environment.returnValue = this.getMemory(offset, length).slice(0)
609+ }
610+ }
611+
612+ /**
613+ * Halt execution and register account for later deletion giving the remaining
614+ * balance to an address path
615+ * @param {integer} offset the offset to load the address from
616+ */
617+ selfDestruct (addressOffset) {
618+ this.kernel.environment.selfDestruct = true
619+ this.kernel.environment.selfDestructAddress = this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)
620+ this.kernel.environment.gasRefund += 24000
621+ }
622+
623+ getMemory (offset, length) {
624+ return new Uint8Array(this.kernel.memory, offset, length)
625+ }
626+
627+ setMemory (offset, length, value) {
628+ const memory = new Uint8Array(this.kernel.memory, offset, length)
629+ memory.set(value)
630+ }
631+
632+ /*
633+ * Takes gas from the tank. Only needs to check if there's gas left to be taken,
634+ * because every caller of this method is trusted.
635+ */
636+ takeGas (amount) {
637+ if (this.kernel.environment.gasLeft < amount) {
638+ throw new Error('Ran out of gas')
639+ }
640+ this.kernel.environment.gasLeft -= amount
641+ }
642+}
643+
644+// converts a 64 bit number to a JS number
645+function from64bit (high, low) {
646+ if (high < 0) {
647+ // convert from a 32-bit two's compliment
648+ high = 0x100000000 - high
649+ }
650+ if (low < 0) {
651+ // convert from a 32-bit two's compliment
652+ low = 0x100000000 - low
653+ }
654+ // JS only bitshift 32bits, so instead of high << 32 we have high * 2 ^ 32
655+ return (high * 4294967296) + low
656+}
interfaceAPI.jsView
@@ -1,55 +1,0 @@
1-module.exports = class interfaceAPI {
2- /**
3- * The interface API is the api the exposed to interfaces. All queries about
4- * the enviroment and call to the kernel go through this API
5- */
6- constructor (code) {
7- this._module = WebAssembly.Module(code)
8- }
9-
10- /**
11- * Runs the core VM with a given environment and imports
12- */
13- run (environment, imports) {
14- this._environment = environment
15- // TODO, delete the instance once done.
16- const instance = this._instance = WebAssembly.Instance(this._module, imports)
17- if (instance.exports.main) {
18- instance.exports.main()
19- }
20- return this.onDone()
21- }
22-
23- /**
24- * returns a promise that resolves when the wasm instance is done running
25- */
26- async onDone () {
27- let prevOps
28- while (prevOps !== this._opsQueue) {
29- prevOps = this._opsQueue
30- await this._opsQueue
31- }
32- }
33-
34- /**
35- * addes an aync operation to the operations queue
36- */
37- pushOpsQueue (promise, callbackIndex, intefaceCallback) {
38- this._opsQueue = Promise.all([this._opsQueue, promise]).then(values => {
39- const result = intefaceCallback(values.pop())
40- this._instance.exports[callbackIndex.toString()](result)
41- })
42- }
43-
44- sendMessage (message) {
45-
46- }
47-
48- get environment () {
49- return this._environment
50- }
51-
52- get memory () {
53- return this._instance.exports.memory
54- }
55-}
vm.jsView
@@ -1,0 +1,55 @@
1+module.exports = class VM {
2+ /**
3+ * The interface API is the api the exposed to interfaces. All queries about
4+ * the enviroment and call to the kernel go through this API
5+ */
6+ constructor (code) {
7+ this._module = WebAssembly.Module(code)
8+ }
9+
10+ /**
11+ * Runs the core VM with a given environment and imports
12+ */
13+ run (environment, imports) {
14+ this._environment = environment
15+ // TODO, delete the instance once done.
16+ const instance = this._instance = WebAssembly.Instance(this._module, imports)
17+ if (instance.exports.main) {
18+ instance.exports.main()
19+ }
20+ return this.onDone()
21+ }
22+
23+ /**
24+ * returns a promise that resolves when the wasm instance is done running
25+ */
26+ async onDone () {
27+ let prevOps
28+ while (prevOps !== this._opsQueue) {
29+ prevOps = this._opsQueue
30+ await this._opsQueue
31+ }
32+ }
33+
34+ /**
35+ * addes an aync operation to the operations queue
36+ */
37+ pushOpsQueue (promise, callbackIndex, intefaceCallback) {
38+ this._opsQueue = Promise.all([this._opsQueue, promise]).then(values => {
39+ const result = intefaceCallback(values.pop())
40+ this._instance.exports[callbackIndex.toString()](result)
41+ })
42+ }
43+
44+ sendMessage (message) {
45+
46+ }
47+
48+ get environment () {
49+ return this._environment
50+ }
51+
52+ get memory () {
53+ return this._instance.exports.memory
54+ }
55+}

Built with git-ssb-web