From 976a84c7e5f857f95e45a5b050b7192f0db69184 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 18 Feb 2014 23:52:16 +0000 Subject: [PATCH] Contract state introspection. --- CMakeLists.txt | 2 +- alethzero/Main.ui | 89 ++++++++++++++++++++++++++++++------------- alethzero/MainWin.cpp | 89 ++++++++++++++++++++++++++++++++++++++----- alethzero/MainWin.h | 1 + libethereum/State.cpp | 8 ++++ libethereum/State.h | 6 +++ 6 files changed, 158 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9affafc0e..23c418da3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_AUTOMOC ON) cmake_policy(SET CMP0015 NEW) -set(ETH_VERSION 0.3.1) +set(ETH_VERSION 0.3.2) set(ETH_BUILD_TYPE ${CMAKE_BUILD_TYPE}) # Default HEADLESS to 0. diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 3a49fa839..50a6b0d24 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -25,37 +25,63 @@ 0 - - - - 0 - 0 - - + - Qt::Horizontal + Qt::Vertical - - - - 0 - 0 - - - - QFrame::NoFrame + + + Qt::Horizontal + + + + 0 + 0 + + + + QFrame::NoFrame + + + + + + 2 + 0 + + + + QFrame::NoFrame + + - - - - 2 - 0 - - - - QFrame::NoFrame + + + Qt::Horizontal + + + + 0 + 0 + + + + QFrame::NoFrame + + + + + + 2 + 0 + + + + QFrame::NoFrame + + @@ -476,6 +502,17 @@ + + + toolBar_2 + + + TopToolBarArea + + + false + + &Quit diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 6ad4b5d87..11297a78b 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -147,20 +147,33 @@ void Main::refresh() auto acs = m_client->state().addresses(); ui->accounts->clear(); + ui->contracts->clear(); for (auto i: acs) { (new QListWidgetItem(QString("%1 [%3] @ %2").arg(formatBalance(i.second).c_str()).arg(i.first.abridged().c_str()).arg((unsigned)m_client->state().transactionsFrom(i.first)), ui->accounts)) ->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); + if (m_client->state().isContractAddress(i.first)) + (new QListWidgetItem(QString("%1 [%3] @ %2").arg(formatBalance(i.second).c_str()).arg(i.first.abridged().c_str()).arg((unsigned)m_client->state().transactionsFrom(i.first)), ui->contracts)) + ->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); } ui->transactionQueue->clear(); for (pair const& i: m_client->pending()) { - ui->transactionQueue->addItem(QString("%1 [%4] @ %2 <- %3") - .arg(formatBalance(i.second.value).c_str()) - .arg(i.second.receiveAddress.abridged().c_str()) - .arg(i.second.safeSender().abridged().c_str()) - .arg((unsigned)i.second.nonce)); + Transaction t = i.second; + QString s = t.receiveAddress ? + QString("%1 [%4] %2 %5> %3") + .arg(formatBalance(t.value).c_str()) + .arg(t.safeSender().abridged().c_str()) + .arg(t.receiveAddress.abridged().c_str()) + .arg((unsigned)t.nonce) + .arg(m_client->state().isContractAddress(t.receiveAddress) ? '*' : '-') : + QString("%1 [%4] %2 +> %3") + .arg(formatBalance(t.value).c_str()) + .arg(t.safeSender().abridged().c_str()) + .arg(right160(t.sha3()).abridged().c_str()) + .arg((unsigned)t.nonce); + ui->transactionQueue->addItem(s); } ui->blocks->clear(); @@ -174,11 +187,19 @@ void Main::refresh() for (auto const& i: RLP(bc.block(h))[1]) { Transaction t(i.data()); - QListWidgetItem* txItem = new QListWidgetItem(QString("%1 [%4] @ %2 <- %3") - .arg(formatBalance(t.value).c_str()) - .arg(t.receiveAddress.abridged().c_str()) - .arg(t.safeSender().abridged().c_str()) - .arg((unsigned)t.nonce), ui->blocks); + QString s = t.receiveAddress ? + QString(" %1 [%4] %2 %5> %3") + .arg(formatBalance(t.value).c_str()) + .arg(t.safeSender().abridged().c_str()) + .arg(t.receiveAddress.abridged().c_str()) + .arg((unsigned)t.nonce) + .arg(m_client->state().isContractAddress(t.receiveAddress) ? '*' : '-') : + QString(" %1 [%4] %2 +> %3") + .arg(formatBalance(t.value).c_str()) + .arg(t.safeSender().abridged().c_str()) + .arg(right160(t.sha3()).abridged().c_str()) + .arg((unsigned)t.nonce); + QListWidgetItem* txItem = new QListWidgetItem(s, ui->blocks); txItem->setData(Qt::UserRole, QByteArray((char const*)h.data(), h.size)); txItem->setData(Qt::UserRole + 1, n); n++; @@ -264,6 +285,54 @@ void Main::on_blocks_currentItemChanged() m_client->unlock(); } +void Main::on_contracts_currentItemChanged() +{ + ui->contractInfo->clear(); + m_client->lock(); + if (auto item = ui->contracts->currentItem()) + { + auto hba = item->data(Qt::UserRole).toByteArray(); + assert(hba.size() == 20); + auto h = h160((byte const*)hba.data(), h160::ConstructFromPointer); + + stringstream s; + auto mem = m_client->state().contractMemory(h); + u256 next = 0; + unsigned numerics = 0; + for (auto i: mem) + { + if (next < i.first) + { + unsigned j; + for (j = 0; j <= numerics && next + j < i.first; ++j) + s << (j < numerics ? " 0" : " STOP"); + numerics -= (j - 1); + s << " ...
@" << showbase << hex << i.first << "    "; + } + else if (!next) + { + s << "@" << showbase << hex << i.first << "    "; + } + auto iit = c_instructionInfo.find((Instruction)(unsigned)i.second); + if (numerics || iit == c_instructionInfo.end() || (u256)(unsigned)iit->first != i.second) // not an instruction or expecting an argument... + { + if (numerics) + numerics--; + s << " 0x" << hex << i.second; + } + else + { + auto const& ii = iit->second; + s << " " << ii.name; + numerics = ii.additional; + } + next = i.first + 1; + } + ui->contractInfo->appendHtml(QString::fromStdString(s.str())); + } + m_client->unlock(); +} + void Main::on_idealPeers_valueChanged() { if (m_client->peerServer()) diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index e1cb44e65..a2d1199fd 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -39,6 +39,7 @@ private slots: void on_valueUnits_currentIndexChanged() { updateFee(); } void on_log_doubleClicked(); void on_blocks_currentItemChanged(); + void on_contracts_currentItemChanged(); void on_about_triggered(); void on_quit_triggered() { close(); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 7bebc5ec5..8badddcb6 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -605,6 +605,14 @@ u256 State::contractMemory(Address _id, u256 _memory) const return RLP(memdb.at(_memory)).toInt(); // TODO: CHECK: check if this is actually an RLP decode } +map const& State::contractMemory(Address _contract) const +{ + if (!isContractAddress(_contract)) + return EmptyMapU256U256; + ensureCached(_contract, true, true); + return m_cache[_contract].memory(); +} + void State::execute(bytesConstRef _rlp) { // Entry point for a user-executed transaction. diff --git a/libethereum/State.h b/libethereum/State.h index 63520da70..af7166eb5 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -60,6 +60,8 @@ struct FeeStructure template class UnitTest {}; +static const std::map EmptyMapU256U256; + /** * @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). @@ -154,6 +156,10 @@ public: /// @returns 0 if no contract exists at that address. u256 contractMemory(Address _contract, u256 _memory) const; + /// Get the memory of a contract. + /// @returns std::map if no contract exists at that address. + std::map const& contractMemory(Address _contract) const; + /// Note that the given address is sending a transaction and thus increment the associated ticker. void noteSending(Address _id);