Commit 7ff034895b684011d11e02744533ab8d7987b4e8
sstore/ssload working
wanderer committed on 10/30/2016, 5:41:14 PMParent: 69097f452f01a61429f16530219f9f1dc548b8d3
Files changed
environment.js | changed |
index.js | changed |
interface.js | changed |
package.json | changed |
environment.js | ||
---|---|---|
@@ -1,11 +1,13 @@ | ||
1 | +const Vertex = require('merkle-trie') | |
2 | +const Store = require('merkle-trie/store') | |
1 | 3 | const U256 = require('./deps/u256.js') |
2 | 4 | const Address = require('./deps/address.js') |
3 | 5 | const Block = require('./deps/block.js') |
4 | 6 | const fakeBlockChain = require('./fakeBlockChain.js') |
5 | 7 | |
6 | 8 | module.exports = class Environment { |
7 | - constructor (data) { | |
9 | + constructor (data = {}) { | |
8 | 10 | const defaults = { |
9 | 11 | block: new Block(), |
10 | 12 | blockchain: fakeBlockChain, |
11 | 13 | // gas tank |
@@ -24,54 +26,54 @@ | ||
24 | 26 | logs: [], |
25 | 27 | selfDestruct: false, |
26 | 28 | selfDestructAddress: new Address('0x0000000000000000000000000000000000000000'), |
27 | 29 | // more output calls |
28 | - returnValue: new Uint8Array() | |
30 | + returnValue: new Uint8Array(), | |
31 | + state: new Vertex({store: new Store()}) | |
29 | 32 | } |
30 | 33 | |
31 | - this.state = new Map() | |
32 | - | |
33 | - Object.assign(this, defaults, data || {}) | |
34 | + Object.assign(this, defaults, data) | |
34 | 35 | } |
35 | 36 | |
36 | - addAccount (address, trie) { | |
37 | - let account = new Map() | |
38 | - account.set('nonce', trie.nonce || new U256(0)) | |
39 | - account.set('balance', trie.balance || new U256(0)) | |
40 | - account.set('code', trie.code || new Uint8Array()) | |
41 | - account.set('storage', trie.storage || new Map()) | |
42 | - this.state.set(address.toString(), account) | |
43 | - } | |
37 | + // addAccount (address, trie) { | |
38 | + // let account = new Vertex() | |
39 | + // account.set('nonce', trie.nonce || new U256(0)) | |
40 | + // account.set('balance', trie.balance || new U256(0)) | |
41 | + // account.set('code', trie.code || new Uint8Array()) | |
42 | + // account.set('storage', trie.storage || new Map()) | |
43 | + // this.state.set(address.toString(), account) | |
44 | + // } | |
44 | 45 | |
45 | 46 | isAccountPresent (address) { |
46 | - const account = this.state.get(address.toString()) | |
47 | - if (account) { | |
48 | - return true | |
49 | - } else { | |
50 | - return false | |
51 | - } | |
47 | + // const account = this.state.get(address.toString()) | |
48 | + // if (account) { | |
49 | + // return true | |
50 | + // } else { | |
51 | + // return false | |
52 | + // } | |
52 | 53 | } |
53 | 54 | |
54 | 55 | getBalance (address) { |
55 | - const account = this.state.get(address.toString()) | |
56 | - if (account) { | |
57 | - return account.get('balance') | |
58 | - } else { | |
59 | - return new U256() | |
60 | - } | |
56 | + // const account = this.state.get(address.toString()) | |
57 | + // if (account) { | |
58 | + // return account.get('balance') | |
59 | + // } else { | |
60 | + // return new U256() | |
61 | + // } | |
61 | 62 | } |
62 | 63 | |
63 | 64 | getCode (address) { |
64 | - const account = this.state.get(address.toString()) | |
65 | - if (account) { | |
66 | - return account.get('code') | |
67 | - } else { | |
68 | - return Uint8Array.from(new Buffer([])) | |
69 | - } | |
65 | + // const account = this.state.get(address.toString()) | |
66 | + // if (account) { | |
67 | + // return account.get('code') | |
68 | + // } else { | |
69 | + // return Uint8Array.from(new Buffer([])) | |
70 | + // } | |
70 | 71 | } |
71 | 72 | |
72 | 73 | getBlockHash (height) { |
73 | - return this.blockchain.getBlock(height).hash() | |
74 | + // return this.blockchain.getBlock(height).hash() | |
75 | + return this.root.getBlockAt(height).then(block => block.hash()) | |
74 | 76 | } |
75 | 77 | |
76 | 78 | set createHandler (value) { |
77 | 79 | this.createhandler = value |
@@ -88,9 +90,9 @@ | ||
88 | 90 | } |
89 | 91 | |
90 | 92 | call (gas, address, value, data) { |
91 | 93 | // FIXME: create a child environment here |
92 | - const ret = this.callhandler({ | |
94 | + const ret = this.root.messagehandler({ | |
93 | 95 | from: this.address, |
94 | 96 | to: address, |
95 | 97 | gasLimit: gas, |
96 | 98 | value: value, |
index.js | ||
---|---|---|
@@ -32,18 +32,21 @@ | ||
32 | 32 | module.exports = class Kernel { |
33 | 33 | // runs some code in the VM |
34 | 34 | constructor (environment = new Environment()) { |
35 | 35 | this.environment = environment |
36 | + this._runningOps = Promise.resolve() | |
36 | 37 | |
37 | - this.environment.addAccount(identityContract, {}) | |
38 | - this.environment.addAccount(meteringContract, {}) | |
39 | - this.environment.addAccount(transcompilerContract, {}) | |
38 | + // this.environment.addAccount(identityContract, {}) | |
39 | + // this.environment.addAccount(meteringContract, {}) | |
40 | + // this.environment.addAccount(transcompilerContract, {}) | |
40 | 41 | } |
41 | 42 | |
42 | 43 | // handles running code. |
43 | 44 | // NOTE: it assumes that wasm will raise an exception if something went wrong, |
44 | 45 | // otherwise execution succeeded |
45 | - codeHandler (code, ethInterface = new Interface(new Environment())) { | |
46 | + codeHandler (code, ethInterface = new Interface(new Environment(), this)) { | |
47 | + // TODO remove | |
48 | + ethInterface.kernel = this | |
46 | 49 | const debugInterface = new DebugInterface(ethInterface.environment) |
47 | 50 | const module = WebAssembly.Module(code) |
48 | 51 | const imports = { |
49 | 52 | 'ethereum': ethInterface.exportTable, |
@@ -61,24 +64,33 @@ | ||
61 | 64 | imports.ethereum.useGas = ethInterface.shims.exports.useGas |
62 | 65 | imports.ethereum.getGasLeft = ethInterface.shims.exports.getGasLeft |
63 | 66 | imports.ethereum.call = ethInterface.shims.exports.call |
64 | 67 | |
65 | - const instance = WebAssembly.Instance(module, imports) | |
68 | + const instance = this.instance = WebAssembly.Instance(module, imports) | |
66 | 69 | |
67 | 70 | ethInterface.setModule(instance) |
68 | 71 | debugInterface.setModule(instance) |
69 | 72 | |
70 | 73 | if (instance.exports.main) { |
71 | 74 | instance.exports.main() |
72 | 75 | } |
73 | - return instance | |
76 | + return this.onDone() | |
74 | 77 | } |
75 | 78 | |
79 | + // returns a promise that resolves when the wasm instance is done running | |
80 | + async onDone () { | |
81 | + let prevOps | |
82 | + while (prevOps !== this._runningOps) { | |
83 | + prevOps = this._runningOps | |
84 | + await this._runningOps | |
85 | + } | |
86 | + } | |
87 | + | |
76 | 88 | // loads code from the merkle trie and delegates the message |
77 | 89 | // Detects if code is EVM or WASM |
78 | 90 | // Detects if the code injection is needed |
79 | 91 | // Detects if transcompilation is needed |
80 | - callHandler (call) { | |
92 | + messageHandler (call) { | |
81 | 93 | // FIXME: this is here until these two contracts are compiled to WASM |
82 | 94 | // The two special contracts (precompiles now, but will be real ones later) |
83 | 95 | if (call.to.equals(meteringContract)) { |
84 | 96 | return Precompile.meteringInjector(call) |
interface.js | ||
---|---|---|
@@ -12,9 +12,10 @@ | ||
12 | 12 | const U256_SIZE_BYTES = 32 |
13 | 13 | |
14 | 14 | // The interface exposed to the WebAessembly Core |
15 | 15 | module.exports = class Interface { |
16 | - constructor (environment) { | |
16 | + constructor (environment, kernel) { | |
17 | + this.kernel = kernel | |
17 | 18 | this.environment = environment |
18 | 19 | const shimBin = fs.readFileSync(path.join(__dirname, '/wasm/interface.wasm')) |
19 | 20 | const shimMod = WebAssembly.Module(shimBin) |
20 | 21 | this.shims = WebAssembly.Instance(shimMod, { |
@@ -403,9 +404,8 @@ | ||
403 | 404 | * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not |
404 | 405 | */ |
405 | 406 | _call (gasHigh, gasLow, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength) { |
406 | 407 | const gas = from64bit(gasHigh, gasLow) |
407 | - console.log(gas); | |
408 | 408 | // Load the params from mem |
409 | 409 | const address = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) |
410 | 410 | const value = U256.fromMemory(this.getMemory(valueOffset, U128_SIZE_BYTES)) |
411 | 411 | |
@@ -499,53 +499,66 @@ | ||
499 | 499 | * @param {interger} pathOffest the memory offset to load the the path from |
500 | 500 | * @param {interger} valueOffset the memory offset to load the value from |
501 | 501 | */ |
502 | 502 | storageStore (pathOffset, valueOffset, cbDest) { |
503 | - console.log('sstore'); | |
504 | - const path = new Buffer(this.getMemory(pathOffset, U256_SIZE_BYTES)).toString('hex') | |
503 | + this.takeGas(5000) | |
504 | + const path = [...this.getMemory(pathOffset, U256_SIZE_BYTES)] | |
505 | 505 | // copy the value |
506 | 506 | const value = this.getMemory(valueOffset, U256_SIZE_BYTES).slice(0) |
507 | - console.log('value:' + value) | |
508 | - console.log('path:' + path) | |
509 | - const oldValue = this.environment.state.get(path) | |
510 | 507 | const valIsZero = value.every((i) => i === 0) |
508 | + const opPromise = this.environment.state.get(path) | |
509 | + .catch(() => { | |
510 | + // TODO: handle errors | |
511 | + // the value was not found | |
512 | + return null | |
513 | + }) | |
511 | 514 | |
512 | - this.takeGas(5000) | |
515 | + // wait for all the prevouse async ops to finish before running the callback | |
516 | + this.kernel._runningOps = Promise.all([this.kernel._runningOps, opPromise]) | |
517 | + .then(values => { | |
518 | + const oldValue = values.pop() | |
519 | + if (valIsZero && oldValue) { | |
520 | + // delete a value | |
521 | + this.environment.gasRefund += 15000 | |
522 | + this.environment.state.del(path) | |
523 | + } else { | |
524 | + if (!valIsZero && !oldValue) { | |
525 | + // creating a new value | |
526 | + this.takeGas(15000) | |
527 | + } | |
528 | + // update | |
529 | + this.environment.state.set(path, value) | |
530 | + } | |
513 | 531 | |
514 | - // write | |
515 | - if (!valIsZero && !oldValue) { | |
516 | - this.takeGas(15000) | |
517 | - } | |
518 | - | |
519 | - // delete | |
520 | - if (valIsZero && oldValue) { | |
521 | - this.environment.gasRefund += 15000 | |
522 | - this.environment.state.delete(path) | |
523 | - } else { | |
524 | - this.environment.state.set(path, value) | |
525 | - } | |
526 | - | |
527 | - setTimeout(() => { | |
528 | - this.module.exports['0']() | |
529 | - }, 0) | |
532 | + this.module.exports[cbDest.toString()]() | |
533 | + }) | |
530 | 534 | } |
531 | 535 | |
532 | 536 | /** |
533 | 537 | * reterives a value at a given path in long term storage |
534 | 538 | * @param {interger} pathOffest the memory offset to load the the path from |
535 | 539 | * @param {interger} resultOffset the memory offset to load the value from |
536 | 540 | */ |
537 | 541 | storageLoad (pathOffset, resultOffset, cbDest) { |
538 | - console.log('sload'); | |
539 | 542 | this.takeGas(50) |
540 | 543 | |
541 | - const path = new Buffer(this.getMemory(pathOffset, U256_SIZE_BYTES)).toString('hex') | |
542 | - console.log(path); | |
543 | - const result = this.environment.state.get(path) || new Uint8Array(32) | |
544 | - this.setMemory(resultOffset, U256_SIZE_BYTES, result) | |
545 | - setTimeout(() => { | |
546 | - this.module.exports['0']() | |
547 | - }, 0) | |
544 | + // convert the path to an array | |
545 | + const path = [...this.getMemory(pathOffset, U256_SIZE_BYTES)] | |
546 | + const opPromise = this.environment.state.get(path) | |
547 | + .catch(() => { | |
548 | + // TODO: handle other possible errors | |
549 | + // if the value was not found return a empty array | |
550 | + return new Uint8Array(32) | |
551 | + }) | |
552 | + | |
553 | + // wait for all the prevouse async ops to finish before running the callback | |
554 | + this.kernel._runningOps = Promise | |
555 | + .all([this.kernel._runningOps, opPromise]) | |
556 | + .then(values => { | |
557 | + const result = values.pop() | |
558 | + this.setMemory(resultOffset, U256_SIZE_BYTES, result) | |
559 | + this.module.exports[cbDest.toString()]() | |
560 | + }) | |
548 | 561 | } |
549 | 562 | |
550 | 563 | /** |
551 | 564 | * Halt execution returning output data. |
Built with git-ssb-web