UNPKG

27.1 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15 return new (P || (P = Promise))(function (resolve, reject) {
16 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19 step((generator = generator.apply(thisArg, _arguments || [])).next());
20 });
21};
22var __generator = (this && this.__generator) || function (thisArg, body) {
23 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25 function verb(n) { return function (v) { return step([n, v]); }; }
26 function step(op) {
27 if (f) throw new TypeError("Generator is already executing.");
28 while (_) try {
29 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30 if (y = 0, t) op = [op[0] & 2, t.value];
31 switch (op[0]) {
32 case 0: case 1: t = op; break;
33 case 4: _.label++; return { value: op[1], done: false };
34 case 5: _.label++; y = op[1]; op = [0]; continue;
35 case 7: op = _.ops.pop(); _.trys.pop(); continue;
36 default:
37 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41 if (t[2]) _.ops.pop();
42 _.trys.pop(); continue;
43 }
44 op = body.call(thisArg, _);
45 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47 }
48};
49Object.defineProperty(exports, "__esModule", { value: true });
50var BN = require("bn.js");
51var ethereumjs_util_1 = require("ethereumjs-util");
52var exceptions_1 = require("../exceptions");
53var message_1 = require("./message");
54var promisify = require('util.promisify');
55/**
56 * External interface made available to EVM bytecode. Modeled after
57 * the ewasm EEI [spec](https://github.com/ewasm/design/blob/master/eth_interface.md).
58 * It includes methods for accessing/modifying state, calling or creating contracts, access
59 * to environment data among other things.
60 * The EEI instance also keeps artifacts produced by the bytecode such as logs
61 * and to-be-selfdestructed addresses.
62 */
63var EEI = /** @class */ (function () {
64 function EEI(env, state, evm, common, gasLeft) {
65 this._env = env;
66 this._state = state;
67 this._evm = evm;
68 this._lastReturned = Buffer.alloc(0);
69 this._common = common;
70 this._gasLeft = gasLeft;
71 this._result = {
72 logs: [],
73 returnValue: undefined,
74 selfdestruct: {},
75 };
76 }
77 /**
78 * Subtracts an amount from the gas counter.
79 * @param amount - Amount of gas to consume
80 * @throws if out of gas
81 */
82 EEI.prototype.useGas = function (amount) {
83 this._gasLeft.isub(amount);
84 if (this._gasLeft.ltn(0)) {
85 this._gasLeft = new BN(0);
86 trap(exceptions_1.ERROR.OUT_OF_GAS);
87 }
88 };
89 /**
90 * Adds a positive amount to the gas counter.
91 * @param amount - Amount of gas refunded
92 */
93 EEI.prototype.refundGas = function (amount) {
94 this._evm._refund.iadd(amount);
95 };
96 /**
97 * Reduces amount of gas to be refunded by a positive value.
98 * @param amount - Amount to subtract from gas refunds
99 */
100 EEI.prototype.subRefund = function (amount) {
101 this._evm._refund.isub(amount);
102 if (this._evm._refund.ltn(0)) {
103 this._evm._refund = new BN(0);
104 trap(exceptions_1.ERROR.REFUND_EXHAUSTED);
105 }
106 };
107 /**
108 * Returns address of currently executing account.
109 */
110 EEI.prototype.getAddress = function () {
111 return this._env.address;
112 };
113 /**
114 * Returns balance of the given account.
115 * @param address - Address of account
116 */
117 EEI.prototype.getExternalBalance = function (address) {
118 return __awaiter(this, void 0, void 0, function () {
119 var account;
120 return __generator(this, function (_a) {
121 switch (_a.label) {
122 case 0:
123 // shortcut if current account
124 if (address.toString('hex') === this._env.address.toString('hex')) {
125 return [2 /*return*/, new BN(this._env.contract.balance)];
126 }
127 return [4 /*yield*/, this._state.getAccount(address)];
128 case 1:
129 account = _a.sent();
130 return [2 /*return*/, new BN(account.balance)];
131 }
132 });
133 });
134 };
135 /**
136 * Returns balance of self.
137 */
138 EEI.prototype.getSelfBalance = function () {
139 return new BN(this._env.contract.balance);
140 };
141 /**
142 * Returns caller address. This is the address of the account
143 * that is directly responsible for this execution.
144 */
145 EEI.prototype.getCaller = function () {
146 return new BN(this._env.caller);
147 };
148 /**
149 * Returns the deposited value by the instruction/transaction
150 * responsible for this execution.
151 */
152 EEI.prototype.getCallValue = function () {
153 return new BN(this._env.callValue);
154 };
155 /**
156 * Returns input data in current environment. This pertains to the input
157 * data passed with the message call instruction or transaction.
158 */
159 EEI.prototype.getCallData = function () {
160 return this._env.callData;
161 };
162 /**
163 * Returns size of input data in current environment. This pertains to the
164 * input data passed with the message call instruction or transaction.
165 */
166 EEI.prototype.getCallDataSize = function () {
167 return new BN(this._env.callData.length);
168 };
169 /**
170 * Returns the size of code running in current environment.
171 */
172 EEI.prototype.getCodeSize = function () {
173 return new BN(this._env.code.length);
174 };
175 /**
176 * Returns the code running in current environment.
177 */
178 EEI.prototype.getCode = function () {
179 return this._env.code;
180 };
181 /**
182 * Returns true if the current call must be executed statically.
183 */
184 EEI.prototype.isStatic = function () {
185 return this._env.isStatic;
186 };
187 /**
188 * Get size of an account’s code.
189 * @param address - Address of account
190 */
191 EEI.prototype.getExternalCodeSize = function (address) {
192 return __awaiter(this, void 0, void 0, function () {
193 var addressBuf, code;
194 return __generator(this, function (_a) {
195 switch (_a.label) {
196 case 0:
197 addressBuf = addressToBuffer(address);
198 return [4 /*yield*/, this._state.getContractCode(addressBuf)];
199 case 1:
200 code = _a.sent();
201 return [2 /*return*/, new BN(code.length)];
202 }
203 });
204 });
205 };
206 /**
207 * Returns code of an account.
208 * @param address - Address of account
209 */
210 EEI.prototype.getExternalCode = function (address) {
211 return __awaiter(this, void 0, void 0, function () {
212 return __generator(this, function (_a) {
213 if (!Buffer.isBuffer(address)) {
214 address = addressToBuffer(address);
215 }
216 return [2 /*return*/, this._state.getContractCode(address)];
217 });
218 });
219 };
220 /**
221 * Returns size of current return data buffer. This contains the return data
222 * from the last executed call, callCode, callDelegate, callStatic or create.
223 * Note: create only fills the return data buffer in case of a failure.
224 */
225 EEI.prototype.getReturnDataSize = function () {
226 return new BN(this._lastReturned.length);
227 };
228 /**
229 * Returns the current return data buffer. This contains the return data
230 * from last executed call, callCode, callDelegate, callStatic or create.
231 * Note: create only fills the return data buffer in case of a failure.
232 */
233 EEI.prototype.getReturnData = function () {
234 return this._lastReturned;
235 };
236 /**
237 * Returns price of gas in current environment.
238 */
239 EEI.prototype.getTxGasPrice = function () {
240 return new BN(this._env.gasPrice);
241 };
242 /**
243 * Returns the execution's origination address. This is the
244 * sender of original transaction; it is never an account with
245 * non-empty associated code.
246 */
247 EEI.prototype.getTxOrigin = function () {
248 return new BN(this._env.origin);
249 };
250 /**
251 * Returns the block’s number.
252 */
253 EEI.prototype.getBlockNumber = function () {
254 return new BN(this._env.block.header.number);
255 };
256 /**
257 * Returns the block's beneficiary address.
258 */
259 EEI.prototype.getBlockCoinbase = function () {
260 return new BN(this._env.block.header.coinbase);
261 };
262 /**
263 * Returns the block's timestamp.
264 */
265 EEI.prototype.getBlockTimestamp = function () {
266 return new BN(this._env.block.header.timestamp);
267 };
268 /**
269 * Returns the block's difficulty.
270 */
271 EEI.prototype.getBlockDifficulty = function () {
272 return new BN(this._env.block.header.difficulty);
273 };
274 /**
275 * Returns the block's gas limit.
276 */
277 EEI.prototype.getBlockGasLimit = function () {
278 return new BN(this._env.block.header.gasLimit);
279 };
280 /**
281 * Returns the chain ID for current chain. Introduced for the
282 * CHAINID opcode proposed in [EIP-1344](https://eips.ethereum.org/EIPS/eip-1344).
283 */
284 EEI.prototype.getChainId = function () {
285 return new BN(this._common.chainId());
286 };
287 /**
288 * Returns Gets the hash of one of the 256 most recent complete blocks.
289 * @param num - Number of block
290 */
291 EEI.prototype.getBlockHash = function (num) {
292 return __awaiter(this, void 0, void 0, function () {
293 var block;
294 return __generator(this, function (_a) {
295 switch (_a.label) {
296 case 0: return [4 /*yield*/, promisify(this._env.blockchain.getBlock).bind(this._env.blockchain)(num)];
297 case 1:
298 block = _a.sent();
299 return [2 /*return*/, new BN(block.hash())];
300 }
301 });
302 });
303 };
304 /**
305 * Store 256-bit a value in memory to persistent storage.
306 */
307 EEI.prototype.storageStore = function (key, value) {
308 return __awaiter(this, void 0, void 0, function () {
309 var account;
310 return __generator(this, function (_a) {
311 switch (_a.label) {
312 case 0: return [4 /*yield*/, this._state.putContractStorage(this._env.address, key, value)];
313 case 1:
314 _a.sent();
315 return [4 /*yield*/, this._state.getAccount(this._env.address)];
316 case 2:
317 account = _a.sent();
318 this._env.contract = account;
319 return [2 /*return*/];
320 }
321 });
322 });
323 };
324 /**
325 * Loads a 256-bit value to memory from persistent storage.
326 * @param key - Storage key
327 */
328 EEI.prototype.storageLoad = function (key) {
329 return __awaiter(this, void 0, void 0, function () {
330 return __generator(this, function (_a) {
331 return [2 /*return*/, this._state.getContractStorage(this._env.address, key)];
332 });
333 });
334 };
335 /**
336 * Returns the current gasCounter.
337 */
338 EEI.prototype.getGasLeft = function () {
339 return this._gasLeft.clone();
340 };
341 /**
342 * Set the returning output data for the execution.
343 * @param returnData - Output data to return
344 */
345 EEI.prototype.finish = function (returnData) {
346 this._result.returnValue = returnData;
347 trap(exceptions_1.ERROR.STOP);
348 };
349 /**
350 * Set the returning output data for the execution. This will halt the
351 * execution immediately and set the execution result to "reverted".
352 * @param returnData - Output data to return
353 */
354 EEI.prototype.revert = function (returnData) {
355 this._result.returnValue = returnData;
356 trap(exceptions_1.ERROR.REVERT);
357 };
358 /**
359 * Mark account for later deletion and give the remaining balance to the
360 * specified beneficiary address. This will cause a trap and the
361 * execution will be aborted immediately.
362 * @param toAddress - Beneficiary address
363 */
364 EEI.prototype.selfDestruct = function (toAddress) {
365 return __awaiter(this, void 0, void 0, function () {
366 return __generator(this, function (_a) {
367 return [2 /*return*/, this._selfDestruct(toAddress)];
368 });
369 });
370 };
371 EEI.prototype._selfDestruct = function (toAddress) {
372 return __awaiter(this, void 0, void 0, function () {
373 var toAccount, newBalance, account;
374 return __generator(this, function (_a) {
375 switch (_a.label) {
376 case 0:
377 // only add to refund if this is the first selfdestruct for the address
378 if (!this._result.selfdestruct[this._env.address.toString('hex')]) {
379 this.refundGas(new BN(this._common.param('gasPrices', 'selfdestructRefund')));
380 }
381 this._result.selfdestruct[this._env.address.toString('hex')] = toAddress;
382 return [4 /*yield*/, this._state.getAccount(toAddress)];
383 case 1:
384 toAccount = _a.sent();
385 newBalance = new BN(this._env.contract.balance).add(new BN(toAccount.balance));
386 toAccount.balance = ethereumjs_util_1.toBuffer(newBalance);
387 return [4 /*yield*/, this._state.putAccount(toAddress, toAccount)
388 // Subtract from contract balance
389 ];
390 case 2:
391 _a.sent();
392 return [4 /*yield*/, this._state.getAccount(this._env.address)];
393 case 3:
394 account = _a.sent();
395 account.balance = ethereumjs_util_1.toBuffer(new BN(0));
396 return [4 /*yield*/, this._state.putAccount(this._env.address, account)];
397 case 4:
398 _a.sent();
399 trap(exceptions_1.ERROR.STOP);
400 return [2 /*return*/];
401 }
402 });
403 });
404 };
405 /**
406 * Creates a new log in the current environment.
407 */
408 EEI.prototype.log = function (data, numberOfTopics, topics) {
409 if (numberOfTopics < 0 || numberOfTopics > 4) {
410 trap(exceptions_1.ERROR.OUT_OF_RANGE);
411 }
412 if (topics.length !== numberOfTopics) {
413 trap(exceptions_1.ERROR.INTERNAL_ERROR);
414 }
415 // add address
416 var log = [this._env.address];
417 log.push(topics);
418 // add data
419 log.push(data);
420 this._result.logs.push(log);
421 };
422 /**
423 * Sends a message with arbitrary data to a given address path.
424 */
425 EEI.prototype.call = function (gasLimit, address, value, data) {
426 return __awaiter(this, void 0, void 0, function () {
427 var msg;
428 return __generator(this, function (_a) {
429 msg = new message_1.default({
430 caller: this._env.address,
431 gasLimit: gasLimit,
432 to: address,
433 value: value,
434 data: data,
435 isStatic: this._env.isStatic,
436 depth: this._env.depth + 1,
437 });
438 return [2 /*return*/, this._baseCall(msg)];
439 });
440 });
441 };
442 /**
443 * Message-call into this account with an alternative account's code.
444 */
445 EEI.prototype.callCode = function (gasLimit, address, value, data) {
446 return __awaiter(this, void 0, void 0, function () {
447 var msg;
448 return __generator(this, function (_a) {
449 msg = new message_1.default({
450 caller: this._env.address,
451 gasLimit: gasLimit,
452 to: this._env.address,
453 codeAddress: address,
454 value: value,
455 data: data,
456 isStatic: this._env.isStatic,
457 depth: this._env.depth + 1,
458 });
459 return [2 /*return*/, this._baseCall(msg)];
460 });
461 });
462 };
463 /**
464 * Sends a message with arbitrary data to a given address path, but disallow
465 * state modifications. This includes log, create, selfdestruct and call with
466 * a non-zero value.
467 */
468 EEI.prototype.callStatic = function (gasLimit, address, value, data) {
469 return __awaiter(this, void 0, void 0, function () {
470 var msg;
471 return __generator(this, function (_a) {
472 msg = new message_1.default({
473 caller: this._env.address,
474 gasLimit: gasLimit,
475 to: address,
476 value: value,
477 data: data,
478 isStatic: true,
479 depth: this._env.depth + 1,
480 });
481 return [2 /*return*/, this._baseCall(msg)];
482 });
483 });
484 };
485 /**
486 * Message-call into this account with an alternative account’s code, but
487 * persisting the current values for sender and value.
488 */
489 EEI.prototype.callDelegate = function (gasLimit, address, value, data) {
490 return __awaiter(this, void 0, void 0, function () {
491 var msg;
492 return __generator(this, function (_a) {
493 msg = new message_1.default({
494 caller: this._env.caller,
495 gasLimit: gasLimit,
496 to: this._env.address,
497 codeAddress: address,
498 value: value,
499 data: data,
500 isStatic: this._env.isStatic,
501 delegatecall: true,
502 depth: this._env.depth + 1,
503 });
504 return [2 /*return*/, this._baseCall(msg)];
505 });
506 });
507 };
508 EEI.prototype._baseCall = function (msg) {
509 return __awaiter(this, void 0, void 0, function () {
510 var selfdestruct, results, account;
511 return __generator(this, function (_a) {
512 switch (_a.label) {
513 case 0:
514 selfdestruct = __assign({}, this._result.selfdestruct);
515 msg.selfdestruct = selfdestruct;
516 // empty the return data buffer
517 this._lastReturned = Buffer.alloc(0);
518 // Check if account has enough ether and max depth not exceeded
519 if (this._env.depth >= this._common.param('vm', 'stackLimit') ||
520 (msg.delegatecall !== true && new BN(this._env.contract.balance).lt(msg.value))) {
521 return [2 /*return*/, new BN(0)];
522 }
523 return [4 /*yield*/, this._evm.executeMessage(msg)];
524 case 1:
525 results = _a.sent();
526 if (results.execResult.logs) {
527 this._result.logs = this._result.logs.concat(results.execResult.logs);
528 }
529 // this should always be safe
530 this.useGas(results.gasUsed);
531 // Set return value
532 if (results.execResult.returnValue &&
533 (!results.execResult.exceptionError ||
534 results.execResult.exceptionError.error === exceptions_1.ERROR.REVERT)) {
535 this._lastReturned = results.execResult.returnValue;
536 }
537 if (!!results.execResult.exceptionError) return [3 /*break*/, 3];
538 Object.assign(this._result.selfdestruct, selfdestruct);
539 return [4 /*yield*/, this._state.getAccount(this._env.address)];
540 case 2:
541 account = _a.sent();
542 this._env.contract = account;
543 _a.label = 3;
544 case 3: return [2 /*return*/, this._getReturnCode(results)];
545 }
546 });
547 });
548 };
549 /**
550 * Creates a new contract with a given value.
551 */
552 EEI.prototype.create = function (gasLimit, value, data, salt) {
553 if (salt === void 0) { salt = null; }
554 return __awaiter(this, void 0, void 0, function () {
555 var selfdestruct, msg, results, account;
556 return __generator(this, function (_a) {
557 switch (_a.label) {
558 case 0:
559 selfdestruct = __assign({}, this._result.selfdestruct);
560 msg = new message_1.default({
561 caller: this._env.address,
562 gasLimit: gasLimit,
563 value: value,
564 data: data,
565 salt: salt,
566 depth: this._env.depth + 1,
567 selfdestruct: selfdestruct,
568 });
569 // empty the return data buffer
570 this._lastReturned = Buffer.alloc(0);
571 // Check if account has enough ether and max depth not exceeded
572 if (this._env.depth >= this._common.param('vm', 'stackLimit') ||
573 (msg.delegatecall !== true && new BN(this._env.contract.balance).lt(msg.value))) {
574 return [2 /*return*/, new BN(0)];
575 }
576 this._env.contract.nonce = ethereumjs_util_1.toBuffer(new BN(this._env.contract.nonce).addn(1));
577 return [4 /*yield*/, this._state.putAccount(this._env.address, this._env.contract)];
578 case 1:
579 _a.sent();
580 return [4 /*yield*/, this._evm.executeMessage(msg)];
581 case 2:
582 results = _a.sent();
583 if (results.execResult.logs) {
584 this._result.logs = this._result.logs.concat(results.execResult.logs);
585 }
586 // this should always be safe
587 this.useGas(results.gasUsed);
588 // Set return buffer in case revert happened
589 if (results.execResult.exceptionError &&
590 results.execResult.exceptionError.error === exceptions_1.ERROR.REVERT) {
591 this._lastReturned = results.execResult.returnValue;
592 }
593 if (!!results.execResult.exceptionError) return [3 /*break*/, 4];
594 Object.assign(this._result.selfdestruct, selfdestruct);
595 return [4 /*yield*/, this._state.getAccount(this._env.address)];
596 case 3:
597 account = _a.sent();
598 this._env.contract = account;
599 if (results.createdAddress) {
600 // push the created address to the stack
601 return [2 /*return*/, new BN(results.createdAddress)];
602 }
603 _a.label = 4;
604 case 4: return [2 /*return*/, this._getReturnCode(results)];
605 }
606 });
607 });
608 };
609 /**
610 * Creates a new contract with a given value. Generates
611 * a deterministic address via CREATE2 rules.
612 */
613 EEI.prototype.create2 = function (gasLimit, value, data, salt) {
614 return __awaiter(this, void 0, void 0, function () {
615 return __generator(this, function (_a) {
616 return [2 /*return*/, this.create(gasLimit, value, data, salt)];
617 });
618 });
619 };
620 /**
621 * Returns true if account is empty (according to EIP-161).
622 * @param address - Address of account
623 */
624 EEI.prototype.isAccountEmpty = function (address) {
625 return __awaiter(this, void 0, void 0, function () {
626 return __generator(this, function (_a) {
627 return [2 /*return*/, this._state.accountIsEmpty(address)];
628 });
629 });
630 };
631 EEI.prototype._getReturnCode = function (results) {
632 // This preserves the previous logic, but seems to contradict the EEI spec
633 // https://github.com/ewasm/design/blob/38eeded28765f3e193e12881ea72a6ab807a3371/eth_interface.md
634 if (results.execResult.exceptionError) {
635 return new BN(0);
636 }
637 else {
638 return new BN(1);
639 }
640 };
641 return EEI;
642}());
643exports.default = EEI;
644function trap(err) {
645 throw new exceptions_1.VmError(err);
646}
647var MASK_160 = new BN(1).shln(160).subn(1);
648function addressToBuffer(address) {
649 if (Buffer.isBuffer(address))
650 return address;
651 return address.and(MASK_160).toArrayLike(Buffer, 'be', 20);
652}
653//# sourceMappingURL=eei.js.map
\No newline at end of file