diff --git a/alethzero/DownloadView.cpp b/alethzero/DownloadView.cpp
index 5112f961c..046b8973a 100644
--- a/alethzero/DownloadView.cpp
+++ b/alethzero/DownloadView.cpp
@@ -52,7 +52,7 @@ void SyncView::paintEvent(QPaintEvent*)
unsigned syncVerified = syncImporting + bqs.verified;
unsigned syncVerifying = syncVerified + bqs.verifying;
unsigned syncUnverified = syncVerifying + bqs.unverified;
- unsigned syncUnknown = syncUnverified + bqs.unknown;
+ unsigned syncCount = syncUnverified + bqs.unknown - syncFrom;
// best effort guess. assumes there's no forks.
unsigned downloadFrom = m_client->numberFromHash(m_client->isKnown(man->firstBlock()) ? man->firstBlock() : PendingBlockHash);
@@ -67,17 +67,19 @@ void SyncView::paintEvent(QPaintEvent*)
unsigned hashDone = hashFrom + (sync.state == SyncState::Hashes ? sync.hashesReceived : hashCount);
m_lastFrom = min(syncFrom, m_lastFrom);
+ m_lastTo = max(max(syncFrom + syncCount, hashFrom + hashCount), m_lastTo);
unsigned from = min(min(hashFrom, downloadFrom), min(syncFrom, m_lastFrom));
- unsigned count = max(hashFrom + hashCount, downloadFrom + downloadCount) - from;
- m_lastFrom = (m_lastFrom * 95 + syncFrom) / 100;
+ unsigned count = max(max(hashFrom + hashCount, downloadFrom + downloadCount), max(syncFrom + syncCount, m_lastTo)) - from;
+ m_lastFrom = (m_lastFrom * 99 + syncFrom * 1) / 100;
+ m_lastTo = (m_lastTo * 99 + max(syncFrom + syncCount, hashFrom + hashCount) * 1) / 100;
if (!count)
{
- m_lastFrom = (unsigned)-1;
+ m_lastFrom = m_lastTo = (unsigned)-1;
return;
}
- cnote << "Range " << from << "-" << (from + count);
+ cnote << "Range " << from << "-" << (from + count) << "(" << hashFrom << "+" << hashCount << "," << downloadFrom << "+" << downloadCount << "," << syncFrom << "+" << syncCount << ")";
auto r = [&](unsigned u) {
return toString((u - from) * 100 / count) + "%";
};
@@ -85,27 +87,48 @@ void SyncView::paintEvent(QPaintEvent*)
if (count)
{
cnote << "Hashes:" << r(hashDone) << " Blocks:" << r(downloadFlank) << r(downloadDone) << r(downloadPoint);
- cnote << "Importing:" << r(syncFrom) << r(syncImported) << r(syncImporting) << r(syncVerified) << r(syncVerifying) << r(syncUnverified) << r(syncUnknown);
+ cnote << "Importing:" << r(syncFrom) << r(syncImported) << r(syncImporting) << r(syncVerified) << r(syncVerifying) << r(syncUnverified);
}
- if (!man || man->chainEmpty() || !man->subCount())
- return;
-
- float s = min(rect().width(), rect().height());
+ float squareSize = min(rect().width(), rect().height());
QPen pen;
pen.setCapStyle(Qt::FlatCap);
- pen.setWidthF(s / 10);
- p.setPen(pen);
+ pen.setWidthF(squareSize / 20);
auto middle = [&](float x) {
- return QRectF(s / 2 - s / 2 * x, 0 + s / 2 - s / 2 * x, s * x, s * x);
+ return QRectF(squareSize / 2 - squareSize / 2 * x, 0 + squareSize / 2 - squareSize / 2 * x, squareSize * x, squareSize * x);
+ };
+
+ auto arcLen = [&](unsigned x) {
+ return x * -5760.f / count;
+ };
+ auto arcPos = [&](unsigned x) {
+ return int(90 * 16.f + arcLen(x - from)) % 5760;
};
- auto toArc = [&](unsigned x) {
- return (x - from) * -5760.f / count;
+ p.setPen(Qt::NoPen);
+ p.setBrush(QColor::fromHsv(0, 0, 210));
+ pen.setWidthF(0.f);
+ p.drawPie(middle(0.4f), arcPos(from), arcLen(hashDone - from));
+
+ auto progress = [&](unsigned h, unsigned s, unsigned v, float size, float thickness, unsigned nfrom, unsigned ncount) {
+ p.setBrush(Qt::NoBrush);
+ pen.setColor(QColor::fromHsv(h, s, v));
+ pen.setWidthF(squareSize * thickness);
+ p.setPen(pen);
+ p.drawArc(middle(size), arcPos(nfrom), arcLen(ncount));
};
- const float arcFrom = 90 * 16.f;
- p.drawArc(middle(0.5f), arcFrom, toArc(downloadDone));
- p.drawPie(middle(0.2f), arcFrom, toArc(hashDone));
+
+ progress(0, 50, 170, 0.4f, 0.12f, downloadFlank, downloadPoint - downloadFlank);
+ progress(0, 0, 150, 0.4f, 0.10f, from, downloadDone - from);
+
+ progress(0, 0, 230, 0.7f, 0.090f, from, syncUnverified - from);
+ progress(60, 25, 210, 0.7f, 0.08f, from, syncVerifying - from);
+ progress(120, 25, 190, 0.7f, 0.07f, from, syncVerified - from);
+
+ progress(0, 0, 220, 0.9f, 0.02f, from, count);
+ progress(0, 0, 100, 0.9f, 0.04f, from, syncFrom - from);
+ progress(0, 50, 100, 0.9f, 0.08f, syncFrom, syncImporting - syncFrom);
+
return;
double ratio = (double)rect().width() / rect().height();
diff --git a/alethzero/DownloadView.h b/alethzero/DownloadView.h
index 9ee2428b2..71fc41f3f 100644
--- a/alethzero/DownloadView.h
+++ b/alethzero/DownloadView.h
@@ -51,4 +51,5 @@ private:
dev::eth::Client const* m_client = nullptr;
unsigned m_lastFrom = (unsigned)-1;
+ unsigned m_lastTo = (unsigned)-1;
};
diff --git a/alethzero/Main.ui b/alethzero/Main.ui
index 37ff7f66c..97853e174 100644
--- a/alethzero/Main.ui
+++ b/alethzero/Main.ui
@@ -199,6 +199,7 @@
+
diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index ba4bec863..60c07fd8d 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -1044,6 +1044,17 @@ void Main::on_vmInterpreter_triggered() { VMFactory::setKind(VMKind::Interpreter
void Main::on_vmJIT_triggered() { VMFactory::setKind(VMKind::JIT); }
void Main::on_vmSmart_triggered() { VMFactory::setKind(VMKind::Smart); }
+void Main::on_rewindChain_triggered()
+{
+ bool ok;
+ int n = QInputDialog::getInt(this, "Rewind Chain", "Enter the number of the new chain head.", ethereum()->number() * 9 / 10, 1, ethereum()->number(), 1, &ok);
+ if (ok)
+ {
+ ethereum()->rewind(n);
+ refreshAll();
+ }
+}
+
void Main::on_urlEdit_returnPressed()
{
QString s = ui->urlEdit->text();
diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h
index b1939534b..0e944b042 100644
--- a/alethzero/MainWin.h
+++ b/alethzero/MainWin.h
@@ -187,6 +187,7 @@ private slots:
void on_vmInterpreter_triggered();
void on_vmJIT_triggered();
void on_vmSmart_triggered();
+ void on_rewindChain_triggered();
// Debugger
void on_debugCurrent_triggered();
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index bf4eaf4d0..719ec016e 100644
--- a/libethereum/BlockChain.cpp
+++ b/libethereum/BlockChain.cpp
@@ -799,6 +799,25 @@ void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end)
}
}
+void BlockChain::rewind(unsigned _newHead)
+{
+ DEV_WRITE_GUARDED(x_lastBlockHash)
+ {
+ if (_newHead >= m_lastBlockNumber)
+ return;
+ m_lastBlockHash = numberHash(_newHead);
+ m_lastBlockNumber = _newHead;
+ auto o = m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&m_lastBlockHash, 32));
+ if (!o.ok())
+ {
+ cwarn << "Error writing to extras database: " << o.ToString();
+ cout << "Put" << toHex(bytesConstRef(ldb::Slice("best"))) << "=>" << toHex(bytesConstRef(ldb::Slice((char const*)&m_lastBlockHash, 32)));
+ cwarn << "Fail writing to extras database. Bombing out.";
+ exit(-1);
+ }
+ }
+}
+
tuple BlockChain::treeRoute(h256 const& _from, h256 const& _to, bool _common, bool _pre, bool _post) const
{
// cdebug << "treeRoute" << _from << "..." << _to;
@@ -1070,7 +1089,8 @@ bool BlockChain::isKnown(h256 const& _hash) const
if (d.empty())
return false;
}
- return true;
+// return true;
+ return details(_hash).number <= m_lastBlockNumber; // to allow rewind functionality.
}
bytes BlockChain::block(h256 const& _hash) const
diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h
index 4cffca2df..1d88430b0 100644
--- a/libethereum/BlockChain.h
+++ b/libethereum/BlockChain.h
@@ -215,6 +215,9 @@ public:
/// Will call _progress with the progress in this operation first param done, second total.
void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function(), bool _prepPoW = false);
+ /// Alter the head of the chain to some prior block along it.
+ void rewind(unsigned _newHead);
+
/** @returns a tuple of:
* - an vector of hashes of all blocks between @a _from and @a _to, all blocks are ordered first by a number of
* blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent;
diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp
index 5e0a523d8..f95a3880e 100644
--- a/libethereum/BlockQueue.cpp
+++ b/libethereum/BlockQueue.cpp
@@ -215,8 +215,10 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
return ImportResult::Malformed;
}
+ clog(BlockQueueTraceChannel) << "Block" << h << "is" << bi.number << "parent is" << bi.parentHash;
+
// Check block doesn't already exist first!
- if (_bc.details(h))
+ if (_bc.isKnown(h))
{
cblockq << "Already known in chain.";
return ImportResult::AlreadyInChain;
diff --git a/libethereum/Client.h b/libethereum/Client.h
index 829ec77c1..fac54b010 100644
--- a/libethereum/Client.h
+++ b/libethereum/Client.h
@@ -217,6 +217,8 @@ public:
std::string const& sentinel() const { return m_sentinel; }
/// Set the extra data that goes into mined blocks.
void setExtraData(bytes const& _extraData) { m_extraData = _extraData; }
+ /// Rewind to a prior head.
+ void rewind(unsigned _n) { m_bc.rewind(_n); }
protected:
/// InterfaceStub methods
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index f50545d08..10d637509 100644
--- a/libethereum/State.cpp
+++ b/libethereum/State.cpp
@@ -1212,6 +1212,7 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
uncommitToMine();
// OK - transaction looks valid - execute.
+ u256 startGasUsed = gasUsed();
#if ETH_PARANOIA
ctrace << "Executing" << e.t() << "on" << h;
ctrace << toHex(e.t().rlp());
@@ -1261,7 +1262,7 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(e.t());
- m_receipts.push_back(TransactionReceipt(rootHash(), e.gasUsed(), e.logs()));
+ m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()));
m_transactionSet.insert(e.t().sha3());
}