Commit 0f4618d69a1944599b2e15d8398df11ca4901eaa
added revert test
wanderer committed on 2/25/2017, 11:36:12 AMParent: d9ab14281e683bbcd8f54b5cb7874edbbdd088a7
Files changed
index.js | changed |
package.json | changed |
port.js | changed |
portManager.js | changed |
tests/apiTests.js | changed |
index.js | ||
---|---|---|
@@ -1,33 +1,34 @@ | ||
1 | 1 | const EventEmitter = require('events') |
2 | 2 | const Vertex = require('merkle-trie') |
3 | 3 | const PortManager = require('./portManager.js') |
4 | +const StateInterface = require('./stateInterface.js') | |
4 | 5 | const imports = require('./EVMinterface.js') |
5 | 6 | const codeHandler = require('./codeHandler.js') |
6 | 7 | const common = require('./common.js') |
7 | 8 | |
8 | 9 | module.exports = class Kernel extends EventEmitter { |
9 | 10 | constructor (opts = {}) { |
10 | 11 | super() |
11 | 12 | const state = this.state = opts.state || new Vertex() |
13 | + this.stateInterface = new StateInterface(state) | |
12 | 14 | this.code = opts.code || state.value |
13 | 15 | this.path = state.path |
14 | 16 | this.imports = opts.imports || [imports] |
15 | 17 | this.ports = new PortManager(state, opts.parent, Kernel) |
18 | + this._sentAtomicMessages = [] | |
16 | 19 | // rename sandbox? |
17 | 20 | this._vm = (opts.codeHandler || codeHandler).init(this.code) |
18 | 21 | this._state = 'idle' |
19 | - this.ports.on('message', message => { | |
20 | - // was this kernel already visted? | |
21 | - if (message.isCyclic(this) || this._state === 'idle') { | |
22 | - this.run(message) | |
23 | - } | |
22 | + this.ports.on('message', index => { | |
23 | + this.runNextMessage(index) | |
24 | 24 | }) |
25 | 25 | } |
26 | 26 | |
27 | - runNextMessage () { | |
28 | - this.ports.dequeue().then(message => { | |
29 | - if (message) { | |
27 | + runNextMessage (index = 0) { | |
28 | + this.ports.peek(index).then(message => { | |
29 | + if (message && (message.isCyclic(this) || this._state === 'idle')) { | |
30 | + this.ports.remove(index) | |
30 | 31 | this.run(message) |
31 | 32 | } else { |
32 | 33 | this._state = 'idle' |
33 | 34 | this.emit('idle') |
@@ -40,29 +41,43 @@ | ||
40 | 41 | * The Kernel Stores all of its state in the Environment. The Interface is used |
41 | 42 | * to by the VM to retrive infromation from the Environment. |
42 | 43 | */ |
43 | 44 | async run (message, imports = this.imports) { |
44 | - this._state = 'running' | |
45 | - const oldState = this.state.copy() | |
46 | - const result = await this._vm.run(message, this, imports) || {} | |
47 | - | |
48 | 45 | function revert () { |
49 | 46 | // revert the state |
50 | 47 | this.state.set([], oldState) |
51 | 48 | // revert all the sent messages |
52 | - this.ports.outbox.revert() | |
53 | - this.runNextMessage() | |
49 | + for (let msg in this._sentAtomicMessages) { | |
50 | + msg.revert() | |
51 | + } | |
52 | + this.runNextMessage(0) | |
54 | 53 | } |
55 | 54 | |
55 | + const oldState = this.state.copy() | |
56 | + let result | |
57 | + this._state = 'running' | |
58 | + try { | |
59 | + result = await this._vm.run(message, this, imports) || {} | |
60 | + } catch (e) { | |
61 | + result = { | |
62 | + exception: true | |
63 | + } | |
64 | + } | |
56 | 65 | if (result.execption) { |
57 | 66 | // failed messages |
58 | 67 | revert() |
59 | 68 | } else if (message.atomic) { |
60 | 69 | // messages |
61 | - message.finished().then(this.runNextMessage).catch(revert) | |
70 | + message.finished().then(vmError => { | |
71 | + if (vmError) { | |
72 | + revert() | |
73 | + } else { | |
74 | + this.runNextMessage(0) | |
75 | + } | |
76 | + }) | |
62 | 77 | } else { |
63 | 78 | // non-atomic messages |
64 | - this.runNextMessage() | |
79 | + this.runNextMessage(0) | |
65 | 80 | } |
66 | 81 | return result |
67 | 82 | } |
68 | 83 | |
@@ -75,22 +90,13 @@ | ||
75 | 90 | portName = common.PARENT |
76 | 91 | message.to = new Array(this.path.length).fill(common.PARENT).concat(message.to) |
77 | 92 | } |
78 | 93 | const port = await this.ports.get(portName) |
94 | + // save the atomic messages for possible reverts | |
95 | + if (message.atomic) { | |
96 | + this._sentAtomicMessages.push(message) | |
97 | + } | |
79 | 98 | return port.send(message) |
80 | 99 | } |
81 | 100 | |
82 | - setValue (name, value) { | |
83 | - this.state.set(name, value) | |
84 | - } | |
85 | - | |
86 | - getValue (name) { | |
87 | - return this.state.get(name) | |
88 | - } | |
89 | - | |
90 | - deleteValue (name) { | |
91 | - return this.state.del(name) | |
92 | - } | |
93 | - | |
94 | - // remove from cache | |
95 | 101 | shutdown () {} |
96 | 102 | } |
package.json | ||
---|---|---|
@@ -44,8 +44,7 @@ | ||
44 | 44 | "bn.js": "^4.11.6", |
45 | 45 | "ethereumjs-block": "^1.4.1", |
46 | 46 | "ethereumjs-tx": "^1.1.2", |
47 | 47 | "ethereumjs-util": "^5.0.0", |
48 | - "merkle-trie": "0.1.2", | |
49 | - "promise-queue": "^2.2.3" | |
48 | + "merkle-trie": "0.1.2" | |
50 | 49 | } |
51 | 50 | } |
port.js | ||
---|---|---|
@@ -2,9 +2,8 @@ | ||
2 | 2 | |
3 | 3 | module.exports = class Port extends EventEmitter { |
4 | 4 | constructor () { |
5 | 5 | super() |
6 | - this.queue = [] | |
7 | 6 | } |
8 | 7 | |
9 | 8 | connect (destPort) { |
10 | 9 | this.destPort = destPort |
@@ -16,11 +15,6 @@ | ||
16 | 15 | } |
17 | 16 | |
18 | 17 | async recieve (message) { |
19 | 18 | this.emit('message', message) |
20 | - this.queue.push(message) | |
21 | 19 | } |
22 | - | |
23 | - dequeue () { | |
24 | - return this.queue.unshift() | |
25 | - } | |
26 | 20 | } |
portManager.js | ||
---|---|---|
@@ -4,27 +4,32 @@ | ||
4 | 4 | |
5 | 5 | module.exports = class PortManager extends EventEmitter { |
6 | 6 | constructor (state, destParentPort, KernelContructor) { |
7 | 7 | super() |
8 | + this._queue = [] | |
8 | 9 | this.state = state |
9 | - this.sentMessage = [] | |
10 | 10 | this.Kernel = KernelContructor |
11 | 11 | // set up the parent port |
12 | 12 | const parentPort = new Port() |
13 | 13 | parentPort.on('message', message => { |
14 | - this.emit('message', message) | |
14 | + this._recieveMessage(message) | |
15 | 15 | }) |
16 | 16 | // create the cache |
17 | 17 | this.cache = new Map() |
18 | 18 | this.cache.set(common.PARENT, parentPort) |
19 | 19 | } |
20 | 20 | |
21 | + _recieveMessage (message) { | |
22 | + const index = this._queue.push(message) - 1 | |
23 | + this.emit('message', index) | |
24 | + } | |
25 | + | |
21 | 26 | async get (name) { |
22 | 27 | let port = this.cache.get(name) |
23 | 28 | if (!port) { |
24 | 29 | port = new Port() |
25 | 30 | port.on('message', message => { |
26 | - this.emit('message', message) | |
31 | + this._recieveMessage(message) | |
27 | 32 | }) |
28 | 33 | // create destination kernel |
29 | 34 | const state = await this.state.get(name) |
30 | 35 | const destKernel = new this.Kernel({ |
@@ -45,20 +50,16 @@ | ||
45 | 50 | } |
46 | 51 | return port |
47 | 52 | } |
48 | 53 | |
49 | - // dequeues the first message that is waiting on a port | |
50 | - async dequeue () { | |
51 | - // clear the outbox | |
52 | - this.sentMessage = [] | |
53 | - for (let port in this.cache) { | |
54 | - const message = port.dequeue() | |
55 | - if (message) { | |
56 | - return message | |
57 | - } | |
58 | - } | |
54 | + async peek (index = 0) { | |
55 | + return this._queue[index] | |
59 | 56 | } |
60 | 57 | |
58 | + remove (index) { | |
59 | + return this._queue.splice(index, index + 1) | |
60 | + } | |
61 | + | |
61 | 62 | close () { |
62 | 63 | for (let port in this.cache) { |
63 | 64 | port.emit('close') |
64 | 65 | } |
tests/apiTests.js | ||
---|---|---|
@@ -1,7 +1,8 @@ | ||
1 | 1 | const tape = require('tape') |
2 | 2 | const Hypervisor = require('../hypervisor.js') |
3 | 3 | const Message = require('../message.js') |
4 | +const Vertex = require('merkle-trie') | |
4 | 5 | |
5 | 6 | tape('send and reciving messages', async t => { |
6 | 7 | try { |
7 | 8 | const hypervisor = new Hypervisor() |
@@ -14,11 +15,42 @@ | ||
14 | 15 | } |
15 | 16 | }) |
16 | 17 | hypervisor.send(new Message({ |
17 | 18 | to: path |
18 | - })).catch(e => { | |
19 | - console.log(e) | |
20 | - }) | |
19 | + })) | |
21 | 20 | } catch (e) { |
22 | 21 | console.log(e) |
23 | 22 | } |
24 | 23 | }) |
24 | + | |
25 | +tape('reverts', async t => { | |
26 | + const hypervisor = new Hypervisor() | |
27 | + const path = ['one', 'two', 'three'] | |
28 | + const path2 = ['one', 'two', 'three', 'four'] | |
29 | + hypervisor.set(path, { | |
30 | + run: async (message, kernel) => { | |
31 | + await kernel.send(new Message({ | |
32 | + to: ['four'] | |
33 | + })) | |
34 | + throw new Error('vm exception') | |
35 | + } | |
36 | + }) | |
37 | + | |
38 | + hypervisor.set(path2, { | |
39 | + run: (message, kernel) => { | |
40 | + kernel.stateInterface.set('key', new Vertex({ | |
41 | + value: 'value' | |
42 | + })) | |
43 | + } | |
44 | + }) | |
45 | + | |
46 | + await hypervisor.send(new Message({ | |
47 | + to: path | |
48 | + })) | |
49 | + | |
50 | + try { | |
51 | + await hypervisor.state.get(path2.concat(['key'])) | |
52 | + } catch (e) { | |
53 | + t.equal(typeof e, 'object') | |
54 | + t.end() | |
55 | + } | |
56 | +}) |
Built with git-ssb-web