diff --git a/README.md b/README.md
index f370b2309..7529fed50 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ See the [Wiki](https://github.com/ethereum/cpp-ethereum/wiki) for build instruct
### Testing
-To run the tests, make sure you clone the tests repository from github.com/ethereum to tests is a sibling to cpp-ethereum-build.
+To run the tests, make sure you clone the tests repository from github.com/ethereum to tests as a sibling to cpp-ethereum.
### Yet To Do
diff --git a/alethzero/Main.ui b/alethzero/Main.ui
index b8612df11..adca65854 100644
--- a/alethzero/Main.ui
+++ b/alethzero/Main.ui
@@ -165,6 +165,7 @@
+
@@ -1430,6 +1431,14 @@ font-family: Monospace, Ubuntu Mono, Lucida Console, Courier New
&Load Javascript...
+
+
+ Single Step &Backwards
+
+
+ Shift+F10
+
+
diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index 00264e539..d45599cd3 100644
--- a/alethzero/MainWin.cpp
+++ b/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() << "
";
- s << "Post: " << ts.rootHash() << "";
-
auto indent = "
";
- for (auto const& i: d.accounts)
+ for (auto const& i: _d.accounts)
{
s << "
";
@@ -773,9 +768,10 @@ void Main::on_transactionQueue_currentItemChanged()
}
s << "
";
- 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() << "
";
+// s << "Post: " << ts.rootHash() << "";
+
+ s << renderDiff(m_client->postState().pendingDiff(i));
}
ui->pendingInfo->setHtml(QString::fromStdString(s.str()));
@@ -872,15 +868,25 @@ void Main::on_blocks_currentItemChanged()
if (tx.data.size())
s << eth::memDump(tx.data, 16, true);
}
- s << "
";
-/* 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().
+ auto st = eth::State(m_client->state().db(), m_client->blockChain(), h);
+ s << renderDiff(st.pendingDiff(txi));
+
+ m_executiveState = st.fromPending(txi);
+ m_currentExecution = unique_ptr(new Executive(m_executiveState));
+ Transaction t = st.pending()[txi];
+ auto r = t.rlp();
+
+ debugFinished();
+ bool done = m_currentExecution->setup(&r);
+ if (!done)
+ {
+ auto startGas = m_currentExecution->vm().gas();
+ for (; !done; done = m_currentExecution->go(1))
+ m_history.append(WorldState({m_currentExecution->vm().curPC(), m_currentExecution->vm().gas(), startGas - m_currentExecution->vm().gas(), m_currentExecution->vm().stack(), m_currentExecution->vm().memory(), m_currentExecution->state().storage(m_currentExecution->ext().myAddress)}));
+ initDebugger();
+ updateDebugger();
+ }
+ m_currentExecution.reset();
}
@@ -1210,14 +1216,15 @@ void Main::on_debug_clicked()
t.receiveAddress = isCreation() ? Address() : fromString(ui->destination->currentText());
t.sign(s);
auto r = t.rlp();
- m_currentExecution->setup(&r);
+ m_currentExecution->setup(&r);
m_pcWarp.clear();
m_history.clear();
bool ok = true;
+ auto gasBegin = m_currentExecution->vm().gas();
while (ok)
{
- m_history.append(WorldState({m_currentExecution->vm().curPC(), m_currentExecution->vm().gas(), m_currentExecution->vm().stack(), m_currentExecution->vm().memory(), m_currentExecution->state().storage(m_currentExecution->ext().myAddress)}));
+ m_history.append(WorldState({m_currentExecution->vm().curPC(), m_currentExecution->vm().gas(), gasBegin - m_currentExecution->vm().gas(), m_currentExecution->vm().stack(), m_currentExecution->vm().memory(), m_currentExecution->state().storage(m_currentExecution->ext().myAddress)}));
ok = !m_currentExecution->go(1);
}
initDebugger();
@@ -1244,6 +1251,11 @@ void Main::on_debugStep_triggered()
ui->debugTimeline->setValue(ui->debugTimeline->value() + 1);
}
+void Main::on_debugStepback_triggered()
+{
+ ui->debugTimeline->setValue(ui->debugTimeline->value() - 1);
+}
+
void Main::debugFinished()
{
m_pcWarp.clear();
@@ -1255,6 +1267,7 @@ void Main::debugFinished()
ui->debugStateInfo->setText("");
// ui->send->setEnabled(true);
ui->debugStep->setEnabled(false);
+ ui->debugStepback->setEnabled(false);
ui->debugPanel->setEnabled(false);
}
@@ -1262,6 +1275,7 @@ void Main::initDebugger()
{
// ui->send->setEnabled(false);
ui->debugStep->setEnabled(true);
+ ui->debugStepback->setEnabled(true);
ui->debugPanel->setEnabled(true);
ui->debugCode->setEnabled(false);
ui->debugTimeline->setMinimum(0);
diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h
index ea96d51be..c372a7b5a 100644
--- a/alethzero/MainWin.h
+++ b/alethzero/MainWin.h
@@ -46,6 +46,7 @@ struct WorldState
{
eth::u256 curPC;
eth::u256 gas;
+ eth::u256 gasUsed;
eth::u256s stack;
eth::bytes memory;
std::map storage;
@@ -101,6 +102,7 @@ private slots:
void on_quit_triggered() { close(); }
void on_urlEdit_returnPressed();
void on_debugStep_triggered();
+ void on_debugStepback_triggered();
void on_debug_clicked();
void on_debugTimeline_valueChanged();
void on_jsInput_returnPressed();
@@ -131,7 +133,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;
diff --git a/libethereum/AddressState.h b/libethereum/AddressState.h
index 3984dd498..2ccdbd53b 100644
--- a/libethereum/AddressState.h
+++ b/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 const& storage() const { return m_storageOverlay; }
void setStorage(u256 _p, u256 _v) { m_storageOverlay[_p] = _v; }
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index 809f3a90a..5a5804637 100644
--- a/libethereum/BlockChain.cpp
+++ b/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
diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h
index c76fbb663..daa19f783 100644
--- a/libethereum/BlockChain.h
+++ b/libethereum/BlockChain.h
@@ -55,6 +55,7 @@ typedef std::map BlockDetailsHash;
static const BlockDetails NullBlockDetails;
static const h256s NullH256s;
+class State;
class OverlayDB;
class AlreadyHaveBlock: public std::exception {};
diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp
index 274c55f08..faabee133 100644
--- a/libethereum/Executive.cpp
+++ b/libethereum/Executive.cpp
@@ -40,7 +40,7 @@ u256 Executive::gasUsed() const
return m_t.gas - m_endGas;
}
-void Executive::setup(bytesConstRef _rlp)
+bool Executive::setup(bytesConstRef _rlp)
{
// Entry point for a user-executed transaction.
m_t = Transaction(_rlp);
@@ -95,12 +95,12 @@ void Executive::setup(bytesConstRef _rlp)
m_s.subBalance(m_sender, cost);
if (m_t.isCreation())
- create(m_sender, m_t.value, m_t.gasPrice, m_t.gas - gasCost, &m_t.data, m_sender);
+ return create(m_sender, m_t.value, m_t.gasPrice, m_t.gas - gasCost, &m_t.data, m_sender);
else
- call(m_t.receiveAddress, m_sender, m_t.value, m_t.gasPrice, bytesConstRef(&m_t.data), m_t.gas - gasCost, m_sender);
+ return call(m_t.receiveAddress, m_sender, m_t.value, m_t.gasPrice, bytesConstRef(&m_t.data), m_t.gas - gasCost, m_sender);
}
-void Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
+bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
{
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
m_s.addBalance(_receiveAddress, _value);
@@ -110,12 +110,16 @@ void Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu
m_vm = new VM(_gas);
bytes const& c = m_s.code(_receiveAddress);
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c);
+ return false;
}
else
+ {
m_endGas = _gas;
+ return true;
+ }
}
-void Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
+bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
{
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
while (m_s.addressInUse(m_newAddress))
@@ -127,6 +131,7 @@ void Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
// Execute _init.
m_vm = new VM(_gas);
m_ext = new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init);
+ return _init.empty();
}
bool Executive::go(uint64_t _steps)
diff --git a/libethereum/Executive.h b/libethereum/Executive.h
index 8a4e976bf..5dd31b69a 100644
--- a/libethereum/Executive.h
+++ b/libethereum/Executive.h
@@ -40,9 +40,9 @@ public:
Executive(State& _s): m_s(_s) {}
~Executive();
- void setup(bytesConstRef _transaction);
- void create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
- void call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
+ bool setup(bytesConstRef _transaction);
+ bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
+ bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool go(uint64_t _steps = (uint64_t)-1);
void finalize();
u256 gasUsed() const;
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index 86b433a8e..07e71dc28 100644
--- a/libethereum/State.cpp
+++ b/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 State::addresses() const
{
map 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() != 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 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 memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't change the overlay! :)
+ TrieDB memdb(const_cast(&m_db), it->second.baseRoot()); // promise we won't change the overlay! :)
string payload = memdb.at(_memory);
u256 ret = payload.size() ? RLP(payload).toInt() : 0;
it->second.setStorage(_memory, ret);
@@ -829,9 +871,9 @@ map 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 memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't alter the overlay! :)
+ TrieDB memdb(const_cast(&m_db), it->second.baseRoot()); // promise we won't alter the overlay! :)
for (auto const& i: memdb)
ret[i.first] = RLP(i.second).toInt();
}
diff --git a/libethereum/State.h b/libethereum/State.h
index afccec91f..fd3fc89e5 100644
--- a/libethereum/State.h
+++ b/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 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 const& _cache, DB& _db, TrieDB storageDB(&_db, i.second.oldRoot());
+ TrieDB storageDB(&_db, i.second.baseRoot());
for (auto const& j: i.second.storage())
if (j.second)
storageDB.insert(j.first, rlp(j.second));