Browse Source

Merge branch 'splitcode' into develop

Conflicts:
	eth/main.cpp
cl-refactor
Gav Wood 11 years ago
parent
commit
9861c4194e
  1. 4
      CMakeLists.txt
  2. 155
      alethzero/Main.ui
  3. 144
      alethzero/MainWin.cpp
  4. 8
      alethzero/MainWin.h
  5. 4
      eth/CMakeLists.txt
  6. 525
      eth/main.cpp
  7. 8
      libethereum/AddressState.cpp
  8. 29
      libethereum/AddressState.h
  9. 34
      libethereum/Client.cpp
  10. 10
      libethereum/Client.h
  11. 2
      libethereum/Common.h
  12. 3
      libethereum/CommonData.cpp
  13. 9
      libethereum/CommonData.h
  14. 2
      libethereum/Exceptions.h
  15. 36
      libethereum/ExtVMFace.h
  16. 32
      libethereum/FeeStructure.cpp
  17. 24
      libethereum/FeeStructure.h
  18. 447
      libethereum/Instruction.cpp
  19. 106
      libethereum/Instruction.h
  20. 5
      libethereum/PeerServer.cpp
  21. 2
      libethereum/PeerServer.h
  22. 11
      libethereum/RLP.h
  23. 280
      libethereum/State.cpp
  24. 82
      libethereum/State.h
  25. 25
      libethereum/Transaction.cpp
  26. 29
      libethereum/Transaction.h
  27. 8
      libethereum/VM.cpp
  28. 543
      libethereum/VM.h
  29. 5
      libethereum/vector_ref.h
  30. 1
      test/crypto.cpp
  31. 8
      test/txTest.cpp
  32. 136
      test/vm.cpp
  33. 11
      walleth/MainWin.cpp
  34. 4
      walleth/MainWin.h

4
CMakeLists.txt

@ -17,6 +17,10 @@ if ("x${TARGET_PLATFORM}" STREQUAL "x")
set(TARGET_PLATFORM "linux")
endif ()
if ("${TARGET_PLATFORM}" STREQUAL "linux")
set(CMAKE_THREAD_LIBS_INIT pthread)
endif ()
if ("${TARGET_PLATFORM}" STREQUAL "w64")
set(CMAKE_SYSTEM_NAME Windows)

155
alethzero/Main.ui

@ -390,19 +390,22 @@
</attribute>
<widget class="QWidget" name="dockWidgetContents_5">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label5">
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;To</string>
<string>&amp;Data</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>destination</cstring>
<cstring>data</cstring>
</property>
</widget>
</item>
@ -416,26 +419,39 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<item row="3" column="3">
<widget class="QComboBox" name="gasPriceUnits"/>
</item>
<item row="2" column="1" colspan="2">
<widget class="QSpinBox" name="value">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>1000</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Data</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
<string>&amp;To</string>
</property>
<property name="buddy">
<cstring>data</cstring>
<cstring>destination</cstring>
</property>
</widget>
</item>
<item row="4" column="0" colspan="4">
<item row="5" column="0" colspan="4">
<widget class="QSplitter" name="splitter_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -451,30 +467,78 @@
</widget>
</widget>
</item>
<item row="5" column="3">
<widget class="QPushButton" name="send">
<item row="3" column="2">
<widget class="QSpinBox" name="gasPrice">
<property name="prefix">
<string>@ </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>430000000</number>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="gas">
<property name="suffix">
<string> gas</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>430000000</number>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QComboBox" name="valueUnits"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&amp;Send</string>
<string>&amp;Gas</string>
</property>
<property name="buddy">
<cstring>gas</cstring>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="total">
<item row="4" column="1" colspan="3">
<widget class="QLabel" name="fee">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="destination">
<item row="6" column="3">
<widget class="QPushButton" name="send">
<property name="text">
<string>&amp;Send</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="3">
<widget class="QLabel" name="total">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="placeholderText">
<string>(Create Contract)</string>
<property name="text">
<string/>
</property>
</widget>
</item>
@ -491,29 +555,16 @@
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QComboBox" name="valueUnits"/>
</item>
<item row="3" column="1" colspan="3">
<widget class="QLabel" name="fee">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QSpinBox" name="value">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="destination">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>1000</number>
<property name="placeholderText">
<string>(Create Contract)</string>
</property>
</widget>
</item>
@ -803,18 +854,16 @@
<tabstop>calculatedName</tabstop>
<tabstop>value</tabstop>
<tabstop>valueUnits</tabstop>
<tabstop>gas</tabstop>
<tabstop>gasPrice</tabstop>
<tabstop>gasPriceUnits</tabstop>
<tabstop>data</tabstop>
<tabstop>code</tabstop>
<tabstop>send</tabstop>
<tabstop>idealPeers</tabstop>
<tabstop>port</tabstop>
<tabstop>clientName</tabstop>
<tabstop>nameReg</tabstop>
<tabstop>verbosity</tabstop>
<tabstop>transactionQueue</tabstop>
<tabstop>accounts</tabstop>
<tabstop>peers</tabstop>
<tabstop>log</tabstop>
<tabstop>ourAccounts</tabstop>
</tabstops>
<resources/>
<connections/>

144
alethzero/MainWin.cpp

@ -37,6 +37,8 @@ using eth::compileLisp;
using eth::disassemble;
using eth::formatBalance;
using eth::fromHex;
using eth::sha3;
using eth::left160;
using eth::right160;
using eth::simpleDebugOut;
using eth::toLog2;
@ -52,7 +54,6 @@ static void initUnits(QComboBox* _b)
{
for (auto n = (::uint)units().size(); n-- != 0; )
_b->addItem(QString::fromStdString(units()[n].second), n);
_b->setCurrentIndex(6);
}
Main::Main(QWidget *parent) :
@ -82,6 +83,8 @@ Main::Main(QWidget *parent) :
int pocnumber = QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1).toInt();
if (pocnumber == 3)
m_servers.push_back("54.201.28.117:30303");
else if (pocnumber == 4)
m_servers.push_back("54.72.31.55:30303");
else
{
connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r)
@ -96,7 +99,11 @@ Main::Main(QWidget *parent) :
#endif
on_verbosity_sliderMoved();
initUnits(ui->gasPriceUnits);
initUnits(ui->valueUnits);
ui->valueUnits->setCurrentIndex(6);
ui->gasPriceUnits->setCurrentIndex(4);
ui->gasPrice->setValue(10);
on_destination_textChanged();
statusBar()->addPermanentWidget(ui->balance);
@ -282,7 +289,7 @@ void Main::refresh(bool _override)
QString("%2 +> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(render(t.safeSender()))
.arg(render(right160(t.sha3())))
.arg(render(left160(sha3(rlpList(t.safeSender(), t.nonce)))))
.arg((unsigned)t.nonce);
ui->transactionQueue->addItem(s);
}
@ -308,7 +315,7 @@ void Main::refresh(bool _override)
QString(" %2 +> %3: %1 [%4]")
.arg(formatBalance(t.value).c_str())
.arg(render(t.safeSender()))
.arg(render(right160(t.sha3())))
.arg(render(left160(sha3(rlpList(t.safeSender(), t.nonce)))))
.arg((unsigned)t.nonce);
QListWidgetItem* txItem = new QListWidgetItem(s, ui->blocks);
txItem->setData(Qt::UserRole, QByteArray((char const*)h.data(), h.size));
@ -385,23 +392,32 @@ void Main::on_blocks_currentItemChanged()
{
unsigned txi = item->data(Qt::UserRole + 1).toInt();
Transaction tx(block[1][txi].data());
h256 th = tx.sha3();
auto ss = tx.safeSender();
h256 th = sha3(rlpList(ss, tx.nonce));
s << "<h3>" << th << "</h3>";
s << "<h4>" << h << "[<b>" << txi << "</b>]</h4>";
auto ss = tx.safeSender();
s << "<br/>From: <b>" << pretty(ss).toStdString() << "</b> " << ss;
if (tx.receiveAddress)
s << "<br/>To: <b>" << pretty(tx.receiveAddress).toStdString() << "</b> " << tx.receiveAddress;
if (tx.isCreation())
s << "<br/>Creates: <b>" << pretty(left160(th)).toStdString() << "</b> " << left160(th);
else
s << "<br/>Creates: <b>" << pretty(right160(th)).toStdString() << "</b> " << right160(th);
s << "<br/>To: <b>" << pretty(tx.receiveAddress).toStdString() << "</b> " << tx.receiveAddress;
s << "<br/>Value: <b>" << formatBalance(tx.value) << "</b>";
s << "&nbsp;&emsp;&nbsp;#<b>" << tx.nonce << "</b>";
if (tx.data.size())
s << "<br/>Gas price: <b>" << formatBalance(tx.gasPrice) << "</b>";
if (tx.isCreation())
{
s << "<br/>Init:";
s << "<br/>" << disassemble(tx.init);
s << "<br/>Code:";
s << "<br/>" << disassemble(tx.data);
}
else
{
s << "<br/>Data:&nbsp;&emsp;&nbsp;";
// for (auto i: tx.data)
// s << "0x<b>" << hex << i << "</b>&emsp;";
s << "</br>" << disassemble(tx.data);
s << "<br/>Gas: <b>" << tx.gas << "</b>";
s << "<br/>Data:&nbsp;&emsp;&nbsp; 0x..." << setw(2) << setfill('0') << hex;
unsigned c = 0;
for (auto i: tx.data)
s << i << (c % 8 ? "" : " ");
}
}
@ -423,42 +439,10 @@ void Main::on_contracts_currentItemChanged()
stringstream s;
auto mem = state().contractMemory(h);
u256 next = 0;
unsigned numerics = 0;
bool unexpectedNumeric = false;
for (auto i: mem)
{
if (next < i.first)
{
unsigned j;
for (j = 0; j <= numerics && next + j < i.first; ++j)
s << (j < numerics || unexpectedNumeric ? " 0" : " <b>STOP</b>");
unexpectedNumeric = false;
numerics -= min(numerics, j);
if (next + j < i.first)
s << " ...<br/>@" << showbase << hex << i.first << "&nbsp;&nbsp;&nbsp;&nbsp;";
}
else if (!next)
{
s << "@" << showbase << hex << i.first << "&nbsp;&nbsp;&nbsp;&nbsp;";
}
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--;
else
unexpectedNumeric = true;
s << " " << showbase << hex << i.second;
}
else
{
auto const& ii = iit->second;
s << " <b>" << ii.name << "</b>";
numerics = ii.additional;
}
next = i.first + 1;
}
for (auto const& i: mem)
s << "@" << showbase << hex << i.first << "&nbsp;&nbsp;&nbsp;&nbsp;" << showbase << hex << i.second << "<br/>";
s << "<br/>Code:";
s << "<br/>" << disassemble(state().contractCode(h));
ui->contractInfo->appendHtml(QString::fromStdString(s.str()));
}
m_client->unlock();
@ -505,27 +489,69 @@ void Main::on_destination_textChanged()
ui->calculatedName->setText("Unknown Address");
else
ui->calculatedName->setText("Create Contract");
updateFee();
on_data_textChanged();
// updateFee();
}
void Main::on_data_textChanged()
{
string code = ui->data->toPlainText().toStdString();
m_data = code[0] == '(' ? compileLisp(code, true) : assemble(code, true);
ui->code->setPlainText(QString::fromStdString(disassemble(m_data)));
if (isCreation())
{
string code = ui->data->toPlainText().toStdString();
m_init.clear();
m_data = compileLisp(code, true, m_init);
ui->code->setPlainText(QString::fromStdString(disassemble(m_data)) + "\n; Init:" + QString::fromStdString(disassemble(m_init)));
ui->gas->setMinimum((qint64)state().createGas(m_data.size() + m_init.size(), 0));
ui->gas->setEnabled(true);
}
else
{
string code = ui->data->toPlainText().replace(" ", "").replace("\n", "").replace("\t", "").toStdString();
try
{
m_data = fromHex(code);
}
catch (...)
{}
ui->code->setPlainText(QString::fromStdString(toHex(m_data)));
if (m_client->postState().isContractAddress(fromString(ui->destination->text())))
{
ui->gas->setMinimum((qint64)state().callGas(m_data.size(), 1));
ui->gas->setEnabled(true);
}
else
{
ui->gas->setValue((qint64)state().callGas(m_data.size()));
ui->gas->setEnabled(false);
}
}
updateFee();
}
bool Main::isCreation() const
{
return ui->destination->text().isEmpty()/* || !ui->destination->text().toInt()*/;
}
u256 Main::fee() const
{
return (ui->destination->text().isEmpty() || !ui->destination->text().toInt()) ? state().fee(m_data.size()) : state().fee();
return ui->gas->value() * gasPrice();
}
u256 Main::value() const
{
if (ui->valueUnits->currentIndex() == -1)
return 0;
return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first;
}
u256 Main::gasPrice() const
{
if (ui->gasPriceUnits->currentIndex() == -1)
return 0;
return ui->gasPrice->value() * units()[units().size() - 1 - ui->gasPriceUnits->currentIndex()].first;
}
u256 Main::total() const
{
return value() + fee();
@ -533,7 +559,7 @@ u256 Main::total() const
void Main::updateFee()
{
ui->fee->setText(QString("(fee: %1)").arg(formatBalance(fee()).c_str()));
ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str()));
auto totalReq = total();
ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str()));
@ -607,12 +633,14 @@ void Main::on_send_clicked()
u256 totalReq = value() + fee();
m_client->lock();
for (auto i: m_myKeys)
if (m_client->state().balance(i.address()) >= totalReq )
if (m_client->state().balance(i.address()) >= totalReq)
{
m_client->unlock();
Secret s = i.secret();
Address r = fromString(ui->destination->text());
m_client->transact(s, r, value(), m_data);
if (isCreation())
m_client->transact(s, value(), gasPrice(), ui->gas->value(), m_data, m_init);
else
m_client->transact(s, value(), gasPrice(), ui->gas->value(), fromString(ui->destination->text()), m_data);
refresh();
return;
}

8
alethzero/MainWin.h

@ -38,7 +38,10 @@ private slots:
void on_data_textChanged();
void on_idealPeers_valueChanged();
void on_value_valueChanged() { updateFee(); }
void on_gas_valueChanged() { updateFee(); }
void on_valueUnits_currentIndexChanged() { updateFee(); }
void on_gasPriceUnits_currentIndexChanged() { updateFee(); }
void on_gasPrice_valueChanged() { updateFee(); }
void on_log_doubleClicked();
void on_blocks_currentItemChanged();
void on_contracts_doubleClicked();
@ -63,9 +66,11 @@ private:
void readSettings();
void writeSettings();
bool isCreation() const;
eth::u256 fee() const;
eth::u256 total() const;
eth::u256 value() const;
eth::u256 gasPrice() const;
std::unique_ptr<Ui::Main> ui;
@ -78,7 +83,8 @@ private:
QStringList m_servers;
QVector<eth::KeyPair> m_myKeys;
bool m_keysChanged = false;
eth::u256s m_data;
eth::bytes m_data;
eth::bytes m_init;
eth::Address m_nameReg;
QNetworkAccessManager m_webCtrl;

4
eth/CMakeLists.txt

@ -16,16 +16,18 @@ if (${TARGET_PLATFORM} STREQUAL "w64")
target_link_libraries(eth shlwapi)
target_link_libraries(eth iphlpapi)
target_link_libraries(eth cryptopp)
target_link_libraries(eth ncurses)
target_link_libraries(eth boost_system-mt-s)
target_link_libraries(eth boost_filesystem-mt-s)
target_link_libraries(eth boost_thread_win32-mt-s)
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)
elseif (UNIX)
target_link_libraries(eth ncurses)
else ()
target_link_libraries(eth ${CRYPTOPP_LIBRARIES})
target_link_libraries(eth boost_system)
target_link_libraries(eth boost_filesystem)
target_link_libraries(eth ncurses)
find_package(Threads REQUIRED)
target_link_libraries(eth ${CMAKE_THREAD_LIBS_INIT})
endif ()

525
eth/main.cpp

@ -20,9 +20,12 @@
* Ethereum client.
*/
#include <ncurses.h>
#undef OK
#include <thread>
#include <chrono>
#include <fstream>
#include <iostream>
#include "Defaults.h"
#include "Client.h"
#include "PeerNetwork.h"
@ -75,8 +78,8 @@ void interactiveHelp()
{
cout
<< "Commands:" << endl
<< " netstart <port> Starts the network sybsystem on a specific port." << endl
<< " netstop Stops the network subsystem." << endl
<< " netstart <port> Starts the network sybsystem on a specific port." << endl
<< " netstop Stops the network subsystem." << endl
<< " connect <addr> <port> Connects to a specific peer." << endl
<< " minestart Starts mining." << endl
<< " minestop Stops mining." << endl
@ -85,10 +88,36 @@ void interactiveHelp()
<< " block Gives the current block height." << endl
<< " balance Gives the current balance." << endl
<< " peers List the peers that are connected" << endl
<< " transact <secret> <dest> <amount> Executes a given transaction." << endl
<< " send <dest> <amount> Executes a given transaction with current secret." << endl
<< " inspect <contract> Dumps a contract to <APPDATA>/<contract>.evm." << endl
<< " exit Exits the application." << endl;
<< " transact <secret> <dest> <amount> <gasPrice> <gas> <data> Executes a given transaction." << endl
<< " send <dest> <amount> <gasPrice> <gas> Executes a given transaction with current secret." << endl
<< " inspect <contract> Dumps a contract to <APPDATA>/<contract>.evm." << endl
<< " exit Exits the application." << endl;
}
string credits(bool _interactive = false)
{
std::ostringstream ccout;
ccout
<< "Ethereum (++) " << ETH_QUOTED(ETH_VERSION) << endl
<< " Code by Gav Wood, (c) 2013, 2014." << endl
<< " Based on a design by Vitalik Buterin." << endl << endl;
if (_interactive)
{
string vs = toString(ETH_QUOTED(ETH_VERSION));
vs = vs.substr(vs.find_first_of('.') + 1)[0];
int pocnumber = stoi(vs);
string m_servers;
if (pocnumber == 3)
m_servers = "54.201.28.117";
if (pocnumber == 4)
m_servers = "54.72.31.55";
ccout << "Type 'netstart 30303' to start networking" << endl;
ccout << "Type 'connect " << m_servers << " 30303' to connect" << endl;
ccout << "Type 'exit' to quit" << endl << endl;
}
return ccout.str();
}
void version()
@ -98,6 +127,133 @@ void version()
exit(0);
}
namespace nc
{
class nc_window_streambuf: public std::streambuf
{
public:
nc_window_streambuf(WINDOW* p, std::ostream& os, unsigned long cursesAttr = 0);
nc_window_streambuf(WINDOW* p, unsigned long _cursesAttr = 0);
nc_window_streambuf(nc_window_streambuf const& _rhs);
virtual ~nc_window_streambuf();
nc_window_streambuf& operator=(nc_window_streambuf const& _rhs);
virtual int overflow(int c);
virtual int sync();
private:
void copy(nc_window_streambuf const& _rhs);
WINDOW* m_pnl;
unsigned long m_flags;
std::ostream* m_os;
std::streambuf* m_old;
};
nc_window_streambuf::nc_window_streambuf(WINDOW * p, unsigned long _cursesAttr):
m_pnl(p),
m_flags(_cursesAttr),
m_os(0),
m_old(0)
{
// Tell parent class that we want to call overflow() for each
// input char:
setp(0, 0);
setg(0, 0, 0);
scrollok(p, true);
mvwinch(p, 0, 0);
}
nc_window_streambuf::nc_window_streambuf(WINDOW* _p, std::ostream& _os, unsigned long _cursesAttr):
m_pnl(_p),
m_flags(_cursesAttr),
m_os(&_os),
m_old(_os.rdbuf())
{
setp(0, 0);
setg(0, 0, 0);
_os.rdbuf(this);
scrollok(_p, true);
mvwinch(_p, 0, 0);
}
void nc_window_streambuf::copy(nc_window_streambuf const& _rhs)
{
if (this != &_rhs)
{
m_pnl = _rhs.m_pnl;
m_flags = _rhs.m_flags;
m_os = _rhs.m_os;
m_old = _rhs.m_old;
}
}
nc_window_streambuf::nc_window_streambuf(nc_window_streambuf const& _rhs):
std::streambuf()
{
copy(_rhs);
}
nc_window_streambuf& nc_window_streambuf::operator=(nc_window_streambuf const& _rhs)
{
copy(_rhs);
return *this;
}
nc_window_streambuf::~nc_window_streambuf()
{
if (m_os)
m_os->rdbuf(m_old);
}
int nc_window_streambuf::overflow(int c)
{
int ret = c;
if (c != EOF)
{
int x = 0;
int y = 0;
int mx = 0;
int my = 0;
getyx(m_pnl, y, x);
getmaxyx(m_pnl, my, mx);
if (y < 1)
y = 1;
if (x < 2)
x = 2;
if (x > mx - 4)
{
y++;
x = 2;
}
if (m_flags)
{
wattron(m_pnl, m_flags);
if (mvwaddch(m_pnl, y, x++, (chtype)c) == ERR)
ret = EOF;
wattroff(m_pnl, m_flags);
}
else if (mvwaddch(m_pnl, y, x++, (chtype)c) == ERR)
ret = EOF;
}
if (c == EOF) // || std::isspace(c)
if (sync() == EOF)
ret = EOF;
return ret;
}
int nc_window_streambuf::sync()
{
if (stdscr && m_pnl)
return (wrefresh(m_pnl) == ERR) ? EOF : 0;
return EOF;
}
}
int main(int argc, char** argv)
{
unsigned short listenPort = 30303;
@ -211,64 +367,133 @@ int main(int argc, char** argv)
if (!clientName.empty())
clientName += "/";
Client c("Ethereum(++)/" + clientName + "v" ETH_QUOTED(ETH_VERSION) "/" ETH_QUOTED(ETH_BUILD_TYPE) "/" ETH_QUOTED(ETH_BUILD_PLATFORM), coinbase, dbPath);
cout << credits();
if (interactive)
{
cout << "Ethereum (++)" << endl;
cout << " Code by Gav Wood, (c) 2013, 2014." << endl;
cout << " Based on a design by Vitalik Buterin." << endl << endl;
std::ostringstream ccout;
/* Initialize ncurses */
const char* chr;
char* str = new char[255];
int width;
int height;
int y = 0;
int x = 2;
string cmd;
WINDOW * mainwin, * consolewin, * logwin, * blockswin, * pendingwin, * contractswin, * peerswin;
if (!(mainwin = initscr()))
{
cerr << "Error initialising ncurses.";
return -1;
}
getmaxyx(mainwin, height, width);
int qwidth = width / 4 - 4;
nonl();
nocbreak();
timeout(30000);
echo();
keypad(mainwin, true);
logwin = newwin(height * 2 / 5 - 2, width, height * 3 / 5, 0);
nc::nc_window_streambuf outbuf(logwin, std::cout);
// nc::nc_window_streambuf errbuf( logwin, std::cerr );
g_logVerbosity = 1; // Force verbosity level for now
consolewin = newwin(height * 3 / 5, width / 4, 0, 0);
nc::nc_window_streambuf coutbuf(consolewin, ccout);
blockswin = newwin(height * 3 / 5, width / 4, 0, width / 4);
pendingwin = newwin(height * 1 / 5, width / 4, 0, width * 2 / 4);
peerswin = newwin(height * 2 / 5, width / 4, height * 1 / 5, width * 2 / 4);
contractswin = newwin(height * 3 / 5, width / 4, 0, width * 3 / 4);
int vl = height * 3 / 5 - 4;
wsetscrreg(consolewin, 1, vl);
wsetscrreg(blockswin, 1, vl);
wsetscrreg(pendingwin, 1, vl);
wsetscrreg(peerswin, 1, vl);
wsetscrreg(contractswin, 1, vl);
mvwprintw(mainwin, 1, x, "> ");
wresize(mainwin, 3, width);
mvwin(mainwin, height - 3, 0);
wmove(mainwin, 1, 4);
if (!remoteHost.empty())
c.startNetwork(listenPort, remoteHost, remotePort, mode, peers, publicIP, upnp);
while (true)
{
cout << "> " << flush;
std::string cmd;
cin >> cmd;
wclrtobot(consolewin);
wclrtobot(pendingwin);
wclrtobot(peerswin);
wclrtobot(contractswin);
ccout << credits(true);
// Prompt
wmove(mainwin, 1, 4);
getstr(str);
string s(str);
istringstream iss(s);
iss >> cmd;
// Address
ccout << "Address:" << endl;
chr = toHex(us.address().asArray()).c_str();
ccout << chr << endl << endl;
mvwprintw(mainwin, 1, x, "> ");
clrtoeol();
if (s.length() > 1)
{
ccout << "> ";
ccout << str << endl;
}
if (cmd == "netstart")
{
eth::uint port;
cin >> port;
iss >> port;
c.startNetwork((short)port);
}
else if (cmd == "connect")
{
string addr;
eth::uint port;
cin >> addr >> port;
iss >> addr >> port;
c.connect(addr, (short)port);
}
else if (cmd == "netstop")
{
c.stopNetwork();
}
else if (cmd == "minestart")
{
c.startMining();
}
else if (cmd == "minestop")
{
c.stopMining();
}
else if (cmd == "address")
{
cout << endl;
cout << "Current address: " + toHex(us.address().asArray()) << endl;
cout << "===" << endl;
ccout << "Current address:" << endl;
const char* addchr = toHex(us.address().asArray()).c_str();
ccout << addchr << endl;
}
else if (cmd == "secret")
{
cout << endl;
cout << "Current secret: " + toHex(us.secret().asArray()) << endl;
cout << "===" << endl;
ccout << "Current secret:" << endl;
const char* addchr = toHex(us.secret().asArray()).c_str();
ccout << addchr << endl;
}
else if (cmd == "block")
{
eth::uint n = c.blockChain().details().number;
cout << endl;
cout << "Current block # " << n << endl;
cout << "===" << endl;
ccout << "Current block # ";
const char* addchr = toString(n).c_str();
ccout << addchr << endl;
}
else if (cmd == "peers")
{
@ -280,93 +505,225 @@ int main(int argc, char** argv)
else if (cmd == "balance")
{
u256 balance = c.state().balance(us.address());
cout << endl;
cout << "Current balance: ";
cout << balance << endl;
cout << "===" << endl;
ccout << "Current balance:" << endl;
const char* addchr = toString(balance).c_str();
ccout << addchr << endl;
}
else if (cmd == "transact")
{
string sechex;
string rechex;
u256 amount;
cin >> sechex >> rechex >> amount;
u256 gasPrice;
u256 gas;
iss >> sechex >> rechex >> amount >> gasPrice >> gas;
Secret secret = h256(fromHex(sechex));
Address dest = h160(fromHex(rechex));
c.transact(secret, dest, amount);
bytes data;
c.transact(secret, amount, gasPrice, gas, dest, data);
}
else if (cmd == "send")
{
string rechex;
u256 amount;
cin >> rechex >> amount;
u256 gasPrice;
u256 gas;
iss >> rechex >> amount >> gasPrice >> gas;
Address dest = h160(fromHex(rechex));
c.transact(us.secret(), dest, amount);
c.transact(us.secret(), amount, gasPrice, gas, dest, bytes());
}
else if (cmd == "inspect")
{
string rechex;
cin >> rechex;
c.lock();
auto h = h160(fromHex(rechex));
iss >> rechex;
stringstream s;
auto mem = c.state().contractMemory(h);
u256 next = 0;
unsigned numerics = 0;
bool unexpectedNumeric = false;
for (auto i: mem)
if (rechex.length() != 40)
cout << "Invalid address length" << endl;
else
{
if (next < i.first)
{
unsigned j;
for (j = 0; j <= numerics && next + j < i.first; ++j)
s << (j < numerics || unexpectedNumeric ? " 0" : " STOP");
unexpectedNumeric = false;
numerics -= min(numerics, j);
if (next + j < i.first)
s << "\n@" << 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...
c.lock();
auto h = h160(fromHex(rechex));
stringstream s;
auto mem = c.state().contractMemory(h);
u256 next = 0;
unsigned numerics = 0;
bool unexpectedNumeric = false;
for (auto const& i: mem)
{
if (numerics)
numerics--;
if (next < i.first)
{
unsigned j;
for (j = 0; j <= numerics && next + j < i.first; ++j)
s << (j < numerics || unexpectedNumeric ? " 0" : " STOP");
unexpectedNumeric = false;
numerics -= min(numerics, j);
if (next + j < i.first)
s << "\n@" << 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--;
else
unexpectedNumeric = true;
s << " " << showbase << hex << i.second;
}
else
unexpectedNumeric = true;
s << " " << showbase << hex << i.second;
}
else
{
auto const& ii = iit->second;
s << " " << ii.name;
numerics = ii.additional;
{
auto const& ii = iit->second;
s << " " << ii.name;
numerics = ii.additional;
}
next = i.first + 1;
}
next = i.first + 1;
}
string outFile = getDataDir() + "/" + rechex + ".evm";
ofstream ofs;
ofs.open(outFile, ofstream::binary);
ofs.write(s.str().c_str(), s.str().length());
ofs.close();
string outFile = getDataDir() + "/" + rechex + ".evm";
ofstream ofs;
ofs.open(outFile, ofstream::binary);
ofs.write(s.str().c_str(), s.str().length());
ofs.close();
c.unlock();
c.unlock();
}
}
else if (cmd == "help")
{
interactiveHelp();
}
else if (cmd == "exit")
{
break;
// Clear cmd at each pass
cmd = "";
// Blocks
auto const& st = c.state();
auto const& bc = c.blockChain();
y = 0;
for (auto h = bc.currentHash(); h != bc.genesisHash(); h = bc.details(h).parent)
{
auto d = bc.details(h);
string s = "# " + std::to_string(d.number) + ' ' + toString(h); // .abridged();
y += 1;
mvwaddnstr(blockswin, y, x, s.c_str(), qwidth);
for (auto const& i: RLP(bc.block(h))[1])
{
Transaction t(i.data());
string ss;
ss = t.receiveAddress ?
" " + toString(toHex(t.safeSender().asArray())) + " " + (st.isContractAddress(t.receiveAddress) ? '*' : '-') + "> " + toString(t.receiveAddress) + ": " + toString(formatBalance(t.value)) + " [" + toString((unsigned)t.nonce) + "]":
" " + toString(toHex(t.safeSender().asArray())) + " +> " + toString(right160(t.sha3())) + ": " + toString(formatBalance(t.value)) + " [" + toString((unsigned)t.nonce) + "]";
y += 1;
mvwaddnstr(blockswin, y, x, ss.c_str(), qwidth - 2);
if (y > height * 3 / 5 - 2)
break;
}
if (y > height * 3 / 5 - 2)
break;
}
// Pending
y = 0;
for (Transaction const& t: c.pending())
{
string ss;
if (t.receiveAddress)
ss = toString(toHex(t.safeSender().asArray())) + " " + (st.isContractAddress(t.receiveAddress) ? '*' : '-') + "> " + toString(t.receiveAddress) + ": " + toString(formatBalance(t.value)) + " " + " [" + toString((unsigned)t.nonce) + "]";
else
ss = toString(toHex(t.safeSender().asArray())) + " +> " + toString(right160(t.sha3())) + ": " + toString(formatBalance(t.value)) + "[" + toString((unsigned)t.nonce) + "]";
y += 1;
mvwaddnstr(pendingwin, y, x, ss.c_str(), qwidth);
if (y > height * 3 / 5 - 4)
break;
}
// Contracts
auto acs = st.addresses();
y = 0;
for (auto n = 0; n < 2; ++n)
for (auto const& i: acs)
{
auto r = i.first;
if (st.isContractAddress(r))
{
string ss;
ss = toString(r) + " : " + toString(formatBalance(i.second)) + " [" + toString((unsigned)st.transactionsFrom(i.first)) + "]";
y += 1;
mvwaddnstr(contractswin, y, x, ss.c_str(), qwidth);
if (y > height * 3 / 5 - 2)
break;
}
}
// Peers
y = 0;
string psc;
string pss;
auto cp = c.peers();
psc = toString(cp.size()) + " peer(s)";
for (PeerInfo const& i: cp)
{
pss = toString(chrono::duration_cast<chrono::milliseconds>(i.lastPing).count()) + " ms - " + i.host + ":" + toString(i.port) + " - " + i.clientVersion;
y += 1;
mvwaddnstr(peerswin, y, x, pss.c_str(), qwidth);
if (y > height * 2 / 5 - 4)
break;
}
box(consolewin, 0, 0);
box(blockswin, 0, 0);
box(pendingwin, 0, 0);
box(peerswin, 0, 0);
box(contractswin, 0, 0);
box(mainwin, 0, 0);
// Balance
mvwprintw(consolewin, 0, x, "Balance: ");
u256 balance = c.state().balance(us.address());
chr = toString(balance).c_str();
mvwprintw(consolewin, 0, 11, chr);
wmove(consolewin, 1, x);
// Block
mvwprintw(blockswin, 0, x, "Block # ");
eth::uint n = c.blockChain().details().number;
chr = toString(n).c_str();
mvwprintw(blockswin, 0, 10, chr);
mvwprintw(pendingwin, 0, x, "Pending");
mvwprintw(contractswin, 0, x, "Contracts");
// Peers
mvwprintw(peerswin, 0, x, "Peers: ");
chr = toString(c.peers().size()).c_str();
mvwprintw(peerswin, 0, 9, chr);
wrefresh(consolewin);
wrefresh(blockswin);
wrefresh(pendingwin);
wrefresh(peerswin);
wrefresh(contractswin);
wrefresh(mainwin);
}
delwin(contractswin);
delwin(peerswin);
delwin(pendingwin);
delwin(blockswin);
delwin(consolewin);
delwin(logwin);
delwin(mainwin);
endwin();
refresh();
}
else
{

8
libethereum/AddressState.cpp

@ -20,6 +20,14 @@
*/
#include "AddressState.h"
#include "CommonEth.h"
using namespace std;
using namespace eth;
AddressState::AddressState(u256 _balance, u256 _nonce, bytesConstRef _code):
m_type(AddressType::Contract),
m_balance(_balance),
m_nonce(_nonce),
m_isComplete(true),
m_code(_code.toBytes())
{}

29
libethereum/AddressState.h

@ -37,14 +37,10 @@ enum class AddressType
class AddressState
{
public:
AddressState(): m_type(AddressType::Dead), m_balance(0), m_nonce(0), m_haveMemory(false) {}
AddressState(u256 _balance, u256 _nonce, AddressType _type = AddressType::Normal): m_type(_type), m_balance(_balance), m_nonce(_nonce), m_haveMemory(true) {}
AddressState(u256 _balance, u256 _nonce, h256 _contractRoot): m_type(AddressType::Contract), m_balance(_balance), m_nonce(_nonce), m_haveMemory(false), m_contractRoot(_contractRoot) {}
AddressState(u256 _balance, u256 _nonce, u256s _memory): m_type(AddressType::Contract), m_balance(_balance), m_nonce(_nonce), m_haveMemory(true)
{
for (unsigned i = 0; i < _memory.size(); ++i)
m_memory[(u256)i] = _memory[i];
}
AddressState(): m_type(AddressType::Dead), m_balance(0), m_nonce(0), m_isComplete(false) {}
AddressState(u256 _balance, u256 _nonce, AddressType _type = AddressType::Normal): m_type(_type), m_balance(_balance), m_nonce(_nonce), m_isComplete(true) {}
AddressState(u256 _balance, u256 _nonce, h256 _contractRoot, h256 _codeHash): m_type(AddressType::Contract), m_balance(_balance), m_nonce(_nonce), m_isComplete(false), m_contractRoot(_contractRoot), m_codeHash(_codeHash) {}
AddressState(u256 _balance, u256 _nonce, bytesConstRef _code);
void incNonce() { m_nonce++; }
void addBalance(bigint _i) { m_balance = (u256)((bigint)m_balance + _i); }
@ -55,20 +51,25 @@ public:
u256 const& balance() const { return m_balance; }
u256& nonce() { return m_nonce; }
u256 const& nonce() const { return m_nonce; }
bool haveMemory() const { return m_haveMemory; }
std::map<u256, u256>& setHaveMemory() { assert(m_type == AddressType::Contract); m_haveMemory = true; m_contractRoot = h256(); return m_memory; }
h256 oldRoot() const { assert(!haveMemory()); return m_contractRoot; }
std::map<u256, u256>& memory() { assert(m_type == AddressType::Contract && haveMemory()); return m_memory; }
std::map<u256, u256> const& memory() const { assert(m_type == AddressType::Contract && haveMemory()); return m_memory; }
bool isComplete() const { return m_isComplete; }
std::map<u256, u256>& setIsComplete(bytesConstRef _code) { assert(m_type == AddressType::Contract); m_isComplete = true; m_contractRoot = h256(); m_code = _code.toBytes(); return m_memory; }
h256 oldRoot() const { assert(!isComplete()); return m_contractRoot; }
h256 codeHash() const { assert(m_codeHash); return m_codeHash; }
std::map<u256, u256>& memory() { assert(m_type == AddressType::Contract && isComplete()); return m_memory; }
std::map<u256, u256> const& memory() const { assert(m_type == AddressType::Contract && isComplete()); return m_memory; }
bytes const& code() const { assert(m_type == AddressType::Contract && isComplete()); return m_code; }
bool freshCode() const { return !m_codeHash && m_isComplete; }
private:
AddressType m_type;
u256 m_balance;
u256 m_nonce;
bool m_haveMemory;
bool m_isComplete;
h256 m_contractRoot;
h256 m_codeHash; // if 0 and m_isComplete, has been created and needs to be inserted.
// TODO: change to unordered_map.
std::map<u256, u256> m_memory;
bytes m_code;
};
}

34
libethereum/Client.cpp

@ -92,7 +92,16 @@ void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHo
{
if (m_net.get())
return;
m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _listenPort, _mode, _publicIP, _upnp));
try
{
m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _listenPort, _mode, _publicIP, _upnp));
}
catch (std::exception const&)
{
// Probably already have the port open.
m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _mode));
}
m_net->setIdealPeerCount(_peers);
if (_seedHost.size())
connect(_seedHost, _port);
@ -131,13 +140,15 @@ void Client::stopMining()
m_doMine = false;
}
void Client::transact(Secret _secret, Address _dest, u256 _amount, u256s _data)
void Client::transact(Secret _secret, u256 _value, u256 _gasPrice, u256 _gas, Address _dest, bytes const& _data)
{
lock_guard<recursive_mutex> l(m_lock);
Transaction t;
t.nonce = m_postMine.transactionsFrom(toAddress(_secret));
t.value = _value;
t.gasPrice = _gasPrice;
t.gas = _gas;
t.receiveAddress = _dest;
t.value = _amount;
t.data = _data;
t.sign(_secret);
cnote << "New transaction " << t;
@ -145,6 +156,23 @@ void Client::transact(Secret _secret, Address _dest, u256 _amount, u256s _data)
m_changed = true;
}
void Client::transact(Secret _secret, u256 _endowment, u256 _gasPrice, u256 _gas, bytes const& _code, bytes const& _init)
{
lock_guard<recursive_mutex> l(m_lock);
Transaction t;
t.nonce = m_postMine.transactionsFrom(toAddress(_secret));
t.value = _endowment;
t.gasPrice = _gasPrice;
t.gas = _gas;
t.receiveAddress = Address();
t.data = _code;
t.init = _init;
t.sign(_secret);
cnote << "New transaction " << t;
m_tq.attemptImport(t.rlp());
m_changed = true;
}
void Client::work()
{
bool changed = false;

10
libethereum/Client.h

@ -83,8 +83,14 @@ public:
/// Destructor.
~Client();
/// Executes the given transaction.
void transact(Secret _secret, Address _dest, u256 _amount, u256s _data = u256s());
/// Submits the given transaction.
void transact(Secret _secret, u256 _value, u256 _gasPrice, u256 _gas, Address _dest, bytes const& _data);
/// Submits a new contract.
void transact(Secret _secret, u256 _endowment, u256 _gasPrice, u256 _gas, bytes const& _code, bytes const& _init);
/// Makes the given call. Nothing is recorded into the state. TODO
// bytes call(Secret _secret, u256 _amount, u256 _gasPrice, Address _dest, u256 _gas, bytes _data = bytes());
/// Requires transactions involving this address be queued for inspection.
void setInterest(Address _dest);

2
libethereum/Common.h

@ -24,7 +24,7 @@
#pragma once
// define version
#define ETH_VERSION 0.3.11
#define ETH_VERSION 0.4.1
// way to many uint to size_t warnings in 32 bit build
#ifdef _M_IX86

3
libethereum/CommonData.cpp

@ -72,8 +72,7 @@ int eth::fromHex(char _i)
bytes eth::fromHex(std::string const& _s)
{
assert(_s.size() % 2 == 0);
if (_s.size() < 2)
if (_s.size() % 2 || _s.size() < 2)
return bytes();
uint s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0;
std::vector<uint8_t> ret;

9
libethereum/CommonData.h

@ -143,6 +143,15 @@ std::string randomWord();
// General datatype convenience functions.
/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero.
template <class _T>
inline uint bytesRequired(_T _i)
{
uint i = 0;
for (; _i != 0; ++i, _i >>= 8) {}
return i;
}
/// Trims a given number of elements from the front of a collection.
/// Only works for POD element types.
template <class _T>

2
libethereum/Exceptions.h

@ -26,9 +26,11 @@ class VMException: public Exception {};
class StepsDone: public VMException {};
class BreakPointHit: public VMException {};
class BadInstruction: public VMException {};
class OutOfGas: public VMException {};
class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; };
class GasPriceTooLow: public Exception {};
class NoSuchContract: public Exception {};
class ContractAddressCollision: public Exception {};
class FeeTooSmall: public Exception {};

36
libethereum/ExtVMFace.h

@ -35,49 +35,51 @@ class ExtVMFace
public:
ExtVMFace() {}
ExtVMFace(FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
fees(_fees),
ExtVMFace(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
previousBlock(_previousBlock),
currentBlock(_currentBlock),
currentNumber(_currentNumber)
{}
ExtVMFace(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
ExtVMFace(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, bytesConstRef _code, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
myAddress(_myAddress),
txSender(_txSender),
txValue(_txValue),
gasPrice(_gasPrice),
txData(_txData),
fees(_fees),
code(_code),
previousBlock(_previousBlock),
currentBlock(_currentBlock),
currentNumber(_currentNumber)
{}
#pragma warning(push)
#pragma warning(disable: 4100)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
u256 store(u256 _n) { return 0; }
void setStore(u256 _n, u256 _v) {}
void mktx(Transaction& _t) {}
u256 balance(Address _a) { return 0; }
void payFee(bigint _fee) {}
u256 txCount(Address _a) { return 0; }
u256 extro(Address _a, u256 _pos) { return 0; }
u256 extroPrice(Address _a) { return 0; }
void suicide(Address _a) {}
byte getCode(u256 _n) const { return _n < code.size() ? code[(unsigned)_n] : 0; }
u256 store(u256 _n) { return 0; }
void setStore(u256 _n, u256 _v) {}
u256 balance(Address _a) { return 0; }
void subBalance(u256 _a) {}
u256 txCount(Address _a) { return 0; }
void suicide(Address _a) {}
h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, bytesConstRef _init) { return h160(); }
bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _tx) { return false; }
#pragma GCC diagnostic pop
#pragma warning(pop)
Address myAddress;
Address txSender;
Address origin;
u256 txValue;
u256s txData;
FeeStructure fees;
u256 gasPrice;
bytesConstRef txData;
bytesConstRef code;
BlockInfo previousBlock; ///< The current block's information.
BlockInfo currentBlock; ///< The current block's information.
BlockInfo currentBlock; ///< The current block's information.
uint currentNumber;
};

32
libethereum/FeeStructure.cpp

@ -24,26 +24,12 @@
using namespace std;
using namespace eth;
u256 const c_stepFee = 1;
u256 const c_dataFee = 20;
u256 const c_memoryFee = 5;
u256 const c_extroFee = 40;
u256 const c_cryptoFee = 20;
u256 const c_newContractFee = 100;
u256 const c_txFee = 100;
void FeeStructure::setMultiplier(u256 _x)
{
m_stepFee = c_stepFee * _x;
m_dataFee = c_dataFee * _x;
m_memoryFee = c_memoryFee * _x;
m_extroFee = c_extroFee * _x;
m_cryptoFee = c_cryptoFee * _x;
m_newContractFee = c_newContractFee * _x;
m_txFee = c_txFee * _x;
}
u256 FeeStructure::multiplier() const
{
return m_stepFee / c_stepFee;
}
u256 const eth::c_stepGas = 1;
u256 const eth::c_balanceGas = 20;
u256 const eth::c_sha3Gas = 20;
u256 const eth::c_sloadGas = 20;
u256 const eth::c_sstoreGas = 100;
u256 const eth::c_createGas = 100;
u256 const eth::c_callGas = 20;
u256 const eth::c_memoryGas = 1;
u256 const eth::c_txDataGas = 5;

24
libethereum/FeeStructure.h

@ -26,20 +26,14 @@
namespace eth
{
/**
* The collection of fee amounts that makes up the fee structure.
*/
struct FeeStructure
{
void setMultiplier(u256 _x); ///< Set the current block multiplier.
u256 multiplier() const; ///< @returns the current block multiplier.
u256 m_stepFee;
u256 m_dataFee;
u256 m_memoryFee;
u256 m_extroFee;
u256 m_cryptoFee;
u256 m_newContractFee;
u256 m_txFee;
};
extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL.
extern u256 const c_balanceGas; ///< Once per BALANCE operation.
extern u256 const c_sha3Gas; ///< Once per SHA3 operation.
extern u256 const c_sloadGas; ///< Once per SLOAD operation.
extern u256 const c_sstoreGas; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once).
extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction.
extern u256 const c_callGas; ///< Once per CALL operation & message call transaction.
extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
extern u256 const c_txDataGas; ///< Per byte of data attached to a message-call transaction. NOTE: Not payable on data of calls between transactions.
}

447
libethereum/Instruction.cpp

@ -41,45 +41,76 @@ const std::map<std::string, Instruction> eth::c_instructions =
{ "EXP", Instruction::EXP },
{ "NEG", Instruction::NEG },
{ "LT", Instruction::LT },
{ "LE", Instruction::LE },
{ "GT", Instruction::GT },
{ "GE", Instruction::GE },
{ "EQ", Instruction::EQ },
{ "NOT", Instruction::NOT },
{ "MYADDRESS", Instruction::MYADDRESS },
{ "TXSENDER", Instruction::TXSENDER },
{ "TXVALUE", Instruction::TXVALUE },
{ "TXDATAN", Instruction::TXDATAN },
{ "TXDATA", Instruction::TXDATA },
{ "BLK_PREVHASH", Instruction::BLK_PREVHASH },
{ "BLK_COINBASE", Instruction::BLK_COINBASE },
{ "BLK_TIMESTAMP", Instruction::BLK_TIMESTAMP },
{ "BLK_NUMBER", Instruction::BLK_NUMBER },
{ "BLK_DIFFICULTY", Instruction::BLK_DIFFICULTY },
{ "BLK_NONCE", Instruction::BLK_NONCE },
{ "BASEFEE", Instruction::BASEFEE },
{ "SHA256", Instruction::SHA256 },
{ "RIPEMD160", Instruction::RIPEMD160 },
{ "ECMUL", Instruction::ECMUL },
{ "ECADD", Instruction::ECADD },
{ "ECSIGN", Instruction::ECSIGN },
{ "ECRECOVER", Instruction::ECRECOVER },
{ "ECVALID", Instruction::ECVALID },
{ "AND", Instruction::AND },
{ "OR", Instruction::OR },
{ "XOR", Instruction::XOR },
{ "BYTE", Instruction::BYTE },
{ "SHA3", Instruction::SHA3 },
{ "PUSH", Instruction::PUSH },
{ "ADDRESS", Instruction::ADDRESS },
{ "BALANCE", Instruction::BALANCE },
{ "ORIGIN", Instruction::ORIGIN },
{ "CALLER", Instruction::CALLER },
{ "CALLVALUE", Instruction::CALLVALUE },
{ "CALLDATALOAD", Instruction::CALLDATALOAD },
{ "CALLDATASIZE", Instruction::CALLDATASIZE },
{ "BASEFEE", Instruction::GASPRICE },
{ "PREVHASH", Instruction::PREVHASH },
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
{ "NUMBER", Instruction::NUMBER },
{ "DIFFICULTY", Instruction::DIFFICULTY },
{ "GASLIMIT", Instruction::GASLIMIT },
{ "POP", Instruction::POP },
{ "DUP", Instruction::DUP },
{ "SWAP", Instruction::SWAP },
{ "MLOAD", Instruction::MLOAD },
{ "MSTORE", Instruction::MSTORE },
{ "MSTORE8", Instruction::MSTORE8 },
{ "SLOAD", Instruction::SLOAD },
{ "SSTORE", Instruction::SSTORE },
{ "JMP", Instruction::JMP },
{ "JMPI", Instruction::JMPI },
{ "IND", Instruction::IND },
{ "EXTRO", Instruction::EXTRO },
{ "BALANCE", Instruction::BALANCE },
{ "MKTX", Instruction::MKTX },
{ "JUMP", Instruction::JUMP },
{ "JUMPI", Instruction::JUMPI },
{ "PC", Instruction::PC },
{ "MEMSIZE", Instruction::MEMSIZE },
{ "GAS", Instruction::GAS },
{ "PUSH1", Instruction::PUSH1 },
{ "PUSH2", Instruction::PUSH2 },
{ "PUSH3", Instruction::PUSH3 },
{ "PUSH4", Instruction::PUSH4 },
{ "PUSH5", Instruction::PUSH5 },
{ "PUSH6", Instruction::PUSH6 },
{ "PUSH7", Instruction::PUSH7 },
{ "PUSH8", Instruction::PUSH8 },
{ "PUSH9", Instruction::PUSH9 },
{ "PUSH10", Instruction::PUSH10 },
{ "PUSH11", Instruction::PUSH11 },
{ "PUSH12", Instruction::PUSH12 },
{ "PUSH13", Instruction::PUSH13 },
{ "PUSH14", Instruction::PUSH14 },
{ "PUSH15", Instruction::PUSH15 },
{ "PUSH16", Instruction::PUSH16 },
{ "PUSH17", Instruction::PUSH17 },
{ "PUSH18", Instruction::PUSH18 },
{ "PUSH19", Instruction::PUSH19 },
{ "PUSH20", Instruction::PUSH20 },
{ "PUSH21", Instruction::PUSH21 },
{ "PUSH22", Instruction::PUSH22 },
{ "PUSH23", Instruction::PUSH23 },
{ "PUSH24", Instruction::PUSH24 },
{ "PUSH25", Instruction::PUSH25 },
{ "PUSH26", Instruction::PUSH26 },
{ "PUSH27", Instruction::PUSH27 },
{ "PUSH28", Instruction::PUSH28 },
{ "PUSH29", Instruction::PUSH29 },
{ "PUSH30", Instruction::PUSH30 },
{ "PUSH31", Instruction::PUSH31 },
{ "PUSH32", Instruction::PUSH32 },
{ "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL },
{ "RETURN", Instruction::RETURN },
{ "SUICIDE", Instruction::SUICIDE }
};
@ -96,45 +127,76 @@ const std::map<Instruction, InstructionInfo> eth::c_instructionInfo =
{ Instruction::EXP, { "EXP", 0, 2, 1 } },
{ Instruction::NEG, { "NEG", 0, 1, 1 } },
{ Instruction::LT, { "LT", 0, 2, 1 } },
{ Instruction::LE, { "LE", 0, 2, 1 } },
{ Instruction::GT, { "GT", 0, 2, 1 } },
{ Instruction::GE, { "GE", 0, 2, 1 } },
{ Instruction::EQ, { "EQ", 0, 2, 1 } },
{ Instruction::NOT, { "NOT", 0, 1, 1 } },
{ Instruction::MYADDRESS, { "MYADDRESS", 0, 0, 1 } },
{ Instruction::TXSENDER, { "TXSENDER", 0, 0, 1 } },
{ Instruction::TXVALUE, { "TXVALUE", 0, 0, 1 } },
{ Instruction::TXDATAN, { "TXDATAN", 0, 0, 1 } },
{ Instruction::TXDATA, { "TXDATA", 0, 1, 1 } },
{ Instruction::BLK_PREVHASH, { "BLK_PREVHASH", 0, 0, 1 } },
{ Instruction::BLK_COINBASE, { "BLK_COINBASE", 0, 0, 1 } },
{ Instruction::BLK_TIMESTAMP, { "BLK_TIMESTAMP", 0, 0, 1 } },
{ Instruction::BLK_NUMBER, { "BLK_NUMBER", 0, 0, 1 } },
{ Instruction::BLK_DIFFICULTY, { "BLK_DIFFICULTY", 0, 0, 1 } },
{ Instruction::BLK_NONCE, { "BLK_NONCE", 0, 0, 1 } },
{ Instruction::BASEFEE, { "BASEFEE", 0, 0, 1 } },
{ Instruction::SHA256, { "SHA256", 0, -1, 1 } },
{ Instruction::RIPEMD160, { "RIPEMD160", 0, -1, 1 } },
{ Instruction::ECMUL, { "ECMUL", 0, 3, 2 } },
{ Instruction::ECADD, { "ECADD", 0, 4, 2 } },
{ Instruction::ECSIGN, { "ECSIGN", 0, 2, 3 } },
{ Instruction::ECRECOVER, { "ECRECOVER", 0, 4, 2 } },
{ Instruction::ECVALID, { "ECVALID", 0, 2, 1 } },
{ Instruction::SHA3, { "SHA3", 0, -1, 1 } },
{ Instruction::PUSH, { "PUSH", 1, 0, 1 } },
{ Instruction::ADD, { "ADD", 0, 2, 1 } },
{ Instruction::OR, { "OR", 0, 2, 1 } },
{ Instruction::XOR, { "XOR", 0, 2, 1 } },
{ Instruction::BYTE, { "BYTE", 0, 2, 1 } },
{ Instruction::SHA3, { "SHA3", 0, 2, 1 } },
{ Instruction::ADDRESS, { "ADDRESS", 0, 0, 1 } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1 } },
{ Instruction::ORIGIN, { "ORIGIN", 0, 1, 1 } },
{ Instruction::CALLER, { "CALLER", 0, 0, 1 } },
{ Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1 } },
{ Instruction::CALLDATALOAD, { "CALLDATALOAD", 0, 1, 1 } },
{ Instruction::CALLDATASIZE, { "CALLDATASIZE", 0, 0, 1 } },
{ Instruction::GASPRICE, { "BASEFEE", 0, 0, 1 } },
{ Instruction::PREVHASH, { "PREVHASH", 0, 0, 1 } },
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1 } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1 } },
{ Instruction::NUMBER, { "NUMBER", 0, 0, 1 } },
{ Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1 } },
{ Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1 } },
{ Instruction::POP, { "POP", 0, 1, 0 } },
{ Instruction::DUP, { "DUP", 0, 1, 2 } },
{ Instruction::SWAP, { "SWAP", 0, 2, 2 } },
{ Instruction::MLOAD, { "MLOAD", 0, 1, 1 } },
{ Instruction::MSTORE, { "MSTORE", 0, 2, 0 } },
{ Instruction::MSTORE8, { "MSTORE8", 0, 2, 0 } },
{ Instruction::SLOAD, { "SLOAD", 0, 1, 1 } },
{ Instruction::SSTORE, { "SSTORE", 0, 2, 0 } },
{ Instruction::JMP, { "JMP", 0, 1, 0 } },
{ Instruction::JMPI, { "JMPI", 0, 2, 0 } },
{ Instruction::IND, { "IND", 0, 0, 1 } },
{ Instruction::EXTRO, { "EXTRO", 0, 2, 1 } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1 } },
{ Instruction::MKTX, { "MKTX", 0, -3, 0 } },
{ Instruction::JUMP, { "JUMP", 0, 1, 0 } },
{ Instruction::JUMPI, { "JUMPI", 0, 2, 0 } },
{ Instruction::PC, { "PC", 0, 0, 1 } },
{ Instruction::MEMSIZE, { "MEMSIZE", 0, 0, 1 } },
{ Instruction::GAS, { "GAS", 0, 0, 1 } },
{ Instruction::PUSH1, { "PUSH1", 1, 0, 1 } },
{ Instruction::PUSH2, { "PUSH2", 2, 0, 1 } },
{ Instruction::PUSH3, { "PUSH3", 3, 0, 1 } },
{ Instruction::PUSH4, { "PUSH4", 4, 0, 1 } },
{ Instruction::PUSH5, { "PUSH5", 5, 0, 1 } },
{ Instruction::PUSH6, { "PUSH6", 6, 0, 1 } },
{ Instruction::PUSH7, { "PUSH7", 7, 0, 1 } },
{ Instruction::PUSH8, { "PUSH8", 8, 0, 1 } },
{ Instruction::PUSH9, { "PUSH9", 9, 0, 1 } },
{ Instruction::PUSH10, { "PUSH10", 10, 0, 1 } },
{ Instruction::PUSH11, { "PUSH11", 11, 0, 1 } },
{ Instruction::PUSH12, { "PUSH12", 12, 0, 1 } },
{ Instruction::PUSH13, { "PUSH13", 13, 0, 1 } },
{ Instruction::PUSH14, { "PUSH14", 14, 0, 1 } },
{ Instruction::PUSH15, { "PUSH15", 15, 0, 1 } },
{ Instruction::PUSH16, { "PUSH16", 16, 0, 1 } },
{ Instruction::PUSH17, { "PUSH17", 17, 0, 1 } },
{ Instruction::PUSH18, { "PUSH18", 18, 0, 1 } },
{ Instruction::PUSH19, { "PUSH19", 19, 0, 1 } },
{ Instruction::PUSH20, { "PUSH20", 20, 0, 1 } },
{ Instruction::PUSH21, { "PUSH21", 21, 0, 1 } },
{ Instruction::PUSH22, { "PUSH22", 22, 0, 1 } },
{ Instruction::PUSH23, { "PUSH23", 23, 0, 1 } },
{ Instruction::PUSH24, { "PUSH24", 24, 0, 1 } },
{ Instruction::PUSH25, { "PUSH25", 25, 0, 1 } },
{ Instruction::PUSH26, { "PUSH26", 26, 0, 1 } },
{ Instruction::PUSH27, { "PUSH27", 27, 0, 1 } },
{ Instruction::PUSH28, { "PUSH28", 28, 0, 1 } },
{ Instruction::PUSH29, { "PUSH29", 29, 0, 1 } },
{ Instruction::PUSH30, { "PUSH30", 30, 0, 1 } },
{ Instruction::PUSH31, { "PUSH31", 31, 0, 1 } },
{ Instruction::PUSH32, { "PUSH32", 32, 0, 1 } },
{ Instruction::CREATE, { "CREATE", 0, 3, 1 } },
{ Instruction::CALL, { "CALL", 0, 7, 1 } },
{ Instruction::RETURN, { "RETURN", 0, 2, 0 } },
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} }
};
@ -174,9 +236,9 @@ static u256 readNumeric(string _v, bool _quiet)
return 0;
}
u256s eth::assemble(std::string const& _code, bool _quiet)
bytes eth::assemble(std::string const& _code, bool _quiet)
{
u256s ret;
bytes ret;
map<string, unsigned> known;
map<unsigned, string> req;
char const* d = _code.data();
@ -184,11 +246,11 @@ u256s eth::assemble(std::string const& _code, bool _quiet)
while (d != e)
{
// skip to next token
for (; d != e && !isalnum(*d) && *d != '_' && *d != ':' && *d != '"'; ++d) {}
for (; d != e && !isalnum(*d) && *d != '_' /*&& *d != ':' && *d != '"'*/; ++d) {}
if (d == e)
break;
if (*d == '"')
/* if (*d == '"')
{
string s = readQuoted(d, e);
if (s.size() > 32)
@ -202,15 +264,15 @@ u256s eth::assemble(std::string const& _code, bool _quiet)
memset(valHash.data() + s.size(), 0, 32 - s.size());
ret.push_back((u256)valHash);
}
else
else*/
{
char const* s = d;
for (; d != e && (isalnum(*d) || *d == '_' || *d == ':' || *d == '"'); ++d) {}
for (; d != e && (isalnum(*d) || *d == '_'/* || *d == ':' || *d == '"'*/); ++d) {}
string t = string(s, d - s);
if (isdigit(t[0]))
ret.push_back(readNumeric(t, _quiet));
else if (t.back() == ':')
ret.push_back((byte)readNumeric(t, _quiet));
/* else if (t.back() == ':')
known[t.substr(0, t.size() - 1)] = (unsigned)ret.size();
else
{
@ -222,7 +284,7 @@ u256s eth::assemble(std::string const& _code, bool _quiet)
req[(unsigned)ret.size()] = t;
ret.push_back(0);
}
}
}*/
}
}
for (auto i: req)
@ -235,12 +297,41 @@ u256s eth::assemble(std::string const& _code, bool _quiet)
return ret;
}
static void appendCode(u256s& o_code, vector<unsigned>& o_locs, u256s _code, vector<unsigned>& _locs)
/// @returns the number of addition bytes required for the PUSH.
static void increaseLocation(bytes& o_code, unsigned _pos, unsigned _inc)
{
assert(o_code[_pos] == (byte)Instruction::PUSH4);
bytesRef r(&o_code[1 + _pos], 4);
toBigEndian(fromBigEndian<uint32_t>(bytesConstRef(&o_code[1 + _pos], 4)) + _inc, r);
}
static void pushLocation(bytes& o_code, uint32_t _locationValue)
{
o_code.push_back((byte)Instruction::PUSH4);
o_code.resize(o_code.size() + 4);
bytesRef r(&o_code[o_code.size() - 4], 4);
toBigEndian(_locationValue, r);
}
static unsigned pushLiteral(bytes& o_code, u256 _literalValue)
{
unsigned br = max<unsigned>(1, bytesRequired(_literalValue));
o_code.push_back((byte)Instruction::PUSH1 + br - 1);
o_code.resize(o_code.size() + br);
for (unsigned i = 0; i < br; ++i)
{
o_code[o_code.size() - 1 - i] = (byte)(_literalValue & 0xff);
_literalValue >>= 8;
}
return br + 1;
}
static void appendCode(bytes& o_code, vector<unsigned>& o_locs, bytes _code, vector<unsigned>& _locs)
{
o_locs.reserve(o_locs.size() + _locs.size());
for (auto i: _locs)
{
_code[i] += (u256)o_code.size();
increaseLocation(_code, i, (unsigned)o_code.size());
o_locs.push_back(i + (unsigned)o_code.size());
}
o_code.reserve(o_code.size() + _code.size());
@ -248,10 +339,10 @@ static void appendCode(u256s& o_code, vector<unsigned>& o_locs, u256s _code, vec
o_code.push_back(i);
}
static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s& o_code, vector<unsigned>& o_locs)
static int compileLispFragment(char const*& d, char const* e, bool _quiet, bytes& o_code, vector<unsigned>& o_locs)
{
std::map<std::string, Instruction> const c_arith = { { "+", Instruction::ADD }, { "-", Instruction::SUB }, { "*", Instruction::MUL }, { "/", Instruction::DIV }, { "%", Instruction::MOD } };
std::map<std::string, Instruction> const c_binary = { { "<", Instruction::LT }, { "<=", Instruction::LE }, { ">", Instruction::GT }, { ">=", Instruction::GE }, { "=", Instruction::EQ }, { "!=", Instruction::NOT } };
std::map<std::string, pair<Instruction, bool>> const c_binary = { { "<", { Instruction::LT, false } }, { "<=", { Instruction::GT, true } }, { ">", { Instruction::GT, false } }, { ">=", { Instruction::LT, true } }, { "=", { Instruction::EQ, false } }, { "!=", { Instruction::EQ, true } } };
std::map<std::string, Instruction> const c_unary = { { "!", Instruction::NOT } };
std::set<char> const c_allowed = { '+', '-', '*', '/', '%', '<', '>', '=', '!' };
@ -321,9 +412,9 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
bool bareLoad = true;
if (exec)
{
u256s codes;
bytes codes;
vector<unsigned> locs;
if (compileLispFragment(d, e, _quiet, codes, locs))
if (compileLispFragment(d, e, _quiet, codes, locs) != -1)
{
appendCode(o_code, o_locs, codes, locs);
while (compileLispFragment(d, e, _quiet, codes, locs) != -1)
@ -332,10 +423,9 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
bareLoad = false;
}
}
o_code.push_back(Instruction::PUSH);
o_code.push_back(literalValue);
pushLiteral(o_code, literalValue);
if (exec)
o_code.push_back(bareLoad ? Instruction::SLOAD : Instruction::SSTORE);
o_code.push_back(bareLoad ? (byte)Instruction::SLOAD : (byte)Instruction::SSTORE);
}
else
{
@ -343,7 +433,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
if (t == "IF")
{
// Compile all the code...
u256s codes[4];
bytes codes[4];
vector<unsigned> locs[4];
for (int i = 0; i < 3; ++i)
{
@ -356,40 +446,38 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
if (compileLispFragment(d, e, _quiet, codes[3], locs[3]) != -1)
return false;
// First fragment - predicate
appendCode(o_code, o_locs, codes[0], locs[0]);
// Push the positive location.
o_code.push_back(Instruction::PUSH);
unsigned posLocation = (unsigned)o_code.size();
o_locs.push_back(posLocation);
o_code.push_back(0);
pushLocation(o_code, 0);
// First fragment - predicate
appendCode(o_code, o_locs, codes[0], locs[0]);
// Jump to positive if true.
o_code.push_back(Instruction::JMPI);
// Jump to negative if false.
o_code.push_back((byte)Instruction::JUMPI);
// Second fragment - negative.
appendCode(o_code, o_locs, codes[2], locs[2]);
// Jump to end after negative.
o_code.push_back(Instruction::PUSH);
unsigned endLocation = (unsigned)o_code.size();
o_locs.push_back(endLocation);
o_code.push_back(0);
o_code.push_back(Instruction::JMP);
pushLocation(o_code, 0);
o_code.push_back((byte)Instruction::JUMP);
// Third fragment - positive.
o_code[posLocation] = o_code.size();
increaseLocation(o_code, posLocation, o_code.size());
appendCode(o_code, o_locs, codes[1], locs[1]);
// At end now.
o_code[endLocation] = o_code.size();
increaseLocation(o_code, endLocation, o_code.size());
}
else if (t == "WHEN" || t == "UNLESS")
{
outs = 0;
// Compile all the code...
u256s codes[3];
bytes codes[3];
vector<unsigned> locs[3];
for (int i = 0; i < 2; ++i)
{
@ -398,36 +486,35 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
return false;
if (i == 1)
for (int j = 0; j < o; ++j)
codes[i].push_back(Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on).
codes[i].push_back((byte)Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on).
}
if (compileLispFragment(d, e, _quiet, codes[2], locs[2]) != -1)
return false;
// First fragment - predicate
appendCode(o_code, o_locs, codes[0], locs[0]);
if (t == "WHEN")
o_code.push_back((byte)Instruction::NOT);
// Push the positive location.
o_code.push_back(Instruction::PUSH);
unsigned endLocation = (unsigned)o_code.size();
o_locs.push_back(endLocation);
o_code.push_back(0);
// First fragment - predicate
appendCode(o_code, o_locs, codes[0], locs[0]);
pushLocation(o_code, 0);
// Jump to end...
if (t == "WHEN")
o_code.push_back(Instruction::NOT);
o_code.push_back(Instruction::JMPI);
o_code.push_back((byte)Instruction::JUMPI);
// Second fragment - negative.
appendCode(o_code, o_locs, codes[1], locs[1]);
// At end now.
o_code[endLocation] = o_code.size();
increaseLocation(o_code, endLocation, o_code.size());
}
else if (t == "FOR")
{
outs = 0;
// Compile all the code...
u256s codes[3];
bytes codes[3];
vector<unsigned> locs[3];
for (int i = 0; i < 2; ++i)
{
@ -436,50 +523,48 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
return false;
if (i == 1)
for (int j = 0; j < o; ++j)
codes[i].push_back(Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on).
codes[i].push_back((byte)Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on).
}
if (compileLispFragment(d, e, _quiet, codes[2], locs[2]) != -1)
return false;
unsigned startLocation = (unsigned)o_code.size();
// First fragment - predicate
appendCode(o_code, o_locs, codes[0], locs[0]);
o_code.push_back((byte)Instruction::NOT);
// Push the positive location.
o_code.push_back(Instruction::PUSH);
unsigned endInsertion = (unsigned)o_code.size();
o_locs.push_back(endInsertion);
o_code.push_back(0);
// First fragment - predicate
appendCode(o_code, o_locs, codes[0], locs[0]);
pushLocation(o_code, 0);
// Jump to positive if true.
o_code.push_back(Instruction::NOT);
o_code.push_back(Instruction::JMPI);
o_code.push_back((byte)Instruction::JUMPI);
// Second fragment - negative.
appendCode(o_code, o_locs, codes[1], locs[1]);
// Jump to end after negative.
o_code.push_back(Instruction::PUSH);
o_locs.push_back((unsigned)o_code.size());
o_code.push_back(startLocation);
o_code.push_back(Instruction::JMP);
pushLocation(o_code, startLocation);
o_code.push_back((byte)Instruction::JUMP);
// At end now.
o_code[endInsertion] = o_code.size();
increaseLocation(o_code, endInsertion, o_code.size());
}
else if (t == "SEQ")
{
while (d != e)
{
u256s codes;
bytes codes;
vector<unsigned> locs;
outs = 0;
int o;
if ((o = compileLispFragment(d, e, _quiet, codes, locs)) > -1)
{
for (int i = 0; i < outs; ++i)
o_code.push_back(Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on).
o_code.push_back((byte)Instruction::POP); // pop additional items off stack for the previous item (final item's returns get left on).
outs = o;
appendCode(o_code, o_locs, codes, locs);
}
@ -491,14 +576,14 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
{
if (exec)
{
vector<pair<u256s, vector<unsigned>>> codes(1);
vector<pair<bytes, vector<unsigned>>> codes(1);
int totalArgs = 0;
while (d != e)
{
int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second);
if (o < 1)
break;
codes.push_back(pair<u256s, vector<unsigned>>());
codes.push_back(pair<bytes, vector<unsigned>>());
totalArgs += o;
}
if (totalArgs < 2)
@ -513,12 +598,9 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
{
appendCode(o_code, o_locs, it->first, it->second);
if (i == datan)
{
o_code.push_back(Instruction::PUSH);
o_code.push_back(datan);
}
pushLocation(o_code, datan);
}
o_code.push_back(Instruction::MKTX);
o_code.push_back((byte)Instruction::CALL);
outs = 0;
}
}
@ -526,7 +608,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
{
while (d != e)
{
u256s codes;
bytes codes;
vector<unsigned> locs;
outs = 0;
int o;
@ -541,7 +623,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
}
else if (t == "AND")
{
vector<u256s> codes;
vector<bytes> codes;
vector<vector<unsigned>> locs;
while (d != e)
{
@ -567,25 +649,23 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
if (codes.size() > 1)
{
o_code.push_back(Instruction::PUSH);
o_code.push_back(0);
pushLiteral(o_code, 0);
for (unsigned i = 1; i < codes.size(); ++i)
{
// Check if true - predicate
appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]);
// Push the false location.
o_code.push_back(Instruction::PUSH);
ends.push_back((unsigned)o_code.size());
o_locs.push_back(ends.back());
o_code.push_back(0);
// Check if true - predicate
appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]);
pushLocation(o_code, 0);
// Jump to end...
o_code.push_back(Instruction::NOT);
o_code.push_back(Instruction::JMPI);
o_code.push_back((byte)Instruction::NOT);
o_code.push_back((byte)Instruction::JUMPI);
}
o_code.push_back(Instruction::POP);
o_code.push_back((byte)Instruction::POP);
}
// Check if true - predicate
@ -593,11 +673,11 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
// At end now.
for (auto i: ends)
o_code[i] = o_code.size();
increaseLocation(o_code, i, o_code.size());
}
else if (t == "OR")
{
vector<u256s> codes;
vector<bytes> codes;
vector<vector<unsigned>> locs;
while (d != e)
{
@ -621,24 +701,22 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
if (codes.size() > 1)
{
o_code.push_back(Instruction::PUSH);
o_code.push_back(1);
pushLiteral(o_code, 1);
for (unsigned i = 1; i < codes.size(); ++i)
{
// Check if true - predicate
appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]);
// Push the false location.
o_code.push_back(Instruction::PUSH);
ends.push_back((unsigned)o_code.size());
o_locs.push_back(ends.back());
o_code.push_back(0);
// Check if true - predicate
appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]);
pushLocation(o_code, 0);
// Jump to end...
o_code.push_back(Instruction::JMPI);
o_code.push_back((byte)Instruction::JUMPI);
}
o_code.push_back(Instruction::POP);
o_code.push_back((byte)Instruction::POP);
}
// Check if true - predicate
@ -646,7 +724,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
// At end now.
for (auto i: ends)
o_code[i] = o_code.size();
increaseLocation(o_code, i, o_code.size());
}
else
{
@ -655,14 +733,14 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
{
if (exec)
{
vector<pair<u256s, vector<unsigned>>> codes(1);
vector<pair<bytes, vector<unsigned>>> codes(1);
int totalArgs = 0;
while (d != e)
{
int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second);
if (o < 1)
break;
codes.push_back(pair<u256s, vector<unsigned>>());
codes.push_back(pair<bytes, vector<unsigned>>());
totalArgs += o;
}
int ea = c_instructionInfo.at(it->second).args;
@ -674,13 +752,13 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
for (auto it = codes.rbegin(); it != codes.rend(); ++it)
appendCode(o_code, o_locs, it->first, it->second);
o_code.push_back((u256)it->second);
o_code.push_back((byte)it->second);
outs = c_instructionInfo.at(it->second).ret;
}
else
{
o_code.push_back(Instruction::PUSH);
o_code.push_back(it->second);
o_code.push_back((byte)Instruction::PUSH1);
o_code.push_back((byte)it->second);
}
}
else
@ -688,42 +766,39 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
auto it = c_arith.find(t);
if (it != c_arith.end())
{
int i = 0;
vector<pair<bytes, vector<unsigned>>> codes(1);
int totalArgs = 0;
while (d != e)
{
u256s codes;
vector<unsigned> locs;
int o = compileLispFragment(d, e, _quiet, codes, locs);
if (o == -1)
{
if (!i)
cwarn << "Expected at least one argument to operation" << t;
break;
}
if (o == 0)
{
cwarn << "null operation given to " << t;
int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second);
if (o < 1)
break;
}
appendCode(o_code, o_locs, codes, locs);
i += o;
for (; i > 1; --i)
o_code.push_back((u256)it->second);
codes.push_back(pair<bytes, vector<unsigned>>());
totalArgs += o;
}
codes.pop_back();
if (!totalArgs)
{
cwarn << "Expected at least one argument to operation" << t;
break;
}
for (auto jt = codes.rbegin(); jt != codes.rend(); ++jt)
appendCode(o_code, o_locs, jt->first, jt->second);
o_code.push_back((byte)it->second);
}
else
{
auto it = c_binary.find(t);
if (it != c_binary.end())
{
vector<pair<u256s, vector<unsigned>>> codes(1);
vector<pair<bytes, vector<unsigned>>> codes(1);
int totalArgs = 0;
while (d != e)
{
int o = compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second);
if (o < 1)
break;
codes.push_back(pair<u256s, vector<unsigned>>());
codes.push_back(pair<bytes, vector<unsigned>>());
totalArgs += o;
}
codes.pop_back();
@ -733,18 +808,18 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
cwarn << "Expected two arguments to binary operator" << t << "; got" << totalArgs << ".";
break;
}
for (auto it = codes.rbegin(); it != codes.rend(); ++it)
appendCode(o_code, o_locs, it->first, it->second);
if (it->second == Instruction::NOT)
o_code.push_back(Instruction::EQ);
o_code.push_back((u256)it->second);
for (auto jt = codes.rbegin(); jt != codes.rend(); ++jt)
appendCode(o_code, o_locs, jt->first, jt->second);
if (it->second.second)
o_code.push_back((byte)Instruction::NOT);
o_code.push_back((byte)it->second.first);
}
else
{
auto it = c_unary.find(t);
if (it != c_unary.end())
{
vector<pair<u256s, vector<unsigned>>> codes(1);
vector<pair<bytes, vector<unsigned>>> codes(1);
int totalArgs = 0;
while (d != e)
{
@ -752,7 +827,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
if (o == -1)
break;
totalArgs += o;
codes.push_back(pair<u256s, vector<unsigned>>());
codes.push_back(pair<bytes, vector<unsigned>>());
}
codes.pop_back();
// int i = (int)codes.size();
@ -763,7 +838,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
}
for (auto it = codes.rbegin(); it != codes.rend(); ++it)
appendCode(o_code, o_locs, it->first, it->second);
o_code.push_back(it->second);
o_code.push_back((byte)it->second);
}
else if (!_quiet)
cwarn << "Unknown assembler token" << t;
@ -781,29 +856,31 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
return -1;
}
u256s eth::compileLisp(std::string const& _code, bool _quiet)
bytes eth::compileLisp(std::string const& _code, bool _quiet, bytes& _init)
{
char const* d = _code.data();
char const* e = _code.data() + _code.size();
u256s ret;
bytes ret;
vector<unsigned> locs;
compileLispFragment(d, e, _quiet, ret, locs);
locs.clear();
compileLispFragment(d, e, _quiet, _init, locs);
return ret;
}
string eth::disassemble(u256s const& _mem)
string eth::disassemble(bytes const& _mem)
{
stringstream ret;
uint numerics = 0;
for (auto it = _mem.begin(); it != _mem.end(); ++it)
{
u256 n = *it;
auto iit = c_instructionInfo.find((Instruction)(uint)n);
if (numerics || iit == c_instructionInfo.end() || (u256)(uint)iit->first != n) // not an instruction or expecting an argument...
byte n = *it;
auto iit = c_instructionInfo.find((Instruction)n);
if (numerics || iit == c_instructionInfo.end() || (byte)iit->first != n) // not an instruction or expecting an argument...
{
if (numerics)
numerics--;
ret << "0x" << hex << n << " ";
ret << "0x" << hex << (int)n << " ";
}
else
{

106
libethereum/Instruction.h

@ -42,46 +42,84 @@ enum class Instruction: uint8_t
EXP,
NEG,
LT,
LE,
GT,
GE,
EQ,
NOT,
MYADDRESS, ///< pushes the transaction sender
TXSENDER, ///< pushes the transaction sender
TXVALUE , ///< pushes the transaction value
TXDATAN, ///< pushes the number of data items
TXDATA, ///< pops one item and pushes data item S[-1], or zero if index out of range
BLK_PREVHASH, ///< pushes the hash of the previous block (NOT the current one since that's impossible!)
BLK_COINBASE, ///< pushes the coinbase of the current block
BLK_TIMESTAMP, ///< pushes the timestamp of the current block
BLK_NUMBER, ///< pushes the current block number
BLK_DIFFICULTY, ///< pushes the difficulty of the current block
BLK_NONCE,
BASEFEE,
SHA256 = 0x20,
RIPEMD160,
ECMUL,
ECADD,
ECSIGN,
ECRECOVER,
ECVALID,
SHA3,
PUSH = 0x30,
POP,
AND = 0x10,
OR,
XOR,
BYTE,
SHA3 = 0x20,
ADDRESS = 0x30,
BALANCE,
ORIGIN,
CALLER,
CALLVALUE,
CALLDATALOAD,
CALLDATASIZE,
GASPRICE,
PREVHASH = 0x40,
COINBASE,
TIMESTAMP,
NUMBER,
DIFFICULTY,
GASLIMIT,
POP = 0x50,
DUP,
SWAP,
MLOAD,
MSTORE,
MSTORE8,
SLOAD,
SSTORE,
JMP,
JMPI,
IND,
EXTRO,
BALANCE,
MKTX,
SUICIDE = 0x3f
JUMP,
JUMPI,
PC,
MEMSIZE,
GAS,
PUSH1 = 0x60,
PUSH2,
PUSH3,
PUSH4,
PUSH5,
PUSH6,
PUSH7,
PUSH8,
PUSH9,
PUSH10,
PUSH11,
PUSH12,
PUSH13,
PUSH14,
PUSH15,
PUSH16,
PUSH17,
PUSH18,
PUSH19,
PUSH20,
PUSH21,
PUSH22,
PUSH23,
PUSH24,
PUSH25,
PUSH26,
PUSH27,
PUSH28,
PUSH29,
PUSH30,
PUSH31,
PUSH32,
CREATE = 0xf0,
CALL,
RETURN,
SUICIDE = 0xff
};
/// Information structure for a particular instruction.
@ -100,12 +138,12 @@ extern const std::map<Instruction, InstructionInfo> c_instructionInfo;
extern const std::map<std::string, Instruction> c_instructions;
/// Convert from simple EVM assembly language to EVM code.
u256s assemble(std::string const& _code, bool _quiet = false);
bytes assemble(std::string const& _code, bool _quiet = false);
/// Convert from EVM code to simple EVM assembly language.
std::string disassemble(u256s const& _mem);
std::string disassemble(bytes const& _mem);
/// Compile a Low-level Lisp-like Language program into EVM-code.
u256s compileLisp(std::string const& _code, bool _quiet = false);
bytes compileLisp(std::string const& _code, bool _quiet, bytes& _init);
}

5
libethereum/PeerServer.cpp

@ -71,10 +71,11 @@ PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch,
clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full");
}
PeerServer::PeerServer(std::string const& _clientVersion, unsigned int _networkId, NodeMode _m):
PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m):
m_clientVersion(_clientVersion),
m_mode(_m),
m_listenPort(0),
m_chain(&_ch),
m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)),
m_socket(m_ioService),
m_key(KeyPair::create()),
@ -95,7 +96,7 @@ PeerServer::~PeerServer()
unsigned PeerServer::protocolVersion()
{
return 8;
return 10;
}
void PeerServer::determinePublic(string const& _publicAddress, bool _upnp)

2
libethereum/PeerServer.h

@ -43,7 +43,7 @@ public:
/// Start server, listening for connections on the given port.
PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, unsigned short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true);
/// Start server, but don't listen.
PeerServer(std::string const& _clientVersion, unsigned int _networkId, NodeMode _m = NodeMode::Full);
PeerServer(std::string const& _clientVersion, BlockChain const& _ch, unsigned int _networkId, NodeMode _m = NodeMode::Full);
~PeerServer();
static unsigned protocolVersion();

11
libethereum/RLP.h

@ -283,8 +283,9 @@ public:
RLPStream& append(RLP const& _rlp, uint _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); }
/// Appends a sequence of data to the stream as a list.
template <class _T> RLPStream& append(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T> RLPStream& append(std::vector<_T> const& _s) { return appendVector(_s); }
template <class _T, size_t S> RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T> RLPStream& appendVector(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
/// Appends a list.
RLPStream& appendList(uint _items);
@ -324,14 +325,6 @@ private:
*(b--) = (byte)_i;
}
/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero.
template <class _T> static uint bytesRequired(_T _i)
{
uint i = 0;
for (; _i != 0; ++i, _i >>= 8) {}
return i;
}
/// Our output byte stream.
bytes m_out;

280
libethereum/State.cpp

@ -72,7 +72,6 @@ State::State(Address _coinbaseAddress, Overlay const& _db):
m_ourAddress(_coinbaseAddress)
{
m_blockReward = 1500 * finney;
m_fees.setMultiplier(100 * szabo);
secp256k1_start();
@ -98,7 +97,6 @@ State::State(State const& _s):
m_currentBlock(_s.m_currentBlock),
m_currentNumber(_s.m_currentNumber),
m_ourAddress(_s.m_ourAddress),
m_fees(_s.m_fees),
m_blockReward(_s.m_blockReward)
{
}
@ -114,15 +112,19 @@ State& State::operator=(State const& _s)
m_currentBlock = _s.m_currentBlock;
m_currentNumber = _s.m_currentNumber;
m_ourAddress = _s.m_ourAddress;
m_fees = _s.m_fees;
m_blockReward = _s.m_blockReward;
return *this;
}
void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const
{
auto it = m_cache.find(_a);
if (it == m_cache.end())
ensureCached(m_cache, _a, _requireMemory, _forceCreate);
}
void State::ensureCached(std::map<Address, AddressState>& _cache, Address _a, bool _requireMemory, bool _forceCreate) const
{
auto it = _cache.find(_a);
if (it == _cache.end())
{
// populate basic info.
string stateBack = m_state.at(_a);
@ -135,16 +137,16 @@ void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) con
else if (state.itemCount() == 2)
s = AddressState(state[0].toInt<u256>(), state[1].toInt<u256>());
else
s = AddressState(state[0].toInt<u256>(), state[1].toInt<u256>(), state[2].toHash<h256>());
s = AddressState(state[0].toInt<u256>(), state[1].toInt<u256>(), state[2].toHash<h256>(), state[3].toHash<h256>());
bool ok;
tie(it, ok) = m_cache.insert(make_pair(_a, s));
tie(it, ok) = _cache.insert(make_pair(_a, s));
}
if (_requireMemory && !it->second.haveMemory())
if (_requireMemory && !it->second.isComplete())
{
// Populate memory.
assert(it->second.type() == AddressType::Contract);
TrieDB<h256, Overlay> memdb(const_cast<Overlay*>(&m_db), it->second.oldRoot()); // promise we won't alter the overlay! :)
map<u256, u256>& mem = it->second.setHaveMemory();
map<u256, u256>& mem = it->second.setIsComplete(bytesConstRef(m_db.lookup(it->second.codeHash())));
for (auto const& i: memdb)
mem[i.first] = RLP(i.second).toInt<u256>();
}
@ -570,7 +572,7 @@ u256 State::contractMemory(Address _id, u256 _memory) const
auto it = m_cache.find(_id);
if (it == m_cache.end() || it->second.type() != AddressType::Contract)
return 0;
else if (it->second.haveMemory())
else if (it->second.isComplete())
{
auto mit = it->second.memory().find(_memory);
if (mit == it->second.memory().end())
@ -591,137 +593,203 @@ map<u256, u256> const& State::contractMemory(Address _contract) const
return m_cache[_contract].memory();
}
bytes const& State::contractCode(Address _contract) const
{
if (!isContractAddress(_contract))
return EmptyBytes;
ensureCached(_contract, true, true);
return m_cache[_contract].code();
}
void State::execute(bytesConstRef _rlp)
{
// Entry point for a user-executed transaction.
Transaction t(_rlp);
executeBare(t, t.sender());
// Add to the user-originated transactions that we've executed.
// NOTE: Here, contract-originated transactions will not get added to the transaction list.
// If this is wrong, move this line into execute(Transaction const& _t, Address _sender) and
// don't forget to allow unsigned transactions in the tx list if they concur with the script execution.
m_transactions.push_back(t);
m_transactionSet.insert(t.sha3());
}
auto sender = t.sender();
void State::applyRewards(Addresses const& _uncleAddresses)
{
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
// Avoid invalid transactions.
auto nonceReq = transactionsFrom(sender);
if (t.nonce != nonceReq)
{
addBalance(i, m_blockReward * 3 / 4);
r += m_blockReward / 8;
clog(StateChat) << "Invalid Nonce.";
throw InvalidNonce(nonceReq, t.nonce);
}
addBalance(m_currentBlock.coinbaseAddress, r);
}
void State::unapplyRewards(Addresses const& _uncleAddresses)
{
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
// Don't like transactions whose gas price is too low. NOTE: this won't stay here forever - it's just until we get a proper gas proce discovery protocol going.
if (t.gasPrice < 10 * szabo)
{
subBalance(i, m_blockReward * 3 / 4);
r += m_blockReward / 8;
clog(StateChat) << "Offered gas-price is too low.";
throw GasPriceTooLow();
}
subBalance(m_currentBlock.coinbaseAddress, r);
}
void State::executeBare(Transaction const& _t, Address _sender)
{
#if ETH_DEBUG
commit();
clog(StateChat) << "State:" << rootHash();
clog(StateChat) << "Executing TX:" << _t;
#endif
// Entry point for a contract-originated transaction.
// Check gas cost is enough.
u256 gasCost;
if (t.isCreation())
gasCost = (t.init.size() + t.data.size()) * c_txDataGas + c_createGas;
else
gasCost = t.data.size() * c_txDataGas + c_callGas;
// Ignore invalid transactions.
auto nonceReq = transactionsFrom(_sender);
if (_t.nonce != nonceReq)
if (t.gas < gasCost)
{
clog(StateChat) << "Invalid Nonce.";
throw InvalidNonce(nonceReq, _t.nonce);
clog(StateChat) << "Not enough gas to pay for the transaction.";
throw OutOfGas();
}
unsigned nonZeroData = 0;
for (auto i: _t.data)
if (i)
nonZeroData++;
u256 fee = _t.receiveAddress ? m_fees.m_txFee : (nonZeroData * m_fees.m_memoryFee + m_fees.m_newContractFee);
u256 cost = t.value + t.gas * t.gasPrice;
// Not considered invalid - just pointless.
if (balance(_sender) < _t.value + fee)
// Avoid unaffordable transactions.
if (balance(sender) < cost)
{
clog(StateChat) << "Not enough cash.";
throw NotEnoughCash();
}
if (_t.receiveAddress)
{
// Increment associated nonce for sender.
noteSending(_sender);
u256 gas = t.gas - gasCost;
// Pay...
subBalance(_sender, _t.value + fee);
addBalance(_t.receiveAddress, _t.value);
// Increment associated nonce for sender.
noteSending(sender);
if (isContractAddress(_t.receiveAddress))
{
// Once we get here, there's no going back.
try
{
MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction.
execute(_t.receiveAddress, _sender, _t.value, _t.data, &feeAdder.fee);
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << _e.description();
}
catch (Exception const& _e)
{
clog(StateChat) << "Exception in VM: " << _e.description();
}
catch (std::exception const& _e)
{
clog(StateChat) << "std::exception in VM: " << _e.what();
}
}
}
// Pay...
cnote << "Paying" << formatBalance(cost) << "from sender (includes" << t.gas << "gas at" << formatBalance(t.gasPrice) << ")";
subBalance(sender, cost);
if (t.isCreation())
create(sender, t.value, t.gasPrice, &gas, &t.data, &t.init);
else
call(t.receiveAddress, sender, t.value, t.gasPrice, bytesConstRef(&t.data), &gas, bytesRef());
cnote << "Refunding" << formatBalance(gas * t.gasPrice) << "to sender (=" << gas << "*" << formatBalance(t.gasPrice) << ")";
addBalance(sender, gas * t.gasPrice);
u256 gasSpent = (t.gas - gas) * t.gasPrice;
/* unsigned c_feesKept = 8;
u256 feesEarned = gasSpent - (gasSpent / c_feesKept);
cnote << "Transferring" << (100.0 - 100.0 / c_feesKept) << "% of" << formatBalance(gasSpent) << "=" << formatBalance(feesEarned) << "to miner (" << formatBalance(gasSpent - feesEarned) << "is burnt).";
*/
u256 feesEarned = gasSpent;
cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
addBalance(m_currentBlock.coinbaseAddress, feesEarned);
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(t);
m_transactionSet.insert(t.sha3());
}
bool State::call(Address _receiveAddress, Address _sendAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out)
{
cnote << "Transferring" << formatBalance(_value) << "to receiver.";
addBalance(_receiveAddress, _value);
if (isContractAddress(_receiveAddress))
{
Address newAddress = right160(_t.sha3());
VM vm(*_gas);
ExtVM evm(*this, _receiveAddress, _sendAddress, _value, _gasPrice, _data, &contractCode(_receiveAddress));
bool revert = false;
if (isContractAddress(newAddress) || isNormalAddress(newAddress))
try
{
auto out = vm.go(evm);
memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
}
catch (OutOfGas const& /*_e*/)
{
clog(StateChat) << "Contract address collision.";
throw ContractAddressCollision();
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << _e.description();
}
catch (Exception const& _e)
{
clog(StateChat) << "Exception in VM: " << _e.description();
}
catch (std::exception const& _e)
{
clog(StateChat) << "std::exception in VM: " << _e.what();
}
// Increment associated nonce for sender.
noteSending(_sender);
// Write state out only in the case of a non-excepted transaction.
if (revert)
evm.revert();
// Pay out of sender...
subBalance(_sender, _t.value + fee);
*_gas = vm.gas();
// Set up new account...
m_cache[newAddress] = AddressState(_t.value, 0, AddressType::Contract);
auto& mem = m_cache[newAddress].memory();
for (uint i = 0; i < _t.data.size(); ++i)
mem[i] = _t.data[i];
return !revert;
}
return true;
}
#if ETH_DEBUG
commit();
clog(StateChat) << "New state:" << rootHash();
#endif
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, bytesConstRef _init)
{
Address newAddress = left160(sha3(rlpList(_sender, transactionsFrom(_sender) - 1)));
while (isContractAddress(newAddress) || isNormalAddress(newAddress))
newAddress = (u160)newAddress + 1;
// Set up new account...
m_cache[newAddress] = AddressState(0, 0, _code);
// Execute _init.
VM vm(*_gas);
ExtVM evm(*this, newAddress, _sender, _endowment, _gasPrice, bytesConstRef(), _init);
bool revert = false;
try
{
/*auto out =*/ vm.go(evm);
// Don't do anything with the output (yet).
//memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
}
catch (OutOfGas const& /*_e*/)
{
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << _e.description();
}
catch (Exception const& _e)
{
clog(StateChat) << "Exception in VM: " << _e.description();
}
catch (std::exception const& _e)
{
clog(StateChat) << "std::exception in VM: " << _e.what();
}
// Write state out only in the case of a non-excepted transaction.
if (revert)
{
evm.revert();
m_cache.erase(newAddress);
newAddress = Address();
}
*_gas = vm.gas();
return newAddress;
}
void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* _totalFee)
void State::applyRewards(Addresses const& _uncleAddresses)
{
VM vm;
ExtVM evm(*this, _myAddress, _txSender, _txValue, _txData);
vm.go(evm);
*_totalFee = vm.runFee();
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
{
addBalance(i, m_blockReward * 3 / 4);
r += m_blockReward / 8;
}
addBalance(m_currentBlock.coinbaseAddress, r);
}
void State::unapplyRewards(Addresses const& _uncleAddresses)
{
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
{
subBalance(i, m_blockReward * 3 / 4);
r += m_blockReward / 8;
}
subBalance(m_currentBlock.coinbaseAddress, r);
}

82
libethereum/State.h

@ -46,6 +46,8 @@ std::map<Address, AddressState> const& genesisState();
static const std::map<u256, u256> EmptyMapU256U256;
static const bytes EmptyBytes;
struct StateChat: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 4; };
class ExtVM;
@ -119,6 +121,7 @@ public:
bool cull(TransactionQueue& _tq) const;
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
void execute(bytes const& _rlp) { return execute(&_rlp); }
void execute(bytesConstRef _rlp);
@ -150,6 +153,10 @@ public:
/// @returns std::map<u256, u256> if no contract exists at that address.
std::map<u256, u256> const& contractMemory(Address _contract) const;
/// Get the code of a contract.
/// @returns bytes() if no contract exists at that address.
bytes const& contractCode(Address _contract) const;
/// Note that the given address is sending a transaction and thus increment the associated ticker.
void noteSending(Address _id);
@ -170,26 +177,21 @@ public:
u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit);
/// Get the fee associated for a contract created with the given data.
u256 fee(uint _dataCount) const { return m_fees.m_memoryFee * _dataCount + m_fees.m_newContractFee; }
u256 createGas(uint _dataCount, u256 _gas = 0) const { return c_txDataGas * _dataCount + c_createGas + _gas; }
/// Get the fee associated for a normal transaction.
u256 fee() const { return m_fees.m_txFee; }
u256 callGas(uint _dataCount, u256 _gas = 0) const { return c_txDataGas * _dataCount + c_callGas + _gas; }
private:
/// Fee-adder on destruction RAII class.
struct MinerFeeAdder
{
~MinerFeeAdder() { /*state->addBalance(state->m_currentBlock.coinbaseAddress, fee);*/ } // No fees paid now.
State* state;
u256 fee;
};
/// Retrieve all information about a given address into the cache.
/// If _requireMemory is true, grab the full memory should it be a contract item.
/// If _forceCreate is true, then insert a default item into the cache, in the case it doesn't
/// exist in the DB.
void ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const;
/// Retrieve all information about a given address into a cache.
void ensureCached(std::map<Address, AddressState>& _cache, Address _a, bool _requireMemory, bool _forceCreate) const;
/// Commit all changes waiting in the address cache to the DB.
void commit();
@ -201,12 +203,17 @@ private:
/// Throws on failure.
u256 playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit);
/// Execute a decoded transaction object, given a sender.
/// This will append @a _t to the transaction list and change the state accordingly.
void executeBare(Transaction const& _t, Address _sender);
// 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.
/// Execute a contract-creation transaction.
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, bytesConstRef _init);
h160 create(Transaction const& _t, Address _sender, u256* _gas);
/// Execute a contract transaction.
void execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* o_totalFee);
/// 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);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();
@ -236,7 +243,6 @@ private:
Dagger m_dagger;
FeeStructure m_fees;
u256 m_blockReward;
static std::string c_defaultPath;
@ -247,8 +253,8 @@ private:
class ExtVM: public ExtVMFace
{
public:
ExtVM(State& _s, Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData):
ExtVMFace(_myAddress, _txSender, _txValue, _txData, _s.m_fees, _s.m_previousBlock, _s.m_currentBlock, _s.m_currentNumber), m_s(_s)
ExtVM(State& _s, Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, bytesConstRef _code):
ExtVMFace(_myAddress, _txSender, _txValue, _gasPrice, _txData, _code, _s.m_previousBlock, _s.m_currentBlock, _s.m_currentNumber), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
m_store = &(m_s.m_cache[_myAddress].memory());
@ -267,30 +273,36 @@ public:
m_store->erase(_n);
}
void payFee(bigint _f)
h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, bytesConstRef _init)
{
if (_f > m_s.balance(myAddress))
throw NotEnoughCash();
m_s.subBalance(myAddress, _f);
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
return m_s.create(myAddress, _endowment, gasPrice, _gas, _code, _init);
}
void mktx(Transaction& _t)
bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out)
{
_t.nonce = m_s.transactionsFrom(myAddress);
m_s.executeBare(_t, myAddress);
return m_s.call(_receiveAddress, myAddress, _txValue, gasPrice, _txData, _gas, _out);
}
u256 balance(Address _a) { return m_s.balance(_a); }
void subBalance(u256 _a) { m_s.subBalance(myAddress, _a); }
u256 txCount(Address _a) { return m_s.transactionsFrom(_a); }
u256 extro(Address _a, u256 _pos) { return m_s.contractMemory(_a, _pos); }
u256 extroPrice(Address /*_a*/) { return 0; }
void suicide(Address _a)
{
m_s.addBalance(_a, m_s.balance(myAddress) + m_store->size() * fees.m_memoryFee);
m_s.addBalance(_a, m_s.balance(myAddress));
m_s.m_cache[myAddress].kill();
}
void revert()
{
m_s.m_cache = m_origCache;
}
private:
State& m_s;
std::map<Address, AddressState> m_origCache;
std::map<u256, u256>* m_store;
};
@ -330,7 +342,7 @@ inline std::ostream& operator<<(std::ostream& _out, State const& _s)
_out << (d.count(i.first) ? "[ ! " : "[ * ") << (i.second.type() == AddressType::Contract ? "CONTRACT] " : " NORMAL] ") << i.first << ": " << std::dec << i.second.nonce() << "@" << i.second.balance();
if (i.second.type() == AddressType::Contract)
{
if (i.second.haveMemory())
if (i.second.isComplete())
{
_out << std::endl << i.second.memory();
}
@ -364,7 +376,7 @@ void commit(std::map<Address, AddressState> const& _cache, DB& _db, TrieDB<Addre
s << i.second.balance() << i.second.nonce();
if (i.second.type() == AddressType::Contract)
{
if (i.second.haveMemory())
if (i.second.isComplete())
{
TrieDB<h256, DB> memdb(&_db);
memdb.init();
@ -372,9 +384,17 @@ void commit(std::map<Address, AddressState> const& _cache, DB& _db, TrieDB<Addre
if (j.second)
memdb.insert(j.first, rlp(j.second));
s << memdb.root();
if (i.second.freshCode())
{
h256 ch = sha3(i.second.code());
_db.insert(ch, &i.second.code());
s << ch;
}
else
s << i.second.codeHash();
}
else
s << i.second.oldRoot();
s << i.second.oldRoot() << i.second.codeHash();
}
_state.insert(i.first, &s.out());
}

25
libethereum/Transaction.cpp

@ -23,6 +23,7 @@
#include "vector_ref.h"
#include "Exceptions.h"
#include "Transaction.h"
#include "Log.h"
using namespace std;
using namespace eth;
@ -35,12 +36,18 @@ Transaction::Transaction(bytesConstRef _rlpData)
try
{
nonce = rlp[field = 0].toInt<u256>();
receiveAddress = rlp[field = 1].toHash<Address>();
value = rlp[field = 2].toInt<u256>();
data.reserve(rlp[field = 3].itemCountStrict());
for (auto const& i: rlp[3])
data.push_back(i.toInt<u256>());
vrs = Signature{ rlp[field = 4].toInt<byte>(), rlp[field = 5].toInt<u256>(), rlp[field = 6].toInt<u256>() };
value = rlp[field = 1].toInt<u256>();
receiveAddress = rlp[field = 2].toHash<Address>();
gasPrice = rlp[field = 3].toInt<u256>();
gas = rlp[field = 4].toInt<u256>();
data = rlp[field = 5].toBytes();
if (isCreation())
{
init = rlp[field = 6].toBytes();
vrs = Signature{ rlp[field = 7].toInt<byte>(), rlp[field = 8].toInt<u256>(), rlp[field = 9].toInt<u256>() };
}
else
vrs = Signature{ rlp[field = 6].toInt<byte>(), rlp[field = 7].toInt<u256>(), rlp[field = 8].toInt<u256>() };
}
catch (RLPException const&)
{
@ -112,8 +119,10 @@ void Transaction::sign(Secret _priv)
void Transaction::fillStream(RLPStream& _s, bool _sig) const
{
_s.appendList(_sig ? 7 : 4);
_s << nonce << receiveAddress << value << data;
_s.appendList((_sig ? 3 : 0) + (isCreation() ? 7 : 6));
_s << nonce << value << receiveAddress << gasPrice << gas << data;
if (isCreation())
_s << init;
if (_sig)
_s << vrs.v << vrs.r << vrs.s;
}

29
libethereum/Transaction.h

@ -34,7 +34,9 @@ struct Signature
u256 s;
};
// [ nonce, receiving_address, value, [ data item 0, data item 1 ... data item n ], v, r, s ]
// [ nonce, value, receiveAddress, gasPrice, gasDeposit, data, v, r, s ]
// or
// [ nonce, endowment, 0, gasPrice, gasDeposit (for init), body, init, v, r, s ]
struct Transaction
{
Transaction() {}
@ -44,15 +46,22 @@ struct Transaction
bool operator==(Transaction const& _c) const { return receiveAddress == _c.receiveAddress && value == _c.value && data == _c.data; }
bool operator!=(Transaction const& _c) const { return !operator==(_c); }
u256 nonce;
Address receiveAddress;
u256 value;
u256s data;
Signature vrs;
u256 nonce; ///< The transaction-count of the sender.
u256 value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address receiveAddress; ///< The receiving address of the transaction.
u256 gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
Address safeSender() const noexcept;
Address sender() const;
void sign(Secret _priv);
bytes data; ///< The data associated with the transaction, or the main body if it's a creation transaction.
bytes init; ///< The initialisation associated with the transaction.
Signature vrs; ///< The signature of the transaction. Encodes the sender.
Address safeSender() const noexcept; ///< Like sender() but will never throw.
Address sender() const; ///< Determine the sender of the transaction from the signature (and hash).
void sign(Secret _priv); ///< Sign the transaction.
bool isCreation() const { return !receiveAddress; }
static h256 kFromMessage(h256 _msg, h256 _priv);
@ -73,7 +82,7 @@ inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t)
else
_out << "[CREATE]";
_out << "/" << _t.nonce << "*" << _t.value;
_out << "/" << _t.nonce << "$" << _t.value << "+" << _t.gas << "@" << _t.gasPrice;
Address s;
try
{

8
libethereum/VM.cpp

@ -24,13 +24,9 @@
using namespace std;
using namespace eth;
VM::VM()
{
reset();
}
void VM::reset()
void VM::reset(u256 _gas)
{
m_gas = _gas;
m_curPC = 0;
m_nextPC = 1;
m_stepCount = 0;

543
libethereum/VM.h

@ -22,8 +22,7 @@
#pragma once
#include <unordered_map>
#include "CryptoHeaders.h"
#include "Common.h"
#include "CommonEth.h"
#include "Exceptions.h"
#include "FeeStructure.h"
#include "Instruction.h"
@ -37,12 +36,14 @@ namespace eth
// Currently we just pull out the right (low-order in BE) 160-bits.
inline Address asAddress(u256 _item)
{
return right160(h256(_item));
return left160(h256(_item));
}
inline u256 fromAddress(Address _a)
{
return (u160)_a;
h256 ret;
memcpy(&ret, &_a, sizeof(_a));
return ret;
}
/**
@ -53,21 +54,24 @@ class VM
public:
/// Construct VM object.
VM();
explicit VM(u256 _gas = 0) { reset(_gas); }
void reset();
void reset(u256 _gas = 0);
template <class Ext>
void go(Ext& _ext, uint64_t _steps = (uint64_t)-1);
bytesConstRef go(Ext& _ext, 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); } }
u256 runFee() const { return m_runFee; }
u256 gas() const { return m_gas; }
private:
u256 m_gas = 0;
u256 m_curPC = 0;
u256 m_nextPC = 1;
uint64_t m_stepCount = 0;
std::map<u256, u256> m_temp;
bytes m_temp;
std::vector<u256> m_stack;
u256 m_runFee = 0;
};
@ -75,57 +79,109 @@ private:
}
// INLINE:
template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps)
{
for (bool stopped = false; !stopped && _steps--; m_curPC = m_nextPC, m_nextPC = m_curPC + 1)
{
m_stepCount++;
// INSTRUCTION...
auto rawInst = _ext.store(m_curPC);
if (rawInst > 0xff)
throw BadInstruction();
Instruction inst = (Instruction)(uint8_t)rawInst;
Instruction inst = (Instruction)_ext.getCode(m_curPC);
// FEES...
bigint runFee = m_stepCount > 16 ? _ext.fees.m_stepFee : 0;
bigint storeCostDelta = 0;
bigint runGas = c_stepGas;
unsigned newTempSize = (unsigned)m_temp.size();
switch (inst)
{
case Instruction::STOP:
runGas = 0;
break;
case Instruction::SSTORE:
require(2);
if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2])
storeCostDelta += _ext.fees.m_memoryFee;
if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
storeCostDelta -= _ext.fees.m_memoryFee;
// continue on to...
runGas = c_sstoreGas * 2;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
runGas = 0;
else
runGas = c_sstoreGas;
break;
case Instruction::SLOAD:
runFee += _ext.fees.m_dataFee;
runGas += c_sloadGas;
break;
// These all operate on memory and therefore potentially expand it:
case Instruction::MSTORE:
require(2);
newTempSize = (unsigned)m_stack.back() + 32;
break;
case Instruction::MSTORE8:
require(2);
newTempSize = (unsigned)m_stack.back() + 1;
break;
case Instruction::MLOAD:
require(1);
newTempSize = (unsigned)m_stack.back() + 32;
break;
case Instruction::RETURN:
require(2);
newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 2];
break;
case Instruction::SHA3:
require(2);
runGas = c_sha3Gas;
newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 2];
break;
case Instruction::EXTRO:
case Instruction::BALANCE:
runFee += _ext.fees.m_extroFee;
runGas = c_balanceGas;
break;
case Instruction::MKTX:
runFee += _ext.fees.m_txFee;
case Instruction::CALL:
require(7);
runGas = c_callGas + (unsigned)m_stack[m_stack.size() - 3];
newTempSize = std::max((unsigned)m_stack[m_stack.size() - 6] + (unsigned)m_stack[m_stack.size() - 7], (unsigned)m_stack[m_stack.size() - 4] + (unsigned)m_stack[m_stack.size() - 5]);
break;
case Instruction::SHA256:
case Instruction::RIPEMD160:
case Instruction::ECMUL:
case Instruction::ECADD:
case Instruction::ECSIGN:
case Instruction::ECRECOVER:
case Instruction::ECVALID:
runFee += _ext.fees.m_cryptoFee;
case Instruction::CREATE:
{
require(3);
u256 gas = (unsigned)m_stack[m_stack.size() - 1];
unsigned inOff = (unsigned)m_stack[m_stack.size() - 2];
unsigned inSize = (unsigned)m_stack[m_stack.size() - 3];
newTempSize = inOff + inSize;
unsigned wc = std::min(inSize / 32 * 32 + inOff, (unsigned)m_temp.size());
unsigned nonZero = 0;
for (unsigned i = inOff; i < wc; i += 32)
if (!!*(h256*)(m_temp.data() + inOff))
nonZero++;
runGas += c_createGas + nonZero * c_sstoreGas + gas;
break;
}
default:
break;
}
_ext.payFee(runFee + storeCostDelta);
m_runFee += (u256)runFee;
newTempSize = (newTempSize + 31) / 32 * 32;
if (newTempSize > m_temp.size())
runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32;
if (m_gas < runGas)
{
// Out of gas!
m_gas = 0;
throw OutOfGas();
}
m_gas = (u256)((bigint)m_gas - runGas);
if (newTempSize > m_temp.size())
m_temp.resize(newTempSize);
// EXECUTE...
switch (inst)
@ -150,28 +206,28 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
case Instruction::DIV:
require(2);
if (!m_stack[m_stack.size() - 2])
return;
return bytesConstRef();
m_stack[m_stack.size() - 2] = m_stack.back() / m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
case Instruction::SDIV:
require(2);
if (!m_stack[m_stack.size() - 2])
return;
return bytesConstRef();
(s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() / (s256&)m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
case Instruction::MOD:
require(2);
if (!m_stack[m_stack.size() - 2])
return;
return bytesConstRef();
m_stack[m_stack.size() - 2] = m_stack.back() % m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
case Instruction::SMOD:
require(2);
if (!m_stack[m_stack.size() - 2])
return;
return bytesConstRef();
(s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() % (s256&)m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
@ -196,21 +252,11 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::LE:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() <= m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::GT:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() > m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::GE:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() >= m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::EQ:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0;
@ -220,223 +266,131 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
require(1);
m_stack.back() = m_stack.back() ? 0 : 1;
break;
case Instruction::MYADDRESS:
m_stack.push_back(fromAddress(_ext.myAddress));
break;
case Instruction::TXSENDER:
m_stack.push_back(fromAddress(_ext.txSender));
break;
case Instruction::TXVALUE:
m_stack.push_back(_ext.txValue);
break;
case Instruction::TXDATAN:
m_stack.push_back(_ext.txData.size());
break;
case Instruction::TXDATA:
require(1);
m_stack.back() = m_stack.back() < _ext.txData.size() ? _ext.txData[(uint)m_stack.back()] : 0;
break;
case Instruction::BLK_PREVHASH:
m_stack.push_back(_ext.previousBlock.hash);
break;
case Instruction::BLK_COINBASE:
m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress);
break;
case Instruction::BLK_TIMESTAMP:
m_stack.push_back(_ext.currentBlock.timestamp);
break;
case Instruction::BLK_NUMBER:
m_stack.push_back(_ext.currentNumber);
break;
case Instruction::BLK_DIFFICULTY:
m_stack.push_back(_ext.currentBlock.difficulty);
break;
case Instruction::BLK_NONCE:
m_stack.push_back(_ext.previousBlock.nonce);
break;
case Instruction::BASEFEE:
m_stack.push_back(_ext.fees.multiplier());
break;
case Instruction::SHA256:
{
require(1);
uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32);
case Instruction::AND:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() & m_stack[m_stack.size() - 2];
m_stack.pop_back();
CryptoPP::SHA256 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(m_stack.back());
digest.Update(b.data(), (int)std::min<u256>(32, s)); // b.size() == 32
m_stack.pop_back();
}
std::array<byte, 32> final;
digest.TruncatedFinal(final.data(), 32);
m_stack.push_back(fromBigEndian<u256>(final));
break;
}
case Instruction::RIPEMD160:
{
require(1);
uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32);
case Instruction::OR:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() | m_stack[m_stack.size() - 2];
m_stack.pop_back();
CryptoPP::RIPEMD160 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(m_stack.back());
digest.Update(b.data(), (int)std::min<u256>(32, s)); // b.size() == 32
m_stack.pop_back();
}
std::array<byte, 20> final;
digest.TruncatedFinal(final.data(), 20);
// NOTE: this aligns to right of 256-bit container (low-order bytes).
// This won't work if they're treated as byte-arrays and thus left-aligned in a 256-bit container.
m_stack.push_back((u256)fromBigEndian<u160>(final));
break;
}
case Instruction::ECMUL:
{
// ECMUL - pops three items.
// If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity.
// Otherwise, pushes (0,0).
require(3);
bytes pub(1, 4);
pub += toBigEndian(m_stack[m_stack.size() - 2]);
pub += toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
bytes x = toBigEndian(m_stack.back());
case Instruction::XOR:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() ^ m_stack[m_stack.size() - 2];
m_stack.pop_back();
if (secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size())) // TODO: Check both are less than P.
{
secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), (int)pub.size(), x.data());
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(1, 32)));
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(33, 32)));
}
else
{
m_stack.push_back(0);
m_stack.push_back(0);
}
break;
}
case Instruction::ECADD:
{
// ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0).
require(4);
bytes pub(1, 4);
pub += toBigEndian(m_stack[m_stack.size() - 2]);
pub += toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
bytes tweak(1, 4);
tweak += toBigEndian(m_stack[m_stack.size() - 2]);
tweak += toBigEndian(m_stack.back());
m_stack.pop_back();
case Instruction::BYTE:
require(2);
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] < 32 ? (m_stack[m_stack.size() - 2] >> (uint)(31 - m_stack.back())) & 0xff : 0;
m_stack.pop_back();
if (secp256k1_ecdsa_pubkey_verify(pub.data(),(int) pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(),(int) tweak.size()))
{
secp256k1_ecdsa_pubkey_tweak_add(pub.data(), (int)pub.size(), tweak.data());
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(1, 32)));
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(33, 32)));
}
else
{
m_stack.push_back(0);
m_stack.push_back(0);
}
break;
}
case Instruction::ECSIGN:
case Instruction::SHA3:
{
require(2);
bytes sig(64);
int v = 0;
u256 msg = m_stack.back();
unsigned inOff = (unsigned)m_stack.back();
m_stack.pop_back();
u256 priv = m_stack.back();
unsigned inSize = (unsigned)m_stack.back();
m_stack.pop_back();
bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv));
if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v))
throw InvalidSignature();
m_stack.push_back(v + 27);
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&sig).cropped(0, 32)));
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&sig).cropped(32)));
m_stack.push_back(sha3(bytesConstRef(m_temp.data() + inOff, inSize)));
break;
}
case Instruction::ECRECOVER:
{
require(4);
bytes sig = toBigEndian(m_stack[m_stack.size() - 2]) + toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
int v = (int)m_stack.back();
m_stack.pop_back();
bytes msg = toBigEndian(m_stack.back());
m_stack.pop_back();
byte pubkey[65];
int pubkeylen = 65;
if (secp256k1_ecdsa_recover_compact(msg.data(), (int)msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27))
{
m_stack.push_back(0);
m_stack.push_back(0);
}
else
{
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pubkey[1], 32)));
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pubkey[33], 32)));
}
case Instruction::ADDRESS:
m_stack.push_back(fromAddress(_ext.myAddress));
break;
}
case Instruction::ECVALID:
case Instruction::ORIGIN:
// TODO get originator from ext.
m_stack.push_back(fromAddress(_ext.txSender));
break;
case Instruction::BALANCE:
{
require(2);
bytes pub(1, 4);
pub += toBigEndian(m_stack[m_stack.size() - 2]);
pub += toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
m_stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size()) ? 1 : 0;
require(1);
m_stack.back() = _ext.balance(asAddress(m_stack.back()));
break;
}
case Instruction::SHA3:
case Instruction::CALLER:
m_stack.push_back(fromAddress(_ext.txSender));
break;
case Instruction::CALLVALUE:
m_stack.push_back(_ext.txValue);
break;
case Instruction::CALLDATALOAD:
{
require(1);
uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32);
m_stack.pop_back();
CryptoPP::SHA3_256 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
if ((unsigned)m_stack.back() + 32 < _ext.txData.size())
m_stack.back() = (u256)*(h256 const*)(_ext.txData.data() + (unsigned)m_stack.back());
else
{
bytes b = toBigEndian(m_stack.back());
digest.Update(b.data(), (int)std::min<u256>(32, s)); // b.size() == 32
m_stack.pop_back();
h256 r;
for (unsigned i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + 32, j = 0; i < e; ++i, ++j)
r[j] = i < _ext.txData.size() ? _ext.txData[i] : 0;
m_stack.back() = (u256)r;
}
std::array<byte, 32> final;
digest.TruncatedFinal(final.data(), 32);
m_stack.push_back(fromBigEndian<u256>(final));
break;
}
case Instruction::PUSH:
case Instruction::CALLDATASIZE:
m_stack.push_back(_ext.txData.size());
break;
case Instruction::GASPRICE:
m_stack.push_back(_ext.gasPrice);
break;
case Instruction::PREVHASH:
m_stack.push_back(_ext.previousBlock.hash);
break;
case Instruction::COINBASE:
m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress);
break;
case Instruction::TIMESTAMP:
m_stack.push_back(_ext.currentBlock.timestamp);
break;
case Instruction::NUMBER:
m_stack.push_back(_ext.currentNumber);
break;
case Instruction::DIFFICULTY:
m_stack.push_back(_ext.currentBlock.difficulty);
break;
case Instruction::GASLIMIT:
m_stack.push_back(1000000);
break;
case Instruction::PUSH1:
case Instruction::PUSH2:
case Instruction::PUSH3:
case Instruction::PUSH4:
case Instruction::PUSH5:
case Instruction::PUSH6:
case Instruction::PUSH7:
case Instruction::PUSH8:
case Instruction::PUSH9:
case Instruction::PUSH10:
case Instruction::PUSH11:
case Instruction::PUSH12:
case Instruction::PUSH13:
case Instruction::PUSH14:
case Instruction::PUSH15:
case Instruction::PUSH16:
case Instruction::PUSH17:
case Instruction::PUSH18:
case Instruction::PUSH19:
case Instruction::PUSH20:
case Instruction::PUSH21:
case Instruction::PUSH22:
case Instruction::PUSH23:
case Instruction::PUSH24:
case Instruction::PUSH25:
case Instruction::PUSH26:
case Instruction::PUSH27:
case Instruction::PUSH28:
case Instruction::PUSH29:
case Instruction::PUSH30:
case Instruction::PUSH31:
case Instruction::PUSH32:
{
m_stack.push_back(_ext.store(m_curPC + 1));
m_nextPC = m_curPC + 2;
int i = (int)inst - (int)Instruction::PUSH1 + 1;
m_nextPC = m_curPC + 1;
m_stack.push_back(0);
for (; i--; m_nextPC++)
m_stack.back() = (m_stack.back() << 8) | _ext.getCode(m_nextPC);
break;
}
case Instruction::POP:
@ -479,13 +433,21 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
case Instruction::MLOAD:
{
require(1);
m_stack.back() = m_temp[m_stack.back()];
m_stack.back() = (u256)*(h256 const*)(m_temp.data() + (unsigned)m_stack.back());
break;
}
case Instruction::MSTORE:
{
require(2);
m_temp[m_stack.back()] = m_stack[m_stack.size() - 2];
*(h256*)&m_temp[(unsigned)m_stack.back()] = (h256)m_stack[m_stack.size() - 2];
m_stack.pop_back();
m_stack.pop_back();
break;
}
case Instruction::MSTORE8:
{
require(2);
m_temp[(unsigned)m_stack.back()] = (byte)(m_stack[m_stack.size() - 2] & 0xff);
m_stack.pop_back();
m_stack.pop_back();
break;
@ -500,60 +462,96 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::JMP:
case Instruction::JUMP:
require(1);
m_nextPC = m_stack.back();
m_stack.pop_back();
break;
case Instruction::JMPI:
case Instruction::JUMPI:
require(2);
if (m_stack.back())
m_nextPC = m_stack[m_stack.size() - 2];
if (m_stack[m_stack.size() - 2])
m_nextPC = m_stack.back();
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::IND:
case Instruction::PC:
m_stack.push_back(m_curPC);
break;
case Instruction::EXTRO:
{
require(2);
auto memoryAddress = m_stack.back();
m_stack.pop_back();
Address contractAddress = asAddress(m_stack.back());
m_stack.back() = _ext.extro(contractAddress, memoryAddress);
case Instruction::MEMSIZE:
m_stack.push_back(m_temp.size());
break;
}
case Instruction::BALANCE:
case Instruction::GAS:
m_stack.push_back(m_gas);
break;
case Instruction::CREATE:
{
require(1);
m_stack.back() = _ext.balance(asAddress(m_stack.back()));
require(5);
u256 endowment = m_stack.back();
m_stack.pop_back();
unsigned codeOff = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned codeSize = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned initOff = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned initSize = (unsigned)m_stack.back();
m_stack.pop_back();
if (_ext.balance(_ext.myAddress) >= endowment)
{
_ext.subBalance(endowment);
m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + codeOff, codeSize), bytesConstRef(m_temp.data() + initOff, initSize)));
}
else
m_stack.push_back(0);
break;
}
case Instruction::MKTX:
case Instruction::CALL:
{
require(3);
require(7);
Transaction t;
t.receiveAddress = asAddress(m_stack.back());
u160 receiveAddress = asAddress(m_stack.back());
m_stack.pop_back();
u256 value = m_stack.back();
m_stack.pop_back();
t.value = m_stack.back();
u256 gas = m_stack.back();
m_stack.pop_back();
auto itemCount = m_stack.back();
unsigned inOff = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned inSize = (unsigned)m_stack.back();
m_stack.pop_back();
if (m_stack.size() < itemCount)
throw OperandOutOfRange(0, m_stack.size(), itemCount);
t.data.reserve((uint)itemCount);
for (auto i = 0; i < itemCount; ++i)
unsigned outOff = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned outSize = (unsigned)m_stack.back();
m_stack.pop_back();
if (!gas)
{
t.data.push_back(m_stack.back());
m_stack.pop_back();
gas = m_gas;
m_gas = 0;
}
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)));
}
_ext.mktx(t);
m_gas += gas;
break;
}
case Instruction::RETURN:
{
require(2);
unsigned b = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned s = (unsigned)m_stack.back();
m_stack.pop_back();
return bytesConstRef(m_temp.data() + b, s);
}
case Instruction::SUICIDE:
{
require(1);
@ -562,12 +560,13 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
// ...follow through to...
}
case Instruction::STOP:
return;
return bytesConstRef();
default:
throw BadInstruction();
}
}
if (_steps == (unsigned)-1)
throw StepsDone();
return bytesConstRef();
}

5
libethereum/vector_ref.h

@ -19,6 +19,7 @@ class vector_ref
public:
typedef _T value_type;
typedef _T element_type;
typedef typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type mutable_value_type;
vector_ref(): m_data(nullptr), m_count(0) {}
vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {}
@ -29,8 +30,8 @@ public:
explicit operator bool() const { return m_data && m_count; }
bool contentsEqual(std::vector<_T> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); }
std::vector<_T> toVector() const { return std::vector<_T>(m_data, m_data + m_count); }
bool contentsEqual(std::vector<mutable_value_type> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); }
std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); }
std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>((unsigned char const*)m_data, m_data + m_count * sizeof(_T)); }
std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count); }
template <class _T2> operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>((_T2*)m_data, m_count * sizeof(_T) / sizeof(_T2)); }

1
test/crypto.cpp

@ -42,7 +42,6 @@ int cryptoTest()
t.nonce = 0;
t.receiveAddress = h160(fromHex("944400f4b88ac9589a0f17ed4671da26bddb668b"));
t.value = 1000;
t.data = u256s();
cnote << RLP(t.rlp(false));
cnote << toHex(t.rlp(false));
cnote << t.sha3(false);

8
test/txTest.cpp

@ -52,15 +52,15 @@ BOOST_AUTO_TEST_CASE(mine_and_send_to_peer)
mine(c1, 1);
auto c1bal = c1.state().balance(kp1.address());
BOOST_REQUIRE(c1bal > 0);
BOOST_REQUIRE(c1bal > c1.state().fee());
// BOOST_REQUIRE(c1bal > c1.state().fee());
//send c2 some eth from c1
auto txAmount = c1bal - c1.state().fee();
c1.transact(kp1.secret(), c2.address(), txAmount);
// auto txAmount = c1bal - c1.state().fee();
// c1.transact(kp1.secret(), c2.address(), txAmount);
//mine some more to include the transaction on chain
mine(c1, 1);
auto c2bal = c2.state().balance(kp2.address());
BOOST_REQUIRE(c2bal > 0);
BOOST_REQUIRE(c2bal == txAmount);
// BOOST_REQUIRE(c2bal == txAmount);
}

136
test/vm.cpp

@ -39,8 +39,8 @@ class FakeExtVM: public ExtVMFace
public:
FakeExtVM()
{}
FakeExtVM(FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
ExtVMFace(Address(), Address(), 0, u256s(), _fees, _previousBlock, _currentBlock, _currentNumber)
FakeExtVM(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
ExtVMFace(Address(), Address(), 0, 1, bytesConstRef(), bytesConstRef(), _previousBlock, _currentBlock, _currentNumber)
{}
u256 store(u256 _n)
@ -51,7 +51,15 @@ public:
{
get<3>(addresses[myAddress])[_n] = _v;
}
void mktx(Transaction& _t)
u256 balance(Address _a) { return get<0>(addresses[_a]); }
void subBalance(u256 _a) { get<0>(addresses[myAddress]) -= _a; }
u256 txCount(Address _a) { return get<1>(addresses[_a]); }
void suicide(Address _a)
{
get<0>(addresses[_a]) += get<0>(addresses[myAddress]);
addresses.erase(myAddress);
}
void transact(Transaction& _t)
{
if (get<0>(addresses[myAddress]) >= _t.value)
{
@ -61,41 +69,57 @@ public:
txs.push_back(_t);
}
}
u256 balance(Address _a) { return get<0>(addresses[_a]); }
void payFee(bigint _fee) { get<0>(addresses[myAddress]) = (u256)(get<0>(addresses[myAddress]) - _fee); }
u256 txCount(Address _a) { return get<1>(addresses[_a]); }
u256 extro(Address _a, u256 _pos)
h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, bytesConstRef _init)
{
return get<3>(addresses[_a])[_pos];
Transaction t;
t.value = _endowment;
t.gasPrice = gasPrice;
t.gas = *_gas;
t.data = _code.toBytes();
t.init = _init.toBytes();
txs.push_back(t);
return right160(t.sha3(false));
}
u256 extroPrice(Address _a) { return get<2>(addresses[_a]); }
void suicide(Address _a)
bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _txOut)
{
for (auto const& i: get<3>(addresses[myAddress]))
if (i.second)
get<0>(addresses[_a]) += fees.m_memoryFee;
get<0>(addresses[_a]) += get<0>(addresses[myAddress]);
addresses.erase(myAddress);
Transaction t;
t.value = _txValue;
t.gasPrice = gasPrice;
t.gas = *_gas;
t.data = _txData.toVector();
t.receiveAddress = _receiveAddress;
txs.push_back(t);
(void)_txOut;
return true;
}
void setTransaction(Address _txSender, u256 _txValue, u256s const& _txData)
void setTransaction(Address _txSender, u256 _txValue, u256 _gasPrice, bytes const& _txData)
{
txSender = _txSender;
txValue = _txValue;
txData = _txData;
txData = &_txData;
gasPrice = _gasPrice;
}
void setContract(Address _myAddress, u256 _myBalance, u256 _myNonce, u256s _myData)
void setContract(Address _myAddress, u256 _myBalance, u256 _myNonce, bytes const& _code, map<u256, u256> const& _storage)
{
myAddress = _myAddress;
set(myAddress, _myBalance, _myNonce, _myData);
set(myAddress, _myBalance, _myNonce, _code, _storage);
}
void set(Address _a, u256 _myBalance, u256 _myNonce, u256s _myData)
void set(Address _a, u256 _myBalance, u256 _myNonce, bytes const& _code, map<u256, u256> const& _storage)
{
get<0>(addresses[_a]) = _myBalance;
get<1>(addresses[_a]) = _myNonce;
get<2>(addresses[_a]) = 0;
for (unsigned i = 0; i < _myData.size(); ++i)
get<3>(addresses[_a])[i] = _myData[i];
get<3>(addresses[_a]) = _storage;
get<4>(addresses[_a]) = _code;
}
void reset(u256 _myBalance, u256 _myNonce, map<u256, u256> const& _storage)
{
txs.clear();
addresses.clear();
set(myAddress, _myBalance, _myNonce, get<4>(addresses[myAddress]), _storage);
}
mObject exportEnv()
@ -106,7 +130,6 @@ public:
push(ret, "currentDifficulty", currentBlock.difficulty);
push(ret, "currentTimestamp", currentBlock.timestamp);
ret["currentCoinbase"] = toString(currentBlock.coinbaseAddress);
push(ret, "feeMultiplier", fees.multiplier());
return ret;
}
@ -117,7 +140,6 @@ public:
currentBlock.difficulty = toInt(_o["currentDifficulty"]);
currentBlock.timestamp = toInt(_o["currentTimestamp"]);
currentBlock.coinbaseAddress = Address(_o["currentCoinbase"].get_str());
fees.setMultiplier(toInt(_o["feeMultiplier"]));
}
static u256 toInt(mValue const& _v)
@ -133,6 +155,19 @@ public:
return 0;
}
static byte toByte(mValue const& _v)
{
switch (_v.type())
{
case str_type: return (byte)stoi(_v.get_str());
case int_type: return (byte)_v.get_uint64();
case bool_type: return (byte)_v.get_bool();
case real_type: return (byte)_v.get_real();
default: cwarn << "Bad type for scalar: " << _v.type();
}
return 0;
}
static void push(mObject& o, string const& _n, u256 _v)
{
if (_v < (u256)1 << 64)
@ -157,7 +192,6 @@ public:
mObject o;
push(o, "balance", get<0>(a.second));
push(o, "nonce", get<1>(a.second));
push(o, "extroPrice", get<2>(a.second));
mObject store;
string curKey;
@ -197,7 +231,6 @@ public:
auto& a = addresses[Address(i.first)];
get<0>(a) = toInt(o["balance"]);
get<1>(a) = toInt(o["nonce"]);
get<2>(a) = toInt(o["extroPrice"]);
if (o.count("store"))
for (auto const& j: o["store"].get_obj())
{
@ -207,9 +240,9 @@ public:
}
if (o.count("code"))
{
u256s d = compileLisp(o["code"].get_str());
for (unsigned i = 0; i < d.size(); ++i)
get<3>(a)[(u256)i] = d[i];
bytes e;
bytes d = compileLisp(o["code"].get_str(), false, e);
get<4>(a) = d;
}
}
}
@ -220,6 +253,7 @@ public:
ret["address"] = toString(myAddress);
ret["sender"] = toString(txSender);
push(ret, "value", txValue);
push(ret, "gasPrice", gasPrice);
mArray d;
for (auto const& i: txData)
push(d, i);
@ -232,8 +266,11 @@ public:
myAddress = Address(_o["address"].get_str());
txSender = Address(_o["sender"].get_str());
txValue = toInt(_o["value"]);
gasPrice = toInt(_o["gasPrice"]);
thisTxData.clear();
for (auto const& j: _o["data"].get_array())
txData.push_back(toInt(j));
thisTxData.push_back(toByte(j));
txData = &thisTxData;
}
mArray exportTxs()
@ -262,20 +299,14 @@ public:
t.receiveAddress = Address(tx["destination"].get_str());
t.value = toInt(tx["value"]);
for (auto const& j: tx["data"].get_array())
t.data.push_back(toInt(j));
t.data.push_back(toByte(j));
txs.push_back(t);
}
}
void reset(u256 _myBalance, u256 _myNonce, u256s _myData)
{
txs.clear();
addresses.clear();
set(myAddress, _myBalance, _myNonce, _myData);
}
map<Address, tuple<u256, u256, u256, map<u256, u256>>> addresses;
map<Address, tuple<u256, u256, u256, map<u256, u256>, bytes>> addresses;
Transactions txs;
bytes thisTxData;
};
#define CREATE_TESTS 0
@ -315,21 +346,37 @@ public:
if (_fillin)
o["pre"] = mValue(fev.exportState());
bytes output;
for (auto i: o["exec"].get_array())
{
fev.importExec(i.get_obj());
vm.go(fev);
output = vm.go(fev).toBytes();
}
if (_fillin)
{
o["post"] = mValue(fev.exportState());
o["txs"] = fev.exportTxs();
mArray df;
for (auto const& i: output)
FakeExtVM::push(df, i);
o["out"] = df;
}
else
{
FakeExtVM test;
test.importState(o["post"].get_obj());
test.importTxs(o["txs"].get_array());
int i = 0;
for (auto const& d: o["out"].get_array())
{
if (output[i] != FakeExtVM::toInt(d))
{
cwarn << "Test failed: output byte" << i << "different.";
passed = false;
}
++i;
}
if (test.addresses != fev.addresses)
{
cwarn << "Test failed: state different.";
@ -359,13 +406,12 @@ public:
cb.difficulty = 256;
cb.timestamp = 1;
cb.coinbaseAddress = toAddress(sha3("coinbase"));
FeeStructure fees;
fees.setMultiplier(1);
FakeExtVM fev(fees, pb, cb, 0);
fev.setContract(toAddress(sha3("contract")), ether, 0, compileLisp("(suicide (txsender))"));
FakeExtVM fev(pb, cb, 0);
bytes init;
fev.setContract(toAddress(sha3("contract")), ether, 0, compileLisp("(suicide (txsender))", false, init), map<u256, u256>());
o["env"] = fev.exportEnv();
o["pre"] = fev.exportState();
fev.setTransaction(toAddress(sha3("sender")), ether, u256s());
fev.setTransaction(toAddress(sha3("sender")), ether, finney, bytes());
mArray execs;
execs.push_back(fev.exportExec());
o["exec"] = execs;

11
walleth/MainWin.cpp

@ -24,6 +24,7 @@ using eth::h160;
using eth::h256;
using eth::u160;
using eth::u256;
using eth::u256s;
using eth::Address;
using eth::BlockInfo;
using eth::Client;
@ -173,9 +174,14 @@ unsigned QEthereum::peerCount() const
return (unsigned)client()->peerCount();
}
void QEthereum::transact(Secret _secret, Address _dest, u256 _amount)
void QEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _code, QByteArray _init)
{
client()->transact(_secret, _dest, _amount);
client()->transact(_secret, _amount, _gasPrice, _gas, bytes(_code.data(), _code.data() + _code.size()), bytes(_init.data(), _init.data() + _init.size()));
}
void QEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data)
{
client()->transact(_secret, _amount, _gasPrice, _gas, _dest, bytes(_data.data(), _data.data() + _data.size()));
}
Main::Main(QWidget *parent) :
@ -304,7 +310,6 @@ void Main::readSettings()
restoreGeometry(s.value("geometry").toByteArray());
restoreState(s.value("windowState").toByteArray());
QByteArray b = s.value("address").toByteArray();
if (b.isEmpty())
m_myKeys.append(KeyPair::create());

4
walleth/MainWin.h

@ -140,8 +140,8 @@ public:
Q_INVOKABLE QEthereum* self() { return this; }
public slots:
void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount);
void transact(eth::Secret _secret, eth::Address _dest, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _data);
void transact(eth::Secret _secret, eth::u256 _amount, eth::u256 _gasPrice, eth::u256 _gas, QByteArray _code, QByteArray _init);
void setCoinbase(eth::Address);
void setMining(bool _l);

Loading…
Cancel
Save