git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 1113088b6d1066919f231e1926ea7ebad172d198

Files: 1113088b6d1066919f231e1926ea7ebad172d198 / index.js

5600 bytesRaw
1const Tree = require('merkle-radix-tree')
2const Graph = require('ipld-graph-builder')
3const chunk = require('chunk')
4const Kernel = require('./kernel.js')
5const Scheduler = require('./scheduler.js')
6const DFSchecker = require('./dfsChecker.js')
7
8module.exports = class Hypervisor {
9 /**
10 * The Hypervisor manages the container instances by instantiating them and
11 * destorying them when possible. It also facilitates localating Containers
12 * @param {Graph} dag an instance of [ipfs.dag](https://github.com/ipfs/interface-ipfs-core/tree/master/API/dag#dag-api)
13 * @param {object} state - the starting state
14 */
15 constructor (dag, state = {'/': Tree.emptyTreeState}) {
16 this.graph = new Graph(dag)
17 this.tree = new Tree({
18 graph: this.graph,
19 root: state
20 })
21 this.scheduler = new Scheduler()
22 this.state = state
23 this._containerTypes = {}
24 this._nodesToCheck = new Set()
25
26 this.ROOT_ID = 'zdpuAm6aTdLVMUuiZypxkwtA7sKm7BWERy8MPbaCrFsmiyzxr'
27 this.CREATION_ID = 0
28 this.ROUTING_ID = 1
29 this.MAX_DATA_BYTES = 65533
30 }
31
32 /**
33 * add a potaintail node in the state graph to check for garbage collection
34 * @param {string} id
35 */
36 addNodeToCheck (id) {
37 this._nodesToCheck.add(id)
38 }
39
40 /**
41 * given a port, this finds the corridsponeding endpoint port of the channel
42 * @param {object} port
43 * @returns {Promise}
44 */
45 async getDestPort (port) {
46 if (port.destPort) {
47 return port.destPort
48 } else {
49 const containerState = await this.tree.get(port.destId)
50 return this.graph.get(containerState, `ports/${port.destName}`)
51 }
52 }
53
54 async send (port, message) {
55 const id = port.destId
56 if (id) {
57 const instance = await this.getInstance(id)
58 instance.queue(port, message)
59 } else {
60 // port is unbound
61 port.destPort.messages.push(message)
62 }
63 }
64
65 // loads an instance of a container from the state
66 async _loadInstance (id, state) {
67 if (!state) {
68 state = await this.tree.get(id)
69 }
70 const container = this._containerTypes[state.type]
71 let code
72
73 // checks if the code stored in the state is an array and that the elements
74 // are merkle link
75 if (state.code && state.code[0]['/']) {
76 await this.graph.tree(state.code, 1)
77 code = state.code.map(a => a['/']).reduce((a, b) => a + b)
78 } else {
79 code = state.code
80 }
81
82 // create a new kernel instance
83 const kernel = new Kernel({
84 hypervisor: this,
85 state: state,
86 code: code,
87 container: container,
88 id: id
89 })
90
91 // save the newly created instance
92 this.scheduler.update(kernel)
93 return kernel
94 }
95
96 // get a hash from a POJO
97 _getHashFromObj (obj) {
98 return this.graph.flush(obj).then(obj => obj['/'])
99 }
100
101 /**
102 * gets an existsing container instances
103 * @param {string} id - the containers ID
104 * @returns {Promise}
105 */
106 async getInstance (id) {
107 let instance = this.scheduler.getInstance(id)
108 if (instance) {
109 return instance
110 } else {
111 const resolve = this.scheduler.lock(id)
112 const instance = await this._loadInstance(id)
113 await instance.startup()
114 resolve(instance)
115 return instance
116 }
117 }
118
119 /**
120 * creates an new container instances and save it in the state
121 * @param {string} type - the type of container to create
122 * @param {*} code
123 * @param {array} entryPorts
124 * @param {object} id
125 * @param {object} id.nonce
126 * @param {object} id.parent
127 * @returns {Promise}
128 */
129 async createInstance (message, id = {nonce: 0, parent: null}) {
130 const idHash = await this._getHashFromObj(id)
131 const state = {
132 nonce: [0],
133 ports: {},
134 type: message.data.type
135 }
136
137 if (message.data.code && message.data.code.length) {
138 state.code = message.data.code
139 }
140
141 // create the container instance
142 const instance = await this._loadInstance(idHash, state)
143
144 // send the intialization message
145 await instance.create(message)
146
147 if (Object.keys(instance.ports.ports).length || instance.id === this.ROOT_ID) {
148 if (state.code && state.code.length > this.MAX_DATA_BYTES) {
149 state.code = chunk(state.code, this.MAX_DATA_BYTES).map(chk => {
150 return {
151 '/': chk
152 }
153 })
154 }
155 // save the container in the state
156 await this.tree.set(idHash, state)
157 } else {
158 this.scheduler.done(idHash)
159 }
160
161 return instance
162 }
163
164 createChannel () {
165 const port1 = {
166 messages: []
167 }
168
169 const port2 = {
170 messages: [],
171 destPort: port1
172 }
173
174 port1.destPort = port2
175 return [port1, port2]
176 }
177
178 /**
179 * creates a state root starting from a given container and a given number of
180 * ticks
181 * @param {Number} ticks the number of ticks at which to create the state root
182 * @returns {Promise}
183 */
184 async createStateRoot (ticks) {
185 await this.scheduler.wait(ticks)
186
187 const unlinked = await DFSchecker(this.tree, this._nodesToCheck, (id) => {
188 return this.ROOT_ID === id
189 })
190 for (const id of unlinked) {
191 await this.tree.delete(id)
192 }
193 return this.graph.flush(this.state)
194 }
195
196 /**
197 * regirsters a container with the hypervisor
198 * @param {Class} Constructor - a Class for instantiating the container
199 * @param {*} args - any args that the contructor takes
200 * @param {interger} typeId - the container's type identification ID
201 */
202 registerContainer (Constructor, args, typeId = Constructor.typeId) {
203 this._containerTypes[typeId] = {
204 Constructor: Constructor,
205 args: args
206 }
207 }
208}
209

Built with git-ssb-web