git ssb

0+

wanderer🌟 / js-primea-hypervisor



Commit 7052b80b5685d1536cd7530b2a53537607899c9c

made callcode async

wanderer committed on 11/7/2016, 6:17:25 PM
Parent: 8ad1e8d0074a39fe6116a5343888dbb3e75c06d2

Files changed

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

Built with git-ssb-web