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

Commit

Permalink
Merge 98b5837 into c305d20
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasjpaterno committed Dec 10, 2019
2 parents c305d20 + 98b5837 commit 8a1c488
Show file tree
Hide file tree
Showing 17 changed files with 828 additions and 633 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Both `.provider()` and `.server()` take a single object which allows you to spec
* `"fork"`: `string` or `object` - Fork from another currently running Ethereum client at a given block. When a `string`, input should be the HTTP location and port of the other client, e.g. `http://localhost:8545`. You can optionally specify the block to fork from using an `@` sign: `http://localhost:8545@1599200`. Can also be a `Web3 Provider` object, optionally used in conjunction with the `fork_block_number` option below.
* `"fork_block_number"`: `string` or `number` - Block number the provider should fork from, when the `fork` option is specified. If the `fork` option is specified as a string including the `@` sign and a block number, the block number in the `fork` parameter takes precedence.
* `"network_id"`: Specify the network id ganache-core will use to identify itself (defaults to the current time or the network id of the forked blockchain if configured)
* `"chainId"`: Specify the network's chain id (defaults to `1` for legacy reasons)
* `"time"`: `Date` - Date that the first block should start. Use this feature, along with the `evm_increaseTime` method to test time-dependent code.
* `"locked"`: `boolean` - whether or not accounts are locked by default.
* `"unlocked_accounts"`: `Array` - array of addresses or address indexes specifying which accounts should be unlocked.
Expand Down
47 changes: 37 additions & 10 deletions lib/blockchain_double.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ var Block = require("ethereumjs-block");
var Log = require("./utils/log");
var Receipt = require("./utils/receipt");
var VM = require("ethereumjs-vm").default;
var Common = require("ethereumjs-common").default;
var RuntimeError = require("./utils/runtimeerror");
var Trie = require("merkle-patricia-tree");
var utils = require("ethereumjs-util");
var async = require("async");
var Heap = require("heap");
var Database = require("./database");
var EventEmitter = require("events");
var estimateGas = require("./utils/gasEstimation");
var estimateGas = require("./utils/gas/estimateGas");
var _ = require("lodash");
var promisify = require("util").promisify;
const BN = utils.BN;
Expand Down Expand Up @@ -43,7 +44,7 @@ const defaultOptions = {
defaultTransactionGasLimit: "0x15f90",
time: null,
debug: false,
hardfork: "petersburg",
hardfork: "istanbul",
allowUnlimitedContractSize: false
};

Expand Down Expand Up @@ -127,8 +128,21 @@ BlockchainDouble.prototype.initialize = function(accounts, callback) {

BlockchainDouble.prototype.createVMFromStateTrie = function(state, activatePrecompiles) {
const self = this;
const common = Common.forCustomChain(
"mainnet", // TODO needs to match chain id
{
name: "ganache",
networkId: self.options.network_id || self.forkVersion,
chainId: self.options.chainId,
comment: "Local test network",
bootstrapNodes: []
},
self.options.hardfork
);

const vm = new VM({
state: state,
common,
blockchain: {
// EthereumJS VM needs a blockchain object in order to get block information.
// When calling getBlock() it will pass a number that's of a Buffer type.
Expand All @@ -149,7 +163,6 @@ BlockchainDouble.prototype.createVMFromStateTrie = function(state, activatePreco
}
},
activatePrecompiles: activatePrecompiles || false,
hardfork: self.options.hardfork,
allowUnlimitedContractSize: self.options.allowUnlimitedContractSize
});

Expand Down Expand Up @@ -491,7 +504,7 @@ BlockchainDouble.prototype.sortByPriceAndNonce = function() {
self.pending_transactions = sortedTransactions;
};

BlockchainDouble.prototype.readyCall = function(tx, emulateParent, blockNumber, callback) {
BlockchainDouble.prototype.getReadyCall = function(tx, emulateParent, blockNumber, callback) {
const readyCall = (tx, err, parentBlock) => {
if (err) {
return callback(err);
Expand All @@ -513,9 +526,8 @@ BlockchainDouble.prototype.readyCall = function(tx, emulateParent, blockNumber,
skipBalance: true,
skipNonce: true
};
const stateTrie = this.createStateTrie(this.data.trie_db, parentBlock.header.stateRoot);
const vm = this.createVMFromStateTrie(stateTrie);
callback(null, vm, runArgs);

callback(null, parentBlock.header.stateRoot, runArgs);
});
};
// Delegate block selection
Expand All @@ -526,6 +538,18 @@ BlockchainDouble.prototype.readyCall = function(tx, emulateParent, blockNumber,
}
};

BlockchainDouble.prototype.readyCall = function(tx, emulateParent, blockNumber, callback) {
this.getReadyCall(tx, emulateParent, blockNumber, (err, stateRoot, runArgs) => {
if (err) {
callback(err);
return;
}
const stateTrie = this.createStateTrie(this.data.trie_db, stateRoot);
const vm = this.createVMFromStateTrie(stateTrie);
callback(null, vm, runArgs);
});
};

BlockchainDouble.prototype.processCall = function(tx, blockNumber, callback) {
this.readyCall(tx, true, blockNumber, async(err, vm, runArgs) => {
if (err) {
Expand Down Expand Up @@ -553,13 +577,16 @@ BlockchainDouble.prototype.processCall = function(tx, blockNumber, callback) {
};

BlockchainDouble.prototype.estimateGas = function(tx, blockNumber, callback) {
this.readyCall(tx, false, blockNumber, (err, vm, runArgs) => {
this.getReadyCall(tx, false, blockNumber, (err, stateRoot, runArgs) => {
if (err) {
callback(err);
return;
}

estimateGas(vm, runArgs, callback);
const generateVM = () => {
const stateTrie = this.createStateTrie(this.data.trie_db, stateRoot);
return this.createVMFromStateTrie(stateTrie);
};
estimateGas(generateVM, runArgs, callback);
});
};

Expand Down
28 changes: 27 additions & 1 deletion lib/database/txserializer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// var to = require("../utils/to");
var Transaction = require("../utils/transaction");
var Common = require("ethereumjs-common").default;
var ethUtil = require("ethereumjs-util");

const decode = function(json, done) {
const options = {
Expand All @@ -16,11 +18,35 @@ const decode = function(json, done) {
s: json.s
};

const sigV = ethUtil.bufferToInt(options.v);
let chainId = Math.floor((sigV - 35) / 2);
if (chainId < 0) {
chainId = 0;
}

const commonOptions = {
name: "ganache",
chainId: 1,
comment: "Local test network"
};

let hardfork = "istanbul";
if (json._common) {
hardfork = json._common.hardfork;
commonOptions.chainId = json._common.chainId;
commonOptions.networkId = json._common.networkId;
}

const common = Common.forCustomChain(
"mainnet", // TODO needs to match chain id
commonOptions,
hardfork
);
// databases generated before ganache-core@2.3.2 didn't have a `_type` and
// and were always fake signed. So if _type is undefined it is "fake" (even
// if we have a valid signature that can generate the tx's `from`).
const type = json._type === undefined ? Transaction.types.fake : json._type;
const tx = Transaction.fromJSON(options, type);
const tx = Transaction.fromJSON(options, type, common);

// Commenting this out because we don't want to throw if the json.hash we
// put in is different that the tx.hash() calculation we now have. There
Expand Down
3 changes: 3 additions & 0 deletions lib/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ function Provider(options) {
}

const defaultOptions = {
// we use `1` instead of `1337` because ganache
// databases historically use `1`
chainId: 1,
vmErrorsOnRPCResponse: true,
verbose: false,
asyncRequestProcessing: false,
Expand Down
6 changes: 3 additions & 3 deletions lib/statemanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ StateManager.prototype.getCode = function(address, number, callback) {
};

StateManager.prototype.queueRawTransaction = function(data, callback) {
const tx = new Transaction(data, Transaction.types.signed);
const tx = new Transaction(data, Transaction.types.signed, this.blockchain.vm.opts.common);
// use toLowerCase() to properly handle from addresses meant to be validated.
const from = to.hex(tx.from).toLowerCase();
this._queueTransaction("eth_sendRawTransaction", tx, from, null, callback);
Expand Down Expand Up @@ -336,7 +336,7 @@ StateManager.prototype.queueTransaction = function(method, txJsonRpc, blockNumbe

let tx;
try {
tx = Transaction.fromJSON(txJsonRpc, type);
tx = Transaction.fromJSON(txJsonRpc, type, this.blockchain.vm.opts.common);
this._setTransactionDefaults(tx, method === "eth_sendTransaction");
} catch (e) {
callback(e);
Expand Down Expand Up @@ -673,7 +673,7 @@ StateManager.prototype.processGasEstimate = function(from, tx, blockNumber, call
}
var result = "0x";
if (!results.error) {
result = results.gasRefund ? to.hex(results.gasEstimate.add(results.gasRefund)) : to.hex(results.gasEstimate);
result = to.hex(results.gasEstimate);
} else {
self.logger.log(`Error calculating gas estimate: ${results.error}`);
}
Expand Down
3 changes: 1 addition & 2 deletions lib/subproviders/geth_api_double.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ GethApiDouble.prototype.eth_blockNumber = function(callback) {
};

GethApiDouble.prototype.eth_chainId = function(callback) {
// chainId of 1337 is the default for private networks as per EIP-155
callback(null, to.hex(1337));
callback(null, to.hex(this.options.chainId));
};

GethApiDouble.prototype.eth_coinbase = function(callback) {
Expand Down
8 changes: 5 additions & 3 deletions lib/utils/forkedblockchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function ForkedBlockchain(options) {
if (!protocolReg.test(options.fork)) {
// we don't have a protocol at all, assume ws
options.fork = "ws://" + options.fork;
fork = new Web3.providers.HttpProvider(options.fork);
fork = new Web3.providers.WebsocketProvider(options.fork);
} else if (validProtocolReg.test(options.fork)) {
if (httpReg.test(options.fork)) {
fork = new Web3.providers.HttpProvider(options.fork);
Expand Down Expand Up @@ -244,7 +244,9 @@ ForkedBlockchain.prototype.getFallbackBlock = function(numberOrHash, cb) {
block.header.extraData = utils.toBuffer(json.extraData);

(json.transactions || []).forEach(function(txJson, index) {
block.transactions.push(Transaction.fromJSON(txJson, Transaction.types.real));
block.transactions.push(
Transaction.fromJSON(txJson, Transaction.types.real, null, self.forkVersion, self.options.hardfork)
);
});

// Fake block. Let's do the worst.
Expand Down Expand Up @@ -425,7 +427,7 @@ ForkedBlockchain.prototype.getTransaction = function(hash, callback) {
}

if (result) {
result = Transaction.fromJSON(result, Transaction.types.signed);
result = Transaction.fromJSON(result, Transaction.types.signed, null, self.forkVersion, self.options.hardfork);
}

callback(null, result);
Expand Down
38 changes: 38 additions & 0 deletions lib/utils/gas/binSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { BN } = require("ethereumjs-util");
const hexToBn = (val = 0) => new BN(parseInt("0x" + val.toString("hex"), 16));
const MULTIPLE = 64 / 63;

module.exports = async function binSearch(generateVM, runArgs, result, callback) {
const MAX = hexToBn(runArgs.block.header.gasLimit);
const gasRefund = result.execResult.gasRefund;
const startingGas = gasRefund ? result.gasEstimate.add(gasRefund) : result.gasEstimate;
const range = { lo: startingGas, hi: startingGas };
const isEnoughGas = async(gas) => {
const vm = generateVM(); // Generate fresh VM
runArgs.tx.gasLimit = gas.toBuffer();
const result = await vm.runTx(runArgs).catch((vmerr) => ({ vmerr }));
return !result.vmerr && !result.execResult.exceptionError;
};

if (!(await isEnoughGas(range.hi))) {
do {
range.hi = range.hi.muln(MULTIPLE);
} while (!(await isEnoughGas(range.hi)));
while (range.lo.addn(1).lt(range.hi)) {
const mid = range.lo.add(range.hi).divn(2);
if (await isEnoughGas(mid)) {
range.hi = mid;
} else {
range.lo = mid;
}
}
if (range.hi.gte(MAX)) {
if (!(await isEnoughGas(range.hi))) {
return callback(new Error("gas required exceeds allowance or always failing transaction"));
}
}
}

result.gasEstimate = range.hi;
callback(null, result);
};
14 changes: 14 additions & 0 deletions lib/utils/gas/estimateGas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const estimateGas = require("./guestimation");
const binSearch = require("./binSearch");

module.exports = async(generateVM, runArgs, callback) => {
const vm = generateVM();
estimateGas(vm, runArgs, async(err, result) => {
if (err) {
callback(err);
return;
}

await binSearch(generateVM, runArgs, result, callback);
});
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const RuntimeError = require("./runtimeerror");
const RuntimeError = require("../runtimeerror");

const { BN } = require("ethereumjs-util");
const bn = (val = 0) => new BN(val);
Expand Down

0 comments on commit 8a1c488

Please sign in to comment.