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

fix(forking): invalidate _deleted in ForkedStorageTrie if a subsequent put happens #612

Merged
merged 2 commits into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions lib/forking/forked_storage_trie.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,23 @@ ForkedStorageBaseTrie.prototype.keyExists = function(key, callback) {
});
};

const originalPut = ForkedStorageBaseTrie.prototype.put;
ForkedStorageBaseTrie.prototype.put = function(key, value, callback) {
let rpcKey = to.rpcDataHexString(key);
if (this.address) {
rpcKey = `${to.rpcDataHexString(this.address)};${rpcKey}`;
}
this._deleted.get(rpcKey, (_, result) => {
if (result === 1) {
this._deleted.put(rpcKey, 0, () => {
originalPut.call(this, key, value, callback);
});
} else {
originalPut.call(this, key, value, callback);
}
});
};

ForkedStorageBaseTrie.prototype.keyIsDeleted = function(key, callback) {
let rpcKey = to.rpcDataHexString(key);
if (this.address) {
Expand Down
18 changes: 18 additions & 0 deletions test/contracts/forking/StorageDelete.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pragma solidity ^0.6.0;

contract StorageDelete {
bool entered;

constructor() public {
entered = true;
}

function test() public nonReentrant {}

modifier nonReentrant() {
require(entered, "re-entered");
entered = false;
_;
entered = true;
}
}
54 changes: 54 additions & 0 deletions test/forking/delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const assert = require("assert");
const bootstrap = require("../helpers/contract/bootstrap");
const intializeTestProvider = require("../helpers/web3/initializeTestProvider");

/**
* NOTE: Naming in these tests is a bit confusing. Here, the "main chain"
* is the main chain the tests interact with; and the "forked chain" is the
* chain that _was forked_. This is in contrast to general naming, where the
* main chain represents the main chain to be forked (like the Ethereum live
* network) and the fork chaing being "the fork".
*/

describe("Forking Deletion", () => {
let forkedContext;
let mainContext;
const logger = {
log: function(msg) {}
};

before("Set up forked provider with web3 instance and deploy a contract", async function() {
this.timeout(5000);

const contractRef = {
contractFiles: ["StorageDelete"],
contractSubdirectory: "forking"
};

const ganacheProviderOptions = {
logger,
seed: "main provider"
};

forkedContext = await bootstrap(contractRef, ganacheProviderOptions);
});

before("Set up main provider and web3 instance", async function() {
const { provider: forkedProvider } = forkedContext;
mainContext = await intializeTestProvider({
fork: forkedProvider,
logger,
seed: "forked provider"
});
});

it("successfully manages storage slot deletion", async() => {
const { instance: forkedInstance, abi } = forkedContext;
const { web3: mainWeb3 } = mainContext;

const instance = new mainWeb3.eth.Contract(abi, forkedInstance._address);

assert.ok(await instance.methods.test().call());
assert.ok(await instance.methods.test().call());
});
});