Browse Source

State diff engine.

cl-refactor
Gav Wood 11 years ago
parent
commit
823fd6be5f
  1. 177
      libethereum/State.cpp
  2. 54
      libethereum/State.h
  3. 2
      libethereum/VM.h

177
libethereum/State.cpp

@ -118,6 +118,122 @@ State& State::operator=(State const& _s)
return *this;
}
struct CachedAddressState
{
CachedAddressState(std::string const& _rlp, AddressState const* _s, Overlay const* _o): rS(_rlp), r(rS), s(_s), o(_o) {}
bool exists() const
{
return (r && (!s || s->isAlive())) || (s && s->isAlive());
}
u256 balance() const
{
return r ? s ? s->balance() : r[0].toInt<u256>() : 0;
}
u256 nonce() const
{
return r ? s ? s->nonce() : r[1].toInt<u256>() : 0;
}
bytes code() const
{
if (s && s->codeCacheValid())
return s->code();
h256 h = r ? s ? s->codeHash() : r[3].toHash<h256>() : EmptySHA3;
return h == EmptySHA3 ? bytes() : asBytes(o->lookup(h));
}
std::map<u256, u256> storage() const
{
std::map<u256, u256> ret;
if (r)
{
TrieDB<h256, Overlay> memdb(const_cast<Overlay*>(o), r[2].toHash<h256>()); // promise we won't alter the overlay! :)
for (auto const& j: memdb)
ret[j.first] = RLP(j.second).toInt<u256>();
}
if (s)
for (auto const& j: s->storage())
if ((!ret.count(j.first) && j.second) || (ret.count(j.first) && ret.at(j.first) != j.second))
ret[j.first] = j.second;
return ret;
}
AccountDiff diff(CachedAddressState const& _c)
{
AccountDiff ret;
ret.exist = Diff<bool>(exists(), _c.exists());
ret.balance = Diff<u256>(balance(), _c.balance());
ret.nonce = Diff<u256>(nonce(), _c.nonce());
ret.code = Diff<bytes>(code(), _c.code());
auto st = storage();
auto cst = _c.storage();
auto it = st.begin();
auto cit = cst.begin();
while (it != st.end() || cit != cst.end())
{
if (it != st.end() && cit != cst.end() && it->first == cit->first && (it->second || cit->second) && (it->second != cit->second))
ret.storage[it->first] = Diff<u256>(it->second, cit->second);
else if (it != st.end() && (cit == cst.end() || it->first < cit->first) && it->second)
ret.storage[it->first] = Diff<u256>(it->second, 0);
else if (cit != cst.end() && (it == st.end() || it->first > cit->first) && cit->second)
ret.storage[cit->first] = Diff<u256>(0, cit->second);
if (it == st.end())
++cit;
else if (cit == cst.end())
++it;
else if (it->first < cit->first)
++it;
else if (it->first > cit->first)
++cit;
else
++it, ++cit;
}
return ret;
}
std::string rS;
RLP r;
AddressState const* s;
Overlay const* o;
};
StateDiff State::diff(State const& _c) const
{
StateDiff ret;
std::set<Address> ads;
std::set<Address> trieAds;
std::set<Address> trieAdsD;
auto trie = TrieDB<Address, Overlay>(const_cast<Overlay*>(&m_db), rootHash());
auto trieD = TrieDB<Address, Overlay>(const_cast<Overlay*>(&_c.m_db), _c.rootHash());
for (auto i: trie)
ads.insert(i.first), trieAds.insert(i.first);
for (auto i: trieD)
ads.insert(i.first), trieAdsD.insert(i.first);
for (auto i: m_cache)
ads.insert(i.first);
for (auto i: _c.m_cache)
ads.insert(i.first);
for (auto i: ads)
{
auto it = m_cache.find(i);
auto itD = _c.m_cache.find(i);
CachedAddressState source(trieAds.count(i) ? trie.at(i) : "", it != m_cache.end() ? &it->second : nullptr, &m_db);
CachedAddressState dest(trieAdsD.count(i) ? trieD.at(i) : "", itD != _c.m_cache.end() ? &itD->second : nullptr, &_c.m_db);
AccountDiff acd = source.diff(dest);
if (acd.changed())
ret.accounts[i] = acd;
}
return ret;
}
void State::ensureCached(Address _a, bool _requireCode, bool _forceCreate) const
{
ensureCached(m_cache, _a, _requireCode, _forceCreate);
@ -681,12 +797,14 @@ bytes const& State::code(Address _contract) const
u256 State::execute(bytesConstRef _rlp)
{
State old(*this);
auto h = rootHash();
Executive e(*this);
e.setup(_rlp);
cnote << "Executing " << e.t();
// cnote << m_state.root() << "\n" << m_state;
cnote << *this;
cnote << "Executing " << e.t() << "on" << h;
u256 startGasUSed = gasUsed();
if (startGasUSed + e.t().gas > m_currentBlock.gasLimit)
@ -695,15 +813,10 @@ u256 State::execute(bytesConstRef _rlp)
e.go();
e.finalize();
cnote << "Executed.";
// cnote << m_state.root() << "\n" << m_state;
cnote << *this;
commit();
cnote << "Committed.";
// cnote << m_state.root() << "\n" << m_state;
cnote << *this;
cnote << "Executed; now" << rootHash();
cnote << old.diff(*this);
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(TransactionReceipt(e.t(), m_state.root(), startGasUSed + e.gasUsed()));
@ -913,3 +1026,47 @@ std::ostream& eth::operator<<(std::ostream& _out, State const& _s)
}
return _out;
}
char const* AccountDiff::lead() const
{
bool bn = (balance || nonce);
bool sc = (!storage.empty() || code);
return exist ? exist.from() ? "XXX" : "+++" : (bn && sc) ? "***" : bn ? " * " : sc ? "* *" : " ";
}
std::ostream& eth::operator<<(std::ostream& _out, AccountDiff const& _s)
{
if (!_s.exist.to())
return _out;
if (_s.balance)
{
_out << std::dec << _s.balance.to() << " ";
if (_s.balance.from())
_out << "(" << std::showpos << (((bigint)_s.balance.to()) - ((bigint)_s.balance.from())) << std::noshowpos << ") ";
}
if (_s.nonce)
{
_out << std::dec << "#" << _s.nonce.to() << " ";
if (_s.nonce.from())
_out << "(" << std::showpos << (((bigint)_s.nonce.to()) - ((bigint)_s.nonce.from())) << std::noshowpos << ") ";
}
if (_s.code)
_out << "$" << std::hex << _s.code.to() << " (" << _s.code.from() << ") ";
for (pair<u256, Diff<u256>> const& i: _s.storage)
if (!i.second.from())
_out << endl << " + " << (h256)i.first << ": " << std::hex << i.second.to();
else if (!i.second.to())
_out << endl << "XXX " << (h256)i.first << " (" << std::hex << i.second.from() << ")";
else
_out << endl << " * " << (h256)i.first << ": " << std::hex << i.second.to() << " (" << i.second.from() << ")";
return _out;
}
std::ostream& eth::operator<<(std::ostream& _out, StateDiff const& _s)
{
_out << _s.accounts.size() << " accounts changed:" << endl;
for (auto const& i: _s.accounts)
_out << i.second.lead() << " " << i.first << ": " << i.second << endl;
return _out;
}

54
libethereum/State.h

@ -63,6 +63,41 @@ struct TransactionReceipt
u256 gasUsed;
};
enum class ExistDiff { Same, New, Dead };
template <class T>
class Diff
{
public:
Diff() {}
Diff(T _from, T _to): m_from(_from), m_to(_to) {}
T const& from() const { return m_from; }
T const& to() const { return m_to; }
explicit operator bool() const { return m_from != m_to; }
private:
T m_from;
T m_to;
};
struct AccountDiff
{
inline bool changed() const { return storage.size() || code || nonce || balance || exist; }
char const* lead() const;
Diff<bool> exist;
Diff<u256> balance;
Diff<u256> nonce;
std::map<u256, Diff<u256>> storage;
Diff<bytes> code;
};
struct StateDiff
{
std::map<Address, AccountDiff> accounts;
};
/**
* @brief Model of the current state of the ledger.
* Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block).
@ -96,18 +131,12 @@ public:
/// @returns the set containing all addresses currently in use in Ethereum.
std::map<Address, u256> addresses() const;
/// Cancels transactions and rolls back the state to the end of the previous block.
/// @warning This will only work for on any transactions after you called the last commitToMine().
/// It's one or the other.
void rollback() { m_cache.clear(); }
/// Prepares the current state for mining.
/// Commits all transactions into the trie, compiles uncles and transactions list, applies all
/// rewards and populates the current block header with the appropriate hashes.
/// The only thing left to do after this is to actually mine().
///
/// This may be called multiple times and without issue, however, until the current state is cleared,
/// calls after the first are ignored.
/// This may be called multiple times and without issue.
void commitToMine(BlockChain const& _bc);
/// Attempt to find valid nonce for block that this state represents.
@ -129,7 +158,10 @@ public:
// TODO: Cleaner interface.
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
bool sync(TransactionQueue& _tq, bool* _changed = nullptr);
/// @returns true if we uncommitted from mining during the operation.
/// @a o_changed boolean pointer, the value of which will be set to true if the state changed and the pointer
/// is non-null
bool sync(TransactionQueue& _tq, bool* o_changed = nullptr);
/// Like sync but only operate on _tq, killing the invalid/old ones.
bool cull(TransactionQueue& _tq) const;
@ -199,6 +231,9 @@ public:
/// Get the fee associated for a normal transaction.
u256 callGas(uint _dataCount, u256 _gas = 0) const { return c_txDataGas * _dataCount + c_callGas + _gas; }
/// @return the difference between this state (origin) and @a _c (destination).
StateDiff diff(State const& _c) const;
private:
/// Undo the changes to the state for committing to mine.
void uncommitToMine();
@ -245,6 +280,7 @@ private:
/// Unfinalise the block, unapplying the earned rewards.
void unapplyRewards(Addresses const& _uncleAddresses);
/// @returns gas used by transactions thus far executed.
u256 gasUsed() const { return m_transactions.size() ? m_transactions.back().gasUsed : 0; }
Overlay m_db; ///< Our overlay for the state tree.
@ -274,6 +310,8 @@ private:
};
std::ostream& operator<<(std::ostream& _out, State const& _s);
std::ostream& operator<<(std::ostream& _out, StateDiff const& _s);
std::ostream& operator<<(std::ostream& _out, AccountDiff const& _s);
template <class DB>
void commit(std::map<Address, AddressState> const& _cache, DB& _db, TrieDB<Address, DB>& _state)

2
libethereum/VM.h

@ -554,6 +554,8 @@ template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps)
_ext.subBalance(value);
m_stack.push_back(_ext.call(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize)));
}
else
m_stack.push_back(0);
m_gas += gas;
break;

Loading…
Cancel
Save