git ssb

0+

wanderer🌟 / js-primea-hypervisor



Commit 0f4618d69a1944599b2e15d8398df11ca4901eaa

added revert test

wanderer committed on 2/25/2017, 11:36:12 AM
Parent: d9ab14281e683bbcd8f54b5cb7874edbbdd088a7

Files changed

index.jschanged
package.jsonchanged
port.jschanged
portManager.jschanged
tests/apiTests.jschanged
index.jsView
@@ -1,33 +1,34 @@
11 const EventEmitter = require('events')
22 const Vertex = require('merkle-trie')
33 const PortManager = require('./portManager.js')
4+const StateInterface = require('./stateInterface.js')
45 const imports = require('./EVMinterface.js')
56 const codeHandler = require('./codeHandler.js')
67 const common = require('./common.js')
78
89 module.exports = class Kernel extends EventEmitter {
910 constructor (opts = {}) {
1011 super()
1112 const state = this.state = opts.state || new Vertex()
13+ this.stateInterface = new StateInterface(state)
1214 this.code = opts.code || state.value
1315 this.path = state.path
1416 this.imports = opts.imports || [imports]
1517 this.ports = new PortManager(state, opts.parent, Kernel)
18+ this._sentAtomicMessages = []
1619 // rename sandbox?
1720 this._vm = (opts.codeHandler || codeHandler).init(this.code)
1821 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)
2424 })
2525 }
2626
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)
3031 this.run(message)
3132 } else {
3233 this._state = 'idle'
3334 this.emit('idle')
@@ -40,29 +41,43 @@
4041 * The Kernel Stores all of its state in the Environment. The Interface is used
4142 * to by the VM to retrive infromation from the Environment.
4243 */
4344 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-
4845 function revert () {
4946 // revert the state
5047 this.state.set([], oldState)
5148 // 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)
5453 }
5554
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+ }
5665 if (result.execption) {
5766 // failed messages
5867 revert()
5968 } else if (message.atomic) {
6069 // 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+ })
6277 } else {
6378 // non-atomic messages
64- this.runNextMessage()
79+ this.runNextMessage(0)
6580 }
6681 return result
6782 }
6883
@@ -75,22 +90,13 @@
7590 portName = common.PARENT
7691 message.to = new Array(this.path.length).fill(common.PARENT).concat(message.to)
7792 }
7893 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+ }
7998 return port.send(message)
8099 }
81100
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
95101 shutdown () {}
96102 }
package.jsonView
@@ -44,8 +44,7 @@
4444 "bn.js": "^4.11.6",
4545 "ethereumjs-block": "^1.4.1",
4646 "ethereumjs-tx": "^1.1.2",
4747 "ethereumjs-util": "^5.0.0",
48- "merkle-trie": "0.1.2",
49- "promise-queue": "^2.2.3"
48+ "merkle-trie": "0.1.2"
5049 }
5150 }
port.jsView
@@ -2,9 +2,8 @@
22
33 module.exports = class Port extends EventEmitter {
44 constructor () {
55 super()
6- this.queue = []
76 }
87
98 connect (destPort) {
109 this.destPort = destPort
@@ -16,11 +15,6 @@
1615 }
1716
1817 async recieve (message) {
1918 this.emit('message', message)
20- this.queue.push(message)
2119 }
22-
23- dequeue () {
24- return this.queue.unshift()
25- }
2620 }
portManager.jsView
@@ -4,27 +4,32 @@
44
55 module.exports = class PortManager extends EventEmitter {
66 constructor (state, destParentPort, KernelContructor) {
77 super()
8+ this._queue = []
89 this.state = state
9- this.sentMessage = []
1010 this.Kernel = KernelContructor
1111 // set up the parent port
1212 const parentPort = new Port()
1313 parentPort.on('message', message => {
14- this.emit('message', message)
14+ this._recieveMessage(message)
1515 })
1616 // create the cache
1717 this.cache = new Map()
1818 this.cache.set(common.PARENT, parentPort)
1919 }
2020
21+ _recieveMessage (message) {
22+ const index = this._queue.push(message) - 1
23+ this.emit('message', index)
24+ }
25+
2126 async get (name) {
2227 let port = this.cache.get(name)
2328 if (!port) {
2429 port = new Port()
2530 port.on('message', message => {
26- this.emit('message', message)
31+ this._recieveMessage(message)
2732 })
2833 // create destination kernel
2934 const state = await this.state.get(name)
3035 const destKernel = new this.Kernel({
@@ -45,20 +50,16 @@
4550 }
4651 return port
4752 }
4853
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]
5956 }
6057
58+ remove (index) {
59+ return this._queue.splice(index, index + 1)
60+ }
61+
6162 close () {
6263 for (let port in this.cache) {
6364 port.emit('close')
6465 }
tests/apiTests.jsView
@@ -1,7 +1,8 @@
11 const tape = require('tape')
22 const Hypervisor = require('../hypervisor.js')
33 const Message = require('../message.js')
4+const Vertex = require('merkle-trie')
45
56 tape('send and reciving messages', async t => {
67 try {
78 const hypervisor = new Hypervisor()
@@ -14,11 +15,42 @@
1415 }
1516 })
1617 hypervisor.send(new Message({
1718 to: path
18- })).catch(e => {
19- console.log(e)
20- })
19+ }))
2120 } catch (e) {
2221 console.log(e)
2322 }
2423 })
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