Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

chore: update ethereumjs-vm to v4.1.0 #466

Merged
merged 12 commits into from
Sep 20, 2019
Merged
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Both `.provider()` and `.server()` take a single object which allows you to spec
* `"account_keys_path"`: `String` - Specifies a file to save accounts and private keys to, for testing.
* `"vmErrorsOnRPCResponse"`: `boolean` - Whether or not to transmit transaction failures as RPC errors. Set to `false` for error reporting behaviour which is compatible with other clients such as geth and Parity. This is `true` by default to replicate the error reporting behavior of previous versions of ganache.
* `"hdPath"`: The hierarchical deterministic path to use when generating accounts. Default: "m/44'/60'/0'/0/"
* `"hardfork"`: `String` Allows to specify which hardfork should be used. Supported hardforks are `byzantium`, `constantinople`, and `petersburg` (default).
* `"hardfork"`: `String` Allows to specify which hardfork should be used. Supported hardforks are `byzantium`, `constantinople`, `petersburg` (default), and `istanbul` (beta).
* `"allowUnlimitedContractSize"`: `boolean` - Allows unlimited contract sizes while debugging (NOTE: this setting is often used in conjuction with an increased `gasLimit`). By setting this to `true`, the check within the EVM for contract size limit of 24KB (see [EIP-170](https://git.io/vxZkK)) is bypassed. Setting this to `true` **will** cause `ganache-core` to behave differently than production environments. (default: `false`; **ONLY** set to `true` during debugging).
* `"gasPrice"`: `String::hex` Sets the default gas price for transactions if not otherwise specified. Must be specified as a `hex` encoded string in `wei`. Defaults to `"0x77359400"` (2 `gwei`).
* `"gasLimit"`: `String::hex` Sets the block gas limit. Must be specified as a `hex` string. Defaults to `"0x6691b7"`.
Expand Down
229 changes: 114 additions & 115 deletions lib/blockchain_double.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var Account = require("ethereumjs-account").default;
var Block = require("ethereumjs-block");
var Log = require("./utils/log");
var Receipt = require("./utils/receipt");
var VM = require("ethereumjs-vm");
var VM = require("ethereumjs-vm").default;
var RuntimeError = require("./utils/runtimeerror");
var Trie = require("merkle-patricia-tree");
var utils = require("ethereumjs-util");
Expand Down Expand Up @@ -527,28 +527,28 @@ BlockchainDouble.prototype.readyCall = function(tx, emulateParent, blockNumber,
};

BlockchainDouble.prototype.processCall = function(tx, blockNumber, callback) {
this.readyCall(tx, true, blockNumber, (err, vm, runArgs) => {
this.readyCall(tx, true, blockNumber, async(err, vm, runArgs) => {
if (err) {
callback(err);
return;
}

vm.runTx(runArgs, function(vmerr, result) {
// This is a check that has been in there for awhile. I'm unsure if it's required, but it can't hurt.
if (vmerr && vmerr instanceof Error === false) {
vmerr = new Error("VM error: " + vmerr);
}
const result = await vm.runTx(runArgs).catch((vmerr) => ({ vmerr }));
let vmerr = result.vmerr;
// This is a check that has been in there for awhile. I'm unsure if it's required, but it can't hurt.
if (vmerr && vmerr instanceof Error === false) {
vmerr = new Error("VM error: " + vmerr);
}

// If we're given an error back directly, it's worse than a runtime error. Expose it and get out.
if (vmerr) {
return callback(vmerr, err);
}
// If we're given an error back directly, it's worse than a runtime error. Expose it and get out.
if (vmerr) {
return callback(vmerr, err);
}

// If no error, check for a runtime error. This can return null if no runtime error.
vmerr = RuntimeError.fromResults([tx], { results: [result] });
// If no error, check for a runtime error. This can return null if no runtime error.
vmerr = RuntimeError.fromResults([tx], { results: [result] });

callback(vmerr, result);
});
callback(vmerr, result);
});
};

Expand All @@ -575,128 +575,127 @@ BlockchainDouble.prototype.estimateGas = function(tx, blockNumber, callback) {
* @param {Function} callback Callback function when transaction processing is completed.
* @return [type] [description]
*/
BlockchainDouble.prototype.processBlock = function(vm, block, commit, callback) {
BlockchainDouble.prototype.processBlock = async function(vm, block, commit, callback) {
var self = this;

if (typeof commit === "function") {
callback = commit;
commit = true;
}

vm.runBlock(
{
const results = await vm
.runBlock({
block: block,
generate: true,
skipBlockValidation: true
},
async function(vmerr, results) {
// This is a check that has been in there for awhile. I'm unsure if it's required, but it can't hurt.
if (vmerr && vmerr instanceof Error === false) {
vmerr = new Error("VM error: " + vmerr);
}

// If we're given an error back directly, it's worse than a runtime error. Expose it and get out.
if (vmerr) {
callback(vmerr);
return;
}
// If no error, check for a runtime error. This can return null if no runtime error.
vmerr = RuntimeError.fromResults(block.transactions, results);
})
.catch((vmerr) => ({ vmerr }));
let vmerr = results.vmerr;
// This is a check that has been in there for awhile. I'm unsure if it's required, but it can't hurt.
if (vmerr && vmerr instanceof Error === false) {
vmerr = new Error("VM error: " + vmerr);
}

// Note, even if we have an error, some transactions may still have succeeded.
// Process their logs if so, returning the error at the end.
// If we're given an error back directly, it's worse than a runtime error. Expose it and get out.
if (vmerr) {
callback(vmerr);
return;
}
// If no error, check for a runtime error. This can return null if no runtime error.
vmerr = RuntimeError.fromResults(block.transactions, results);

var logs = [];
var receipts = [];
// Note, even if we have an error, some transactions may still have succeeded.
// Process their logs if so, returning the error at the end.

var totalBlockGasUsage = 0;
var logs = [];
var receipts = [];

results.results.forEach(function(result) {
totalBlockGasUsage += to.number(result.gasUsed);
});
var totalBlockGasUsage = 0;

block.header.gasUsed = utils.toBuffer(to.hex(totalBlockGasUsage));

const txTrie = new Trie();
const rcptTrie = new Trie();
const promises = [];
const putInTrie = (trie, key, val) => promisify(trie.put.bind(trie))(key, val);

for (var v = 0; v < results.receipts.length; v++) {
var result = results.results[v];
var receipt = results.receipts[v];
var tx = block.transactions[v];
var txHash = tx.hash();
var txLogs = [];

// Only process the transaction's logs if it didn't error.
if (result.vm.exception === 1) {
for (var i = 0; i < receipt.logs.length; i++) {
var receiptLog = receipt.logs[i];
var address = to.hex(receiptLog[0]);
var topics = [];

for (var j = 0; j < receiptLog[1].length; j++) {
topics.push(to.hex(receiptLog[1][j]));
}
results.results.forEach(function(result) {
totalBlockGasUsage += to.number(result.gasUsed);
});

var data = to.hex(receiptLog[2]);

var log = new Log({
logIndex: to.hex(i),
transactionIndex: to.hex(v),
transactionHash: txHash,
block: block,
address: address,
data: data,
topics: topics,
type: "mined"
});

logs.push(log);
txLogs.push(log);
}
block.header.gasUsed = utils.toBuffer(to.hex(totalBlockGasUsage));

const txTrie = new Trie();
const rcptTrie = new Trie();
const promises = [];
const putInTrie = (trie, key, val) => promisify(trie.put.bind(trie))(key, val);

for (var v = 0; v < results.receipts.length; v++) {
var result = results.results[v];
var receipt = results.receipts[v];
var tx = block.transactions[v];
var txHash = tx.hash();
var txLogs = [];

// Only process the transaction's logs if it didn't error.
if (result.execResult.exceptionError === undefined) {
for (var i = 0; i < receipt.logs.length; i++) {
var receiptLog = receipt.logs[i];
var address = to.hex(receiptLog[0]);
var topics = [];

for (var j = 0; j < receiptLog[1].length; j++) {
topics.push(to.hex(receiptLog[1][j]));
}

let rcpt = new Receipt(
tx,
block,
txLogs,
result.gasUsed.toArrayLike(Buffer),
receipt.gasUsed,
result.createdAddress,
receipt.status,
to.hex(receipt.bitvector)
);
receipts.push(rcpt);

const rawReceipt = [receipt.status, receipt.gasUsed, receipt.bitvector, receipt.logs];
const rcptBuffer = utils.rlp.encode(rawReceipt);
const key = utils.rlp.encode(v);
promises.push(putInTrie(txTrie, key, tx.serialize()));
promises.push(putInTrie(rcptTrie, key, rcptBuffer));
var data = to.hex(receiptLog[2]);

var log = new Log({
logIndex: to.hex(i),
transactionIndex: to.hex(v),
transactionHash: txHash,
block: block,
address: address,
data: data,
topics: topics,
type: "mined"
});

logs.push(log);
txLogs.push(log);
}
await Promise.all(promises);
}

block.header.transactionsTrie = utils.toBuffer(txTrie.root);
block.header.receiptTrie = utils.toBuffer(rcptTrie.root);
let rcpt = new Receipt(
tx,
block,
txLogs,
result.gasUsed.toArrayLike(Buffer),
receipt.gasUsed,
result.createdAddress,
receipt.status,
to.hex(receipt.bitvector)
);
receipts.push(rcpt);

if (commit) {
// Put that block on the end of the chain
self.putBlock(block, logs, receipts, done);
} else {
done();
}
const rawReceipt = [receipt.status, receipt.gasUsed, receipt.bitvector, receipt.logs];
const rcptBuffer = utils.rlp.encode(rawReceipt);
const key = utils.rlp.encode(v);
promises.push(putInTrie(txTrie, key, tx.serialize()));
promises.push(putInTrie(rcptTrie, key, rcptBuffer));
}
await Promise.all(promises);

function done(e) {
if (e) {
return callback(e);
}
// Note we return the vm err here too, if it exists.
callback(vmerr, block.transactions, results);
}
block.header.transactionsTrie = utils.toBuffer(txTrie.root);
block.header.receiptTrie = utils.toBuffer(rcptTrie.root);

if (commit) {
// Put that block on the end of the chain
self.putBlock(block, logs, receipts, done);
} else {
done();
}

function done(e) {
if (e) {
return callback(e);
}
);
// Note we return the vm err here too, if it exists.
callback(vmerr, block.transactions, results);
}
};

/**
Expand Down Expand Up @@ -1004,7 +1003,7 @@ BlockchainDouble.prototype.processStorageTrace = function(structLog, storageStac
// this one's more fun, we need to get the value the contract is loading from current storage
key = to.rpcDataHexString(stack[stack.length - 1], 64).replace("0x", "");

vm.stateManager.getContractStorage(event.address, "0x" + key, function(err, result) {
vm.stateManager.getContractStorage(event.address, Buffer.from(key, "hex"), function(err, result) {
if (err) {
return callback(err);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/statemanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,8 @@ StateManager.prototype.processCall = function(from, tx, blockNumber, callback) {
}

var result = "0x";
if (!results.error && results.vm.return) {
result = to.hex(results.vm.return);
if (!results.error && results.execResult.returnValue) {
result = to.hex(results.execResult.returnValue);
} else if (results.error) {
self.logger.log(`Error processing call: ${results.error}`);
}
Expand Down
41 changes: 18 additions & 23 deletions lib/utils/gasEstimation.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,8 @@
const STIPEND = 2300;
module.exports = (vm, runArgs, callback) => {
module.exports = async(vm, runArgs, callback) => {
const steps = stepTracker();

vm.on("step", steps.collect);

vm.runTx(runArgs, function(vmerr, result) {
if (vmerr) {
return callback(vmerr);
} else if (result.vm.exception === 0) {
const error = result.vm.exceptionError
? new Error(`execution error: ${result.vm.exceptionError.error}`)
: new Error("execution error");
error.code = -32000;
return callback(error);
} else if (steps.done()) {
let estimate = result.gasUsed;
result.gasEstimate = estimate;
} else {
const actualUsed = steps.ops[0].gasLeft.sub(steps.ops[steps.ops.length - 1].gasLeft).toNumber();
const total = getTotal();
const sixtyFloorths = total - actualUsed;
result.gasEstimate = result.gasUsed.addn(sixtyFloorths);
}
callback(vmerr, result);
});

const Context = (index, fee) => {
const base = index === 0;
let start = index;
Expand Down Expand Up @@ -134,6 +112,23 @@ module.exports = (vm, runArgs, callback) => {
let gas = context.getCost();
return gas.cost + gas.sixtyFloorths;
};

const result = await vm.runTx(runArgs).catch((vmerr) => ({ vmerr }));
const vmerr = result.vmerr;
if (vmerr) {
return callback(vmerr);
} else if (result.execResult.exceptionError) {
return callback(new Error(`execution error: ${result.execResult.exceptionError.error}`));
} else if (steps.done()) {
let estimate = result.gasUsed;
result.gasEstimate = estimate;
} else {
const actualUsed = steps.ops[0].gasLeft.sub(steps.ops[steps.ops.length - 1].gasLeft).toNumber();
const total = getTotal();
const sixtyFloorths = total - actualUsed;
result.gasEstimate = result.gasUsed.addn(sixtyFloorths);
}
callback(vmerr, result);
};

const check = (arr) => (opname) => arr.includes(opname);
Expand Down
10 changes: 5 additions & 5 deletions lib/utils/runtimeerror.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@ RuntimeError.prototype.combine = function(transactions, vmOutput) {
var result = results[i];

// 1 means no error, oddly.
if (result.vm.exception !== 1) {
if (result.execResult.exceptionError) {
var hash = to.hex(tx.hash());
this.hashes.push(hash);
var reason;
var returnData = result.vm.return;
var returnData = result.execResult.returnValue;
if (returnData && returnData.slice(0, 4).toString("hex") === "08c379a0") {
reason = abi.rawDecode(["string"], returnData.slice(4))[0];
}

this.results[hash] = {
error: result.vm.exceptionError.error || result.vm.exceptionError,
program_counter: result.vm.runState.programCounter,
return: to.hex(result.vm.return),
error: result.execResult.exceptionError.error || result.execResult.exceptionError,
program_counter: result.execResult.runState.programCounter,
return: to.hex(result.execResult.returnValue),
reason: reason
};
}
Expand Down
Loading