diff --git a/lib/stateManager.js b/lib/stateManager.js index 559f0c5535..d9dfba26ec 100644 --- a/lib/stateManager.js +++ b/lib/stateManager.js @@ -396,11 +396,17 @@ proto.setStateRoot = function (stateRoot, cb) { self._cache.flush(function (err) { if (err) { return cb(err) } + if (stateRoot === self._trie.EMPTY_TRIE_ROOT) { + self._trie.root = stateRoot + self._cache.clear() + return cb() + } self._trie.checkRoot(stateRoot, function (err, hasRoot) { if (err || !hasRoot) { cb(err || new Error('State trie does not contain state root')) } else { self._trie.root = stateRoot + self._cache.clear() cb() } }) diff --git a/tests/api/runBlock.js b/tests/api/runBlock.js index dc7ab112fb..ffdd30802d 100644 --- a/tests/api/runBlock.js +++ b/tests/api/runBlock.js @@ -57,14 +57,13 @@ tape('runBlock', async (t) => { }) t.test('should fail when runTx fails', async (st) => { - const genesis = createGenesis() const block = new Block(util.rlp.decode(suite.data.blocks[0].rlp)) await suite.p.generateCanonicalGenesis() // The mocked VM uses a mocked runTx // which always returns an error. - await suite.p.runBlock({ block, root: genesis.header.stateRoot, skipBlockValidation: true }) + await suite.p.runBlock({ block, skipBlockValidation: true }) .then(() => t.fail('should have returned error')) .catch((e) => t.equal(e.message, 'test')) @@ -106,13 +105,12 @@ tape('should fail when block validation fails', async (t) => { tape('should fail when tx gas limit higher than block gas limit', async (t) => { const suite = setup() - const genesis = createGenesis() const block = new Block(util.rlp.decode(suite.data.blocks[0].rlp)) block.transactions[0].gasLimit = Buffer.from('3fefba', 'hex') await suite.p.generateCanonicalGenesis() - await suite.p.runBlock({ block, root: genesis.header.stateRoot, skipBlockValidation: true }) + await suite.p.runBlock({ block, skipBlockValidation: true }) .then(() => t.fail('should have returned error')) .catch((e) => t.ok(e.message.includes('higher gas limit'))) @@ -137,7 +135,7 @@ tape('should fail when runCall fails', async (t) => { // runTx is a full implementation that works. suite.vm.runTx = runTx - await suite.p.runBlock({ block, root: suite.vm.stateManager._trie.root, skipBlockValidation: true }) + await suite.p.runBlock({ block, skipBlockValidation: true }) .then(() => t.fail('should have returned error')) .catch((e) => t.equal(e.message, 'test')) diff --git a/tests/api/stateManager.js b/tests/api/stateManager.js index 172790d862..9ab7425f67 100644 --- a/tests/api/stateManager.js +++ b/tests/api/stateManager.js @@ -18,6 +18,36 @@ tape('StateManager', (t) => { st.end() }) + t.test('should clear the cache when the state root is set', async (st) => { + const stateManager = new StateManager() + const addressBuffer = Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex') + const account = createAccount() + + const getStateRoot = promisify((...args) => stateManager.getStateRoot(...args)) + const checkpoint = promisify((...args) => stateManager.checkpoint(...args)) + const putAccount = promisify((...args) => stateManager.putAccount(...args)) + const getAccount = promisify((...args) => stateManager.getAccount(...args)) + const commit = promisify((...args) => stateManager.commit(...args)) + const setStateRoot = promisify((...args) => stateManager.setStateRoot(...args)) + + const initialStateRoot = await getStateRoot() + await checkpoint() + await putAccount(addressBuffer, account) + + const account0 = await getAccount(addressBuffer) + st.equal(account0.balance.toString('hex'), account.balance.toString('hex'), 'account value is set in the cache') + + await commit() + const account1 = await getAccount(addressBuffer) + st.equal(account1.balance.toString('hex'), account.balance.toString('hex'), 'account value is set in the state trie') + + await setStateRoot(initialStateRoot) + const account2 = await getAccount(addressBuffer) + st.equal(account2.balance.toString('hex'), '', 'account value is set to 0 in original state root') + + st.end() + }) + t.test('should put and get account, and add to the underlying cache if the account is not found', async (st) => { const stateManager = new StateManager() const account = createAccount()