UNPKG

@eth-optimism/ethereumjs-vm

Version:
1,137 lines 49.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { 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; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.handlers = void 0; var BN = require("bn.js"); var utils = require("ethereumjs-util"); var exceptions_1 = require("../exceptions"); var MASK_160 = new BN(1).shln(160).subn(1); // Find Ceil(`this` / `num`) function divCeil(a, b) { var div = a.div(b); var mod = a.mod(b); // Fast case - exact division if (mod.isZero()) return div; // Round up return div.isNeg() ? div.isubn(1) : div.iaddn(1); } function addressToBuffer(address) { return address.and(MASK_160).toArrayLike(Buffer, 'be', 20); } // the opcode functions exports.handlers = { STOP: function (runState) { trap(exceptions_1.ERROR.STOP); }, ADD: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = a.add(b).mod(utils.TWO_POW256); runState.stack.push(r); }, MUL: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = a.mul(b).mod(utils.TWO_POW256); runState.stack.push(r); }, SUB: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = a.sub(b).toTwos(256); runState.stack.push(r); }, DIV: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r; if (b.isZero()) { r = new BN(b); } else { r = a.div(b); } runState.stack.push(r); }, SDIV: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r; if (b.isZero()) { r = new BN(b); } else { a = a.fromTwos(256); b = b.fromTwos(256); r = a.div(b).toTwos(256); } runState.stack.push(r); }, MOD: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r; if (b.isZero()) { r = new BN(b); } else { r = a.mod(b); } runState.stack.push(r); }, SMOD: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r; if (b.isZero()) { r = new BN(b); } else { a = a.fromTwos(256); b = b.fromTwos(256); r = a.abs().mod(b.abs()); if (a.isNeg()) { r = r.ineg(); } r = r.toTwos(256); } runState.stack.push(r); }, ADDMOD: function (runState) { var _a = runState.stack.popN(3), a = _a[0], b = _a[1], c = _a[2]; var r; if (c.isZero()) { r = new BN(c); } else { r = a.add(b).mod(c); } runState.stack.push(r); }, MULMOD: function (runState) { var _a = runState.stack.popN(3), a = _a[0], b = _a[1], c = _a[2]; var r; if (c.isZero()) { r = new BN(c); } else { r = a.mul(b).mod(c); } runState.stack.push(r); }, EXP: function (runState) { var _a = runState.stack.popN(2), base = _a[0], exponent = _a[1]; if (exponent.isZero()) { runState.stack.push(new BN(1)); return; } var byteLength = exponent.byteLength(); if (byteLength < 1 || byteLength > 32) { trap(exceptions_1.ERROR.OUT_OF_RANGE); } var gasPrice = runState._common.param('gasPrices', 'expByte'); var amount = new BN(byteLength).muln(gasPrice); runState.eei.useGas(amount); if (base.isZero()) { runState.stack.push(new BN(0)); return; } var m = BN.red(utils.TWO_POW256); var redBase = base.toRed(m); var r = redBase.redPow(exponent); runState.stack.push(r.fromRed()); }, SIGNEXTEND: function (runState) { var _a = runState.stack.popN(2), k = _a[0], val = _a[1]; if (k.ltn(31)) { var signBit = k .muln(8) .iaddn(7) .toNumber(); var mask = new BN(1).ishln(signBit).isubn(1); if (val.testn(signBit)) { val = val.or(mask.notn(256)); } else { val = val.and(mask); } } else { // return the same value val = new BN(val); } runState.stack.push(val); }, // 0x10 range - bit ops LT: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = new BN(a.lt(b) ? 1 : 0); runState.stack.push(r); }, GT: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = new BN(a.gt(b) ? 1 : 0); runState.stack.push(r); }, SLT: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = new BN(a.fromTwos(256).lt(b.fromTwos(256)) ? 1 : 0); runState.stack.push(r); }, SGT: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = new BN(a.fromTwos(256).gt(b.fromTwos(256)) ? 1 : 0); runState.stack.push(r); }, EQ: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = new BN(a.eq(b) ? 1 : 0); runState.stack.push(r); }, ISZERO: function (runState) { var a = runState.stack.pop(); var r = new BN(a.isZero() ? 1 : 0); runState.stack.push(r); }, AND: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = a.and(b); runState.stack.push(r); }, OR: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = a.or(b); runState.stack.push(r); }, XOR: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; var r = a.xor(b); runState.stack.push(r); }, NOT: function (runState) { var a = runState.stack.pop(); var r = a.notn(256); runState.stack.push(r); }, BYTE: function (runState) { var _a = runState.stack.popN(2), pos = _a[0], word = _a[1]; if (pos.gten(32)) { runState.stack.push(new BN(0)); return; } var r = new BN(word.shrn((31 - pos.toNumber()) * 8).andln(0xff)); runState.stack.push(r); }, SHL: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; if (!runState._common.gteHardfork('constantinople')) { trap(exceptions_1.ERROR.INVALID_OPCODE); } if (a.gten(256)) { runState.stack.push(new BN(0)); return; } var r = b.shln(a.toNumber()).iand(utils.MAX_INTEGER); runState.stack.push(r); }, SHR: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; if (!runState._common.gteHardfork('constantinople')) { trap(exceptions_1.ERROR.INVALID_OPCODE); } if (a.gten(256)) { runState.stack.push(new BN(0)); return; } var r = b.shrn(a.toNumber()); runState.stack.push(r); }, SAR: function (runState) { var _a = runState.stack.popN(2), a = _a[0], b = _a[1]; if (!runState._common.gteHardfork('constantinople')) { trap(exceptions_1.ERROR.INVALID_OPCODE); } var r; var isSigned = b.testn(255); if (a.gten(256)) { if (isSigned) { r = new BN(utils.MAX_INTEGER); } else { r = new BN(0); } runState.stack.push(r); return; } var c = b.shrn(a.toNumber()); if (isSigned) { var shiftedOutWidth = 255 - a.toNumber(); var mask = utils.MAX_INTEGER.shrn(shiftedOutWidth).shln(shiftedOutWidth); r = c.ior(mask); } else { r = c; } runState.stack.push(r); }, // 0x20 range - crypto SHA3: function (runState) { var _a = runState.stack.popN(2), offset = _a[0], length = _a[1]; subMemUsage(runState, offset, length); var data = Buffer.alloc(0); if (!length.isZero()) { data = runState.memory.read(offset.toNumber(), length.toNumber()); } // copy fee runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sha3Word')).imul(divCeil(length, new BN(32)))); var r = new BN(utils.keccak256(data)); runState.stack.push(r); }, // 0x30 range - closure state ADDRESS: function (runState) { runState.stack.push(new BN(runState.eei.getAddress())); }, BALANCE: function (runState) { return __awaiter(this, void 0, void 0, function () { var address, addressBuf, balance; return __generator(this, function (_a) { switch (_a.label) { case 0: address = runState.stack.pop(); addressBuf = addressToBuffer(address); return [4 /*yield*/, runState.eei.getExternalBalance(addressBuf)]; case 1: balance = _a.sent(); runState.stack.push(balance); return [2 /*return*/]; } }); }); }, ORIGIN: function (runState) { runState.stack.push(runState.eei.getTxOrigin()); }, CALLER: function (runState) { runState.stack.push(runState.eei.getCaller()); }, CALLVALUE: function (runState) { runState.stack.push(runState.eei.getCallValue()); }, CALLDATALOAD: function (runState) { var pos = runState.stack.pop(); if (pos.gt(runState.eei.getCallDataSize())) { runState.stack.push(new BN(0)); return; } var i = pos.toNumber(); var loaded = runState.eei.getCallData().slice(i, i + 32); loaded = loaded.length ? loaded : Buffer.from([0]); var r = new BN(utils.setLengthRight(loaded, 32)); runState.stack.push(r); }, CALLDATASIZE: function (runState) { var r = runState.eei.getCallDataSize(); runState.stack.push(r); }, CALLDATACOPY: function (runState) { var _a = runState.stack.popN(3), memOffset = _a[0], dataOffset = _a[1], dataLength = _a[2]; subMemUsage(runState, memOffset, dataLength); runState.eei.useGas(new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(dataLength, new BN(32)))); var data = getDataSlice(runState.eei.getCallData(), dataOffset, dataLength); var memOffsetNum = memOffset.toNumber(); var dataLengthNum = dataLength.toNumber(); runState.memory.extend(memOffsetNum, dataLengthNum); runState.memory.write(memOffsetNum, dataLengthNum, data); }, CODESIZE: function (runState) { runState.stack.push(runState.eei.getCodeSize()); }, CODECOPY: function (runState) { var _a = runState.stack.popN(3), memOffset = _a[0], codeOffset = _a[1], length = _a[2]; subMemUsage(runState, memOffset, length); runState.eei.useGas(new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(length, new BN(32)))); var data = getDataSlice(runState.eei.getCode(), codeOffset, length); var memOffsetNum = memOffset.toNumber(); var lengthNum = length.toNumber(); runState.memory.extend(memOffsetNum, lengthNum); runState.memory.write(memOffsetNum, lengthNum, data); }, EXTCODESIZE: function (runState) { return __awaiter(this, void 0, void 0, function () { var address, size; return __generator(this, function (_a) { switch (_a.label) { case 0: address = runState.stack.pop(); return [4 /*yield*/, runState.eei.getExternalCodeSize(address)]; case 1: size = _a.sent(); runState.stack.push(size); return [2 /*return*/]; } }); }); }, EXTCODECOPY: function (runState) { return __awaiter(this, void 0, void 0, function () { var _a, address, memOffset, codeOffset, length, code, data, memOffsetNum, lengthNum; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = runState.stack.popN(4), address = _a[0], memOffset = _a[1], codeOffset = _a[2], length = _a[3]; // FIXME: for some reason this must come before subGas subMemUsage(runState, memOffset, length); // copy fee runState.eei.useGas(new BN(runState._common.param('gasPrices', 'copy')).imul(divCeil(length, new BN(32)))); return [4 /*yield*/, runState.eei.getExternalCode(address)]; case 1: code = _b.sent(); data = getDataSlice(code, codeOffset, length); memOffsetNum = memOffset.toNumber(); lengthNum = length.toNumber(); runState.memory.extend(memOffsetNum, lengthNum); runState.memory.write(memOffsetNum, lengthNum, data); return [2 /*return*/]; } }); }); }, EXTCODEHASH: function (runState) { return __awaiter(this, void 0, void 0, function () { var address, addressBuf, empty, code; return __generator(this, function (_a) { switch (_a.label) { case 0: address = runState.stack.pop(); if (!runState._common.gteHardfork('constantinople')) { trap(exceptions_1.ERROR.INVALID_OPCODE); } addressBuf = addressToBuffer(address); return [4 /*yield*/, runState.eei.isAccountEmpty(addressBuf)]; case 1: empty = _a.sent(); if (empty) { runState.stack.push(new BN(0)); return [2 /*return*/]; } return [4 /*yield*/, runState.eei.getExternalCode(address)]; case 2: code = _a.sent(); if (code.length === 0) { runState.stack.push(new BN(utils.KECCAK256_NULL)); return [2 /*return*/]; } runState.stack.push(new BN(utils.keccak256(code))); return [2 /*return*/]; } }); }); }, RETURNDATASIZE: function (runState) { runState.stack.push(runState.eei.getReturnDataSize()); }, RETURNDATACOPY: function (runState) { var _a = runState.stack.popN(3), memOffset = _a[0], returnDataOffset = _a[1], length = _a[2]; if (returnDataOffset.add(length).gt(runState.eei.getReturnDataSize())) { trap(exceptions_1.ERROR.OUT_OF_GAS); } subMemUsage(runState, memOffset, length); runState.eei.useGas(new BN(runState._common.param('gasPrices', 'copy')).mul(divCeil(length, new BN(32)))); var data = getDataSlice(runState.eei.getReturnData(), returnDataOffset, length); var memOffsetNum = memOffset.toNumber(); var lengthNum = length.toNumber(); runState.memory.extend(memOffsetNum, lengthNum); runState.memory.write(memOffsetNum, lengthNum, data); }, GASPRICE: function (runState) { runState.stack.push(runState.eei.getTxGasPrice()); }, // '0x40' range - block operations BLOCKHASH: function (runState) { return __awaiter(this, void 0, void 0, function () { var number, diff, hash; return __generator(this, function (_a) { switch (_a.label) { case 0: number = runState.stack.pop(); diff = runState.eei.getBlockNumber().sub(number); // block lookups must be within the past 256 blocks if (diff.gtn(256) || diff.lten(0)) { runState.stack.push(new BN(0)); return [2 /*return*/]; } return [4 /*yield*/, runState.eei.getBlockHash(number)]; case 1: hash = _a.sent(); runState.stack.push(hash); return [2 /*return*/]; } }); }); }, COINBASE: function (runState) { runState.stack.push(runState.eei.getBlockCoinbase()); }, TIMESTAMP: function (runState) { runState.stack.push(runState.eei.getBlockTimestamp()); }, NUMBER: function (runState) { runState.stack.push(runState.eei.getBlockNumber()); }, DIFFICULTY: function (runState) { runState.stack.push(runState.eei.getBlockDifficulty()); }, GASLIMIT: function (runState) { runState.stack.push(runState.eei.getBlockGasLimit()); }, CHAINID: function (runState) { if (!runState._common.gteHardfork('istanbul')) { trap(exceptions_1.ERROR.INVALID_OPCODE); } runState.stack.push(runState.eei.getChainId()); }, SELFBALANCE: function (runState) { if (!runState._common.gteHardfork('istanbul')) { trap(exceptions_1.ERROR.INVALID_OPCODE); } runState.stack.push(runState.eei.getSelfBalance()); }, // 0x50 range - 'storage' and execution POP: function (runState) { runState.stack.pop(); }, MLOAD: function (runState) { var pos = runState.stack.pop(); subMemUsage(runState, pos, new BN(32)); var word = runState.memory.read(pos.toNumber(), 32); runState.stack.push(new BN(word)); }, MSTORE: function (runState) { var _a = runState.stack.popN(2), offset = _a[0], word = _a[1]; var buf = word.toArrayLike(Buffer, 'be', 32); subMemUsage(runState, offset, new BN(32)); var offsetNum = offset.toNumber(); runState.memory.extend(offsetNum, 32); runState.memory.write(offsetNum, 32, buf); }, MSTORE8: function (runState) { var _a = runState.stack.popN(2), offset = _a[0], byte = _a[1]; // NOTE: we're using a 'trick' here to get the least significant byte // NOTE: force cast necessary because `BN.andln` returns number but // the types are wrong var buf = Buffer.from([byte.andln(0xff)]); subMemUsage(runState, offset, new BN(1)); var offsetNum = offset.toNumber(); runState.memory.extend(offsetNum, 1); runState.memory.write(offsetNum, 1, buf); }, SLOAD: function (runState) { return __awaiter(this, void 0, void 0, function () { var key, keyBuf, value, valueBN; return __generator(this, function (_a) { switch (_a.label) { case 0: key = runState.stack.pop(); keyBuf = key.toArrayLike(Buffer, 'be', 32); return [4 /*yield*/, runState.eei.storageLoad(keyBuf)]; case 1: value = _a.sent(); valueBN = value.length ? new BN(value) : new BN(0); runState.stack.push(valueBN); return [2 /*return*/]; } }); }); }, SSTORE: function (runState) { return __awaiter(this, void 0, void 0, function () { var _a, key, val, keyBuf, value, found; return __generator(this, function (_b) { switch (_b.label) { case 0: if (runState.eei.isStatic()) { trap(exceptions_1.ERROR.STATIC_STATE_CHANGE); } _a = runState.stack.popN(2), key = _a[0], val = _a[1]; keyBuf = key.toArrayLike(Buffer, 'be', 32); if (val.isZero()) { value = Buffer.from([]); } else { value = val.toArrayLike(Buffer, 'be'); } return [4 /*yield*/, getContractStorage(runState, runState.eei.getAddress(), keyBuf)]; case 1: found = _b.sent(); updateSstoreGas(runState, found, value); return [4 /*yield*/, runState.eei.storageStore(keyBuf, value)]; case 2: _b.sent(); return [2 /*return*/]; } }); }); }, JUMP: function (runState) { var dest = runState.stack.pop(); if (dest.gt(runState.eei.getCodeSize())) { trap(exceptions_1.ERROR.INVALID_JUMP + ' at ' + describeLocation(runState)); } var destNum = dest.toNumber(); if (!jumpIsValid(runState, destNum)) { trap(exceptions_1.ERROR.INVALID_JUMP + ' at ' + describeLocation(runState)); } runState.programCounter = destNum; }, JUMPI: function (runState) { var _a = runState.stack.popN(2), dest = _a[0], cond = _a[1]; if (!cond.isZero()) { if (dest.gt(runState.eei.getCodeSize())) { trap(exceptions_1.ERROR.INVALID_JUMP + ' at ' + describeLocation(runState)); } var destNum = dest.toNumber(); if (!jumpIsValid(runState, destNum)) { trap(exceptions_1.ERROR.INVALID_JUMP + ' at ' + describeLocation(runState)); } runState.programCounter = destNum; } }, PC: function (runState) { runState.stack.push(new BN(runState.programCounter - 1)); }, MSIZE: function (runState) { runState.stack.push(runState.memoryWordCount.muln(32)); }, GAS: function (runState) { runState.stack.push(new BN(runState.eei.getGasLeft())); }, JUMPDEST: function (runState) { }, PUSH: function (runState) { var numToPush = runState.opCode - 0x5f; var loaded = new BN(runState.eei .getCode() .slice(runState.programCounter, runState.programCounter + numToPush) .toString('hex'), 16); runState.programCounter += numToPush; runState.stack.push(loaded); }, DUP: function (runState) { var stackPos = runState.opCode - 0x7f; runState.stack.dup(stackPos); }, SWAP: function (runState) { var stackPos = runState.opCode - 0x8f; runState.stack.swap(stackPos); }, LOG: function (runState) { if (runState.eei.isStatic()) { trap(exceptions_1.ERROR.STATIC_STATE_CHANGE); } var _a = runState.stack.popN(2), memOffset = _a[0], memLength = _a[1]; var topicsCount = runState.opCode - 0xa0; if (topicsCount < 0 || topicsCount > 4) { trap(exceptions_1.ERROR.OUT_OF_RANGE); } var topics = runState.stack.popN(topicsCount); var topicsBuf = topics.map(function (a) { return a.toArrayLike(Buffer, 'be', 32); }); subMemUsage(runState, memOffset, memLength); var mem = Buffer.alloc(0); if (!memLength.isZero()) { mem = runState.memory.read(memOffset.toNumber(), memLength.toNumber()); } runState.eei.useGas(new BN(runState._common.param('gasPrices', 'logTopic')) .imuln(topicsCount) .iadd(memLength.muln(runState._common.param('gasPrices', 'logData')))); runState.eei.log(mem, topicsCount, topicsBuf); }, // '0xf0' range - closures CREATE: function (runState) { return __awaiter(this, void 0, void 0, function () { var _a, value, offset, length, gasLimit, data, ret; return __generator(this, function (_b) { switch (_b.label) { case 0: if (runState.eei.isStatic()) { trap(exceptions_1.ERROR.STATIC_STATE_CHANGE); } _a = runState.stack.popN(3), value = _a[0], offset = _a[1], length = _a[2]; subMemUsage(runState, offset, length); gasLimit = new BN(runState.eei.getGasLeft()); gasLimit = maxCallGas(gasLimit, runState.eei.getGasLeft()); data = Buffer.alloc(0); if (!length.isZero()) { data = runState.memory.read(offset.toNumber(), length.toNumber()); } return [4 /*yield*/, runState.eei.create(gasLimit, value, data)]; case 1: ret = _b.sent(); runState.stack.push(ret); return [2 /*return*/]; } }); }); }, CREATE2: function (runState) { return __awaiter(this, void 0, void 0, function () { var _a, value, offset, length, salt, gasLimit, data, ret; return __generator(this, function (_b) { switch (_b.label) { case 0: if (!runState._common.gteHardfork('constantinople')) { trap(exceptions_1.ERROR.INVALID_OPCODE); } if (runState.eei.isStatic()) { trap(exceptions_1.ERROR.STATIC_STATE_CHANGE); } _a = runState.stack.popN(4), value = _a[0], offset = _a[1], length = _a[2], salt = _a[3]; subMemUsage(runState, offset, length); // Deduct gas costs for hashing runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sha3Word')).imul(divCeil(length, new BN(32)))); gasLimit = new BN(runState.eei.getGasLeft()); gasLimit = maxCallGas(gasLimit, runState.eei.getGasLeft()); data = Buffer.alloc(0); if (!length.isZero()) { data = runState.memory.read(offset.toNumber(), length.toNumber()); } return [4 /*yield*/, runState.eei.create2(gasLimit, value, data, salt.toArrayLike(Buffer, 'be', 32))]; case 1: ret = _b.sent(); runState.stack.push(ret); return [2 /*return*/]; } }); }); }, CALL: function (runState) { return __awaiter(this, void 0, void 0, function () { var _a, gasLimit, toAddress, value, inOffset, inLength, outOffset, outLength, toAddressBuf, data, empty, ret; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = runState.stack.popN(7), gasLimit = _a[0], toAddress = _a[1], value = _a[2], inOffset = _a[3], inLength = _a[4], outOffset = _a[5], outLength = _a[6]; toAddressBuf = addressToBuffer(toAddress); if (runState.eei.isStatic() && !value.isZero()) { trap(exceptions_1.ERROR.STATIC_STATE_CHANGE); } subMemUsage(runState, inOffset, inLength); subMemUsage(runState, outOffset, outLength); if (!value.isZero()) { runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callValueTransfer'))); } gasLimit = maxCallGas(gasLimit, runState.eei.getGasLeft()); data = Buffer.alloc(0); if (!inLength.isZero()) { data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()); } return [4 /*yield*/, runState.eei.isAccountEmpty(toAddressBuf)]; case 1: empty = _b.sent(); if (empty) { if (!value.isZero()) { runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount'))); } } if (!value.isZero()) { // TODO: Don't use private attr directly runState.eei._gasLeft.iaddn(runState._common.param('gasPrices', 'callStipend')); gasLimit.iaddn(runState._common.param('gasPrices', 'callStipend')); } return [4 /*yield*/, runState.eei.call(gasLimit, toAddressBuf, value, data) // Write return data to memory ]; case 2: ret = _b.sent(); // Write return data to memory writeCallOutput(runState, outOffset, outLength); runState.stack.push(ret); return [2 /*return*/]; } }); }); }, CALLCODE: function (runState) { return __awaiter(this, void 0, void 0, function () { var _a, gasLimit, toAddress, value, inOffset, inLength, outOffset, outLength, toAddressBuf, data, ret; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = runState.stack.popN(7), gasLimit = _a[0], toAddress = _a[1], value = _a[2], inOffset = _a[3], inLength = _a[4], outOffset = _a[5], outLength = _a[6]; toAddressBuf = addressToBuffer(toAddress); subMemUsage(runState, inOffset, inLength); subMemUsage(runState, outOffset, outLength); if (!value.isZero()) { runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callValueTransfer'))); } gasLimit = maxCallGas(gasLimit, runState.eei.getGasLeft()); if (!value.isZero()) { // TODO: Don't use private attr directly runState.eei._gasLeft.iaddn(runState._common.param('gasPrices', 'callStipend')); gasLimit.iaddn(runState._common.param('gasPrices', 'callStipend')); } data = Buffer.alloc(0); if (!inLength.isZero()) { data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()); } return [4 /*yield*/, runState.eei.callCode(gasLimit, toAddressBuf, value, data) // Write return data to memory ]; case 1: ret = _b.sent(); // Write return data to memory writeCallOutput(runState, outOffset, outLength); runState.stack.push(ret); return [2 /*return*/]; } }); }); }, DELEGATECALL: function (runState) { return __awaiter(this, void 0, void 0, function () { var value, _a, gasLimit, toAddress, inOffset, inLength, outOffset, outLength, toAddressBuf, data, ret; return __generator(this, function (_b) { switch (_b.label) { case 0: value = runState.eei.getCallValue(); _a = runState.stack.popN(6), gasLimit = _a[0], toAddress = _a[1], inOffset = _a[2], inLength = _a[3], outOffset = _a[4], outLength = _a[5]; toAddressBuf = addressToBuffer(toAddress); subMemUsage(runState, inOffset, inLength); subMemUsage(runState, outOffset, outLength); gasLimit = maxCallGas(gasLimit, runState.eei.getGasLeft()); data = Buffer.alloc(0); if (!inLength.isZero()) { data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()); } return [4 /*yield*/, runState.eei.callDelegate(gasLimit, toAddressBuf, value, data) // Write return data to memory ]; case 1: ret = _b.sent(); // Write return data to memory writeCallOutput(runState, outOffset, outLength); runState.stack.push(ret); return [2 /*return*/]; } }); }); }, STATICCALL: function (runState) { return __awaiter(this, void 0, void 0, function () { var value, _a, gasLimit, toAddress, inOffset, inLength, outOffset, outLength, toAddressBuf, data, ret; return __generator(this, function (_b) { switch (_b.label) { case 0: value = new BN(0); _a = runState.stack.popN(6), gasLimit = _a[0], toAddress = _a[1], inOffset = _a[2], inLength = _a[3], outOffset = _a[4], outLength = _a[5]; toAddressBuf = addressToBuffer(toAddress); subMemUsage(runState, inOffset, inLength); subMemUsage(runState, outOffset, outLength); gasLimit = maxCallGas(gasLimit, runState.eei.getGasLeft()); data = Buffer.alloc(0); if (!inLength.isZero()) { data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()); } return [4 /*yield*/, runState.eei.callStatic(gasLimit, toAddressBuf, value, data) // Write return data to memory ]; case 1: ret = _b.sent(); // Write return data to memory writeCallOutput(runState, outOffset, outLength); runState.stack.push(ret); return [2 /*return*/]; } }); }); }, RETURN: function (runState) { var _a = runState.stack.popN(2), offset = _a[0], length = _a[1]; subMemUsage(runState, offset, length); var returnData = Buffer.alloc(0); if (!length.isZero()) { returnData = runState.memory.read(offset.toNumber(), length.toNumber()); } runState.eei.finish(returnData); }, REVERT: function (runState) { var _a = runState.stack.popN(2), offset = _a[0], length = _a[1]; subMemUsage(runState, offset, length); var returnData = Buffer.alloc(0); if (!length.isZero()) { returnData = runState.memory.read(offset.toNumber(), length.toNumber()); } runState.eei.revert(returnData); }, // '0x70', range - other SELFDESTRUCT: function (runState) { return __awaiter(this, void 0, void 0, function () { var selfdestructToAddress, selfdestructToAddressBuf, balance, empty; return __generator(this, function (_a) { switch (_a.label) { case 0: selfdestructToAddress = runState.stack.pop(); if (runState.eei.isStatic()) { trap(exceptions_1.ERROR.STATIC_STATE_CHANGE); } selfdestructToAddressBuf = addressToBuffer(selfdestructToAddress); return [4 /*yield*/, runState.eei.getExternalBalance(runState.eei.getAddress())]; case 1: balance = _a.sent(); if (!balance.gtn(0)) return [3 /*break*/, 3]; return [4 /*yield*/, runState.eei.isAccountEmpty(selfdestructToAddressBuf)]; case 2: empty = _a.sent(); if (empty) { runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount'))); } _a.label = 3; case 3: return [2 /*return*/, runState.eei.selfDestruct(selfdestructToAddressBuf)]; } }); }); }, }; function describeLocation(runState) { var hash = utils.keccak256(runState.eei.getCode()).toString('hex'); var address = runState.eei.getAddress().toString('hex'); var pc = runState.programCounter - 1; return hash + '/' + address + ':' + pc; } function trap(err) { // TODO: facilitate extra data along with errors throw new exceptions_1.VmError(err); } /** * Subtracts the amount needed for memory usage from `runState.gasLeft` * @method subMemUsage * @param {Object} runState * @param {BN} offset * @param {BN} length * @returns {String} */ function subMemUsage(runState, offset, length) { // YP (225): access with zero length will not extend the memory if (length.isZero()) return; var newMemoryWordCount = divCeil(offset.add(length), new BN(32)); if (newMemoryWordCount.lte(runState.memoryWordCount)) return; var words = newMemoryWordCount; var fee = new BN(runState._common.param('gasPrices', 'memory')); var quadCoeff = new BN(runState._common.param('gasPrices', 'quadCoeffDiv')); // words * 3 + words ^2 / 512 var cost = words.mul(fee).add(words.mul(words).div(quadCoeff)); if (cost.gt(runState.highestMemCost)) { runState.eei.useGas(cost.sub(runState.highestMemCost)); runState.highestMemCost = cost; } runState.memoryWordCount = newMemoryWordCount; } /** * Returns an overflow-safe slice of an array. It right-pads * the data with zeros to `length`. * @param {BN} offset * @param {BN} length * @param {Buffer} data */ function getDataSlice(data, offset, length) { var len = new BN(data.length); if (offset.gt(len)) { offset = len; } var end = offset.add(length); if (end.gt(len)) { end = len; } data = data.slice(offset.toNumber(), end.toNumber()); // Right-pad with zeros to fill dataLength bytes data = utils.setLengthRight(data, length.toNumber()); return data; } // checks if a jump is valid given a destination function jumpIsValid(runState, dest) { return runState.validJumps.indexOf(dest) !== -1; } function maxCallGas(gasLimit, gasLeft) { var gasAllowed = gasLeft.sub(gasLeft.divn(64)); return gasLimit.gt(gasAllowed) ? gasAllowed : gasLimit; } function getContractStorage(runState, address, key) { return new Promise(function (resolve, reject) { var cb = function (err, res) { if (err) return reject(err); resolve(res); }; runState.stateManager.getContractStorage(address, key, function (err, current) { if (err) return cb(err, null); if (runState._common.hardfork() === 'constantinople' || runState._common.gteHardfork('istanbul')) { runState.stateManager.getOriginalContractStorage(address, key, function (err, original) { if (err) return cb(err, null); cb(null, { current: current, original: original }); }); } else { cb(null, current); } }); }); } function updateSstoreGas(runState, found, value) { if (runState._common.hardfork() === 'constantinople') { var original = found.original; var current = found.current; if (current.equals(value)) { // If current value equals new value (this is a no-op), 200 gas is deducted. runState.eei.useGas(new BN(runState._common.param('gasPrices', 'netSstoreNoopGas'))); return; } // If current value does not equal new value if (original.equals(current)) { // If original value equals current value (this storage slot has not been changed by the current execution context) if (original.length === 0) { // If original value is 0, 20000 gas is deducted. return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'netSstoreInitGas'))); } if (value.length === 0) { // If new value is 0, add 15000 gas to refund counter. runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'netSstoreClearRefund'))); } // Otherwise, 5000 gas is deducted. return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'netSstoreCleanGas'))); } // If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. if (original.length !== 0) { // If original value is not 0 if (current.length === 0) { // If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. runState.eei.subRefund(new BN(runState._common.param('gasPrices', 'netSstoreClearRefund'))); } else if (value.length === 0) { // If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'netSstoreClearRefund'))); } } if (original.equals(value)) { // If original value equals new value (this storage slot is reset) if (original.length === 0) { // If original value is 0, add 19800 gas to refund counter. runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'netSstoreResetClearRefund'))); } else { // Otherwise, add 4800 gas to refund counter. runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'netSstoreResetRefund'))); } } return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'netSstoreDirtyGas'))); } else if (runState._common.gteHardfork('istanbul')) { // EIP-2200 var original_1 = found.original; var current_1 = found.current; // Fail if not enough gas is left if (runState.eei.getGasLeft().lten(runState._common.param('gasPrices', 'sstoreSentryGasEIP2200'))) { trap(exceptions_1.ERROR.OUT_OF_GAS); } // Noop if (current_1.equals(value)) { return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sstoreNoopGasEIP2200'))); } if (original_1.equals(current_1)) { // Create slot if (original_1.length === 0) { return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sstoreInitGasEIP2200'))); } // Delete slot if (value.length === 0) { runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'sstoreClearRefundEIP2200'))); } // Write existing slot return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sstoreCleanGasEIP2200'))); } if (original_1.length > 0) { if (current_1.length === 0) { // Recreate slot runState.eei.subRefund(new BN(runState._common.param('gasPrices', 'sstoreClearRefundEIP2200'))); } else if (value.length === 0) { // Delete slot runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'sstoreClearRefundEIP2200'))); } } if (original_1.equals(value)) { if (original_1.length === 0) { // Reset to original non-existent slot runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'sstoreInitRefundEIP2200'))); } else { // Reset to original existing slot runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'sstoreCleanRefundEIP2200'))); } } // Dirty update return runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sstoreDirtyGasEIP2200'))); } else { if (value.length === 0 && !found.length) { runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sstoreReset'))); } else if (value.length === 0 && found.length) { runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sstoreReset'))); runState.eei.refundGas(new BN(runState._common.param('gasPrices', 'sstoreRefund'))); } else if (value.length !== 0 && !found.length) { runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sstoreSet'))); } else if (value.length !== 0 && found.length) { runState.eei.useGas(new BN(runState._common.param('gasPrices', 'sstoreReset'))); } } } function writeCallOutput(runState, outOffset, outLength) { var returnData = runState.eei.getReturnData(); if (returnData.length > 0) { var memOffset = outOffset.toNumber(); var dataLength = outLength.toNumber(); if (returnData.length < dataLength) { dataLength = returnData.length; } var data = getDataSlice(returnData, new BN(0), new BN(dataLength)); runState.memory.extend(memOffset, dataLength); runState.memory.write(memOffset, dataLength, data); } } //# sourceMappingURL=opFns.js.map