git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 85f7c3ade0ebc8c21a6734cf20824c455a7e572b

Files: 85f7c3ade0ebc8c21a6734cf20824c455a7e572b / interface.js

13820 bytesRaw
1/**
2 * This is the Ethereum interface that is exposed to the WASM instance which
3 * enables to interact with the Ethereum Environment
4 */
5const constants = require('./constants.js')
6// const Graph = require('generic-digraph')
7// function.bind is not working corretly whith Wasm imports. So instead create
8// a global for now. TODO REMOVE
9let ENV
10let MOD
11// The interface exposed to the WebAessembly Core
12module.exports = class Interface {
13 print (a) {
14 console.log(a)
15 }
16
17 memPrint () {
18 console.log((new Uint8Array(MOD.exports.memory)).toString())
19 }
20
21 constructor (environment) {
22 ENV = this.environment = environment
23 }
24
25 get exportTable () {
26 let exportMethods = [
27 // include all the public methods according to the Ethereum Environment Interface (EEI)
28 // FIXME: this currently doesn't match EEI r0
29 'useGas',
30 'gas',
31 'address',
32 'balance',
33 'origin',
34 'caller',
35 'callValue',
36 'callDataSize',
37 'callDataCopy',
38 'codeSize',
39 'codeCopy',
40 'extCodeSize',
41 'extCodeCopy',
42 'gasPrice',
43 'blockHash',
44 'coinbase',
45 'timestamp',
46 'number',
47 'difficulty',
48 'gasLimit',
49 'log',
50 'create',
51 'call',
52 'callDelegate',
53 'sstore',
54 'sload',
55 'return',
56 'suicide'
57 ]
58 let ret = {}
59 exportMethods.forEach((method) => {
60 ret[method] = this[method].bind(this)
61 })
62 return ret
63 }
64
65 // FIXME: this shouldn't be needed
66 get env () {
67 return ENV
68 }
69
70 setModule (mod) {
71 this.module = MOD = mod
72 }
73
74 /**
75 * Subtracts an amount to the gas counter
76 * @param {integer} amount the amount to subtract to the gas counter
77 */
78 useGas (amount) {
79 if (amount > 0) {
80 ENV.gasLimit -= amount
81 }
82 }
83
84 /**
85 * Returns the current amount of gas
86 * @return {integer}
87 */
88 gas () {
89 return ENV.gasLimit
90 }
91
92 /**
93 * Gets address of currently executing account and loads it into memory at
94 * the given offset.
95 * @param {integer} offset
96 */
97 address (offset) {
98 const address = ENV.address
99 const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES)
100 memory.set(address)
101 }
102
103 /**
104 * Gets balance of the given account and loads it into memory at the given
105 * offset.
106 * @param {integer} addressOffset the memory offset to laod the address
107 * @param {integer} resultOffset
108 */
109 balance (addressOffset, offset) {
110 const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES)
111 const memory = new Uint8Array(MOD.exports.memory, offset, constants.MAX_BAL_BYTES)
112 // call the parent contract and ask for the balance of one of its child contracts
113 const balance = ENV.parent.environment.getBalance(address)
114 memory.set(balance)
115 }
116
117 /**
118 * Gets the execution's origination address and loads it into memory at the
119 * given offset. This is the sender of original transaction; it is never an
120 * account with non-empty associated code.
121 * @param {integer} offset
122 */
123 origin (offset) {
124 const origin = ENV.origin
125 const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES)
126 memory.set(origin)
127 }
128
129 /**
130 * Gets caller address and loads it into memory at the given offset. This is
131 * the address of the account that is directly responsible for this execution.
132 * @param {integer} offset
133 */
134 caller (offset) {
135 const caller = ENV.caller
136 const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES)
137 memory.set(caller)
138 }
139
140 /**
141 * Gets the deposited value by the instruction/transaction responsible for
142 * this execution and loads it into memory at the given location.
143 * @param {integer} offset
144 */
145 callValue (offset) {
146 const callValue = ENV.callValue
147 const memory = new Uint8Array(MOD.exports.memory, offset, constants.MAX_BAL_BYTES)
148 memory.set(callValue)
149 }
150
151 /**
152 * Get size of input data in current environment. This pertains to the input
153 * data passed with the message call instruction or transaction.
154 * @return {integer}
155 */
156 callDataSize () {
157 return ENV.callData.byteLength
158 }
159
160 /**
161 * Copys the input data in current environment to memory. This pertains to
162 * the input data passed with the message call instruction or transaction.
163 * @param {integer} offset the offset in memory to load into
164 * @param {integer} dataOffset the offset in the input data
165 * @param {integer} length the length of data to copy
166 */
167 callDataCopy (offset, dataOffset, length) {
168 const callData = new Uint8Array(ENV.callData, offset, length)
169 const memory = new Uint8Array(MOD.exports.memory, offset, length)
170 memory.set(callData)
171 }
172
173 /**
174 * Gets the size of code running in current environment.
175 * @return {interger}
176 */
177 codeSize () {
178 return ENV.code.byteLength
179 }
180
181 /**
182 * Copys the code running in current environment to memory.
183 * @param {integer} offset the memory offset
184 * @param {integer} codeOffset the code offset
185 * @param {integer} length the length of code to copy
186 */
187 codeCopy (offset, codeOffset, length) {
188 const code = new Uint8Array(ENV.code, codeOffset, length)
189 const memory = new Uint8Array(MOD.exports.memory, offset, length)
190 memory.set(code)
191 }
192
193 /**
194 * Get size of an account’s code.
195 * @param {integer} addressOffset the offset in memory to load the address from
196 * @return {integer}
197 */
198 extCodeSize (addressOffset) {
199 const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES)
200 const code = this.environment.getCode(address)
201 return code.byteLength
202 }
203
204 /**
205 * Copys the code of an account to memory.
206 * @param {integer} addressOffset the memory offset of the address
207 * @param {integer} offset the memory offset
208 * @param {integer} codeOffset the code offset
209 * @param {integer} length the length of code to copy
210 */
211 extCodeCopy (addressOffset, offset, codeOffset, length) {
212 const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES)
213 let code = this.environment.getCode(address)
214 code = new Uint8Array(code, codeOffset, length)
215 const memory = new Uint8Array(MOD.exports.memory, offset, length)
216 memory.set(code)
217 }
218
219 /**
220 * Gets price of gas in current environment.
221 * @return {integer}
222 */
223 gasPrice () {
224 return ENV.gasPrice
225 }
226
227 /**
228 * Gets the hash of one of the 256 most recent complete blocks.
229 * @param {integer} number which block to load
230 * @param {integer} offset the offset to load the hash into
231 */
232 blockHash (number, offset) {
233 const hash = this.environment.getBlockHash(number)
234 const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES)
235 memory.set(hash)
236 }
237
238 /**
239 * Gets the block’s beneficiary address and loads into memory.
240 * @param offset
241 */
242 coinbase (offset) {
243 const memory = new Uint8Array(MOD.exports.memory, offset, constants.ADD_SIZE_BYTES)
244 memory.set(ENV.coinbase)
245 }
246
247 /**
248 * Get the block’s timestamp.
249 * @return {integer}
250 */
251 timestamp () {
252 return ENV.timestamp
253 }
254
255 /**
256 * Get the block’s number.
257 * @return {integer}
258 */
259 number () {
260 return ENV.number
261 }
262
263 /**
264 * Get the block’s difficulty.
265 * @return {integer}
266 */
267 difficulty () {
268 return ENV.difficulty
269 }
270
271 /**
272 * Get the block’s gas limit.
273 * @return {integer}
274 */
275 gasLimit () {
276 return ENV.gasLimit
277 }
278
279 /**
280 * Creates a new log in the current environment
281 * @param {integer} dataOffset the offset in memory to load the memory
282 * @param {integer} length the data length
283 * TODO: replace with variadic
284 */
285 log (dataOffset, length, topic1, topic2, topic3, topic4, topic5) {
286 const data = new Uint8Array(MOD.exports.memory, dataOffset, length)
287 ENV.logs.push({
288 data: data,
289 topics: [topic1, topic2, topic3, topic4, topic5]
290 })
291 }
292
293 /**
294 * Creates a new contract with a given value.
295 * @param {integer} valueOffset the offset in memory to the value from
296 * @param {integer} dataOffset the offset to load the code for the new contract from
297 * @param {integer} length the data length
298 */
299 create (valueOffset, dataOffset, length) {
300 const value = new Uint8Array(MOD.exports.memory, valueOffset, constants.MAX_BAL_BYTES)
301 const data = new Uint8Array(MOD.exports.memory, dataOffset, length)
302 const result = ENV.create(value, data)
303 return result
304 }
305
306 /**
307 * Sends a message with arbiatary data to a given address path
308 * @param {integer} addressOffset the offset to load the address path from
309 * @param {integer} valueOffset the offset to load the value from
310 * @param {integer} dataOffset the offset to load data from
311 * @param {integer} dataLength the length of data
312 * @param {integer} resultOffset the offset to store the result data at
313 * @param {integer} resultLength
314 * @param {integer} gas
315 * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not
316 * TODO: add proper gas counting
317 */
318 call (addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength, gas) {
319 if (gas === undefined) {
320 gas = this.gasLeft()
321 }
322 // Load the params from mem
323 const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES)
324 const value = new Uint8Array(MOD.exports.memory, valueOffset, constants.MAX_BAL_BYTES)
325 const data = new Uint8Array(MOD.exports.memory, dataOffset, dataLength)
326 // Run the call
327 const [result, errorCode] = ENV.call(gas, address, value, data)
328 const memory = new Uint8Array(MOD.exports.memory, resultOffset, resultLength)
329 memory.set(result)
330
331 return errorCode
332 }
333
334 /**
335 * Message-call into this account with an alternative account’s code, but
336 * persisting the current values for sender and value.
337 * @param {integer} gas
338 * @param {integer} addressOffset the offset to load the address path from
339 * @param {integer} valueOffset the offset to load the value from
340 * @param {integer} dataOffset the offset to load data from
341 * @param {integer} dataLength the length of data
342 * @param {integer} resultOffset the offset to store the result data at
343 * @param {integer} resultLength
344 * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not
345 */
346 callDelegate (gas, addressOffset, dataOffset, dataLength, resultOffset, resultLength) {
347 const data = new Uint8Array(MOD.exports.memory, dataOffset, dataLength)
348 const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES)
349 const [result, errorCode] = this.environment.callDelegate(gas, address, data)
350 const memory = new Uint8Array(MOD.exports.memory, resultOffset, resultLength)
351 memory.set(result)
352
353 return errorCode
354 }
355
356 /**
357 * store a value at a given path in long term storage which are both loaded
358 * from Memory
359 * @param {interger} pathOffest the memory offset to load the the path from
360 * @param {interger} valueOffset the memory offset to load the value from
361 */
362 sstore (pathOffest, valueOffset) {
363 const path = new Buffer(MOD.exports.memory, pathOffest, 32).toString('hex')
364 const value = new Uint8Array(MOD.exports.memory, valueOffset, 32)
365 const oldValue = ENV.state.get(path)
366 const valIsZero = value.every((i) => i === 0)
367
368 // write
369 if (!valIsZero && !oldValue) {
370 ENV.gasLimit -= 15000
371 }
372
373 // delete
374 if (valIsZero && oldValue) {
375 ENV.gasRefund += 15000
376 ENV.state.delete(path)
377 } else {
378 ENV.state.set(path, value)
379 }
380 }
381
382 /**
383 * reterives a value at a given path in long term storage
384 * @param {interger} pathOffest the memory offset to load the the path from
385 * @param {interger} resultOffset the memory offset to load the value from
386 */
387 sload (pathOffest, resultOffset) {
388 const path = new Buffer(MOD.exports.memory, pathOffest, 32).toString('hex')
389 const result = ENV.state.get(path)
390 const memory = new Uint8Array(MOD.exports.memory, resultOffset, 32)
391 memory.set(result)
392 }
393
394 /**
395 * Halt execution returning output data.
396 * @param {integer} offset the offset of the output data.
397 * @param {integer} length the length of the output data.
398 */
399 return (offset, length) {
400 ENV.returnValue = new Uint8Array(MOD.exports.memory, offset, length)
401 }
402
403 /**
404 * Halt execution and register account for later deletion giving the remaining
405 * balance to an address path
406 * @param {integer} offset the offset to load the address from
407 */
408 suicide (addressOffset) {
409 const address = new Uint8Array(MOD.exports.memory, addressOffset, constants.ADD_SIZE_BYTES)
410 this.environment.suicideAddress = address
411 }
412}
413
414//
415// Polyfill required unless this is sorted: https://bugs.chromium.org/p/chromium/issues/detail?id=633895
416//
417// Polyfill from: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
418//
419Function.prototype.bind = function (oThis) { // eslint-disable-line
420 if (typeof this !== 'function') {
421 // closest thing possible to the ECMAScript 5
422 // internal IsCallable function
423 throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')
424 }
425
426 var aArgs = Array.prototype.slice.call(arguments, 1)
427 var fToBind = this
428 var fNOP = function () {}
429 var fBound = function () {
430 return fToBind.apply(this instanceof fNOP ? this : oThis,
431 aArgs.concat(Array.prototype.slice.call(arguments)))
432 }
433
434 if (this.prototype) {
435 // Function.prototype doesn't have a prototype property
436 fNOP.prototype = this.prototype
437 }
438
439 fBound.prototype = new fNOP() // eslint-disable-line new-cap
440
441 return fBound
442}
443

Built with git-ssb-web