Commit a12fcbf698618cd91b74a1da931067ede4b57f45
use primea messaging
wanderer committed on 4/3/2017, 10:03:46 PMParent: 0e68cbae9b8717bdfa591e624e3135e8a84ecea5
Files changed
EVMinterface.js | changed |
codeHandler.js | changed |
common.js | changed |
defaultAgent.js | changed |
hypervisor.js | changed |
index.js | changed |
package.json | changed |
port.js | changed |
portManager.js | changed |
tests/apiTests.js | changed |
tests/interfaceRunner.js | changed |
message.js | deleted |
EVMinterface.js | ||
---|---|---|
@@ -3,12 +3,12 @@ | ||
3 | 3 | * enables to interact with the Ethereum Environment |
4 | 4 | */ |
5 | 5 | const fs = require('fs') |
6 | 6 | const ethUtil = require('ethereumjs-util') |
7 | -const Vertex = require('merkle-trie') | |
7 | +const Vertex = require('ipld-graph-builder') | |
8 | 8 | const U256 = require('fixed-bn.js').U256 |
9 | 9 | const U128 = require('fixed-bn.js').U128 |
10 | -const Message = require('./message.js') | |
10 | +const Message = require('primea-message') | |
11 | 11 | const common = require('./common.js') |
12 | 12 | |
13 | 13 | const U128_SIZE_BYTES = 16 |
14 | 14 | const ADDRESS_SIZE_BYTES = 20 |
codeHandler.js | ||
---|---|---|
@@ -1,31 +1,33 @@ | ||
1 | 1 | const Wasm = require('primea-wasm-container') |
2 | 2 | |
3 | 3 | const defaultHandler = { |
4 | - test: (code) => { | |
5 | - return !code | |
4 | + test: (state) => { | |
5 | + return !state.code | |
6 | 6 | }, |
7 | 7 | init: () => { |
8 | 8 | return require('./defaultAgent.js') |
9 | 9 | } |
10 | 10 | } |
11 | 11 | |
12 | 12 | const wasm = { |
13 | - test: (code) => { | |
14 | - code = new Buffer(code) | |
13 | + test: (state) => { | |
14 | + const code = new Buffer(state.code) | |
15 | 15 | return code && code.slice(0, 4).toString() === '\x00asm' |
16 | 16 | }, |
17 | 17 | init: (code) => { |
18 | 18 | return new Wasm(code) |
19 | 19 | } |
20 | 20 | } |
21 | 21 | |
22 | 22 | const javascript = { |
23 | - test: (code) => { | |
24 | - return typeof code === 'object' | |
23 | + test: (state) => { | |
24 | + return typeof state.code === 'function' | |
25 | 25 | }, |
26 | - init: (code) => { | |
27 | - return code | |
26 | + init: (state) => { | |
27 | + return { | |
28 | + run: state.code | |
29 | + } | |
28 | 30 | } |
29 | 31 | } |
30 | 32 | |
31 | 33 | let codeHandlers = exports.handlers = { |
common.js | ||
---|---|---|
@@ -1,8 +1,8 @@ | ||
1 | -const Message = require('./message') | |
1 | +const Message = require('primea-message') | |
2 | 2 | |
3 | -exports.PARENT = 0 | |
4 | -exports.ROOT = 1 | |
3 | +exports.PARENT = '..' | |
4 | +exports.ROOT = '/' | |
5 | 5 | exports.getterMessage = (name, path) => { |
6 | 6 | const message = new Message({ |
7 | 7 | data: { |
8 | 8 | getValue: name |
defaultAgent.js | ||
---|---|---|
@@ -1,9 +1,9 @@ | ||
1 | 1 | exports.run = async (message, kernel) => { |
2 | - const to = message.to[message.hops] | |
2 | + const to = message.nextPort() | |
3 | 3 | if (to !== undefined) { |
4 | - return kernel.send(message) | |
4 | + await kernel.send(message) | |
5 | + return | |
5 | 6 | } else if (message.data.getValue) { |
6 | - console.log('get value') | |
7 | 7 | return (await kernel.state.get(message.data.getValue)).value |
8 | 8 | } |
9 | 9 | } |
hypervisor.js | ||
---|---|---|
@@ -1,23 +1,20 @@ | ||
1 | 1 | const Kernel = require('./index.js') |
2 | -const Vertex = require('merkle-trie') | |
3 | -// const Block = require('./deps/block.js') | |
4 | -// const blockchain = require('./fakeBlockChain.js') | |
5 | 2 | const codeHandlers = require('./codeHandler.js') |
6 | 3 | |
7 | 4 | module.exports = class Hypervisor { |
8 | - constructor (state = new Vertex(), imports = []) { | |
5 | + constructor (graph, state, imports = []) { | |
9 | 6 | this.state = state |
7 | + this.graph = graph | |
10 | 8 | this.root = new Kernel({ |
11 | 9 | imports: imports, |
10 | + graph: graph, | |
12 | 11 | state: state |
13 | 12 | }) |
14 | 13 | } |
15 | 14 | |
16 | - set (path, kernel) { | |
17 | - this.state.set(path, new Vertex({ | |
18 | - value: kernel | |
19 | - })) | |
15 | + set (path, value) { | |
16 | + return this.graph.set(this.state, path, value) | |
20 | 17 | } |
21 | 18 | |
22 | 19 | send (message) { |
23 | 20 | return this.root.send(message) |
index.js | ||
---|---|---|
@@ -1,33 +1,40 @@ | ||
1 | 1 | const EventEmitter = require('events') |
2 | -const Vertex = require('merkle-trie') | |
3 | 2 | const PortManager = require('./portManager.js') |
4 | 3 | const codeHandler = require('./codeHandler.js') |
5 | 4 | |
6 | 5 | module.exports = class Kernel extends EventEmitter { |
7 | 6 | constructor (opts = {}) { |
8 | 7 | super() |
9 | 8 | // set up the state |
10 | - const state = this.state = opts.state || new Vertex() | |
11 | - this.path = state.path | |
9 | + this.graph = opts.graph | |
10 | + this.path = opts.path || '' | |
11 | + this.imports = opts.imports | |
12 | + const state = this.state = opts.state || {} | |
12 | 13 | |
13 | 14 | // set up the vm |
14 | - this.imports = opts.imports | |
15 | - this._vm = (opts.codeHandler || codeHandler).init(opts.code || state.value) | |
15 | + this._vm = (opts.codeHandler || codeHandler).init(opts.code || state) | |
16 | 16 | this._vmstate = 'idle' |
17 | 17 | |
18 | 18 | // set up ports |
19 | - this.ports = new PortManager(state, opts.parentPort, Kernel, this.imports) | |
19 | + this.ports = new PortManager({ | |
20 | + state: state, | |
21 | + graph: this.graph, | |
22 | + parentPort: opts.parentPort, | |
23 | + Kernel: Kernel, | |
24 | + imports: this.imports, | |
25 | + path: this.path | |
26 | + }) | |
27 | + | |
20 | 28 | this.ports.on('message', index => { |
21 | 29 | this.runNextMessage(index) |
22 | 30 | }) |
23 | - this._sentAtomicMessages = [] | |
24 | 31 | } |
25 | 32 | |
26 | 33 | runNextMessage (index = 0) { |
27 | 34 | // load the next message from port space |
28 | 35 | return this.ports.peek(index).then(message => { |
29 | - if (message && (message._isCyclic(this) || this._vmstate === 'idle')) { | |
36 | + if (message && (message.isCyclic(this) || this._vmstate === 'idle')) { | |
30 | 37 | this._currentMessage = message |
31 | 38 | this.ports.remove(index) |
32 | 39 | return this.run(message) |
33 | 40 | } else { |
@@ -42,18 +49,17 @@ | ||
42 | 49 | * The Kernel Stores all of its state in the Environment. The Interface is used |
43 | 50 | * to by the VM to retrive infromation from the Environment. |
44 | 51 | */ |
45 | 52 | async run (message, imports = this.imports) { |
53 | + const self = this | |
46 | 54 | function revert (oldState) { |
47 | 55 | // revert the state |
48 | - this.state.set([], oldState) | |
49 | - // revert all the sent messages | |
50 | - for (let msg in this._sentAtomicMessages) { | |
51 | - msg.revert() | |
52 | - } | |
56 | + clearObject(self.state) | |
57 | + Object.assign(self.state, oldState) | |
53 | 58 | } |
54 | 59 | |
55 | - const oldState = this.state.copy() | |
60 | + // shallow copy | |
61 | + const oldState = Object.assign({}, this.state) | |
56 | 62 | let result |
57 | 63 | this._vmstate = 'running' |
58 | 64 | try { |
59 | 65 | result = await this._vm.run(message, this, imports) || {} |
@@ -63,44 +69,40 @@ | ||
63 | 69 | exceptionError: e |
64 | 70 | } |
65 | 71 | } |
66 | 72 | |
67 | - if (message.atomic) { | |
68 | - // if we trapped revert all the sent messages | |
69 | - if (result.execption) { | |
70 | - // revert to the old state | |
71 | - revert(oldState) | |
72 | - } | |
73 | - message._finish() | |
74 | - message.result().then(result => { | |
75 | - if (result.execption) { | |
76 | - revert() | |
77 | - } else { | |
78 | - this.runNextMessage(0) | |
79 | - } | |
80 | - }) | |
73 | + // if we trapped revert all the sent messages | |
74 | + if (result.exception) { | |
75 | + // revert to the old state | |
76 | + revert(oldState) | |
77 | + message.reject(result) | |
78 | + } else if (!message.hasResponded) { | |
79 | + message.respond(result) | |
80 | + } | |
81 | 81 | |
82 | - if (message.hops === message.to.length || result.exception) { | |
83 | - message._respond(result) | |
84 | - } | |
85 | - } else { | |
86 | - // non-atomic messages | |
82 | + message.committed().then(() => { | |
87 | 83 | this.runNextMessage(0) |
88 | - } | |
84 | + }).catch((e) => { | |
85 | + revert(oldState) | |
86 | + }) | |
89 | 87 | return result |
90 | 88 | } |
91 | 89 | |
92 | 90 | async send (message) { |
93 | 91 | if (message.atomic) { |
94 | 92 | // record that this message has traveled thourgh this kernel. This is used |
95 | 93 | // to detect re-entry |
96 | 94 | message._visited(this, this._currentMessage) |
97 | - // recoded that this message was sent, so that we can revert it if needed | |
98 | - this._sentAtomicMessages.push(message) | |
99 | 95 | } |
100 | 96 | return this.ports.send(message) |
101 | 97 | } |
102 | 98 | |
103 | 99 | shutdown () { |
104 | 100 | this.ports.close() |
105 | 101 | } |
106 | 102 | } |
103 | + | |
104 | +function clearObject (myObject) { | |
105 | + for (var member in myObject) { | |
106 | + delete myObject[member] | |
107 | + } | |
108 | +} |
package.json | ||
---|---|---|
@@ -27,8 +27,9 @@ | ||
27 | 27 | "contributors": "Alex Beregszaszi <alex@rtfs.hu>", |
28 | 28 | "license": "MPL-2.0", |
29 | 29 | "devDependencies": { |
30 | 30 | "coveralls": "^2.11.16", |
31 | + "ipfs": "^0.23.1", | |
31 | 32 | "istanbul": "^1.1.0-alpha.1", |
32 | 33 | "standard": "10.0.0-beta.0", |
33 | 34 | "tape": "^4.5.1" |
34 | 35 | }, |
@@ -40,8 +41,9 @@ | ||
40 | 41 | "WebAssembly" |
41 | 42 | ] |
42 | 43 | }, |
43 | 44 | "dependencies": { |
45 | + "deepcopy": "^0.6.3", | |
44 | 46 | "ethereumjs-block": "^1.5.0", |
45 | 47 | "ethereumjs-tx": "^1.2.5", |
46 | 48 | "ethereumjs-util": "^5.1.0", |
47 | 49 | "fixed-bn.js": "0.0.2", |
port.js | ||
---|---|---|
@@ -15,9 +15,9 @@ | ||
15 | 15 | } |
16 | 16 | } |
17 | 17 | |
18 | 18 | async send (message) { |
19 | - message.hops++ | |
19 | + message._hops++ | |
20 | 20 | this.destPort.recieve(message) |
21 | 21 | } |
22 | 22 | |
23 | 23 | async recieve (message) { |
portManager.js | ||
---|---|---|
@@ -1,15 +1,14 @@ | ||
1 | 1 | const EventEmitter = require('events') |
2 | +const path = require('path') | |
2 | 3 | const Port = require('./port.js') |
3 | 4 | const common = require('./common.js') |
4 | 5 | |
5 | 6 | module.exports = class PortManager extends EventEmitter { |
6 | - constructor (state, destParentPort, KernelContructor, imports) { | |
7 | + constructor (opts) { | |
7 | 8 | super() |
9 | + Object.assign(this, opts) | |
8 | 10 | this._queue = [] |
9 | - this.state = state | |
10 | - this.imports = imports | |
11 | - this.Kernel = KernelContructor | |
12 | 11 | // set up the parent port |
13 | 12 | const parentPort = new Port(common.PARENT) |
14 | 13 | parentPort.on('message', message => { |
15 | 14 | this._recieveMessage(message) |
@@ -32,13 +31,16 @@ | ||
32 | 31 | port.on('message', message => { |
33 | 32 | this._recieveMessage(message) |
34 | 33 | }) |
35 | 34 | // create destination kernel |
36 | - const state = await this.state.get(name) | |
35 | + const state = await this.graph.get(this.state, name) | |
36 | + | |
37 | 37 | const destKernel = new this.Kernel({ |
38 | 38 | state: state, |
39 | + graph: this.graph, | |
39 | 40 | parentPort: port, |
40 | - imports: this.imports | |
41 | + imports: this.imports, | |
42 | + path: path.join(this.path, name) | |
41 | 43 | }) |
42 | 44 | |
43 | 45 | // shutdown the kernel when it is done doing it work |
44 | 46 | destKernel.on('idle', () => { |
tests/apiTests.js | ||
---|---|---|
@@ -1,53 +1,65 @@ | ||
1 | 1 | const tape = require('tape') |
2 | 2 | const Hypervisor = require('../hypervisor.js') |
3 | -const Message = require('../message.js') | |
4 | -const Vertex = require('merkle-trie') | |
3 | +const Message = require('primea-message') | |
4 | +const IPFS = require('ipfs') | |
5 | +const Graph = require('ipld-graph-builder') | |
5 | 6 | |
6 | -tape('send and reciving messages', async t => { | |
7 | - try { | |
8 | - const hypervisor = new Hypervisor() | |
9 | - const path = ['one', 'two', 'three'] | |
10 | - hypervisor.set(path, { | |
11 | - run: message => { | |
12 | - t.pass('got message') | |
13 | - t.end() | |
14 | - return {} | |
15 | - } | |
16 | - }) | |
17 | - hypervisor.send(new Message({ | |
18 | - to: path | |
19 | - })) | |
20 | - } catch (e) { | |
21 | - console.log(e) | |
22 | - } | |
23 | -}) | |
7 | +const ipfs = new IPFS() | |
8 | +const graph = new Graph(ipfs) | |
24 | 9 | |
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'] | |
10 | +ipfs.on('start', async () => { | |
11 | + tape('send and reciving messages', async t => { | |
12 | + const root = {} | |
13 | + try { | |
14 | + const hypervisor = new Hypervisor(graph, root) | |
15 | + const path = 'one/two/three' | |
16 | + await hypervisor.set(path, { | |
17 | + code: message => { | |
18 | + t.pass('got message') | |
19 | + t.end() | |
20 | + return {} | |
21 | + } | |
22 | + }) | |
23 | + hypervisor.send(new Message({ | |
24 | + to: path | |
33 | 25 | })) |
34 | - throw new Error('vm exception') | |
26 | + } catch (e) { | |
27 | + // console.log(e) | |
35 | 28 | } |
36 | 29 | }) |
37 | 30 | |
38 | - hypervisor.set(path2, { | |
39 | - run: (message, kernel) => { | |
40 | - kernel.stateInterface.set('key', new Vertex({ | |
41 | - value: 'value' | |
42 | - })) | |
43 | - } | |
44 | - }) | |
31 | + tape('reverts', async t => { | |
32 | + const root = {} | |
33 | + const hypervisor = new Hypervisor(graph, root) | |
34 | + const path = 'one/two/three' | |
35 | + const path2 = 'one/two/three/four' | |
36 | + await hypervisor.set(path, { | |
37 | + code: async (message, kernel) => { | |
38 | + await kernel.send(new Message({ | |
39 | + to: 'four' | |
40 | + })) | |
41 | + throw new Error('vm exception') | |
42 | + } | |
43 | + }) | |
45 | 44 | |
46 | - const message = new Message({ | |
47 | - to: path | |
45 | + await hypervisor.set(path2, { | |
46 | + code: (message, kernel) => { | |
47 | + kernel.graph.set(kernel.state, 'something', { | |
48 | + somevalue: 'value' | |
49 | + }) | |
50 | + return 'done!' | |
51 | + } | |
52 | + }) | |
53 | + | |
54 | + const message = new Message({ | |
55 | + to: path | |
56 | + }) | |
57 | + hypervisor.send(message) | |
58 | + const result = await message.result() | |
59 | + t.equals(result.exception, true) | |
60 | + const expectedRoot = '{"one":{"two":{"three":{"/":{"four":{"/":{}}}}}}}' | |
61 | + t.equals(JSON.stringify(root), expectedRoot, 'should produce correct root') | |
62 | + t.end() | |
63 | + process.exit() | |
48 | 64 | }) |
49 | - hypervisor.send(message) | |
50 | - const result = await message.result() | |
51 | - t.equals(result.exception, true) | |
52 | - t.end() | |
53 | 65 | }) |
tests/interfaceRunner.js | ||
---|---|---|
@@ -1,14 +1,14 @@ | ||
1 | 1 | const tape = require('tape') |
2 | 2 | const fs = require('fs') |
3 | -const Vertex = require('merkle-trie') | |
3 | +const Vertex = require('ipld-graph-builder') | |
4 | 4 | const Block = require('../deps/block') |
5 | 5 | const U128 = require('fixed-bn.js').U128 |
6 | 6 | const Address = require('fixed-bn.js').Address |
7 | 7 | // TODO remove fakeblockchain |
8 | 8 | const fakeBlockChain = require('../fakeBlockChain.js') |
9 | 9 | const Hypervisor = require('../hypervisor.js') |
10 | -const Message = require('../message.js') | |
10 | +const Message = require('primea-message') | |
11 | 11 | const common = require('../common') |
12 | 12 | const EVMinterface = require('../EVMinterface.js') |
13 | 13 | |
14 | 14 | const dir = `${__dirname}/interface` |
message.js | ||
---|---|---|
@@ -1,49 +1,0 @@ | ||
1 | -const U128 = require('fixed-bn.js').U128 | |
2 | - | |
3 | -module.exports = class Message { | |
4 | - constructor (opts = {}) { | |
5 | - const defaults = { | |
6 | - // call infromation | |
7 | - to: [], | |
8 | - from: [], | |
9 | - data: new Uint8Array(), | |
10 | - atomic: true, | |
11 | - // resource info | |
12 | - gas: new U128(0), | |
13 | - gasPrices: new U128(0) | |
14 | - } | |
15 | - Object.assign(this, defaults, opts) | |
16 | - this.hops = 0 | |
17 | - this._visitedKernels = [] | |
18 | - this._resultPromise = new Promise((resolve, reject) => { | |
19 | - this._resolve = resolve | |
20 | - }) | |
21 | - } | |
22 | - | |
23 | - result () { | |
24 | - return this._resultPromise | |
25 | - } | |
26 | - | |
27 | - nextPort () { | |
28 | - return this.to[this.hops] | |
29 | - } | |
30 | - | |
31 | - _respond (result) { | |
32 | - this._resolve(result) | |
33 | - } | |
34 | - | |
35 | - _finish () { | |
36 | - this._visitedKernels.pop() | |
37 | - } | |
38 | - | |
39 | - _visited (kernel, currentMessage) { | |
40 | - if (currentMessage && this !== currentMessage) { | |
41 | - this._visitedKernels = currentMessage._visitedKernels | |
42 | - } | |
43 | - this._visitedKernels.push(kernel) | |
44 | - } | |
45 | - | |
46 | - _isCyclic (kernel) { | |
47 | - return this.atomic && this._visitedKernels.some(process => process === kernel) | |
48 | - } | |
49 | -} |
Built with git-ssb-web