From dfea821aaf4de7e0487468cacc0d611d7c0c14ef Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 27 Oct 2014 12:02:01 +0100 Subject: [PATCH 1/4] DB prototyped. --- alethzero/MainWin.cpp | 86 ++++++++++++++++++++++++++---------- alethzero/MainWin.h | 10 +++-- libp2p/Host.cpp | 2 +- libqethereum/QEthereum.cpp | 89 ++++++++++++++++++++++++++++++++++++-- libqethereum/QEthereum.h | 67 ++++++++++++++++++++++++++-- third/MainWin.cpp | 9 ++-- third/MainWin.h | 6 ++- 7 files changed, 231 insertions(+), 38 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index c42cddc4f..674509b53 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -131,6 +131,7 @@ Main::Main(QWidget *parent) : connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"})); + m_ldb = new QLDB(this); connect(ui->webView, &QWebView::loadStarted, [this]() { @@ -138,8 +139,8 @@ Main::Main(QWidget *parent) : m_whisper = nullptr; // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. m_dev = new QDev(this); - m_ethereum = new QEthereum(this, ethereum(), owned()); - m_whisper = new QWhisper(this, whisper()); + m_ethereum = new QEthereum(this, ethereum(), m_myKeys); + m_whisper = new QWhisper(this, whisper(), owned()); QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); QWebFrame* f = ui->webView->page()->mainFrame(); @@ -147,8 +148,9 @@ Main::Main(QWidget *parent) : auto qdev = m_dev; auto qeth = m_ethereum; auto qshh = m_whisper; - connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qdev, qeth, qshh)); - connect(m_whisper, SIGNAL(idsChanged()), this, SLOT(refreshWhisper())); + auto qldb = m_ldb; + connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qdev, qeth, qshh, qldb)); + connect(m_whisper, SIGNAL(newIdToAdd(QString)), this, SLOT(addNewId(QString))); }); connect(ui->webView, &QWebView::loadFinished, [=]() @@ -183,11 +185,20 @@ Main::~Main() // Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor) // *after* the client is dead. m_ethereum->clientDieing(); + m_whisper->faceDieing(); g_logPost = simpleDebugOut; writeSettings(); } +void Main::addNewId(QString _ids) +{ + Secret _id = toSecret(_ids); + KeyPair kp(_id); + m_myIdentities.push_back(kp); + m_whisper->setIdentities(owned()); +} + dev::p2p::NetworkPreferences Main::netPrefs() const { return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); @@ -494,15 +505,28 @@ void Main::on_paranoia_triggered() void Main::writeSettings() { QSettings s("ethereum", "alethzero"); - QByteArray b; - b.resize(sizeof(Secret) * m_myKeys.size()); - auto p = b.data(); - for (auto i: m_myKeys) { - memcpy(p, &(i.secret()), sizeof(Secret)); - p += sizeof(Secret); + QByteArray b; + b.resize(sizeof(Secret) * m_myKeys.size()); + auto p = b.data(); + for (auto i: m_myKeys) + { + memcpy(p, &(i.secret()), sizeof(Secret)); + p += sizeof(Secret); + } + s.setValue("address", b); + } + { + QByteArray b; + b.resize(sizeof(Secret) * m_myIdentities.size()); + auto p = b.data(); + for (auto i: m_myIdentities) + { + memcpy(p, &(i.secret()), sizeof(Secret)); + p += sizeof(Secret); + } + s.setValue("identities", b); } - s.setValue("address", b); s.setValue("upnp", ui->upnp->isChecked()); s.setValue("forceAddress", ui->forceAddress->text()); @@ -538,21 +562,39 @@ void Main::readSettings(bool _skipGeometry) restoreGeometry(s.value("geometry").toByteArray()); restoreState(s.value("windowState").toByteArray()); - m_myKeys.clear(); - QByteArray b = s.value("address").toByteArray(); - if (b.isEmpty()) - m_myKeys.append(KeyPair::create()); - else { - h256 k; - for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) + m_myKeys.clear(); + QByteArray b = s.value("address").toByteArray(); + if (b.isEmpty()) + m_myKeys.append(KeyPair::create()); + else { - memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); - if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k))) - m_myKeys.append(KeyPair(k)); + h256 k; + for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) + { + memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); + if (!count(m_myKeys.begin(), m_myKeys.end(), KeyPair(k))) + m_myKeys.append(KeyPair(k)); + } } + ethereum()->setAddress(m_myKeys.back().address()); } - ethereum()->setAddress(m_myKeys.back().address()); + + { + m_myIdentities.clear(); + QByteArray b = s.value("identities").toByteArray(); + if (!b.isEmpty()) + { + h256 k; + for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) + { + memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); + if (!count(m_myIdentities.begin(), m_myIdentities.end(), KeyPair(k))) + m_myIdentities.append(KeyPair(k)); + } + } + } + m_peers = s.value("peers").toByteArray(); ui->upnp->setChecked(s.value("upnp", true).toBool()); ui->forceAddress->setText(s.value("forceAddress", "").toString()); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index b6c4c85e1..8905e53dc 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -77,7 +77,7 @@ public: dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } std::shared_ptr whisper() const { return m_webThree->whisper(); } - QList const& owned() const { return m_myKeys; } + QList owned() const { return m_myIdentities + m_myKeys; } public slots: void load(QString _file); @@ -153,9 +153,11 @@ private slots: void on_newIdentity_triggered(); void refreshWhisper(); + void addNewId(QString); signals: void poll(); + void idsChanged(); private: dev::p2p::NetworkPreferences netPrefs() const; @@ -179,6 +181,8 @@ private: void readSettings(bool _skipGeometry = false); void writeSettings(); + void keysChanged(); + bool isCreation() const; dev::u256 fee() const; dev::u256 total() const; @@ -189,8 +193,6 @@ private: unsigned installWatch(dev::h256 _tf, std::function const& _f); void uninstallWatch(unsigned _w); - void keysChanged(); - void onNewPending(); void onNewBlock(); void onNameRegChange(); @@ -228,6 +230,7 @@ private: QByteArray m_peers; QStringList m_servers; QList m_myKeys; + QList m_myIdentities; QString m_privateChain; dev::bytes m_data; dev::Address m_nameReg; @@ -255,4 +258,5 @@ private: QDev* m_dev = nullptr; QEthereum* m_ethereum = nullptr; QWhisper* m_whisper = nullptr; + QLDB* m_ldb = nullptr; }; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b2d56c7d1..2af4d2808 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -369,7 +369,7 @@ void Host::populateAddresses() shared_ptr Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId) { RecursiveGuard l(x_peers); - if (_a.port() < 30300 && _a.port() > 30303) + if (_a.port() < 30300 || _a.port() > 30303) cwarn << "Wierd port being recorded!"; if (_a.port() >= /*49152*/32768) diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index 535c43325..a13559ec5 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file QEthereum.cpp + * @author Gav Wood + * @date 2014 + */ + +#include #include #include #include @@ -536,8 +558,9 @@ void QEthereum::poll() // TODO: repot and hook all these up. -QWhisper::QWhisper(QObject* _p, std::shared_ptr const& _c): QObject(_p), m_face(_c) +QWhisper::QWhisper(QObject* _p, std::shared_ptr const& _c, QList _ids): QObject(_p), m_face(_c) { + setIdentities(_ids); } QWhisper::~QWhisper() @@ -613,6 +636,14 @@ void QWhisper::doPost(QString _json) face()->inject(toSealed(_json, m, from)); } +void QWhisper::setIdentities(QList const& _l) +{ + m_ids.clear(); + for (auto i: _l) + m_ids[i.pub()] = i.secret(); + emit idsChanged(); +} + static pair toWatch(QString _json) { shh::BuildTopicMask bt(shh::BuildTopicMask::Empty); @@ -714,11 +745,18 @@ QString QWhisper::newIdentity() Public QWhisper::makeIdentity() { KeyPair kp = KeyPair::create(); - m_ids[kp.pub()] = kp.sec(); - emit idsChanged(); + emit newIdToAdd(toQJS(kp.sec())); return kp.pub(); } +QString QWhisper::createGroup(QString _json) +{ +} + +QString QWhisper::addToGroup(QString _group, QString _who) +{ +} + void QWhisper::poll() { for (auto const& w: m_watches) @@ -740,6 +778,51 @@ void QWhisper::poll() } } +#include + +QLDB::QLDB(QObject* _p): QObject(_p) +{ + auto path = getDataDir() + "/.web3"; + boost::filesystem::create_directories(path); + ldb::Options o; + o.create_if_missing = true; + ldb::DB::Open(o, path, &m_db); +} + +QLDB::~QLDB() +{ +} + +void QLDB::put(QString _p, QString _k, QString _v) +{ + bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes(); + bytes v = toBytes(_v); + m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size())); +} + +QString QLDB::get(QString _p, QString _k) +{ + bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes(); + string ret; + m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret); + return toQJS(dev::asBytes(ret)); +} + +void QLDB::putString(QString _p, QString _k, QString _v) +{ + bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes(); + string v = _v.toStdString(); + m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size())); +} + +QString QLDB::getString(QString _p, QString _k) +{ + bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes(); + string ret; + m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret); + return QString::fromStdString(ret); +} + // extra bits needed to link on VS #ifdef _MSC_VER diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h index 9866a4e7a..042b3c081 100644 --- a/libqethereum/QEthereum.h +++ b/libqethereum/QEthereum.h @@ -1,11 +1,39 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file QEthereum.h + * @author Gav Wood + * @date 2014 + */ + #pragma once +#pragma warning(push) +#pragma warning(disable: 4100 4267) +#include +#pragma warning(pop) + #include #include #include #include #include +namespace ldb = leveldb; + namespace dev { namespace eth { class Interface; @@ -214,12 +242,14 @@ class QWhisper: public QObject Q_OBJECT public: - QWhisper(QObject* _p, std::shared_ptr const& _c); + QWhisper(QObject* _p, std::shared_ptr const& _c, QList _ids); virtual ~QWhisper(); std::shared_ptr face() const; void setFace(std::shared_ptr const& _c) { m_face = _c; } + void setIdentities(QList const& _l); + /// Call when the face() is going to be deleted to make this object useless but safe. void faceDieing(); @@ -230,6 +260,9 @@ public: Q_INVOKABLE QString newIdentity(); + Q_INVOKABLE QString newGroup(QString _id, QString _who); + Q_INVOKABLE QString addToGroup(QString _group, QString _who); + // Watches interface Q_INVOKABLE unsigned newWatch(QString _json); Q_INVOKABLE void killWatch(unsigned _w); @@ -247,6 +280,7 @@ public slots: signals: void watchChanged(unsigned _w, QString _envelopeJson); void idsChanged(); + void newIdToAdd(QString _id); private: std::weak_ptr m_face; @@ -255,16 +289,41 @@ private: std::map m_ids; }; +class QLDB: public QObject +{ + Q_OBJECT + +public: + QLDB(QObject* _p); + ~QLDB(); + + Q_INVOKABLE void put(QString _name, QString _key, QString _value); + Q_INVOKABLE QString get(QString _name, QString _key); + Q_INVOKABLE void putString(QString _name, QString _key, QString _value); + Q_INVOKABLE QString getString(QString _name, QString _key); + +private: + ldb::ReadOptions m_readOptions; + ldb::WriteOptions m_writeOptions; + + ldb::DB* m_db; +}; + // TODO: add p2p object -#define QETH_INSTALL_JS_NAMESPACE(_frame, _env, _web3, _eth, _shh) [_frame, _env, _web3, _eth, _shh]() \ +#define QETH_INSTALL_JS_NAMESPACE(_frame, _env, _web3, _eth, _shh, _ldb) [_frame, _env, _web3, _eth, _shh, _ldb]() \ { \ _frame->disconnect(); \ _frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \ _frame->addToJavaScriptWindowObject("web3", _web3, QWebFrame::ScriptOwnership); \ + if (_ldb) \ + { \ + _frame->addToJavaScriptWindowObject("_web3_dot_db", _ldb, QWebFrame::QtOwnership); \ + _frame->evaluateJavaScript("web3.db = _web3_dot_db"); \ + } \ if (_eth) \ { \ _frame->addToJavaScriptWindowObject("_web3_dot_eth", _eth, QWebFrame::ScriptOwnership); \ - _frame->evaluateJavaScript("_web3_dot_eth.makeWatch = function(a) { var ww = _web3_dot_eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_eth.killWatch(w); }; ret.changed = function(f) { _web3_dot_eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(_web3_dot_eth.watchMessages(this.w)) }; return ret; }"); \ + _frame->evaluateJavaScript("_web3_dot_eth.makeWatch = function(a) { var ww = _web3_dot_eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_eth.killWatch(this.w); }; ret.changed = function(f) { _web3_dot_eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(_web3_dot_eth.watchMessages(this.w)) }; return ret; }"); \ _frame->evaluateJavaScript("_web3_dot_eth.watch = function(a) { return _web3_dot_eth.makeWatch(JSON.stringify(a)) }"); \ _frame->evaluateJavaScript("_web3_dot_eth.transact = function(a, f) { var r = _web3_dot_eth.doTransact(JSON.stringify(a)); if (f) f(r); }"); \ _frame->evaluateJavaScript("_web3_dot_eth.call = function(a, f) { var ret = _web3_dot_eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \ @@ -277,7 +336,7 @@ private: if (_shh) \ { \ _frame->addToJavaScriptWindowObject("_web3_dot_shh", _shh, QWebFrame::ScriptOwnership); \ - _frame->evaluateJavaScript("_web3_dot_shh.makeWatch = function(json) { var ww = _web3_dot_shh.newWatch(json); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_shh.killWatch(w); }; ret.arrived = function(f) { _web3_dot_shh.watchChanged.connect(function(nw, envelope) { if (nw == ww) f(JSON.parse(envelope)) }); var existing = JSON.parse(_web3_dot_shh.watchMessages(ww)); for (var e in existing) f(existing[e]) }; return ret; }"); \ + _frame->evaluateJavaScript("_web3_dot_shh.makeWatch = function(json) { var ww = _web3_dot_shh.newWatch(json); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_shh.killWatch(this.w); }; ret.arrived = function(f) { _web3_dot_shh.watchChanged.connect(function(nw, envelope) { if (nw == ww) f(JSON.parse(envelope)) }); var existing = JSON.parse(_web3_dot_shh.watchMessages(this.w)); for (var e in existing) f(existing[e]) }; return ret; }"); \ _frame->evaluateJavaScript("_web3_dot_shh.watch = function(filter) { return _web3_dot_shh.makeWatch(JSON.stringify(filter)) }"); \ _frame->evaluateJavaScript("_web3_dot_shh.post = function(message) { return _web3_dot_shh.doPost(JSON.stringify(message)) }"); \ _frame->evaluateJavaScript("web3.shh = _web3_dot_shh"); \ diff --git a/third/MainWin.cpp b/third/MainWin.cpp index dd5b01733..1af1de7fc 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -102,19 +102,22 @@ Main::Main(QWidget *parent) : m_web3.reset(new WebThreeDirect("Third", getDataDir() + "/Third", false, {"eth", "shh"})); m_web3->connect(Host::pocHost()); + m_ldb = new QLDB(this); + connect(ui->webView, &QWebView::loadStarted, [this]() { // NOTE: no need to delete as QETH_INSTALL_JS_NAMESPACE adopts it. m_dev = new QDev(this); - m_ethereum = new QEthereum(this, ethereum(), owned()); - m_whisper = new QWhisper(this, whisper()); + m_ethereum = new QEthereum(this, ethereum(), m_myKeys); + m_whisper = new QWhisper(this, whisper(), owned()); QWebFrame* f = ui->webView->page()->mainFrame(); f->disconnect(SIGNAL(javaScriptWindowObjectCleared())); auto qdev = m_dev; auto qeth = m_ethereum; auto qshh = m_whisper; - connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qdev, qeth, qshh)); + auto qldb = m_ldb; + connect(f, &QWebFrame::javaScriptWindowObjectCleared, QETH_INSTALL_JS_NAMESPACE(f, this, qdev, qeth, qshh, qldb)); }); connect(ui->webView, &QWebView::loadFinished, [=]() diff --git a/third/MainWin.h b/third/MainWin.h index 0122cc257..3fa848439 100644 --- a/third/MainWin.h +++ b/third/MainWin.h @@ -61,8 +61,8 @@ public: dev::eth::Client* ethereum() const; std::shared_ptr whisper() const; - QList const& owned() const { return m_myKeys; } - + QList owned() const { return m_myKeys + m_myIdentities; } + public slots: void note(QString _entry); void debug(QString _entry); @@ -121,6 +121,7 @@ private: std::unique_ptr m_web3; QList m_myKeys; + QList m_myIdentities; std::map> m_handlers; unsigned m_nameRegFilter = (unsigned)-1; @@ -135,4 +136,5 @@ private: QDev* m_dev = nullptr; QEthereum* m_ethereum = nullptr; QWhisper* m_whisper = nullptr; + QLDB* m_ldb = nullptr; }; From eb73eb2974e88ecadbe4da7b382f3648b5c980e5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 27 Oct 2014 12:17:06 +0100 Subject: [PATCH 2/4] Build fix. --- libqethereum/QEthereum.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index a13559ec5..89d5f1ea5 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -749,12 +749,18 @@ Public QWhisper::makeIdentity() return kp.pub(); } -QString QWhisper::createGroup(QString _json) +QString QWhisper::newGroup(QString _me, QString _others) { + (void)_me; + (void)_others; + return ""; } QString QWhisper::addToGroup(QString _group, QString _who) { + (void)_group; + (void)_who; + return ""; } void QWhisper::poll() From 15013a3e8866052206a394fd04e396e757761a69 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 27 Oct 2014 14:13:16 +0100 Subject: [PATCH 3/4] PoC-7: Reversion of 0-hashes, empty-list hashes and sha3('') -> '' --- alethzero/MainWin.cpp | 1 + libdevcrypto/SHA3.cpp | 4 +++- libdevcrypto/SHA3.h | 1 + libdevcrypto/TrieDB.h | 6 +++--- libethcore/BlockInfo.cpp | 10 +++------- libethcore/CommonEth.cpp | 2 +- libethereum/BlockChain.cpp | 4 ++-- libethereum/Client.cpp | 3 +++ libethereum/State.cpp | 4 ++-- libethereum/State.h | 10 ++-------- libethereum/Transaction.cpp | 3 ++- libethereum/Transaction.h | 9 ++++++++- libevm/VM.h | 23 ++++++++++++++++++++--- libevmface/Instruction.cpp | 6 ++++-- libevmface/Instruction.h | 3 ++- test/MemTrie.cpp | 4 ++-- test/TrieHash.cpp | 6 +++--- test/crypto.cpp | 3 +++ test/state.cpp | 1 + test/vm.cpp | 7 ++++--- 20 files changed, 70 insertions(+), 40 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 674509b53..227f739ce 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1784,6 +1784,7 @@ void Main::on_debug_clicked() t.gasPrice = gasPrice(); t.gas = ui->gas->value(); t.data = m_data; + t.type = isCreation() ? Transaction::ContractCreation : Transaction::MessageCall; t.receiveAddress = isCreation() ? Address() : fromString(ui->destination->currentText()); t.sign(s); auto r = t.rlp(); diff --git a/libdevcrypto/SHA3.cpp b/libdevcrypto/SHA3.cpp index b3a6e5955..eaabae0ff 100644 --- a/libdevcrypto/SHA3.cpp +++ b/libdevcrypto/SHA3.cpp @@ -20,8 +20,9 @@ */ #include "SHA3.h" -#include "CryptoPP.h" +#include +#include "CryptoPP.h" using namespace std; using namespace dev; @@ -29,6 +30,7 @@ namespace dev { h256 EmptySHA3 = sha3(bytesConstRef()); +h256 ZeroRLPSHA3 = sha3(rlp(bytesConstRef())); std::string sha3(std::string const& _input, bool _hex) { diff --git a/libdevcrypto/SHA3.h b/libdevcrypto/SHA3.h index fc2cfcfc3..5948cb3bd 100644 --- a/libdevcrypto/SHA3.h +++ b/libdevcrypto/SHA3.h @@ -57,6 +57,7 @@ inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef((bytes*)&_inpu inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); } extern h256 EmptySHA3; +extern h256 ZeroRLPSHA3; // Other crypto convenience routines diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index 1fca92294..0ca0b9d98 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -74,7 +74,7 @@ public: void init(); void setRoot(h256 _root) { - m_root = _root == h256() ? c_shaNull : _root; + m_root = _root; if (m_root == c_shaNull && !m_db->exists(m_root)) init(); @@ -82,14 +82,14 @@ public: if (!node(m_root).size()) BOOST_THROW_EXCEPTION(RootNotFound()); } - bool haveRoot(h256 _root, bool _enforceRefs = true) { return _root == h256() ? true : m_db->lookup(_root, _enforceRefs).size(); } + bool haveRoot(h256 _root, bool _enforceRefs = true) { return _root == c_shaNull ? true : m_db->lookup(_root, _enforceRefs).size(); } /// True if the trie is uninitialised (i.e. that the DB doesn't contain the root node). bool isNull() const { return !node(m_root).size(); } /// True if the trie is initialised but empty (i.e. that the DB contains the root node which is empty). bool isEmpty() const { return m_root == c_shaNull && node(m_root).size(); } - h256 root() const { assert(node(m_root).size()); h256 ret = (m_root == c_shaNull ? h256() : m_root); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return ret; } // patch the root in the case of the empty trie. TODO: handle this properly. + h256 root() const { assert(node(m_root).size()); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return m_root; } // patch the root in the case of the empty trie. TODO: handle this properly. void debugPrint() {} diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 4c9e2bef7..6e4af247f 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -60,11 +60,9 @@ auto static const c_sha3EmptyList = sha3(RLPEmptyList); void BlockInfo::fillStream(RLPStream& _s, bool _nonce) const { - _s.appendList(_nonce ? 13 : 12) << parentHash; - _s.append(sha3Uncles == c_sha3EmptyList ? h256() : sha3Uncles, false, true); - _s << coinbaseAddress; - _s.append(stateRoot, false, true).append(transactionsRoot, false, true); - _s << difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData; + _s.appendList(_nonce ? 13 : 12) + << parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot + << difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData; if (_nonce) _s << nonce; } @@ -83,8 +81,6 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce) { parentHash = _header[field = 0].toHash(); sha3Uncles = _header[field = 1].toHash(); - if (sha3Uncles == h256()) - sha3Uncles = c_sha3EmptyList; coinbaseAddress = _header[field = 2].toHash
(); stateRoot = _header[field = 3].toHash(); transactionsRoot = _header[field = 4].toHash(); diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 5f989689c..44ebe19c5 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -34,7 +34,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 36; +const unsigned c_protocolVersion = 37; const unsigned c_databaseVersion = 3; static const vector> g_units = diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 7b7b8a9e5..b08179f9e 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -101,8 +101,8 @@ bytes BlockChain::createGenesisBlock() stateRoot = state.root(); } - block.appendList(13) << h256() << bytes() << h160(); - block.append(stateRoot, false, true) << bytes() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); + block.appendList(13) + << h256() << c_shaNull << h160() << stateRoot << c_shaNull << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList); return block.out(); diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index ecce7406d..dc36957ee 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -325,6 +325,7 @@ void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _ t.value = _value; t.gasPrice = _gasPrice; t.gas = _gas; + t.type = Transaction::MessageCall; t.receiveAddress = _dest; t.data = _data; t.sign(_secret); @@ -348,6 +349,7 @@ bytes Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _dat t.value = _value; t.gasPrice = _gasPrice; t.gas = _gas; + t.type = Transaction::ContractCreation; t.receiveAddress = _dest; t.data = _data; t.sign(_secret); @@ -373,6 +375,7 @@ Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u2 t.value = _endowment; t.gasPrice = _gasPrice; t.gas = _gas; + t.type = Transaction::ContractCreation; t.receiveAddress = Address(); t.data = _init; t.sign(_secret); diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 931ee2cf6..9ec835833 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -353,9 +353,9 @@ void State::ensureCached(std::map& _cache, Address _a, bo RLP state(stateBack); AddressState s; if (state.isNull()) - s = AddressState(0, 0, h256(), EmptySHA3); + s = AddressState(0, 0, ZeroRLPSHA3, EmptySHA3); else - s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].isEmpty() ? EmptySHA3 : state[3].toHash()); + s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].toHash()); bool ok; tie(it, ok) = _cache.insert(make_pair(_a, s)); } diff --git a/libethereum/State.h b/libethereum/State.h index dd6043c73..6e65bfd53 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -367,16 +367,10 @@ void commit(std::map const& _cache, DB& _db, TrieDB(); gasPrice = rlp[field = 1].toInt(); gas = rlp[field = 2].toInt(); + type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall; receiveAddress = rlp[field = 3].toHash
(); value = rlp[field = 4].toInt(); data = rlp[field = 5].toBytes(); @@ -88,7 +89,7 @@ void Transaction::fillStream(RLPStream& _s, bool _sig) const { _s.appendList((_sig ? 3 : 0) + 6); _s << nonce << gasPrice << gas; - if (receiveAddress) + if (type == MessageCall) _s << receiveAddress; else _s << ""; diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index eb40c5fcb..ca73ba06a 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -32,13 +32,20 @@ namespace eth struct Transaction { + enum Type + { + ContractCreation, + MessageCall + }; + Transaction() {} Transaction(bytesConstRef _rlp, bool _checkSender = false); Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {} - bool operator==(Transaction const& _c) const { return receiveAddress == _c.receiveAddress && value == _c.value && data == _c.data; } + bool operator==(Transaction const& _c) const { return type == _c.type && (type == ContractCreation || receiveAddress == _c.receiveAddress) && value == _c.value && data == _c.data; } bool operator!=(Transaction const& _c) const { return !operator==(_c); } + Type type; ///< True if this is a contract-creation transaction. F 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. diff --git a/libevm/VM.h b/libevm/VM.h index ce8001bbf..226b1d6cf 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -236,7 +236,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::PUSH31: case Instruction::PUSH32: break; - case Instruction::NEG: + case Instruction::BNOT: case Instruction::NOT: case Instruction::CALLDATALOAD: case Instruction::EXTCODESIZE: @@ -262,6 +262,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::XOR: case Instruction::BYTE: case Instruction::JUMPI: + case Instruction::SIGNEXTEND: require(2); break; case Instruction::ADDMOD: @@ -368,8 +369,8 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256); break; } - case Instruction::NEG: - m_stack.back() = ~(m_stack.back() - 1); + case Instruction::BNOT: + m_stack.back() = ~m_stack.back(); break; case Instruction::LT: m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0; @@ -420,6 +421,22 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.pop_back(); m_stack.pop_back(); break; + case Instruction::SIGNEXTEND: + { + unsigned k = m_stack[m_stack.size() - 2]; + if (k > 31) + m_stack[m_stack.size() - 2] = m_stack.back(); + else + { + u256 b = m_stack.back(); + if ((b >> (k * 8)) & 0x80) + for (int i = 31; i > k; --i) + b |= (u256(0xff) << i); + m_stack[m_stack.size() - 2] = b; + } + m_stack.pop_back(); + break; + } case Instruction::SHA3: { unsigned inOff = (unsigned)m_stack.back(); diff --git a/libevmface/Instruction.cpp b/libevmface/Instruction.cpp index 7b253f388..c9b6ea2ce 100644 --- a/libevmface/Instruction.cpp +++ b/libevmface/Instruction.cpp @@ -39,7 +39,7 @@ const std::map dev::eth::c_instructions = { "MOD", Instruction::MOD }, { "SMOD", Instruction::SMOD }, { "EXP", Instruction::EXP }, - { "NEG", Instruction::NEG }, + { "BNOT", Instruction::BNOT }, { "LT", Instruction::LT }, { "GT", Instruction::GT }, { "SLT", Instruction::SLT }, @@ -52,6 +52,7 @@ const std::map dev::eth::c_instructions = { "BYTE", Instruction::BYTE }, { "ADDMOD", Instruction::ADDMOD }, { "MULMOD", Instruction::MULMOD }, + { "SIGNEXTEND", Instruction::SIGNEXTEND }, { "SHA3", Instruction::SHA3 }, { "ADDRESS", Instruction::ADDRESS }, { "BALANCE", Instruction::BALANCE }, @@ -166,7 +167,7 @@ static const std::map c_instructionInfo = { Instruction::MOD, { "MOD", 0, 2, 1 } }, { Instruction::SMOD, { "SMOD", 0, 2, 1 } }, { Instruction::EXP, { "EXP", 0, 2, 1 } }, - { Instruction::NEG, { "NEG", 0, 1, 1 } }, + { Instruction::BNOT, { "BNOT", 0, 1, 1 } }, { Instruction::LT, { "LT", 0, 2, 1 } }, { Instruction::GT, { "GT", 0, 2, 1 } }, { Instruction::SLT, { "SLT", 0, 2, 1 } }, @@ -179,6 +180,7 @@ static const std::map c_instructionInfo = { Instruction::BYTE, { "BYTE", 0, 2, 1 } }, { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1 } }, { Instruction::MULMOD, { "MULMOD", 0, 3, 1 } }, + { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1 } }, { Instruction::SHA3, { "SHA3", 0, 2, 1 } }, { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1 } }, { Instruction::BALANCE, { "BALANCE", 0, 1, 1 } }, diff --git a/libevmface/Instruction.h b/libevmface/Instruction.h index b6aa477b1..faad50fb2 100644 --- a/libevmface/Instruction.h +++ b/libevmface/Instruction.h @@ -44,7 +44,7 @@ enum class Instruction: uint8_t MOD, ///< modulo remainder operation SMOD, ///< signed modulo remainder operation EXP, ///< exponential operation - NEG, ///< negation operation + BNOT, ///< bitwise not LT, ///< less-than comparision GT, ///< greater-than comparision SLT, ///< signed less-than comparision @@ -58,6 +58,7 @@ enum class Instruction: uint8_t BYTE, ///< retrieve single byte from word ADDMOD, ///< unsigned modular addition MULMOD, ///< unsigned modular multiplication + SIGNEXTEND, ///< extend length of signed integer SHA3 = 0x20, ///< compute SHA3-256 hash ADDRESS = 0x30, ///< get address of currently executing account diff --git a/test/MemTrie.cpp b/test/MemTrie.cpp index 4879f2674..c3a44e1e5 100644 --- a/test/MemTrie.cpp +++ b/test/MemTrie.cpp @@ -437,12 +437,12 @@ MemTrie::~MemTrie() h256 MemTrie::hash256() const { - return m_root ? m_root->hash256() : h256(); + return m_root ? m_root->hash256() : sha3(dev::rlp(bytesConstRef())); } bytes MemTrie::rlp() const { - return m_root ? m_root->rlp() : bytes(); + return m_root ? m_root->rlp() : dev::rlp(bytesConstRef()); } void MemTrie::debugPrint() diff --git a/test/TrieHash.cpp b/test/TrieHash.cpp index af32e870d..ee4f2e87d 100644 --- a/test/TrieHash.cpp +++ b/test/TrieHash.cpp @@ -162,7 +162,7 @@ h256 hash256(StringMap const& _s) { // build patricia tree. if (_s.empty()) - return h256(); + return sha3(rlp("")); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) hexMap[asNibbles(i->first)] = i->second; @@ -175,7 +175,7 @@ bytes rlp256(StringMap const& _s) { // build patricia tree. if (_s.empty()) - return bytes(); + return rlp(""); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) hexMap[asNibbles(i->first)] = i->second; @@ -188,7 +188,7 @@ h256 hash256(u256Map const& _s) { // build patricia tree. if (_s.empty()) - return h256(); + return sha3(rlp("")); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) hexMap[asNibbles(toBigEndianString(i->first))] = asString(rlp(i->second)); diff --git a/test/crypto.cpp b/test/crypto.cpp index 0d3b6202f..67286bfca 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -341,6 +341,7 @@ BOOST_AUTO_TEST_CASE(eth_keypairs) { eth::Transaction t; t.nonce = 0; + t.type = eth::Transaction::MessageCall; t.receiveAddress = h160(fromHex("944400f4b88ac9589a0f17ed4671da26bddb668b")); t.value = 1000; auto rlp = t.rlp(false); @@ -369,6 +370,7 @@ int cryptoTest() { eth::Transaction t; t.nonce = 0; + t.type = eth::Transaction::MessageCall; t.receiveAddress = h160(fromHex("944400f4b88ac9589a0f17ed4671da26bddb668b")); t.value = 1000; auto rlp = t.rlp(false); @@ -397,6 +399,7 @@ int cryptoTest() Transaction t; t.nonce = 0; t.value = 1; // 1 wei. + t.type = eth::Transaction::MessageCall; t.receiveAddress = toAddress(sha3("123")); bytes sig64 = toBigEndian(t.vrs.r) + toBigEndian(t.vrs.s); diff --git a/test/state.cpp b/test/state.cpp index 99ce30957..b0f279bac 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -68,6 +68,7 @@ int stateTest() Transaction t; t.nonce = s.transactionsFrom(myMiner.address()); t.value = 1000; // 1e3 wei. + t.type = eth::Transaction::MessageCall; t.receiveAddress = me.address(); t.sign(myMiner.secret()); assert(t.sender() == myMiner.address()); diff --git a/test/vm.cpp b/test/vm.cpp index 2e4571be5..b49c5cd27 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -56,14 +56,13 @@ h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFun get<3>(addresses[ret]) = m_s.code(ret); } - t.receiveAddress = ret; + t.type = eth::Transaction::ContractCreation; callcreates.push_back(t); return ret; } bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc const&, Address _myAddressOverride, Address _codeAddressOverride) { - u256 contractgas = 0xffff; Transaction t; @@ -71,6 +70,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, t.gasPrice = gasPrice; t.gas = *_gas; t.data = _data.toVector(); + t.type = eth::Transaction::MessageCall; t.receiveAddress = _receiveAddress; callcreates.push_back(t); @@ -384,7 +384,7 @@ mArray FakeExtVM::exportCallCreates() for (Transaction const& tx: callcreates) { mObject o; - o["destination"] = toString(tx.receiveAddress); + o["destination"] = tx.type == Transaction::ContractCreation ? "" : toString(tx.receiveAddress); push(o, "gasLimit", tx.gas); push(o, "value", tx.value); o["data"] = "0x" + toHex(tx.data); @@ -403,6 +403,7 @@ void FakeExtVM::importCallCreates(mArray& _callcreates) BOOST_REQUIRE(tx.count("destination") > 0); BOOST_REQUIRE(tx.count("gasLimit") > 0); Transaction t; + t.type = tx["destination"].get_str().empty() ? Transaction::ContractCreation : Transaction::MessageCall; t.receiveAddress = Address(tx["destination"].get_str()); t.value = toInt(tx["value"]); t.gas = toInt(tx["gasLimit"]); From 52e385905e2a1730296b7b75c178e0a63d0da5cc Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 27 Oct 2014 16:32:53 +0100 Subject: [PATCH 4/4] Move suicides into SubState and include logs and refunds. Refund gas from zero-resetting SSTOREs. --- libdevcore/CommonData.h | 30 +++++++++++++++++++----------- libethereum/Executive.cpp | 4 +++- libethereum/Executive.h | 3 +++ libethereum/ExtVM.h | 4 ++-- libethereum/State.cpp | 16 +++++++--------- libethereum/State.h | 4 ++-- libevm/ExtVMFace.h | 28 ++++++++++++++++++++++------ libevm/FeeStructure.cpp | 4 +++- libevm/FeeStructure.h | 4 +++- libevm/VM.h | 7 +++++-- test/vm.cpp | 24 ++++++++++++++---------- test/vm.h | 2 +- 12 files changed, 84 insertions(+), 46 deletions(-) diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 11850fa69..5f14f38f8 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -190,7 +190,7 @@ void pushFront(_T& _t, _U _e) _t[0] = _e; } -/// Concatenate two vectors of elements. _T must be POD. +/// Concatenate two vectors of elements of POD types. template inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) { @@ -201,30 +201,38 @@ inline std::vector<_T>& operator+=(std::vector -inline std::vector<_T> operator+(std::vector::value, _T>::type> const& _a, std::vector<_T> const& _b) +inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) +{ + _a.reserve(_a.size() + _b.size()); + for (auto& i: _b) + _a.push_back(i); + return _a; +} + +/// Concatenate two vectors of elements. +template +inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b) { std::vector<_T> ret(_a); return ret += _b; } -/// Concatenate two vectors of elements. _T must be POD. +/// Merge two sets of elements. template -inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) +inline std::set<_T>& operator+=(std::set<_T>& _a, std::set<_T> const& _b) { - _a.reserve(_a.size() + _b.size()); for (auto& i: _b) - _a.push_back(i); + _a.insert(i); return _a; - } -/// Concatenate two vectors of elements. _T must be POD. +/// Merge two sets of elements. template -inline std::vector<_T> operator+(std::vector::value, _T>::type> const& _a, std::vector<_T> const& _b) +inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b) { - std::vector<_T> ret(_a); + std::set<_T> ret(_a); return ret += _b; } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 193010cfa..840287ca9 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -172,6 +172,8 @@ bool Executive::go(OnOpFunc const& _onOp) try { m_out = m_vm->go(*m_ext, _onOp); + if (m_ext) + m_endGas += min((m_t.gas - m_endGas) / 2, m_ext->sub.refunds); m_endGas = m_vm->gas(); } catch (StepsDone const&) @@ -236,6 +238,6 @@ void Executive::finalize(OnOpFunc const&) // Suicides... if (m_ext) - for (auto a: m_ext->suicides) + for (auto a: m_ext->sub.suicides) m_s.m_cache[a].kill(); } diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 82b7df7e9..cdfe23966 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -61,6 +61,7 @@ public: bytesConstRef out() const { return m_out; } h160 newAddress() const { return m_newAddress; } + LogEntries const& logs() const { return m_logs; } VM const& vm() const { return *m_vm; } State const& state() const { return m_s; } @@ -77,6 +78,8 @@ private: Transaction m_t; Address m_sender; u256 m_endGas; + + LogEntries m_logs; }; } diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index fc76d56b0..59b6eb2ab 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -61,7 +61,7 @@ public: m_s.noteSending(myAddress); if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); + auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; @@ -72,7 +72,7 @@ public: { if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); + auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 9ec835833..40fac4883 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1119,7 +1119,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) return e.gasUsed(); } -bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_originAddress) _originAddress = _senderAddress; @@ -1154,9 +1154,8 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA { auto out = vm.go(evm, _onOp); memcpy(_out.data(), out.data(), std::min(out.size(), _out.size())); - if (o_suicides) - for (auto i: evm.suicides) - o_suicides->insert(i); + if (o_sub) + *o_sub += evm.sub; if (o_ms) o_ms->output = out.toBytes(); } @@ -1189,7 +1188,7 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA return true; } -h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_origin) _origin = _sender; @@ -1218,9 +1217,8 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, out = vm.go(evm, _onOp); if (o_ms) o_ms->output = out.toBytes(); - if (o_suicides) - for (auto i: evm.suicides) - o_suicides->insert(i); + if (o_sub) + *o_sub += evm.sub; } catch (OutOfGas const& /*_e*/) { @@ -1240,7 +1238,7 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, clog(StateChat) << "std::exception in VM: " << _e.what(); } - // TODO: CHECK: IS THIS CORRECT?! (esp. given account created prior to revertion init.) + // TODO: CHECK: AUDIT: IS THIS CORRECT?! (esp. given account created prior to revertion init.) // Write state out only in the case of a non-out-of-gas transaction. if (revert) diff --git a/libethereum/State.h b/libethereum/State.h index 6e65bfd53..776fb80e7 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -288,12 +288,12 @@ private: // We assume all instrinsic fees are paid up before this point. /// Execute a contract-creation transaction. - h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Execute a call. /// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. /// @returns false if the call ran out of gas before completion. true otherwise. - bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 67fec9321..b57818907 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -33,13 +34,28 @@ namespace dev namespace eth { -struct Post +struct LogEntry { Address from; - Address to; - u256 value; + h256 topics; bytes data; - u256 gas; +}; + +using LogEntries = std::vector; + +struct SubState +{ + std::set
suicides; ///< Any accounts that have suicided. + LogEntries logs; ///< Any logs. + u256 refunds; ///< Refund counter of SSTORE nonzero->zero. + + SubState& operator+=(SubState const& _s) + { + suicides += _s.suicides; + refunds += _s.refunds; + suicides += _s.suicides; + return *this; + } }; using OnOpFunc = std::function; @@ -80,7 +96,7 @@ public: virtual u256 txCount(Address) { return 0; } /// Suicide the associated contract and give proceeds to the given address. - virtual void suicide(Address) { suicides.insert(myAddress); } + virtual void suicide(Address) { sub.suicides.insert(myAddress); } /// Create a new (contract) account. virtual h160 create(u256, u256*, bytesConstRef, OnOpFunc const&) { return h160(); } @@ -103,7 +119,7 @@ public: bytesConstRef code; ///< Current code that is executing. BlockInfo previousBlock; ///< The previous block's information. BlockInfo currentBlock; ///< The current block's information. - std::set
suicides; ///< Any accounts that have suicided. + SubState sub; ///< Sub-band VM state (suicides, refund counter, logs). unsigned depth; ///< Depth of the present call. }; diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index d29b9fef9..47236b506 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -29,7 +29,9 @@ u256 const dev::eth::c_stepGas = 1; u256 const dev::eth::c_balanceGas = 20; u256 const dev::eth::c_sha3Gas = 20; u256 const dev::eth::c_sloadGas = 20; -u256 const dev::eth::c_sstoreGas = 100; +u256 const dev::eth::c_sstoreSetGas = 300; +u256 const dev::eth::c_sstoreResetGas = 100; +u256 const dev::eth::c_sstoreRefundGas = 100; u256 const dev::eth::c_createGas = 100; u256 const dev::eth::c_callGas = 20; u256 const dev::eth::c_memoryGas = 1; diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 76be9a398..84a2551d9 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -32,7 +32,9 @@ extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD 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_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero. +extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change. +extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero. 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. diff --git a/libevm/VM.h b/libevm/VM.h index 226b1d6cf..f3c82c714 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -117,11 +117,14 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::SSTORE: require(2); if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) - runGas = c_sstoreGas * 2; + runGas = c_sstoreSetGas; else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) + { runGas = 0; + _ext.sub.refunds += c_sstoreRefundGas; + } else - runGas = c_sstoreGas; + runGas = c_sstoreResetGas; break; case Instruction::SLOAD: diff --git a/test/vm.cpp b/test/vm.cpp index b49c5cd27..fe4863041 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -45,7 +45,7 @@ h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFun m_s.noteSending(myAddress); m_ms.internal.resize(m_ms.internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1); + auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); @@ -91,7 +91,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, if (!m_s.addresses().count(myAddress)) { m_ms.internal.resize(m_ms.internal.size() + 1); - auto na = m_s.createNewAddress(myAddress, myAddress, balance(myAddress), gasPrice, &contractgas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1); + auto na = m_s.createNewAddress(myAddress, myAddress, balance(myAddress), gasPrice, &contractgas, init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); if (na != myAddress) @@ -116,7 +116,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, { m_s.noteSending(myAddress); m_ms.internal.resize(m_ms.internal.size() + 1); - auto na = m_s.createNewAddress(_codeAddressOverride ? _codeAddressOverride : _receiveAddress, myAddress, balance(_codeAddressOverride ? _codeAddressOverride : _receiveAddress), gasPrice, &contractgas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); + auto na = m_s.createNewAddress(_codeAddressOverride ? _codeAddressOverride : _receiveAddress, myAddress, balance(_codeAddressOverride ? _codeAddressOverride : _receiveAddress), gasPrice, &contractgas, init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); @@ -131,7 +131,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, m_ms.internal.resize(m_ms.internal.size() + 1); - auto ret = m_s.call(_receiveAddress,_codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _value, gasPrice, _data, _gas, _out, origin, &suicides, &(m_ms.internal.back()), OnOpFunc(), 1); + auto ret = m_s.call(_receiveAddress,_codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _value, gasPrice, _data, _gas, _out, origin, &sub, &(m_ms.internal.back()), OnOpFunc(), 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); @@ -146,12 +146,15 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, if (!ret) return false; + // TODO: @CJentzsch refund SSTORE stuff. + // TODO: @CJentzsch test logs. + // do suicides - for (auto const& f: suicides) + for (auto const& f: sub.suicides) addresses.erase(f); // get storage - if ((get<0>(addresses[myAddress]) >= _value) && (suicides.find(_receiveAddress) == suicides.end())) + if ((get<0>(addresses[myAddress]) >= _value) && (sub.suicides.find(_receiveAddress) == sub.suicides.end())) { for (auto const& j: m_s.storage(_receiveAddress)) { @@ -419,8 +422,11 @@ void FakeExtVM::importCallCreates(mArray& _callcreates) } } -h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +// THIS IS BROKEN AND NEEDS TO BE REMOVED. +h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { + (void)o_sub; + if (!_origin) _origin = _sender; @@ -446,9 +452,7 @@ h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _end out = vm.go(evm, _onOp); if (o_ms) o_ms->output = out.toBytes(); - if (o_suicides) - for (auto i: evm.suicides) - o_suicides->insert(i); + // TODO: deal with evm.sub } catch (OutOfGas const& /*_e*/) { diff --git a/test/vm.h b/test/vm.h index d9dca1d7a..58ff58cd9 100644 --- a/test/vm.h +++ b/test/vm.h @@ -44,7 +44,7 @@ class FakeState: public eth::State { public: /// Execute a contract-creation transaction. - h160 createNewAddress(Address _newAddress, Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = {}, std::set
* o_suicides = nullptr, eth::Manifest* o_ms = nullptr, eth::OnOpFunc const& _onOp = {}, unsigned _level = 0); + h160 createNewAddress(Address _newAddress, Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = {}, eth::SubState* o_suicides = nullptr, eth::Manifest* o_ms = nullptr, eth::OnOpFunc const& _onOp = {}, unsigned _level = 0); }; class FakeExtVM: public eth::ExtVMFace