Browse Source

Refactored a lot of State.

Killed playback() API.
State diffing at arbitrary places in chain.
cl-refactor
Gav Wood 11 years ago
parent
commit
e11b572df1
  1. 26
      alethzero/MainWin.cpp
  2. 2
      alethzero/MainWin.h
  3. 2
      libethereum/AddressState.h
  4. 14
      libethereum/BlockChain.cpp
  5. 1
      libethereum/BlockChain.h
  6. 142
      libethereum/State.cpp
  7. 53
      libethereum/State.h

26
alethzero/MainWin.cpp

@ -674,17 +674,12 @@ void Main::refresh(bool _override)
}
}
string Main::renderDiff(eth::State const& fs, eth::State const& ts) const
string Main::renderDiff(eth::StateDiff const& _d) const
{
stringstream s;
eth::StateDiff d = fs.diff(ts);
s << "Pre: " << fs.rootHash() << "<br/>";
s << "Post: <b>" << ts.rootHash() << "</b>";
auto indent = "<code style=\"white-space: pre\"> </code>";
for (auto const& i: d.accounts)
for (auto const& i: _d.accounts)
{
s << "<hr/>";
@ -773,9 +768,10 @@ void Main::on_transactionQueue_currentItemChanged()
}
s << "<hr/>";
eth::State fs = m_client->postState().fromPending(i);
eth::State ts = m_client->postState().fromPending(i + 1);
s << renderDiff(fs, ts);
// s << "Pre: " << fs.rootHash() << "<br/>";
// s << "Post: <b>" << ts.rootHash() << "</b>";
s << renderDiff(m_client->postState().pendingDiff(i));
}
ui->pendingInfo->setHtml(QString::fromStdString(s.str()));
@ -873,14 +869,8 @@ void Main::on_blocks_currentItemChanged()
s << eth::memDump(tx.data, 16, true);
}
s << "<br/><br/>";
/* BlockInfo parentBlockInfo();
eth::State s = m_client->state();
s.resetTo(bi.);
s <<*/
// eth::State s = m_client->blockChain().stateAt(h);
// StateDiff d = s.pendingDiff(txi);
// TODO: Make function: BlockChain::stateAt (grabs block's parent's stateRoot, playback()'s transactions), then use State::fromPending(). Maybe even make a State::pendingDiff().
s << renderDiff(eth::State(m_client->state().db(), m_client->blockChain(), h).pendingDiff(txi));
}

2
alethzero/MainWin.h

@ -131,7 +131,7 @@ private:
void debugFinished();
QString render(eth::Address _a) const;
eth::Address fromString(QString const& _a) const;
std::string renderDiff(eth::State const& fs, eth::State const& ts) const;
std::string renderDiff(eth::StateDiff const& _d) const;
eth::State const& state() const;

2
libethereum/AddressState.h

@ -46,7 +46,7 @@ public:
u256 const& nonce() const { return m_nonce; }
void incNonce() { m_nonce++; }
h256 oldRoot() const { return m_storageRoot; }
h256 baseRoot() const { return m_storageRoot; }
std::map<u256, u256> const& storage() const { return m_storageOverlay; }
void setStorage(u256 _p, u256 _v) { m_storageOverlay[_p] = _v; }

14
libethereum/BlockChain.cpp

@ -234,19 +234,11 @@ void BlockChain::import(bytes const& _block, OverlayDB const& _db)
try
#endif
{
// Check family:
BlockInfo biParent(block(bi.parentHash));
bi.verifyParent(biParent);
// Check transactions are valid and that they result in a state equivalent to our state_root.
State s(bi.coinbaseAddress, _db);
s.sync(*this, bi.parentHash);
// Get total difficulty increase and update state, checking it.
BlockInfo biGrandParent;
if (pd.number)
biGrandParent.populate(block(pd.parent));
auto tdIncrease = s.playback(&_block, bi, biParent, biGrandParent, true);
State s(bi.coinbaseAddress, _db);
auto tdIncrease = s.enactOn(&_block, bi, *this);
s.cleanup(true);
td = pd.totalDifficulty + tdIncrease;
#if ETH_PARANOIA

1
libethereum/BlockChain.h

@ -55,6 +55,7 @@ typedef std::map<h256, BlockDetails> BlockDetailsHash;
static const BlockDetails NullBlockDetails;
static const h256s NullH256s;
class State;
class OverlayDB;
class AlreadyHaveBlock: public std::exception {};

142
libethereum/State.cpp

@ -37,6 +37,8 @@ using namespace eth;
#define ctrace clog(StateTrace)
static const u256 c_blockReward = 1500 * finney;
OverlayDB State::openDB(std::string _path, bool _killExisting)
{
if (_path.empty())
@ -57,10 +59,9 @@ OverlayDB State::openDB(std::string _path, bool _killExisting)
State::State(Address _coinbaseAddress, OverlayDB const& _db):
m_db(_db),
m_state(&m_db),
m_ourAddress(_coinbaseAddress)
m_ourAddress(_coinbaseAddress),
m_blockReward(c_blockReward)
{
m_blockReward = 1500 * finney;
secp256k1_start();
// Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly.
@ -81,6 +82,31 @@ State::State(Address _coinbaseAddress, OverlayDB const& _db):
paranoia("end of normal construction.", true);
}
State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h):
m_db(_db),
m_state(&m_db),
m_blockReward(c_blockReward)
{
secp256k1_start();
// TODO THINK: is this necessary?
m_state.init();
auto b = _bc.block(_h);
BlockInfo bi;
BlockInfo bip;
if (_h)
bi.populate(b);
if (bi && bi.number)
bip.populate(_bc.block(bi.parentHash));
if (!_h || !bip)
return;
m_ourAddress = bi.coinbaseAddress;
sync(_bc, bi.parentHash, bip);
enact(b);
}
State::State(State const& _s):
m_db(_s.m_db),
m_state(&m_db, _s.m_state.root()),
@ -280,16 +306,19 @@ bool State::sync(BlockChain const& _bc)
return sync(_bc, _bc.currentHash());
}
bool State::sync(BlockChain const& _bc, h256 _block)
bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi)
{
bool ret = false;
// BLOCK
BlockInfo bi;
BlockInfo bi = _bi;
try
{
auto b = _bc.block(_block);
bi.populate(b);
// bi.verifyInternals(_bc.block(_block)); // Unneeded - we already verify on import into the blockchain.
if (!bi)
{
auto b = _bc.block(_block);
bi.populate(b);
// bi.verifyInternals(_bc.block(_block)); // Unneeded - we already verify on import into the blockchain.
}
}
catch (...)
{
@ -328,8 +357,23 @@ bool State::sync(BlockChain const& _bc, h256 _block)
resetCurrent();
// Iterate through in reverse, playing back each of the blocks.
for (auto it = chain.rbegin(); it != chain.rend(); ++it)
trustedPlayback(_bc.block(*it), true);
try
{
for (auto it = chain.rbegin(); it != chain.rend(); ++it)
{
auto b = _bc.block(*it);
m_currentBlock.populate(b);
m_currentBlock.verifyInternals(b);
enact(b, BlockInfo());
cleanup(true);
}
}
catch (...)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
exit(1);
}
resetCurrent();
ret = true;
@ -337,6 +381,21 @@ bool State::sync(BlockChain const& _bc, h256 _block)
return ret;
}
u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const& _bc)
{
// Check family:
BlockInfo biParent(_bc.block(_bi.parentHash));
_bi.verifyParent(biParent);
BlockInfo biGrandParent;
if (biParent.number)
biGrandParent.populate(_bc.block(biParent.parentHash));
sync(_bc, _bi.parentHash);
resetCurrent();
m_currentBlock = _bi;
m_previousBlock = biParent;
return enact(_block, biGrandParent);
}
map<Address, u256> State::addresses() const
{
map<Address, u256> ret;
@ -366,7 +425,7 @@ void State::resetCurrent()
// TODO: check.
m_lastTx = m_db;
m_state.setRoot(m_currentBlock.stateRoot);
m_state.setRoot(m_previousBlock.stateRoot);
paranoia("begin resetCurrent", true);
}
@ -448,33 +507,16 @@ bool State::sync(TransactionQueue& _tq, bool* _changed)
return ret;
}
u256 State::playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit)
u256 State::enact(bytesConstRef _block, BlockInfo const& _grandParent)
{
resetCurrent();
m_currentBlock = _bi;
m_previousBlock = _parent;
return playbackRaw(_block, _grandParent, _fullCommit);
}
// m_currentBlock is assumed to be prepopulated and reset.
u256 State::trustedPlayback(bytesConstRef _block, bool _fullCommit)
{
try
{
m_currentBlock.populate(_block);
m_currentBlock.verifyInternals(_block);
return playbackRaw(_block, BlockInfo(), _fullCommit);
}
catch (...)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
exit(1);
}
}
u256 State::playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit)
{
// m_currentBlock is assumed to be prepopulated.
#if !RELEASE
BlockInfo bi(_block);
assert(m_previousBlock.hash == bi.parentHash);
assert(m_currentBlock.parentHash == bi.parentHash);
assert(rootHash() == m_previousBlock.stateRoot);
#endif
if (m_currentBlock.parentHash != m_previousBlock.hash)
throw InvalidParentHash();
@ -503,15 +545,12 @@ u256 State::playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, boo
}
if (tr[2].toInt<u256>() != gasUsed())
throw InvalidTransactionGasUsed();
if (_fullCommit)
{
bytes k = rlp(i);
transactionManifest.insert(&k, tr.data());
}
bytes k = rlp(i);
transactionManifest.insert(&k, tr.data());
++i;
}
if (transactionManifest.root() != m_currentBlock.transactionsRoot)
if (m_currentBlock.transactionsRoot && transactionManifest.root() != m_currentBlock.transactionsRoot)
{
cwarn << "Bad transactions state root!";
throw InvalidTransactionStateRoot();
@ -521,7 +560,6 @@ u256 State::playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, boo
u256 tdIncrease = m_currentBlock.difficulty;
// Check uncles & apply their rewards to state.
// TODO: Check for uniqueness of uncles.
set<h256> nonces = { m_currentBlock.nonce };
Addresses rewarded;
for (auto const& i: RLP(_block)[2])
@ -545,7 +583,7 @@ u256 State::playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, boo
commit();
// Hash the state trie and check against the state_root hash in m_currentBlock.
if (m_currentBlock.stateRoot != rootHash())
if (m_currentBlock.stateRoot != m_previousBlock.stateRoot && m_currentBlock.stateRoot != rootHash())
{
cwarn << "Bad state root!";
cnote << "Given to be:" << m_currentBlock.stateRoot;
@ -558,6 +596,11 @@ u256 State::playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, boo
throw InvalidStateRoot();
}
return tdIncrease;
}
void State::cleanup(bool _fullCommit)
{
if (_fullCommit)
{
paranoia("immediately before database commit", true);
@ -574,8 +617,6 @@ u256 State::playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, boo
}
resetCurrent();
return tdIncrease;
}
void State::uncommitToMine()
@ -614,7 +655,8 @@ bool State::amIJustParanoid(BlockChain const& _bc)
cnote << "PARANOIA root:" << s.rootHash();
s.m_currentBlock.populate(&block.out(), false); // don't check nonce for this since we haven't mined it yet.
s.m_currentBlock.verifyInternals(&block.out());
s.playbackRaw(&block.out(), BlockInfo(), false);
s.enact(&block.out(), BlockInfo());
s.cleanup(false);
return true;
}
catch (Exception const& e)
@ -813,7 +855,7 @@ u256 State::storage(Address _id, u256 _memory) const
return mit->second;
// Not in the storage cache - go to the DB.
TrieDB<h256, OverlayDB> memdb(const_cast<OverlayDB*>(&m_db), it->second.oldRoot()); // promise we won't change the overlay! :)
TrieDB<h256, OverlayDB> memdb(const_cast<OverlayDB*>(&m_db), it->second.baseRoot()); // promise we won't change the overlay! :)
string payload = memdb.at(_memory);
u256 ret = payload.size() ? RLP(payload).toInt<u256>() : 0;
it->second.setStorage(_memory, ret);
@ -829,9 +871,9 @@ map<u256, u256> State::storage(Address _id) const
if (it != m_cache.end())
{
// Pull out all values from trie storage.
if (it->second.oldRoot())
if (it->second.baseRoot())
{
TrieDB<h256, OverlayDB> memdb(const_cast<OverlayDB*>(&m_db), it->second.oldRoot()); // promise we won't alter the overlay! :)
TrieDB<h256, OverlayDB> memdb(const_cast<OverlayDB*>(&m_db), it->second.baseRoot()); // promise we won't alter the overlay! :)
for (auto const& i: memdb)
ret[i.first] = RLP(i.second).toInt<u256>();
}

53
libethereum/State.h

@ -113,6 +113,9 @@ public:
/// Construct state object.
State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB());
/// Construct state object from arbitrary point in blockchain.
State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash);
/// Copy state object.
State(State const& _s);
@ -127,6 +130,7 @@ public:
/// Open a DB - useful for passing into the constructor & keeping for other states that are necessary.
static OverlayDB openDB(std::string _path, bool _killExisting = false);
static OverlayDB openDB(bool _killExisting = false) { return openDB(std::string(), _killExisting); }
OverlayDB const& db() const { return m_db; }
/// @returns the set containing all addresses currently in use in Ethereum.
std::map<Address, u256> addresses() const;
@ -154,13 +158,6 @@ public:
/// Only valid after mine() returns true.
bytes const& blockData() const { return m_currentBytes; }
/// Sync our state with the block chain.
/// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue.
bool sync(BlockChain const& _bc);
/// Sync with the block chain, but rather than synching to the latest block, instead sync to the given block.
bool sync(BlockChain const& _bc, h256 _blockHash);
// TODO: Cleaner interface.
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
/// @returns true if we uncommitted from mining during the operation.
@ -232,11 +229,11 @@ public:
/// If (_i == pending().size()) returns the final state of the block, prior to rewards.
State fromPending(unsigned _i) const;
/// Execute all transactions within a given block.
/// @returns the additional total difficulty.
/// If the _grandParent is passed, it will check the validity of each of the uncles.
/// This might throw.
u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit);
/// @returns the StateDiff caused by the pending transaction of index @a _i.
StateDiff pendingDiff(unsigned _i) const { return fromPending(_i).diff(fromPending(_i + 1)); }
/// @return the difference between this state (origin) and @a _c (destination).
StateDiff diff(State const& _c) const;
/// Get the fee associated for a transaction with the given data.
u256 txGas(uint _dataCount, u256 _gas = 0) const { return c_txDataGas * _dataCount + c_txGas + _gas; }
@ -247,8 +244,26 @@ public:
/// Get the fee associated for a normal transaction.
u256 callGas(uint _dataCount, u256 _gas = 0) const { return txGas(_dataCount, _gas); }
/// @return the difference between this state (origin) and @a _c (destination).
StateDiff diff(State const& _c) const;
/// Sync our state with the block chain.
/// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue.
bool sync(BlockChain const& _bc);
/// Sync with the block chain, but rather than synching to the latest block, instead sync to the given block.
bool sync(BlockChain const& _bc, h256 _blockHash, BlockInfo const& _bi = BlockInfo());
/// Execute all transactions within a given block.
/// @warning We must already have been sync()ed with said block.
/// @returns the additional total difficulty.
/// If the @a _grandParent is passed, it will check the validity of each of the uncles.
/// @throws if there are any validation errors.
u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent = BlockInfo());
u256 enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const& _bc);
/// Returns back to a pristine state after having done a playback.
/// @arg _fullCommit if true flush everything out to disk. If false, this effectively only validates
/// the block since all state changes are ultimately reversed.
void cleanup(bool _fullCommit);
private:
/// Undo the changes to the state for committing to mine.
@ -266,13 +281,9 @@ private:
/// Commit all changes waiting in the address cache to the DB.
void commit();
/// Execute the given block on our previous block. This will set up m_currentBlock first, then call the other playback().
/// Any failure will be critical.
u256 trustedPlayback(bytesConstRef _block, bool _fullCommit);
/// Execute the given block, assuming it corresponds to m_currentBlock. If _grandParent is passed, it will be used to check the uncles.
/// Throws on failure.
u256 playbackRaw(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit);
u256 enact(bytesConstRef _block, BlockInfo const& _grandParent = BlockInfo());
// Two priviledged entry points for transaction processing used by the VM (these don't get added to the Transaction lists):
// We assume all instrinsic fees are paid up before this point.
@ -342,10 +353,10 @@ void commit(std::map<Address, AddressState> const& _cache, DB& _db, TrieDB<Addre
s << i.second.nonce() << i.second.balance();
if (i.second.storage().empty())
s.append(i.second.oldRoot(), false, true);
s.append(i.second.baseRoot(), false, true);
else
{
TrieDB<h256, DB> storageDB(&_db, i.second.oldRoot());
TrieDB<h256, DB> storageDB(&_db, i.second.baseRoot());
for (auto const& j: i.second.storage())
if (j.second)
storageDB.insert(j.first, rlp(j.second));

Loading…
Cancel
Save