Files: 6bb03a319c96631972b44da6f0ed51b01c501f17 / tests / index.js
31096 bytesRaw
1 | const tape = require('tape') |
2 | const AbstractContainer = require('primea-abstract-container') |
3 | const Message = require('primea-message') |
4 | const Hypervisor = require('../') |
5 | |
6 | const level = require('level') |
7 | const RadixTree = require('dfinity-radix-tree') |
8 | const db = level('./testdb') |
9 | |
10 | class CreationMessage extends Message { |
11 | constructor (params) { |
12 | const buf = [Buffer.from([0x0]), Buffer.from([params.data.type])] |
13 | if (params.data.code) { |
14 | buf.push(params.data.code) |
15 | } |
16 | params.data = Buffer.concat(buf) |
17 | super(params) |
18 | } |
19 | } |
20 | |
21 | class BaseContainer extends AbstractContainer { |
22 | onCreation (message) { |
23 | const port = message.ports[0] |
24 | if (port) { |
25 | return this.kernel.ports.bind('root', port) |
26 | } |
27 | } |
28 | static get typeId () { |
29 | return 9 |
30 | } |
31 | } |
32 | |
33 | tape('basic', async t => { |
34 | t.plan(3) |
35 | let message |
36 | const expectedState = { |
37 | '/': Buffer.from('646e7b89a3a4bb5dd6d43d1a7a29c69e72943bcd', 'hex') |
38 | } |
39 | |
40 | const tree = new RadixTree({ |
41 | db: db |
42 | }) |
43 | |
44 | class testVMContainer extends BaseContainer { |
45 | onMessage (m) { |
46 | t.true(m === message, 'should recive a message') |
47 | } |
48 | } |
49 | |
50 | const hypervisor = new Hypervisor(tree) |
51 | hypervisor.registerContainer(testVMContainer) |
52 | |
53 | const port = hypervisor.creationService.getPort() |
54 | |
55 | let rootContainer = await hypervisor.send(port, new CreationMessage({ |
56 | data: { |
57 | type: testVMContainer.typeId |
58 | } |
59 | })) |
60 | |
61 | rootContainer = await hypervisor.getInstance(rootContainer.id) |
62 | |
63 | hypervisor.pin(rootContainer) |
64 | |
65 | const [portRef1, portRef2] = rootContainer.ports.createChannel() |
66 | const initMessage = new CreationMessage({ |
67 | data: { |
68 | code: Buffer.from('test code'), |
69 | type: testVMContainer.typeId |
70 | }, |
71 | ports: [portRef2] |
72 | }) |
73 | |
74 | message = rootContainer.createMessage() |
75 | await Promise.all([ |
76 | rootContainer.send(port, initMessage), |
77 | rootContainer.ports.bind('first', portRef1), |
78 | rootContainer.send(portRef1, message) |
79 | ]) |
80 | rootContainer.shutdown() |
81 | |
82 | const stateRoot = await hypervisor.createStateRoot(Infinity) |
83 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
84 | |
85 | t.equals(hypervisor.scheduler.leastNumberOfTicks(), 0) |
86 | }) |
87 | |
88 | tape('basic - do not store containers with no ports bound', async t => { |
89 | t.plan(1) |
90 | |
91 | const tree = new RadixTree({ |
92 | db: db |
93 | }) |
94 | |
95 | const expectedState = { |
96 | '/': Buffer.from('5a316218edbc909f511b41d936517b27bb51cd6c', 'hex') |
97 | } |
98 | |
99 | class testVMContainer extends BaseContainer { |
100 | onCreation () {} |
101 | } |
102 | |
103 | const hypervisor = new Hypervisor(tree) |
104 | hypervisor.registerContainer(testVMContainer) |
105 | |
106 | const creationPort = hypervisor.creationService.getPort() |
107 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
108 | data: { |
109 | type: testVMContainer.typeId |
110 | } |
111 | })) |
112 | |
113 | hypervisor.pin(root) |
114 | |
115 | root = await hypervisor.getInstance(root.id) |
116 | |
117 | const [portRef1, portRef2] = root.ports.createChannel() |
118 | |
119 | await Promise.all([ |
120 | root.ports.bind('one', portRef1), |
121 | root.send(creationPort, new CreationMessage({ |
122 | data: { |
123 | type: testVMContainer.typeId |
124 | }, |
125 | ports: [portRef2] |
126 | })) |
127 | ]) |
128 | |
129 | root.shutdown() |
130 | |
131 | const stateRoot = await hypervisor.createStateRoot(Infinity) |
132 | |
133 | // await hypervisor.graph.tree(stateRoot, Infinity, true) |
134 | // console.log(JSON.stringify(stateRoot, null, 2)) |
135 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
136 | }) |
137 | |
138 | tape('one child contract', async t => { |
139 | t.plan(4) |
140 | |
141 | const tree = new RadixTree({ |
142 | db: db |
143 | }) |
144 | |
145 | let message |
146 | const expectedState = { |
147 | '/': Buffer.from('c91821c303cd07adde06c0d46c40aafe4542dea1', 'hex') |
148 | } |
149 | |
150 | let hasResolved = false |
151 | |
152 | class testVMContainer2 extends BaseContainer { |
153 | onMessage (m) { |
154 | t.true(m === message, 'should recive a message') |
155 | return new Promise((resolve, reject) => { |
156 | setTimeout(() => { |
157 | this.kernel.incrementTicks(1) |
158 | hasResolved = true |
159 | resolve() |
160 | }, 200) |
161 | }) |
162 | } |
163 | |
164 | static get typeId () { |
165 | return 99 |
166 | } |
167 | } |
168 | |
169 | class testVMContainer extends BaseContainer { |
170 | async onMessage (m) { |
171 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
172 | const port = this.kernel.hypervisor.creationService.getPort() |
173 | |
174 | await Promise.all([ |
175 | this.kernel.send(port, new CreationMessage({ |
176 | data: { |
177 | type: testVMContainer2.typeId |
178 | }, |
179 | ports: [portRef2] |
180 | })), |
181 | this.kernel.send(portRef1, m) |
182 | ]) |
183 | |
184 | this.kernel.incrementTicks(1) |
185 | return this.kernel.ports.bind('child', portRef1) |
186 | } |
187 | } |
188 | |
189 | const hypervisor = new Hypervisor(tree) |
190 | hypervisor.registerContainer(testVMContainer) |
191 | hypervisor.registerContainer(testVMContainer2) |
192 | |
193 | let creationPort = hypervisor.creationService.getPort() |
194 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
195 | data: { |
196 | type: testVMContainer.typeId |
197 | } |
198 | })) |
199 | |
200 | hypervisor.pin(root) |
201 | |
202 | const rootId = root.id |
203 | root = await hypervisor.getInstance(rootId) |
204 | const [portRef1, portRef2] = root.ports.createChannel() |
205 | |
206 | message = root.createMessage() |
207 | await Promise.all([ |
208 | root.send(creationPort, new CreationMessage({ |
209 | data: { |
210 | type: testVMContainer.typeId |
211 | }, |
212 | ports: [portRef2] |
213 | })), |
214 | root.ports.bind('first', portRef1), |
215 | root.send(portRef1, message) |
216 | ]) |
217 | |
218 | root.shutdown() |
219 | |
220 | const stateRoot = await hypervisor.createStateRoot(Infinity) |
221 | t.true(hasResolved, 'should resolve before generating the state root') |
222 | t.deepEquals(stateRoot, expectedState, 'expected state') |
223 | |
224 | // test reviving the state |
225 | class testVMContainer3 extends BaseContainer { |
226 | onMessage (m) { |
227 | const port = this.kernel.ports.get('child') |
228 | this.kernel.send(port, m) |
229 | this.kernel.incrementTicks(1) |
230 | } |
231 | } |
232 | |
233 | hypervisor.registerContainer(testVMContainer3) |
234 | root = await hypervisor.getInstance(rootId) |
235 | const port = root.ports.get('first') |
236 | root.send(port, message) |
237 | }) |
238 | |
239 | tape('traps', async t => { |
240 | t.plan(1) |
241 | |
242 | const tree = new RadixTree({ |
243 | db: db |
244 | }) |
245 | class Root extends BaseContainer { |
246 | async onMessage (m) { |
247 | const [portRef1] = this.kernel.ports.createChannel() |
248 | const [portRef3] = this.kernel.ports.createChannel() |
249 | const [portRef5] = this.kernel.ports.createChannel() |
250 | |
251 | await Promise.all([ |
252 | this.kernel.ports.bind('one', portRef1), |
253 | this.kernel.ports.bind('two', portRef3), |
254 | this.kernel.ports.bind('three', portRef5) |
255 | ]) |
256 | |
257 | throw new Error('it is a trap!!!') |
258 | } |
259 | } |
260 | |
261 | const hypervisor = new Hypervisor(tree) |
262 | |
263 | hypervisor.registerContainer(Root) |
264 | const creationPort = hypervisor.creationService.getPort() |
265 | const root = await hypervisor.send(creationPort, new CreationMessage({ |
266 | data: { |
267 | type: Root.typeId |
268 | } |
269 | })) |
270 | |
271 | hypervisor.pin(root) |
272 | |
273 | await root.message(root.createMessage()) |
274 | const stateRoot = await hypervisor.createStateRoot() |
275 | |
276 | t.deepEquals(stateRoot, { |
277 | '/': Buffer.from('308b10121e2c46102e2d9701cfe11032786ef955', 'hex') |
278 | }, 'should revert the state') |
279 | }) |
280 | |
281 | tape('recieving older messages', async t => { |
282 | t.plan(2) |
283 | |
284 | const tree = new RadixTree({ |
285 | db: db |
286 | }) |
287 | let runs = 0 |
288 | const hypervisor = new Hypervisor(tree) |
289 | const creationPort = hypervisor.creationService.getPort() |
290 | |
291 | class Root extends BaseContainer { |
292 | async onMessage (m) { |
293 | if (!runs) { |
294 | runs++ |
295 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
296 | const [portRef3, portRef4] = this.kernel.ports.createChannel() |
297 | |
298 | const message1 = new CreationMessage({ |
299 | data: { |
300 | type: First.typeId |
301 | }, |
302 | ports: [portRef2] |
303 | }) |
304 | const message2 = new CreationMessage({ |
305 | data: { |
306 | type: Waiter.typeId |
307 | }, |
308 | ports: [portRef4] |
309 | }) |
310 | |
311 | return Promise.all([ |
312 | this.kernel.send(creationPort, message1), |
313 | this.kernel.send(portRef1, this.kernel.createMessage()), |
314 | this.kernel.send(portRef3, this.kernel.createMessage()), |
315 | this.kernel.ports.bind('one', portRef1), |
316 | this.kernel.ports.bind('two', portRef3), |
317 | this.kernel.send(creationPort, message2) |
318 | ]) |
319 | } else if (runs === 1) { |
320 | runs++ |
321 | t.equals(m.data, 'first', 'should recive the first message') |
322 | } else { |
323 | runs++ |
324 | t.equals(m.data, 'second', 'should recive the second message') |
325 | } |
326 | } |
327 | static get typeId () { |
328 | return 211 |
329 | } |
330 | } |
331 | |
332 | class First extends BaseContainer { |
333 | onMessage (m) { |
334 | this.kernel.incrementTicks(2) |
335 | return this.kernel.send(m.fromPort, this.kernel.createMessage({ |
336 | data: 'second' |
337 | })) |
338 | } |
339 | static get typeId () { |
340 | return 29 |
341 | } |
342 | } |
343 | |
344 | class Waiter extends BaseContainer { |
345 | onMessage (m) { |
346 | return new Promise((resolve, reject) => { |
347 | setTimeout(() => { |
348 | this.kernel.send(m.fromPort, this.kernel.createMessage({ |
349 | data: 'first' |
350 | })).then(resolve) |
351 | }, 200) |
352 | }) |
353 | } |
354 | } |
355 | |
356 | hypervisor.registerContainer(Root) |
357 | hypervisor.registerContainer(First) |
358 | hypervisor.registerContainer(Waiter) |
359 | |
360 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
361 | data: { |
362 | type: Root.typeId |
363 | } |
364 | })) |
365 | |
366 | hypervisor.pin(root) |
367 | |
368 | root = await hypervisor.getInstance(root.id) |
369 | const [portRef1, portRef2] = root.ports.createChannel() |
370 | |
371 | const message = root.createMessage() |
372 | await Promise.all([ |
373 | root.send(portRef1, message), |
374 | root.ports.bind('first', portRef1), |
375 | root.send(creationPort, new CreationMessage({ |
376 | data: { |
377 | type: Root.typeId |
378 | }, |
379 | ports: [portRef2] |
380 | })) |
381 | ]) |
382 | root.shutdown() |
383 | }) |
384 | |
385 | tape('saturation', async t => { |
386 | t.plan(3) |
387 | |
388 | const tree = new RadixTree({ |
389 | db: db |
390 | }) |
391 | let runs = 0 |
392 | |
393 | const hypervisor = new Hypervisor(tree) |
394 | const creationPort = hypervisor.creationService.getPort() |
395 | |
396 | class Root extends BaseContainer { |
397 | onIdle () {} |
398 | async onMessage (m) { |
399 | if (!runs) { |
400 | runs++ |
401 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
402 | const [portRef3, portRef4] = this.kernel.ports.createChannel() |
403 | |
404 | const message1 = new CreationMessage({ |
405 | data: { |
406 | type: First.typeId |
407 | }, |
408 | ports: [portRef2] |
409 | }) |
410 | |
411 | const message2 = new CreationMessage({ |
412 | data: { |
413 | type: Second.typeId |
414 | }, |
415 | ports: [portRef4] |
416 | }) |
417 | |
418 | this.kernel.incrementTicks(6) |
419 | await Promise.all([ |
420 | this.kernel.send(creationPort, message1), |
421 | this.kernel.send(creationPort, message2), |
422 | this.kernel.send(portRef1, this.kernel.createMessage()), |
423 | this.kernel.send(portRef3, this.kernel.createMessage()), |
424 | this.kernel.ports.bind('one', portRef1), |
425 | this.kernel.ports.bind('two', portRef3) |
426 | ]) |
427 | } else if (runs === 1) { |
428 | runs++ |
429 | t.equals(m.data, 'first', 'should recive the first message') |
430 | } else if (runs === 2) { |
431 | runs++ |
432 | t.equals(m.data, 'second', 'should recive the second message') |
433 | } else { |
434 | runs++ |
435 | t.equals(m.data, 'third', 'should recived the third message') |
436 | } |
437 | } |
438 | static get typeId () { |
439 | return 112 |
440 | } |
441 | } |
442 | |
443 | class First extends BaseContainer { |
444 | onMessage (m) { |
445 | this.kernel.incrementTicks(2) |
446 | return this.kernel.send(m.fromPort, this.kernel.createMessage({ |
447 | data: 'second' |
448 | })) |
449 | } |
450 | static get typeId () { |
451 | return 29 |
452 | } |
453 | } |
454 | |
455 | class Second extends BaseContainer { |
456 | onMessage (m) { |
457 | this.kernel.incrementTicks(3) |
458 | return this.kernel.send(m.fromPort, this.kernel.createMessage({ |
459 | data: 'third' |
460 | })) |
461 | } |
462 | static get typeId () { |
463 | return 2 |
464 | } |
465 | } |
466 | |
467 | class Waiter extends BaseContainer { |
468 | onCreation (m) { |
469 | return new Promise((resolve, reject) => { |
470 | setTimeout(() => { |
471 | this.kernel.send(m.ports[0], this.kernel.createMessage({ |
472 | data: 'first' |
473 | })).then(resolve) |
474 | }, 200) |
475 | }) |
476 | } |
477 | } |
478 | |
479 | hypervisor.registerContainer(Root) |
480 | hypervisor.registerContainer(First) |
481 | hypervisor.registerContainer(Second) |
482 | hypervisor.registerContainer(Waiter) |
483 | |
484 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
485 | data: { |
486 | type: Root.typeId |
487 | } |
488 | })) |
489 | |
490 | hypervisor.pin(root) |
491 | root = await hypervisor.getInstance(root.id) |
492 | |
493 | const [portRef1, portRef2] = root.ports.createChannel() |
494 | const [portRef3, portRef4] = root.ports.createChannel() |
495 | |
496 | const message = root.createMessage() |
497 | await Promise.all([ |
498 | root.send(portRef1, message), |
499 | root.ports.bind('first', portRef1), |
500 | root.send(creationPort, new CreationMessage({ |
501 | data: { |
502 | type: Root.typeId |
503 | }, |
504 | ports: [portRef2] |
505 | })), |
506 | root.ports.bind('sencond', portRef3), |
507 | root.send(creationPort, new CreationMessage({ |
508 | data: { |
509 | type: Waiter.typeId |
510 | }, |
511 | ports: [portRef4] |
512 | })) |
513 | ]) |
514 | |
515 | root.incrementTicks(100) |
516 | await root.send(portRef1, root.createMessage({ |
517 | data: 'testss' |
518 | })) |
519 | root.shutdown() |
520 | }) |
521 | |
522 | tape('send to the same container at the same time', async t => { |
523 | t.plan(2) |
524 | |
525 | const tree = new RadixTree({ |
526 | db: db |
527 | }) |
528 | let runs = 0 |
529 | let instance |
530 | |
531 | const hypervisor = new Hypervisor(tree) |
532 | const creationPort = hypervisor.creationService.getPort() |
533 | |
534 | class Root extends BaseContainer { |
535 | async onMessage (m) { |
536 | let one = this.kernel.ports.get('one') |
537 | if (!one) { |
538 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
539 | const message1 = new CreationMessage({ |
540 | data: { |
541 | type: First.typeId |
542 | }, |
543 | ports: [portRef2] |
544 | }) |
545 | await this.kernel.send(creationPort, message1) |
546 | return this.kernel.ports.bind('one', portRef1) |
547 | } else { |
548 | return Promise.all([ |
549 | this.kernel.send(one, this.kernel.createMessage()), |
550 | this.kernel.send(one, this.kernel.createMessage()) |
551 | ]) |
552 | } |
553 | } |
554 | static get typeId () { |
555 | return 111 |
556 | } |
557 | } |
558 | |
559 | class First extends BaseContainer { |
560 | onMessage (m) { |
561 | ++runs |
562 | if (runs === 2) { |
563 | t.equals(instance, this, 'should have same instances') |
564 | } else { |
565 | instance = this |
566 | } |
567 | } |
568 | } |
569 | |
570 | hypervisor.registerContainer(Root) |
571 | hypervisor.registerContainer(First) |
572 | |
573 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
574 | data: { |
575 | type: Root.typeId |
576 | } |
577 | })) |
578 | |
579 | hypervisor.pin(root) |
580 | root = await hypervisor.getInstance(root.id) |
581 | |
582 | const [portRef1, portRef2] = root.ports.createChannel() |
583 | await Promise.all([ |
584 | root.ports.bind('first', portRef1), |
585 | root.send(creationPort, new CreationMessage({ |
586 | data: { |
587 | type: Root.typeId |
588 | }, |
589 | ports: [portRef2] |
590 | })) |
591 | ]) |
592 | |
593 | const message = root.createMessage() |
594 | |
595 | await root.send(portRef1, message) |
596 | root.shutdown() |
597 | await hypervisor.createStateRoot() |
598 | await root.send(portRef1, root.createMessage()) |
599 | await hypervisor.createStateRoot() |
600 | t.equals(runs, 2) |
601 | }) |
602 | |
603 | tape('checking ports', async t => { |
604 | t.plan(4) |
605 | |
606 | const tree = new RadixTree({ |
607 | db: db |
608 | }) |
609 | const hypervisor = new Hypervisor(tree) |
610 | const creationPort = hypervisor.creationService.getPort() |
611 | hypervisor.registerContainer(BaseContainer) |
612 | |
613 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
614 | data: { |
615 | type: BaseContainer.typeId |
616 | } |
617 | })) |
618 | |
619 | hypervisor.pin(root) |
620 | root = await hypervisor.getInstance(root.id) |
621 | |
622 | const [portRef1, portRef2] = root.ports.createChannel() |
623 | root.send(creationPort, new CreationMessage({ |
624 | data: { |
625 | type: BaseContainer.typeId |
626 | }, |
627 | ports: [portRef2] |
628 | })) |
629 | await root.ports.bind('test', portRef1) |
630 | |
631 | try { |
632 | root.createMessage({ |
633 | ports: [portRef1] |
634 | }) |
635 | } catch (e) { |
636 | t.pass('should thow if sending a port that is bound') |
637 | } |
638 | |
639 | try { |
640 | await root.ports.bind('test', portRef1) |
641 | } catch (e) { |
642 | t.pass('should thow if binding an already bound port') |
643 | } |
644 | |
645 | try { |
646 | const [portRef3] = root.ports.createChannel() |
647 | await root.ports.bind('test', portRef3) |
648 | } catch (e) { |
649 | t.pass('should thow if binding an already bound name') |
650 | } |
651 | |
652 | await root.ports.unbind('test') |
653 | const message = root.createMessage({ |
654 | ports: [portRef1] |
655 | }) |
656 | t.equals(message.ports[0], portRef1, 'should create a message if the port is unbound') |
657 | }) |
658 | |
659 | tape('port deletion', async t => { |
660 | const expectedSr = { |
661 | '/': Buffer.from('1f0673f23b4eeb86115992621d7edc981a6afade', 'hex') |
662 | } |
663 | |
664 | const tree = new RadixTree({ |
665 | db: db |
666 | }) |
667 | const hypervisor = new Hypervisor(tree) |
668 | const creationPort = hypervisor.creationService.getPort() |
669 | |
670 | class Root extends BaseContainer { |
671 | async onMessage (m) { |
672 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
673 | const message1 = new CreationMessage({ |
674 | data: { |
675 | type: First.typeId |
676 | }, |
677 | ports: [portRef2] |
678 | }) |
679 | |
680 | await Promise.all([ |
681 | this.kernel.send(creationPort, message1), |
682 | this.kernel.send(portRef1, this.kernel.createMessage()) |
683 | ]) |
684 | this.kernel.incrementTicks(6) |
685 | return this.kernel.ports.bind('one', portRef1) |
686 | } |
687 | } |
688 | |
689 | class First extends BaseContainer { |
690 | onMessage (m) { |
691 | this.kernel.incrementTicks(2) |
692 | return this.kernel.ports.delete('root') |
693 | } |
694 | static get typeId () { |
695 | return 111 |
696 | } |
697 | } |
698 | |
699 | hypervisor.registerContainer(Root) |
700 | hypervisor.registerContainer(First) |
701 | |
702 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
703 | data: { |
704 | type: Root.typeId |
705 | } |
706 | })) |
707 | |
708 | hypervisor.pin(root) |
709 | root = await hypervisor.getInstance(root.id) |
710 | |
711 | const [portRef1, portRef2] = root.ports.createChannel() |
712 | await root.ports.bind('first', portRef1) |
713 | await root.send(creationPort, new CreationMessage({ |
714 | data: { |
715 | type: Root.typeId |
716 | }, |
717 | ports: [portRef2] |
718 | })) |
719 | |
720 | const message = root.createMessage() |
721 | await root.send(portRef1, message) |
722 | |
723 | root.shutdown() |
724 | |
725 | const sr = await hypervisor.createStateRoot() |
726 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
727 | |
728 | t.end() |
729 | }) |
730 | |
731 | tape('clear unbounded ports', async t => { |
732 | const expectedSr = { |
733 | '/': Buffer.from('1f0673f23b4eeb86115992621d7edc981a6afade', 'hex') |
734 | } |
735 | |
736 | const tree = new RadixTree({ |
737 | db: db |
738 | }) |
739 | const hypervisor = new Hypervisor(tree) |
740 | const creationPort = hypervisor.creationService.getPort() |
741 | |
742 | class Root extends BaseContainer { |
743 | onMessage (m) { |
744 | return this.kernel.send(creationPort, new CreationMessage({ |
745 | data: { |
746 | type: Root.typeId |
747 | } |
748 | })) |
749 | } |
750 | } |
751 | |
752 | hypervisor.registerContainer(Root) |
753 | |
754 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
755 | data: { |
756 | type: Root.typeId |
757 | } |
758 | })) |
759 | |
760 | root = await hypervisor.getInstance(root.id) |
761 | hypervisor.pin(root) |
762 | |
763 | const [portRef1, portRef2] = root.ports.createChannel() |
764 | await root.ports.bind('first', portRef1) |
765 | await root.send(creationPort, new CreationMessage({ |
766 | data: { |
767 | type: Root.typeId |
768 | }, |
769 | ports: [portRef2] |
770 | })) |
771 | |
772 | const message = root.createMessage() |
773 | await root.send(portRef1, message) |
774 | root.shutdown() |
775 | const sr = await hypervisor.createStateRoot() |
776 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
777 | |
778 | t.end() |
779 | }) |
780 | |
781 | tape('should remove subgraphs', async t => { |
782 | const expectedSr = { |
783 | '/': Buffer.from('1f0673f23b4eeb86115992621d7edc981a6afade', 'hex') |
784 | } |
785 | |
786 | const tree = new RadixTree({ |
787 | db: db |
788 | }) |
789 | const hypervisor = new Hypervisor(tree) |
790 | const creationPort = hypervisor.creationService.getPort() |
791 | |
792 | class Root extends BaseContainer { |
793 | onMessage (m) { |
794 | const [, portRef2] = this.kernel.ports.createChannel() |
795 | return this.kernel.send(creationPort, new CreationMessage({ |
796 | data: { |
797 | type: Sub.typeId |
798 | }, |
799 | ports: [portRef2] |
800 | })) |
801 | } |
802 | } |
803 | |
804 | class Sub extends BaseContainer { |
805 | static get typeId () { |
806 | return 299 |
807 | } |
808 | } |
809 | |
810 | hypervisor.registerContainer(Root) |
811 | hypervisor.registerContainer(Sub) |
812 | |
813 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
814 | data: { |
815 | type: Root.typeId |
816 | } |
817 | })) |
818 | |
819 | root = await hypervisor.getInstance(root.id) |
820 | |
821 | hypervisor.pin(root) |
822 | |
823 | const [portRef1, portRef2] = root.ports.createChannel() |
824 | await root.ports.bind('first', portRef1) |
825 | await root.send(creationPort, new CreationMessage({ |
826 | data: { |
827 | type: Root.typeId |
828 | }, |
829 | ports: [portRef2] |
830 | })) |
831 | |
832 | await root.send(portRef1, root.createMessage()) |
833 | root.shutdown() |
834 | const sr = await hypervisor.createStateRoot() |
835 | |
836 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
837 | t.end() |
838 | }) |
839 | |
840 | tape('should not remove connected nodes', async t => { |
841 | const tree = new RadixTree({ |
842 | db: db |
843 | }) |
844 | |
845 | const expectedSr = { |
846 | '/': Buffer.from('9aeb0ce35c0ab845e7b273afe0329f826297124e', 'hex') |
847 | } |
848 | |
849 | const hypervisor = new Hypervisor(tree) |
850 | const creationPort = hypervisor.creationService.getPort() |
851 | |
852 | class Root extends BaseContainer { |
853 | async onMessage (m) { |
854 | if (m.ports.length) { |
855 | const port = this.kernel.ports.get('test1') |
856 | await this.kernel.send(port, m) |
857 | return this.kernel.ports.unbind('test1') |
858 | } else { |
859 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
860 | await this.kernel.send(creationPort, new CreationMessage({ |
861 | data: { |
862 | type: Sub.typeId |
863 | }, |
864 | ports: [portRef2] |
865 | })) |
866 | await this.kernel.ports.bind('test1', portRef1) |
867 | |
868 | const [portRef3, portRef4] = this.kernel.ports.createChannel() |
869 | await this.kernel.send(creationPort, new CreationMessage({ |
870 | data: { |
871 | type: Sub.typeId |
872 | }, |
873 | ports: [portRef4] |
874 | })) |
875 | await this.kernel.ports.bind('test2', portRef3) |
876 | await this.kernel.send(portRef3, this.kernel.createMessage({ |
877 | data: 'getChannel' |
878 | })) |
879 | } |
880 | } |
881 | } |
882 | |
883 | class Sub extends BaseContainer { |
884 | async onMessage (message) { |
885 | if (message.data === 'getChannel') { |
886 | const ports = this.kernel.ports.createChannel() |
887 | await this.kernel.send(message.fromPort, this.kernel.createMessage({ |
888 | data: 'bindPort', |
889 | ports: [ports[1]] |
890 | })) |
891 | return this.kernel.ports.bind('channel', ports[0]) |
892 | } else { |
893 | return this.kernel.ports.bind('channel', message.ports[0]) |
894 | } |
895 | } |
896 | static get typeId () { |
897 | return 111 |
898 | } |
899 | } |
900 | |
901 | hypervisor.registerContainer(Root) |
902 | hypervisor.registerContainer(Sub) |
903 | |
904 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
905 | data: { |
906 | type: Root.typeId |
907 | } |
908 | })) |
909 | |
910 | root = await hypervisor.getInstance(root.id) |
911 | hypervisor.pin(root) |
912 | |
913 | const [portRef1, portRef2] = root.ports.createChannel() |
914 | await root.ports.bind('first', portRef1) |
915 | await root.send(creationPort, new CreationMessage({ |
916 | data: { |
917 | type: Root.typeId |
918 | }, |
919 | ports: [portRef2] |
920 | })) |
921 | |
922 | await root.send(portRef1, root.createMessage()) |
923 | root.shutdown() |
924 | const sr = await hypervisor.createStateRoot() |
925 | |
926 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
927 | t.end() |
928 | }) |
929 | |
930 | tape('should remove multiple subgraphs', async t => { |
931 | const tree = new RadixTree({ |
932 | db: db |
933 | }) |
934 | const expectedSr = { |
935 | '/': Buffer.from('196a7b55f26afb41f065923332e14b40cd0edf2e', 'hex') |
936 | } |
937 | |
938 | const hypervisor = new Hypervisor(tree) |
939 | const creationPort = hypervisor.creationService.getPort() |
940 | |
941 | class Root extends BaseContainer { |
942 | onMessage (m) { |
943 | if (m.ports.length) { |
944 | const port = this.kernel.ports.get('test1') |
945 | return Promise.all([ |
946 | this.kernel.send(port, m), |
947 | this.kernel.ports.unbind('test1'), |
948 | this.kernel.ports.unbind('test2') |
949 | ]) |
950 | } else { |
951 | const [portRef1, portRef2] = this.kernel.ports.createChannel() |
952 | const [portRef3, portRef4] = this.kernel.ports.createChannel() |
953 | return Promise.all([ |
954 | this.kernel.send(creationPort, new CreationMessage({ |
955 | data: { |
956 | type: Sub.typeId |
957 | }, |
958 | ports: [portRef2] |
959 | })), |
960 | this.kernel.ports.bind('test1', portRef1), |
961 | this.kernel.send(creationPort, new CreationMessage({ |
962 | data: { |
963 | type: Sub.typeId |
964 | }, |
965 | ports: [portRef4] |
966 | })), |
967 | this.kernel.ports.bind('test2', portRef3), |
968 | this.kernel.send(portRef3, this.kernel.createMessage({ |
969 | data: 'getChannel' |
970 | })) |
971 | ]) |
972 | } |
973 | } |
974 | } |
975 | |
976 | class Sub extends BaseContainer { |
977 | async onMessage (message) { |
978 | if (message.data === 'getChannel') { |
979 | const ports = this.kernel.ports.createChannel() |
980 | await this.kernel.send(message.fromPort, this.kernel.createMessage({ |
981 | data: 'bindPort', |
982 | ports: [ports[1]] |
983 | })) |
984 | return this.kernel.ports.bind('channel', ports[0]) |
985 | } else { |
986 | return this.kernel.ports.bind('channel', message.ports[0]) |
987 | } |
988 | } |
989 | static get typeId () { |
990 | return 111 |
991 | } |
992 | } |
993 | |
994 | hypervisor.registerContainer(Root) |
995 | hypervisor.registerContainer(Sub) |
996 | |
997 | let root = await hypervisor.send(creationPort, new CreationMessage({ |
998 | data: { |
999 | type: Root.typeId |
1000 | } |
1001 | })) |
1002 | |
1003 | root = await hypervisor.getInstance(root.id) |
1004 | hypervisor.pin(root) |
1005 | |
1006 | const [portRef1, portRef2] = root.ports.createChannel() |
1007 | await Promise.all([ |
1008 | root.ports.bind('first', portRef1), |
1009 | root.send(creationPort, new CreationMessage({ |
1010 | data: { |
1011 | type: Root.typeId |
1012 | }, |
1013 | ports: [portRef2] |
1014 | })), |
1015 | root.send(portRef1, root.createMessage()) |
1016 | ]) |
1017 | |
1018 | root.shutdown() |
1019 | |
1020 | const sr = await hypervisor.createStateRoot() |
1021 | |
1022 | t.deepEquals(sr, expectedSr, 'should produce the corret state root') |
1023 | |
1024 | t.end() |
1025 | }) |
1026 | |
1027 | tape('response ports', async t => { |
1028 | t.plan(2) |
1029 | const tree = new RadixTree({ |
1030 | db: db |
1031 | }) |
1032 | let runs = 0 |
1033 | const returnValue = 'this is a test' |
1034 | const hypervisor = new Hypervisor(tree) |
1035 | const creationPort = hypervisor.creationService.getPort() |
1036 | |
1037 | class testVMContainer extends BaseContainer { |
1038 | onMessage (m) { |
1039 | runs++ |
1040 | if (runs === 1) { |
1041 | return returnValue |
1042 | } else { |
1043 | t.equals(m.data, returnValue, 'should have correct return value') |
1044 | } |
1045 | } |
1046 | } |
1047 | |
1048 | hypervisor.registerContainer(testVMContainer) |
1049 | |
1050 | let rootContainer = await hypervisor.send(creationPort, new CreationMessage({ |
1051 | data: { |
1052 | type: testVMContainer.typeId |
1053 | } |
1054 | })) |
1055 | |
1056 | rootContainer = await hypervisor.getInstance(rootContainer.id) |
1057 | |
1058 | hypervisor.pin(rootContainer) |
1059 | |
1060 | const [portRef1, portRef2] = rootContainer.ports.createChannel() |
1061 | const initMessage = new CreationMessage({ |
1062 | data: { |
1063 | type: testVMContainer.typeId |
1064 | }, |
1065 | ports: [portRef2] |
1066 | }) |
1067 | |
1068 | rootContainer.send(creationPort, initMessage) |
1069 | |
1070 | await rootContainer.ports.bind('first', portRef1) |
1071 | const message = rootContainer.createMessage() |
1072 | const rPort = rootContainer.getResponsePort(message) |
1073 | const rPort2 = rootContainer.getResponsePort(message) |
1074 | |
1075 | t.equals(rPort2, rPort) |
1076 | |
1077 | rootContainer.send(portRef1, message) |
1078 | await rootContainer.ports.bind('response', rPort) |
1079 | }) |
1080 | |
1081 | tape('start up', async t => { |
1082 | t.plan(1) |
1083 | |
1084 | const tree = new RadixTree({ |
1085 | db: db |
1086 | }) |
1087 | |
1088 | class testVMContainer extends BaseContainer { |
1089 | onStartup () { |
1090 | t.pass('should start up') |
1091 | } |
1092 | } |
1093 | |
1094 | const hypervisor = new Hypervisor(tree) |
1095 | const creationPort = hypervisor.creationService.getPort() |
1096 | hypervisor.registerContainer(testVMContainer) |
1097 | const instance = await hypervisor.send(creationPort, new CreationMessage({ |
1098 | data: { |
1099 | type: testVMContainer.typeId |
1100 | } |
1101 | })) |
1102 | hypervisor.getInstance(instance.id) |
1103 | }) |
1104 | |
1105 | tape('large code size', async t => { |
1106 | t.plan(1) |
1107 | const tree = new RadixTree({ |
1108 | db: db |
1109 | }) |
1110 | const content = Buffer.from(new ArrayBuffer(1e6)) |
1111 | class testVMContainer extends BaseContainer {} |
1112 | |
1113 | const hypervisor = new Hypervisor(tree) |
1114 | const creationPort = hypervisor.creationService.getPort() |
1115 | hypervisor.registerContainer(testVMContainer) |
1116 | const oldInst = await hypervisor.send(creationPort, new CreationMessage({ |
1117 | data: { |
1118 | type: testVMContainer.typeId, |
1119 | code: content |
1120 | } |
1121 | })) |
1122 | const instance = await hypervisor.getInstance(oldInst.id) |
1123 | t.equals(content.length, instance.code.length) |
1124 | }) |
1125 | |
1126 | tape('creation service messaging', async t => { |
1127 | t.plan(1) |
1128 | |
1129 | const tree = new RadixTree({ |
1130 | db: db |
1131 | }) |
1132 | |
1133 | class TestVMContainer extends BaseContainer { |
1134 | async onCreation (m) { |
1135 | const creationPort = m.ports[0] |
1136 | const [port1, port2] = this.kernel.ports.createChannel() |
1137 | await this.kernel.ports.bind('child', port1) |
1138 | |
1139 | const message = new CreationMessage({ |
1140 | data: { |
1141 | type: TestVMContainer2.typeId |
1142 | }, |
1143 | ports: [port2] |
1144 | }) |
1145 | return this.kernel.send(creationPort, message) |
1146 | } |
1147 | } |
1148 | |
1149 | class TestVMContainer2 extends BaseContainer { |
1150 | static get typeId () { |
1151 | return 66 |
1152 | } |
1153 | } |
1154 | |
1155 | const hypervisor = new Hypervisor(tree) |
1156 | hypervisor.registerContainer(TestVMContainer) |
1157 | hypervisor.registerContainer(TestVMContainer2) |
1158 | |
1159 | const port = hypervisor.creationService.getPort() |
1160 | const port2 = hypervisor.creationService.getPort() |
1161 | |
1162 | const root = await hypervisor.send(port2, new CreationMessage({ |
1163 | data: { |
1164 | type: TestVMContainer.typeId |
1165 | }, |
1166 | ports: [port] |
1167 | })) |
1168 | |
1169 | hypervisor.pin(root) |
1170 | |
1171 | const stateRoot = await hypervisor.createStateRoot() |
1172 | // await hypervisor.graph.tree(hypervisor.state, Infinity, true) |
1173 | const expectedSR = { |
1174 | '/': Buffer.from('c86f6a4519b4a18e1f31abe357a84712aabce8d2', 'hex') |
1175 | } |
1176 | t.deepEquals(stateRoot, expectedSR) |
1177 | }) |
1178 | |
1179 | tape('creation service - port copy', async t => { |
1180 | t.plan(2) |
1181 | |
1182 | const tree = new RadixTree({ |
1183 | db: db |
1184 | }) |
1185 | |
1186 | class TestVMContainer extends BaseContainer { |
1187 | onCreation (m) { |
1188 | const creationPort = m.ports[0] |
1189 | |
1190 | const message = this.kernel.createMessage() |
1191 | const responePort = this.kernel.getResponsePort(message) |
1192 | |
1193 | return Promise.all([ |
1194 | this.kernel.ports.bind('response', responePort), |
1195 | this.kernel.send(creationPort, message) |
1196 | ]) |
1197 | } |
1198 | onMessage (m) { |
1199 | t.equal(m.fromName, 'response') |
1200 | t.equal(m.ports.length, 1) |
1201 | } |
1202 | } |
1203 | |
1204 | const hypervisor = new Hypervisor(tree) |
1205 | hypervisor.registerContainer(TestVMContainer) |
1206 | |
1207 | const port = hypervisor.creationService.getPort() |
1208 | |
1209 | const root = await hypervisor.send(port, new CreationMessage({ |
1210 | data: { |
1211 | type: TestVMContainer.typeId |
1212 | }, |
1213 | ports: [port] |
1214 | })) |
1215 | |
1216 | hypervisor.pin(root) |
1217 | }) |
1218 | |
1219 | tape('waiting on ports', async t => { |
1220 | t.plan(1) |
1221 | |
1222 | const tree = new RadixTree({ |
1223 | db: db |
1224 | }) |
1225 | |
1226 | class TestVMContainer extends BaseContainer { |
1227 | async onCreation (m) { |
1228 | await this.kernel.ports.bind('test', m.ports[0]) |
1229 | this.kernel.ports.getNextMessage() |
1230 | try { |
1231 | await this.kernel.ports.getNextMessage() |
1232 | } catch (e) { |
1233 | t.pass('should throw if already trying to get a message') |
1234 | } |
1235 | } |
1236 | } |
1237 | |
1238 | const hypervisor = new Hypervisor(tree) |
1239 | hypervisor.registerContainer(TestVMContainer) |
1240 | |
1241 | const port = hypervisor.creationService.getPort() |
1242 | |
1243 | await hypervisor.send(port, new CreationMessage({ |
1244 | data: { |
1245 | type: TestVMContainer.typeId |
1246 | }, |
1247 | ports: [port] |
1248 | })) |
1249 | }) |
1250 |
Built with git-ssb-web