Commit 80cc9ff203abb03e4eb39d18d2801f1e2f0e52dd
Merge pull request #54 from ewasm/shim
Shim (WIP)wanderer authored on 10/5/2016, 12:12:39 PM
GitHub committed on 10/5/2016, 12:12:39 PM
Parent: c8cc38c35e9154a3862a3e470612f8c1820401e2
Parent: 6fcf57fa14199694357281d7b29614acd8ab234c
Files changed
index.js | changed |
interface.js | changed |
package.json | changed |
wasm/interface.wasm | added |
wasm/interface.wast | added |
index.js | ||
---|---|---|
@@ -43,10 +43,10 @@ | ||
43 | 43 | // NOTE: it assumes that wasm will raise an exception if something went wrong, |
44 | 44 | // otherwise execution succeeded |
45 | 45 | codeHandler (code, ethInterface = new Interface(new Environment())) { |
46 | 46 | const debugInterface = new DebugInterface(ethInterface.environment) |
47 | - | |
48 | - const instance = Wasm.instantiateModule(code, { | |
47 | + const module = WebAssembly.Module(code) | |
48 | + const imports = { | |
49 | 49 | 'ethereum': ethInterface.exportTable, |
50 | 50 | 'debug': debugInterface.exportTable, |
51 | 51 | |
52 | 52 | // export this for Rust |
@@ -55,10 +55,16 @@ | ||
55 | 55 | |
56 | 56 | // export this for Binaryen |
57 | 57 | // FIXME: remove once C has proper imports, see https://github.com/ethereum/evm2.0-design/issues/16 |
58 | 58 | 'env': ethInterface.exportTable |
59 | - }) | |
59 | + } | |
60 | + // add shims | |
61 | + imports.ethereum.useGas = ethInterface.shims.exports.useGas | |
62 | + imports.ethereum.getGasLeft = ethInterface.shims.exports.getGasLeft | |
63 | + imports.ethereum.call = ethInterface.shims.exports.call | |
60 | 64 | |
65 | + const instance = WebAssembly.Instance(module, imports) | |
66 | + | |
61 | 67 | ethInterface.setModule(instance) |
62 | 68 | debugInterface.setModule(instance) |
63 | 69 | |
64 | 70 | if (instance.exports.main) { |
interface.js | ||
---|---|---|
@@ -3,8 +3,10 @@ | ||
3 | 3 | * enables to interact with the Ethereum Environment |
4 | 4 | */ |
5 | 5 | const Address = require('./address.js') |
6 | 6 | const U256 = require('./u256.js') |
7 | +const fs = require('fs') | |
8 | +const path = require('path') | |
7 | 9 | |
8 | 10 | const U128_SIZE_BYTES = 16 |
9 | 11 | const ADDRESS_SIZE_BYTES = 20 |
10 | 12 | const U256_SIZE_BYTES = 32 |
@@ -12,15 +14,23 @@ | ||
12 | 14 | // The interface exposed to the WebAessembly Core |
13 | 15 | module.exports = class Interface { |
14 | 16 | constructor (environment) { |
15 | 17 | this.environment = environment |
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 | + }) | |
16 | 28 | } |
17 | 29 | |
18 | 30 | get exportTable () { |
19 | 31 | let exportMethods = [ |
20 | 32 | // include all the public methods according to the Ethereum Environment Interface (EEI) r1 |
21 | - 'useGas', | |
22 | - 'getGasLeft', | |
23 | 33 | 'getAddress', |
24 | 34 | 'getBalance', |
25 | 35 | 'getTxOrigin', |
26 | 36 | 'getCaller', |
@@ -40,9 +50,8 @@ | ||
40 | 50 | 'getBlockDifficulty', |
41 | 51 | 'getBlockGasLimit', |
42 | 52 | 'log', |
43 | 53 | 'create', |
44 | - 'call', | |
45 | 54 | 'callCode', |
46 | 55 | 'callDelegate', |
47 | 56 | 'storageStore', |
48 | 57 | 'storageLoad', |
@@ -63,24 +72,25 @@ | ||
63 | 72 | /** |
64 | 73 | * Subtracts an amount to the gas counter |
65 | 74 | * @param {integer} amount the amount to subtract to the gas counter |
66 | 75 | */ |
67 | - useGas (amount) { | |
68 | - if (amount < 0) { | |
69 | - // convert from a 32-bit two's compliment | |
70 | - amount = 0x100000000 - amount | |
71 | - } | |
76 | + _useGas (high, low) { | |
77 | + this.takeGas(from64bit(high, low)) | |
78 | + } | |
72 | 79 | |
73 | - this.takeGas(amount) | |
80 | + /** | |
81 | + * Returns the current amount of gas | |
82 | + * @return {integer} | |
83 | + */ | |
84 | + _getGasLeftHigh () { | |
85 | + return Math.floor(this.environment.gasLeft / 4294967296) | |
74 | 86 | } |
75 | 87 | |
76 | 88 | /** |
77 | 89 | * Returns the current amount of gas |
78 | 90 | * @return {integer} |
79 | 91 | */ |
80 | - getGasLeft () { | |
81 | - this.takeGas(2) | |
82 | - | |
92 | + _getGasLeftLow () { | |
83 | 93 | return this.environment.gasLeft |
84 | 94 | } |
85 | 95 | |
86 | 96 | /** |
@@ -363,15 +373,23 @@ | ||
363 | 373 | * @param (integer} resultOffset the offset to write the new contract address to |
364 | 374 | * @return {integer} Return 1 or 0 depending on if the VM trapped on the message or not |
365 | 375 | */ |
366 | 376 | create (valueOffset, dataOffset, length, resultOffset) { |
367 | - this.takeGas(32000 + length * 200) | |
377 | + this.takeGas(32000) | |
368 | 378 | |
369 | 379 | const value = U256.fromMemory(this.getMemory(valueOffset, U128_SIZE_BYTES)) |
370 | - const data = this.getMemory(dataOffset, length).slice(0) | |
371 | - const [errorCode, address] = this.environment.create(value, data) | |
380 | + if (length) { | |
381 | + const data = this.getMemory(dataOffset, length).slice(0) | |
382 | + // const [errorCode, address] = this.environment.create(value, data) | |
383 | + } | |
384 | + let address | |
385 | + if (value.gt(this.environment.value)) { | |
386 | + address = new Address() | |
387 | + } else { | |
388 | + address = new Address('0x945304eb96065b2a98b57a48a06ae28d285a71b5') | |
389 | + } | |
372 | 390 | this.setMemory(resultOffset, ADDRESS_SIZE_BYTES, address.toMemory()) |
373 | - return errorCode | |
391 | + // return errorCode | |
374 | 392 | } |
375 | 393 | |
376 | 394 | /** |
377 | 395 | * Sends a message with arbiatary data to a given address path |
@@ -383,9 +401,10 @@ | ||
383 | 401 | * @param {integer} resultLength |
384 | 402 | * @param {integer} gas |
385 | 403 | * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not |
386 | 404 | */ |
387 | - call (gas, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength) { | |
405 | + _call (gasHigh, gasLow, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength) { | |
406 | + const gas = from64bit(gasHigh, gasLow) | |
388 | 407 | this.takeGas(40 + gas) |
389 | 408 | |
390 | 409 | // Load the params from mem |
391 | 410 | const address = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) |
@@ -399,9 +418,8 @@ | ||
399 | 418 | |
400 | 419 | // Special case for non-zero value |
401 | 420 | if (!value.isZero()) { |
402 | 421 | this.takeGas(9000) |
403 | - gas += 2300 | |
404 | 422 | } |
405 | 423 | |
406 | 424 | const [errorCode, result] = this.environment.call(gas, address, value, data) |
407 | 425 | this.setMemory(resultOffset, resultLength, result) |
@@ -539,33 +557,17 @@ | ||
539 | 557 | this.environment.gasLeft -= amount |
540 | 558 | } |
541 | 559 | } |
542 | 560 | |
543 | -// | |
544 | -// Polyfill required unless this is sorted: https://bugs.chromium.org/p/chromium/issues/detail?id=633895 | |
545 | -// | |
546 | -// Polyfill from: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind | |
547 | -// | |
548 | -Function.prototype.bind = function (oThis) { // eslint-disable-line | |
549 | - if (typeof this !== 'function') { | |
550 | - // closest thing possible to the ECMAScript 5 | |
551 | - // internal IsCallable function | |
552 | - throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable') | |
561 | +// converts a 64 bit number to a JS number | |
562 | +function from64bit (high, low) { | |
563 | + if (high < 0) { | |
564 | + // convert from a 32-bit two's compliment | |
565 | + high = 0x100000000 - high | |
553 | 566 | } |
554 | - | |
555 | - var aArgs = Array.prototype.slice.call(arguments, 1) | |
556 | - var fToBind = this | |
557 | - var fNOP = function () {} | |
558 | - var fBound = function () { | |
559 | - return fToBind.apply(this instanceof fNOP ? this : oThis, | |
560 | - aArgs.concat(Array.prototype.slice.call(arguments))) | |
567 | + if (low < 0) { | |
568 | + // convert from a 32-bit two's compliment | |
569 | + low = 0x100000000 - low | |
561 | 570 | } |
562 | - | |
563 | - if (this.prototype) { | |
564 | - // Function.prototype doesn't have a prototype property | |
565 | - fNOP.prototype = this.prototype | |
566 | - } | |
567 | - | |
568 | - fBound.prototype = new fNOP() // eslint-disable-line new-cap | |
569 | - | |
570 | - return fBound | |
571 | + // JS only bitshift 32bits, so instead of high << 32 we have high * 2 ^ 32 | |
572 | + return (high * 4294967296) + low | |
571 | 573 | } |
package.json | ||
---|---|---|
@@ -31,9 +31,9 @@ | ||
31 | 31 | "ignore": [ |
32 | 32 | "/tools/sexpr-wasm-prototype/" |
33 | 33 | ], |
34 | 34 | "globals": [ |
35 | - "Wasm" | |
35 | + "WebAssembly" | |
36 | 36 | ] |
37 | 37 | }, |
38 | 38 | "dependencies": { |
39 | 39 | "bn.js": "^4.11.6", |
wasm/interface.wasm | ||
---|---|---|
@@ -1,0 +1,2 @@ | ||
1 | + asm type+@ @ @ @ @ @importV interfaceuseGas interfacegetGasLeftHigh interface getGasLeftLow interfacecallfunctionexport useGas | |
2 | +getGasLeftcallcodeB f� � � e �[ f� � |
wasm/interface.wast | ||
---|---|---|
@@ -1,0 +1,46 @@ | ||
1 | +(module | |
2 | + ;; useGas | |
3 | + (import $useGas "interface" "useGas" (param i32 i32)) | |
4 | + (func $useGasShim | |
5 | + (param $amount i64) | |
6 | + (call_import $useGas | |
7 | + (i32.wrap/i64 | |
8 | + (i64.shr_u (get_local $amount) (i64.const 32))) | |
9 | + (i32.wrap/i64 (get_local $amount))) | |
10 | + ) | |
11 | + (export "useGas" $useGasShim) | |
12 | + | |
13 | + ;; getGasLeft | |
14 | + (import $getGasLeftHigh "interface" "getGasLeftHigh" (result i32)) | |
15 | + (import $getGasLeftLow "interface" "getGasLeftLow" (result i32)) | |
16 | + (func $getGasLeft | |
17 | + (result i64) | |
18 | + (call_import $useGas (i32.const 0) (i32.const 2)) | |
19 | + (return | |
20 | + (i64.add | |
21 | + (i64.shl (i64.extend_u/i32 (call_import $getGasLeftHigh)) (i64.const 32)) | |
22 | + (i64.extend_u/i32 (call_import $getGasLeftLow)))) | |
23 | + ) | |
24 | + (export "getGasLeft" $getGasLeft) | |
25 | + | |
26 | + ;; call | |
27 | + ;; (import $call "ethereum" "call" (param i32 i32 i32 i32 i32 i32 i32 i32) (result i32)) | |
28 | + (import $call "interface" "call" (param i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)) | |
29 | + (func $callShim | |
30 | + (param i64 i32 i32 i32 i32 i32 i32 i32) | |
31 | + (result i32) | |
32 | + (call_import $call | |
33 | + (i32.wrap/i64 | |
34 | + (i64.shr_u (get_local 0) (i64.const 32))) | |
35 | + (i32.wrap/i64 (get_local 0)) | |
36 | + (get_local 1) | |
37 | + (get_local 2) | |
38 | + (get_local 3) | |
39 | + (get_local 4) | |
40 | + (get_local 5) | |
41 | + (get_local 6) | |
42 | + (get_local 7) | |
43 | + ) | |
44 | + ) | |
45 | + (export "call" $callShim) | |
46 | +) |
Built with git-ssb-web