Browse Source

Proper debugger.

cl-refactor
Gav Wood 11 years ago
parent
commit
d91198a335
  1. 76
      alethzero/Main.ui
  2. 149
      alethzero/MainWin.cpp
  3. 17
      alethzero/MainWin.h
  4. 54
      libethereum/Executive.cpp
  5. 9
      libethereum/Executive.h
  6. 18
      libethereum/ExtVM.h
  7. 12
      libethereum/State.cpp
  8. 4
      libethereum/State.h
  9. 3
      libevm/ExtVMFace.h
  10. 38
      libevm/VM.h
  11. 4
      test/vm.cpp

76
alethzero/Main.ui

@ -163,6 +163,7 @@
</property>
<addaction name="debugStep"/>
<addaction name="debugStepback"/>
<addaction name="dumpTrace"/>
<addaction name="separator"/>
<addaction name="paranoia"/>
<addaction name="killBlockchain"/>
@ -518,11 +519,24 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QPlainTextEdit" name="data"/>
<widget class="QPlainTextEdit" name="data">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
<widget class="QTextEdit" name="code">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</widget>
</item>
@ -930,6 +944,21 @@
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter_6">
<property name="orientation">
@ -942,6 +971,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
<widget class="QSplitter" name="splitter_4">
<property name="sizePolicy">
@ -954,11 +989,23 @@
<enum>Qt::Vertical</enum>
</property>
<widget class="QListWidget" name="debugStack">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
<widget class="QTextEdit" name="debugMemory">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
@ -967,6 +1014,12 @@
</property>
</widget>
<widget class="QTextEdit" name="debugStorage">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
@ -975,6 +1028,19 @@
</property>
</widget>
</widget>
<widget class="QListWidget" name="callStack">
<property name="font">
<font>
<family>Ubuntu Mono</family>
</font>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</widget>
</item>
<item>
@ -1450,6 +1516,14 @@ font-size: 14pt</string>
<string>&amp;Clear Pending</string>
</property>
</action>
<action name="dumpTrace">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Dump Standard Trace</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

149
alethzero/MainWin.cpp

@ -898,16 +898,7 @@ void Main::on_blocks_currentItemChanged()
Transaction t = st.pending()[txi];
auto r = t.rlp();
bool done = m_currentExecution->setup(&r);
if (!done)
{
debugFinished();
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();
}
populateDebugger(&r);
m_currentExecution.reset();
}
@ -916,6 +907,39 @@ void Main::on_blocks_currentItemChanged()
}
}
void Main::populateDebugger(eth::bytesConstRef _r)
{
bool done = m_currentExecution->setup(_r);
if (!done)
{
debugFinished();
vector<WorldState const*> levels;
m_codes.clear();
bytesConstRef lastExtCode;
h256 lastHash;
auto onOp = [&](uint64_t steps, Instruction inst, unsigned newMemSize, eth::bigint gasCost, void* voidVM, void const* voidExt)
{
eth::VM& vm = *(eth::VM*)voidVM;
eth::ExtVM const& ext = *(eth::ExtVM const*)voidExt;
if (ext.code != lastExtCode)
{
lastExtCode = ext.code;
lastHash = sha3(lastExtCode);
if (!m_codes.count(lastHash))
m_codes[lastHash] = ext.code.toBytes();
}
if (levels.size() < ext.level)
levels.push_back(&m_history.back());
else
levels.resize(ext.level);
m_history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
m_currentExecution->go(onOp);
initDebugger();
updateDebugger();
}
}
void Main::on_contracts_currentItemChanged()
{
ui->contractInfo->clear();
@ -1236,15 +1260,7 @@ void Main::on_debug_clicked()
t.receiveAddress = isCreation() ? Address() : fromString(ui->destination->currentText());
t.sign(s);
auto r = t.rlp();
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();
}
populateDebugger(&r);
m_currentExecution.reset();
return;
}
@ -1265,17 +1281,35 @@ void Main::on_create_triggered()
void Main::on_debugStep_triggered()
{
ui->debugTimeline->setValue(ui->debugTimeline->value() + 1);
ui->callStack->setCurrentRow(0);
}
void Main::on_debugStepback_triggered()
{
ui->debugTimeline->setValue(ui->debugTimeline->value() - 1);
ui->callStack->setCurrentRow(0);
}
void Main::on_dumpTrace_triggered()
{
for (auto i: m_history)
{
}
}
void Main::on_callStack_currentItemChanged()
{
updateDebugger();
}
void Main::debugFinished()
{
m_codes.clear();
m_pcWarp.clear();
m_history.clear();
m_lastLevels.clear();
m_inDebug = h256();
ui->callStack->clear();
ui->debugCode->clear();
ui->debugStack->clear();
ui->debugMemory->setHtml("");
@ -1283,6 +1317,7 @@ void Main::debugFinished()
ui->debugStateInfo->setText("");
// ui->send->setEnabled(true);
ui->debugStep->setEnabled(false);
ui->dumpTrace->setEnabled(false);
ui->debugStepback->setEnabled(false);
ui->debugPanel->setEnabled(false);
}
@ -1293,30 +1328,66 @@ void Main::initDebugger()
if (m_history.size())
{
ui->debugStep->setEnabled(true);
ui->dumpTrace->setEnabled(true);
ui->debugStepback->setEnabled(true);
ui->debugPanel->setEnabled(true);
ui->debugCode->setEnabled(false);
ui->debugTimeline->setMinimum(0);
ui->debugTimeline->setMaximum(m_history.size() - 1);
ui->debugTimeline->setValue(0);
}
}
void Main::on_debugTimeline_valueChanged()
{
updateDebugger();
}
void Main::updateDebugger()
{
if (m_history.size())
{
QListWidget* ds = ui->debugStack;
ds->clear();
QListWidget* dc = ui->debugCode;
dc->clear();
if (m_currentExecution)
WorldState const& nws = m_history[ui->debugTimeline->value()];
if (m_lastLevels != nws.levels || !ui->callStack->count())
{
for (unsigned i = 0; i <= m_currentExecution->ext().code.size(); ++i)
m_lastLevels = nws.levels;
ui->callStack->clear();
for (unsigned i = 0; i <= nws.levels.size(); ++i)
{
byte b = i < m_currentExecution->ext().code.size() ? m_currentExecution->ext().code[i] : 0;
WorldState const& s = i ? *nws.levels[nws.levels.size() - i] : nws;
ostringstream out;
out << s.cur.abridged();
if (i)
out << " @0x" << hex << s.curPC;
ui->callStack->addItem(QString::fromStdString(out.str()));
}
}
WorldState const& ws = ui->callStack->currentRow() > 0 ? *nws.levels[nws.levels.size() - ui->callStack->currentRow()] : nws;
if (ws.code != m_inDebug)
{
bytes const& code = m_codes[ws.code];
QListWidget* dc = ui->debugCode;
dc->clear();
m_pcWarp.clear();
for (unsigned i = 0; i <= code.size(); ++i)
{
byte b = i < code.size() ? code[i] : 0;
try
{
QString s = c_instructionInfo.at((Instruction)b).name;
m_pcWarp[i] = dc->count();
ostringstream out;
out << hex << setw(4) << setfill('0') << i;
m_pcWarp[i] = dc->count();
if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32)
{
unsigned bc = b - (byte)Instruction::PUSH1 + 1;
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&m_currentExecution->ext().code[i + 1], bc)));
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&code[i + 1], bc)));
i += bc;
}
dc->addItem(QString::fromStdString(out.str()) + " " + s);
@ -1326,30 +1397,20 @@ void Main::initDebugger()
break; // probably hit data segment
}
}
m_inDebug = ws.code;
}
}
}
void Main::on_debugTimeline_valueChanged()
{
updateDebugger();
}
void Main::updateDebugger()
{
if (m_history.size())
{
QListWidget* ds = ui->debugStack;
ds->clear();
WorldState const& ws = m_history[ui->debugTimeline->value()];
for (auto i: ws.stack)
ds->insertItem(0, QString::fromStdString(toHex(((h256)i).asArray())));
ui->debugMemory->setHtml(QString::fromStdString(eth::memDump(ws.memory, 16, true)));
ui->debugCode->setCurrentRow(m_pcWarp[(unsigned)ws.curPC]);
assert(m_codes.count(ws.code));
assert(m_codes[ws.code].size() > (unsigned)ws.curPC);
int l = m_pcWarp[(unsigned)ws.curPC];
ui->debugCode->setCurrentRow(max(0, l - 5));
ui->debugCode->setCurrentRow(min(ui->debugCode->count() - 1, l + 5));
ui->debugCode->setCurrentRow(l);
ostringstream ss;
ss << hex << "PC: 0x" << ws.curPC << " | GAS: " << dec << ws.gas << " | (- " << dec << ws.gasUsed << ")";
ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << c_instructionInfo.at(ws.inst).name << " | ADDMEM: " << dec << ws.newMemSize << " words | COST: " << dec << ws.gasCost << " | GAS: " << dec << ws.gas;
ui->debugStateInfo->setText(QString::fromStdString(ss.str()));
stringstream s;

17
alethzero/MainWin.h

@ -21,7 +21,8 @@
#pragma once
//#include <QtQml/QJSValue>
#include <map>
#include <QtNetwork/QNetworkAccessManager>
#include <QtCore/QAbstractListModel>
#include <QtCore/QMutex>
@ -44,12 +45,18 @@ class QQuickView;
struct WorldState
{
uint64_t steps;
eth::Address cur;
eth::u256 curPC;
eth::Instruction inst;
unsigned newMemSize;
eth::u256 gas;
eth::u256 gasUsed;
eth::h256 code;
eth::u256s stack;
eth::bytes memory;
eth::bigint gasCost;
std::map<eth::u256, eth::u256> storage;
std::vector<WorldState const*> levels;
};
class Main : public QMainWindow
@ -115,6 +122,8 @@ private slots:
void on_loadJS_triggered();
void on_blockChainFilter_textChanged();
void on_clearPending_triggered();
void on_dumpTrace_triggered();
void on_callStack_currentItemChanged();
void refresh(bool _override = false);
void refreshNetwork();
@ -129,6 +138,7 @@ private:
QString pretty(eth::Address _a) const;
void populateDebugger(eth::bytesConstRef r);
void initDebugger();
void updateDebugger();
void debugFinished();
@ -167,9 +177,12 @@ private:
eth::State m_executiveState;
std::unique_ptr<eth::Executive> m_currentExecution;
eth::h256 m_inDebug;
std::vector<WorldState const*> m_lastLevels;
QMap<unsigned, unsigned> m_pcWarp;
QList<WorldState> m_history;
std::map<eth::u256, eth::bytes> m_codes; // and pcWarps
QNetworkAccessManager m_webCtrl;

54
libethereum/Executive.cpp

@ -140,44 +140,34 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
return _init.empty();
}
bool Executive::go(uint64_t _steps)
OnOpFunc Executive::simpleTrace()
{
return [](uint64_t steps, Instruction inst, unsigned newMemSize, bigint gasCost, void* voidVM, void const* voidExt)
{
ExtVM const& ext = *(ExtVM const*)voidExt;
VM& vm = *(VM*)voidVM;
ostringstream o;
o << endl << " STACK" << endl;
for (auto i: vm.stack())
o << (h256)i << endl;
o << " MEMORY" << endl << memDump(vm.memory());
o << " STORAGE" << endl;
for (auto const& i: ext.state().storage(ext.myAddress))
o << showbase << hex << i.first << ": " << i.second << endl;
eth::LogOutputStream<VMTraceChannel, false>(true) << o.str();
eth::LogOutputStream<VMTraceChannel, false>(false) << " | " << dec << ext.level << " | " << ext.myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << c_instructionInfo.at(inst).name << " | " << dec << vm.gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32" << " ]";
};
}
bool Executive::go(OnOpFunc const& _onOp)
{
if (m_vm)
{
bool revert = false;
try
{
#if ETH_VMTRACE
/*
if (_steps == (uint64_t)0 - 1)
for (uint64_t s = 0;; ++s)
{
ostringstream o;
o << endl << " STACK" << endl;
for (auto i: vm().stack())
o << (h256)i << endl;
o << " MEMORY" << endl << memDump(vm().memory());
o << " STORAGE" << endl;
for (auto const& i: state().storage(ext().myAddress))
o << showbase << hex << i.first << ": " << i.second << endl;
eth::LogOutputStream<VMTraceChannel, false>(true) << o.str();
eth::LogOutputStream<VMTraceChannel, false>(false) << dec << " | #" << s << " | " << hex << setw(4) << setfill('0') << vm().curPC() << " : " << c_instructionInfo.at((Instruction)ext().getCode(vm().curPC())).name << " | " << dec << vm().gas() << " ]";
if (s >= _steps)
break;
try
{
m_out = m_vm->go(*m_ext, 1);
break;
}
catch (StepsDone const&) {}
}
else
m_out = m_vm->go(*m_ext, _steps);*/
auto s = state().storage(m_ext->myAddress);
m_out = m_vm->go(*m_ext, _steps, &s);
#else
m_out = m_vm->go(*m_ext, _steps);
#endif
m_out = m_vm->go(*m_ext, _onOp);
m_endGas = m_vm->gas();
}
catch (StepsDone const&)

9
libethereum/Executive.h

@ -21,8 +21,11 @@
#pragma once
#include <functional>
#include <libethential/Log.h>
#include <libevmface/Instruction.h>
#include <libethcore/CommonEth.h>
#include <libevm/ExtVMFace.h>
#include "Transaction.h"
namespace eth
@ -35,6 +38,8 @@ class State;
struct Manifest;
using Manifests = std::vector<Manifest>;
struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; };
/**
* @brief A record of the state-interaction of a transaction/call/create.
*/
@ -57,10 +62,12 @@ public:
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);
bool go(OnOpFunc const& _onOp = OnOpFunc());
void finalize();
u256 gasUsed() const;
static OnOpFunc simpleTrace();
Transaction const& t() const { return m_t; }
u256 gas() const;

18
libethereum/ExtVM.h

@ -22,6 +22,7 @@
#pragma once
#include <map>
#include <functional>
#include <libethcore/CommonEth.h>
#include <libevm/ExtVMFace.h>
#include "State.h"
@ -36,8 +37,8 @@ class ExtVM: public ExtVMFace
{
public:
/// Full constructor.
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms)
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms, unsigned _level = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock), level(_level), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms)
{
m_s.ensureCached(_myAddress, true, true);
}
@ -49,24 +50,24 @@ public:
void setStore(u256 _n, u256 _v) { m_s.setStorage(myAddress, _n, _v); if (m_ms) m_ms->altered.push_back(_n); }
/// Create a new contract.
h160 create(u256 _endowment, u256* _gas, bytesConstRef _code)
h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc())
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr);
auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
}
/// Create a new message call.
bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out)
bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = OnOpFunc())
{
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
auto ret = m_s.call(_receiveAddress, myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr);
auto ret = m_s.call(_receiveAddress, myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
@ -92,6 +93,11 @@ public:
/// @TODO check call site for the parent manifest being discarded.
void revert() { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; }
State& state() const { return m_s; }
/// @note not a part of the main API; just for use by tracing/debug stuff.
unsigned level = 0;
private:
State& m_s; ///< A reference to the base state.
std::map<Address, AddressState> m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution.

12
libethereum/State.cpp

@ -1021,7 +1021,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit, Manifest*
return e.gasUsed();
}
bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set<Address>* o_suicides, Manifest* o_ms)
bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set<Address>* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
if (!_originAddress)
_originAddress = _senderAddress;
@ -1039,12 +1039,12 @@ bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u
if (addressHasCode(_receiveAddress))
{
VM vm(*_gas);
ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_receiveAddress), o_ms);
ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_receiveAddress), o_ms, _level);
bool revert = false;
try
{
auto out = vm.go(evm);
auto out = vm.go(evm, _onOp);
memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
if (o_suicides)
for (auto i: evm.suicides)
@ -1081,7 +1081,7 @@ bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u
return true;
}
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set<Address>* o_suicides, Manifest* o_ms)
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set<Address>* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
if (!_origin)
_origin = _sender;
@ -1102,13 +1102,13 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
// Execute init code.
VM vm(*_gas);
ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms);
ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level);
bool revert = false;
bytesConstRef out;
try
{
out = vm.go(evm);
out = vm.go(evm, _onOp);
if (o_ms)
o_ms->output = out.toBytes();
if (o_suicides)

4
libethereum/State.h

@ -304,12 +304,12 @@ private:
// We assume all instrinsic fees are paid up before this point.
/// Execute a contract-creation transaction.
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set<Address>* o_suicides = nullptr, Manifest* o_ms = nullptr);
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set<Address>* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Execute a call.
/// @a _gas points to the amount of gas to use for the call, and will lower it accordingly.
/// @returns false if the call ran out of gas before completion. true otherwise.
bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set<Address>* o_suicides = nullptr, Manifest* o_ms = nullptr);
bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set<Address>* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();

3
libevm/ExtVMFace.h

@ -22,6 +22,7 @@
#pragma once
#include <libethential/Common.h>
#include <libevmface/Instruction.h>
#include <libethcore/CommonEth.h>
#include <libethcore/BlockInfo.h>
@ -82,4 +83,6 @@ public:
std::set<Address> suicides; ///< Any accounts that have suicided.
};
typedef std::function<void(uint64_t /*steps*/, Instruction /*instr*/, unsigned /*newMemSize*/, bigint /*gasCost*/, void/*VM*/*, void/*ExtVM*/ const*)> OnOpFunc;
}

38
libevm/VM.h

@ -21,13 +21,7 @@
#pragma once
#define ETH_VMTRACE 1
#include <unordered_map>
#if ETH_VMTRACE
#include <sstream>
#include <libethential/Log.h>
#endif
#include <libethential/Exceptions.h>
#include <libethcore/CommonEth.h>
#include <libevmface/Instruction.h>
@ -73,7 +67,7 @@ public:
void reset(u256 _gas = 0);
template <class Ext>
bytesConstRef go(Ext& _ext, uint64_t _steps = (uint64_t)-1, std::map<u256, u256> const* _storage = nullptr);
bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1);
void require(u256 _n) { if (m_stack.size() < _n) throw StackTooSmall(_n, m_stack.size()); }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
@ -90,36 +84,15 @@ private:
u256s m_stack;
};
struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; };
}
// INLINE:
template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps, std::map<u256, u256> const* _storage)
template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
u256 nextPC = m_curPC + 1;
auto osteps = _steps;
for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1)
{
// TRACE
#if ETH_VMTRACE
{
std::ostringstream o;
o << std::endl << " STACK" << std::endl;
for (auto i: stack())
o << (h256)i << std::endl;
o << " MEMORY" << std::endl << memDump(memory());
if (_storage)
{
o << " STORAGE" << std::endl;
for (auto const& i: *_storage)
o << std::showbase << std::hex << i.first << ": " << i.second << std::endl;
}
eth::LogOutputStream<VMTraceChannel, false>(true) << o.str();
eth::LogOutputStream<VMTraceChannel, false>(false) << std::dec << " | #" << (osteps - _steps) << " | " << std::hex << std::setw(4) << std::setfill('0') << curPC() << " : " << c_instructionInfo.at((Instruction)_ext.getCode(curPC())).name << " | " << std::dec << gas() << " ]";
}
#endif
// INSTRUCTION...
Instruction inst = (Instruction)_ext.getCode(m_curPC);
@ -209,6 +182,9 @@ template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps,
if (newTempSize > m_temp.size())
runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32;
if (_onOp)
_onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : 0, runGas, this, &_ext);
if (m_gas < runGas)
{
// Out of gas!
@ -564,7 +540,7 @@ template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps,
if (_ext.balance(_ext.myAddress) >= endowment)
{
_ext.subBalance(endowment);
m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + initOff, initSize)));
m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
}
else
m_stack.push_back(0);
@ -593,7 +569,7 @@ template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps,
if (_ext.balance(_ext.myAddress) >= value)
{
_ext.subBalance(value);
m_stack.push_back(_ext.call(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize)));
m_stack.push_back(_ext.call(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize), _onOp));
}
else
m_stack.push_back(0);

4
test/vm.cpp

@ -62,7 +62,7 @@ public:
get<0>(addresses[_a]) += get<0>(addresses[myAddress]);
addresses.erase(myAddress);
}
h160 create(u256 _endowment, u256* _gas, bytesConstRef _init)
h160 create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFunc)
{
Address na = right160(sha3(rlpList(myAddress, get<1>(addresses[myAddress]))));
/* if (get<0>(addresses[myAddress]) >= _endowment)
@ -80,7 +80,7 @@ public:
return na;
}
bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out)
bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc)
{
/* if (get<0>(addresses[myAddress]) >= _value)
{

Loading…
Cancel
Save