git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: efd9ca81724858a16212833d23944dcec15769c4

Files: efd9ca81724858a16212833d23944dcec15769c4 / interface.js

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

Built with git-ssb-web