diff --git a/libethashseal/Ethash.cpp b/libethashseal/Ethash.cpp index 55d9c039394..a59cc77d321 100644 --- a/libethashseal/Ethash.cpp +++ b/libethashseal/Ethash.cpp @@ -86,7 +86,7 @@ EVMSchedule const& Ethash::evmSchedule(EnvInfo const& _envInfo) const { if (_envInfo.number() >= chainParams().u256Param("EIP158ForkBlock")) return EIP158Schedule; - if (_envInfo.number() >= chainParams().u256Param("EIP150ForkBlock")) + else if (_envInfo.number() >= chainParams().u256Param("EIP150ForkBlock")) return EIP150Schedule; else if (_envInfo.number() >= chainParams().u256Param("homsteadForkBlock")) return HomesteadSchedule; diff --git a/libethereum/Account.h b/libethereum/Account.h index 7bd83428e64..6a58b8990f0 100644 --- a/libethereum/Account.h +++ b/libethereum/Account.h @@ -111,6 +111,9 @@ class Account /// @returns true if the account is unchanged from creation. bool isDirty() const { return !m_isUnchanged; } + /// @returns true if the nonce, balance and code is zero / empty. Code is considered empty + /// during creation phase. + bool isEmpty() const { return nonce() == 0 && balance() == 0 && ((isFreshCode() && code().empty()) || codeHash() == EmptySHA3); } /// @returns the balance of this account. Can be altered in place. u256& balance() { return m_balance; } diff --git a/libethereum/Block.cpp b/libethereum/Block.cpp index 68be97771eb..8ee69c7dabf 100644 --- a/libethereum/Block.cpp +++ b/libethereum/Block.cpp @@ -625,8 +625,9 @@ u256 Block::enact(VerifiedBlockRef const& _block, BlockChain const& _bc) applyRewards(rewarded, _bc.chainParams().blockReward); // Commit all cached state changes to the state trie. + bool killEmptyAccounts = m_currentBlock.number() >= _bc.chainParams().u256Param("EIP158ForkBlock"); DEV_TIMED_ABOVE("commit", 500) - m_state.commit(); + m_state.commit(killEmptyAccounts); // Hash the state trie and check against the state_root hash in m_currentBlock. if (m_currentBlock.stateRoot() != m_previousBlock.stateRoot() && m_currentBlock.stateRoot() != rootHash()) @@ -688,7 +689,7 @@ void Block::performIrregularModifications() Addresses allDAOs = childDaos(); for (Address const& dao: allDAOs) m_state.transferBalance(dao, recipient, m_state.balance(dao)); - m_state.commit(); + m_state.commit(false); } } @@ -768,7 +769,9 @@ void Block::commitToSeal(BlockChain const& _bc, bytes const& _extraData) applyRewards(uncleBlockHeaders, _bc.chainParams().blockReward); // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. - m_state.commit(); + bool killEmptyAccounts = m_currentBlock.number() >= _bc.chainParams().u256Param("EIP158ForkBlock"); + DEV_TIMED_ABOVE("commit", 500) + m_state.commit(killEmptyAccounts); clog(StateDetail) << "Post-reward stateRoot:" << m_state.rootHash(); clog(StateDetail) << m_state; diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index ff7a8c0d499..a0d6d25d54b 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -79,7 +79,7 @@ class ExtVM: public ExtVMFace virtual bool exists(Address _a) override final { if (evmSchedule().emptinessIsNonexistence()) - return m_s.accountNonemptyOrExisting(_a); + return m_s.accountNonemptyAndExisting(_a); else return m_s.addressInUse(_a); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 086c0b22234..e3d373738eb 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -66,6 +66,7 @@ State::State(State const& _s): m_db(_s.m_db), m_state(&m_db, _s.m_state.root(), Verification::Skip), m_cache(_s.m_cache), + m_unchangedCacheEntries(_s.m_unchangedCacheEntries), m_touched(_s.m_touched), m_accountStartNonce(_s.m_accountStartNonce) { @@ -116,7 +117,7 @@ OverlayDB State::openDB(std::string const& _basePath, h256 const& _genesisHash, void State::populateFrom(AccountMap const& _map) { eth::commit(_map, m_state); - commit(); + commit(false); } u256 const& State::requireAccountStartNonce() const @@ -134,6 +135,13 @@ void State::noteAccountStartNonce(u256 const& _actual) BOOST_THROW_EXCEPTION(IncorrectAccountStartNonceInState()); } +void State::killEmptyAccounts() +{ + for (auto& i: m_cache) + if (i.second.isDirty() && i.second.isEmpty()) + i.second.kill(); +} + void State::paranoia(std::string const& _when, bool _enforceRefs) const { #if ETH_PARANOIA && !ETH_FATDB @@ -158,6 +166,7 @@ State& State::operator=(State const& _s) m_db = _s.m_db; m_state.open(&m_db, _s.m_state.root(), Verification::Skip); m_cache = _s.m_cache; + m_unchangedCacheEntries = _s.m_unchangedCacheEntries; m_touched = _s.m_touched; m_accountStartNonce = _s.m_accountStartNonce; paranoia("after state cloning (assignment op)", true); @@ -266,8 +275,10 @@ void State::clearCacheIfTooLarge() const } } -void State::commit() +void State::commit(bool _killEmptyAccounts) { + if (_killEmptyAccounts) + killEmptyAccounts(); m_touched += dev::eth::commit(m_cache, m_state); m_cache.clear(); } @@ -301,6 +312,14 @@ bool State::addressInUse(Address const& _id) const return !!account(_id); } +bool State::accountNonemptyAndExisting(Address const& _address) const +{ + if (Account const* a = account(_address)) + return !a->isEmpty(); + else + return false; +} + bool State::addressHasCode(Address const& _id) const { if (auto a = account(_id)) @@ -542,7 +561,8 @@ std::pair State::execute(EnvInfo const& _en m_cache.clear(); else { - commit(); + bool killEmptyAccounts = _envInfo.number() >= _sealEngine->chainParams().u256Param("EIP158ForkBlock"); + commit(killEmptyAccounts); #if ETH_PARANOIA && !ETH_FATDB ctrace << "Executed; now" << rootHash(); diff --git a/libethereum/State.h b/libethereum/State.h index e6599760979..89110a05591 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -159,9 +159,9 @@ class State /// Check if the address is in use. bool addressInUse(Address const& _address) const; - /// Check if the account does not exist in the state or is empty (nonce == 0, balance == 0, code empty). + /// Check if the account exists in the state and is non empty (nonce > 0 || balance > 0 || code nonempty). /// These two notions are equivalent after EIP158. - bool accountNonemptyOrExisting(Address const& _address) const; + bool accountNonemptyAndExisting(Address const& _address) const; /// Check if the address contains executable code. bool addressHasCode(Address const& _address) const; @@ -243,7 +243,8 @@ class State StateDiff diff(State const& _c, bool _quick = false) const; /// Commit all changes waiting in the address cache to the DB. - void commit(); + /// @param _killEmptyAccounts if true, will remove all "touched" empty accounts from the state. + void commit(bool _killEmptyAccounts); /// Resets any uncommitted changes to the cache. void setRoot(h256 const& _root); @@ -254,6 +255,9 @@ class State void noteAccountStartNonce(u256 const& _actual); private: + /// Turns all "touched" empty accounts into non-alive accounts. + void killEmptyAccounts(); + /// @returns the account at the given address or a null pointer if it does not exist. /// The pointer is valid until the next access to the state or account. Account const* account(Address const& _a, bool _requireCode = false) const; diff --git a/libevm/VM.cpp b/libevm/VM.cpp index aec3479d918..d0544492230 100755 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -209,10 +209,12 @@ void VM::interpretCases() m_runGas = toUint64(m_schedule->suicideGas); Address dest = asAddress(*m_sp); - // After EIP150 hard fork charge additional cost of sending - // ethers to non-existing account. - if (m_schedule->suicideChargesNewAccountGas() && !m_ext->exists(dest)) - m_runGas += m_schedule->callNewAccountGas; + // After EIP158 zero-value suicides do not have to pay account creation gas. + if (m_ext->balance(m_ext->myAddress) > 0 || m_schedule->zeroValueTransferChargesNewAccountGas()) + // After EIP150 hard fork charge additional cost of sending + // ethers to non-existing account. + if (m_schedule->suicideChargesNewAccountGas() && !m_ext->exists(dest)) + m_runGas += m_schedule->callNewAccountGas; onOperation(); updateIOGas(); diff --git a/libevm/VMCalls.cpp b/libevm/VMCalls.cpp index 3ef61eb4a1d..9d712b8a270 100755 --- a/libevm/VMCalls.cpp +++ b/libevm/VMCalls.cpp @@ -138,7 +138,8 @@ bool VM::caseCallSetup(CallParameters *callParams) m_runGas = toUint64(m_schedule->callGas); if (m_op == Instruction::CALL && !m_ext->exists(asAddress(*(m_sp - 1)))) - m_runGas += toUint64(m_schedule->callNewAccountGas); + if (*(m_sp - 2) > 0 || m_schedule->zeroValueTransferChargesNewAccountGas()) + m_runGas += toUint64(m_schedule->callNewAccountGas); if (m_op != Instruction::DELEGATECALL && *(m_sp - 2) > 0) m_runGas += toUint64(m_schedule->callValueTransferGas); diff --git a/libevmcore/EVMSchedule.h b/libevmcore/EVMSchedule.h index fcff2a8904c..9f753b02cbb 100644 --- a/libevmcore/EVMSchedule.h +++ b/libevmcore/EVMSchedule.h @@ -74,6 +74,7 @@ struct EVMSchedule bool staticCallDepthLimit() const { return !eip150Mode; } bool suicideChargesNewAccountGas() const { return eip150Mode; } bool emptinessIsNonexistence() const { return eip158Mode; } + bool zeroValueTransferChargesNewAccountGas() const { return !eip158Mode; } }; static const EVMSchedule DefaultSchedule = EVMSchedule(); diff --git a/test/BlockChainHelper.cpp b/test/BlockChainHelper.cpp index 1a620cbb533..eb544f7488e 100644 --- a/test/BlockChainHelper.cpp +++ b/test/BlockChainHelper.cpp @@ -55,7 +55,7 @@ TestBlock::TestBlock(mObject const& _blockObj, mObject const& _stateObj): m_state = std::unique_ptr(new State(0, OverlayDB(State::openDB(m_tempDirState.get()->path(), h256{}, WithExisting::Kill)), BaseState::Empty)); ImportTest::importState(_stateObj, *m_state.get()); - m_state.get()->commit(); + m_state.get()->commit(false); json_spirit::mObject state = _stateObj; dev::test::replaceLLLinState(state); diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 975d76a3efa..5bef048c339 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -166,9 +166,10 @@ bytes ImportTest::executeTest(eth::Network _sealEngineNetwork) { ExecutionResult res; eth::State tmpState = m_statePre; + unique_ptr se; try { - unique_ptr se(ChainParams(genesisInfo(_sealEngineNetwork)).createSealEngine()); + se.reset(ChainParams(genesisInfo(_sealEngineNetwork)).createSealEngine()); std::pair execOut = m_statePre.execute(m_envInfo, se.get(), m_transaction); res = execOut.first; m_logs = execOut.second.log(); @@ -182,7 +183,8 @@ bytes ImportTest::executeTest(eth::Network _sealEngineNetwork) cnote << "state execution exception: " << _e.what(); } - m_statePre.commit(); + bool killEmptyAccounts = m_envInfo.number() >= se->chainParams().u256Param("EIP158ForkBlock"); + m_statePre.commit(killEmptyAccounts); m_statePost = m_statePre; m_statePre = tmpState; diff --git a/test/TestUtils.cpp b/test/TestUtils.cpp index 415d147db42..9f51f3d8bb1 100644 --- a/test/TestUtils.cpp +++ b/test/TestUtils.cpp @@ -102,7 +102,7 @@ void ClientBaseFixture::enumerateClients(std::function void { cerr << "void ClientBaseFixture::enumerateClients. FixedClient now accepts block not sate!" << endl; - _state.commit(); //unused variable. remove this line + _state.commit(false); //unused variable. remove this line eth::Block b(Block::Null); b.noteChain(_bc); FixedClient client(_bc, b);