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

Commit

Permalink
Merge bffbe01 into 4ad1c27
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmurdoch committed Jul 12, 2019
2 parents 4ad1c27 + bffbe01 commit 2bc03c5
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 76 deletions.
92 changes: 35 additions & 57 deletions lib/blockchain_double.js
Original file line number Diff line number Diff line change
Expand Up @@ -481,93 +481,71 @@ BlockchainDouble.prototype.sortByPriceAndNonce = function() {
self.pending_transactions = sortedTransactions;
};

BlockchainDouble.prototype.processCall = function(tx, blockNumber, callback) {
var self = this;

var runCall = function(tx, err, parentBlock) {
BlockchainDouble.prototype.readyCall = function(tx, blockNumber, callback) {
const readyCall = (tx, err, parentBlock) => {
if (err) {
return callback(err);
}

// create a fake block with this fake transaction
self.createBlock(parentBlock, function(err, newBlock) {
this.createBlock(parentBlock, (err, newBlock) => {
if (err) {
return callback(err);
}

newBlock.transactions.push(tx);

var runArgs = {
// gas estimates and eth_calls shouldn't be subject to block gas limits
newBlock.header.gasLimit = tx.gasLimit;

const runArgs = {
tx: tx,
block: newBlock,
skipBalance: true,
skipNonce: true
};

var stateTrie = self.createStateTrie(self.data.trie_db, parentBlock.header.stateRoot);
var vm = self.createVMFromStateTrie(stateTrie);

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);
}

// 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] });

callback(vmerr, result);
});
const stateTrie = this.createStateTrie(this.data.trie_db, parentBlock.header.stateRoot);
const vm = this.createVMFromStateTrie(stateTrie);
callback(null, vm, runArgs);
});
};

// Delegate block selection
if (blockNumber === "latest") {
self.latestBlock(runCall.bind(null, tx));
this.latestBlock(readyCall.bind(null, tx));
} else {
self.getBlock(blockNumber, runCall.bind(null, tx));
this.getBlock(blockNumber, readyCall.bind(null, tx));
}
};

BlockchainDouble.prototype.estimateGas = function(tx, blockNumber, callback) {
var self = this;

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

// create a fake block with this fake transaction
self.createBlock(parentBlock, function(err, newBlock) {
vm.runTx(runArgs, (err, result) => {
// If we're given an error back directly, it's worse than a runtime error. Expose it and get out.
if (err) {
return callback(err);
callback(err);
return;
}
newBlock.transactions.push(tx);

var runArgs = {
tx: tx,
block: newBlock,
skipBalance: true,
skipNonce: true
};

var stateTrie = self.createStateTrie(self.data.trie_db, parentBlock.header.stateRoot);
var vm = self.createVMFromStateTrie(stateTrie);

estimateGas(vm, runArgs, err, callback);
// If no vm error, check for a runtime error. This can return null if no runtime error.
const runtimeErr = RuntimeError.fromResults([tx], { results: [result] });
callback(runtimeErr, result);
});
};
});
};

// Delegate block selection
if (blockNumber === "latest") {
self.latestBlock(runCall.bind(null, tx));
} else {
self.getBlock(blockNumber, runCall.bind(null, tx));
}
BlockchainDouble.prototype.estimateGas = function(tx, blockNumber, callback) {
this.readyCall(tx, blockNumber, (err, vm, runArgs) => {
if (err) {
callback(err);
return;
}

estimateGas(vm, runArgs, err, callback);
});
};

/**
Expand Down
11 changes: 7 additions & 4 deletions lib/statemanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,16 +324,16 @@ StateManager.prototype.queueTransaction = function(method, txJsonRpc, blockNumbe
let tx;
try {
tx = Transaction.fromJSON(txJsonRpc, type);
this._setTransactionDefaults(tx);
this._setTransactionDefaults(tx, method === "eth_sendTransaction");
} catch (e) {
callback(e);
return;
}
this._queueTransaction(method, tx, from, blockNumber, callback);
};

StateManager.prototype._setTransactionDefaults = function(tx) {
if (tx.gasLimit.length === 0) {
StateManager.prototype._setTransactionDefaults = function(tx, isTransaction) {
if (isTransaction && tx.gasLimit.length === 0) {
tx.gasLimit = utils.toBuffer(this.blockchain.defaultTransactionGasLimit);
}

Expand All @@ -356,7 +356,10 @@ StateManager.prototype._queueTransaction = function(method, tx, from, blockNumbe
}

// If the transaction has a higher gas limit than the block gas limit, error.
if (to.number(tx.gasLimit) > to.number(this.blockchain.blockGasLimit)) {
if (
(method === "eth_sendRawTransaction" || method === "eth_sendTransaction") &&
to.number(tx.gasLimit) > to.number(this.blockchain.blockGasLimit)
) {
return callback(new TXRejectedError("Exceeds block gas limit"));
}

Expand Down
30 changes: 22 additions & 8 deletions lib/subproviders/geth_api_double.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const { BlockOutOfRangeError } = require("../utils/errorhelper");

var Subprovider = require("web3-provider-engine/subproviders/subprovider.js");

const maxUInt64 = "0x" + Number.MAX_SAFE_INTEGER.toString(16);

inherits(GethApiDouble, Subprovider);

function GethApiDouble(options, provider) {
Expand Down Expand Up @@ -333,18 +335,30 @@ GethApiDouble.prototype.eth_sendRawTransaction = function(rawTx, callback) {
this.state.queueRawTransaction(data, callback);
};

GethApiDouble.prototype.eth_call = function(txData, blockNumber, callback) {
if (!txData.gas) {
txData.gas = this.state.blockchain.blockGasLimit;
GethApiDouble.prototype._setCallGasLimit = function(txData) {
// if the caller didn't specify a gas limit make sure we set one
if (!txData.gas && !txData.gasLimit) {
// if the user configured a global `callGasLimit`
// use it
const globalCallGasLimit = this.options.callGasLimit;
if (globalCallGasLimit != null) {
txData.gas = globalCallGasLimit;
} else {
// otherwise, set a very high gas limit. We'd use Infinity, or some VM flag to ignore gasLimit checks like
// geth does, but the VM doesn't currently support that for `runTx`.
// https://github.com/ethereumjs/ethereumjs-vm/blob/4bbb6e394a344717890d618a6be1cf67b8e5b74d/lib/runTx.ts#L71
txData.gas = maxUInt64;
// txData.gas = this.state.blockchain.blockGasLimit;
}
}

this.state.queueTransaction("eth_call", txData, blockNumber, callback); // :(
};
GethApiDouble.prototype.eth_call = function(txData, blockNumber, callback) {
this._setCallGasLimit(txData);
this.state.queueTransaction("eth_call", txData, blockNumber, callback);
};

GethApiDouble.prototype.eth_estimateGas = function(txData, blockNumber, callback) {
if (!txData.gas) {
txData.gas = this.state.blockchain.blockGasLimit;
}
this._setCallGasLimit(txData);
this.state.queueTransaction("eth_estimateGas", txData, blockNumber, callback);
};

Expand Down
32 changes: 25 additions & 7 deletions test/call.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,45 @@ const assert = require("assert");
const bootstrap = require("./helpers/contract/bootstrap");

describe("eth_call", function() {
let context;

before("Setting up web3 and contract", async function() {
this.timeout(10000);
it("should use the call gas limit if no call gas limit is specified in the call", async function() {
const contractRef = {
contractFiles: ["EstimateGas"],
contractSubdirectory: "gas"
};
let context;

context = await bootstrap(contractRef);
context = await bootstrap(contractRef, {
callGasLimit: "0x6691b7"
});
const { accounts, instance } = context;

const name = "0x54696d"; // Byte code for "Tim"
const description = "0x4120677265617420677579"; // Byte code for "A great guy"
const value = 5;

// this call uses more than the default transaction gas limit and will
// therefore fail if the block gas limit isn't used for calls
const status = await instance.methods.add(name, description, value).call({ from: accounts[0] });

assert.strictEqual(status, true);
});

it("should use the block gas limit if no gas limit is specified", async function() {
it("should use maxUInt64 call gas limit if no gas limit is specified in the provider or the call", async function() {
const contractRef = {
contractFiles: ["EstimateGas"],
contractSubdirectory: "gas"
};
let context;

context = await bootstrap(contractRef);
const { accounts, instance } = context;

const name = "0x54696d"; // Byte code for "Tim"
const description = "0x4120677265617420677579"; // Byte code for "A great guy"
const value = 5;

// this call uses more than the default transaction gas limit and will
// therefore fail if the block gas limit isn't used for calls
// therefore fail if the maxUInt64 limit isn't used for calls
const status = await instance.methods.add(name, description, value).call({ from: accounts[0] });

assert.strictEqual(status, true);
Expand Down

0 comments on commit 2bc03c5

Please sign in to comment.