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

Commit

Permalink
Merge 82acc5f into f606f47
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmurdoch committed Jul 15, 2019
2 parents f606f47 + 82acc5f commit 5b2b5aa
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 53 deletions.
57 changes: 36 additions & 21 deletions lib/blockchain_double.js
Expand Up @@ -354,12 +354,16 @@ BlockchainDouble.prototype.clearPendingTransactions = function() {
* @param {Function} callback Callback function called after block is created
* @return Block The block created.
*/
BlockchainDouble.prototype.createBlock = function(parent, callback) {
BlockchainDouble.prototype.createBlock = function(parent, emulateParent, callback) {
var self = this;

if (typeof parent === "function") {
callback = parent;
parent = null;
emulateParent = false;
} else if (typeof emulateParent === "function") {
callback = emulateParent;
emulateParent = false;
}

var block = new Block();
Expand All @@ -377,18 +381,24 @@ BlockchainDouble.prototype.createBlock = function(parent, callback) {
return callback(err);
}

var parentNumber = parent != null ? to.number(parent.header.number) : -1;

block.header.gasLimit = self.blockGasLimit;

// Ensure we have the right block number for the VM.
block.header.number = to.hex(parentNumber + 1);
if (parent != null && emulateParent) {
block.header.number = parent.header.number;
block.header.timestamp = parent.header.timestamp;
block.header.parentHash = parent.header.parentHash;
} else {
var parentNumber = parent != null ? to.number(parent.header.number) : -1;

// Ensure we have the right block number for the VM.
block.header.number = to.hex(parentNumber + 1);

// Set the timestamp before processing txs
block.header.timestamp = to.hex(self.currentTime());
// Set the timestamp before processing txs
block.header.timestamp = to.hex(self.currentTime());

if (parent != null) {
block.header.parentHash = to.hex(parent.hash());
if (parent != null) {
block.header.parentHash = to.hex(parent.hash());
}
}

callback(null, block);
Expand Down Expand Up @@ -481,13 +491,13 @@ BlockchainDouble.prototype.sortByPriceAndNonce = function() {
self.pending_transactions = sortedTransactions;
};

BlockchainDouble.prototype.readyCall = function(tx, blockNumber, callback) {
BlockchainDouble.prototype.readyCall = function(tx, emulateParent, blockNumber, callback) {
const readyCall = (tx, err, parentBlock) => {
if (err) {
return callback(err);
}
// create a fake block with this fake transaction
this.createBlock(parentBlock, (err, newBlock) => {
this.createBlock(parentBlock, emulateParent, (err, newBlock) => {
if (err) {
return callback(err);
}
Expand Down Expand Up @@ -517,28 +527,33 @@ BlockchainDouble.prototype.readyCall = function(tx, blockNumber, callback) {
};

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

vm.runTx(runArgs, (err, result) => {
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 (err) {
callback(err);
return;
if (vmerr) {
return callback(vmerr, err);
}

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

BlockchainDouble.prototype.estimateGas = function(tx, blockNumber, callback) {
this.readyCall(tx, blockNumber, (err, vm, runArgs) => {
this.readyCall(tx, false, blockNumber, (err, vm, runArgs) => {
if (err) {
callback(err);
return;
Expand Down Expand Up @@ -882,7 +897,7 @@ BlockchainDouble.prototype.processTransactionTrace = async function(hash, params
vm = self.createVMFromStateTrie(stateTrie);

// Prepare the "next" block with necessary transactions
self.createBlock(parent, function(err, block) {
self.createBlock(parent, false, function(err, block) {
if (err) {
return callback(err);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/subproviders/geth_api_double.js
Expand Up @@ -10,7 +10,7 @@ const { BlockOutOfRangeError } = require("../utils/errorhelper");

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

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

inherits(GethApiDouble, Subprovider);

Expand Down Expand Up @@ -346,7 +346,7 @@ GethApiDouble.prototype._setCallGasLimit = function(txData) {
// 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 = maxSafeInt;
}
}
};
Expand Down
23 changes: 12 additions & 11 deletions lib/utils/forkedblockchain.js
Expand Up @@ -79,18 +79,19 @@ ForkedBlockchain.prototype.patchVM = function(vm) {
* like `forkBlockNumber` (required for tracing transactions)
*/
ForkedBlockchain.prototype.createStateTrie = function(db, root, options) {
return new ForkedStorageTrie(
db,
root,
Object.assign(
{
fork: this.fork,
forkBlockNumber: this.forkBlockNumber,
blockchain: this
},
options
)
options = Object.assign(
{
fork: this.fork,
forkBlockNumber: this.forkBlockNumber,
blockchain: this
},
options
);
// never allow the forkBlockNumber to go beyond our root forkBlockNumber
if (options.forkBlockNumber > this.forkBlockNumber) {
options.forkBlockNumber = this.forkBlockNumber;
}
return new ForkedStorageTrie(db, root, options);
};

ForkedBlockchain.prototype.createGenesisBlock = function(callback) {
Expand Down
28 changes: 17 additions & 11 deletions test/call.js
Expand Up @@ -2,14 +2,13 @@ const assert = require("assert");
const bootstrap = require("./helpers/contract/bootstrap");

describe("eth_call", function() {
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;
const contractRef = {
contractFiles: ["EstimateGas"],
contractSubdirectory: "gas"
};

context = await bootstrap(contractRef, {
it("should use the call gas limit if no call gas limit is specified in the call", async function() {
const context = await bootstrap(contractRef, {
callGasLimit: "0x6691b7"
});
const { accounts, instance } = context;
Expand All @@ -25,14 +24,12 @@ describe("eth_call", function() {
assert.strictEqual(status, true);
});

it("should use maxUInt64 call gas limit if no gas limit is specified in the provider or the call", async function() {
it("should use max 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 context = await bootstrap(contractRef);
const { accounts, instance } = context;

const name = "0x54696d"; // Byte code for "Tim"
Expand All @@ -45,4 +42,13 @@ describe("eth_call", function() {

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

it("should use the current block number via `eth_call`", async() => {
const context = await bootstrap(contractRef);

const actualBlockNumber = await context.web3.eth.getBlockNumber();
// should read the block number, too
const callBlockNumber = await context.instance.methods.currentBlock().call();
assert.strictEqual(parseInt(callBlockNumber, 10), actualBlockNumber);
});
});
2 changes: 2 additions & 0 deletions test/contracts/debug/DebugContract.sol
Expand Up @@ -4,10 +4,12 @@ pragma solidity ^0.5.0;
contract DebugContract {
uint public value = 5;
uint public otherValue = 5;
uint public currentBlock = 0;

function setValue(uint _val) public {
value = _val;
otherValue += _val;
currentBlock = block.number;
}

function callSetValueTwice() public {
Expand Down
4 changes: 4 additions & 0 deletions test/contracts/gas/EstimateGas.sol
Expand Up @@ -99,4 +99,8 @@ contract EstimateGas {

return true;
}

function currentBlock() public returns (uint) {
return block.number;
}
}
36 changes: 31 additions & 5 deletions test/debug/debug.js
Expand Up @@ -84,6 +84,9 @@ function test(forked) {

describe("Trace a successful transaction", function() {
let options;
let originalStoredBlockNumber;
let latestStoredBlockNumber;
let latestBlockNumber;
before("set up transaction that should be traced", async() => {
const { accounts, instance } = context;
options = { from: accounts[0], gas };
Expand All @@ -93,6 +96,14 @@ function test(forked) {
hashToTrace = tx.transactionHash;
});

it("sets the blockNumber in storage", async() => {
const { instance, web3 } = context;
// check the value is what we expect it to be: 26
originalStoredBlockNumber = await instance.methods.currentBlock().call(options);
const blockNumber = await web3.eth.getBlockNumber();
assert.strictEqual(parseInt(originalStoredBlockNumber, 10), blockNumber);
});

it("sets the value to 26", async() => {
const { instance } = context;
// check the value is what we expect it to be: 26
Expand All @@ -101,9 +112,12 @@ function test(forked) {
});

it("changes state of contract to ensure trace doesn't overwrite data", async() => {
const { accounts, instance } = context;
const { accounts, instance, web3 } = context;
options = { from: accounts[0], gas };
await instance.methods.setValue(expectedValueBeforeTrace).send(options);
latestStoredBlockNumber = await instance.methods.currentBlock().call(options);

latestBlockNumber = await web3.eth.getBlockNumber();

// check the value is what we expect it to be: 1234
const value = await instance.methods.value().call(options);
Expand All @@ -112,7 +126,7 @@ function test(forked) {

it("should trace a successful transaction without changing state", async function() {
// We want to trace the transaction that sets the value to 26
const { accounts, instance, send } = context;
const { accounts, instance, send, web3 } = context;

const response = await send("debug_traceTransaction", hashToTrace, []);

Expand All @@ -138,7 +152,7 @@ function test(forked) {

assert.strictEqual(lastop.op, "STOP");
assert.strictEqual(lastop.gasCost, 1);
assert.strictEqual(lastop.pc, 208);
assert.strictEqual(lastop.pc, 235);
assert.strictEqual(
lastop.storage["0000000000000000000000000000000000000000000000000000000000000000"],
"000000000000000000000000000000000000000000000000000000000000001a"
Expand All @@ -147,12 +161,24 @@ function test(forked) {
lastop.storage["0000000000000000000000000000000000000000000000000000000000000001"],
"000000000000000000000000000000000000000000000000000000000000001f"
);
assert.strictEqual(
lastop.storage["0000000000000000000000000000000000000000000000000000000000000002"],
originalStoredBlockNumber.padStart(64, "0")
);
console.log("--------------------------------------------------");
const value = await instance.methods.value().call({ from: accounts[0], gas });
assert.strictEqual(value, expectedValueBeforeTrace);

const otherValue = await instance.methods.otherValue().call({ from: accounts[0], gas });
assert.strictEqual(otherValue, "1265");

// stored block number should not have changed:
const storedBlockNumber = await instance.methods.currentBlock().call({ from: accounts[0], gas });
assert.strictEqual(storedBlockNumber, latestStoredBlockNumber);

// block number should not have incremented because of `debug_traceTransaction`
const currentBlockNumber = await web3.eth.getBlockNumber();
assert.strictEqual(currentBlockNumber, latestBlockNumber);
});
});

Expand Down Expand Up @@ -233,12 +259,12 @@ function test(forked) {

// ensure the call to setValue with 2 was successfully stored for value
assert.strictEqual(
arrayOfStorageKeyValues[2]["0000000000000000000000000000000000000000000000000000000000000000"],
arrayOfStorageKeyValues[3]["0000000000000000000000000000000000000000000000000000000000000000"],
"0000000000000000000000000000000000000000000000000000000000000002"
);
// ensure the call to setValue with 2 was successfully stored for otherValue
assert.strictEqual(
arrayOfStorageKeyValues[3]["0000000000000000000000000000000000000000000000000000000000000001"],
arrayOfStorageKeyValues[4]["0000000000000000000000000000000000000000000000000000000000000001"],
"00000000000000000000000000000000000000000000000000000000000004f4"
);

Expand Down
9 changes: 7 additions & 2 deletions test/debug/debugStorage.js
Expand Up @@ -82,15 +82,20 @@ describe("Debug Storage", function() {
"0000000000000000000000000000000000000000000000000000000000000006"
);

assert.strictEqual(
arrayOfStorageKeyValues[1]["0000000000000000000000000000000000000000000000000000000000000001"],
"0000000000000000000000000000000000000000000000000000000000000006"
);

// ensure the call to setValue with 2 was successfully stored for value
assert.strictEqual(
arrayOfStorageKeyValues[2]["0000000000000000000000000000000000000000000000000000000000000000"],
arrayOfStorageKeyValues[3]["0000000000000000000000000000000000000000000000000000000000000000"],
"0000000000000000000000000000000000000000000000000000000000000002"
);

// ensure the call to setValue with 2 was successfully stored for otherValue, making it 8
assert.strictEqual(
arrayOfStorageKeyValues[3]["0000000000000000000000000000000000000000000000000000000000000001"],
arrayOfStorageKeyValues[4]["0000000000000000000000000000000000000000000000000000000000000001"],
"0000000000000000000000000000000000000000000000000000000000000008"
);
}
Expand Down
2 changes: 1 addition & 1 deletion test/forking.js
Expand Up @@ -340,7 +340,7 @@ describe("Forking", function() {
const expectedNumber = await mainWeb3.eth.getBlockNumber();

const number = await oracle.methods.currentBlock().call();
assert.strictEqual(to.number(number), expectedNumber + 1);
assert.strictEqual(to.number(number), expectedNumber);

await oracle.methods.setCurrentBlock().send({ from: mainAccounts[0], gas: 3141592 });
const val = await oracle.methods.lastBlock().call({ from: mainAccounts[0] });
Expand Down

0 comments on commit 5b2b5aa

Please sign in to comment.