git ssb

0+

wanderer🌟 / js-primea-hypervisor



Commit ccc86c04a14c0346d7695e768be928faf4c41267

move to pure message passing

wanderer committed on 1/20/2017, 5:22:01 PM
Parent: a01401f258acd7e0b1f4ecbe82cdd1bc26048b4c

Files changed

EVMinterface.jschanged
deps/block.jschanged
index.jschanged
package.jsonchanged
tests/interface/address.jsonchanged
tests/interface/balance.jsonchanged
tests/interface/basic_gas_ops.jsonchanged
tests/interface/call.jsonchanged
tests/interface/callDataCopy.jsonchanged
tests/interface/callDataSize.jsonchanged
tests/interface/callValue.jsonchanged
tests/interface/coinbase.jsonchanged
tests/interface/origin.jsonchanged
tests/interface/sstore.jsonchanged
tests/interfaceRunner.jschanged
vm.jschanged
codeHandler.jsadded
defaultAgent.jsadded
environment.jsdeleted
message.jsadded
messageQueue.jsadded
testEnvironment.jsdeleted
EVMinterface.jsView
@@ -6,17 +6,22 @@
66 const path = require('path')
77 const ethUtil = require('ethereumjs-util')
88 const Vertex = require('merkle-trie')
99 const U256 = require('./deps/u256.js')
10+const Message = require('./message.js')
1011
1112 const U128_SIZE_BYTES = 16
1213 const ADDRESS_SIZE_BYTES = 20
1314 const U256_SIZE_BYTES = 32
1415
1516 // The interface exposed to the WebAessembly VM
1617 module.exports = class Interface {
17- constructor (kernel) {
18- this.kernel = kernel
18+ constructor (api, message, state) {
19+ this.message = message
20+ this.kernel = api.kernel
21+ this.state = api.kernel.state
22+ this.api = api
23+ this._results = {}
1924 const shimBin = fs.readFileSync(path.join(__dirname, '/wasm/interface.wasm'))
2025 const shimMod = WebAssembly.Module(shimBin)
2126 this.shims = WebAssembly.Instance(shimMod, {
2227 'interface': {
@@ -27,13 +32,28 @@
2732 }
2833 })
2934 }
3035
31- async initialize (state) {
32- this.block = await state.root.get(['block'])
33- // this.blockchain = await state.get(['blockchain'])
36+ async initialize () {
37+ this.block = await this.kernel.send(this.kernel.ROOT, new Message({
38+ data: {
39+ getValue: 'block'
40+ },
41+ sync: true
42+ }))
43+
44+ this.blockchain = await this.kernel.send(this.kernel.ROOT, new Message({
45+ data: {
46+ getValue: 'blockchain'
47+ },
48+ sync: true
49+ }))
3450 }
3551
52+ get results () {
53+ return this._results
54+ }
55+
3656 static get name () {
3757 return 'ethereum'
3858 }
3959
@@ -96,17 +116,17 @@
96116 * Returns the current amount of gas
97117 * @return {integer}
98118 */
99119 _getGasLeftHigh () {
100- return Math.floor(this.kernel.environment.gasLeft / 4294967296)
120+ return Math.floor(this.message.gas / 4294967296)
101121 }
102122
103123 /**
104124 * Returns the current amount of gas
105125 * @return {integer}
106126 */
107127 _getGasLeftLow () {
108- return this.kernel.environment.gasLeft
128+ return this.message.gas
109129 }
110130
111131 /**
112132 * Gets address of currently executing account and loads it into memory at
@@ -114,10 +134,12 @@
114134 * @param {integer} offset
115135 */
116136 getAddress (offset) {
117137 this.takeGas(2)
118-
119- this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.address.toMemory())
138+ const path = this.message.to
139+ path.pop()
140+ const address = path.pop()
141+ this.setMemory(offset, ADDRESS_SIZE_BYTES, new Buffer(address.slice(2), 'hex'))
120142 }
121143
122144 /**
123145 * Gets balance of the given account and loads it into memory at the given
@@ -127,15 +149,20 @@
127149 */
128150 getBalance (addressOffset, offset, cbIndex) {
129151 this.takeGas(20)
130152
131- const path = ['accounts', ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'balance']
132- const opPromise = this.kernel.environment.state.root.get(path)
133- .then(vertex => new U256(vertex.value))
153+ const path = [this.kernel.PARENT, '0x' + new Buffer(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)).toString('hex')]
154+ const opPromise = this.kernel.send(this.kernel.PARENT, new Message({
155+ to: path,
156+ data: {
157+ getValue: 'balance'
158+ },
159+ sync: true
160+ }))
134161 .catch(() => new U256(0))
135162
136- this.kernel.pushOpsQueue(opPromise, cbIndex, balance => {
137- this.setMemory(offset, U128_SIZE_BYTES, balance.toMemory(U128_SIZE_BYTES))
163+ this.api.pushOpsQueue(opPromise, cbIndex, balance => {
164+ this.setMemory(offset, U128_SIZE_BYTES, new U256(balance).toMemory(U128_SIZE_BYTES))
138165 })
139166 }
140167
141168 /**
@@ -146,9 +173,9 @@
146173 */
147174 getTxOrigin (offset) {
148175 this.takeGas(2)
149176
150- this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.origin.toMemory())
177+ this.setMemory(offset, ADDRESS_SIZE_BYTES, this.message.origin.toMemory())
151178 }
152179
153180 /**
154181 * Gets caller address and loads it into memory at the given offset. This is
@@ -157,9 +184,10 @@
157184 */
158185 getCaller (offset) {
159186 this.takeGas(2)
160187
161- this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.caller.toMemory())
188+ const caller = this.message.from[1]
189+ this.setMemory(offset, ADDRESS_SIZE_BYTES, new Buffer(caller.slice(2), 'hex'))
162190 }
163191
164192 /**
165193 * Gets the deposited value by the instruction/transaction responsible for
@@ -168,9 +196,9 @@
168196 */
169197 getCallValue (offset) {
170198 this.takeGas(2)
171199
172- this.setMemory(offset, U128_SIZE_BYTES, this.kernel.environment.callValue.toMemory(U128_SIZE_BYTES))
200+ this.setMemory(offset, U128_SIZE_BYTES, this.message.value.toMemory(U128_SIZE_BYTES))
173201 }
174202
175203 /**
176204 * Get size of input data in current environment. This pertains to the input
@@ -179,9 +207,9 @@
179207 */
180208 getCallDataSize () {
181209 this.takeGas(2)
182210
183- return this.kernel.environment.callData.length
211+ return this.message.data.length
184212 }
185213
186214 /**
187215 * Copys the input data in current environment to memory. This pertains to
@@ -193,9 +221,9 @@
193221 callDataCopy (offset, dataOffset, length) {
194222 this.takeGas(3 + Math.ceil(length / 32) * 3)
195223
196224 if (length) {
197- const callData = this.kernel.environment.callData.slice(dataOffset, dataOffset + length)
225+ const callData = this.message.data.slice(dataOffset, dataOffset + length)
198226 this.setMemory(offset, length, callData)
199227 }
200228 }
201229
@@ -206,9 +234,9 @@
206234 * @param {integer} dataOffset the offset in the input data
207235 */
208236 callDataCopy256 (offset, dataOffset) {
209237 this.takeGas(3)
210- const callData = this.kernel.environment.callData.slice(dataOffset, dataOffset + 32)
238+ const callData = this.message.data.slice(dataOffset, dataOffset + 32)
211239 this.setMemory(offset, U256_SIZE_BYTES, callData)
212240 }
213241
214242 /**
@@ -217,9 +245,9 @@
217245 */
218246 getCodeSize (cbIndex) {
219247 this.takeGas(2)
220248
221- const opPromise = this.kernel.environment.state
249+ const opPromise = this.state
222250 .get('code')
223251 .then(vertex => vertex.value.length)
224252
225253 // wait for all the prevouse async ops to finish before running the callback
@@ -237,9 +265,9 @@
237265
238266 let opPromise
239267
240268 if (length) {
241- opPromise = this.kernel.environment.state
269+ opPromise = this.state
242270 .get('code')
243271 .then(vertex => vertex.value)
244272 } else {
245273 opPromise = Promise.resolve([])
@@ -261,9 +289,9 @@
261289 */
262290 getExternalCodeSize (addressOffset, cbOffset) {
263291 this.takeGas(20)
264292 const address = ['accounts', ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code']
265- const opPromise = this.kernel.environment.state.root
293+ const opPromise = this.state.root
266294 .get(address)
267295 .then(vertex => vertex.value.length)
268296 .catch(() => 0)
269297
@@ -284,9 +312,9 @@
284312 const address = ['accounts', ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code']
285313 let opPromise
286314
287315 if (length) {
288- opPromise = this.kernel.environment.state.root
316+ opPromise = this.state.root
289317 .get(address)
290318 .then(vertex => vertex.value)
291319 .catch(() => [])
292320 } else {
@@ -308,9 +336,9 @@
308336 */
309337 getTxGasPrice () {
310338 this.takeGas(2)
311339
312- return this.kernel.environment.gasPrice
340+ return this.message.gasPrice
313341 }
314342
315343 /**
316344 * Gets the hash of one of the 256 most recent complete blocks.
@@ -325,9 +353,9 @@
325353
326354 if (diff > 256 || diff <= 0) {
327355 opPromise = Promise.resolve(new U256(0))
328356 } else {
329- opPromise = this.kernel.environment.getBlockHash(number)
357+ opPromise = this.state.get(['blockchain', number]).then(vertex => vertex.hash())
330358 }
331359
332360 // wait for all the prevouse async ops to finish before running the callback
333361 this.kernel.pushOpsQueue(opPromise, cbOffset, hash => {
@@ -341,9 +369,9 @@
341369 */
342370 getBlockCoinbase (offset) {
343371 this.takeGas(2)
344372
345- this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.block.header.coinbase)
373+ this.setMemory(offset, ADDRESS_SIZE_BYTES, this.block.header.coinbase)
346374 }
347375
348376 /**
349377 * Get the block’s timestamp.
@@ -416,12 +444,12 @@
416444 if (numberOfTopics > 3) {
417445 topics.push(U256.fromMemory(this.getMemory(topic4, U256_SIZE_BYTES)))
418446 }
419447
420- this.kernel.environment.logs.push({
448+ this.kernel.sendMessage([this.kernel.root, 'logs'], new Message({
421449 data: data,
422450 topics: topics
423- })
451+ }))
424452 }
425453
426454 /**
427455 * Creates a new contract with a given value.
@@ -469,25 +497,29 @@
469497 this.takeGas(40)
470498
471499 const gas = from64bit(gasHigh, gasLow)
472500 // Load the params from mem
473- const address = ['accounts', ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)]
501+ const address = [this.kernel.PARENT, ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)]
474502 const value = new U256(this.getMemory(valueOffset, U128_SIZE_BYTES))
475503
476504 // Special case for non-zero value; why does this exist?
477505 if (!value.isZero()) {
478506 this.takeGas(9000 - 2300 + gas)
479507 this.takeGas(-gas)
480508 }
481509
482- let opPromise = this.kernel.environment.state.root.get(address)
510+ // should be
511+ let opPromise = this.kernel.send(new Message({
512+ to: address,
513+ value: value
514+ }))
483515 .catch(() => {
484516 // why does this exist?
485517 this.takeGas(25000)
486518 })
487519
488520 // wait for all the prevouse async ops to finish before running the callback
489- this.kernel.pushOpsQueue(opPromise, cbIndex, () => {
521+ this.api.pushOpsQueue(opPromise, cbIndex, () => {
490522 return 1
491523 })
492524 }
493525
@@ -513,16 +545,16 @@
513545 this.takeGas(6700)
514546 }
515547
516548 // TODO: should be message?
517- const opPromise = this.kernel.environment.state.root.get(path)
549+ const opPromise = this.state.root.get(path)
518550 .catch(() => {
519551 // TODO: handle errors
520552 // the value was not found
521553 return null
522554 })
523555
524- this.kernel.pushOpsQueue(opPromise, cbIndex, oldValue => {
556+ this.api.pushOpsQueue(opPromise, cbIndex, oldValue => {
525557 return 1
526558 })
527559 }
528560
@@ -556,28 +588,28 @@
556588 * @param {interger} valueOffset the memory offset to load the value from
557589 */
558590 storageStore (pathOffset, valueOffset, cbIndex) {
559591 this.takeGas(5000)
560- const path = ['storage', ...this.getMemory(pathOffset, U256_SIZE_BYTES)]
592+ const path = [new Buffer(this.getMemory(pathOffset, U256_SIZE_BYTES)).toString('hex')]
561593 // copy the value
562594 const value = this.getMemory(valueOffset, U256_SIZE_BYTES).slice(0)
563595 const valIsZero = value.every((i) => i === 0)
564- const opPromise = this.kernel.environment.state.get(path)
596+ const opPromise = this.state.get(path)
565597 .then(vertex => vertex.value)
566598 .catch(() => null)
567599
568- this.kernel.pushOpsQueue(opPromise, cbIndex, oldValue => {
600+ this.api.pushOpsQueue(opPromise, cbIndex, oldValue => {
569601 if (valIsZero && oldValue) {
570602 // delete a value
571603 this.kernel.environment.gasRefund += 15000
572- this.kernel.environment.state.del(path)
604+ this.state.del(path)
573605 } else {
574606 if (!valIsZero && !oldValue) {
575607 // creating a new value
576608 this.takeGas(15000)
577609 }
578610 // update
579- this.kernel.environment.state.set(path, new Vertex({
611+ this.state.set(path, new Vertex({
580612 value: value
581613 }))
582614 }
583615 })
@@ -591,15 +623,15 @@
591623 storageLoad (pathOffset, resultOffset, cbIndex) {
592624 this.takeGas(50)
593625
594626 // convert the path to an array
595- const path = ['storage', ...this.getMemory(pathOffset, U256_SIZE_BYTES)]
627+ const path = [new Buffer(this.getMemory(pathOffset, U256_SIZE_BYTES)).toString('hex')]
596628 // get the value from the state
597- const opPromise = this.kernel.environment.state.get(path)
629+ const opPromise = this.state.get(path)
598630 .then(vertex => vertex.value)
599631 .catch(() => new Uint8Array(32))
600632
601- this.kernel.pushOpsQueue(opPromise, cbIndex, value => {
633+ this.api.pushOpsQueue(opPromise, cbIndex, value => {
602634 this.setMemory(resultOffset, U256_SIZE_BYTES, value)
603635 })
604636 }
605637
@@ -625,25 +657,25 @@
625657 this.kernel.environment.gasRefund += 24000
626658 }
627659
628660 getMemory (offset, length) {
629- return new Uint8Array(this.kernel.memory, offset, length)
661+ return new Uint8Array(this.api.memory(), offset, length)
630662 }
631663
632664 setMemory (offset, length, value) {
633- const memory = new Uint8Array(this.kernel.memory, offset, length)
665+ const memory = new Uint8Array(this.api.memory(), offset, length)
634666 memory.set(value)
635667 }
636668
637669 /*
638670 * Takes gas from the tank. Only needs to check if there's gas left to be taken,
639671 * because every caller of this method is trusted.
640672 */
641673 takeGas (amount) {
642- if (this.kernel.environment.gasLeft < amount) {
674+ if (this.message.gas < amount) {
643675 throw new Error('Ran out of gas')
644676 }
645- this.kernel.environment.gasLeft -= amount
677+ this.message.gas -= amount
646678 }
647679 }
648680
649681 // converts a 64 bit number to a JS number
deps/block.jsView
@@ -1,9 +1,4 @@
1-//
2-// This class parses a serialised Ethereum Block
3-//
4-// The input is a Buffer.
5-//
61 const Address = require('./address.js')
72 const ethUtil = require('ethereumjs-util')
83 const OldBlock = require('ethereumjs-block')
94 const U256 = require('./u256.js')
index.jsView
@@ -1,58 +1,97 @@
11 const Vertex = require('merkle-trie')
2-// The Kernel Exposes this Interface to VM instances it makes
3-const defaultInterface = require('./EVMinterface.js')
4-const VM = require('./vm.js')
5-const Environment = require('./environment.js')
2+const imports = require('./EVMinterface.js')
3+const codeHandler = require('./codeHandler.js')
4+const MessageQueue = require('./messageQueue')
65
6+const PARENT_SYMBOL = Symbol('parent')
7+const ROOT_SYMBOL = Symbol('root')
8+
79 module.exports = class Kernel {
810 constructor (opts = {}) {
9- this.state = opts.state || new Vertex()
10- this.state.value = opts.code || this.state.value
11- this.interfaces = opts.interfaces || [defaultInterface]
12- this._vm = new VM(this.state.value)
11+ const state = this.state = opts.state || new Vertex()
12+ state.value = opts.code || state.value
13+ this.imports = opts.imports || [imports]
14+ this.parent = opts.parent
15+ // RENAME agent
16+ this._vm = (opts.codeHandler || codeHandler).init(state.value)
17+ this._messageQueue = new MessageQueue(this)
18+ this._instanceCache = new Map()
1319 }
1420
21+ static get PARENT () {
22+ return PARENT_SYMBOL
23+ }
24+
25+ static get ROOT () {
26+ return ROOT_SYMBOL
27+ }
28+
29+ get PARENT () {
30+ return PARENT_SYMBOL
31+ }
32+
33+ get ROOT () {
34+ return ROOT_SYMBOL
35+ }
36+
1537 /**
1638 * run the kernels code with a given enviroment
1739 * The Kernel Stores all of its state in the Environment. The Interface is used
1840 * to by the VM to retrive infromation from the Environment.
1941 */
20- async run (environment = new Environment({state: this}), interfaces = this.interfaces) {
21- /**
22- * Builds a import map with an array of given interfaces
23- */
24- async function buildImports (kernelApi, imports, state) {
25- const result = {}
26- for (const Import of imports) {
27- const newIterface = new Import(kernelApi)
28- result[Import.name] = newIterface.exports
29- // initailize the import
30- await newIterface.initialize(state)
31- }
42+ async run (message, imports = this.imports) {
43+ const state = this.state.copy()
44+
45+ // const stateInterface = new StateInterface(state)
46+ const result = await this._vm.run(message, this, imports)
47+ if (!result.execption) {
48+ // update the state
49+ await this.state.set([], state)
50+ }
51+ return result
52+ }
53+
54+ async recieve (message) {
55+ if (message.isCyclic(this)) {
56+ const result = await this.run(message)
57+ message.finished()
3258 return result
59+ } else {
60+ return this._messageQueue.add(message)
3361 }
34-
35- const initializedImports = await buildImports(this._vm, interfaces, this.state)
36- return await this._vm.run(environment, initializedImports)
3762 }
3863
39- async messageReceiver (message) {
40- // let the code handle the message if there is code
41- const environment = new Environment({
42- message: message
43- })
44- let result = await this.run(environment)
45- if (!result.execption) {
46- this.state = result.state
64+ async send (port, message) {
65+ message.sending(this, this._messageQueue.currentMessage)
66+ // replace root with parent path to root
67+ if (port === ROOT_SYMBOL) {
68+ port = PARENT_SYMBOL
69+ message.to = new Array(this.state.path.length - 1).fill(PARENT_SYMBOL).concat(message.to)
4770 }
71+
72+ if (port === PARENT_SYMBOL) {
73+ message.from.push(this.state.name)
74+ } else {
75+ message.from.push(this.PARENT)
76+ }
77+
78+ const dest = await this.getPort(port)
79+ return dest.recieve(message)
4880 }
4981
50- copy () {
51- return new Kernel({
52- state: this.state.copy(),
53- code: this.code,
54- interfaces: this.interfaces,
55- parent: this.parent
56- })
82+ async getPort (port) {
83+ if (this._instanceCache.has(port)) {
84+ return this._instanceCache.get(port)
85+ } else {
86+ const destState = await (port === this.PARENT
87+ ? this.state.getParent()
88+ : this.state.get([port]))
89+
90+ const kernel = new Kernel({
91+ state: destState
92+ })
93+ this._instanceCache.set(port, kernel)
94+ return kernel
95+ }
5796 }
5897 }
package.jsonView
@@ -39,8 +39,9 @@
3939 "dependencies": {
4040 "bn.js": "^4.11.6",
4141 "ethereumjs-block": "^1.2.2",
4242 "ethereumjs-tx": "^1.1.2",
43+ "ethereumjs-util": "^5.0.0",
4344 "merkle-trie": "0.1.2",
44- "ethereumjs-util": "^5.0.0"
45+ "promise-queue": "^2.2.3"
4546 }
4647 }
tests/interface/address.jsonView
@@ -8,6 +8,7 @@
88 "nonce": "0x00"
99 }
1010 },
1111 "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
12+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
1213 "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b"
1314 }
tests/interface/balance.jsonView
@@ -8,6 +8,7 @@
88 "nonce": "0x00"
99 }
1010 },
1111 "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
12+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
1213 "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b"
1314 }
tests/interface/basic_gas_ops.jsonView
@@ -8,7 +8,8 @@
88 "nonce": "0x00"
99 }
1010 },
1111 "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
12+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
1213 "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
1314 "gasLeft": 1000
1415 }
tests/interface/call.jsonView
@@ -8,6 +8,7 @@
88 "nonce": "0x00"
99 }
1010 },
1111 "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
12+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
1213 "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b"
1314 }
tests/interface/callDataCopy.jsonView
@@ -8,6 +8,7 @@
88 "nonce": "0x00"
99 }
1010 },
1111 "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
12+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
1213 "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b"
1314 }
tests/interface/callDataSize.jsonView
@@ -1,5 +1,6 @@
11 {
2+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
23 "callValue": "0x00",
34 "callData": "0x596f75206172652077616974696e6720666f7220746865207265766f6c7574696f6e3f204c657420697420626521204d79206f776e20626567616e2061206c6f6e672074696d652061676f21205768656e20796f752077696c6c2062652072656164792e2e2e4920776f6ee2809974206d696e6420676f696e6720616c6f6e67207769746820796f7520666f722061207768696c652e20427574207768656e20796f75e280996c6c2073746f702c2049207368616c6c20636f6e74696e7565206f6e206d7920696e73616e6520616e6420747269756d7068616e742077617920746f776172642074686520677265617420616e64207375626c696d6520636f6e7175657374206f6620746865206e6f7468696e6721",
45 "state": {
56 "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": {
tests/interface/callValue.jsonView
@@ -1,5 +1,6 @@
11 {
2+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
23 "callValue": "0x056bc75e2d63100000",
34 "callData": "0x596f75206172652077616974696e6720666f7220746865207265766f6c7574696f6e3f204c657420697420626521204d79206f776e20626567616e2061206c6f6e672074696d652061676f21205768656e20796f752077696c6c2062652072656164792e2e2e4920776f6ee2809974206d696e6420676f696e6720616c6f6e67207769746820796f7520666f722061207768696c652e20427574207768656e20796f75e280996c6c2073746f702c2049207368616c6c20636f6e74696e7565206f6e206d7920696e73616e6520616e6420747269756d7068616e742077617920746f776172642074686520677265617420616e64207375626c696d6520636f6e7175657374206f6620746865206e6f7468696e6721",
45 "state": {
56 "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": {
tests/interface/coinbase.jsonView
@@ -1,5 +1,6 @@
11 {
2+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
23 "callValue": "0x00",
34 "callData": "0x00",
45 "state": {
56 "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": {
tests/interface/origin.jsonView
@@ -1,5 +1,6 @@
11 {
2+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
23 "callValue": "0x00",
34 "callData": "0x00",
45 "state": {
56 "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": {
tests/interface/sstore.jsonView
@@ -1,5 +1,6 @@
11 {
2+ "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b",
23 "callValue": "0x00",
34 "callData": "0x00",
45 "state": {
56 "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": {
tests/interfaceRunner.jsView
@@ -2,17 +2,19 @@
22 const fs = require('fs')
33 const path = require('path')
44 const Vertex = require('merkle-trie')
55 const Address = require('../deps/address')
6+const Block = require('../deps/block')
67 const U256 = require('../deps/u256')
7-
8+// TODO remove fakeblockchain
9+const fakeBlockChain = require('../fakeBlockChain.js')
810 const Kernel = require('../index.js')
9-const Environment = require('../testEnvironment.js')
11+const Message = require('../message.js')
1012
1113 const dir = path.join(__dirname, '/interface')
1214 // get the test names
1315 let tests = fs.readdirSync(dir).filter((file) => file.endsWith('.wast'))
14-// tests = ['callDataSize.wast']
16+// tests = ['sstore.wast']
1517
1618 runTests(tests)
1719
1820 function runTests (tests) {
@@ -23,20 +25,14 @@
2325 const rootVertex = new Vertex()
2426 const code = fs.readFileSync(`${dir}/${testName}.wasm`)
2527 const envData = JSON.parse(fs.readFileSync(`${dir}/${testName}.json`).toString())
2628
27- envData.caller = new Address(envData.caller)
28- envData.address = new Address(envData.address)
29- envData.origin = new Address(envData.origin)
30- envData.callData = new Buffer(envData.callData.slice(2), 'hex')
31- envData.callValue = new U256(envData.callValue)
32-
3329 for (let address in envData.state) {
3430 const account = envData.state[address]
3531 const accountVertex = new Vertex()
3632
3733 accountVertex.set('code', new Vertex({
38- value: new Buffer(account.code.slice(2), 'hex')
34+ value: code
3935 }))
4036
4137 accountVertex.set('balance', new Vertex({
4238 value: new Buffer(account.balance.slice(2), 'hex')
@@ -47,35 +43,41 @@
4743 value: new Buffer(account.storage[key].slice(2), 'hex')
4844 }))
4945 }
5046
51- const path = ['accounts', ...new Buffer(address.slice(2), 'hex')]
47+ const path = ['accounts', address]
5248 rootVertex.set(path, accountVertex)
5349 }
5450
55- const state = envData.state = await rootVertex.get(['accounts', ...envData.address.toBuffer()])
56- state.value = code
51+ const block = new Block()
52+ block.header.coinbase = new Address(envData.coinbase)
5753
58- const env = new Environment(envData)
59- env.block.header.coinbase = new Address(envData.coinbase)
60-
6154 rootVertex.set('block', new Vertex({
62- value: env.block
55+ value: block
6356 }))
6457
65- const kernel = new Kernel({
66- state: state
67- })
58+ rootVertex.set('blockchain', new Vertex({
59+ value: fakeBlockChain
60+ }))
6861
62+ const message = new Message()
63+ // message.from = new Address()
64+ message.to = ['accounts', envData.address, 'code']
65+ message.origin = new Address(envData.origin)
66+ message.data = new Buffer(envData.callData.slice(2), 'hex')
67+ message.value = new U256(envData.callValue)
68+ message.gas = 1000000
69+
70+ const callerState = await rootVertex.get(['accounts', envData.caller, 'code'])
71+ const caller = new Kernel({state: callerState})
6972 try {
70- await kernel.run(env)
73+ await caller.send(Kernel.ROOT, message)
7174 } catch (e) {
7275 t.fail('Exception: ' + e)
7376 console.error('FAIL')
7477 console.error(e)
7578 } finally {
7679 t.pass(testName)
77- console.log('done')
7880 }
7981 t.end()
8082 })
8183 }
vm.jsView
@@ -5,23 +5,53 @@
55 */
66 constructor (code) {
77 this._module = WebAssembly.Module(code)
88 }
9-
109 /**
1110 * Runs the core VM with a given environment and imports
1211 */
13- async run (environment, imports) {
14- this._environment = environment
15- // TODO, delete the instance once done.
16- const instance = this._instance = WebAssembly.Instance(this._module, imports)
12+ async run (message, kernel, imports) {
13+ const responses = {}
14+ /**
15+ * Builds a import map with an array of given interfaces
16+ */
17+ async function buildImports (kernelApi, kernel, imports) {
18+ const result = {}
19+ for (const Import of imports) {
20+ const response = responses[Import.name] = {}
21+ const newIterface = new Import(kernelApi, message, response)
22+ result[Import.name] = newIterface.exports
23+ // initailize the import
24+ await newIterface.initialize()
25+ }
26+ return result
27+ }
28+
29+ let instance
30+ const kernelApi = {
31+ /**
32+ * adds an aync operation to the operations queue
33+ */
34+ pushOpsQueue: (promise, callbackIndex, intefaceCallback) => {
35+ this._opsQueue = Promise.all([this._opsQueue, promise]).then(values => {
36+ const result = intefaceCallback(values.pop())
37+ instance.exports[callbackIndex.toString()](result)
38+ })
39+ },
40+ memory: () => {
41+ return instance.exports.memory.buffer
42+ },
43+ kernel: kernel
44+ }
45+
46+ const initializedImports = await buildImports(kernelApi, kernel, imports)
47+ instance = WebAssembly.Instance(this._module, initializedImports)
48+
1749 if (instance.exports.main) {
1850 instance.exports.main()
1951 }
2052 await this.onDone()
21- const env = this._environment
22- delete this._environment
23- return env
53+ return responses
2454 }
2555
2656 /**
2757 * returns a promise that resolves when the wasm instance is done running
@@ -33,26 +63,7 @@
3363 await this._opsQueue
3464 }
3565 }
3666
37- /**
38- * addes an aync operation to the operations queue
39- */
40- pushOpsQueue (promise, callbackIndex, intefaceCallback) {
41- this._opsQueue = Promise.all([this._opsQueue, promise]).then(values => {
42- const result = intefaceCallback(values.pop())
43- this._instance.exports[callbackIndex.toString()](result)
44- })
45- }
46-
4767 sendMessage (message) {
48-
4968 }
50-
51- get environment () {
52- return this._environment
53- }
54-
55- get memory () {
56- return this._instance.exports.memory.buffer
57- }
5869 }
codeHandler.jsView
@@ -1,0 +1,32 @@
1+const Wasm = require('./vm.js')
2+
3+const defaultHandler = {
4+ test: (code) => {
5+ return !code
6+ },
7+ init: () => {
8+ return require('./defaultAgent.js')
9+ }
10+}
11+
12+const wasm = {
13+ test: (code) => {
14+ return code && code.slice(0, 4).toString() === '\x00asm'
15+ },
16+ init: (code) => {
17+ return new Wasm(code)
18+ }
19+}
20+
21+let codeHandlers = exports.codeHandlers = [
22+ defaultHandler,
23+ wasm
24+]
25+
26+exports.init = (code) => {
27+ for (let handler of codeHandlers) {
28+ if (handler.test(code)) {
29+ return handler.init(code)
30+ }
31+ }
32+}
defaultAgent.jsView
@@ -1,0 +1,8 @@
1+exports.run = async (message, kernel) => {
2+ const to = message.nextPort()
3+ if (message.toPort) {
4+ return kernel.send(to, message)
5+ } else if (message.data.getValue) {
6+ return (await kernel.state.get(message.data.getValue)).value
7+ }
8+}
environment.jsView
@@ -1,104 +1,0 @@
1-const Vertex = require('merkle-trie')
2-const Store = require('merkle-trie/store')
3-const U256 = require('./deps/u256.js')
4-const Address = require('./deps/address.js')
5-const Block = require('./deps/block.js')
6-// TODO remove fakeblockchain
7-const fakeBlockChain = require('./fakeBlockChain.js')
8-
9-module.exports = class Environment {
10- constructor (data = {}) {
11- const defaults = {
12- block: new Block(),
13- blockchain: fakeBlockChain,
14- // gas tank
15- gasPrice: 0,
16- gasLeft: 1000000,
17- gasRefund: 0,
18- // call infromation
19- address: new Address('0x0000000000000000000000000000000000000000'),
20- origin: new Address('0x0000000000000000000000000000000000000000'),
21- caller: new Address('0x0000000000000000000000000000000000000000'),
22- callValue: new U256(0),
23- callData: new Uint8Array(),
24- // the ROM
25- code: new Uint8Array(), // the current running code
26- // output calls
27- logs: [],
28- selfDestruct: false,
29- selfDestructAddress: new Address('0x0000000000000000000000000000000000000000'),
30- // more output calls
31- returnValue: new Uint8Array(),
32- state: new Vertex({store: new Store()})
33- }
34- Object.assign(this, defaults, data)
35- }
36-
37- isAccountPresent (address) {
38- // const account = this.state.get(address.toString())
39- // if (account) {
40- // return true
41- // } else {
42- // return false
43- // }
44- }
45-
46- getBalance (address) {
47- // const account = this.state.get(address.toString())
48- // if (account) {
49- // return account.get('balance')
50- // } else {
51- // return new U256()
52- // }
53- }
54-
55- getCode (address) {
56- // const account = this.state.get(address.toString())
57- // if (account) {
58- // return account.get('code')
59- // } else {
60- // return Uint8Array.from(new Buffer([]))
61- // }
62- }
63-
64- async getBlockHash (height) {
65- const block = await this.blockchain.getBlock(height)
66- return block.hash()
67- }
68-
69- set createHandler (value) {
70- this.createhandler = value
71- }
72-
73- set callHandler (value) {
74- this.callhandler = value
75- }
76-
77- // kernal
78- create (code, value) {
79- // STUB
80- return [ 1, Address.zero() ]
81- }
82-
83- call (gas, address, value, data) {
84- // FIXME: create a child environment here
85- const ret = this.root.messagehandler({
86- from: this.address,
87- to: address,
88- gasLimit: gas,
89- value: value,
90- data: data
91- })
92- return [ !!ret.executionOutcome, ret.returnValue ]
93- }
94-
95- callCode (gas, address, value, data) {
96- // STUB
97- return [ 1, new Uint8Array() ]
98- }
99-
100- delegateCall (gas, address, data) {
101- // STUB
102- return [ 1, new Uint8Array() ]
103- }
104-}
message.jsView
@@ -1,0 +1,45 @@
1+const U256 = require('./deps/u256.js')
2+const Address = require('./deps/address.js')
3+
4+module.exports = class Message {
5+ constructor (opts = {}) {
6+ const defaults = {
7+ // call infromation
8+ to: [],
9+ origin: new Address('0x0000000000000000000000000000000000000000'),
10+ from: [],
11+ data: new Uint8Array(),
12+ sync: true,
13+ // resource info
14+ gas: new U256(0),
15+ gasPrices: new U256(0)
16+ }
17+ Object.assign(this, defaults, opts)
18+ this._index = 0
19+ this._parentProcesses = []
20+ }
21+
22+ nextPort () {
23+ // this.from.push(message.toPort)
24+ this.toPort = this.to[this._index]
25+ this._index++
26+ return this.toPort
27+ }
28+
29+ finished () {
30+ if (this.sync) {
31+ this._parentProcesses.pop()
32+ }
33+ }
34+
35+ sending (kernel, parentMessage) {
36+ if (this.sync && parentMessage) {
37+ this._parentProcesses = parentMessage._parentProcesses
38+ this._parentProcesses.push(kernel)
39+ }
40+ }
41+
42+ isCyclic (kernel) {
43+ return this.sync && this._parentProcesses.some(process => process === kernel)
44+ }
45+}
messageQueue.jsView
@@ -1,0 +1,10 @@
1+module.exports = class MessageQueue {
2+ constructor (kernel) {
3+ this.kernel = kernel
4+ }
5+
6+ add (message) {
7+ this.currentMessage = message
8+ return this.kernel.run(message)
9+ }
10+}
testEnvironment.jsView
@@ -1,8 +1,0 @@
1-const Environment = require('./environment.js')
2-const fakeBlockchain = require('./fakeBlockChain')
3-
4-module.exports = class TestEnvironment extends Environment {
5- async getBlockHash (height) {
6- return fakeBlockchain.getBlock(height).hash()
7- }
8-}

Built with git-ssb-web