From dcddeaccb0a504e176309ca74a52a878e00d95b8 Mon Sep 17 00:00:00 2001 From: David Murdoch Date: Wed, 3 Jul 2019 14:33:20 -0400 Subject: [PATCH 1/3] when creating a block via creatBlock, allow for copying the parent block --- lib/blockchain_double.js | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/blockchain_double.js b/lib/blockchain_double.js index 7e6d502690..11718e8276 100644 --- a/lib/blockchain_double.js +++ b/lib/blockchain_double.js @@ -354,12 +354,17 @@ 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 = parent; + parent = null; + emulateParent = false; } var block = new Block(); @@ -377,18 +382,26 @@ 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; - // Set the timestamp before processing txs - block.header.timestamp = to.hex(self.currentTime()); + 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); - if (parent != null) { - block.header.parentHash = to.hex(parent.hash()); + // Set the timestamp before processing txs + block.header.timestamp = to.hex(self.currentTime()); + + if (parent != null) { + block.header.parentHash = to.hex(parent.hash()); + } } callback(null, block); @@ -490,7 +503,7 @@ BlockchainDouble.prototype.processCall = function(tx, blockNumber, callback) { } // create a fake block with this fake transaction - self.createBlock(parentBlock, function(err, newBlock) { + self.createBlock(parentBlock, true, function(err, newBlock) { if (err) { return callback(err); } @@ -542,7 +555,7 @@ BlockchainDouble.prototype.estimateGas = function(tx, blockNumber, callback) { } // create a fake block with this fake transaction - self.createBlock(parentBlock, function(err, newBlock) { + self.createBlock(parentBlock, false, function(err, newBlock) { if (err) { return callback(err); } @@ -902,7 +915,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); } From 35f718f73bc96df9468d138ec3b1284b77d74e9e Mon Sep 17 00:00:00 2001 From: David Murdoch Date: Wed, 3 Jul 2019 14:36:00 -0400 Subject: [PATCH 2/3] make sure forking will never exceed our inital fork block number --- lib/utils/forkedblockchain.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/utils/forkedblockchain.js b/lib/utils/forkedblockchain.js index d524162efa..8277592527 100644 --- a/lib/utils/forkedblockchain.js +++ b/lib/utils/forkedblockchain.js @@ -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) { From a2c342714629d3b8494e631928df1d4fa76caa76 Mon Sep 17 00:00:00 2001 From: David Murdoch Date: Wed, 3 Jul 2019 14:37:34 -0400 Subject: [PATCH 3/3] Add and update tests --- test/call.js | 7 +++++ test/contracts/debug/DebugContract.sol | 2 ++ test/contracts/gas/EstimateGas.sol | 4 +++ test/debug/debug.js | 36 ++++++++++++++++++++++---- test/debug/debugStorage.js | 9 +++++-- test/forking.js | 2 +- 6 files changed, 52 insertions(+), 8 deletions(-) diff --git a/test/call.js b/test/call.js index 3157e3781e..4a39a11b08 100644 --- a/test/call.js +++ b/test/call.js @@ -27,4 +27,11 @@ describe("eth_call", function() { assert.strictEqual(status, true); }); + + it("should use the current block number via `eth_call`", async() => { + 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); + }); }); diff --git a/test/contracts/debug/DebugContract.sol b/test/contracts/debug/DebugContract.sol index 88317dec28..48d682b6b6 100644 --- a/test/contracts/debug/DebugContract.sol +++ b/test/contracts/debug/DebugContract.sol @@ -4,10 +4,12 @@ pragma solidity ^0.4.2; 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 { diff --git a/test/contracts/gas/EstimateGas.sol b/test/contracts/gas/EstimateGas.sol index 73b1bdc98a..161eeb51b8 100644 --- a/test/contracts/gas/EstimateGas.sol +++ b/test/contracts/gas/EstimateGas.sol @@ -99,4 +99,8 @@ contract EstimateGas { return true; } + + function currentBlock() returns (uint) { + return block.number; + } } diff --git a/test/debug/debug.js b/test/debug/debug.js index ec646757ef..81b1f58bb0 100644 --- a/test/debug/debug.js +++ b/test/debug/debug.js @@ -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 }; @@ -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 @@ -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); @@ -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, []); @@ -138,7 +152,7 @@ function test(forked) { assert.strictEqual(lastop.op, "STOP"); assert.strictEqual(lastop.gasCost, 1); - assert.strictEqual(lastop.pc, 209); + assert.strictEqual(lastop.pc, 220); assert.strictEqual( lastop.storage["0000000000000000000000000000000000000000000000000000000000000000"], "000000000000000000000000000000000000000000000000000000000000001a" @@ -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); }); }); @@ -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" ); diff --git a/test/debug/debugStorage.js b/test/debug/debugStorage.js index 30e64b81be..f0c0e3868e 100644 --- a/test/debug/debugStorage.js +++ b/test/debug/debugStorage.js @@ -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" ); } diff --git a/test/forking.js b/test/forking.js index a045edbabb..bb757053bc 100644 --- a/test/forking.js +++ b/test/forking.js @@ -308,7 +308,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] });