Commit 40fce8207151c8b05af5d4bd34e82751421a7893
first test passing
wanderer committed on 4/24/2017, 4:10:41 PMParent: d6ecef73fd6edc80197140774535f1adea617314
Files changed
index.js | changed |
kernel.js | changed |
package.json | changed |
port.js | changed |
portManager.js | changed |
tests/index.js | changed |
index.js | ||
---|---|---|
@@ -14,18 +14,20 @@ | ||
14 | 14 | Object.assign(this._opts, opts) |
15 | 15 | } |
16 | 16 | |
17 | 17 | async getInstance (port) { |
18 | - const id = await this.generateID(port.id) | |
18 | + const id = await this.generateID(port) | |
19 | 19 | let kernel = this._vmInstances.get(id) |
20 | 20 | if (!kernel) { |
21 | 21 | // load the container from the state |
22 | 22 | await this.graph.tree(port, 2) |
23 | 23 | |
24 | 24 | // create a new kernel instance |
25 | + const VM = this._opts.VMs[port.type] | |
25 | 26 | const opts = Object.assign({ |
26 | 27 | state: port.vm, |
27 | - id: port.id | |
28 | + id: port.id, | |
29 | + VM: VM | |
28 | 30 | }, this._opts) |
29 | 31 | |
30 | 32 | kernel = new Kernel(opts) |
31 | 33 | await kernel.start() |
@@ -38,10 +40,9 @@ | ||
38 | 40 | } |
39 | 41 | |
40 | 42 | async send (port, message) { |
41 | 43 | const vm = await this.getInstance(port) |
42 | - const id = await this.generateID(port.id) | |
43 | - message._fromPort = id | |
44 | + message._fromPort = 'root' | |
44 | 45 | vm.queue(message) |
45 | 46 | } |
46 | 47 | |
47 | 48 | // given a port, wait untill its source contract has reached the threshold |
@@ -52,16 +53,20 @@ | ||
52 | 53 | return kernel |
53 | 54 | } |
54 | 55 | |
55 | 56 | async createStateRoot (port, ticks) { |
56 | - const kernel = await this.wait(port, ticks) | |
57 | - return this.graph.flush(kernel.state) | |
57 | + await this.wait(port, ticks) | |
58 | + return this.graph.flush(port) | |
58 | 59 | } |
59 | 60 | |
60 | 61 | async generateID (port) { |
61 | - let id = Buffer.concat([port.nonce, port.parent]) | |
62 | - id = await crypto.subtle.digest('SHA-256', id) | |
63 | - return new Buffer(id).toString('hex') | |
62 | + if (typeof port === 'object') { | |
63 | + let id = Buffer.concat([port.id.nonce, port.id.parent]) | |
64 | + id = await crypto.subtle.digest('SHA-256', id) | |
65 | + return new Buffer(id).toString('hex') | |
66 | + } else { | |
67 | + return port | |
68 | + } | |
64 | 69 | } |
65 | 70 | |
66 | 71 | addVM (type, vm) { |
67 | 72 | this._opts.VMs[type] = vm |
kernel.js | ||
---|---|---|
@@ -2,16 +2,16 @@ | ||
2 | 2 | const EventEmitter = require('events') |
3 | 3 | const BN = require('bn.js') |
4 | 4 | const PortManager = require('./portManager.js') |
5 | 5 | |
6 | -const VMSTATES = ['idle', 'running', 'result'] | |
7 | - | |
8 | 6 | module.exports = class Kernel extends EventEmitter { |
9 | 7 | constructor (opts) { |
10 | 8 | super() |
11 | 9 | this._opts = opts |
12 | - this._vmStateIndex = 0 | |
10 | + this.vmState = 'idle' | |
11 | + this.ticks = 0 | |
13 | 12 | this.ports = new PortManager(this) |
13 | + this.vm = new opts.VM(this) | |
14 | 14 | this._waitingQueue = new PriorityQueue((a, b) => { |
15 | 15 | return a.threshold > b.threshold |
16 | 16 | }) |
17 | 17 | this.on('result', this._runNextMessage) |
@@ -20,18 +20,13 @@ | ||
20 | 20 | start () { |
21 | 21 | return this.ports.start() |
22 | 22 | } |
23 | 23 | |
24 | - _updateVmState (message) { | |
25 | - this._vmStateIndex++ | |
26 | - const vmState = VMSTATES[this._stateVmIndex] | |
27 | - this._emit(vmState, message) | |
24 | + _updateVmState (state, message) { | |
25 | + this.vmState = state | |
26 | + this.emit(this.vmState, message) | |
28 | 27 | } |
29 | 28 | |
30 | - get vmState () { | |
31 | - return VMSTATES[this._stateVmIndex] | |
32 | - } | |
33 | - | |
34 | 29 | queue (message) { |
35 | 30 | this.ports.queue(message) |
36 | 31 | if (this.vmState === 'idle') { |
37 | 32 | this._runNextMessage() |
@@ -42,9 +37,9 @@ | ||
42 | 37 | this.ports.getNextMessage(this.ticks).then(message => { |
43 | 38 | if (message) { |
44 | 39 | this.run(message) |
45 | 40 | } else { |
46 | - this._updateState() | |
41 | + this._updateVmState('idle', message) | |
47 | 42 | } |
48 | 43 | }) |
49 | 44 | } |
50 | 45 | |
@@ -52,15 +47,15 @@ | ||
52 | 47 | * run the kernels code with a given enviroment |
53 | 48 | * The Kernel Stores all of its state in the Environment. The Interface is used |
54 | 49 | * to by the VM to retrive infromation from the Environment. |
55 | 50 | */ |
56 | - async run (message, imports = this.imports) { | |
51 | + async run (message) { | |
52 | + this._updateVmState('running', message) | |
57 | 53 | // shallow copy |
58 | 54 | const oldState = Object.assign({}, this._opts.state) |
59 | 55 | let result |
60 | - this._updateState(message) | |
61 | 56 | try { |
62 | - result = await this._vm.run(message, this, imports) || {} | |
57 | + result = await this.vm.run(message) || {} | |
63 | 58 | } catch (e) { |
64 | 59 | result = { |
65 | 60 | exception: true, |
66 | 61 | exceptionError: e |
@@ -72,18 +67,18 @@ | ||
72 | 67 | clearObject(this._opts.state) |
73 | 68 | Object.assign(this._opts.state, oldState) |
74 | 69 | } |
75 | 70 | |
76 | - this._updateVmState(result) | |
71 | + this._updateVmState('result', result) | |
77 | 72 | return result |
78 | 73 | } |
79 | 74 | |
80 | 75 | // returns a promise that resolves once the kernel hits the threshould tick |
81 | 76 | // count |
82 | 77 | async wait (threshold) { |
83 | - if (this._vmState === 'idle' && threshold > this.ticks) { | |
78 | + if (this.vmState === 'idle' && threshold > this.ticks) { | |
84 | 79 | // the cotract is at idle so wait |
85 | - return this.portManager.wait(threshold) | |
80 | + return this.ports.wait(threshold) | |
86 | 81 | } else { |
87 | 82 | return new Promise((resolve, reject) => { |
88 | 83 | if (threshold <= this.ticks) { |
89 | 84 | resolve(this.ticks) |
@@ -106,9 +101,9 @@ | ||
106 | 101 | |
107 | 102 | createPort () { |
108 | 103 | const nonce = new BN(this.nonce) |
109 | 104 | nonce.iaddn(1) |
110 | - this.nonce = nonce.toArrayLike(Uint8Array) | |
105 | + this.nonce = nonce.toArrayLike(Buffer) | |
111 | 106 | return { |
112 | 107 | id: { |
113 | 108 | '/': { |
114 | 109 | nonce: this.nonce, |
@@ -121,8 +116,9 @@ | ||
121 | 116 | } |
122 | 117 | } |
123 | 118 | |
124 | 119 | async send (port, message) { |
120 | + message._ticks = this.ticks | |
125 | 121 | return this._opts.hypervisor.send(port, message) |
126 | 122 | } |
127 | 123 | } |
128 | 124 |
package.json | ||
---|---|---|
@@ -2,9 +2,9 @@ | ||
2 | 2 | "name": "primea-hypervisor", |
3 | 3 | "version": "0.0.0", |
4 | 4 | "description": "this is a JS implemention of the primea hypervisor", |
5 | 5 | "scripts": { |
6 | - "coverage": "node --harmony ./node_modules/istanbul/lib/cli.js cover ./tests/apiTests.js", | |
6 | + "coverage": "node --harmony ./node_modules/istanbul/lib/cli.js cover ./tests/index.js", | |
7 | 7 | "coveralls": "npm run coverage && coveralls <coverage/lcov.info", |
8 | 8 | "lint": "standard", |
9 | 9 | "test": "node --harmony --expose-wasm ./tests/interfaceRunner.js", |
10 | 10 | "build": "node ./tests/buildTests.js && ./tools/wabt/out/wast2wasm ./wasm/interface.wast -o ./wasm/interface.wasm" |
port.js | ||
---|---|---|
@@ -5,9 +5,9 @@ | ||
5 | 5 | this.ticks = 0 |
6 | 6 | } |
7 | 7 | |
8 | 8 | queue (message) { |
9 | - this.ticks = message.ticks | |
9 | + this.ticks = message._ticks | |
10 | 10 | if (this._resolve) { |
11 | 11 | return this._resolve(message) |
12 | 12 | } else { |
13 | 13 | this._queue.push(message) |
@@ -31,7 +31,7 @@ | ||
31 | 31 | return this._queue[0] |
32 | 32 | } |
33 | 33 | |
34 | 34 | shift () { |
35 | - this._queue.shift() | |
35 | + return this._queue.shift() | |
36 | 36 | } |
37 | 37 | } |
portManager.js | ||
---|---|---|
@@ -1,6 +1,28 @@ | ||
1 | 1 | const Port = require('./port.js') |
2 | 2 | |
3 | +// decides which message to go firts | |
4 | +function messageArbiter (portA, portB) { | |
5 | + const a = portA.peek() | |
6 | + const b = portB.peek() | |
7 | + | |
8 | + if (!a) { | |
9 | + return b | |
10 | + } else if (!b) { | |
11 | + return a | |
12 | + } | |
13 | + | |
14 | + const aGasPrice = a.resources.gasPrice | |
15 | + const bGasPrice = b.resources.gasPrice | |
16 | + if (a.ticks !== b.ticks) { | |
17 | + return a.ticks < b.ticks ? a : b | |
18 | + } else if (aGasPrice === bGasPrice) { | |
19 | + return a.hash() > b.hash() ? a : b | |
20 | + } else { | |
21 | + return aGasPrice > bGasPrice ? a : b | |
22 | + } | |
23 | +} | |
24 | + | |
3 | 25 | module.exports = class PortManager { |
4 | 26 | constructor (kernel) { |
5 | 27 | this.kernel = kernel |
6 | 28 | this.hypervisor = kernel._opts.hypervisor |
@@ -17,13 +39,13 @@ | ||
17 | 39 | }) |
18 | 40 | }) |
19 | 41 | |
20 | 42 | // create the parent port |
21 | - ports.push(this.hypervisor.generateID(this.kernel._opts.id).then(id => { | |
22 | - return [id, new Port('parent')] | |
23 | - })) | |
24 | 43 | ports = await Promise.all(ports) |
25 | 44 | this._portMap = new Map(ports) |
45 | + // add the parent port | |
46 | + const parent = this.kernel._opts.id.parent.length === 0 ? 'root' : this.kernel._opts.id.parent | |
47 | + this._portMap.set(parent, new Port('parent')) | |
26 | 48 | } |
27 | 49 | |
28 | 50 | queue (message) { |
29 | 51 | this._portMap.get(message.fromPort).queue(message) |
@@ -48,22 +70,21 @@ | ||
48 | 70 | return this._portMap.get(id) |
49 | 71 | } |
50 | 72 | |
51 | 73 | async getParent () { |
52 | - const id = await this.hypervisor.generateID(this.kernel._opts.id) | |
74 | + const id = await this.hypervisor.generateID(this.kernel._opts) | |
53 | 75 | return this._portMap.get(id) |
54 | 76 | } |
55 | 77 | |
56 | 78 | // waits till all ports have reached a threshold tick count |
57 | 79 | async wait (threshold) { |
58 | 80 | // find the ports that have a smaller tick count then the threshold tick count |
59 | - const unkownPorts = [...this._ports].filter((id, port) => { | |
60 | - const message = port.peek() | |
61 | - return !message || message.ticks < threshold | |
81 | + const unkownPorts = [...this._portMap].filter(([id, port]) => { | |
82 | + return port._ticks < threshold | |
62 | 83 | }) |
63 | 84 | |
64 | - const promises = unkownPorts.map(port => { | |
65 | - this.hypervisor.wait(port, threshold).then(ticks => { | |
85 | + const promises = unkownPorts.map(([id, port]) => { | |
86 | + this.hypervisor.wait(id, threshold).then(ticks => { | |
66 | 87 | // update the port's tick count |
67 | 88 | port.ticks = ticks |
68 | 89 | }) |
69 | 90 | }) |
@@ -71,29 +92,8 @@ | ||
71 | 92 | } |
72 | 93 | |
73 | 94 | async getNextMessage (ticks) { |
74 | 95 | await this.wait(ticks) |
75 | - return [...this._portMap].reduce(messageArbiter).shift() | |
96 | + return [...this._portMap].reduce(messageArbiter)[1].shift() | |
76 | 97 | } |
77 | 98 | } |
78 | 99 | |
79 | -// decides which message to go firts | |
80 | -function messageArbiter (portA, portB) { | |
81 | - const a = portA.peek() | |
82 | - const b = portB.peek() | |
83 | - | |
84 | - if (!a) { | |
85 | - return b | |
86 | - } else if (!b) { | |
87 | - return a | |
88 | - } | |
89 | - | |
90 | - const aGasPrice = a.resources.gasPrice | |
91 | - const bGasPrice = b.resources.gasPrice | |
92 | - if (a.ticks !== b.ticks) { | |
93 | - return a.ticks < b.ticks ? a : b | |
94 | - } else if (aGasPrice === bGasPrice) { | |
95 | - return a.hash() > b.hash() ? a : b | |
96 | - } else { | |
97 | - return aGasPrice > bGasPrice ? a : b | |
98 | - } | |
99 | -} |
tests/index.js | ||
---|---|---|
@@ -9,37 +9,41 @@ | ||
9 | 9 | }) |
10 | 10 | |
11 | 11 | node.on('start', () => { |
12 | 12 | tape.only('basic', async t => { |
13 | - const testVM = { | |
14 | - run (message) { | |
15 | - console.log('made it!!!!') | |
13 | + const message = new Message() | |
14 | + class testVM { | |
15 | + run (m) { | |
16 | + t.true(m === message, 'should recive a message') | |
17 | + t.end() | |
16 | 18 | } |
17 | 19 | } |
18 | 20 | |
19 | 21 | try { |
20 | 22 | const state = { |
21 | 23 | id: { |
22 | - nonce: new Uint8Array([0]), | |
23 | - parent: new Uint8Array() | |
24 | + nonce: new Buffer([0]), | |
25 | + parent: new Buffer([]) | |
24 | 26 | }, |
25 | 27 | type: 'test', |
26 | 28 | vm: { |
27 | 29 | ports: {} |
28 | 30 | } |
29 | 31 | } |
30 | 32 | |
31 | - const hypervisor = new Hypervisor(node.dag) | |
33 | + const expectedState = { '/': 'zdpuApqUjZFhw8LTkw8gXAbVcqc5Y7TsbTVadU879TgucoqSF' } | |
34 | + | |
35 | + const hypervisor = new Hypervisor({ | |
36 | + dag: node.dag | |
37 | + }) | |
32 | 38 | hypervisor.addVM('test', testVM) |
33 | 39 | |
34 | - const message = new Message() | |
35 | 40 | await hypervisor.send(state, message) |
36 | 41 | |
37 | 42 | await hypervisor.createStateRoot(state, Infinity) |
38 | - console.log(state) | |
43 | + t.deepEquals(state, expectedState, 'expected') | |
39 | 44 | |
40 | 45 | node.stop(() => { |
41 | - t.end() | |
42 | 46 | process.exit() |
43 | 47 | }) |
44 | 48 | } catch (e) { |
45 | 49 | console.log(e) |
Built with git-ssb-web