Files: e7082de1f1be0792df1a00c4efb43344119a6e11 / tests / index.js
17168 bytesRaw
1 | const tape = require('tape') |
2 | const Hypervisor = require('../') |
3 | const errors = require('../errors.json') |
4 | const {Message, FunctionRef, ActorRef, ModuleRef} = require('primea-objects') |
5 | |
6 | const level = require('level-browserify') |
7 | const EgressDriver = require('../egressDriver') |
8 | const RadixTree = require('dfinity-radix-tree') |
9 | const db = level('./testdb') |
10 | |
11 | class BaseModule { |
12 | constructor (actor) { |
13 | this.actor = actor |
14 | } |
15 | onStartup () {} |
16 | static onCreation (code) { |
17 | const exp = {} |
18 | Object.getOwnPropertyNames(this.prototype).filter(name => name !== 'constructor').forEach(name => { |
19 | exp[name] = {} |
20 | }) |
21 | return { |
22 | exports: exp, |
23 | state: [] |
24 | } |
25 | } |
26 | onMessage (message) { |
27 | return this[message.funcRef.identifier[1]](...message.funcArguments) |
28 | } |
29 | static get typeId () { |
30 | return 9 |
31 | } |
32 | } |
33 | |
34 | tape('system objects', async t => { |
35 | t.plan(4) |
36 | const tree = new RadixTree({ |
37 | db |
38 | }) |
39 | |
40 | let id |
41 | let mod |
42 | let funcref |
43 | |
44 | class TestModule extends BaseModule { |
45 | store () { |
46 | id = this.actor.id.id.toString('hex') |
47 | const modRef = new ModuleRef(1, 1, {'test': ['i32', 'i64']}) |
48 | mod = new ActorRef(this.actor.id, modRef) |
49 | funcref = mod.getFuncRef('test') |
50 | this.actor.storage = [this.actor.id, {'/': 'test'}, mod, funcref] |
51 | } |
52 | load () { |
53 | const loadedID = this.actor.storage[0].id.toString('hex') |
54 | const link = this.actor.storage[1] |
55 | const loadedMod = this.actor.storage[2] |
56 | const loadedFuncref = this.actor.storage[3] |
57 | t.equals(id, loadedID, 'should load id correctly') |
58 | t.equals(link['/'].toString('hex'), '6fe3180f700090697285ac1e0e8dc400259373d7', 'should load link correctly') |
59 | t.deepEquals(loadedMod, mod) |
60 | t.deepEquals(funcref, loadedFuncref) |
61 | } |
62 | } |
63 | |
64 | const hypervisor = new Hypervisor({tree, modules: [TestModule]}) |
65 | const actor = hypervisor.newActor(TestModule) |
66 | const message = new Message({ |
67 | funcRef: actor.getFuncRef('store') |
68 | }) |
69 | |
70 | hypervisor.send(message) |
71 | await hypervisor.createStateRoot() |
72 | |
73 | const message2 = new Message({ |
74 | funcRef: actor.getFuncRef('load') |
75 | }) |
76 | hypervisor.send(message2) |
77 | await hypervisor.createStateRoot() |
78 | t.end() |
79 | }) |
80 | |
81 | tape('basic', async t => { |
82 | // t.plan(2) |
83 | const expectedState = Buffer.from('be8e779df9d2ba23e5a6fcb3e551708cbc0fde82', 'hex') |
84 | const tree = new RadixTree({ |
85 | db |
86 | }) |
87 | |
88 | class TestModule extends BaseModule { |
89 | main (m) { |
90 | t.equals(m, 1, 'should recive a message') |
91 | } |
92 | } |
93 | |
94 | const hypervisor = new Hypervisor({tree, modules: [TestModule]}) |
95 | await hypervisor.createStateRoot() |
96 | |
97 | const module = hypervisor.createModule(TestModule) |
98 | const actor = hypervisor.createActor(module) |
99 | |
100 | const message = new Message({ |
101 | funcRef: actor.getFuncRef('main'), |
102 | funcArguments: [1] |
103 | }) |
104 | hypervisor.send(message) |
105 | |
106 | const stateRoot2 = await hypervisor.createStateRoot() |
107 | t.deepEquals(stateRoot2, expectedState, 'expected root!') |
108 | t.end() |
109 | }) |
110 | |
111 | tape('two communicating actors', async t => { |
112 | t.plan(2) |
113 | const expectedState = Buffer.from('25bc7e81511bfded44a1846f4bca1acc99f24273', 'hex') |
114 | |
115 | const tree = new RadixTree({ |
116 | db |
117 | }) |
118 | |
119 | class TestModuleA extends BaseModule { |
120 | main (funcRef) { |
121 | const message = new Message({ |
122 | funcRef, |
123 | funcArguments: [2] |
124 | }) |
125 | return this.actor.send(message) |
126 | } |
127 | } |
128 | |
129 | class TestModuleB extends BaseModule { |
130 | main (args) { |
131 | t.equals(args, 2, 'should recive a message') |
132 | } |
133 | |
134 | static get typeId () { |
135 | return 8 |
136 | } |
137 | } |
138 | |
139 | const hypervisor = new Hypervisor({ |
140 | tree, |
141 | modules: [TestModuleA, TestModuleB] |
142 | }) |
143 | |
144 | const actorB = hypervisor.newActor(TestModuleB) |
145 | const actorA = hypervisor.newActor(TestModuleA) |
146 | |
147 | const message = new Message({ |
148 | funcRef: actorA.getFuncRef('main'), |
149 | funcArguments: [actorB.getFuncRef('main')] |
150 | }) |
151 | |
152 | hypervisor.send(message) |
153 | |
154 | const stateRoot = await hypervisor.createStateRoot() |
155 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
156 | }) |
157 | |
158 | tape('three communicating actors', async t => { |
159 | t.plan(3) |
160 | const expectedState = Buffer.from('862f3393675dd33fb7e9b93b43aac7a9131ef665', 'hex') |
161 | const tree = new RadixTree({ |
162 | db: db |
163 | }) |
164 | |
165 | class TestModuleA extends BaseModule { |
166 | main (funcRef) { |
167 | const message = new Message({ |
168 | funcRef: funcRef, |
169 | funcArguments: [2] |
170 | }) |
171 | this.actor.send(message) |
172 | } |
173 | } |
174 | |
175 | class TestModuleB extends BaseModule { |
176 | main (arg) { |
177 | t.equals(arg, 2, 'should recive a message') |
178 | } |
179 | |
180 | static get typeId () { |
181 | return 8 |
182 | } |
183 | } |
184 | |
185 | const hypervisor = new Hypervisor({tree, modules: [TestModuleA, TestModuleB]}) |
186 | |
187 | let actorB = hypervisor.newActor(TestModuleB) |
188 | let actorA0 = hypervisor.newActor(TestModuleA) |
189 | let actorA1 = hypervisor.newActor(TestModuleA) |
190 | |
191 | const message0 = new Message({ |
192 | funcRef: actorA0.getFuncRef('main'), |
193 | funcArguments: [actorB.getFuncRef('main')] |
194 | }) |
195 | |
196 | const message1 = new Message({ |
197 | funcRef: actorA1.getFuncRef('main'), |
198 | funcArguments: [actorB.getFuncRef('main')] |
199 | }) |
200 | |
201 | await hypervisor.send(message0) |
202 | await hypervisor.send(message1) |
203 | |
204 | const stateRoot = await hypervisor.createStateRoot() |
205 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
206 | }) |
207 | |
208 | tape('three communicating actors, with tick counting', async t => { |
209 | t.plan(3) |
210 | const expectedState = Buffer.from('862f3393675dd33fb7e9b93b43aac7a9131ef665', 'hex') |
211 | const tree = new RadixTree({ |
212 | db |
213 | }) |
214 | |
215 | class TestModuleA extends BaseModule { |
216 | main (funcRef) { |
217 | this.actor.incrementTicks(1) |
218 | const message = new Message({ |
219 | funcRef, |
220 | funcArguments: [2] |
221 | }) |
222 | this.actor.send(message) |
223 | } |
224 | } |
225 | |
226 | class TestModuleB extends BaseModule { |
227 | main (arg) { |
228 | t.equals(arg, 2, 'should recive a message') |
229 | } |
230 | |
231 | static get typeId () { |
232 | return 8 |
233 | } |
234 | } |
235 | |
236 | const hypervisor = new Hypervisor({tree, modules: [TestModuleA, TestModuleB]}) |
237 | |
238 | let actorB = hypervisor.newActor(TestModuleB) |
239 | let actorA0 = hypervisor.newActor(TestModuleA) |
240 | let actorA1 = hypervisor.newActor(TestModuleA) |
241 | |
242 | const funcRef0 = actorA0.getFuncRef('main') |
243 | funcRef0.gas = 10000 |
244 | |
245 | const message0 = new Message({ |
246 | funcRef: funcRef0, |
247 | funcArguments: [actorB.getFuncRef('main')] |
248 | }) |
249 | const funcRef1 = actorA1.getFuncRef('main') |
250 | funcRef1.gas = 10000 |
251 | const message1 = new Message({ |
252 | funcRef: funcRef1, |
253 | funcArguments: [actorB.getFuncRef('main')] |
254 | }) |
255 | |
256 | hypervisor.send(message0) |
257 | hypervisor.send(message1) |
258 | |
259 | const stateRoot = await hypervisor.createStateRoot() |
260 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
261 | }) |
262 | |
263 | tape('errors', async t => { |
264 | t.plan(3) |
265 | const expectedState = Buffer.from('25bc7e81511bfded44a1846f4bca1acc99f24273', 'hex') |
266 | const tree = new RadixTree({ |
267 | db |
268 | }) |
269 | |
270 | class TestModuleA extends BaseModule { |
271 | main (funcRef) { |
272 | const message = new Message({ |
273 | funcRef |
274 | }) |
275 | message.on('execution:error', () => { |
276 | t.pass('should recive a exeption') |
277 | }) |
278 | this.actor.send(message) |
279 | } |
280 | } |
281 | |
282 | class TestModuleB extends BaseModule { |
283 | main (funcRef) { |
284 | t.true(true, 'should recive a message') |
285 | throw new Error('test error') |
286 | } |
287 | |
288 | static get typeId () { |
289 | return 8 |
290 | } |
291 | } |
292 | |
293 | const hypervisor = new Hypervisor({tree, modules: [TestModuleB, TestModuleA]}) |
294 | |
295 | let actorB = hypervisor.newActor(TestModuleB) |
296 | let actorA = hypervisor.newActor(TestModuleA) |
297 | const message = new Message({ |
298 | funcRef: actorA.getFuncRef('main'), |
299 | funcArguments: [actorB.getFuncRef('main')] |
300 | }) |
301 | hypervisor.send(message) |
302 | const stateRoot = await hypervisor.createStateRoot() |
303 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
304 | }) |
305 | |
306 | tape('out-of-gas', async t => { |
307 | t.plan(1) |
308 | const tree = new RadixTree({ |
309 | db |
310 | }) |
311 | |
312 | class testVMContainer extends BaseModule { |
313 | main (m) { |
314 | this.actor.incrementTicks(1) |
315 | } |
316 | } |
317 | |
318 | const hypervisor = new Hypervisor({tree, modules: [testVMContainer]}) |
319 | await hypervisor.createStateRoot() |
320 | |
321 | const actor = hypervisor.newActor(testVMContainer) |
322 | |
323 | const message = new Message({ |
324 | funcRef: actor.getFuncRef('main'), |
325 | funcArguments: [1] |
326 | }).on('execution:error', e => { |
327 | t.equals(e.message, errors.OUT_OF_GAS) |
328 | }) |
329 | hypervisor.send(message) |
330 | }) |
331 | |
332 | tape('no mettering', async t => { |
333 | t.plan(1) |
334 | const tree = new RadixTree({ |
335 | db |
336 | }) |
337 | |
338 | class testVMContainer extends BaseModule { |
339 | main (m) { |
340 | this.actor.incrementTicks(1) |
341 | t.pass('shouldnt meter') |
342 | } |
343 | } |
344 | |
345 | const hypervisor = new Hypervisor({ |
346 | tree, |
347 | modules: [testVMContainer], |
348 | meter: false |
349 | }) |
350 | await hypervisor.createStateRoot() |
351 | |
352 | const actor = hypervisor.newActor(testVMContainer) |
353 | |
354 | const message = new Message({ |
355 | funcRef: actor.getFuncRef('main'), |
356 | funcArguments: [1] |
357 | }) |
358 | hypervisor.send(message) |
359 | }) |
360 | |
361 | tape('actor creation', async t => { |
362 | t.plan(2) |
363 | const expectedState = Buffer.from('007c3ef07195d2c3959f22f1d2719ed4f4e4193a', 'hex') |
364 | |
365 | const tree = new RadixTree({ |
366 | db |
367 | }) |
368 | |
369 | class TestModuleA extends BaseModule { |
370 | async start (funcRef) { |
371 | const actor = this.actor.newActor(TestModuleB) |
372 | const message = new Message({ |
373 | funcRef: actor.getFuncRef('main'), |
374 | funcArguments: [{ |
375 | identifier: [0, 'main'], |
376 | actorId: this.actor.id |
377 | }] |
378 | }) |
379 | this.actor.send(message) |
380 | } |
381 | main (data) { |
382 | t.equals(data, 'test', 'should recive a response message') |
383 | } |
384 | } |
385 | |
386 | class TestModuleB extends BaseModule { |
387 | main (funcRef) { |
388 | this.actor.send(new Message({funcRef, funcArguments: ['test']})) |
389 | } |
390 | |
391 | static get typeId () { |
392 | return 8 |
393 | } |
394 | } |
395 | |
396 | const hypervisor = new Hypervisor({tree, modules: [TestModuleA, TestModuleB]}) |
397 | |
398 | const actor = hypervisor.newActor(TestModuleA) |
399 | await hypervisor.send(new Message({funcRef: actor.getFuncRef('start')})) |
400 | |
401 | const stateRoot = await hypervisor.createStateRoot() |
402 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
403 | t.end() |
404 | }) |
405 | |
406 | tape('simple message arbiter test', async t => { |
407 | t.plan(4) |
408 | const expectedState = Buffer.from('25bc7e81511bfded44a1846f4bca1acc99f24273', 'hex') |
409 | const tree = new RadixTree({ |
410 | db |
411 | }) |
412 | |
413 | class TestModuleA extends BaseModule { |
414 | main (funcRef) { |
415 | funcRef.gas = 1000 |
416 | const message1 = new Message({ |
417 | funcArguments: ['first'], |
418 | funcRef |
419 | }) |
420 | const message2 = new Message({ |
421 | funcArguments: ['second'], |
422 | funcRef |
423 | }) |
424 | const message3 = new Message({ |
425 | funcArguments: ['third'], |
426 | funcRef |
427 | }) |
428 | this.actor.send(message1) |
429 | this.actor.incrementTicks(1) |
430 | this.actor.send(message2) |
431 | this.actor.incrementTicks(1) |
432 | this.actor.send(message3) |
433 | } |
434 | } |
435 | |
436 | let recMsg = 0 |
437 | |
438 | class TestModuleB extends BaseModule { |
439 | main (data) { |
440 | this.actor.incrementTicks(1) |
441 | if (recMsg === 0) { |
442 | t.equal(data, 'first', 'should recive fist message') |
443 | } else if (recMsg === 1) { |
444 | t.equal(data, 'second', 'should recive second message') |
445 | } else { |
446 | t.equal(data, 'third', 'should recive third message') |
447 | } |
448 | recMsg++ |
449 | } |
450 | |
451 | static get typeId () { |
452 | return 8 |
453 | } |
454 | } |
455 | |
456 | const hypervisor = new Hypervisor({tree, modules: [TestModuleA, TestModuleB]}) |
457 | |
458 | const actorB = hypervisor.newActor(TestModuleB) |
459 | const actorA = hypervisor.newActor(TestModuleA) |
460 | const funcRef = actorA.getFuncRef('main') |
461 | funcRef.gas = 4000 |
462 | |
463 | const message = new Message({ |
464 | funcRef, |
465 | funcArguments: [actorB.getFuncRef('main')] |
466 | }) |
467 | hypervisor.send(message) |
468 | |
469 | const stateRoot = await hypervisor.createStateRoot() |
470 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
471 | }) |
472 | |
473 | tape('arbiter test for id comparision', async t => { |
474 | t.plan(5) |
475 | let message |
476 | const expectedState = Buffer.from('862f3393675dd33fb7e9b93b43aac7a9131ef665', 'hex') |
477 | |
478 | const tree = new RadixTree({ |
479 | db: db |
480 | }) |
481 | |
482 | class TestModuleA extends BaseModule { |
483 | main (funcRef, funcArguments) { |
484 | message = new Message({ |
485 | funcRef, |
486 | funcArguments: [funcArguments] |
487 | }) |
488 | this.actor.send(message) |
489 | } |
490 | } |
491 | |
492 | let recMsg = 0 |
493 | |
494 | class TestModuleB extends BaseModule { |
495 | main (data) { |
496 | if (recMsg === 0) { |
497 | t.equal(data, 'first', 'should recive fist message') |
498 | } else if (recMsg === 1) { |
499 | t.equal(data, 'second', 'should recive second message') |
500 | } else { |
501 | t.equal(data, 'third', 'should recive third message') |
502 | } |
503 | recMsg++ |
504 | } |
505 | |
506 | static get typeId () { |
507 | return 8 |
508 | } |
509 | } |
510 | |
511 | const hypervisor = new Hypervisor({tree, modules: [TestModuleA, TestModuleB]}) |
512 | |
513 | let actorB = hypervisor.newActor(TestModuleB) |
514 | hypervisor.send(new Message({ |
515 | funcRef: actorB.getFuncRef('main'), |
516 | funcArguments: ['first'] |
517 | })) |
518 | |
519 | const sr1 = await hypervisor.createStateRoot() |
520 | |
521 | const actorA1 = hypervisor.newActor(TestModuleA) |
522 | const actorA0 = hypervisor.newActor(TestModuleA) |
523 | |
524 | hypervisor.send(new Message({ |
525 | funcRef: actorA0.getFuncRef('main'), |
526 | funcArguments: [actorB.getFuncRef('main'), 'second'] |
527 | })) |
528 | |
529 | hypervisor.send(new Message({ |
530 | funcRef: actorA1.getFuncRef('main'), |
531 | funcArguments: [actorB.getFuncRef('main'), 'third'] |
532 | })) |
533 | |
534 | const stateRoot = await hypervisor.createStateRoot() |
535 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
536 | |
537 | await hypervisor.setStateRoot(sr1) |
538 | t.equals(hypervisor.nonce, 2, 'should get the correct nonce') |
539 | t.end() |
540 | }) |
541 | |
542 | tape('async work', async t => { |
543 | t.plan(3) |
544 | const expectedState = Buffer.from('25bc7e81511bfded44a1846f4bca1acc99f24273', 'hex') |
545 | |
546 | const tree = new RadixTree({ |
547 | db |
548 | }) |
549 | |
550 | class TestModuleA extends BaseModule { |
551 | main (funcRef) { |
552 | funcRef.gas = 10 |
553 | const message = new Message({ |
554 | funcRef, |
555 | funcArguments: [2] |
556 | }) |
557 | this.actor.send(message) |
558 | |
559 | const message2 = new Message({ |
560 | funcRef, |
561 | funcArguments: [2] |
562 | }) |
563 | this.actor.send(message2) |
564 | this.actor.incrementTicks(1) |
565 | return new Promise((resolve, reject) => { |
566 | setTimeout(() => { |
567 | resolve() |
568 | }, 10) |
569 | }) |
570 | } |
571 | } |
572 | |
573 | class TestModuleB extends BaseModule { |
574 | main (args) { |
575 | this.actor.incrementTicks(1) |
576 | t.equals(args, 2, 'should recive a message') |
577 | } |
578 | |
579 | static get typeId () { |
580 | return 8 |
581 | } |
582 | } |
583 | |
584 | const hypervisor = new Hypervisor({tree, modules: [TestModuleA, TestModuleB]}) |
585 | |
586 | const actorB = hypervisor.newActor(TestModuleB) |
587 | const actorA = hypervisor.newActor(TestModuleA) |
588 | const funcRef = actorA.getFuncRef('main') |
589 | funcRef.gas = 200 |
590 | |
591 | const message = new Message({ |
592 | funcRef, |
593 | funcArguments: [actorB.getFuncRef('main')] |
594 | }) |
595 | |
596 | hypervisor.send(message) |
597 | |
598 | const stateRoot = await hypervisor.createStateRoot() |
599 | t.deepEquals(stateRoot, expectedState, 'expected root!') |
600 | }) |
601 | |
602 | tape('driver', async t => { |
603 | const tree = new RadixTree({ |
604 | db |
605 | }) |
606 | |
607 | const egress = new EgressDriver() |
608 | |
609 | egress.on('message', msg => { |
610 | t.equals(msg.funcArguments[0], 'hello') |
611 | t.end() |
612 | }) |
613 | |
614 | class testVMContainer extends BaseModule { |
615 | main (funcRef) { |
616 | this.actor.send(new Message({ |
617 | funcRef, |
618 | funcArguments: ['hello'] |
619 | })) |
620 | } |
621 | } |
622 | |
623 | const hypervisor = new Hypervisor({ |
624 | tree, |
625 | modules: [testVMContainer], |
626 | drivers: [egress] |
627 | }) |
628 | const actor = hypervisor.newActor(testVMContainer) |
629 | |
630 | const message = new Message({ |
631 | funcRef: actor.getFuncRef('main'), |
632 | funcArguments: [new FunctionRef({actorId: egress.id})] |
633 | }) |
634 | |
635 | hypervisor.send(message) |
636 | }) |
637 | |
638 | tape('random', async t => { |
639 | const numOfActors = 10 |
640 | const depth = 10 |
641 | const messageOrder = {} |
642 | let numOfMsg = 0 |
643 | const tree = new RadixTree({ |
644 | db |
645 | }) |
646 | |
647 | class BenchmarkContainer extends BaseModule { |
648 | main () { |
649 | const refs = [...arguments] |
650 | const ref = refs.pop() |
651 | const last = messageOrder[this.actor.id.id.toString('hex')] |
652 | const message = this.actor.currentMessage |
653 | if (last !== undefined) { |
654 | t.ok(last <= message._fromTicks, 'message should be in correct order') |
655 | } |
656 | messageOrder[this.actor.id.id.toString('hex')] = message._fromTicks |
657 | numOfMsg++ |
658 | |
659 | this.actor.incrementTicks(10) |
660 | if (ref) { |
661 | ref.gas = this.actor.currentMessage.funcRef.gas |
662 | this.actor.send(new Message({ |
663 | funcRef: ref, |
664 | funcArguments: refs |
665 | })) |
666 | } |
667 | } |
668 | } |
669 | |
670 | const hypervisor = new Hypervisor({tree}) |
671 | hypervisor.registerModule(BenchmarkContainer) |
672 | |
673 | const references = [] |
674 | let _numOfActors = numOfActors |
675 | while (_numOfActors--) { |
676 | const actor = hypervisor.newActor(BenchmarkContainer) |
677 | const funcRef = actor.getFuncRef('main') |
678 | references.push(funcRef) |
679 | } |
680 | _numOfActors = numOfActors |
681 | const msgs = [] |
682 | while (_numOfActors--) { |
683 | let _depth = depth |
684 | const funcArguments = [] |
685 | while (_depth--) { |
686 | const r = Math.floor(Math.random() * numOfActors) |
687 | const ref = references[r].copy() |
688 | funcArguments.push(ref) |
689 | } |
690 | const funcRef = references[_numOfActors] |
691 | funcRef.gas = 1000000 |
692 | |
693 | const message = new Message({ |
694 | funcArguments, |
695 | funcRef |
696 | }) |
697 | msgs.push(message) |
698 | } |
699 | |
700 | hypervisor.send(msgs) |
701 | await hypervisor.scheduler.on('idle', () => { |
702 | t.equals(numOfMsg, depth * numOfActors + numOfActors) |
703 | t.end() |
704 | }) |
705 | }) |
706 |
Built with git-ssb-web