diff --git a/.gitignore b/.gitignore index 8d8974809..43ed10adf 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,8 @@ profile DerivedData project.pbxproj +# JetBrains stuff +.idea/ doc/html *.autosave diff --git a/CMakeLists.txt b/CMakeLists.txt index 4603bb898..778f7dafe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ function(createDefaultCacheConfig) set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.") set(JUSTTESTS OFF CACHE BOOL "Build only for tests.") set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (requried unless HEADLESS)") + set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed") endfunction() @@ -125,7 +126,7 @@ configureProject() set (ETH_HAVE_WEBENGINE 1) message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") -message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}") +message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}; USENPM: ${USENPM}") # Default TARGET_PLATFORM to "linux". diff --git a/README.md b/README.md index 6cd9c7adf..2bb57566c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ## Ethereum C++ Client. +[![Join the chat at https://gitter.im/ethereum/cpp-ethereum](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/cpp-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + By Gav Wood et al, 2013, 2014, 2015. | Linux | OSX | Windows diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index cbc9e087a..49c847a16 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -56,6 +56,7 @@ target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} web3jsonrpc) target_link_libraries(${EXECUTABLE} jsqrc) target_link_libraries(${EXECUTABLE} natspec) +target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES}) if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) target_link_libraries(${EXECUTABLE} serpent) diff --git a/alethzero/DappHost.cpp b/alethzero/DappHost.cpp new file mode 100644 index 000000000..fd73c1b17 --- /dev/null +++ b/alethzero/DappHost.cpp @@ -0,0 +1,139 @@ +/* + 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 DappHost.cpp + * @author Arkadiy Paronyan + * @date 2015 + */ + +#include "DappHost.h" +#include +#include +#include +#include + +using namespace dev; + +DappHost::DappHost(int _port, int _threads): + m_port(_port), m_threads(_threads), m_running(false), m_daemon(nullptr) +{ + startListening(); +} + +DappHost::~DappHost() +{ + stopListening(); +} + +void DappHost::startListening() +{ + if(!this->m_running) + { + this->m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, this->m_port, nullptr, nullptr, &DappHost::callback, this, MHD_OPTION_THREAD_POOL_SIZE, this->m_threads, MHD_OPTION_END); + if (this->m_daemon != nullptr) + this->m_running = true; + } +} + +void DappHost::stopListening() +{ + if(this->m_running) + { + MHD_stop_daemon(this->m_daemon); + this->m_running = false; + } +} + +void DappHost::sendOptionsResponse(MHD_Connection* _connection) +{ + MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1); + MHD_add_response_header(result, "Allow", "GET, OPTIONS"); + MHD_add_response_header(result, "Access-Control-Allow-Headers", "origin, content-type, accept"); + MHD_add_response_header(result, "DAV", "1"); + MHD_queue_response(_connection, MHD_HTTP_OK, result); + MHD_destroy_response(result); +} + +void DappHost::sendNotAllowedResponse(MHD_Connection* _connection) +{ + MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1); + MHD_add_response_header(result, "Allow", "GET, OPTIONS"); + MHD_queue_response(_connection, MHD_HTTP_METHOD_NOT_ALLOWED, result); + MHD_destroy_response(result); +} + +void DappHost::sendResponse(std::string const& _url, MHD_Connection* _connection) +{ + QUrl requestUrl(QString::fromStdString(_url)); + QString path = requestUrl.path().toLower(); + if (path.isEmpty()) + path = "/"; + + bytesConstRef response; + unsigned code = MHD_HTTP_NOT_FOUND; + std::string contentType; + + while (!path.isEmpty()) + { + auto iter = m_entriesByPath.find(path); + if (iter != m_entriesByPath.end()) + { + ManifestEntry const* entry = iter->second; + auto contentIter = m_dapp.content.find(entry->hash); + if (contentIter == m_dapp.content.end()) + break; + + response = bytesConstRef(contentIter->second.data(), contentIter->second.size()); + code = entry->httpStatus != 0 ? entry->httpStatus : MHD_HTTP_OK; + contentType = entry->contentType; + break; + } + path.truncate(path.length() - 1); + path = path.mid(0, path.lastIndexOf('/')); + } + + MHD_Response *result = MHD_create_response_from_data(response.size(), const_cast(response.data()), 0, 1); + if (!contentType.empty()) + MHD_add_response_header(result, "Content-Type", contentType.c_str()); + MHD_queue_response(_connection, code, result); + MHD_destroy_response(result); +} + +int DappHost::callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls) +{ + (void)_version; + (void)_uploadData; + (void)_uploadDataSize; + (void)_conCls; + DappHost* host = static_cast(_cls); + if (std::string("GET") == _method) + host->sendResponse(std::string(_url), _connection); + else if (std::string("OPTIONS") == _method) + host->sendOptionsResponse(_connection); + else + host->sendNotAllowedResponse(_connection); + return MHD_YES; +} + +QUrl DappHost::hostDapp(Dapp&& _dapp) +{ + m_dapp = std::move(_dapp); + m_entriesByPath.clear(); + for (ManifestEntry const& entry: m_dapp.manifest.entries) + m_entriesByPath[QString::fromStdString(entry.path)] = &entry; + + return QUrl(QString("http://localhost:%1/").arg(m_port)); +} diff --git a/alethzero/DappHost.h b/alethzero/DappHost.h new file mode 100644 index 000000000..985bd34d9 --- /dev/null +++ b/alethzero/DappHost.h @@ -0,0 +1,58 @@ +/* + 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 DappHost.h + * @author Arkadiy Paronyan + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include "DappLoader.h" + +struct MHD_Daemon; +struct MHD_Connection; + +/// DApp web server. Servers web content, resolves paths by hashes +class DappHost +{ +public: + /// @param _port Network pork to listen for incoming connections + /// @param _threads Max number of threads to process requests + DappHost(int _port, int _threads = 10); + virtual ~DappHost(); + /// Load and host a dapp. Previsous dapp in discarded. Synchronous + QUrl hostDapp(Dapp&& _dapp); + +private: + void startListening(); + void stopListening(); + void sendOptionsResponse(MHD_Connection* _connection); + void sendNotAllowedResponse(MHD_Connection* _connection); + void sendResponse(std::string const& _url, MHD_Connection* _connection); + static int callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls); + + int m_port; + int m_threads; + bool m_running; + MHD_Daemon* m_daemon; + Dapp m_dapp; + std::map m_entriesByPath; +}; + diff --git a/alethzero/DappLoader.cpp b/alethzero/DappLoader.cpp new file mode 100644 index 000000000..41ac22b23 --- /dev/null +++ b/alethzero/DappLoader.cpp @@ -0,0 +1,203 @@ +/* + 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 DappLoader.cpp + * @author Arkadiy Paronyan + * @date 2015 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "DappLoader.h" + +using namespace dev; +using namespace dev::eth; +using namespace dev::crypto; + +Address c_registrar = Address("0000000000000000000000000000000000000a28"); +Address c_urlHint = Address("0000000000000000000000000000000000000a29"); + +QString contentsOfQResource(std::string const& res); + +DappLoader::DappLoader(QObject* _parent, WebThreeDirect* _web3): + QObject(_parent), m_web3(_web3) +{ + connect(&m_net, &QNetworkAccessManager::finished, this, &DappLoader::downloadComplete); +} + +DappLocation DappLoader::resolveAppUri(QString const& _uri) +{ + QUrl url(_uri); + if (!url.scheme().isEmpty() && url.scheme() != "eth") + throw dev::Exception(); //TODO: + + QStringList parts = url.host().split('.', QString::SkipEmptyParts); + QStringList domainParts; + std::reverse(parts.begin(), parts.end()); + parts.append(url.path().split('/', QString::SkipEmptyParts)); + + Address address = c_registrar; + Address lastAddress; + int partIndex = 0; + + h256 contentHash; + + while (address && partIndex < parts.length()) + { + lastAddress = address; + string32 name = { 0 }; + QByteArray utf8 = parts[partIndex].toUtf8(); + std::copy(utf8.data(), utf8.data() + utf8.size(), name.data()); + address = abiOut
(web3()->ethereum()->call(address, abiIn("addr(string32)", name))); + domainParts.append(parts[partIndex]); + if (!address) + { + //we have the address of the last part, try to get content hash + contentHash = abiOut(web3()->ethereum()->call(lastAddress, abiIn("content(string32)", name))); + if (!contentHash) + throw dev::Exception() << errinfo_comment("Can't resolve address"); + } + ++partIndex; + } + + + string32 contentUrl = abiOut(web3()->ethereum()->call(c_urlHint, abiIn("url(hash256)", contentHash))); + QString domain = domainParts.join('/'); + parts.erase(parts.begin(), parts.begin() + partIndex); + QString path = parts.join('/'); + QString contentUrlString = QString::fromUtf8(std::string(contentUrl.data(), contentUrl.size()).c_str()); + if (!contentUrlString.startsWith("http://") || !contentUrlString.startsWith("https://")) + contentUrlString = "http://" + contentUrlString; + return DappLocation { domain, path, contentUrlString, contentHash }; +} + +void DappLoader::downloadComplete(QNetworkReply* _reply) +{ + try + { + //try to interpret as rlp + QByteArray data = _reply->readAll(); + _reply->deleteLater(); + + h256 expected = m_uriHashes[_reply->request().url()]; + bytes package(reinterpret_cast(data.constData()), reinterpret_cast(data.constData() + data.size())); + Secp256k1 dec; + dec.decrypt(expected, package); + h256 got = sha3(package); + if (got != expected) + { + //try base64 + data = QByteArray::fromBase64(data); + package = bytes(reinterpret_cast(data.constData()), reinterpret_cast(data.constData() + data.size())); + dec.decrypt(expected, package); + got = sha3(package); + if (got != expected) + throw dev::Exception() << errinfo_comment("Dapp content hash does not match"); + } + + RLP rlp(package); + loadDapp(rlp); + } + catch (...) + { + qWarning() << tr("Error downloading DApp: ") << boost::current_exception_diagnostic_information().c_str(); + emit dappError(); + } + +} + +void DappLoader::loadDapp(RLP const& _rlp) +{ + Dapp dapp; + unsigned len = _rlp.itemCountStrict(); + dapp.manifest = loadManifest(_rlp[0].toString()); + for (unsigned c = 1; c < len; ++c) + { + bytesConstRef content = _rlp[c].toBytesConstRef(); + h256 hash = sha3(content); + auto entry = std::find_if(dapp.manifest.entries.cbegin(), dapp.manifest.entries.cend(), [=](ManifestEntry const& _e) { return _e.hash == hash; }); + if (entry != dapp.manifest.entries.cend()) + { + if (entry->path == "/deployment.js") + { + //inject web3 code + QString code; + code += contentsOfQResource(":/js/bignumber.min.js"); + code += "\n"; + code += contentsOfQResource(":/js/webthree.js"); + code += "\n"; + code += contentsOfQResource(":/js/setup.js"); + code += "\n"; + QByteArray res = code.toLatin1(); + bytes b(res.data(), res.data() + res.size()); + b.insert(b.end(), content.begin(), content.end()); + dapp.content[hash] = b; + } + else + dapp.content[hash] = content.toBytes(); + } + else + throw dev::Exception() << errinfo_comment("Dapp content hash does not match"); + } + emit dappReady(dapp); +} + +Manifest DappLoader::loadManifest(std::string const& _manifest) +{ + /// https://github.com/ethereum/go-ethereum/wiki/URL-Scheme + Manifest manifest; + Json::Reader jsonReader; + Json::Value root; + jsonReader.parse(_manifest, root, false); + + Json::Value entries = root["entries"]; + for (Json::ValueIterator it = entries.begin(); it != entries.end(); ++it) + { + Json::Value const& entryValue = *it; + std::string path = entryValue["path"].asString(); + if (path.size() == 0 || path[0] != '/') + path = "/" + path; + std::string contentType = entryValue["contentType"].asString(); + std::string strHash = entryValue["hash"].asString(); + if (strHash.length() == 64) + strHash = "0x" + strHash; + h256 hash = jsToFixed<32>(strHash); + unsigned httpStatus = entryValue["status"].asInt(); + manifest.entries.push_back(ManifestEntry{ path, hash, contentType, httpStatus }); + } + return manifest; +} + +void DappLoader::loadDapp(QString const& _uri) +{ + DappLocation location = resolveAppUri(_uri); + QUrl uri(location.contentUri); + QNetworkRequest request(uri); + m_uriHashes[uri] = location.contentHash; + m_net.get(request); +} + diff --git a/alethzero/DappLoader.h b/alethzero/DappLoader.h new file mode 100644 index 000000000..463b65d0a --- /dev/null +++ b/alethzero/DappLoader.h @@ -0,0 +1,94 @@ +/* + 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 DappLoader.h + * @author Arkadiy Paronyan + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dev +{ + class WebThreeDirect; + class RLP; +} + +struct ManifestEntry +{ + std::string path; + dev::h256 hash; + std::string contentType; + unsigned httpStatus; +}; + +struct Manifest +{ + std::vector entries; +}; + +struct Dapp +{ + Manifest manifest; + std::map content; +}; + + +struct DappLocation +{ + QString canonDomain; + QString path; + QString contentUri; + dev::h256 contentHash; +}; + +///Downloads, unpacks and prepares DApps for hosting +class DappLoader: public QObject +{ + Q_OBJECT +public: + DappLoader(QObject* _parent, dev::WebThreeDirect* _web3); + ///Load a new DApp. Resolves a name with a name reg contract. Asynchronous. dappReady is emitted once everything is read, dappError othervise + ///@param _uri Eth name path + void loadDapp(QString const& _uri); + +signals: + void dappReady(Dapp& _dapp); + void dappError(); + +private slots: + void downloadComplete(QNetworkReply* _reply); + +private: + dev::WebThreeDirect* web3() const { return m_web3; } + DappLocation resolveAppUri(QString const& _uri); + void loadDapp(dev::RLP const& _rlp); + Manifest loadManifest(std::string const& _manifest); + + dev::WebThreeDirect* m_web3; + QNetworkAccessManager m_net; + std::map m_uriHashes; +}; + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 88b9103cb..0fc4d34f6 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -19,7 +19,6 @@ * @date 2014 */ -#define QWEBENGINEINSPECTOR 1 #include // Make sure boost/asio.hpp is included before windows.h. @@ -68,6 +67,9 @@ #include "OurWebThreeStubServer.h" #include "Transact.h" #include "Debugger.h" +#include "DappLoader.h" +#include "DappHost.h" +#include "WebPage.h" #include "ui_Main.h" using namespace std; using namespace dev; @@ -116,7 +118,9 @@ Address c_newConfig = Address("c6d9d2cd449a754c494264e1809c50e34d64562b"); Main::Main(QWidget *parent) : QMainWindow(parent), ui(new Ui::Main), - m_transact(this, this) + m_transact(this, this), + m_dappLoader(nullptr), + m_webPage(nullptr) { QtWebEngine::initialize(); setWindowFlags(Qt::Window); @@ -166,10 +170,12 @@ Main::Main(QWidget *parent) : m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); + WebPage* webPage= new WebPage(this); + m_webPage = webPage; + connect(webPage, &WebPage::consoleMessage, [this](QString const& _msg) { Main::addConsoleMessage(_msg, QString()); }); + ui->webView->setPage(m_webPage); connect(ui->webView, &QWebEngineView::loadFinished, [this]() { -// f->disconnect(); -// f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership); auto f = ui->webView->page(); f->runJavaScript(contentsOfQResource(":/js/bignumber.min.js")); f->runJavaScript(contentsOfQResource(":/js/webthree.js")); @@ -181,6 +187,9 @@ Main::Main(QWidget *parent) : ui->tabWidget->setTabText(0, ui->webView->title()); }); + m_dappHost.reset(new DappHost(8081)); + m_dappLoader = new DappLoader(this, web3()); + connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // QWebEngineInspector* inspector = new QWebEngineInspector(); // inspector->setPage(page); @@ -425,13 +434,7 @@ void Main::eval(QString const& _js) s = "" + jsonEv.toString().toHtmlEscaped() + ""; else s = "unknown type"; - m_consoleHistory.push_back(qMakePair(_js, s)); - s = "" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%"); - for (auto const& i: m_consoleHistory) - s += "
>" + i.first.toHtmlEscaped() + "
" - "
 " + i.second + "
"; - s += ""; - ui->jsConsole->setHtml(s); + addConsoleMessage(_js, s); }; ui->webView->page()->runJavaScript("JSON.stringify(___RET)", f2); }; @@ -439,6 +442,17 @@ void Main::eval(QString const& _js) ui->webView->page()->runJavaScript(c, f); } +void Main::addConsoleMessage(QString const& _js, QString const& _s) +{ + m_consoleHistory.push_back(qMakePair(_js, _s)); + QString r = "" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%"); + for (auto const& i: m_consoleHistory) + r += "
>" + i.first.toHtmlEscaped() + "
" + "
 " + i.second + "
"; + r += ""; + ui->jsConsole->setHtml(r); +} + static Public stringToPublic(QString const& _a) { string sn = _a.toStdString(); @@ -780,15 +794,28 @@ void Main::on_jitvm_triggered() void Main::on_urlEdit_returnPressed() { QString s = ui->urlEdit->text(); - QRegExp r("([a-z]+://)?([^/]*)(.*)"); - if (r.exactMatch(s)) - if (r.cap(2).isEmpty()) - s = (r.cap(1).isEmpty() ? "file://" : r.cap(1)) + r.cap(3); + QUrl url(s); + if (url.scheme().isEmpty() || url.scheme() == "eth") + { + try + { + //try do resolve dapp url + m_dappLoader->loadDapp(s); + } + catch (...) + { + qWarning() << boost::current_exception_diagnostic_information().c_str(); + } + } + + if (url.scheme().isEmpty()) + if (url.path().indexOf('/') < url.path().indexOf('.')) + url.setScheme("file"); else - s = (r.cap(1).isEmpty() ? "http://" : r.cap(1)) + lookup(r.cap(2)) + r.cap(3); - else{} - qDebug() << s; - ui->webView->setUrl(s); + url.setScheme("http"); + else {} + qDebug() << url.toString(); + ui->webView->page()->setUrl(url); } void Main::on_nameReg_textChanged() @@ -973,7 +1000,7 @@ void Main::refreshBlockChain() // TODO: keep the same thing highlighted. // TODO: refactor into MVC // TODO: use get by hash/number - // TODO: transactions, log addresses, log topics + // TODO: transactions auto const& bc = ethereum()->blockChain(); QStringList filters = ui->blockChainFilter->text().toLower().split(QRegExp("\\s+"), QString::SkipEmptyParts); @@ -985,15 +1012,17 @@ void Main::refreshBlockChain() h256 h(f.toStdString()); if (bc.isKnown(h)) blocks.insert(h); + for (auto const& b: bc.withBlockBloom(LogBloom().shiftBloom<3, 32>(sha3(h)), 0, -1)) + blocks.insert(bc.numberHash(b)); } else if (f.toLongLong() <= bc.number()) blocks.insert(bc.numberHash(u256(f.toLongLong()))); - /*else if (f.size() == 40) + else if (f.size() == 40) { - Address h(f[0]); - if (bc.(h)) - blocks.insert(h); - }*/ + Address h(f.toStdString()); + for (auto const& b: bc.withBlockBloom(LogBloom().shiftBloom<3, 32>(sha3(h)), 0, -1)) + blocks.insert(bc.numberHash(b)); + } QByteArray oldSelected = ui->blocks->count() ? ui->blocks->currentItem()->data(Qt::UserRole).toByteArray() : QByteArray(); ui->blocks->clear(); @@ -1835,3 +1864,9 @@ void Main::refreshWhispers() ui->whispers->addItem(item); } } + +void Main::dappLoaded(Dapp& _dapp) +{ + QUrl url = m_dappHost->hostDapp(std::move(_dapp)); + ui->webView->page()->setUrl(url); +} diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 378877468..ba89b455a 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -54,8 +54,11 @@ namespace jsonrpc { class HttpServer; } -class QQuickView; +class QWebEnginePage; class OurWebThreeStubServer; +class DappLoader; +class DappHost; +struct Dapp; using WatchHandler = std::function; @@ -99,6 +102,7 @@ public slots: private slots: void eval(QString const& _js); + void addConsoleMessage(QString const& _js, QString const& _s); // Application void on_about_triggered(); @@ -172,6 +176,9 @@ private slots: void refreshBlockChain(); void addNewId(QString _ids); + // Dapps + void dappLoaded(Dapp& _dapp); //qt does not support rvalue refs for signals + signals: void poll(); @@ -234,8 +241,6 @@ private: QString m_privateChain; dev::Address m_nameReg; - QNetworkAccessManager m_webCtrl; - QList> m_consoleHistory; QMutex m_logLock; QString m_logHistory; @@ -248,4 +253,7 @@ private: NatspecHandler m_natSpecDB; Transact m_transact; + std::unique_ptr m_dappHost; + DappLoader* m_dappLoader; + QWebEnginePage* m_webPage; }; diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index df6c5258d..640ffe3d8 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -175,7 +175,7 @@ void Transact::rejigData() m_data = fromHex(src); else if (sourceIsSolidity(src)) { - dev::solidity::CompilerStack compiler; + dev::solidity::CompilerStack compiler(true); try { // compiler.addSources(dev::solidity::StandardSources); @@ -287,7 +287,7 @@ void Transact::on_send_clicked() if (sourceIsSolidity(src)) try { - dev::solidity::CompilerStack compiler; + dev::solidity::CompilerStack compiler(true); m_data = compiler.compile(src, ui->optimize->isChecked()); for (string const& s: compiler.getContractNames()) { diff --git a/alethzero/WebPage.cpp b/alethzero/WebPage.cpp new file mode 100644 index 000000000..08c5c3cc6 --- /dev/null +++ b/alethzero/WebPage.cpp @@ -0,0 +1,28 @@ +/* + 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 WebPage.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com> + * @date 2015 + */ + +#include "WebPage.h" + +void WebPage::javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID) +{ + QString prefix = _level == QWebEnginePage::ErrorMessageLevel ? "error" : _level == QWebEnginePage::WarningMessageLevel ? "warning" : ""; + emit consoleMessage(QString("%1(%2:%3):%4").arg(prefix).arg(_sourceID).arg(_lineNumber).arg(_message)); +} diff --git a/alethzero/WebPage.h b/alethzero/WebPage.h new file mode 100644 index 000000000..5f1bb1dc8 --- /dev/null +++ b/alethzero/WebPage.h @@ -0,0 +1,40 @@ +/* + 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 WebPage.h + * @author Arkadiy Paronyan arkadiy@ethdev.com> + * @date 2015 + */ + +#pragma once + +#include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" //QtWebEngineWidgets/qwebenginecertificateerror.h:78:348: error: extra ';' +#include +#pragma GCC diagnostic pop + +class WebPage: public QWebEnginePage +{ + Q_OBJECT +public: + WebPage(QObject* _parent): QWebEnginePage(_parent) { } +signals: + void consoleMessage(QString const& _msg); + +protected: + void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID) override; +}; diff --git a/chrono_io/chrono_io b/chrono_io/chrono_io new file mode 100644 index 000000000..8de9d4f45 --- /dev/null +++ b/chrono_io/chrono_io @@ -0,0 +1,1140 @@ +// chrono_io +// +// (C) Copyright Howard Hinnant +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). + +#ifndef _CHRONO_IO +#define _CHRONO_IO + +/* + + chrono_io synopsis + +#include +#include + +namespace std +{ +namespace chrono +{ + +template +class durationpunct + : public locale::facet +{ +public: + static locale::id id; + + typedef basic_string string_type; + enum {use_long, use_short}; + + explicit durationpunct(int use = use_long); + + durationpunct(int use, + const string_type& long_seconds, const string_type& long_minutes, + const string_type& long_hours, const string_type& short_seconds, + const string_type& short_minutes, const string_type& short_hours); + + durationpunct(int use, const durationpunct& d); + + template string_type short_name() const; + template string_type long_name() const; + template string_type name() const; + + bool is_short_name() const; + bool is_long_name() const; +}; + +template + basic_ostream& + duration_short(basic_ostream& os); + +template + basic_ostream& + duration_long(basic_ostream& os); + +template + basic_ostream& + operator<<(basic_ostream& os, const duration& d); + +template + basic_istream& + operator>>(basic_istream& is, duration& d); + +template + basic_ostream& + operator<<(basic_ostream& os, + const time_point& tp); + +template + basic_ostream& + operator<<(basic_ostream& os, + const time_point& tp); + +template + basic_ostream& + operator<<(basic_ostream& os, + const time_point& tp); + +template + basic_istream& + operator>>(basic_istream& is, + time_point& tp); + +template + basic_istream& + operator>>(basic_istream& is, + time_point& tp); + +template + basic_istream& + operator>>(basic_istream& is, + time_point& tp); + +template +class timepunct + : public std::locale::facet +{ +public: + typedef std::basic_string string_type; + + static std::locale::id id; + + explicit timepunct(std::size_t refs = 0, + const string_type& fmt = string_type(), + bool local = false); + + explicit timepunct(std::size_t refs, + string_type&& fmt, + bool local = false); + + const string_type& fmt() const {return fmt_;} + bool local() const {return local_;} +}; + +template + unspecfiied + local_fmt(std::basic_string fmt = std::basic_string()); + +template + unspecfiied + local_fmt(const CharT* fmt); + +template + unspecfiied + utc_fmt(std::basic_string fmt = std::basic_string()); + +template + unspecfiied + utc_fmt(const CharT* fmt); + +} // chrono +} // std + +*/ + +#include +#include "ratio_io" + +namespace std { + +namespace chrono +{ + +template +To +round(const duration& d) +{ + To t0 = duration_cast(d); + To t1 = t0; + ++t1; + typedef typename common_type >::type _D; + _D diff0 = d - t0; + _D diff1 = t1 - d; + if (diff0 == diff1) + { + if (t0.count() & 1) + return t1; + return t0; + } + else if (diff0 < diff1) + return t0; + return t1; +} + +template +class durationpunct + : public locale::facet +{ +public: + typedef basic_string<_CharT> string_type; + enum {use_long, use_short}; + +private: + bool __use_short_; + string_type __seconds_; + string_type __minutes_; + string_type __hours_; +public: + static locale::id id; + + explicit durationpunct(size_t refs = 0, + int __use = use_long) + : locale::facet(refs), __use_short_(__use) {} + + durationpunct(size_t refs, int __use, + string_type __seconds, string_type __minutes, + string_type __hours) + : locale::facet(refs), __use_short_(__use), + __seconds_(std::move(__seconds)), + __minutes_(std::move(__minutes)), + __hours_(std::move(__hours)) {} + + bool is_short_name() const {return __use_short_;} + bool is_long_name() const {return !__use_short_;} + + string_type seconds() const {return __seconds_;} + string_type minutes() const {return __minutes_;} + string_type hours() const {return __hours_;} +}; + +template +locale::id +durationpunct<_CharT>::id; + +enum {prefix, symbol}; +enum {utc, local}; + +template +struct __duration_manip +{ + bool __use_short_; + basic_string<_CharT> __seconds_; + basic_string<_CharT> __minutes_; + basic_string<_CharT> __hours_; + + __duration_manip(bool __use_short, basic_string<_CharT> __seconds, + basic_string<_CharT> __minutes, basic_string<_CharT> __hours) + : __use_short_(__use_short), + __seconds_(std::move(__seconds)), + __minutes_(std::move(__minutes)), + __hours_(std::move(__hours)) {} +}; + +class duration_fmt +{ + int form_; +public: + explicit duration_fmt(int f) : form_(f) {} + // explicit + operator int() const {return form_;} +}; + +template +std::basic_ostream& +operator <<(std::basic_ostream& os, duration_fmt d) +{ + os.imbue(std::locale(os.getloc(), new durationpunct(0, d == symbol))); + return os; +} + +template +std::basic_istream& +operator >>(std::basic_istream& is, duration_fmt d) +{ + is.imbue(std::locale(is.getloc(), new durationpunct(0, d == symbol))); + return is; +} + +template +std::basic_ostream& +operator <<(std::basic_ostream& os, __duration_manip m) +{ + os.imbue(std::locale(os.getloc(), new durationpunct(0, m.__use_short_, + std::move(m.__seconds_), std::move(m.__minutes_), std::move(m.__hours_)))); + return os; +} + +template +std::basic_istream& +operator >>(std::basic_istream& is, __duration_manip m) +{ + is.imbue(std::locale(is.getloc(), new durationpunct(0, m.__use_short_, + std::move(m.__seconds_), std::move(m.__minutes_), std::move(m.__hours_)))); + return is; +} + +inline +__duration_manip +duration_short() +{ + return __duration_manip(durationpunct::use_short, "", "", ""); +} + +template +inline +__duration_manip<_CharT> +duration_short(basic_string<_CharT> __seconds, + basic_string<_CharT> __minutes, + basic_string<_CharT> __hours) +{ + return __duration_manip<_CharT>(durationpunct<_CharT>::use_short, + std::move(__seconds), std::move(__minutes), + std::move(__hours)); +} + +inline +__duration_manip +duration_long() +{ + return __duration_manip(durationpunct::use_long, "", "", ""); +} + +template +inline +typename enable_if +< + is_convertible<_T2, basic_string<_CharT> >::value && + is_convertible<_T3, basic_string<_CharT> >::value, + __duration_manip<_CharT> +>::type +duration_long(basic_string<_CharT> __seconds, + _T2 __minutes, + _T3 __hours) +{ + typedef basic_string<_CharT> string_type; + return __duration_manip<_CharT>(durationpunct<_CharT>::use_long, + std::move(__seconds), + string_type(std::move(__minutes)), + string_type(std::move(__hours))); +} + +template +inline +typename enable_if +< + is_convertible<_T2, basic_string<_CharT> >::value && + is_convertible<_T3, basic_string<_CharT> >::value, + __duration_manip<_CharT> +>::type +duration_long(const _CharT* __seconds, + _T2 __minutes, + _T3 __hours) +{ + typedef basic_string<_CharT> string_type; + return __duration_manip<_CharT>(durationpunct<_CharT>::use_long, + string_type(__seconds), + string_type(std::move(__minutes)), + string_type(std::move(__hours))); +} + +template +basic_string<_CharT> +__get_unit(bool __is_long, const basic_string<_CharT>& __seconds, + const basic_string<_CharT>&, const basic_string<_CharT>&, _Period) +{ + if (__is_long) + { + if (__seconds.empty()) + { + _CharT __p[] = {'s', 'e', 'c', 'o', 'n', 'd', 's', 0}; + return ratio_string<_Period, _CharT>::prefix() + __p; + } + return ratio_string<_Period, _CharT>::prefix() + __seconds; + } + if (__seconds.empty()) + return ratio_string<_Period, _CharT>::symbol() + 's'; + return ratio_string<_Period, _CharT>::symbol() + __seconds; +} + +template +inline +basic_string<_CharT> +__get_unit(bool __is_long, const basic_string<_CharT>& __seconds, + const basic_string<_CharT>&, const basic_string<_CharT>&, ratio<1>) +{ + if (__seconds.empty()) + { + if (__is_long) + { + _CharT __p[] = {'s', 'e', 'c', 'o', 'n', 'd', 's'}; + return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT)); + } + else + return basic_string<_CharT>(1, 's'); + } + return __seconds; +} + +template +basic_string<_CharT> +__get_unit(bool __is_long, const basic_string<_CharT>&, + const basic_string<_CharT>& __minutes, + const basic_string<_CharT>&, ratio<60>) +{ + if (__minutes.empty()) + { + if (__is_long) + { + _CharT __p[] = {'m', 'i', 'n', 'u', 't', 'e', 's'}; + return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT)); + } + else + return basic_string<_CharT>(1, 'm'); + } + return __minutes; +} + +template +inline +basic_string<_CharT> +__get_unit(bool __is_long, const basic_string<_CharT>&, + const basic_string<_CharT>&, + const basic_string<_CharT>& __hours, ratio<3600>) +{ + if (__hours.empty()) + { + if (__is_long) + { + _CharT __p[] = {'h', 'o', 'u', 'r', 's'}; + return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT)); + } + else + return basic_string<_CharT>(1, 'h'); + } + return __hours; +} + +template +basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, const duration<_Rep, _Period>& __d) +{ + typename basic_ostream<_CharT, _Traits>::sentry ok(__os); + if (ok) + { + typedef durationpunct<_CharT> _F; + typedef basic_string<_CharT> string_type; + bool failed = false; + try + { + bool __is_long = true; + string_type __seconds; + string_type __minutes; + string_type __hours; + locale __loc = __os.getloc(); + if (has_facet<_F>(__loc)) + { + const _F& f = use_facet<_F>(__loc); + __is_long = f.is_long_name(); + __seconds = f.seconds(); + __minutes = f.minutes(); + __hours = f.hours(); + } + string_type __unit = __get_unit(__is_long, __seconds, __minutes, + __hours, typename _Period::type()); + __os << __d.count() << ' ' << __unit; + } + catch (...) + { + failed = true; + } + if (failed) + __os.setstate(ios_base::failbit | ios_base::badbit); + } + return __os; +} + +template ::value> +struct __duration_io_intermediate +{ + typedef _Rep type; +}; + +template +struct __duration_io_intermediate<_Rep, true> +{ + typedef typename conditional + < + is_floating_point<_Rep>::value, + long double, + typename conditional + < + is_signed<_Rep>::value, + long long, + unsigned long long + >::type + >::type type; +}; + +template +T +__gcd(T x, T y) +{ + while (y != 0) + { + T old_x = x; + x = y; + y = old_x % y; + } + return x; +} + +template <> +long double +inline +__gcd(long double x, long double y) +{ + (void)x; + (void)y; + return 1; +} + +template +basic_istream<_CharT, _Traits>& +operator>>(basic_istream<_CharT, _Traits>& __is, duration<_Rep, _Period>& __d) +{ + typedef basic_string<_CharT> string_type; + typedef durationpunct<_CharT> _F; + typedef typename __duration_io_intermediate<_Rep>::type _IR; + _IR __r; + // read value into __r + __is >> __r; + if (__is.good()) + { + // now determine unit + typedef istreambuf_iterator<_CharT, _Traits> _I; + _I __i(__is); + _I __e; + if (__i != __e && *__i == ' ') // mandatory ' ' after value + { + ++__i; + if (__i != __e) + { + string_type __seconds; + string_type __minutes; + string_type __hours; + locale __loc = __is.getloc(); + if (has_facet<_F>(__loc)) + { + const _F& f = use_facet<_F>(__loc); + __seconds = f.seconds(); + __minutes = f.minutes(); + __hours = f.hours(); + } + // unit is num / den (yet to be determined) + unsigned long long num = 0; + unsigned long long den = 0; + if (*__i == '[') + { + // parse [N/D]s or [N/D]seconds format + ++__i; + _CharT __x; + __is >> num >> __x >> den; + if (!__is.good() || __x != '/') + { + __is.setstate(__is.failbit); + return __is; + } + __i = _I(__is); + if (*__i != ']') + { + __is.setstate(__is.failbit); + return __is; + } + ++__i; + const basic_string<_CharT> __units[] = + { + __get_unit(true, __seconds, __minutes, __hours, ratio<1>()), + __get_unit(false, __seconds, __minutes, __hours, ratio<1>()) + }; + ios_base::iostate __err = ios_base::goodbit; + const basic_string<_CharT>* __k = __scan_keyword(__i, __e, + __units, __units + sizeof(__units)/sizeof(__units[0]), + use_facet >(__loc), + __err); + switch ((__k - __units) / 2) + { + case 0: + break; + default: + __is.setstate(__err); + return __is; + } + } + else + { + // parse SI name, short or long + const basic_string<_CharT> __units[] = + { + __get_unit(true, __seconds, __minutes, __hours, atto()), + __get_unit(false, __seconds, __minutes, __hours, atto()), + __get_unit(true, __seconds, __minutes, __hours, femto()), + __get_unit(false, __seconds, __minutes, __hours, femto()), + __get_unit(true, __seconds, __minutes, __hours, pico()), + __get_unit(false, __seconds, __minutes, __hours, pico()), + __get_unit(true, __seconds, __minutes, __hours, nano()), + __get_unit(false, __seconds, __minutes, __hours, nano()), + __get_unit(true, __seconds, __minutes, __hours, micro()), + __get_unit(false, __seconds, __minutes, __hours, micro()), + __get_unit(true, __seconds, __minutes, __hours, milli()), + __get_unit(false, __seconds, __minutes, __hours, milli()), + __get_unit(true, __seconds, __minutes, __hours, centi()), + __get_unit(false, __seconds, __minutes, __hours, centi()), + __get_unit(true, __seconds, __minutes, __hours, deci()), + __get_unit(false, __seconds, __minutes, __hours, deci()), + __get_unit(true, __seconds, __minutes, __hours, deca()), + __get_unit(false, __seconds, __minutes, __hours, deca()), + __get_unit(true, __seconds, __minutes, __hours, hecto()), + __get_unit(false, __seconds, __minutes, __hours, hecto()), + __get_unit(true, __seconds, __minutes, __hours, kilo()), + __get_unit(false, __seconds, __minutes, __hours, kilo()), + __get_unit(true, __seconds, __minutes, __hours, mega()), + __get_unit(false, __seconds, __minutes, __hours, mega()), + __get_unit(true, __seconds, __minutes, __hours, giga()), + __get_unit(false, __seconds, __minutes, __hours, giga()), + __get_unit(true, __seconds, __minutes, __hours, tera()), + __get_unit(false, __seconds, __minutes, __hours, tera()), + __get_unit(true, __seconds, __minutes, __hours, peta()), + __get_unit(false, __seconds, __minutes, __hours, peta()), + __get_unit(true, __seconds, __minutes, __hours, exa()), + __get_unit(false, __seconds, __minutes, __hours, exa()), + __get_unit(true, __seconds, __minutes, __hours, ratio<1>()), + __get_unit(false, __seconds, __minutes, __hours, ratio<1>()), + __get_unit(true, __seconds, __minutes, __hours, ratio<60>()), + __get_unit(false, __seconds, __minutes, __hours, ratio<60>()), + __get_unit(true, __seconds, __minutes, __hours, ratio<3600>()), + __get_unit(false, __seconds, __minutes, __hours, ratio<3600>()) + }; + ios_base::iostate __err = ios_base::goodbit; + const basic_string<_CharT>* __k = __scan_keyword(__i, __e, + __units, __units + sizeof(__units)/sizeof(__units[0]), + use_facet >(__loc), + __err); + switch ((__k - __units) / 2) + { + case 0: + num = 1ULL; + den = 1000000000000000000ULL; + break; + case 1: + num = 1ULL; + den = 1000000000000000ULL; + break; + case 2: + num = 1ULL; + den = 1000000000000ULL; + break; + case 3: + num = 1ULL; + den = 1000000000ULL; + break; + case 4: + num = 1ULL; + den = 1000000ULL; + break; + case 5: + num = 1ULL; + den = 1000ULL; + break; + case 6: + num = 1ULL; + den = 100ULL; + break; + case 7: + num = 1ULL; + den = 10ULL; + break; + case 8: + num = 10ULL; + den = 1ULL; + break; + case 9: + num = 100ULL; + den = 1ULL; + break; + case 10: + num = 1000ULL; + den = 1ULL; + break; + case 11: + num = 1000000ULL; + den = 1ULL; + break; + case 12: + num = 1000000000ULL; + den = 1ULL; + break; + case 13: + num = 1000000000000ULL; + den = 1ULL; + break; + case 14: + num = 1000000000000000ULL; + den = 1ULL; + break; + case 15: + num = 1000000000000000000ULL; + den = 1ULL; + break; + case 16: + num = 1; + den = 1; + break; + case 17: + num = 60; + den = 1; + break; + case 18: + num = 3600; + den = 1; + break; + default: + __is.setstate(__err); + return __is; + } + } + // unit is num/den + // __r should be multiplied by (num/den) / _Period + // Reduce (num/den) / _Period to lowest terms + unsigned long long __gcd_n1_n2 = __gcd(num, _Period::num); + unsigned long long __gcd_d1_d2 = __gcd(den, _Period::den); + num /= __gcd_n1_n2; + den /= __gcd_d1_d2; + unsigned long long __n2 = _Period::num / __gcd_n1_n2; + unsigned long long __d2 = _Period::den / __gcd_d1_d2; + if (num > numeric_limits::max() / __d2 || + den > numeric_limits::max() / __n2) + { + // (num/den) / _Period overflows + __is.setstate(__is.failbit); + return __is; + } + num *= __d2; + den *= __n2; + // num / den is now factor to multiply by __r + typedef typename common_type<_IR, unsigned long long>::type _CT; + if (is_integral<_IR>::value) + { + // Reduce __r * num / den + _CT __t = __gcd<_CT>(__r, den); + __r /= __t; + den /= __t; + if (den != 1) + { + // Conversion to _Period is integral and not exact + __is.setstate(__is.failbit); + return __is; + } + } + if (__r > duration_values<_CT>::max() / num) + { + // Conversion to _Period overflowed + __is.setstate(__is.failbit); + return __is; + } + _CT __t = __r * num; + __t /= den; + if (duration_values<_Rep>::max() < __t) + { + // Conversion to _Period overflowed + __is.setstate(__is.failbit); + return __is; + } + // Success! Store it. + __r = _Rep(__t); + __d = duration<_Rep, _Period>(__r); + } + else + __is.setstate(__is.failbit | __is.eofbit); + } + else + { + if (__i == __e) + __is.setstate(__is.eofbit); + __is.setstate(__is.failbit); + } + } + else + __is.setstate(__is.failbit); + return __is; +} + +template +class timepunct + : public std::locale::facet +{ +public: + typedef std::basic_string string_type; + +private: + string_type fmt_; + bool local_; + +public: + static std::locale::id id; + + explicit timepunct(std::size_t refs = 0, + const string_type& fmt = string_type(), + bool local = false) + : std::locale::facet(refs), + fmt_(fmt), + local_(local) {} + + explicit timepunct(std::size_t refs, + string_type&& fmt, + bool local = false) + : std::locale::facet(refs), + fmt_(std::move(fmt)), + local_(local) {} + + const string_type& fmt() const {return fmt_;} + bool local() const {return local_;} +}; + +template +std::locale::id +timepunct::id; + +template +basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, + const time_point& __tp) +{ + return __os << __tp.time_since_epoch() << " since boot"; +} + +template +struct __time_manip +{ + std::basic_string fmt_; + bool local_; + + __time_manip(std::basic_string fmt, bool local) + : fmt_(std::move(fmt)), + local_(local) {} +}; + +template +std::basic_ostream& +operator <<(std::basic_ostream& os, __time_manip m) +{ + os.imbue(std::locale(os.getloc(), new timepunct(0, std::move(m.fmt_), m.local_))); + return os; +} + +template +std::basic_istream& +operator >>(std::basic_istream& is, __time_manip m) +{ + is.imbue(std::locale(is.getloc(), new timepunct(0, std::move(m.fmt_), m.local_))); + return is; +} + +template +inline +__time_manip +local_fmt(std::basic_string fmt) +{ + return __time_manip(std::move(fmt), true); +} + +template +inline +__time_manip +local_fmt(const charT* fmt) +{ + return __time_manip(fmt, true); +} + +inline +__time_manip +local_fmt() +{ + return __time_manip("", true); +} + +template +inline +__time_manip +utc_fmt(std::basic_string fmt) +{ + return __time_manip(std::move(fmt), false); +} + +template +inline +__time_manip +utc_fmt(const charT* fmt) +{ + return __time_manip(fmt, false); +} + +inline +__time_manip +utc_fmt() +{ + return __time_manip("", false); +} + +class __time_man +{ + int form_; +public: + explicit __time_man(int f) : form_(f) {} + // explicit + operator int() const {return form_;} +}; + +template +std::basic_ostream& +operator <<(std::basic_ostream& os, __time_man m) +{ + os.imbue(std::locale(os.getloc(), new timepunct(0, basic_string(), m == local))); + return os; +} + +template +std::basic_istream& +operator >>(std::basic_istream& is, __time_man m) +{ + is.imbue(std::locale(is.getloc(), new timepunct(0, basic_string(), m == local))); + return is; +} + + +inline +__time_man +time_fmt(int f) +{ + return __time_man(f); +} + +template +basic_istream<_CharT, _Traits>& +operator>>(basic_istream<_CharT, _Traits>& __is, + time_point& __tp) +{ + _Duration __d; + __is >> __d; + if (__is.good()) + { + const _CharT __u[] = {' ', 's', 'i', 'n', 'c', 'e', ' ', 'b', 'o', 'o', 't'}; + const basic_string<_CharT> __units(__u, __u + sizeof(__u)/sizeof(__u[0])); + ios_base::iostate __err = ios_base::goodbit; + typedef istreambuf_iterator<_CharT, _Traits> _I; + _I __i(__is); + _I __e; + ptrdiff_t __k = __scan_keyword(__i, __e, + &__units, &__units + 1, + use_facet >(__is.getloc()), + __err) - &__units; + if (__k == 1) + { + // failed to read epoch string + __is.setstate(__err); + return __is; + } + __tp = time_point(__d); + } + else + __is.setstate(__is.failbit); + return __is; +} + +template +basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, + const time_point& __tp) +{ + typename basic_ostream<_CharT, _Traits>::sentry ok(__os); + if (ok) + { + bool failed = false; + try + { + const _CharT* pb = nullptr; + const _CharT* pe = pb; + bool __local = false; + typedef timepunct<_CharT> F; + locale loc = __os.getloc(); + if (has_facet(loc)) + { + const F& f = use_facet(loc); + pb = f.fmt().data(); + pe = pb + f.fmt().size(); + __local = f.local(); + } + time_t __t = system_clock::to_time_t(__tp); + tm __tm; + if (__local) + { + if (localtime_r(&__t, &__tm) == 0) + failed = true; + } + else + { + if (gmtime_r(&__t, &__tm) == 0) + failed = true; + } + if (!failed) + { + const time_put<_CharT>& tp = use_facet >(loc); + if (pb == pe) + { + _CharT pattern[] = {'%', 'F', ' ', '%', 'H', ':', '%', 'M', ':'}; + pb = pattern; + pe = pb + sizeof(pattern) / sizeof(_CharT); + failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed(); + if (!failed) + { + duration __d = __tp - system_clock::from_time_t(__t) + + seconds(__tm.tm_sec); + if (__d.count() < 10) + __os << _CharT('0'); + ios::fmtflags __flgs = __os.flags(); + __os.setf(ios::fixed, ios::floatfield); + __os << __d.count(); + __os.flags(__flgs); + if (__local) + { + _CharT sub_pattern[] = {' ', '%', 'z'}; + pb = sub_pattern; + pe = pb + + sizeof(sub_pattern) / sizeof(_CharT); + failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed(); + } + else + { + _CharT sub_pattern[] = {' ', '+', '0', '0', '0', '0', 0}; + __os << sub_pattern; + } + } + } + else + failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed(); + } + } + catch (...) + { + failed = true; + } + if (failed) + __os.setstate(ios_base::failbit | ios_base::badbit); + } + return __os; +} + +template +basic_istream<_CharT, _Traits>& +operator>>(basic_istream<_CharT, _Traits>& __is, + time_point& __tp) +{ + typename basic_istream<_CharT,_Traits>::sentry ok(__is); + if (ok) + { + ios_base::iostate err = ios_base::goodbit; + try + { + const _CharT* pb = nullptr; + const _CharT* pe = pb; + bool __local = false; + typedef timepunct<_CharT> F; + locale loc = __is.getloc(); + if (has_facet(loc)) + { + const F& f = use_facet(loc); + pb = f.fmt().data(); + pe = pb + f.fmt().size(); + __local = f.local(); + } + const time_get<_CharT>& tg = use_facet >(loc); + tm __tm = {0}; + if (pb == pe) + { + _CharT pattern[] = {'%', 'Y', '-', '%', 'm', '-', '%', 'd', + ' ', '%', 'H', ':', '%', 'M', ':'}; + pb = pattern; + pe = pb + sizeof(pattern) / sizeof(_CharT); + tg.get(__is, 0, __is, err, &__tm, pb, pe); + if (err & ios_base::failbit) + goto __exit; + typedef istreambuf_iterator<_CharT, _Traits> _I; + double __sec; + _CharT __c = _CharT(); + __is >> __sec; + if (__is.fail()) + { + err |= ios_base::failbit; + goto __exit; + } + _I __i(__is); + _I __eof; + __c = *__i; + if (++__i == __eof || __c != ' ') + { + err |= ios_base::failbit; + goto __exit; + } + __c = *__i; + if (++__i == __eof || (__c != '+' && __c != '-')) + { + err |= ios_base::failbit; + goto __exit; + } + int __n = __c == '-' ? 1 : -1; + __c = *__i; + if (++__i == __eof || !isdigit(__c)) + { + err |= ios_base::failbit; + goto __exit; + } + int __min = (__c - '0') * 600; + __c = *__i; + if (++__i == __eof || !isdigit(__c)) + { + err |= ios_base::failbit; + goto __exit; + } + __min += (__c - '0') * 60; + __c = *__i; + if (++__i == __eof || !isdigit(__c)) + { + err |= ios_base::failbit; + goto __exit; + } + __min += (__c - '0') * 10; + __c = *__i; + if (!isdigit(__c)) + { + err |= ios_base::failbit; + goto __exit; + } + ++__i; + __min += __c - '0'; + __min *= __n; + time_t __t; + __t = timegm(&__tm); + __tp = system_clock::from_time_t(__t) + minutes(__min) + + round(duration(__sec)); + } + else + { + tg.get(__is, 0, __is, err, &__tm, pb, pe); + } + } + catch (...) + { + err |= ios_base::badbit | ios_base::failbit; + } + __exit: + __is.setstate(err); + } + return __is; +} + +} // chrono + +} + +#endif // _CHRONO_IO diff --git a/chrono_io/ratio_io b/chrono_io/ratio_io new file mode 100644 index 000000000..ade52376c --- /dev/null +++ b/chrono_io/ratio_io @@ -0,0 +1,601 @@ +// ratio_io +// +// (C) Copyright Howard Hinnant +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). + +#ifndef _RATIO_IO +#define _RATIO_IO + +/* + + ratio_io synopsis + +#include +#include + +namespace std +{ + +template +struct ratio_string +{ + static basic_string symbol(); + static basic_string prefix(); +}; + +} // std + +*/ + +#include +#include +#include + +namespace std { + +template +struct ratio_string +{ + static basic_string<_CharT> symbol() {return prefix();} + static basic_string<_CharT> prefix(); +}; + +template +basic_string<_CharT> +ratio_string<_Ratio, _CharT>::prefix() +{ + basic_ostringstream<_CharT> __os; + __os << _CharT('[') << _Ratio::num << _CharT('/') + << _Ratio::den << _CharT(']'); + return __os.str(); +} + +// atto + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'a');} + static string prefix() {return string("atto");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'a');} + static u16string prefix() {return u16string(u"atto");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'a');} + static u32string prefix() {return u32string(U"atto");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'a');} + static wstring prefix() {return wstring(L"atto");} +}; + +// femto + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'f');} + static string prefix() {return string("femto");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'f');} + static u16string prefix() {return u16string(u"femto");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'f');} + static u32string prefix() {return u32string(U"femto");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'f');} + static wstring prefix() {return wstring(L"femto");} +}; + +// pico + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'p');} + static string prefix() {return string("pico");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'p');} + static u16string prefix() {return u16string(u"pico");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'p');} + static u32string prefix() {return u32string(U"pico");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'p');} + static wstring prefix() {return wstring(L"pico");} +}; + +// nano + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'n');} + static string prefix() {return string("nano");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'n');} + static u16string prefix() {return u16string(u"nano");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'n');} + static u32string prefix() {return u32string(U"nano");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'n');} + static wstring prefix() {return wstring(L"nano");} +}; + +// micro + +template <> +struct ratio_string +{ + static string symbol() {return string("\xC2\xB5");} + static string prefix() {return string("micro");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'\xB5');} + static u16string prefix() {return u16string(u"micro");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'\xB5');} + static u32string prefix() {return u32string(U"micro");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'\xB5');} + static wstring prefix() {return wstring(L"micro");} +}; + +// milli + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'm');} + static string prefix() {return string("milli");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'm');} + static u16string prefix() {return u16string(u"milli");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'm');} + static u32string prefix() {return u32string(U"milli");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'm');} + static wstring prefix() {return wstring(L"milli");} +}; + +// centi + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'c');} + static string prefix() {return string("centi");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'c');} + static u16string prefix() {return u16string(u"centi");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'c');} + static u32string prefix() {return u32string(U"centi");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'c');} + static wstring prefix() {return wstring(L"centi");} +}; + +// deci + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'd');} + static string prefix() {return string("deci");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'd');} + static u16string prefix() {return u16string(u"deci");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'd');} + static u32string prefix() {return u32string(U"deci");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'd');} + static wstring prefix() {return wstring(L"deci");} +}; + +// deca + +template <> +struct ratio_string +{ + static string symbol() {return string("da");} + static string prefix() {return string("deca");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(u"da");} + static u16string prefix() {return u16string(u"deca");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(U"da");} + static u32string prefix() {return u32string(U"deca");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(L"da");} + static wstring prefix() {return wstring(L"deca");} +}; + +// hecto + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'h');} + static string prefix() {return string("hecto");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'h');} + static u16string prefix() {return u16string(u"hecto");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'h');} + static u32string prefix() {return u32string(U"hecto");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'h');} + static wstring prefix() {return wstring(L"hecto");} +}; + +// kilo + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'k');} + static string prefix() {return string("kilo");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'k');} + static u16string prefix() {return u16string(u"kilo");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'k');} + static u32string prefix() {return u32string(U"kilo");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'k');} + static wstring prefix() {return wstring(L"kilo");} +}; + +// mega + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'M');} + static string prefix() {return string("mega");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'M');} + static u16string prefix() {return u16string(u"mega");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'M');} + static u32string prefix() {return u32string(U"mega");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'M');} + static wstring prefix() {return wstring(L"mega");} +}; + +// giga + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'G');} + static string prefix() {return string("giga");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'G');} + static u16string prefix() {return u16string(u"giga");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'G');} + static u32string prefix() {return u32string(U"giga");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'G');} + static wstring prefix() {return wstring(L"giga");} +}; + +// tera + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'T');} + static string prefix() {return string("tera");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'T');} + static u16string prefix() {return u16string(u"tera");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'T');} + static u32string prefix() {return u32string(U"tera");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'T');} + static wstring prefix() {return wstring(L"tera");} +}; + +// peta + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'P');} + static string prefix() {return string("peta");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'P');} + static u16string prefix() {return u16string(u"peta");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'P');} + static u32string prefix() {return u32string(U"peta");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'P');} + static wstring prefix() {return wstring(L"peta");} +}; + +// exa + +template <> +struct ratio_string +{ + static string symbol() {return string(1, 'E');} + static string prefix() {return string("exa");} +}; + +#if HAS_UNICODE_SUPPORT + +template <> +struct ratio_string +{ + static u16string symbol() {return u16string(1, u'E');} + static u16string prefix() {return u16string(u"exa");} +}; + +template <> +struct ratio_string +{ + static u32string symbol() {return u32string(1, U'E');} + static u32string prefix() {return u32string(U"exa");} +}; + +#endif + +template <> +struct ratio_string +{ + static wstring symbol() {return wstring(1, L'E');} + static wstring prefix() {return wstring(L"exa");} +}; + +} + +#endif // _RATIO_IO diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 645942998..ea272da56 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -134,14 +134,24 @@ if (NOT HEADLESS) message(" - windeployqt path: ${WINDEPLOYQT_APP}") endif() -# TODO check node && npm version - find_program(ETH_NODE node) - string(REGEX REPLACE "node" "" ETH_NODE_DIRECTORY ${ETH_NODE}) - message(" - nodejs location : ${ETH_NODE}") - - find_program(ETH_NPM npm) - string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) - message(" - npm location : ${ETH_NPM}") + if (USENPM) + + # TODO check node && npm version + find_program(ETH_NODE node) + string(REGEX REPLACE "node" "" ETH_NODE_DIRECTORY ${ETH_NODE}) + message(" - nodejs location : ${ETH_NODE}") + + find_program(ETH_NPM npm) + string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) + message(" - npm location : ${ETH_NPM}") + + if (NOT ETH_NODE) + message(FATAL_ERROR "node not found!") + endif() + if (NOT ETH_NPM) + message(FATAL_ERROR "npm not found!") + endif() + endif() endif() #HEADLESS diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 431a95580..07e92f542 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.8.2"; +char const* Version = "0.9.0"; } diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 03fc9cffc..314600e43 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -35,6 +35,7 @@ #include #include #include +#include #include "Common.h" #include "Base64.h" diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 49c6ed2bf..bbc40e66c 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -154,25 +154,17 @@ public: } }; - inline FixedHash<32> bloom() const - { - FixedHash<32> ret; - for (auto i: m_data) - ret[i / 8] |= 1 << (i % 8); - return ret; - } - template inline FixedHash& shiftBloom(FixedHash const& _h) { - return (*this |= _h.template nbloom()); + return (*this |= _h.template bloom()); } template inline bool containsBloom(FixedHash const& _h) { - return contains(_h.template nbloom()); + return contains(_h.template bloom()); } - template inline FixedHash nbloom() const + template inline FixedHash bloom() const { static const unsigned c_bloomBits = M * 8; unsigned mask = c_bloomBits - 1; diff --git a/libdevcore/StructuredLogger.cpp b/libdevcore/StructuredLogger.cpp index d93a9496e..ff267ff4e 100644 --- a/libdevcore/StructuredLogger.cpp +++ b/libdevcore/StructuredLogger.cpp @@ -22,11 +22,12 @@ */ #include "StructuredLogger.h" - #include +#include #include #include "Guards.h" +namespace ba = boost::asio; using namespace std; namespace dev diff --git a/libdevcore/StructuredLogger.h b/libdevcore/StructuredLogger.h index 58b408ede..eea19d107 100644 --- a/libdevcore/StructuredLogger.h +++ b/libdevcore/StructuredLogger.h @@ -27,9 +27,10 @@ #include #include -#include namespace Json { class Value; } +namespace boost { namespace asio { namespace ip { templateclass basic_endpoint; class tcp; }}} +namespace bi = boost::asio::ip; namespace dev { @@ -61,12 +62,16 @@ public: static void stopping(std::string const& _clientImpl, const char* _ethVersion); static void p2pConnected( std::string const& _id, - bi::tcp::endpoint const& _addr, + bi::basic_endpoint const& _addr, std::chrono::system_clock::time_point const& _ts, std::string const& _remoteVersion, unsigned int _numConnections ); - static void p2pDisconnected(std::string const& _id, bi::tcp::endpoint const& _addr, unsigned int _numConnections); + static void p2pDisconnected( + std::string const& _id, + bi::basic_endpoint const& _addr, + unsigned int _numConnections + ); static void minedNewBlock( std::string const& _hash, std::string const& _blockNumber, diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 2c5f07e51..4ecc1cbd1 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -40,7 +40,7 @@ public: bool empty() const { return !m_count; } vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); } vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } - void retarget(_T const* _d, size_t _s) { m_data = _d; m_count = _s; } + void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; } void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } void copyTo(vector_ref::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } void populate(vector_ref::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); } diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 048134de8..e108b230f 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -15,8 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Common.cpp - * @author Gav Wood * @author Alex Leverington + * @author Gav Wood * @date 2014 */ @@ -82,6 +82,22 @@ bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext) return true; } +void dev::encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher) +{ + bytes io = _plain.toBytes(); + s_secp256k1.encryptECIES(_k, io); + o_cipher = std::move(io); +} + +bool dev::decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext) +{ + bytes io = _cipher.toBytes(); + if (!s_secp256k1.decryptECIES(_k, io)) + return false; + o_plaintext = std::move(io); + return true; +} + void dev::encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) { // TOOD: @alex @subtly do this properly. @@ -94,6 +110,54 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain) return decrypt(_k, _cipher, o_plain); } +h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) +{ + h128 iv(Nonce::get()); + return encryptSymNoAuth(_k, _plain, o_cipher, iv); +} + +h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv) +{ + o_cipher.resize(_plain.size()); + + const int c_aesKeyLen = 16; + SecByteBlock key(_k.data(), c_aesKeyLen); + try + { + CTR_Mode::Encryption e; + e.SetKeyWithIV(key, key.size(), _iv.data()); + e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); + return _iv; + } + catch (CryptoPP::Exception& _e) + { + cerr << _e.what() << endl; + o_cipher.resize(0); + return h128(); + } +} + +bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) +{ + o_plaintext.resize(_cipher.size()); + + const size_t c_aesKeyLen = 16; + SecByteBlock key(_k.data(), c_aesKeyLen); + try + { + CTR_Mode::Decryption d; + d.SetKeyWithIV(key, key.size(), _iv.data()); + d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size()); + return true; + } + catch (CryptoPP::Exception& _e) + { + cerr << _e.what() << endl; + o_plaintext.resize(0); + return false; + } +} + Public dev::recover(Signature const& _sig, h256 const& _message) { return s_secp256k1.recover(_sig, _message.ref()); diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index ccbd0953b..38e5649fb 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -15,8 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Common.h - * @author Gav Wood * @author Alex Leverington + * @author Gav Wood * @date 2014 * * Ethereum-specific data structures & algorithms. @@ -96,6 +96,21 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); +/// Encrypt payload using ECIES standard with AES128-CTR. +void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); + +/// Decrypt payload using ECIES standard with AES128-CTR. +bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); + +/// Encrypts payload with random IV/ctr using AES128-CTR. +h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); + +/// Encrypts payload with specified IV/ctr using AES128-CTR. +h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); + +/// Decrypts payload with specified IV/ctr. +bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); + /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 43993e0c5..79fd62262 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -19,8 +19,9 @@ * @date 2014 */ -#include "CryptoPP.h" #include +#include "ECDHE.h" +#include "CryptoPP.h" using namespace std; using namespace dev; @@ -31,6 +32,119 @@ static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes."); static_assert(dev::Public::size == 64, "Public key must be 64 bytes."); static_assert(dev::Signature::size == 65, "Signature must be 65 bytes."); +bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen) +{ + // interop w/go ecies implementation + + // for sha3, blocksize is 136 bytes + // for sha256, blocksize is 64 bytes + auto reps = ((kdByteLen + 7) * 8) / (64 * 8); + bytes ctr({0, 0, 0, 1}); + bytes k; + CryptoPP::SHA256 ctx; + for (unsigned i = 0; i <= reps; i++) + { + ctx.Update(ctr.data(), ctr.size()); + ctx.Update(_z.data(), Secret::size); + ctx.Update(_s1.data(), _s1.size()); + // append hash to k + bytes digest(32); + ctx.Final(digest.data()); + ctx.Restart(); + + k.reserve(k.size() + h256::size); + move(digest.begin(), digest.end(), back_inserter(k)); + + if (++ctr[3] || ++ctr[2] || ++ctr[1] || ++ctr[0]) + continue; + } + + k.resize(kdByteLen); + return move(k); +} + +void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) +{ + // interop w/go ecies implementation + auto r = KeyPair::create(); + h256 z; + ecdh::agree(r.sec(), _k, z); + auto key = eciesKDF(z, bytes(), 32); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16); + bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16); + CryptoPP::SHA256 ctx; + ctx.Update(mKeyMaterial.data(), mKeyMaterial.size()); + bytes mKey(32); + ctx.Final(mKey.data()); + + bytes cipherText; + encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); + if (cipherText.empty()) + return; + + bytes msg(1 + Public::size + h128::size + cipherText.size() + 32); + msg[0] = 0x04; + r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size)); + bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size + h128::size, cipherText.size()); + bytesConstRef(&cipherText).copyTo(msgCipherRef); + + // tag message + CryptoPP::HMAC hmacctx(mKey.data(), mKey.size()); + bytesConstRef cipherWithIV = bytesRef(&msg).cropped(1 + Public::size, h128::size + cipherText.size()); + hmacctx.Update(cipherWithIV.data(), cipherWithIV.size()); + hmacctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size()); + + io_cipher.resize(msg.size()); + io_cipher.swap(msg); +} + +bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) +{ + // interop w/go ecies implementation + + // io_cipher[0] must be 2, 3, or 4, else invalidpublickey + if (io_text[0] < 2 || io_text[0] > 4) + // invalid message: publickey + return false; + + if (io_text.size() < (1 + Public::size + h128::size + 1 + h256::size)) + // invalid message: length + return false; + + h256 z; + ecdh::agree(_k, *(Public*)(io_text.data()+1), z); + auto key = eciesKDF(z, bytes(), 64); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16); + bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16); + bytes mKey(32); + CryptoPP::SHA256 ctx; + ctx.Update(mKeyMaterial.data(), mKeyMaterial.size()); + ctx.Final(mKey.data()); + + bytes plain; + size_t cipherLen = io_text.size() - 1 - Public::size - h128::size - h256::size; + bytesConstRef cipherWithIV(io_text.data() + 1 + Public::size, h128::size + cipherLen); + bytesConstRef cipherIV = cipherWithIV.cropped(0, h128::size); + bytesConstRef cipherNoIV = cipherWithIV.cropped(h128::size, cipherLen); + bytesConstRef msgMac(cipherNoIV.data() + cipherLen, h256::size); + h128 iv(cipherIV.toBytes()); + + // verify tag + CryptoPP::HMAC hmacctx(mKey.data(), mKey.size()); + hmacctx.Update(cipherWithIV.data(), cipherWithIV.size()); + h256 mac; + hmacctx.Final(mac.data()); + for (unsigned i = 0; i < h256::size; i++) + if (mac[i] != msgMac[i]) + return false; + + decryptSymNoAuth(*(Secret*)eKey.data(), iv, cipherNoIV, plain); + io_text.resize(plain.size()); + io_text.swap(plain); + + return true; +} + void Secp256k1::encrypt(Public const& _k, bytes& io_cipher) { ECIES::Encryptor e; @@ -199,13 +313,13 @@ bool Secp256k1::verifySecret(Secret const& _s, Public& _p) void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s) { - (void)o_s; - (void)_s; - ECDH::Domain d(m_oid); + // TODO: mutex ASN1::secp256k1() singleton + // Creating Domain is non-const for m_oid and m_oid is not thread-safe + ECDH::Domain d(ASN1::secp256k1()); assert(d.AgreedValueLength() == sizeof(o_s)); byte remote[65] = {0x04}; memcpy(&remote[1], _r.data(), 64); - assert(d.Agree(o_s.data(), _s.data(), remote)); + d.Agree(o_s.data(), _s.data(), remote); } void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC const& _k, Public& o_p) diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index fa9d92aa1..4991e3713 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -65,6 +65,7 @@ inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s. /** * CryptoPP secp256k1 algorithms. + * @todo Collect ECIES methods into class. */ class Secp256k1 { @@ -75,12 +76,21 @@ public: void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); } - /// Encrypts text (replace input). + /// Encrypts text (replace input). (ECIES w/XOR-SHA1) void encrypt(Public const& _k, bytes& io_cipher); - /// Decrypts text (replace input). + /// Decrypts text (replace input). (ECIES w/XOR-SHA1) void decrypt(Secret const& _k, bytes& io_text); + /// Encrypts text (replace input). (ECIES w/AES128-CTR-SHA256) + void encryptECIES(Public const& _k, bytes& io_cipher); + + /// Decrypts text (replace input). (ECIES w/AES128-CTR-SHA256) + bool decryptECIES(Secret const& _k, bytes& io_text); + + /// Key derivation function used by encryptECIES and decryptECIES. + bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256); + /// @returns siganture of message. Signature sign(Secret const& _k, bytesConstRef _message); diff --git a/libdevcrypto/ECDHE.cpp b/libdevcrypto/ECDHE.cpp index deae3bc6d..a00a92872 100644 --- a/libdevcrypto/ECDHE.cpp +++ b/libdevcrypto/ECDHE.cpp @@ -29,7 +29,12 @@ using namespace dev::crypto; static Secp256k1 s_secp256k1; -void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) +void dev::crypto::ecdh::agree(Secret const& _s, Public const& _r, h256& o_s) +{ + s_secp256k1.agree(_s, _r, o_s); +} + +void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) const { if (m_remoteEphemeral) // agreement can only occur once diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index 4450aec4b..d3c9ae325 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -48,6 +48,11 @@ private: std::map m_sessions; Secret m_secret; }; + +namespace ecdh +{ +void agree(Secret const& _s, Public const& _r, h256& o_s); +} /** * @brief Derive DH shared secret from EC keypairs. @@ -62,12 +67,14 @@ public: /// Public key sent to remote. Public pubkey() { return m_ephemeral.pub(); } + Secret seckey() { return m_ephemeral.sec(); } + /// Input public key for dh agreement, output generated shared secret. - void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret); + void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret) const; protected: - KeyPair m_ephemeral; ///< Ephemeral keypair; generated. - Public m_remoteEphemeral; ///< Public key of remote; parameter. + KeyPair m_ephemeral; ///< Ephemeral keypair; generated. + mutable Public m_remoteEphemeral; ///< Public key of remote; parameter. Set once when agree is called, otherwise immutable. }; /** @@ -80,10 +87,10 @@ class ECDHEKeyExchange: private ECDHE { public: /// Exchange with unknown remote (pass public key for ingress exchange) - ECDHEKeyExchange(Alias& _k): m_alias(_k) {}; + ECDHEKeyExchange(Alias& _k): m_alias(_k) {} /// Exchange with known remote - ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {}; + ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {} /// Provide public key for dh agreement to generate shared secret. void agree(Public const& _remoteEphemeral); diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index 9eb622fe3..8bd4ef143 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -33,7 +33,7 @@ namespace eth { const unsigned c_protocolVersion = 56; -const unsigned c_databaseBaseVersion = 7; +const unsigned c_databaseBaseVersion = 8; #if ETH_FATDB const unsigned c_databaseVersionModifier = 1000; #else diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index a28895a1a..a56026459 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -45,18 +45,9 @@ bytes const& Ethasher::cache(BlockInfo const& _header) RecursiveGuard l(x_this); if (!m_caches.count(_header.seedHash)) { - try { - boost::filesystem::create_directories(getDataDir() + "/ethashcache"); - } catch (...) {} - std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".cache"; - m_caches[_header.seedHash] = contents(memoFile); - if (m_caches[_header.seedHash].empty()) - { - ethash_params p = params((unsigned)_header.number); - m_caches[_header.seedHash].resize(p.cache_size); - ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data()); - writeFile(memoFile, m_caches[_header.seedHash]); - } + ethash_params p = params((unsigned)_header.number); + m_caches[_header.seedHash].resize(p.cache_size); + ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data()); } return m_caches[_header.seedHash]; } @@ -71,6 +62,9 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) delete [] m_fulls.begin()->second.data(); m_fulls.erase(m_fulls.begin()); } + try { + boost::filesystem::create_directories(getDataDir() + "/ethashcache"); + } catch (...) {} std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".full"; m_fulls[_header.seedHash] = contentsNew(memoFile); if (!m_fulls[_header.seedHash]) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index dd0bfad67..95bfcb015 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -324,6 +324,18 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) WriteGuard l(x_blockHashes); m_blockHashes[h256(bi.number)].value = newHash; } + { + WriteGuard l(x_blocksBlooms); + LogBloom blockBloom = bi.logBloom; + blockBloom.shiftBloom<3, 32>(sha3(bi.coinbaseAddress.ref())); + unsigned index = (unsigned)bi.number; + for (unsigned level = 0; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize) + { + unsigned i = index / c_bloomIndexSize % c_bloomIndexSize; + unsigned o = index % c_bloomIndexSize; + m_blocksBlooms[chunkId(level, i)].blooms[o] |= blockBloom; + } + } // Collate transaction hashes and remember who they were. h256s tas; { @@ -346,14 +358,23 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) m_receipts[newHash] = br; } - m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp())); - for (auto const& h: tas) - m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); - m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); + { + ReadGuard l1(x_blocksBlooms); + ReadGuard l2(x_details); + ReadGuard l3(x_blockHashes); + ReadGuard l4(x_receipts); + ReadGuard l5(x_logBlooms); + ReadGuard l6(x_transactionAddresses); + m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp())); + for (auto const& h: tas) + m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraBlocksBlooms), (ldb::Slice)dev::ref(m_blocksBlooms[newHash].rlp())); + } #if ETH_PARANOIA checkConsistency(); @@ -475,29 +496,30 @@ template static unsigned getHashSize(map const& _map) void BlockChain::updateStats() const { { - ReadGuard l1(x_blocks); + ReadGuard l(x_blocks); m_lastStats.memBlocks = 0; for (auto const& i: m_blocks) m_lastStats.memBlocks += i.second.size() + 64; } { - ReadGuard l2(x_details); + ReadGuard l(x_details); m_lastStats.memDetails = getHashSize(m_details); } { - ReadGuard l5(x_logBlooms); - m_lastStats.memLogBlooms = getHashSize(m_logBlooms); + ReadGuard l1(x_logBlooms); + ReadGuard l2(x_blocksBlooms); + m_lastStats.memLogBlooms = getHashSize(m_logBlooms) + getHashSize(m_blocksBlooms); } { - ReadGuard l4(x_receipts); + ReadGuard l(x_receipts); m_lastStats.memReceipts = getHashSize(m_receipts); } { - ReadGuard l3(x_blockHashes); + ReadGuard l(x_blockHashes); m_lastStats.memBlockHashes = getHashSize(m_blockHashes); } { - ReadGuard l6(x_transactionAddresses); + ReadGuard l(x_transactionAddresses); m_lastStats.memTransactionAddresses = getHashSize(m_transactionAddresses); } } @@ -520,6 +542,7 @@ void BlockChain::garbageCollect(bool _force) WriteGuard l4(x_receipts); WriteGuard l5(x_logBlooms); WriteGuard l6(x_transactionAddresses); + WriteGuard l7(x_blocksBlooms); for (CacheID const& id: m_cacheUsage.back()) { m_inUse.erase(id); @@ -544,6 +567,9 @@ void BlockChain::garbageCollect(bool _force) case ExtraTransactionAddress: m_transactionAddresses.erase(id.first); break; + case ExtraBlocksBlooms: + m_blocksBlooms.erase(id.first); + break; } } m_cacheUsage.pop_back(); @@ -579,6 +605,76 @@ void BlockChain::checkConsistency() delete it; } +static inline unsigned upow(unsigned a, unsigned b) { while (b-- > 0) a *= a; return a; } +static inline unsigned ceilDiv(unsigned n, unsigned d) { return n / (n + d - 1); } +static inline unsigned floorDivPow(unsigned n, unsigned a, unsigned b) { return n / upow(a, b); } +static inline unsigned ceilDivPow(unsigned n, unsigned a, unsigned b) { return ceilDiv(n, upow(a, b)); } + +// Level 1 +// [xxx. ] + +// Level 0 +// [.x............F.] +// [........x.......] +// [T.............x.] +// [............ ] + +// F = 14. T = 32 + +vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const +{ + vector ret; + + // start from the top-level + unsigned u = upow(c_bloomIndexSize, c_bloomIndexLevels); + + // run through each of the top-level blockbloom blocks + for (unsigned index = _earliest / u; index <= ceilDiv(_latest, u); ++index) // 0 + ret += withBlockBloom(_b, _earliest, _latest, c_bloomIndexLevels - 1, index); + + return ret; +} + +vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _level, unsigned _index) const +{ + // 14, 32, 1, 0 + // 14, 32, 0, 0 + // 14, 32, 0, 1 + // 14, 32, 0, 2 + + vector ret; + + unsigned uCourse = upow(c_bloomIndexSize, _level + 1); + // 256 + // 16 + unsigned uFine = upow(c_bloomIndexSize, _level); + // 16 + // 1 + + unsigned obegin = _index == _earliest / uCourse ? _earliest / uFine % c_bloomIndexSize : 0; + // 0 + // 14 + // 0 + // 0 + unsigned oend = _index == _latest / uCourse ? (_latest / uFine) % c_bloomIndexSize + 1 : c_bloomIndexSize; + // 3 + // 16 + // 16 + // 1 + + BlocksBlooms bb = blocksBlooms(_level, _index); + for (unsigned o = obegin; o < oend; ++o) + if (bb.blooms[o].contains(_b)) + { + // This level has something like what we want. + if (_level > 0) + ret += withBlockBloom(_b, _earliest, _latest, _level - 1, o + _index * c_bloomIndexSize); + else + ret.push_back(o + _index * c_bloomIndexSize); + } + return ret; +} + h256Set BlockChain::allUnclesFrom(h256 _parent) const { // Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 0c1a81066..a6760a3f6 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -71,7 +71,8 @@ enum { ExtraBlockHash, ExtraTransactionAddress, ExtraLogBlooms, - ExtraReceipts + ExtraReceipts, + ExtraBlocksBlooms }; /** @@ -132,6 +133,26 @@ public: /// Get a list of transaction hashes for a given block. Thread-safe. h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; } + /** Get the block blooms for a number of blocks. Thread-safe. + * @returns the object pertaining to the blocks: + * level 0: + * 0x, 0x + 1, .. (1x - 1) + * 1x, 1x + 1, .. (2x - 1) + * ... + * (255x .. (256x - 1)) + * level 1: + * 0x .. (1x - 1), 1x .. (2x - 1), ..., (255x .. (256x - 1)) + * 256x .. (257x - 1), 257x .. (258x - 1), ..., (511x .. (512x - 1)) + * ... + * level n, index i, offset o: + * i * (x ^ n) + o * x ^ (n - 1) + */ + BlocksBlooms blocksBlooms(unsigned _level, unsigned _index) const { return blocksBlooms(chunkId(_level, _index)); } + BlocksBlooms blocksBlooms(h256 const& _chunkId) const { return queryExtras(_chunkId, m_blocksBlooms, x_blocksBlooms, NullBlocksBlooms); } + LogBloom blockBloom(unsigned _number) const { return blocksBlooms(chunkId(0, _number / c_bloomIndexSize)).blooms[_number % c_bloomIndexSize]; } + std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const; + std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const; + /// Get a transaction from its hash. Thread-safe. bytes transaction(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } @@ -188,6 +209,8 @@ public: void garbageCollect(bool _force = false); private: + static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); } + void open(std::string _path, bool _killExisting = false); void close(); @@ -230,6 +253,8 @@ private: mutable TransactionAddressHash m_transactionAddresses; mutable SharedMutex x_blockHashes; mutable BlockHashHash m_blockHashes; + mutable SharedMutex x_blocksBlooms; + mutable BlocksBloomsHash m_blocksBlooms; using CacheID = std::pair; mutable Mutex x_cacheUsage; diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index ed478568d..572ed1888 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -36,6 +36,11 @@ namespace dev namespace eth { +// TODO: OPTIMISE: constructors take bytes, RLP used only in necessary classes. + +static const unsigned c_bloomIndexSize = 16; +static const unsigned c_bloomIndexLevels = 2; + struct BlockDetails { BlockDetails(): number(0), totalDifficulty(0) {} @@ -64,6 +69,16 @@ struct BlockLogBlooms mutable unsigned size; }; +struct BlocksBlooms +{ + BlocksBlooms() {} + BlocksBlooms(RLP const& _r) { blooms = _r.toArray(); size = _r.data().size(); } + bytes rlp() const { RLPStream s; s << blooms; size = s.out().size(); return s.out(); } + + std::array blooms; + mutable unsigned size; +}; + struct BlockReceipts { BlockReceipts() {} @@ -103,12 +118,14 @@ using BlockLogBloomsHash = std::map; using BlockReceiptsHash = std::map; using TransactionAddressHash = std::map; using BlockHashHash = std::map; +using BlocksBloomsHash = std::map; static const BlockDetails NullBlockDetails; static const BlockLogBlooms NullBlockLogBlooms; static const BlockReceipts NullBlockReceipts; static const TransactionAddress NullTransactionAddress; static const BlockHash NullBlockHash; +static const BlocksBlooms NullBlocksBlooms; } } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index b6c310d34..165ac20ca 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -833,8 +833,6 @@ LocalisedLogEntries Client::logs(LogFilter const& _f) const LocalisedLogEntries ret; unsigned begin = min(m_bc.number() + 1, (unsigned)_f.latest()); unsigned end = min(m_bc.number(), min(begin, (unsigned)_f.earliest())); - unsigned m = _f.max(); - unsigned s = _f.skip(); // Handle pending transactions differently as they're not on the block chain. if (begin > m_bc.number()) @@ -847,68 +845,52 @@ LocalisedLogEntries Client::logs(LogFilter const& _f) const auto sha3 = m_postMine.pending()[i].sha3(); LogEntries le = _f.matches(tr); if (le.size()) - { - for (unsigned j = 0; j < le.size() && ret.size() != m; ++j) - if (s) - s--; - else - ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3)); - } + for (unsigned j = 0; j < le.size(); ++j) + ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3)); } begin = m_bc.number(); } + set matchingBlocks; + for (auto const& i: _f.bloomPossibilities()) + for (auto u: m_bc.withBlockBloom(i, end, begin)) + matchingBlocks.insert(u); + #if ETH_DEBUG - // fill these params - unsigned skipped = 0; unsigned falsePos = 0; #endif - auto h = m_bc.numberHash(begin); - unsigned n = begin; - for (; ret.size() != m && n != end; n--, h = m_bc.details(h).parent) + for (auto n: matchingBlocks) { #if ETH_DEBUG int total = 0; #endif - // check block bloom - auto info = m_bc.info(h); + auto h = m_bc.numberHash(n); auto receipts = m_bc.receipts(h).receipts; - if (_f.matches(info.logBloom)) - for (size_t i = 0; i < receipts.size(); i++) + for (size_t i = 0; i < receipts.size(); i++) + { + TransactionReceipt receipt = receipts[i]; + if (_f.matches(receipt.bloom())) { - TransactionReceipt receipt = receipts[i]; - if (_f.matches(receipt.bloom())) + auto info = m_bc.info(h); + auto h = transaction(info.hash, i).sha3(); + LogEntries le = _f.matches(receipt); + if (le.size()) { - auto h = transaction(info.hash, i).sha3(); - LogEntries le = _f.matches(receipt); - if (le.size()) - { #if ETH_DEBUG - total += le.size(); + total += le.size(); #endif - for (unsigned j = 0; j < le.size() && ret.size() != m; ++j) - { - if (s) - s--; - else - ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h)); - } - } + for (unsigned j = 0; j < le.size(); ++j) + ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h)); } -#if ETH_DEBUG - if (!total) - falsePos++; -#endif } #if ETH_DEBUG - else - skipped++; + if (!total) + falsePos++; #endif - if (n == end) - break; + } } #if ETH_DEBUG - cdebug << (begin - n) << "searched; " << skipped << "skipped; " << falsePos << "false +ves"; + cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves"; #endif return ret; } diff --git a/libethereum/Client.h b/libethereum/Client.h index 9cbfd7989..2c8db2aed 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -46,7 +46,6 @@ namespace dev { - namespace eth { diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 95e1aadda..58cc5ca72 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -297,13 +297,13 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) { case StatusPacket: { - m_protocolVersion = _r[1].toInt(); - m_networkId = _r[2].toInt(); + m_protocolVersion = _r[0].toInt(); + m_networkId = _r[1].toInt(); // a bit dirty as we're misusing these to communicate the values to transition, but harmless. - m_totalDifficulty = _r[3].toInt(); - m_latestHash = _r[4].toHash(); - auto genesisHash = _r[5].toHash(); + m_totalDifficulty = _r[2].toInt(); + m_latestHash = _r[3].toHash(); + auto genesisHash = _r[4].toHash(); clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged(); @@ -324,10 +324,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case GetTransactionsPacket: break; // DEPRECATED. case TransactionsPacket: { - clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; - addRating(_r.itemCount() - 1); + clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)"; + addRating(_r.itemCount()); Guard l(x_knownTransactions); - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = sha3(_r[i].data()); m_knownTransactions.insert(h); @@ -339,8 +339,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case GetBlockHashesPacket: { - h256 later = _r[1].toHash(); - unsigned limit = _r[2].toInt(); + h256 later = _r[0].toHash(); + unsigned limit = _r[1].toInt(); clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")"; unsigned c = min(host()->m_chain.number(later), limit); @@ -355,19 +355,19 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case BlockHashesPacket: { - clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); + clogS(NetMessageSummary) << "BlockHashes (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreHashes"); if (m_asking != Asking::Hashes) { cwarn << "Peer giving us hashes when we didn't ask for them."; break; } - if (_r.itemCount() == 1) + if (_r.itemCount() == 0) { transition(Asking::Blocks); return true; } - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = _r[i].toHash(); if (host()->m_chain.isKnown(h)) @@ -384,11 +384,11 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case GetBlocksPacket: { - clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << "entries)"; + clogS(NetMessageSummary) << "GetBlocks (" << dec << _r.itemCount() << "entries)"; // return the requested blocks. bytes rlp; unsigned n = 0; - for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i) + for (unsigned i = 0; i < _r.itemCount() && i <= c_maxBlocks; ++i) { auto b = host()->m_chain.block(_r[i].toHash()); if (b.size()) @@ -404,12 +404,12 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case BlocksPacket: { - clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); + clogS(NetMessageSummary) << "Blocks (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreBlocks"); if (m_asking != Asking::Blocks) clogS(NetWarn) << "Unexpected Blocks received!"; - if (_r.itemCount() == 1) + if (_r.itemCount() == 0) { // Got to this peer's latest block - just give up. transition(Asking::Nothing); @@ -422,7 +422,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) unsigned got = 0; unsigned repeated = 0; - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = BlockInfo::headerHash(_r[i].data()); if (m_sub.noteBlock(h)) @@ -467,14 +467,14 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case NewBlockPacket: { - auto h = BlockInfo::headerHash(_r[1].data()); + auto h = BlockInfo::headerHash(_r[0].data()); clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); - if (_r.itemCount() != 3) + if (_r.itemCount() != 2) disable("NewBlock without 2 data fields."); else { - switch (host()->m_bq.import(_r[1].data(), host()->m_chain)) + switch (host()->m_bq.import(_r[0].data(), host()->m_chain)) { case ImportResult::Success: addRating(100); @@ -493,7 +493,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case ImportResult::UnknownParent: clogS(NetMessageSummary) << "Received block with no known parent. Resyncing..."; - setNeedsSyncing(h, _r[2].toInt()); + setNeedsSyncing(h, _r[1].toInt()); break; } Guard l(x_knownBlocks); diff --git a/libethereum/LogFilter.cpp b/libethereum/LogFilter.cpp index 1784094b0..d1b3cf437 100644 --- a/libethereum/LogFilter.cpp +++ b/libethereum/LogFilter.cpp @@ -30,13 +30,13 @@ using namespace dev::eth; std::ostream& dev::eth::operator<<(std::ostream& _out, LogFilter const& _s) { // TODO - _out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< +" << _s.m_skip << "^" << _s.m_max << ")"; + _out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< )"; return _out; } void LogFilter::streamRLP(RLPStream& _s) const { - _s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip; + _s.appendList(4) << m_addresses << m_topics << m_earliest << m_latest; } h256 LogFilter::sha3() const @@ -73,6 +73,16 @@ bool LogFilter::matches(State const& _s, unsigned _i) const return matches(_s.receipt(_i)).size() > 0; } +vector LogFilter::bloomPossibilities() const +{ + // return combination of each of the addresses/topics + vector ret; + // TODO proper combinatorics. + for (auto i: m_addresses) + ret.push_back(LogBloom().shiftBloom<3, 32>(dev::sha3(i))); + return ret; +} + LogEntries LogFilter::matches(TransactionReceipt const& _m) const { LogEntries ret; diff --git a/libethereum/LogFilter.h b/libethereum/LogFilter.h index 7b8922a03..a28bfe806 100644 --- a/libethereum/LogFilter.h +++ b/libethereum/LogFilter.h @@ -45,23 +45,21 @@ class State; class LogFilter { public: - LogFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} + LogFilter(int _earliest = 0, int _latest = -1): m_earliest(_earliest), m_latest(_latest) {} void streamRLP(RLPStream& _s) const; h256 sha3() const; int earliest() const { return m_earliest; } int latest() const { return m_latest; } - unsigned max() const { return m_max; } - unsigned skip() const { return m_skip; } + + std::vector bloomPossibilities() const; bool matches(LogBloom _bloom) const; bool matches(State const& _s, unsigned _i) const; LogEntries matches(TransactionReceipt const& _r) const; LogFilter address(Address _a) { m_addresses.insert(_a); return *this; } LogFilter topic(unsigned _index, h256 const& _t) { if (_index < 4) m_topics[_index].insert(_t); return *this; } - LogFilter withMax(unsigned _m) { m_max = _m; return *this; } - LogFilter withSkip(unsigned _m) { m_skip = _m; return *this; } LogFilter withEarliest(int _e) { m_earliest = _e; return *this; } LogFilter withLatest(int _e) { m_latest = _e; return *this; } @@ -72,8 +70,6 @@ private: std::array m_topics; int m_earliest = 0; int m_latest = -1; - unsigned m_max = 10; - unsigned m_skip = 0; }; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index d1e430dbe..9bfc0c08c 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -555,7 +555,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) BOOST_THROW_EXCEPTION(TooManyUncles()); set nonces = { m_currentBlock.nonce }; - Addresses rewarded; + vector rewarded; set knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash); for (auto const& i: rlp[2]) @@ -574,7 +574,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) nonces.insert(uncle.nonce); tdIncrease += uncle.difficulty; - rewarded.push_back(uncle.coinbaseAddress); + rewarded.push_back(uncle); } applyRewards(rewarded); @@ -696,7 +696,7 @@ void State::commitToMine(BlockChain const& _bc) m_lastTx = m_db; - Addresses uncleAddresses; + vector uncleBlockHeaders; RLPStream unclesData; unsigned unclesCount = 0; @@ -716,7 +716,7 @@ void State::commitToMine(BlockChain const& _bc) BlockInfo ubi(_bc.block(u)); ubi.streamRLP(unclesData, WithNonce); ++unclesCount; - uncleAddresses.push_back(ubi.coinbaseAddress); + uncleBlockHeaders.push_back(ubi); if (unclesCount == 2) break; } @@ -760,7 +760,7 @@ void State::commitToMine(BlockChain const& _bc) m_currentBlock.sha3Uncles = sha3(m_currentUncles); // Apply rewards last of all. - applyRewards(uncleAddresses); + applyRewards(uncleBlockHeaders); // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. commit(); @@ -1148,12 +1148,12 @@ State State::fromPending(unsigned _i) const return ret; } -void State::applyRewards(Addresses const& _uncleAddresses) +void State::applyRewards(vector const& _uncleBlockHeaders) { u256 r = m_blockReward; - for (auto const& i: _uncleAddresses) + for (auto const& i: _uncleBlockHeaders) { - addBalance(i, m_blockReward * 15 / 16); + addBalance(i.coinbaseAddress, m_blockReward * (8 + i.number - m_currentBlock.number) / 8); r += m_blockReward / 32; } addBalance(m_currentBlock.coinbaseAddress, r); diff --git a/libethereum/State.h b/libethereum/State.h index 78ec85f38..1f8516fce 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -314,7 +314,7 @@ private: u256 enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce = true); /// Finalise the block, applying the earned rewards. - void applyRewards(Addresses const& _uncleAddresses); + void applyRewards(std::vector const& _uncleBlockHeaders); /// @returns gas used by transactions thus far executed. u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; } diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index ef4021d51..0a0611292 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -77,6 +77,20 @@ int AssemblyItem::deposit() const return 0; } +string AssemblyItem::getJumpTypeAsString() const +{ + switch (m_jumpType) + { + case JumpType::IntoFunction: + return "[in]"; + case JumpType::OutOfFunction: + return "[out]"; + case JumpType::Ordinary: + default: + return ""; + } +} + unsigned Assembly::bytesRequired() const { for (unsigned br = 1;; ++br) @@ -99,6 +113,8 @@ void Assembly::append(Assembly const& _a) { if (i.type() == Tag || i.type() == PushTag) i.m_data += m_usedTags; + else if (i.type() == PushSub || i.type() == PushSubSize) + i.m_data += m_subs.size(); append(i); } m_deposit = newDeposit; @@ -108,7 +124,7 @@ void Assembly::append(Assembly const& _a) for (auto const& i: _a.m_strings) m_strings.insert(i); for (auto const& i: _a.m_subs) - m_subs.insert(i); + m_subs.push_back(i); assert(!_a.m_baseDeposit); assert(!_a.m_totalDeposit); @@ -194,7 +210,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc return move(cut); } -ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const +ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const { _out << _prefix << ".code:" << endl; for (AssemblyItem const& i: m_items) @@ -203,7 +219,7 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con switch (i.m_type) { case Operation: - _out << " " << instructionInfo((Instruction)(byte)i.m_data).name; + _out << " " << instructionInfo((Instruction)(byte)i.m_data).name << "\t" << i.getJumpTypeAsString(); break; case Push: _out << " PUSH " << i.m_data; @@ -238,19 +254,19 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con default: BOOST_THROW_EXCEPTION(InvalidOpcode()); } - _out << string("\t\t") << getLocationFromSources(_sourceCodes, i.getLocation()) << endl; + _out << "\t\t" << getLocationFromSources(_sourceCodes, i.getLocation()) << endl; } if (!m_data.empty() || !m_subs.empty()) { _out << _prefix << ".data:" << endl; for (auto const& i: m_data) - if (!m_subs.count(i.first)) + if (u256(i.first) >= m_subs.size()) _out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << toHex(i.second) << endl; - for (auto const& i: m_subs) + for (size_t i = 0; i < m_subs.size(); ++i) { - _out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << endl; - i.second.streamRLP(_out, _prefix + " ", _sourceCodes); + _out << _prefix << " " << hex << i << ": " << endl; + m_subs[i].stream(_out, _prefix + " ", _sourceCodes); } } return _out; @@ -493,8 +509,8 @@ Assembly& Assembly::optimise(bool _enable) copt << total << " optimisations done."; - for (auto& i: m_subs) - i.second.optimise(true); + for (auto& sub: m_subs) + sub.optimise(true); return *this; } @@ -511,8 +527,8 @@ bytes Assembly::assemble() const unsigned bytesPerTag = dev::bytesRequired(totalBytes); byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; - for (auto const& i: m_subs) - m_data[i.first] = i.second.assemble(); + for (size_t i = 0; i < m_subs.size(); ++i) + m_data[u256(i)] = m_subs[i].assemble(); unsigned bytesRequiredIncludingData = bytesRequired(); unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData); diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index c3bc1bc55..242f6f1ff 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -42,6 +42,8 @@ class AssemblyItem friend class Assembly; public: + enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; + AssemblyItem(u256 _push): m_type(Push), m_data(_push) {} AssemblyItem(Instruction _i): m_type(Operation), m_data((byte)_i) {} AssemblyItem(AssemblyItemType _type, u256 _data = 0): m_type(_type), m_data(_data) {} @@ -58,13 +60,18 @@ public: int deposit() const; bool match(AssemblyItem const& _i) const { return _i.m_type == UndefinedItem || (m_type == _i.m_type && (m_type != Operation || m_data == _i.m_data)); } - void setLocation(SourceLocation const& _location) { m_location = _location;} + void setLocation(SourceLocation const& _location) { m_location = _location; } SourceLocation const& getLocation() const { return m_location; } + void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; } + JumpType getJumpType() const { return m_jumpType; } + std::string getJumpTypeAsString() const; + private: AssemblyItemType m_type; u256 m_data; SourceLocation m_location; + JumpType m_jumpType = JumpType::Ordinary; }; using AssemblyItems = std::vector; @@ -81,9 +88,9 @@ public: AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); } - AssemblyItem newSub(Assembly const& _sub) { h256 h = h256::random(s_fixedHashEngine); m_subs[h] = _sub; return AssemblyItem(PushSub, h); } + AssemblyItem newSub(Assembly const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); } - AssemblyItem newPushSubSize(h256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } + AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem append() { return append(newTag()); } void append(Assembly const& _a); @@ -114,7 +121,7 @@ public: void popTo(int _deposit) { while (m_deposit > _deposit) append(Instruction::POP); } void injectStart(AssemblyItem const& _i); - std::string out() const { std::stringstream ret; streamRLP(ret); return ret.str(); } + std::string out() const { std::stringstream ret; stream(ret); return ret.str(); } int deposit() const { return m_deposit; } void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); } void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); } @@ -124,7 +131,7 @@ public: bytes assemble() const; Assembly& optimise(bool _enable); - std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const; + std::ostream& stream(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const; protected: std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const; @@ -134,7 +141,7 @@ protected: unsigned m_usedTags = 0; AssemblyItems m_items; mutable std::map m_data; - std::map m_subs; + std::vector m_subs; std::map m_strings; int m_deposit = 0; @@ -146,7 +153,7 @@ protected: inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a) { - _a.streamRLP(_out); + _a.stream(_out); return _out; } diff --git a/libjsqrc/CMakeLists.txt b/libjsqrc/CMakeLists.txt index 68500ada6..6554e523d 100644 --- a/libjsqrc/CMakeLists.txt +++ b/libjsqrc/CMakeLists.txt @@ -12,7 +12,7 @@ qt5_add_resources(JSQRC js.qrc) add_library(jsqrc STATIC ${JSQRC}) target_link_libraries(jsqrc Qt5::Core) -if (ETH_NODE AND ETH_NPM) +if (USENPM) add_custom_target(ethereumjs) add_custom_command(TARGET ethereumjs POST_BUILD diff --git a/libnatspec/NatspecExpressionEvaluator.cpp b/libnatspec/NatspecExpressionEvaluator.cpp index fd8369145..ab957295e 100644 --- a/libnatspec/NatspecExpressionEvaluator.cpp +++ b/libnatspec/NatspecExpressionEvaluator.cpp @@ -35,25 +35,23 @@ static QString contentsOfQResource(string const& _res) return in.readAll(); } -NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _method, QString const& _params) +NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _transaction, QString const& _method) +: m_abi(_abi), m_transaction(_transaction), m_method(_method) { Q_INIT_RESOURCE(natspec); QJSValue result = m_engine.evaluate(contentsOfQResource(":/natspec/natspec.js")); if (result.isError()) BOOST_THROW_EXCEPTION(FileError()); - - m_engine.evaluate("globals.abi = " + _abi); - m_engine.evaluate("globals.method = " + _method); - m_engine.evaluate("globals.params = " + _params); + + m_engine.evaluate("var natspec = require('natspec')"); } QString NatspecExpressionEvaluator::evalExpression(QString const& _expression) { - QJSValue result = m_engine.evaluate("evaluateExpression(\"" + _expression + "\")"); - if (result.isError()) - { - cerr << "Could not evaluate expression: \"" << _expression.toStdString() << "\"" << endl; - return _expression; - } + QString call = ""; + if (!m_abi.isEmpty() && !m_transaction.isEmpty() && !m_method.isEmpty()) + call = ", {abi:" + m_abi + ", transaction:" + m_transaction + ", method: '" + m_method + "' }"; + + QJSValue result = m_engine.evaluate("natspec.evaluateExpressionSafe(\"" + _expression + "\"" + call + ")"); return result.toString(); } diff --git a/libnatspec/NatspecExpressionEvaluator.h b/libnatspec/NatspecExpressionEvaluator.h index 2ea224027..4aca090d6 100644 --- a/libnatspec/NatspecExpressionEvaluator.h +++ b/libnatspec/NatspecExpressionEvaluator.h @@ -34,12 +34,10 @@ class NatspecExpressionEvaluator public: /// Construct natspec expression evaluator /// @params abi - contract's abi in json format, passed as string + /// @params transaction - json object containing transaction data /// @params method - name of the contract's method for which we evaluate the natspec. - /// If we want to use raw string, it should be passed with quotation marks eg. "\"helloWorld\"" - /// If we pass string "helloWorld", the value of the object with name "helloWorld" will be used - /// @params params - array of method input params, passed as string, objects in array should be /// javascript valid objects - NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _method = "", QString const& _params = "[]"); + NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _transaction = "{}", QString const& _method = ""); /// Should be called to evaluate natspec expression /// @params expression - natspec expression @@ -48,4 +46,7 @@ public: private: QJSEngine m_engine; + QString m_abi; + QString m_transaction; + QString m_method; }; diff --git a/libnatspec/natspec.js b/libnatspec/natspec.js deleted file mode 100644 index ed30b5378..000000000 --- a/libnatspec/natspec.js +++ /dev/null @@ -1,102 +0,0 @@ - -/** - * This plugin exposes 'evaluateExpression' method which should be used - * to evaluate natspec description - */ - -/// Object which should be used by NatspecExpressionEvaluator -/// abi - abi of the contract that will be used -/// method - name of the method that is called -/// params - input params of the method that will be called -var globals = { - abi: [], - method: "", - params: [] -}; - -/// Helper method -/// Should be called to copy values from object to global context -var copyToContext = function (obj, context) { - var keys = Object.keys(obj); - keys.forEach(function (key) { - context[key] = obj[key]; - }); -} - -/// Helper method -/// Should be called to get method with given name from the abi -/// @param contract's abi -/// @param name of the method that we are looking for -var getMethodWithName = function(abi, name) { - for (var i = 0; i < abi.length; i++) { - if (abi[i].name === name) { - return abi[i]; - } - } - //console.warn('could not find method with name: ' + name); - return undefined; -}; - -/// Function called to get all contract's storage values -/// @returns hashmap with contract properties which are used -/// TODO: check if this function will be used -var getContractProperties = function (address, abi) { - return {}; -}; - -/// Function called to get all contract's methods -/// @returns hashmap with used contract's methods -/// TODO: check if this function will be used -var getContractMethods = function (address, abi) { - //return web3.eth.contract(address, abi); // commented out web3 usage - return {}; -}; - -/// Function called to get all contract method input variables -/// @returns hashmap with all contract's method input variables -var getMethodInputParams = function (method, params) { - return method.inputs.reduce(function (acc, current, index) { - acc[current.name] = params[index]; - return acc; - }, {}); -}; - -/// Should be called to evaluate single expression -/// Is internally using javascript's 'eval' method -/// Should be checked if it is safe -var evaluateExpression = function (expression) { - - var self = this; - - //var storage = getContractProperties(address, abi); - //var methods = getContractMethods(address, abi); - - var method = getMethodWithName(globals.abi, globals.method); - if (method) { - var input = getMethodInputParams(method, globals.params); - copyToContext(input, self); - } - - // TODO: test if it is safe - var evaluatedExpression = ""; - - // match everything in `` quotes - var pattern = /\`(?:\\.|[^`\\])*\`/gim - var match; - var lastIndex = 0; - while ((match = pattern.exec(expression)) !== null) { - var startIndex = pattern.lastIndex - match[0].length; - - var toEval = match[0].slice(1, match[0].length - 1); - - evaluatedExpression += expression.slice(lastIndex, startIndex); - evaluatedExpression += eval(toEval).toString(); - - lastIndex = pattern.lastIndex; - } - - evaluatedExpression += expression.slice(lastIndex); - - return evaluatedExpression; -}; - diff --git a/libnatspec/natspec.qrc b/libnatspec/natspec.qrc index db125974c..377b8d33e 100644 --- a/libnatspec/natspec.qrc +++ b/libnatspec/natspec.qrc @@ -1,5 +1,5 @@ - natspec.js + natspecjs/dist/natspec.min.js diff --git a/libnatspec/natspecjs/.gitignore b/libnatspec/natspecjs/.gitignore new file mode 100644 index 000000000..95b26b70d --- /dev/null +++ b/libnatspec/natspecjs/.gitignore @@ -0,0 +1,3 @@ +# VIM stuff +*.swp +node_modules/ diff --git a/libnatspec/natspecjs/.travis.yml b/libnatspec/natspecjs/.travis.yml new file mode 100644 index 000000000..dca4078ce --- /dev/null +++ b/libnatspec/natspecjs/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: + - "0.11" + - "0.10" +before_script: + - npm install +after_script: + - npm run-script test-coveralls diff --git a/libnatspec/natspecjs/README.md b/libnatspec/natspecjs/README.md new file mode 100644 index 000000000..63f08e47a --- /dev/null +++ b/libnatspec/natspecjs/README.md @@ -0,0 +1,47 @@ +# natspec.js +Javascript Library used to evaluate natspec expressions + +[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] + +[travis-image]: https://travis-ci.org/ethereum/natspec.js.svg +[travis-url]: https://travis-ci.org/ethereum/natspec.js +[coveralls-image]: https://coveralls.io/repos/ethereum/natspec.js/badge.svg?branch=master +[coveralls-url]: https://coveralls.io/r/ethereum/natspec.js?branch=master + +## Usage + +It exposes global object `natspec` with method `evaluateExpression`. + +```javascript +var natspec = require('natspec'); + +var natspecExpression = "Will multiply `a` by 7 and return `a * 7`."; +var call = { + method: 'multiply', + abi: abi, + transaction: transaction +}; + +var evaluatedExpression = natspec.evaluateExpression(natspecExpression, call); +console.log(evaluatedExpression); // "Will multiply 4 by 7 and return 28." +``` + +More examples are available [here](https://github.com/ethereum/natspec.js/blob/master/test/test.js). + +## Building + +```bash +npm run-script build +``` + +## Testing (mocha) + +```bash +npm test +``` + +## Wiki + +* [Ethereum Natural Specification Format](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format) +* [Natspec Example](https://github.com/ethereum/wiki/wiki/Natspec-Example) + diff --git a/libnatspec/natspecjs/dist/natspec.js b/libnatspec/natspecjs/dist/natspec.js new file mode 100644 index 000000000..a2256414b --- /dev/null +++ b/libnatspec/natspecjs/dist/natspec.js @@ -0,0 +1,3570 @@ +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. +*/ +/** @file abi.js + * @authors: + * Marek Kotewicz + * Gav Wood + * @date 2014 + */ + +var utils = require('./utils'); +var types = require('./types'); +var c = require('./const'); +var f = require('./formatters'); + +var displayTypeError = function (type) { + console.error('parser does not support type: ' + type); +}; + +/// This method should be called if we want to check if givent type is an array type +/// @returns true if it is, otherwise false +var arrayType = function (type) { + return type.slice(-2) === '[]'; +}; + +var dynamicTypeBytes = function (type, value) { + // TODO: decide what to do with array of strings + if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + return f.formatInputInt(value.length); + return ""; +}; + +var inputTypes = types.inputTypes(); + +/// Formats input params to bytes +/// @param abi contract method inputs +/// @param array of params that will be formatted to bytes +/// @returns bytes representation of input params +var formatInput = function (inputs, params) { + var bytes = ""; + var toAppendConstant = ""; + var toAppendArrayContent = ""; + + /// first we iterate in search for dynamic + inputs.forEach(function (input, index) { + bytes += dynamicTypeBytes(input.type, params[index]); + }); + + inputs.forEach(function (input, i) { + /*jshint maxcomplexity:5 */ + var typeMatch = false; + for (var j = 0; j < inputTypes.length && !typeMatch; j++) { + typeMatch = inputTypes[j].type(inputs[i].type, params[i]); + } + if (!typeMatch) { + displayTypeError(inputs[i].type); + } + + var formatter = inputTypes[j - 1].format; + + if (arrayType(inputs[i].type)) + toAppendArrayContent += params[i].reduce(function (acc, curr) { + return acc + formatter(curr); + }, ""); + else if (inputs[i].type === 'string') + toAppendArrayContent += formatter(params[i]); + else + toAppendConstant += formatter(params[i]); + }); + + bytes += toAppendConstant + toAppendArrayContent; + + return bytes; +}; + +var dynamicBytesLength = function (type) { + if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + return c.ETH_PADDING * 2; + return 0; +}; + +var outputTypes = types.outputTypes(); + +/// Formats output bytes back to param list +/// @param contract abi method outputs +/// @param bytes representtion of output +/// @returns array of output params +var formatOutput = function (outs, output) { + + output = output.slice(2); + var result = []; + var padding = c.ETH_PADDING * 2; + + var dynamicPartLength = outs.reduce(function (acc, curr) { + return acc + dynamicBytesLength(curr.type); + }, 0); + + var dynamicPart = output.slice(0, dynamicPartLength); + output = output.slice(dynamicPartLength); + + outs.forEach(function (out, i) { + /*jshint maxcomplexity:6 */ + var typeMatch = false; + for (var j = 0; j < outputTypes.length && !typeMatch; j++) { + typeMatch = outputTypes[j].type(outs[i].type); + } + + if (!typeMatch) { + displayTypeError(outs[i].type); + } + + var formatter = outputTypes[j - 1].format; + if (arrayType(outs[i].type)) { + var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); + dynamicPart = dynamicPart.slice(padding); + var array = []; + for (var k = 0; k < size; k++) { + array.push(formatter(output.slice(0, padding))); + output = output.slice(padding); + } + result.push(array); + } + else if (types.prefixedType('string')(outs[i].type)) { + dynamicPart = dynamicPart.slice(padding); + result.push(formatter(output.slice(0, padding))); + output = output.slice(padding); + } else { + result.push(formatter(output.slice(0, padding))); + output = output.slice(padding); + } + }); + + return result; +}; + +/// @param json abi for contract +/// @returns input parser object for given json abi +/// TODO: refactor creating the parser, do not double logic from contract +var inputParser = function (json) { + var parser = {}; + json.forEach(function (method) { + var displayName = utils.extractDisplayName(method.name); + var typeName = utils.extractTypeName(method.name); + + var impl = function () { + var params = Array.prototype.slice.call(arguments); + return formatInput(method.inputs, params); + }; + + if (parser[displayName] === undefined) { + parser[displayName] = impl; + } + + parser[displayName][typeName] = impl; + }); + + return parser; +}; + +/// @param json abi for contract +/// @returns output parser for given json abi +var outputParser = function (json) { + var parser = {}; + json.forEach(function (method) { + + var displayName = utils.extractDisplayName(method.name); + var typeName = utils.extractTypeName(method.name); + + var impl = function (output) { + return formatOutput(method.outputs, output); + }; + + if (parser[displayName] === undefined) { + parser[displayName] = impl; + } + + parser[displayName][typeName] = impl; + }); + + return parser; +}; + +module.exports = { + inputParser: inputParser, + outputParser: outputParser, + formatInput: formatInput, + formatOutput: formatOutput +}; + +},{"./const":4,"./formatters":5,"./types":6,"./utils":7}],4:[function(require,module,exports){ +(function (process){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file const.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/// required to define ETH_BIGNUMBER_ROUNDING_MODE +if (process.env.NODE_ENV !== 'build') { + var BigNumber = require('bignumber.js'); // jshint ignore:line +} + +var ETH_UNITS = [ + 'wei', + 'Kwei', + 'Mwei', + 'Gwei', + 'szabo', + 'finney', + 'ether', + 'grand', + 'Mether', + 'Gether', + 'Tether', + 'Pether', + 'Eether', + 'Zether', + 'Yether', + 'Nether', + 'Dether', + 'Vether', + 'Uether' +]; + +module.exports = { + ETH_PADDING: 32, + ETH_SIGNATURE_LENGTH: 4, + ETH_UNITS: ETH_UNITS, + ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, + ETH_POLLING_TIMEOUT: 1000 +}; + + +}).call(this,require('_process')) +},{"_process":2,"bignumber.js":8}],5:[function(require,module,exports){ +(function (process){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file formatters.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +if (process.env.NODE_ENV !== 'build') { + var BigNumber = require('bignumber.js'); // jshint ignore:line +} + +var utils = require('./utils'); +var c = require('./const'); + +/// @param string string to be padded +/// @param number of characters that result string should have +/// @param sign, by default 0 +/// @returns right aligned string +var padLeft = function (string, chars, sign) { + return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; +}; + +/// Formats input value to byte representation of int +/// If value is negative, return it's two's complement +/// If the value is floating point, round it down +/// @returns right-aligned byte representation of int +var formatInputInt = function (value) { + /*jshint maxcomplexity:7 */ + var padding = c.ETH_PADDING * 2; + if (value instanceof BigNumber || typeof value === 'number') { + if (typeof value === 'number') + value = new BigNumber(value); + BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); + value = value.round(); + + if (value.lessThan(0)) + value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1); + value = value.toString(16); + } + else if (value.indexOf('0x') === 0) + value = value.substr(2); + else if (typeof value === 'string') + value = formatInputInt(new BigNumber(value)); + else + value = (+value).toString(16); + return padLeft(value, padding); +}; + +/// Formats input value to byte representation of string +/// @returns left-algined byte representation of string +var formatInputString = function (value) { + return utils.fromAscii(value, c.ETH_PADDING).substr(2); +}; + +/// Formats input value to byte representation of bool +/// @returns right-aligned byte representation bool +var formatInputBool = function (value) { + return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); +}; + +/// Formats input value to byte representation of real +/// Values are multiplied by 2^m and encoded as integers +/// @returns byte representation of real +var formatInputReal = function (value) { + return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); +}; + + +/// Check if input value is negative +/// @param value is hex format +/// @returns true if it is negative, otherwise false +var signedIsNegative = function (value) { + return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; +}; + +/// Formats input right-aligned input bytes to int +/// @returns right-aligned input bytes formatted to int +var formatOutputInt = function (value) { + value = value || "0"; + // check if it's negative number + // it it is, return two's complement + if (signedIsNegative(value)) { + return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); + } + return new BigNumber(value, 16); +}; + +/// Formats big right-aligned input bytes to uint +/// @returns right-aligned input bytes formatted to uint +var formatOutputUInt = function (value) { + value = value || "0"; + return new BigNumber(value, 16); +}; + +/// @returns input bytes formatted to real +var formatOutputReal = function (value) { + return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/// @returns input bytes formatted to ureal +var formatOutputUReal = function (value) { + return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/// @returns right-aligned input bytes formatted to hex +var formatOutputHash = function (value) { + return "0x" + value; +}; + +/// @returns right-aligned input bytes formatted to bool +var formatOutputBool = function (value) { + return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; +}; + +/// @returns left-aligned input bytes formatted to ascii string +var formatOutputString = function (value) { + return utils.toAscii(value); +}; + +/// @returns right-aligned input bytes formatted to address +var formatOutputAddress = function (value) { + return "0x" + value.slice(value.length - 40, value.length); +}; + + +module.exports = { + formatInputInt: formatInputInt, + formatInputString: formatInputString, + formatInputBool: formatInputBool, + formatInputReal: formatInputReal, + formatOutputInt: formatOutputInt, + formatOutputUInt: formatOutputUInt, + formatOutputReal: formatOutputReal, + formatOutputUReal: formatOutputUReal, + formatOutputHash: formatOutputHash, + formatOutputBool: formatOutputBool, + formatOutputString: formatOutputString, + formatOutputAddress: formatOutputAddress +}; + + +}).call(this,require('_process')) +},{"./const":4,"./utils":7,"_process":2,"bignumber.js":8}],6:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file types.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var f = require('./formatters'); + +/// @param expected type prefix (string) +/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false +var prefixedType = function (prefix) { + return function (type) { + return type.indexOf(prefix) === 0; + }; +}; + +/// @param expected type name (string) +/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false +var namedType = function (name) { + return function (type) { + return name === type; + }; +}; + +/// Setups input formatters for solidity types +/// @returns an array of input formatters +var inputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatInputInt }, + { type: prefixedType('int'), format: f.formatInputInt }, + { type: prefixedType('hash'), format: f.formatInputInt }, + { type: prefixedType('string'), format: f.formatInputString }, + { type: prefixedType('real'), format: f.formatInputReal }, + { type: prefixedType('ureal'), format: f.formatInputReal }, + { type: namedType('address'), format: f.formatInputInt }, + { type: namedType('bool'), format: f.formatInputBool } + ]; +}; + +/// Setups output formaters for solidity types +/// @returns an array of output formatters +var outputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatOutputUInt }, + { type: prefixedType('int'), format: f.formatOutputInt }, + { type: prefixedType('hash'), format: f.formatOutputHash }, + { type: prefixedType('string'), format: f.formatOutputString }, + { type: prefixedType('real'), format: f.formatOutputReal }, + { type: prefixedType('ureal'), format: f.formatOutputUReal }, + { type: namedType('address'), format: f.formatOutputAddress }, + { type: namedType('bool'), format: f.formatOutputBool } + ]; +}; + +module.exports = { + prefixedType: prefixedType, + namedType: namedType, + inputTypes: inputTypes, + outputTypes: outputTypes +}; + + +},{"./formatters":5}],7:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file utils.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var c = require('./const'); + +/// Finds first index of array element matching pattern +/// @param array +/// @param callback pattern +/// @returns index of element +var findIndex = function (array, callback) { + var end = false; + var i = 0; + for (; i < array.length && !end; i++) { + end = callback(array[i]); + } + return end ? i - 1 : -1; +}; + +/// @returns ascii string representation of hex value prefixed with 0x +var toAscii = function(hex) { +// Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') { + i = 2; + } + for (; i < l; i+=2) { + var code = parseInt(hex.substr(i, 2), 16); + if (code === 0) { + break; + } + + str += String.fromCharCode(code); + } + + return str; +}; + +var toHex = function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return hex; +}; + +/// @returns hex representation (prefixed by 0x) of ascii string +var fromAscii = function(str, pad) { + pad = pad === undefined ? 0 : pad; + var hex = toHex(str); + while (hex.length < pad*2) + hex += "00"; + return "0x" + hex; +}; + +/// @returns display name for function/event eg. multiply(uint256) -> multiply +var extractDisplayName = function (name) { + var length = name.indexOf('('); + return length !== -1 ? name.substr(0, length) : name; +}; + +/// @returns overloaded part of function/event name +var extractTypeName = function (name) { + /// TODO: make it invulnerable + var length = name.indexOf('('); + return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; +}; + +/// Filters all function from input abi +/// @returns abi array with filtered objects of type 'function' +var filterFunctions = function (json) { + return json.filter(function (current) { + return current.type === 'function'; + }); +}; + +/// Filters all events form input abi +/// @returns abi array with filtered objects of type 'event' +var filterEvents = function (json) { + return json.filter(function (current) { + return current.type === 'event'; + }); +}; + +/// used to transform value/string to eth string +/// TODO: use BigNumber.js to parse int +/// TODO: add tests for it! +var toEth = function (str) { + /*jshint maxcomplexity:7 */ + var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; + var unit = 0; + var units = c.ETH_UNITS; + while (val > 3000 && unit < units.length - 1) + { + val /= 1000; + unit++; + } + var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); + var replaceFunction = function($0, $1, $2) { + return $1 + ',' + $2; + }; + + while (true) { + var o = s; + s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); + if (o === s) + break; + } + return s + ' ' + units[unit]; +}; + +module.exports = { + findIndex: findIndex, + toAscii: toAscii, + fromAscii: fromAscii, + extractDisplayName: extractDisplayName, + extractTypeName: extractTypeName, + filterFunctions: filterFunctions, + filterEvents: filterEvents, + toEth: toEth +}; + + +},{"./const":4}],8:[function(require,module,exports){ +/*! bignumber.js v2.0.3 https://github.com/MikeMcl/bignumber.js/LICENCE */ + +;(function (global) { + 'use strict'; + + /* + bignumber.js v2.0.3 + A JavaScript library for arbitrary-precision arithmetic. + https://github.com/MikeMcl/bignumber.js + Copyright (c) 2015 Michael Mclaughlin + MIT Expat Licence + */ + + + var BigNumber, crypto, parseNumeric, + isNumeric = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, + mathceil = Math.ceil, + mathfloor = Math.floor, + notBool = ' not a boolean or binary digit', + roundingMode = 'rounding mode', + tooManyDigits = 'number type has more than 15 significant digits', + ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_', + BASE = 1e14, + LOG_BASE = 14, + MAX_SAFE_INTEGER = 0x1fffffffffffff, // 2^53 - 1 + // MAX_INT32 = 0x7fffffff, // 2^31 - 1 + POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], + SQRT_BASE = 1e7, + + /* + * The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and + * the arguments to toExponential, toFixed, toFormat, and toPrecision, beyond which an + * exception is thrown (if ERRORS is true). + */ + MAX = 1E9; // 0 to MAX_INT32 + + + /* + * Create and return a BigNumber constructor. + */ + function another(configObj) { + var div, + + // id tracks the caller function, so its name can be included in error messages. + id = 0, + P = BigNumber.prototype, + ONE = new BigNumber(1), + + + /********************************* EDITABLE DEFAULTS **********************************/ + + + /* + * The default values below must be integers within the inclusive ranges stated. + * The values can also be changed at run-time using BigNumber.config. + */ + + // The maximum number of decimal places for operations involving division. + DECIMAL_PLACES = 20, // 0 to MAX + + /* + * The rounding mode used when rounding to the above decimal places, and when using + * toExponential, toFixed, toFormat and toPrecision, and round (default value). + * UP 0 Away from zero. + * DOWN 1 Towards zero. + * CEIL 2 Towards +Infinity. + * FLOOR 3 Towards -Infinity. + * HALF_UP 4 Towards nearest neighbour. If equidistant, up. + * HALF_DOWN 5 Towards nearest neighbour. If equidistant, down. + * HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour. + * HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity. + * HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity. + */ + ROUNDING_MODE = 4, // 0 to 8 + + // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS] + + // The exponent value at and beneath which toString returns exponential notation. + // Number type: -7 + TO_EXP_NEG = -7, // 0 to -MAX + + // The exponent value at and above which toString returns exponential notation. + // Number type: 21 + TO_EXP_POS = 21, // 0 to MAX + + // RANGE : [MIN_EXP, MAX_EXP] + + // The minimum exponent value, beneath which underflow to zero occurs. + // Number type: -324 (5e-324) + MIN_EXP = -1e7, // -1 to -MAX + + // The maximum exponent value, above which overflow to Infinity occurs. + // Number type: 308 (1.7976931348623157e+308) + // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow. + MAX_EXP = 1e7, // 1 to MAX + + // Whether BigNumber Errors are ever thrown. + ERRORS = true, // true or false + + // Change to intValidatorNoErrors if ERRORS is false. + isValidInt = intValidatorWithErrors, // intValidatorWithErrors/intValidatorNoErrors + + // Whether to use cryptographically-secure random number generation, if available. + CRYPTO = false, // true or false + + /* + * The modulo mode used when calculating the modulus: a mod n. + * The quotient (q = a / n) is calculated according to the corresponding rounding mode. + * The remainder (r) is calculated as: r = a - n * q. + * + * UP 0 The remainder is positive if the dividend is negative, else is negative. + * DOWN 1 The remainder has the same sign as the dividend. + * This modulo mode is commonly known as 'truncated division' and is + * equivalent to (a % n) in JavaScript. + * FLOOR 3 The remainder has the same sign as the divisor (Python %). + * HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function. + * EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). + * The remainder is always positive. + * + * The truncated division, floored division, Euclidian division and IEEE 754 remainder + * modes are commonly used for the modulus operation. + * Although the other rounding modes can also be used, they may not give useful results. + */ + MODULO_MODE = 1, // 0 to 9 + + // The maximum number of significant digits of the result of the toPower operation. + // If POW_PRECISION is 0, there will be unlimited significant digits. + POW_PRECISION = 100, // 0 to MAX + + // The format specification used by the BigNumber.prototype.toFormat method. + FORMAT = { + decimalSeparator: '.', + groupSeparator: ',', + groupSize: 3, + secondaryGroupSize: 0, + fractionGroupSeparator: '\xA0', // non-breaking space + fractionGroupSize: 0 + }; + + + /******************************************************************************************/ + + + // CONSTRUCTOR + + + /* + * The BigNumber constructor and exported function. + * Create and return a new instance of a BigNumber object. + * + * n {number|string|BigNumber} A numeric value. + * [b] {number} The base of n. Integer, 2 to 64 inclusive. + */ + function BigNumber( n, b ) { + var c, e, i, num, len, str, + x = this; + + // Enable constructor usage without new. + if ( !( x instanceof BigNumber ) ) { + + // 'BigNumber() constructor call without new: {n}' + if (ERRORS) raise( 26, 'constructor call without new', n ); + return new BigNumber( n, b ); + } + + // 'new BigNumber() base not an integer: {b}' + // 'new BigNumber() base out of range: {b}' + if ( b == null || !isValidInt( b, 2, 64, id, 'base' ) ) { + + // Duplicate. + if ( n instanceof BigNumber ) { + x.s = n.s; + x.e = n.e; + x.c = ( n = n.c ) ? n.slice() : n; + id = 0; + return; + } + + if ( ( num = typeof n == 'number' ) && n * 0 == 0 ) { + x.s = 1 / n < 0 ? ( n = -n, -1 ) : 1; + + // Fast path for integers. + if ( n === ~~n ) { + for ( e = 0, i = n; i >= 10; i /= 10, e++ ); + x.e = e; + x.c = [n]; + id = 0; + return; + } + + str = n + ''; + } else { + if ( !isNumeric.test( str = n + '' ) ) return parseNumeric( x, str, num ); + x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; + } + } else { + b = b | 0; + str = n + ''; + + // Ensure return value is rounded to DECIMAL_PLACES as with other bases. + // Allow exponential notation to be used with base 10 argument. + if ( b == 10 ) { + x = new BigNumber( n instanceof BigNumber ? n : str ); + return round( x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE ); + } + + // Avoid potential interpretation of Infinity and NaN as base 44+ values. + // Any number in exponential form will fail due to the [Ee][+-]. + if ( ( num = typeof n == 'number' ) && n * 0 != 0 || + !( new RegExp( '^-?' + ( c = '[' + ALPHABET.slice( 0, b ) + ']+' ) + + '(?:\\.' + c + ')?$',b < 37 ? 'i' : '' ) ).test(str) ) { + return parseNumeric( x, str, num, b ); + } + + if (num) { + x.s = 1 / n < 0 ? ( str = str.slice(1), -1 ) : 1; + + if ( ERRORS && str.replace( /^0\.0*|\./, '' ).length > 15 ) { + + // 'new BigNumber() number type has more than 15 significant digits: {n}' + raise( id, tooManyDigits, n ); + } + + // Prevent later check for length on converted number. + num = false; + } else { + x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; + } + + str = convertBase( str, 10, b, x.s ); + } + + // Decimal point? + if ( ( e = str.indexOf('.') ) > -1 ) str = str.replace( '.', '' ); + + // Exponential form? + if ( ( i = str.search( /e/i ) ) > 0 ) { + + // Determine exponent. + if ( e < 0 ) e = i; + e += +str.slice( i + 1 ); + str = str.substring( 0, i ); + } else if ( e < 0 ) { + + // Integer. + e = str.length; + } + + // Determine leading zeros. + for ( i = 0; str.charCodeAt(i) === 48; i++ ); + + // Determine trailing zeros. + for ( len = str.length; str.charCodeAt(--len) === 48; ); + str = str.slice( i, len + 1 ); + + if (str) { + len = str.length; + + // Disallow numbers with over 15 significant digits if number type. + // 'new BigNumber() number type has more than 15 significant digits: {n}' + if ( num && ERRORS && len > 15 ) raise( id, tooManyDigits, x.s * n ); + + e = e - i - 1; + + // Overflow? + if ( e > MAX_EXP ) { + + // Infinity. + x.c = x.e = null; + + // Underflow? + } else if ( e < MIN_EXP ) { + + // Zero. + x.c = [ x.e = 0 ]; + } else { + x.e = e; + x.c = []; + + // Transform base + + // e is the base 10 exponent. + // i is where to slice str to get the first element of the coefficient array. + i = ( e + 1 ) % LOG_BASE; + if ( e < 0 ) i += LOG_BASE; + + if ( i < len ) { + if (i) x.c.push( +str.slice( 0, i ) ); + + for ( len -= LOG_BASE; i < len; ) { + x.c.push( +str.slice( i, i += LOG_BASE ) ); + } + + str = str.slice(i); + i = LOG_BASE - str.length; + } else { + i -= len; + } + + for ( ; i--; str += '0' ); + x.c.push( +str ); + } + } else { + + // Zero. + x.c = [ x.e = 0 ]; + } + + id = 0; + } + + + // CONSTRUCTOR PROPERTIES + + + BigNumber.another = another; + + BigNumber.ROUND_UP = 0; + BigNumber.ROUND_DOWN = 1; + BigNumber.ROUND_CEIL = 2; + BigNumber.ROUND_FLOOR = 3; + BigNumber.ROUND_HALF_UP = 4; + BigNumber.ROUND_HALF_DOWN = 5; + BigNumber.ROUND_HALF_EVEN = 6; + BigNumber.ROUND_HALF_CEIL = 7; + BigNumber.ROUND_HALF_FLOOR = 8; + BigNumber.EUCLID = 9; + + + /* + * Configure infrequently-changing library-wide settings. + * + * Accept an object or an argument list, with one or many of the following properties or + * parameters respectively: + * + * DECIMAL_PLACES {number} Integer, 0 to MAX inclusive + * ROUNDING_MODE {number} Integer, 0 to 8 inclusive + * EXPONENTIAL_AT {number|number[]} Integer, -MAX to MAX inclusive or + * [integer -MAX to 0 incl., 0 to MAX incl.] + * RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + * [integer -MAX to -1 incl., integer 1 to MAX incl.] + * ERRORS {boolean|number} true, false, 1 or 0 + * CRYPTO {boolean|number} true, false, 1 or 0 + * MODULO_MODE {number} 0 to 9 inclusive + * POW_PRECISION {number} 0 to MAX inclusive + * FORMAT {object} See BigNumber.prototype.toFormat + * decimalSeparator {string} + * groupSeparator {string} + * groupSize {number} + * secondaryGroupSize {number} + * fractionGroupSeparator {string} + * fractionGroupSize {number} + * + * (The values assigned to the above FORMAT object properties are not checked for validity.) + * + * E.g. + * BigNumber.config(20, 4) is equivalent to + * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 }) + * + * Ignore properties/parameters set to null or undefined. + * Return an object with the properties current values. + */ + BigNumber.config = function () { + var v, p, + i = 0, + r = {}, + a = arguments, + o = a[0], + has = o && typeof o == 'object' + ? function () { if ( o.hasOwnProperty(p) ) return ( v = o[p] ) != null; } + : function () { if ( a.length > i ) return ( v = a[i++] ) != null; }; + + // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive. + // 'config() DECIMAL_PLACES not an integer: {v}' + // 'config() DECIMAL_PLACES out of range: {v}' + if ( has( p = 'DECIMAL_PLACES' ) && isValidInt( v, 0, MAX, 2, p ) ) { + DECIMAL_PLACES = v | 0; + } + r[p] = DECIMAL_PLACES; + + // ROUNDING_MODE {number} Integer, 0 to 8 inclusive. + // 'config() ROUNDING_MODE not an integer: {v}' + // 'config() ROUNDING_MODE out of range: {v}' + if ( has( p = 'ROUNDING_MODE' ) && isValidInt( v, 0, 8, 2, p ) ) { + ROUNDING_MODE = v | 0; + } + r[p] = ROUNDING_MODE; + + // EXPONENTIAL_AT {number|number[]} + // Integer, -MAX to MAX inclusive or [integer -MAX to 0 inclusive, 0 to MAX inclusive]. + // 'config() EXPONENTIAL_AT not an integer: {v}' + // 'config() EXPONENTIAL_AT out of range: {v}' + if ( has( p = 'EXPONENTIAL_AT' ) ) { + + if ( isArray(v) ) { + if ( isValidInt( v[0], -MAX, 0, 2, p ) && isValidInt( v[1], 0, MAX, 2, p ) ) { + TO_EXP_NEG = v[0] | 0; + TO_EXP_POS = v[1] | 0; + } + } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { + TO_EXP_NEG = -( TO_EXP_POS = ( v < 0 ? -v : v ) | 0 ); + } + } + r[p] = [ TO_EXP_NEG, TO_EXP_POS ]; + + // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive]. + // 'config() RANGE not an integer: {v}' + // 'config() RANGE cannot be zero: {v}' + // 'config() RANGE out of range: {v}' + if ( has( p = 'RANGE' ) ) { + + if ( isArray(v) ) { + if ( isValidInt( v[0], -MAX, -1, 2, p ) && isValidInt( v[1], 1, MAX, 2, p ) ) { + MIN_EXP = v[0] | 0; + MAX_EXP = v[1] | 0; + } + } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { + if ( v | 0 ) MIN_EXP = -( MAX_EXP = ( v < 0 ? -v : v ) | 0 ); + else if (ERRORS) raise( 2, p + ' cannot be zero', v ); + } + } + r[p] = [ MIN_EXP, MAX_EXP ]; + + // ERRORS {boolean|number} true, false, 1 or 0. + // 'config() ERRORS not a boolean or binary digit: {v}' + if ( has( p = 'ERRORS' ) ) { + + if ( v === !!v || v === 1 || v === 0 ) { + id = 0; + isValidInt = ( ERRORS = !!v ) ? intValidatorWithErrors : intValidatorNoErrors; + } else if (ERRORS) { + raise( 2, p + notBool, v ); + } + } + r[p] = ERRORS; + + // CRYPTO {boolean|number} true, false, 1 or 0. + // 'config() CRYPTO not a boolean or binary digit: {v}' + // 'config() crypto unavailable: {crypto}' + if ( has( p = 'CRYPTO' ) ) { + + if ( v === !!v || v === 1 || v === 0 ) { + CRYPTO = !!( v && crypto && typeof crypto == 'object' ); + if ( v && !CRYPTO && ERRORS ) raise( 2, 'crypto unavailable', crypto ); + } else if (ERRORS) { + raise( 2, p + notBool, v ); + } + } + r[p] = CRYPTO; + + // MODULO_MODE {number} Integer, 0 to 9 inclusive. + // 'config() MODULO_MODE not an integer: {v}' + // 'config() MODULO_MODE out of range: {v}' + if ( has( p = 'MODULO_MODE' ) && isValidInt( v, 0, 9, 2, p ) ) { + MODULO_MODE = v | 0; + } + r[p] = MODULO_MODE; + + // POW_PRECISION {number} Integer, 0 to MAX inclusive. + // 'config() POW_PRECISION not an integer: {v}' + // 'config() POW_PRECISION out of range: {v}' + if ( has( p = 'POW_PRECISION' ) && isValidInt( v, 0, MAX, 2, p ) ) { + POW_PRECISION = v | 0; + } + r[p] = POW_PRECISION; + + // FORMAT {object} + // 'config() FORMAT not an object: {v}' + if ( has( p = 'FORMAT' ) ) { + + if ( typeof v == 'object' ) { + FORMAT = v; + } else if (ERRORS) { + raise( 2, p + ' not an object', v ); + } + } + r[p] = FORMAT; + + return r; + }; + + + /* + * Return a new BigNumber whose value is the maximum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.max = function () { return maxOrMin( arguments, P.lt ); }; + + + /* + * Return a new BigNumber whose value is the minimum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.min = function () { return maxOrMin( arguments, P.gt ); }; + + + /* + * Return a new BigNumber with a random value equal to or greater than 0 and less than 1, + * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing + * zeros are produced). + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * + * 'random() decimal places not an integer: {dp}' + * 'random() decimal places out of range: {dp}' + * 'random() crypto unavailable: {crypto}' + */ + BigNumber.random = (function () { + var pow2_53 = 0x20000000000000; + + // Return a 53 bit integer n, where 0 <= n < 9007199254740992. + // Check if Math.random() produces more than 32 bits of randomness. + // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits. + // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1. + var random53bitInt = (Math.random() * pow2_53) & 0x1fffff + ? function () { return mathfloor( Math.random() * pow2_53 ); } + : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) + + (Math.random() * 0x800000 | 0); }; + + return function (dp) { + var a, b, e, k, v, + i = 0, + c = [], + rand = new BigNumber(ONE); + + dp = dp == null || !isValidInt( dp, 0, MAX, 14 ) ? DECIMAL_PLACES : dp | 0; + k = mathceil( dp / LOG_BASE ); + + if (CRYPTO) { + + // Browsers supporting crypto.getRandomValues. + if ( crypto && crypto.getRandomValues ) { + + a = crypto.getRandomValues( new Uint32Array( k *= 2 ) ); + + for ( ; i < k; ) { + + // 53 bits: + // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2) + // 11111 11111111 11111111 11111111 11100000 00000000 00000000 + // ((Math.pow(2, 32) - 1) >>> 11).toString(2) + // 11111 11111111 11111111 + // 0x20000 is 2^21. + v = a[i] * 0x20000 + (a[i + 1] >>> 11); + + // Rejection sampling: + // 0 <= v < 9007199254740992 + // Probability that v >= 9e15, is + // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251 + if ( v >= 9e15 ) { + b = crypto.getRandomValues( new Uint32Array(2) ); + a[i] = b[0]; + a[i + 1] = b[1]; + } else { + + // 0 <= v <= 8999999999999999 + // 0 <= (v % 1e14) <= 99999999999999 + c.push( v % 1e14 ); + i += 2; + } + } + i = k / 2; + + // Node.js supporting crypto.randomBytes. + } else if ( crypto && crypto.randomBytes ) { + + // buffer + a = crypto.randomBytes( k *= 7 ); + + for ( ; i < k; ) { + + // 0x1000000000000 is 2^48, 0x10000000000 is 2^40 + // 0x100000000 is 2^32, 0x1000000 is 2^24 + // 11111 11111111 11111111 11111111 11111111 11111111 11111111 + // 0 <= v < 9007199254740992 + v = ( ( a[i] & 31 ) * 0x1000000000000 ) + ( a[i + 1] * 0x10000000000 ) + + ( a[i + 2] * 0x100000000 ) + ( a[i + 3] * 0x1000000 ) + + ( a[i + 4] << 16 ) + ( a[i + 5] << 8 ) + a[i + 6]; + + if ( v >= 9e15 ) { + crypto.randomBytes(7).copy( a, i ); + } else { + + // 0 <= (v % 1e14) <= 99999999999999 + c.push( v % 1e14 ); + i += 7; + } + } + i = k / 7; + } else if (ERRORS) { + raise( 14, 'crypto unavailable', crypto ); + } + } + + // Use Math.random: CRYPTO is false or crypto is unavailable and ERRORS is false. + if (!i) { + + for ( ; i < k; ) { + v = random53bitInt(); + if ( v < 9e15 ) c[i++] = v % 1e14; + } + } + + k = c[--i]; + dp %= LOG_BASE; + + // Convert trailing digits to zeros according to dp. + if ( k && dp ) { + v = POWS_TEN[LOG_BASE - dp]; + c[i] = mathfloor( k / v ) * v; + } + + // Remove trailing elements which are zero. + for ( ; c[i] === 0; c.pop(), i-- ); + + // Zero? + if ( i < 0 ) { + c = [ e = 0 ]; + } else { + + // Remove leading elements which are zero and adjust exponent accordingly. + for ( e = -1 ; c[0] === 0; c.shift(), e -= LOG_BASE); + + // Count the digits of the first element of c to determine leading zeros, and... + for ( i = 1, v = c[0]; v >= 10; v /= 10, i++); + + // adjust the exponent accordingly. + if ( i < LOG_BASE ) e -= LOG_BASE - i; + } + + rand.e = e; + rand.c = c; + return rand; + }; + })(); + + + // PRIVATE FUNCTIONS + + + // Convert a numeric string of baseIn to a numeric string of baseOut. + function convertBase( str, baseOut, baseIn, sign ) { + var d, e, k, r, x, xc, y, + i = str.indexOf( '.' ), + dp = DECIMAL_PLACES, + rm = ROUNDING_MODE; + + if ( baseIn < 37 ) str = str.toLowerCase(); + + // Non-integer. + if ( i >= 0 ) { + k = POW_PRECISION; + + // Unlimited precision. + POW_PRECISION = 0; + str = str.replace( '.', '' ); + y = new BigNumber(baseIn); + x = y.pow( str.length - i ); + POW_PRECISION = k; + + // Convert str as if an integer, then restore the fraction part by dividing the + // result by its base raised to a power. + y.c = toBaseOut( toFixedPoint( coeffToString( x.c ), x.e ), 10, baseOut ); + y.e = y.c.length; + } + + // Convert the number as integer. + xc = toBaseOut( str, baseIn, baseOut ); + e = k = xc.length; + + // Remove trailing zeros. + for ( ; xc[--k] == 0; xc.pop() ); + if ( !xc[0] ) return '0'; + + if ( i < 0 ) { + --e; + } else { + x.c = xc; + x.e = e; + + // sign is needed for correct rounding. + x.s = sign; + x = div( x, y, dp, rm, baseOut ); + xc = x.c; + r = x.r; + e = x.e; + } + + d = e + dp + 1; + + // The rounding digit, i.e. the digit to the right of the digit that may be rounded up. + i = xc[d]; + k = baseOut / 2; + r = r || d < 0 || xc[d + 1] != null; + + r = rm < 4 ? ( i != null || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) + : i > k || i == k &&( rm == 4 || r || rm == 6 && xc[d - 1] & 1 || + rm == ( x.s < 0 ? 8 : 7 ) ); + + if ( d < 1 || !xc[0] ) { + + // 1^-dp or 0. + str = r ? toFixedPoint( '1', -dp ) : '0'; + } else { + xc.length = d; + + if (r) { + + // Rounding up may mean the previous digit has to be rounded up and so on. + for ( --baseOut; ++xc[--d] > baseOut; ) { + xc[d] = 0; + + if ( !d ) { + ++e; + xc.unshift(1); + } + } + } + + // Determine trailing zeros. + for ( k = xc.length; !xc[--k]; ); + + // E.g. [4, 11, 15] becomes 4bf. + for ( i = 0, str = ''; i <= k; str += ALPHABET.charAt( xc[i++] ) ); + str = toFixedPoint( str, e ); + } + + // The caller will add the sign. + return str; + } + + + // Perform division in the specified base. Called by div and convertBase. + div = (function () { + + // Assume non-zero x and k. + function multiply( x, k, base ) { + var m, temp, xlo, xhi, + carry = 0, + i = x.length, + klo = k % SQRT_BASE, + khi = k / SQRT_BASE | 0; + + for ( x = x.slice(); i--; ) { + xlo = x[i] % SQRT_BASE; + xhi = x[i] / SQRT_BASE | 0; + m = khi * xlo + xhi * klo; + temp = klo * xlo + ( ( m % SQRT_BASE ) * SQRT_BASE ) + carry; + carry = ( temp / base | 0 ) + ( m / SQRT_BASE | 0 ) + khi * xhi; + x[i] = temp % base; + } + + if (carry) x.unshift(carry); + + return x; + } + + function compare( a, b, aL, bL ) { + var i, cmp; + + if ( aL != bL ) { + cmp = aL > bL ? 1 : -1; + } else { + + for ( i = cmp = 0; i < aL; i++ ) { + + if ( a[i] != b[i] ) { + cmp = a[i] > b[i] ? 1 : -1; + break; + } + } + } + return cmp; + } + + function subtract( a, b, aL, base ) { + var i = 0; + + // Subtract b from a. + for ( ; aL--; ) { + a[aL] -= i; + i = a[aL] < b[aL] ? 1 : 0; + a[aL] = i * base + a[aL] - b[aL]; + } + + // Remove leading zeros. + for ( ; !a[0] && a.length > 1; a.shift() ); + } + + // x: dividend, y: divisor. + return function ( x, y, dp, rm, base ) { + var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, + yL, yz, + s = x.s == y.s ? 1 : -1, + xc = x.c, + yc = y.c; + + // Either NaN, Infinity or 0? + if ( !xc || !xc[0] || !yc || !yc[0] ) { + + return new BigNumber( + + // Return NaN if either NaN, or both Infinity or 0. + !x.s || !y.s || ( xc ? yc && xc[0] == yc[0] : !yc ) ? NaN : + + // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0. + xc && xc[0] == 0 || !yc ? s * 0 : s / 0 + ); + } + + q = new BigNumber(s); + qc = q.c = []; + e = x.e - y.e; + s = dp + e + 1; + + if ( !base ) { + base = BASE; + e = bitFloor( x.e / LOG_BASE ) - bitFloor( y.e / LOG_BASE ); + s = s / LOG_BASE | 0; + } + + // Result exponent may be one less then the current value of e. + // The coefficients of the BigNumbers from convertBase may have trailing zeros. + for ( i = 0; yc[i] == ( xc[i] || 0 ); i++ ); + if ( yc[i] > ( xc[i] || 0 ) ) e--; + + if ( s < 0 ) { + qc.push(1); + more = true; + } else { + xL = xc.length; + yL = yc.length; + i = 0; + s += 2; + + // Normalise xc and yc so highest order digit of yc is >= base/2 + + n = mathfloor( base / ( yc[0] + 1 ) ); + + if ( n > 1 ) { + yc = multiply( yc, n, base ); + xc = multiply( xc, n, base ); + yL = yc.length; + xL = xc.length; + } + + xi = yL; + rem = xc.slice( 0, yL ); + remL = rem.length; + + // Add zeros to make remainder as long as divisor. + for ( ; remL < yL; rem[remL++] = 0 ); + yz = yc.slice(); + yz.unshift(0); + yc0 = yc[0]; + if ( yc[1] >= base / 2 ) yc0++; + + do { + n = 0; + + // Compare divisor and remainder. + cmp = compare( yc, rem, yL, remL ); + + // If divisor < remainder. + if ( cmp < 0 ) { + + // Calculate trial digit, n. + + rem0 = rem[0]; + if ( yL != remL ) rem0 = rem0 * base + ( rem[1] || 0 ); + + // n is how many times the divisor goes into the current remainder. + n = mathfloor( rem0 / yc0 ); + + // Algorithm: + // 1. product = divisor * trial digit (n) + // 2. if product > remainder: product -= divisor, n-- + // 3. remainder -= product + // 4. if product was < remainder at 2: + // 5. compare new remainder and divisor + // 6. If remainder > divisor: remainder -= divisor, n++ + + if ( n > 1 ) { + if ( n >= base ) n = base - 1; + + // product = divisor * trial digit. + prod = multiply( yc, n, base ); + prodL = prod.length; + remL = rem.length; + + // Compare product and remainder. + cmp = compare( prod, rem, prodL, remL ); + + // product > remainder. + if ( cmp == 1 ) { + n--; + + // Subtract divisor from product. + subtract( prod, yL < prodL ? yz : yc, prodL, base ); + } + } else { + + // cmp is -1. + // If n is 0, there is no need to compare yc and rem again + // below, so change cmp to 1 to avoid it. + // If n is 1, compare yc and rem again below. + if ( n == 0 ) cmp = n = 1; + prod = yc.slice(); + } + + prodL = prod.length; + if ( prodL < remL ) prod.unshift(0); + + // Subtract product from remainder. + subtract( rem, prod, remL, base ); + + // If product was < previous remainder. + if ( cmp == -1 ) { + remL = rem.length; + + // Compare divisor and new remainder. + cmp = compare( yc, rem, yL, remL ); + + // If divisor < new remainder, subtract divisor from remainder. + if ( cmp < 1 ) { + n++; + + // Subtract divisor from remainder. + subtract( rem, yL < remL ? yz : yc, remL, base ); + } + } + remL = rem.length; + } else if ( cmp === 0 ) { + n++; + rem = [0]; + } + // if cmp === 1, n will be 0 + + // Add the next digit, n, to the result array. + qc[i++] = n; + + // Update the remainder. + if ( cmp && rem[0] ) { + rem[remL++] = xc[xi] || 0; + } else { + rem = [ xc[xi] ]; + remL = 1; + } + } while ( ( xi++ < xL || rem[0] != null ) && s-- ); + + more = rem[0] != null; + + // Leading zero? + if ( !qc[0] ) qc.shift(); + } + + if ( base == BASE ) { + + // To calculate q.e, first get the number of digits of qc[0]. + for ( i = 1, s = qc[0]; s >= 10; s /= 10, i++ ); + round( q, dp + ( q.e = i + e * LOG_BASE - 1 ) + 1, rm, more ); + + // Caller is convertBase. + } else { + q.e = e; + q.r = +more; + } + + return q; + }; + })(); + + + /* + * Return a string representing the value of BigNumber n in fixed-point or exponential + * notation rounded to the specified decimal places or significant digits. + * + * n is a BigNumber. + * i is the index of the last digit required (i.e. the digit that may be rounded up). + * rm is the rounding mode. + * caller is caller id: toExponential 19, toFixed 20, toFormat 21, toPrecision 24. + */ + function format( n, i, rm, caller ) { + var c0, e, ne, len, str; + + rm = rm != null && isValidInt( rm, 0, 8, caller, roundingMode ) + ? rm | 0 : ROUNDING_MODE; + + if ( !n.c ) return n.toString(); + c0 = n.c[0]; + ne = n.e; + + if ( i == null ) { + str = coeffToString( n.c ); + str = caller == 19 || caller == 24 && ne <= TO_EXP_NEG + ? toExponential( str, ne ) + : toFixedPoint( str, ne ); + } else { + n = round( new BigNumber(n), i, rm ); + + // n.e may have changed if the value was rounded up. + e = n.e; + + str = coeffToString( n.c ); + len = str.length; + + // toPrecision returns exponential notation if the number of significant digits + // specified is less than the number of digits necessary to represent the integer + // part of the value in fixed-point notation. + + // Exponential notation. + if ( caller == 19 || caller == 24 && ( i <= e || e <= TO_EXP_NEG ) ) { + + // Append zeros? + for ( ; len < i; str += '0', len++ ); + str = toExponential( str, e ); + + // Fixed-point notation. + } else { + i -= ne; + str = toFixedPoint( str, e ); + + // Append zeros? + if ( e + 1 > len ) { + if ( --i > 0 ) for ( str += '.'; i--; str += '0' ); + } else { + i += e - len; + if ( i > 0 ) { + if ( e + 1 == len ) str += '.'; + for ( ; i--; str += '0' ); + } + } + } + } + + return n.s < 0 && c0 ? '-' + str : str; + } + + + // Handle BigNumber.max and BigNumber.min. + function maxOrMin( args, method ) { + var m, n, + i = 0; + + if ( isArray( args[0] ) ) args = args[0]; + m = new BigNumber( args[0] ); + + for ( ; ++i < args.length; ) { + n = new BigNumber( args[i] ); + + // If any number is NaN, return NaN. + if ( !n.s ) { + m = n; + break; + } else if ( method.call( m, n ) ) { + m = n; + } + } + + return m; + } + + + /* + * Return true if n is an integer in range, otherwise throw. + * Use for argument validation when ERRORS is true. + */ + function intValidatorWithErrors( n, min, max, caller, name ) { + if ( n < min || n > max || n != truncate(n) ) { + raise( caller, ( name || 'decimal places' ) + + ( n < min || n > max ? ' out of range' : ' not an integer' ), n ); + } + + return true; + } + + + /* + * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP. + * Called by minus, plus and times. + */ + function normalise( n, c, e ) { + var i = 1, + j = c.length; + + // Remove trailing zeros. + for ( ; !c[--j]; c.pop() ); + + // Calculate the base 10 exponent. First get the number of digits of c[0]. + for ( j = c[0]; j >= 10; j /= 10, i++ ); + + // Overflow? + if ( ( e = i + e * LOG_BASE - 1 ) > MAX_EXP ) { + + // Infinity. + n.c = n.e = null; + + // Underflow? + } else if ( e < MIN_EXP ) { + + // Zero. + n.c = [ n.e = 0 ]; + } else { + n.e = e; + n.c = c; + } + + return n; + } + + + // Handle values that fail the validity test in BigNumber. + parseNumeric = (function () { + var basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i, + dotAfter = /^([^.]+)\.$/, + dotBefore = /^\.([^.]+)$/, + isInfinityOrNaN = /^-?(Infinity|NaN)$/, + whitespaceOrPlus = /^\s*\+(?=[\w.])|^\s+|\s+$/g; + + return function ( x, str, num, b ) { + var base, + s = num ? str : str.replace( whitespaceOrPlus, '' ); + + // No exception on ±Infinity or NaN. + if ( isInfinityOrNaN.test(s) ) { + x.s = isNaN(s) ? null : s < 0 ? -1 : 1; + } else { + if ( !num ) { + + // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i + s = s.replace( basePrefix, function ( m, p1, p2 ) { + base = ( p2 = p2.toLowerCase() ) == 'x' ? 16 : p2 == 'b' ? 2 : 8; + return !b || b == base ? p1 : m; + }); + + if (b) { + base = b; + + // E.g. '1.' to '1', '.1' to '0.1' + s = s.replace( dotAfter, '$1' ).replace( dotBefore, '0.$1' ); + } + + if ( str != s ) return new BigNumber( s, base ); + } + + // 'new BigNumber() not a number: {n}' + // 'new BigNumber() not a base {b} number: {n}' + if (ERRORS) raise( id, 'not a' + ( b ? ' base ' + b : '' ) + ' number', str ); + x.s = null; + } + + x.c = x.e = null; + id = 0; + } + })(); + + + // Throw a BigNumber Error. + function raise( caller, msg, val ) { + var error = new Error( [ + 'new BigNumber', // 0 + 'cmp', // 1 + 'config', // 2 + 'div', // 3 + 'divToInt', // 4 + 'eq', // 5 + 'gt', // 6 + 'gte', // 7 + 'lt', // 8 + 'lte', // 9 + 'minus', // 10 + 'mod', // 11 + 'plus', // 12 + 'precision', // 13 + 'random', // 14 + 'round', // 15 + 'shift', // 16 + 'times', // 17 + 'toDigits', // 18 + 'toExponential', // 19 + 'toFixed', // 20 + 'toFormat', // 21 + 'toFraction', // 22 + 'pow', // 23 + 'toPrecision', // 24 + 'toString', // 25 + 'BigNumber' // 26 + ][caller] + '() ' + msg + ': ' + val ); + + error.name = 'BigNumber Error'; + id = 0; + throw error; + } + + + /* + * Round x to sd significant digits using rounding mode rm. Check for over/under-flow. + * If r is truthy, it is known that there are more digits after the rounding digit. + */ + function round( x, sd, rm, r ) { + var d, i, j, k, n, ni, rd, + xc = x.c, + pows10 = POWS_TEN; + + // if x is not Infinity or NaN... + if (xc) { + + // rd is the rounding digit, i.e. the digit after the digit that may be rounded up. + // n is a base 1e14 number, the value of the element of array x.c containing rd. + // ni is the index of n within x.c. + // d is the number of digits of n. + // i is the index of rd within n including leading zeros. + // j is the actual index of rd within n (if < 0, rd is a leading zero). + out: { + + // Get the number of digits of the first element of xc. + for ( d = 1, k = xc[0]; k >= 10; k /= 10, d++ ); + i = sd - d; + + // If the rounding digit is in the first element of xc... + if ( i < 0 ) { + i += LOG_BASE; + j = sd; + n = xc[ ni = 0 ]; + + // Get the rounding digit at index j of n. + rd = n / pows10[ d - j - 1 ] % 10 | 0; + } else { + ni = mathceil( ( i + 1 ) / LOG_BASE ); + + if ( ni >= xc.length ) { + + if (r) { + + // Needed by sqrt. + for ( ; xc.length <= ni; xc.push(0) ); + n = rd = 0; + d = 1; + i %= LOG_BASE; + j = i - LOG_BASE + 1; + } else { + break out; + } + } else { + n = k = xc[ni]; + + // Get the number of digits of n. + for ( d = 1; k >= 10; k /= 10, d++ ); + + // Get the index of rd within n. + i %= LOG_BASE; + + // Get the index of rd within n, adjusted for leading zeros. + // The number of leading zeros of n is given by LOG_BASE - d. + j = i - LOG_BASE + d; + + // Get the rounding digit at index j of n. + rd = j < 0 ? 0 : n / pows10[ d - j - 1 ] % 10 | 0; + } + } + + r = r || sd < 0 || + + // Are there any non-zero digits after the rounding digit? + // The expression n % pows10[ d - j - 1 ] returns all digits of n to the right + // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714. + xc[ni + 1] != null || ( j < 0 ? n : n % pows10[ d - j - 1 ] ); + + r = rm < 4 + ? ( rd || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) + : rd > 5 || rd == 5 && ( rm == 4 || r || rm == 6 && + + // Check whether the digit to the left of the rounding digit is odd. + ( ( i > 0 ? j > 0 ? n / pows10[ d - j ] : 0 : xc[ni - 1] ) % 10 ) & 1 || + rm == ( x.s < 0 ? 8 : 7 ) ); + + if ( sd < 1 || !xc[0] ) { + xc.length = 0; + + if (r) { + + // Convert sd to decimal places. + sd -= x.e + 1; + + // 1, 0.1, 0.01, 0.001, 0.0001 etc. + xc[0] = pows10[ sd % LOG_BASE ]; + x.e = -sd || 0; + } else { + + // Zero. + xc[0] = x.e = 0; + } + + return x; + } + + // Remove excess digits. + if ( i == 0 ) { + xc.length = ni; + k = 1; + ni--; + } else { + xc.length = ni + 1; + k = pows10[ LOG_BASE - i ]; + + // E.g. 56700 becomes 56000 if 7 is the rounding digit. + // j > 0 means i > number of leading zeros of n. + xc[ni] = j > 0 ? mathfloor( n / pows10[ d - j ] % pows10[j] ) * k : 0; + } + + // Round up? + if (r) { + + for ( ; ; ) { + + // If the digit to be rounded up is in the first element of xc... + if ( ni == 0 ) { + + // i will be the length of xc[0] before k is added. + for ( i = 1, j = xc[0]; j >= 10; j /= 10, i++ ); + j = xc[0] += k; + for ( k = 1; j >= 10; j /= 10, k++ ); + + // if i != k the length has increased. + if ( i != k ) { + x.e++; + if ( xc[0] == BASE ) xc[0] = 1; + } + + break; + } else { + xc[ni] += k; + if ( xc[ni] != BASE ) break; + xc[ni--] = 0; + k = 1; + } + } + } + + // Remove trailing zeros. + for ( i = xc.length; xc[--i] === 0; xc.pop() ); + } + + // Overflow? Infinity. + if ( x.e > MAX_EXP ) { + x.c = x.e = null; + + // Underflow? Zero. + } else if ( x.e < MIN_EXP ) { + x.c = [ x.e = 0 ]; + } + } + + return x; + } + + + // PROTOTYPE/INSTANCE METHODS + + + /* + * Return a new BigNumber whose value is the absolute value of this BigNumber. + */ + P.absoluteValue = P.abs = function () { + var x = new BigNumber(this); + if ( x.s < 0 ) x.s = 1; + return x; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole + * number in the direction of Infinity. + */ + P.ceil = function () { + return round( new BigNumber(this), this.e + 1, 2 ); + }; + + + /* + * Return + * 1 if the value of this BigNumber is greater than the value of BigNumber(y, b), + * -1 if the value of this BigNumber is less than the value of BigNumber(y, b), + * 0 if they have the same value, + * or null if the value of either is NaN. + */ + P.comparedTo = P.cmp = function ( y, b ) { + id = 1; + return compare( this, new BigNumber( y, b ) ); + }; + + + /* + * Return the number of decimal places of the value of this BigNumber, or null if the value + * of this BigNumber is ±Infinity or NaN. + */ + P.decimalPlaces = P.dp = function () { + var n, v, + c = this.c; + + if ( !c ) return null; + n = ( ( v = c.length - 1 ) - bitFloor( this.e / LOG_BASE ) ) * LOG_BASE; + + // Subtract the number of trailing zeros of the last number. + if ( v = c[v] ) for ( ; v % 10 == 0; v /= 10, n-- ); + if ( n < 0 ) n = 0; + + return n; + }; + + + /* + * n / 0 = I + * n / N = N + * n / I = 0 + * 0 / n = 0 + * 0 / 0 = N + * 0 / N = N + * 0 / I = 0 + * N / n = N + * N / 0 = N + * N / N = N + * N / I = N + * I / n = I + * I / 0 = I + * I / N = N + * I / I = N + * + * Return a new BigNumber whose value is the value of this BigNumber divided by the value of + * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.dividedBy = P.div = function ( y, b ) { + id = 3; + return div( this, new BigNumber( y, b ), DECIMAL_PLACES, ROUNDING_MODE ); + }; + + + /* + * Return a new BigNumber whose value is the integer part of dividing the value of this + * BigNumber by the value of BigNumber(y, b). + */ + P.dividedToIntegerBy = P.divToInt = function ( y, b ) { + id = 4; + return div( this, new BigNumber( y, b ), 0, 1 ); + }; + + + /* + * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b), + * otherwise returns false. + */ + P.equals = P.eq = function ( y, b ) { + id = 5; + return compare( this, new BigNumber( y, b ) ) === 0; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole + * number in the direction of -Infinity. + */ + P.floor = function () { + return round( new BigNumber(this), this.e + 1, 3 ); + }; + + + /* + * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b), + * otherwise returns false. + */ + P.greaterThan = P.gt = function ( y, b ) { + id = 6; + return compare( this, new BigNumber( y, b ) ) > 0; + }; + + + /* + * Return true if the value of this BigNumber is greater than or equal to the value of + * BigNumber(y, b), otherwise returns false. + */ + P.greaterThanOrEqualTo = P.gte = function ( y, b ) { + id = 7; + return ( b = compare( this, new BigNumber( y, b ) ) ) === 1 || b === 0; + + }; + + + /* + * Return true if the value of this BigNumber is a finite number, otherwise returns false. + */ + P.isFinite = function () { + return !!this.c; + }; + + + /* + * Return true if the value of this BigNumber is an integer, otherwise return false. + */ + P.isInteger = P.isInt = function () { + return !!this.c && bitFloor( this.e / LOG_BASE ) > this.c.length - 2; + }; + + + /* + * Return true if the value of this BigNumber is NaN, otherwise returns false. + */ + P.isNaN = function () { + return !this.s; + }; + + + /* + * Return true if the value of this BigNumber is negative, otherwise returns false. + */ + P.isNegative = P.isNeg = function () { + return this.s < 0; + }; + + + /* + * Return true if the value of this BigNumber is 0 or -0, otherwise returns false. + */ + P.isZero = function () { + return !!this.c && this.c[0] == 0; + }; + + + /* + * Return true if the value of this BigNumber is less than the value of BigNumber(y, b), + * otherwise returns false. + */ + P.lessThan = P.lt = function ( y, b ) { + id = 8; + return compare( this, new BigNumber( y, b ) ) < 0; + }; + + + /* + * Return true if the value of this BigNumber is less than or equal to the value of + * BigNumber(y, b), otherwise returns false. + */ + P.lessThanOrEqualTo = P.lte = function ( y, b ) { + id = 9; + return ( b = compare( this, new BigNumber( y, b ) ) ) === -1 || b === 0; + }; + + + /* + * n - 0 = n + * n - N = N + * n - I = -I + * 0 - n = -n + * 0 - 0 = 0 + * 0 - N = N + * 0 - I = -I + * N - n = N + * N - 0 = N + * N - N = N + * N - I = N + * I - n = I + * I - 0 = I + * I - N = N + * I - I = N + * + * Return a new BigNumber whose value is the value of this BigNumber minus the value of + * BigNumber(y, b). + */ + P.minus = P.sub = function ( y, b ) { + var i, j, t, xLTy, + x = this, + a = x.s; + + id = 10; + y = new BigNumber( y, b ); + b = y.s; + + // Either NaN? + if ( !a || !b ) return new BigNumber(NaN); + + // Signs differ? + if ( a != b ) { + y.s = -b; + return x.plus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if ( !xe || !ye ) { + + // Either Infinity? + if ( !xc || !yc ) return xc ? ( y.s = -b, y ) : new BigNumber( yc ? x : NaN ); + + // Either zero? + if ( !xc[0] || !yc[0] ) { + + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + return yc[0] ? ( y.s = -b, y ) : new BigNumber( xc[0] ? x : + + // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity + ROUNDING_MODE == 3 ? -0 : 0 ); + } + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Determine which is the bigger number. + if ( a = xe - ye ) { + + if ( xLTy = a < 0 ) { + a = -a; + t = xc; + } else { + ye = xe; + t = yc; + } + + t.reverse(); + + // Prepend zeros to equalise exponents. + for ( b = a; b--; t.push(0) ); + t.reverse(); + } else { + + // Exponents equal. Check digit by digit. + j = ( xLTy = ( a = xc.length ) < ( b = yc.length ) ) ? a : b; + + for ( a = b = 0; b < j; b++ ) { + + if ( xc[b] != yc[b] ) { + xLTy = xc[b] < yc[b]; + break; + } + } + } + + // x < y? Point xc to the array of the bigger number. + if (xLTy) t = xc, xc = yc, yc = t, y.s = -y.s; + + b = ( j = yc.length ) - ( i = xc.length ); + + // Append zeros to xc if shorter. + // No need to add zeros to yc if shorter as subtract only needs to start at yc.length. + if ( b > 0 ) for ( ; b--; xc[i++] = 0 ); + b = BASE - 1; + + // Subtract yc from xc. + for ( ; j > a; ) { + + if ( xc[--j] < yc[j] ) { + for ( i = j; i && !xc[--i]; xc[i] = b ); + --xc[i]; + xc[j] += BASE; + } + + xc[j] -= yc[j]; + } + + // Remove leading zeros and adjust exponent accordingly. + for ( ; xc[0] == 0; xc.shift(), --ye ); + + // Zero? + if ( !xc[0] ) { + + // Following IEEE 754 (2008) 6.3, + // n - n = +0 but n - n = -0 when rounding towards -Infinity. + y.s = ROUNDING_MODE == 3 ? -1 : 1; + y.c = [ y.e = 0 ]; + return y; + } + + // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity + // for finite x and y. + return normalise( y, xc, ye ); + }; + + + /* + * n % 0 = N + * n % N = N + * n % I = n + * 0 % n = 0 + * -0 % n = -0 + * 0 % 0 = N + * 0 % N = N + * 0 % I = 0 + * N % n = N + * N % 0 = N + * N % N = N + * N % I = N + * I % n = N + * I % 0 = N + * I % N = N + * I % I = N + * + * Return a new BigNumber whose value is the value of this BigNumber modulo the value of + * BigNumber(y, b). The result depends on the value of MODULO_MODE. + */ + P.modulo = P.mod = function ( y, b ) { + var q, s, + x = this; + + id = 11; + y = new BigNumber( y, b ); + + // Return NaN if x is Infinity or NaN, or y is NaN or zero. + if ( !x.c || !y.s || y.c && !y.c[0] ) { + return new BigNumber(NaN); + + // Return x if y is Infinity or x is zero. + } else if ( !y.c || x.c && !x.c[0] ) { + return new BigNumber(x); + } + + if ( MODULO_MODE == 9 ) { + + // Euclidian division: q = sign(y) * floor(x / abs(y)) + // r = x - qy where 0 <= r < abs(y) + s = y.s; + y.s = 1; + q = div( x, y, 0, 3 ); + y.s = s; + q.s *= s; + } else { + q = div( x, y, 0, MODULO_MODE ); + } + + return x.minus( q.times(y) ); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber negated, + * i.e. multiplied by -1. + */ + P.negated = P.neg = function () { + var x = new BigNumber(this); + x.s = -x.s || null; + return x; + }; + + + /* + * n + 0 = n + * n + N = N + * n + I = I + * 0 + n = n + * 0 + 0 = 0 + * 0 + N = N + * 0 + I = I + * N + n = N + * N + 0 = N + * N + N = N + * N + I = N + * I + n = I + * I + 0 = I + * I + N = N + * I + I = I + * + * Return a new BigNumber whose value is the value of this BigNumber plus the value of + * BigNumber(y, b). + */ + P.plus = P.add = function ( y, b ) { + var t, + x = this, + a = x.s; + + id = 12; + y = new BigNumber( y, b ); + b = y.s; + + // Either NaN? + if ( !a || !b ) return new BigNumber(NaN); + + // Signs differ? + if ( a != b ) { + y.s = -b; + return x.minus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if ( !xe || !ye ) { + + // Return ±Infinity if either ±Infinity. + if ( !xc || !yc ) return new BigNumber( a / 0 ); + + // Either zero? + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + if ( !xc[0] || !yc[0] ) return yc[0] ? y : new BigNumber( xc[0] ? x : a * 0 ); + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts. + if ( a = xe - ye ) { + if ( a > 0 ) { + ye = xe; + t = yc; + } else { + a = -a; + t = xc; + } + + t.reverse(); + for ( ; a--; t.push(0) ); + t.reverse(); + } + + a = xc.length; + b = yc.length; + + // Point xc to the longer array, and b to the shorter length. + if ( a - b < 0 ) t = yc, yc = xc, xc = t, b = a; + + // Only start adding at yc.length - 1 as the further digits of xc can be ignored. + for ( a = 0; b; ) { + a = ( xc[--b] = xc[b] + yc[b] + a ) / BASE | 0; + xc[b] %= BASE; + } + + if (a) { + xc.unshift(a); + ++ye; + } + + // No need to check for zero, as +x + +y != 0 && -x + -y != 0 + // ye = MAX_EXP + 1 possible + return normalise( y, xc, ye ); + }; + + + /* + * Return the number of significant digits of the value of this BigNumber. + * + * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0. + */ + P.precision = P.sd = function (z) { + var n, v, + x = this, + c = x.c; + + // 'precision() argument not a boolean or binary digit: {z}' + if ( z != null && z !== !!z && z !== 1 && z !== 0 ) { + if (ERRORS) raise( 13, 'argument' + notBool, z ); + if ( z != !!z ) z = null; + } + + if ( !c ) return null; + v = c.length - 1; + n = v * LOG_BASE + 1; + + if ( v = c[v] ) { + + // Subtract the number of trailing zeros of the last element. + for ( ; v % 10 == 0; v /= 10, n-- ); + + // Add the number of digits of the first element. + for ( v = c[0]; v >= 10; v /= 10, n++ ); + } + + if ( z && x.e + 1 > n ) n = x.e + 1; + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of + * dp decimal places using rounding mode rm, or to 0 and ROUNDING_MODE respectively if + * omitted. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'round() decimal places out of range: {dp}' + * 'round() decimal places not an integer: {dp}' + * 'round() rounding mode not an integer: {rm}' + * 'round() rounding mode out of range: {rm}' + */ + P.round = function ( dp, rm ) { + var n = new BigNumber(this); + + if ( dp == null || isValidInt( dp, 0, MAX, 15 ) ) { + round( n, ~~dp + this.e + 1, rm == null || + !isValidInt( rm, 0, 8, 15, roundingMode ) ? ROUNDING_MODE : rm | 0 ); + } + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber shifted by k places + * (powers of 10). Shift to the right if n > 0, and to the left if n < 0. + * + * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. + * + * If k is out of range and ERRORS is false, the result will be ±0 if k < 0, or ±Infinity + * otherwise. + * + * 'shift() argument not an integer: {k}' + * 'shift() argument out of range: {k}' + */ + P.shift = function (k) { + var n = this; + return isValidInt( k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 16, 'argument' ) + + // k < 1e+21, or truncate(k) will produce exponential notation. + ? n.times( '1e' + truncate(k) ) + : new BigNumber( n.c && n.c[0] && ( k < -MAX_SAFE_INTEGER || k > MAX_SAFE_INTEGER ) + ? n.s * ( k < 0 ? 0 : 1 / 0 ) + : n ); + }; + + + /* + * sqrt(-n) = N + * sqrt( N) = N + * sqrt(-I) = N + * sqrt( I) = I + * sqrt( 0) = 0 + * sqrt(-0) = -0 + * + * Return a new BigNumber whose value is the square root of the value of this BigNumber, + * rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.squareRoot = P.sqrt = function () { + var m, n, r, rep, t, + x = this, + c = x.c, + s = x.s, + e = x.e, + dp = DECIMAL_PLACES + 4, + half = new BigNumber('0.5'); + + // Negative/NaN/Infinity/zero? + if ( s !== 1 || !c || !c[0] ) { + return new BigNumber( !s || s < 0 && ( !c || c[0] ) ? NaN : c ? x : 1 / 0 ); + } + + // Initial estimate. + s = Math.sqrt( +x ); + + // Math.sqrt underflow/overflow? + // Pass x to Math.sqrt as integer, then adjust the exponent of the result. + if ( s == 0 || s == 1 / 0 ) { + n = coeffToString(c); + if ( ( n.length + e ) % 2 == 0 ) n += '0'; + s = Math.sqrt(n); + e = bitFloor( ( e + 1 ) / 2 ) - ( e < 0 || e % 2 ); + + if ( s == 1 / 0 ) { + n = '1e' + e; + } else { + n = s.toExponential(); + n = n.slice( 0, n.indexOf('e') + 1 ) + e; + } + + r = new BigNumber(n); + } else { + r = new BigNumber( s + '' ); + } + + // Check for zero. + // r could be zero if MIN_EXP is changed after the this value was created. + // This would cause a division by zero (x/t) and hence Infinity below, which would cause + // coeffToString to throw. + if ( r.c[0] ) { + e = r.e; + s = e + dp; + if ( s < 3 ) s = 0; + + // Newton-Raphson iteration. + for ( ; ; ) { + t = r; + r = half.times( t.plus( div( x, t, dp, 1 ) ) ); + + if ( coeffToString( t.c ).slice( 0, s ) === ( n = + coeffToString( r.c ) ).slice( 0, s ) ) { + + // The exponent of r may here be one less than the final result exponent, + // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits + // are indexed correctly. + if ( r.e < e ) --s; + n = n.slice( s - 3, s + 1 ); + + // The 4th rounding digit may be in error by -1 so if the 4 rounding digits + // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the + // iteration. + if ( n == '9999' || !rep && n == '4999' ) { + + // On the first iteration only, check to see if rounding up gives the + // exact result as the nines may infinitely repeat. + if ( !rep ) { + round( t, t.e + DECIMAL_PLACES + 2, 0 ); + + if ( t.times(t).eq(x) ) { + r = t; + break; + } + } + + dp += 4; + s += 4; + rep = 1; + } else { + + // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact + // result. If not, then there are further digits and m will be truthy. + if ( !+n || !+n.slice(1) && n.charAt(0) == '5' ) { + + // Truncate to the first rounding digit. + round( r, r.e + DECIMAL_PLACES + 2, 1 ); + m = !r.times(r).eq(x); + } + + break; + } + } + } + } + + return round( r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m ); + }; + + + /* + * n * 0 = 0 + * n * N = N + * n * I = I + * 0 * n = 0 + * 0 * 0 = 0 + * 0 * N = N + * 0 * I = N + * N * n = N + * N * 0 = N + * N * N = N + * N * I = N + * I * n = I + * I * 0 = N + * I * N = N + * I * I = I + * + * Return a new BigNumber whose value is the value of this BigNumber times the value of + * BigNumber(y, b). + */ + P.times = P.mul = function ( y, b ) { + var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, + base, sqrtBase, + x = this, + xc = x.c, + yc = ( id = 17, y = new BigNumber( y, b ) ).c; + + // Either NaN, ±Infinity or ±0? + if ( !xc || !yc || !xc[0] || !yc[0] ) { + + // Return NaN if either is NaN, or one is 0 and the other is Infinity. + if ( !x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc ) { + y.c = y.e = y.s = null; + } else { + y.s *= x.s; + + // Return ±Infinity if either is ±Infinity. + if ( !xc || !yc ) { + y.c = y.e = null; + + // Return ±0 if either is ±0. + } else { + y.c = [0]; + y.e = 0; + } + } + + return y; + } + + e = bitFloor( x.e / LOG_BASE ) + bitFloor( y.e / LOG_BASE ); + y.s *= x.s; + xcL = xc.length; + ycL = yc.length; + + // Ensure xc points to longer array and xcL to its length. + if ( xcL < ycL ) zc = xc, xc = yc, yc = zc, i = xcL, xcL = ycL, ycL = i; + + // Initialise the result array with zeros. + for ( i = xcL + ycL, zc = []; i--; zc.push(0) ); + + base = BASE; + sqrtBase = SQRT_BASE; + + for ( i = ycL; --i >= 0; ) { + c = 0; + ylo = yc[i] % sqrtBase; + yhi = yc[i] / sqrtBase | 0; + + for ( k = xcL, j = i + k; j > i; ) { + xlo = xc[--k] % sqrtBase; + xhi = xc[k] / sqrtBase | 0; + m = yhi * xlo + xhi * ylo; + xlo = ylo * xlo + ( ( m % sqrtBase ) * sqrtBase ) + zc[j] + c; + c = ( xlo / base | 0 ) + ( m / sqrtBase | 0 ) + yhi * xhi; + zc[j--] = xlo % base; + } + + zc[j] = c; + } + + if (c) { + ++e; + } else { + zc.shift(); + } + + return normalise( y, zc, e ); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of + * sd significant digits using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toDigits() precision out of range: {sd}' + * 'toDigits() precision not an integer: {sd}' + * 'toDigits() rounding mode not an integer: {rm}' + * 'toDigits() rounding mode out of range: {rm}' + */ + P.toDigits = function ( sd, rm ) { + var n = new BigNumber(this); + sd = sd == null || !isValidInt( sd, 1, MAX, 18, 'precision' ) ? null : sd | 0; + rm = rm == null || !isValidInt( rm, 0, 8, 18, roundingMode ) ? ROUNDING_MODE : rm | 0; + return sd ? round( n, sd, rm ) : n; + }; + + + /* + * Return a string representing the value of this BigNumber in exponential notation and + * rounded using ROUNDING_MODE to dp fixed decimal places. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toExponential() decimal places not an integer: {dp}' + * 'toExponential() decimal places out of range: {dp}' + * 'toExponential() rounding mode not an integer: {rm}' + * 'toExponential() rounding mode out of range: {rm}' + */ + P.toExponential = function ( dp, rm ) { + return format( this, + dp != null && isValidInt( dp, 0, MAX, 19 ) ? ~~dp + 1 : null, rm, 19 ); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounding + * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * Note: as with JavaScript's number type, (-0).toFixed(0) is '0', + * but e.g. (-0.00001).toFixed(0) is '-0'. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toFixed() decimal places not an integer: {dp}' + * 'toFixed() decimal places out of range: {dp}' + * 'toFixed() rounding mode not an integer: {rm}' + * 'toFixed() rounding mode out of range: {rm}' + */ + P.toFixed = function ( dp, rm ) { + return format( this, dp != null && isValidInt( dp, 0, MAX, 20 ) + ? ~~dp + this.e + 1 : null, rm, 20 ); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounded + * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties + * of the FORMAT object (see BigNumber.config). + * + * FORMAT = { + * decimalSeparator : '.', + * groupSeparator : ',', + * groupSize : 3, + * secondaryGroupSize : 0, + * fractionGroupSeparator : '\xA0', // non-breaking space + * fractionGroupSize : 0 + * }; + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toFormat() decimal places not an integer: {dp}' + * 'toFormat() decimal places out of range: {dp}' + * 'toFormat() rounding mode not an integer: {rm}' + * 'toFormat() rounding mode out of range: {rm}' + */ + P.toFormat = function ( dp, rm ) { + var str = format( this, dp != null && isValidInt( dp, 0, MAX, 21 ) + ? ~~dp + this.e + 1 : null, rm, 21 ); + + if ( this.c ) { + var i, + arr = str.split('.'), + g1 = +FORMAT.groupSize, + g2 = +FORMAT.secondaryGroupSize, + groupSeparator = FORMAT.groupSeparator, + intPart = arr[0], + fractionPart = arr[1], + isNeg = this.s < 0, + intDigits = isNeg ? intPart.slice(1) : intPart, + len = intDigits.length; + + if (g2) i = g1, g1 = g2, g2 = i, len -= i; + + if ( g1 > 0 && len > 0 ) { + i = len % g1 || g1; + intPart = intDigits.substr( 0, i ); + + for ( ; i < len; i += g1 ) { + intPart += groupSeparator + intDigits.substr( i, g1 ); + } + + if ( g2 > 0 ) intPart += groupSeparator + intDigits.slice(i); + if (isNeg) intPart = '-' + intPart; + } + + str = fractionPart + ? intPart + FORMAT.decimalSeparator + ( ( g2 = +FORMAT.fractionGroupSize ) + ? fractionPart.replace( new RegExp( '\\d{' + g2 + '}\\B', 'g' ), + '$&' + FORMAT.fractionGroupSeparator ) + : fractionPart ) + : intPart; + } + + return str; + }; + + + /* + * Return a string array representing the value of this BigNumber as a simple fraction with + * an integer numerator and an integer denominator. The denominator will be a positive + * non-zero value less than or equal to the specified maximum denominator. If a maximum + * denominator is not specified, the denominator will be the lowest value necessary to + * represent the number exactly. + * + * [md] {number|string|BigNumber} Integer >= 1 and < Infinity. The maximum denominator. + * + * 'toFraction() max denominator not an integer: {md}' + * 'toFraction() max denominator out of range: {md}' + */ + P.toFraction = function (md) { + var arr, d0, d2, e, exp, n, n0, q, s, + k = ERRORS, + x = this, + xc = x.c, + d = new BigNumber(ONE), + n1 = d0 = new BigNumber(ONE), + d1 = n0 = new BigNumber(ONE); + + if ( md != null ) { + ERRORS = false; + n = new BigNumber(md); + ERRORS = k; + + if ( !( k = n.isInt() ) || n.lt(ONE) ) { + + if (ERRORS) { + raise( 22, + 'max denominator ' + ( k ? 'out of range' : 'not an integer' ), md ); + } + + // ERRORS is false: + // If md is a finite non-integer >= 1, round it to an integer and use it. + md = !k && n.c && round( n, n.e + 1, 1 ).gte(ONE) ? n : null; + } + } + + if ( !xc ) return x.toString(); + s = coeffToString(xc); + + // Determine initial denominator. + // d is a power of 10 and the minimum max denominator that specifies the value exactly. + e = d.e = s.length - x.e - 1; + d.c[0] = POWS_TEN[ ( exp = e % LOG_BASE ) < 0 ? LOG_BASE + exp : exp ]; + md = !md || n.cmp(d) > 0 ? ( e > 0 ? d : n1 ) : n; + + exp = MAX_EXP; + MAX_EXP = 1 / 0; + n = new BigNumber(s); + + // n0 = d1 = 0 + n0.c[0] = 0; + + for ( ; ; ) { + q = div( n, d, 0, 1 ); + d2 = d0.plus( q.times(d1) ); + if ( d2.cmp(md) == 1 ) break; + d0 = d1; + d1 = d2; + n1 = n0.plus( q.times( d2 = n1 ) ); + n0 = d2; + d = n.minus( q.times( d2 = d ) ); + n = d2; + } + + d2 = div( md.minus(d0), d1, 0, 1 ); + n0 = n0.plus( d2.times(n1) ); + d0 = d0.plus( d2.times(d1) ); + n0.s = n1.s = x.s; + e *= 2; + + // Determine which fraction is closer to x, n0/d0 or n1/d1 + arr = div( n1, d1, e, ROUNDING_MODE ).minus(x).abs().cmp( + div( n0, d0, e, ROUNDING_MODE ).minus(x).abs() ) < 1 + ? [ n1.toString(), d1.toString() ] + : [ n0.toString(), d0.toString() ]; + + MAX_EXP = exp; + return arr; + }; + + + /* + * Return the value of this BigNumber converted to a number primitive. + */ + P.toNumber = function () { + var x = this; + + // Ensure zero has correct sign. + return +x || ( x.s ? x.s * 0 : NaN ); + }; + + + /* + * Return a BigNumber whose value is the value of this BigNumber raised to the power n. + * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE. + * If POW_PRECISION is not 0, round to POW_PRECISION using ROUNDING_MODE. + * + * n {number} Integer, -9007199254740992 to 9007199254740992 inclusive. + * (Performs 54 loop iterations for n of 9007199254740992.) + * + * 'pow() exponent not an integer: {n}' + * 'pow() exponent out of range: {n}' + */ + P.toPower = P.pow = function (n) { + var k, y, + i = mathfloor( n < 0 ? -n : +n ), + x = this; + + // Pass ±Infinity to Math.pow if exponent is out of range. + if ( !isValidInt( n, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 23, 'exponent' ) && + ( !isFinite(n) || i > MAX_SAFE_INTEGER && ( n /= 0 ) || + parseFloat(n) != n && !( n = NaN ) ) ) { + return new BigNumber( Math.pow( +x, n ) ); + } + + // Truncating each coefficient array to a length of k after each multiplication equates + // to truncating significant digits to POW_PRECISION + [28, 41], i.e. there will be a + // minimum of 28 guard digits retained. (Using + 1.5 would give [9, 21] guard digits.) + k = POW_PRECISION ? mathceil( POW_PRECISION / LOG_BASE + 2 ) : 0; + y = new BigNumber(ONE); + + for ( ; ; ) { + + if ( i % 2 ) { + y = y.times(x); + if ( !y.c ) break; + if ( k && y.c.length > k ) y.c.length = k; + } + + i = mathfloor( i / 2 ); + if ( !i ) break; + + x = x.times(x); + if ( k && x.c && x.c.length > k ) x.c.length = k; + } + + if ( n < 0 ) y = ONE.div(y); + return k ? round( y, POW_PRECISION, ROUNDING_MODE ) : y; + }; + + + /* + * Return a string representing the value of this BigNumber rounded to sd significant digits + * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits + * necessary to represent the integer part of the value in fixed-point notation, then use + * exponential notation. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toPrecision() precision not an integer: {sd}' + * 'toPrecision() precision out of range: {sd}' + * 'toPrecision() rounding mode not an integer: {rm}' + * 'toPrecision() rounding mode out of range: {rm}' + */ + P.toPrecision = function ( sd, rm ) { + return format( this, sd != null && isValidInt( sd, 1, MAX, 24, 'precision' ) + ? sd | 0 : null, rm, 24 ); + }; + + + /* + * Return a string representing the value of this BigNumber in base b, or base 10 if b is + * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and + * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent + * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than + * TO_EXP_NEG, return exponential notation. + * + * [b] {number} Integer, 2 to 64 inclusive. + * + * 'toString() base not an integer: {b}' + * 'toString() base out of range: {b}' + */ + P.toString = function (b) { + var str, + n = this, + s = n.s, + e = n.e; + + // Infinity or NaN? + if ( e === null ) { + + if (s) { + str = 'Infinity'; + if ( s < 0 ) str = '-' + str; + } else { + str = 'NaN'; + } + } else { + str = coeffToString( n.c ); + + if ( b == null || !isValidInt( b, 2, 64, 25, 'base' ) ) { + str = e <= TO_EXP_NEG || e >= TO_EXP_POS + ? toExponential( str, e ) + : toFixedPoint( str, e ); + } else { + str = convertBase( toFixedPoint( str, e ), b | 0, 10, s ); + } + + if ( s < 0 && n.c[0] ) str = '-' + str; + } + + return str; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber truncated to a whole + * number. + */ + P.truncated = P.trunc = function () { + return round( new BigNumber(this), this.e + 1, 1 ); + }; + + + + /* + * Return as toString, but do not accept a base argument. + */ + P.valueOf = P.toJSON = function () { + return this.toString(); + }; + + + // Aliases for BigDecimal methods. + //P.add = P.plus; // P.add included above + //P.subtract = P.minus; // P.sub included above + //P.multiply = P.times; // P.mul included above + //P.divide = P.div; + //P.remainder = P.mod; + //P.compareTo = P.cmp; + //P.negate = P.neg; + + + if ( configObj != null ) BigNumber.config(configObj); + + return BigNumber; + } + + + // PRIVATE HELPER FUNCTIONS + + + function bitFloor(n) { + var i = n | 0; + return n > 0 || n === i ? i : i - 1; + } + + + // Return a coefficient array as a string of base 10 digits. + function coeffToString(a) { + var s, z, + i = 1, + j = a.length, + r = a[0] + ''; + + for ( ; i < j; ) { + s = a[i++] + ''; + z = LOG_BASE - s.length; + for ( ; z--; s = '0' + s ); + r += s; + } + + // Determine trailing zeros. + for ( j = r.length; r.charCodeAt(--j) === 48; ); + return r.slice( 0, j + 1 || 1 ); + } + + + // Compare the value of BigNumbers x and y. + function compare( x, y ) { + var a, b, + xc = x.c, + yc = y.c, + i = x.s, + j = y.s, + k = x.e, + l = y.e; + + // Either NaN? + if ( !i || !j ) return null; + + a = xc && !xc[0]; + b = yc && !yc[0]; + + // Either zero? + if ( a || b ) return a ? b ? 0 : -j : i; + + // Signs differ? + if ( i != j ) return i; + + a = i < 0; + b = k == l; + + // Either Infinity? + if ( !xc || !yc ) return b ? 0 : !xc ^ a ? 1 : -1; + + // Compare exponents. + if ( !b ) return k > l ^ a ? 1 : -1; + + j = ( k = xc.length ) < ( l = yc.length ) ? k : l; + + // Compare digit by digit. + for ( i = 0; i < j; i++ ) if ( xc[i] != yc[i] ) return xc[i] > yc[i] ^ a ? 1 : -1; + + // Compare lengths. + return k == l ? 0 : k > l ^ a ? 1 : -1; + } + + + /* + * Return true if n is a valid number in range, otherwise false. + * Use for argument validation when ERRORS is false. + * Note: parseInt('1e+1') == 1 but parseFloat('1e+1') == 10. + */ + function intValidatorNoErrors( n, min, max ) { + return ( n = truncate(n) ) >= min && n <= max; + } + + + function isArray(obj) { + return Object.prototype.toString.call(obj) == '[object Array]'; + } + + + /* + * Convert string of baseIn to an array of numbers of baseOut. + * Eg. convertBase('255', 10, 16) returns [15, 15]. + * Eg. convertBase('ff', 16, 10) returns [2, 5, 5]. + */ + function toBaseOut( str, baseIn, baseOut ) { + var j, + arr = [0], + arrL, + i = 0, + len = str.length; + + for ( ; i < len; ) { + for ( arrL = arr.length; arrL--; arr[arrL] *= baseIn ); + arr[ j = 0 ] += ALPHABET.indexOf( str.charAt( i++ ) ); + + for ( ; j < arr.length; j++ ) { + + if ( arr[j] > baseOut - 1 ) { + if ( arr[j + 1] == null ) arr[j + 1] = 0; + arr[j + 1] += arr[j] / baseOut | 0; + arr[j] %= baseOut; + } + } + } + + return arr.reverse(); + } + + + function toExponential( str, e ) { + return ( str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str ) + + ( e < 0 ? 'e' : 'e+' ) + e; + } + + + function toFixedPoint( str, e ) { + var len, z; + + // Negative exponent? + if ( e < 0 ) { + + // Prepend zeros. + for ( z = '0.'; ++e; z += '0' ); + str = z + str; + + // Positive exponent + } else { + len = str.length; + + // Append zeros. + if ( ++e > len ) { + for ( z = '0', e -= len; --e; z += '0' ); + str += z; + } else if ( e < len ) { + str = str.slice( 0, e ) + '.' + str.slice(e); + } + } + + return str; + } + + + function truncate(n) { + n = parseFloat(n); + return n < 0 ? mathceil(n) : mathfloor(n); + } + + + // EXPORT + + + BigNumber = another(); + + // AMD. + if ( typeof define == 'function' && define.amd ) { + define( function () { return BigNumber; } ); + + // Node and other environments that support module.exports. + } else if ( typeof module != 'undefined' && module.exports ) { + module.exports = BigNumber; + if ( !crypto ) try { crypto = require('crypto'); } catch (e) {} + + // Browser. + } else { + global.BigNumber = BigNumber; + } +})(this); + +},{"crypto":1}],"natspec":[function(require,module,exports){ +/* + This file is part of natspec.js. + + natspec.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + natspec.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with natspec.js. If not, see . +*/ +/** @file natspec.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var abi = require('./node_modules/ethereum.js/lib/abi.js'); + +/** + * This object should be used to evaluate natspec expression + * It has one method evaluateExpression which shoul be used + */ +var natspec = (function () { + /** + * Helper method + * Should be called to copy values from object to global context + * + * @method copyToContext + * @param {Object} object from which we want to copy properties + * @param {Object} object to which we copy + */ + var copyToContext = function (obj, context) { + Object.keys(obj).forEach(function (key) { + context[key] = obj[key]; + }); + } + + /** + * Should be used to generate codes, which will be evaluated + * + * @method generateCode + * @param {Object} object from which code will be generated + * @return {String} javascript code which is used to initalized variables + */ + var generateCode = function (obj) { + return Object.keys(obj).reduce(function (acc, key) { + return acc + "var " + key + " = context['" + key + "'];\n"; + }, ""); + }; + + /** + * Helper method + * Should be called to get method with given name from the abi + * + * @method getMethodWithName + * @param {Array} contract's abi + * @param {String} name of the method that we are looking for + * @return {Object} abi for method with name + */ + var getMethodWithName = function(abi, name) { + return abi.filter(function (method) { + return method.name === name; + })[0]; + }; + + /** + * Should be used to get all contract method input variables + * + * @method getMethodInputParams + * @param {Object} abi for certain method + * @param {Object} transaction object + * @return {Object} object with all contract's method input variables + */ + var getMethodInputParams = function (method, transaction) { + // do it with output formatter (cause we have to decode) + var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); + + return method.inputs.reduce(function (acc, current, index) { + acc[current.name] = params[index]; + return acc; + }, {}); + }; + + /** + * Should be called when we want to evaluate natspec expression + * Replaces all natspec 'subexpressions' with evaluated value + * + * @method mapExpressionToEvaluate + * @param {String} expression to evaluate + * @param {Function} callback which is called to evaluate te expression + * @return {String} evaluated expression + */ + var mapExpressionsToEvaluate = function (expression, cb) { + var evaluatedExpression = ""; + + // match everything in `` quotes + var pattern = /\`(?:\\.|[^`\\])*\`/gim + var match; + var lastIndex = 0; + try { + while ((match = pattern.exec(expression)) !== null) { + var startIndex = pattern.lastIndex - match[0].length; + var toEval = match[0].slice(1, match[0].length - 1); + evaluatedExpression += expression.slice(lastIndex, startIndex); + var evaluatedPart = cb(toEval); + evaluatedExpression += evaluatedPart; + lastIndex = pattern.lastIndex; + } + + evaluatedExpression += expression.slice(lastIndex); + } + catch (err) { + throw new Error("Natspec evaluation failed, wrong input params"); + } + + return evaluatedExpression; + }; + + /** + * Should be called to evaluate single expression + * Is internally using javascript's 'eval' method + * + * @method evaluateExpression + * @param {String} expression which should be evaluated + * @param {Object} [call] object containing contract abi, transaction, called method + * @return {String} evaluated expression + * @throws exception if method is not found or we are trying to evaluate input params that does not exists + */ + var evaluateExpression = function (expression, call) { + //var self = this; + var context = {}; + + if (!!call) { + try { + var method = getMethodWithName(call.abi, call.method); + var params = getMethodInputParams(method, call.transaction); + copyToContext(params, context); + } + catch (err) { + throw new Error("Natspec evaluation failed, method does not exist"); + } + } + + var code = generateCode(context); + + var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) { + var fn = new Function("context", code + "return " + toEval + ";"); + return fn(context).toString(); + }); + + return evaluatedExpression; + }; + + /** + * Safe version of evaluateExpression + * Instead of throwing an exception it returns it as a string + * + * @method evaluateExpressionSafe + * @param {String} expression which should be evaluated + * @param {Object} [call] object containing contract abi, transaction, called method + * @return {String} evaluated expression + */ + var evaluateExpressionSafe = function (expression, call) { + try { + return evaluateExpression(expression, call); + } + catch (err) { + return err.message; + } + }; + + return { + evaluateExpression: evaluateExpression, + evaluateExpressionSafe: evaluateExpressionSafe + }; + +})(); + +module.exports = natspec; + + +},{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); diff --git a/libnatspec/natspecjs/dist/natspec.js.map b/libnatspec/natspecjs/dist/natspec.js.map new file mode 100644 index 000000000..126dc498d --- /dev/null +++ b/libnatspec/natspecjs/dist/natspec.js.map @@ -0,0 +1 @@ +{"version":3,"file":"natspec.min.js","sources":["natspec.js"],"names":["require","e","t","n","r","s","o","u","a","i","f","Error","code","l","exports","call","length",1,2,"module","drainQueue","draining","currentQueue","len","queue","noop","process","nextTick","fun","push","setTimeout","title","browser","env","argv","version","on","addListener","once","off","removeListener","removeAllListeners","emit","binding","cwd","chdir","umask",3,"utils","types","c","displayTypeError","type","console","error","arrayType","slice","dynamicTypeBytes","value","formatInputInt","inputTypes","formatInput","inputs","params","bytes","toAppendConstant","toAppendArrayContent","forEach","input","index","typeMatch","j","formatter","format","reduce","acc","curr","dynamicBytesLength","ETH_PADDING","outputTypes","formatOutput","outs","output","result","padding","dynamicPartLength","dynamicPart","out","size","formatOutputUInt","array","k","prefixedType","inputParser","json","parser","method","displayName","extractDisplayName","name","typeName","extractTypeName","impl","Array","prototype","arguments","undefined","outputParser","outputs","./const","./formatters","./types","./utils",4,"NODE_ENV","BigNumber","ETH_UNITS","ETH_SIGNATURE_LENGTH","ETH_BIGNUMBER_ROUNDING_MODE","ROUNDING_MODE","ROUND_DOWN","ETH_POLLING_TIMEOUT","this","_process","bignumber.js",5,"padLeft","string","chars","sign","join","config","round","lessThan","plus","toString","indexOf","substr","formatInputString","fromAscii","formatInputBool","formatInputReal","times","pow","signedIsNegative","formatOutputInt","minus","formatOutputReal","dividedBy","formatOutputUReal","formatOutputHash","formatOutputBool","formatOutputString","toAscii","formatOutputAddress",6,"prefix","namedType",7,"findIndex","callback","end","hex","str","substring","parseInt","String","fromCharCode","toHex","charCodeAt","pad","replace","filterFunctions","filter","current","filterEvents","toEth","val","unit","units","toFixed","replaceFunction","$0","$1","$2",8,"global","another","configObj","b","num","x","ERRORS","raise","isValidInt","id","DECIMAL_PLACES","RegExp","ALPHABET","test","parseNumeric","tooManyDigits","convertBase","isNumeric","search","MAX_EXP","MIN_EXP","LOG_BASE","baseOut","baseIn","d","xc","y","dp","rm","toLowerCase","POW_PRECISION","toBaseOut","toFixedPoint","coeffToString","pop","div","unshift","charAt","caller","c0","ne","roundingMode","TO_EXP_NEG","toExponential","maxOrMin","args","m","isArray","intValidatorWithErrors","min","max","truncate","normalise","msg","sd","ni","rd","pows10","POWS_TEN","mathceil","mathfloor","BASE","P","ONE","TO_EXP_POS","CRYPTO","MODULO_MODE","FORMAT","decimalSeparator","groupSeparator","groupSize","secondaryGroupSize","fractionGroupSeparator","fractionGroupSize","ROUND_UP","ROUND_CEIL","ROUND_FLOOR","ROUND_HALF_UP","ROUND_HALF_DOWN","ROUND_HALF_EVEN","ROUND_HALF_CEIL","ROUND_HALF_FLOOR","EUCLID","v","p","has","hasOwnProperty","MAX","intValidatorNoErrors","notBool","crypto","lt","gt","random","pow2_53","random53bitInt","Math","rand","getRandomValues","Uint32Array","randomBytes","copy","shift","multiply","base","temp","xlo","xhi","carry","klo","SQRT_BASE","khi","compare","aL","bL","cmp","subtract","more","prod","prodL","q","qc","rem","remL","rem0","xi","xL","yc0","yL","yz","yc","NaN","bitFloor","basePrefix","dotAfter","dotBefore","isInfinityOrNaN","whitespaceOrPlus","isNaN","p1","p2","absoluteValue","abs","ceil","comparedTo","decimalPlaces","dividedToIntegerBy","divToInt","equals","eq","floor","greaterThan","greaterThanOrEqualTo","gte","isFinite","isInteger","isInt","isNegative","isNeg","isZero","lessThanOrEqualTo","lte","sub","xLTy","xe","ye","reverse","modulo","mod","negated","neg","add","precision","z","MAX_SAFE_INTEGER","squareRoot","sqrt","rep","half","mul","xcL","ycL","ylo","yhi","zc","sqrtBase","toDigits","toFormat","arr","split","g1","g2","intPart","fractionPart","intDigits","toFraction","md","d0","d2","exp","n0","n1","d1","toNumber","toPower","parseFloat","toPrecision","truncated","trunc","valueOf","toJSON","obj","Object","arrL","define","amd","natspec","abi","copyToContext","context","keys","key","generateCode","getMethodWithName","getMethodInputParams","transaction","data","mapExpressionsToEvaluate","expression","cb","match","evaluatedExpression","pattern","lastIndex","exec","startIndex","toEval","evaluatedPart","err","evaluateExpression","fn","Function","evaluateExpressionSafe","message","./node_modules/ethereum.js/lib/abi.js"],"mappings":"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"} \ No newline at end of file diff --git a/libnatspec/natspecjs/dist/natspec.min.js b/libnatspec/natspecjs/dist/natspec.min.js new file mode 100644 index 000000000..6cf28d756 --- /dev/null +++ b/libnatspec/natspecjs/dist/natspec.min.js @@ -0,0 +1,2 @@ +require=function t(e,n,r){function i(f,u){if(!n[f]){if(!e[f]){var s="function"==typeof require&&require;if(!u&&s)return s(f,!0);if(o)return o(f,!0);var c=new Error("Cannot find module '"+f+"'");throw c.code="MODULE_NOT_FOUND",c}var a=n[f]={exports:{}};e[f][0].call(a.exports,function(t){var n=e[f][1][t];return i(n?n:t)},a,a.exports,t,e,n,r)}return n[f].exports}for(var o="function"==typeof require&&require,f=0;fv;v++)d.push(g(e.slice(0,s))),e=e.slice(s);n.push(d)}else r.prefixedType("string")(t[c].type)?(a=a.slice(s),n.push(g(e.slice(0,s))),e=e.slice(s)):(n.push(g(e.slice(0,s))),e=e.slice(s))}),n},g=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return a(t.inputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},m=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(e){return h(t.outputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e};e.exports={inputParser:g,outputParser:m,formatInput:a,formatOutput:h}},{"./const":4,"./formatters":5,"./types":6,"./utils":7}],4:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:i,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3}}).call(this,t("_process"))},{_process:2,"bignumber.js":8}],5:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=t("./utils"),o=t("./const"),f=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},u=function(t){var e=2*o.ETH_PADDING;return t instanceof r||"number"==typeof t?("number"==typeof t&&(t=new r(t)),r.config(o.ETH_BIGNUMBER_ROUNDING_MODE),t=t.round(),t.lessThan(0)&&(t=new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16).plus(t).plus(1)),t=t.toString(16)):t=0===t.indexOf("0x")?t.substr(2):"string"==typeof t?u(new r(t)):(+t).toString(16),f(t,e)},s=function(t){return i.fromAscii(t,o.ETH_PADDING).substr(2)},c=function(t){return"000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0")},a=function(t){return u(new r(t).times(new r(2).pow(128)))},l=function(t){return"1"===new r(t.substr(0,1),16).toString(2).substr(0,1)},p=function(t){return t=t||"0",l(t)?new r(t,16).minus(new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new r(t,16)},h=function(t){return t=t||"0",new r(t,16)},g=function(t){return p(t).dividedBy(new r(2).pow(128))},m=function(t){return h(t).dividedBy(new r(2).pow(128))},d=function(t){return"0x"+t},v=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t?!0:!1},w=function(t){return i.toAscii(t)},y=function(t){return"0x"+t.slice(t.length-40,t.length)};e.exports={formatInputInt:u,formatInputString:s,formatInputBool:c,formatInputReal:a,formatOutputInt:p,formatOutputUInt:h,formatOutputReal:g,formatOutputUReal:m,formatOutputHash:d,formatOutputBool:v,formatOutputString:w,formatOutputAddress:y}}).call(this,t("_process"))},{"./const":4,"./utils":7,_process:2,"bignumber.js":8}],6:[function(t,e){var n=t("./formatters"),r=function(t){return function(e){return 0===e.indexOf(t)}},i=function(t){return function(e){return t===e}},o=function(){return[{type:r("uint"),format:n.formatInputInt},{type:r("int"),format:n.formatInputInt},{type:r("hash"),format:n.formatInputInt},{type:r("string"),format:n.formatInputString},{type:r("real"),format:n.formatInputReal},{type:r("ureal"),format:n.formatInputReal},{type:i("address"),format:n.formatInputInt},{type:i("bool"),format:n.formatInputBool}]},f=function(){return[{type:r("uint"),format:n.formatOutputUInt},{type:r("int"),format:n.formatOutputInt},{type:r("hash"),format:n.formatOutputHash},{type:r("string"),format:n.formatOutputString},{type:r("real"),format:n.formatOutputReal},{type:r("ureal"),format:n.formatOutputUReal},{type:i("address"),format:n.formatOutputAddress},{type:i("bool"),format:n.formatOutputBool}]};e.exports={prefixedType:r,namedType:i,inputTypes:o,outputTypes:f}},{"./formatters":5}],7:[function(t,e){var n=t("./const"),r=function(t,e){for(var n=!1,r=0;rn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},o=function(t){for(var e="",n=0;n3e3&&rr?"i":"").test(c))return m(a,c,u,r);u?(a.s=0>1/t?(c=c.slice(1),-1):1,$&&c.replace(/^0\.0*|\./,"").length>15&&U(L,O,t),u=!1):a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1,c=n(c,10,r,a.s)}else{if(t instanceof e)return a.s=t.s,a.e=t.e,a.c=(t=t.c)?t.slice():t,void(L=0);if((u="number"==typeof t)&&0*t==0){if(a.s=0>1/t?(t=-t,-1):1,t===~~t){for(o=0,f=t;f>=10;f/=10,o++);return a.e=o,a.c=[t],void(L=0)}c=t+""}else{if(!d.test(c=t+""))return m(a,c,u);a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1}}for((o=c.indexOf("."))>-1&&(c=c.replace(".","")),(f=c.search(/e/i))>0?(0>o&&(o=f),o+=+c.slice(f+1),c=c.substring(0,f)):0>o&&(o=c.length),f=0;48===c.charCodeAt(f);f++);for(s=c.length;48===c.charCodeAt(--s););if(c=c.slice(f,s+1))if(s=c.length,u&&$&&s>15&&U(L,O,a.s*t),o=o-f-1,o>q)a.c=a.e=null;else if(k>o)a.c=[a.e=0];else{if(a.e=o,a.c=[],f=(o+1)%I,0>o&&(f+=I),s>f){for(f&&a.c.push(+c.slice(0,f)),s-=I;s>f;)a.c.push(+c.slice(f,f+=I));c=c.slice(f),f=I-c.length}else f-=s;for(;f--;c+="0");a.c.push(+c)}else a.c=[a.e=0];L=0}function n(t,n,r,i){var f,u,s,a,p,h,g,m=t.indexOf("."),d=B,v=H;for(37>r&&(t=t.toLowerCase()),m>=0&&(s=Y,Y=0,t=t.replace(".",""),g=new e(r),p=g.pow(t.length-m),Y=s,g.c=c(l(o(p.c),p.e),10,n),g.e=g.c.length),h=c(t,r,n),u=s=h.length;0==h[--s];h.pop());if(!h[0])return"0";if(0>m?--u:(p.c=h,p.e=u,p.s=i,p=G(p,g,d,v,n),h=p.c,a=p.r,u=p.e),f=u+d+1,m=h[f],s=n/2,a=a||0>f||null!=h[f+1],a=4>v?(null!=m||a)&&(0==v||v==(p.s<0?3:2)):m>s||m==s&&(4==v||a||6==v&&1&h[f-1]||v==(p.s<0?8:7)),1>f||!h[0])t=a?l("1",-d):"0";else{if(h.length=f,a)for(--n;++h[--f]>n;)h[f]=0,f||(++u,h.unshift(1));for(s=h.length;!h[--s];);for(m=0,t="";s>=m;t+=N.charAt(h[m++]));t=l(t,u)}return t}function h(t,n,r,i){var f,u,s,c,p;if(r=null!=r&&z(r,0,8,i,b)?0|r:H,!t.c)return t.toString();if(f=t.c[0],s=t.e,null==n)p=o(t.c),p=19==i||24==i&&C>=s?a(p,s):l(p,s);else if(t=F(new e(t),n,r),u=t.e,p=o(t.c),c=p.length,19==i||24==i&&(u>=n||C>=u)){for(;n>c;p+="0",c++);p=a(p,u)}else if(n-=s,p=l(p,u),u+1>c){if(--n>0)for(p+=".";n--;p+="0");}else if(n+=u-c,n>0)for(u+1==c&&(p+=".");n--;p+="0");return t.s<0&&f?"-"+p:p}function S(t,n){var r,i,o=0;for(s(t[0])&&(t=t[0]),r=new e(t[0]);++ot||t>n||t!=p(t))&&U(r,(i||"decimal places")+(e>t||t>n?" out of range":" not an integer"),t),!0}function R(t,e,n){for(var r=1,i=e.length;!e[--i];e.pop());for(i=e[0];i>=10;i/=10,r++);return(n=r+n*I-1)>q?t.c=t.e=null:k>n?t.c=[t.e=0]:(t.e=n,t.c=e),t}function U(t,e,n){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][t]+"() "+e+": "+n);throw r.name="BigNumber Error",L=0,r}function F(t,e,n,r){var i,o,f,u,s,c,a,l=t.c,p=_;if(l){t:{for(i=1,u=l[0];u>=10;u/=10,i++);if(o=e-i,0>o)o+=I,f=e,s=l[c=0],a=s/p[i-f-1]%10|0;else if(c=v((o+1)/I),c>=l.length){if(!r)break t;for(;l.length<=c;l.push(0));s=a=0,i=1,o%=I,f=o-I+1}else{for(s=u=l[c],i=1;u>=10;u/=10,i++);o%=I,f=o-I+i,a=0>f?0:s/p[i-f-1]%10|0}if(r=r||0>e||null!=l[c+1]||(0>f?s:s%p[i-f-1]),r=4>n?(a||r)&&(0==n||n==(t.s<0?3:2)):a>5||5==a&&(4==n||r||6==n&&(o>0?f>0?s/p[i-f]:0:l[c-1])%10&1||n==(t.s<0?8:7)),1>e||!l[0])return l.length=0,r?(e-=t.e+1,l[0]=p[e%I],t.e=-e||0):l[0]=t.e=0,t;if(0==o?(l.length=c,u=1,c--):(l.length=c+1,u=p[I-o],l[c]=f>0?w(s/p[i-f]%p[f])*u:0),r)for(;;){if(0==c){for(o=1,f=l[0];f>=10;f/=10,o++);for(f=l[0]+=u,u=1;f>=10;f/=10,u++);o!=u&&(t.e++,l[0]==E&&(l[0]=1));break}if(l[c]+=u,l[c]!=E)break;l[c--]=0,u=1}for(o=l.length;0===l[--o];l.pop());}t.e>q?t.c=t.e=null:t.en?null!=(t=i[n++]):void 0};return f(e="DECIMAL_PLACES")&&z(t,0,D,2,e)&&(B=0|t),r[e]=B,f(e="ROUNDING_MODE")&&z(t,0,8,2,e)&&(H=0|t),r[e]=H,f(e="EXPONENTIAL_AT")&&(s(t)?z(t[0],-D,0,2,e)&&z(t[1],0,D,2,e)&&(C=0|t[0],j=0|t[1]):z(t,-D,D,2,e)&&(C=-(j=0|(0>t?-t:t)))),r[e]=[C,j],f(e="RANGE")&&(s(t)?z(t[0],-D,-1,2,e)&&z(t[1],1,D,2,e)&&(k=0|t[0],q=0|t[1]):z(t,-D,D,2,e)&&(0|t?k=-(q=0|(0>t?-t:t)):$&&U(2,e+" cannot be zero",t))),r[e]=[k,q],f(e="ERRORS")&&(t===!!t||1===t||0===t?(L=0,z=($=!!t)?A:u):$&&U(2,e+y,t)),r[e]=$,f(e="CRYPTO")&&(t===!!t||1===t||0===t?(V=!(!t||!g||"object"!=typeof g),t&&!V&&$&&U(2,"crypto unavailable",g)):$&&U(2,e+y,t)),r[e]=V,f(e="MODULO_MODE")&&z(t,0,9,2,e)&&(W=0|t),r[e]=W,f(e="POW_PRECISION")&&z(t,0,D,2,e)&&(Y=0|t),r[e]=Y,f(e="FORMAT")&&("object"==typeof t?Z=t:$&&U(2,e+" not an object",t)),r[e]=Z,r},e.max=function(){return S(arguments,M.lt)},e.min=function(){return S(arguments,M.gt)},e.random=function(){var t=9007199254740992,n=Math.random()*t&2097151?function(){return w(Math.random()*t)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(t){var r,i,o,f,u,s=0,c=[],a=new e(P);if(t=null!=t&&z(t,0,D,14)?0|t:B,f=v(t/I),V)if(g&&g.getRandomValues){for(r=g.getRandomValues(new Uint32Array(f*=2));f>s;)u=131072*r[s]+(r[s+1]>>>11),u>=9e15?(i=g.getRandomValues(new Uint32Array(2)),r[s]=i[0],r[s+1]=i[1]):(c.push(u%1e14),s+=2);s=f/2}else if(g&&g.randomBytes){for(r=g.randomBytes(f*=7);f>s;)u=281474976710656*(31&r[s])+1099511627776*r[s+1]+4294967296*r[s+2]+16777216*r[s+3]+(r[s+4]<<16)+(r[s+5]<<8)+r[s+6],u>=9e15?g.randomBytes(7).copy(r,s):(c.push(u%1e14),s+=7);s=f/7}else $&&U(14,"crypto unavailable",g);if(!s)for(;f>s;)u=n(),9e15>u&&(c[s++]=u%1e14);for(f=c[--s],t%=I,f&&t&&(u=_[I-t],c[s]=w(f/u)*u);0===c[s];c.pop(),s--);if(0>s)c=[o=0];else{for(o=-1;0===c[0];c.shift(),o-=I);for(s=1,u=c[0];u>=10;u/=10,s++);I>s&&(o-=I-s)}return a.e=o,a.c=c,a}}(),G=function(){function t(t,e,n){var r,i,o,f,u=0,s=t.length,c=e%T,a=e/T|0;for(t=t.slice();s--;)o=t[s]%T,f=t[s]/T|0,r=a*o+f*c,i=c*o+r%T*T+u,u=(i/n|0)+(r/T|0)+a*f,t[s]=i%n;return u&&t.unshift(u),t}function n(t,e,n,r){var i,o;if(n!=r)o=n>r?1:-1;else for(i=o=0;n>i;i++)if(t[i]!=e[i]){o=t[i]>e[i]?1:-1;break}return o}function r(t,e,n,r){for(var i=0;n--;)t[n]-=i,i=t[n]1;t.shift());}return function(o,f,u,s,c){var a,l,p,h,g,m,d,v,y,b,O,N,x,_,T,D,S,A=o.s==f.s?1:-1,R=o.c,U=f.c;if(!(R&&R[0]&&U&&U[0]))return new e(o.s&&f.s&&(R?!U||R[0]!=U[0]:U)?R&&0==R[0]||!U?0*A:A/0:0/0);for(v=new e(A),y=v.c=[],l=o.e-f.e,A=u+l+1,c||(c=E,l=i(o.e/I)-i(f.e/I),A=A/I|0),p=0;U[p]==(R[p]||0);p++);if(U[p]>(R[p]||0)&&l--,0>A)y.push(1),h=!0;else{for(_=R.length,D=U.length,p=0,A+=2,g=w(c/(U[0]+1)),g>1&&(U=t(U,g,c),R=t(R,g,c),D=U.length,_=R.length),x=D,b=R.slice(0,D),O=b.length;D>O;b[O++]=0);S=U.slice(),S.unshift(0),T=U[0],U[1]>=c/2&&T++;do g=0,a=n(U,b,D,O),0>a?(N=b[0],D!=O&&(N=N*c+(b[1]||0)),g=w(N/T),g>1?(g>=c&&(g=c-1),m=t(U,g,c),d=m.length,O=b.length,a=n(m,b,d,O),1==a&&(g--,r(m,d>D?S:U,d,c))):(0==g&&(a=g=1),m=U.slice()),d=m.length,O>d&&m.unshift(0),r(b,m,O,c),-1==a&&(O=b.length,a=n(U,b,D,O),1>a&&(g++,r(b,O>D?S:U,O,c))),O=b.length):0===a&&(g++,b=[0]),y[p++]=g,a&&b[0]?b[O++]=R[x]||0:(b=[R[x]],O=1);while((x++<_||null!=b[0])&&A--);h=null!=b[0],y[0]||y.shift()}if(c==E){for(p=1,A=y[0];A>=10;A/=10,p++);F(v,u+(v.e=p+l*I-1)+1,s,h)}else v.e=l,v.r=+h;return v}}(),m=function(){var t=/^(-?)0([xbo])(?=\w[\w.]*$)/i,n=/^([^.]+)\.$/,r=/^\.([^.]+)$/,i=/^-?(Infinity|NaN)$/,o=/^\s*\+(?=[\w.])|^\s+|\s+$/g;return function(f,u,s,c){var a,l=s?u:u.replace(o,"");if(i.test(l))f.s=isNaN(l)?null:0>l?-1:1;else{if(!s&&(l=l.replace(t,function(t,e,n){return a="x"==(n=n.toLowerCase())?16:"b"==n?2:8,c&&c!=a?t:e}),c&&(a=c,l=l.replace(n,"$1").replace(r,"0.$1")),u!=l))return new e(l,a);$&&U(L,"not a"+(c?" base "+c:"")+" number",u),f.s=null}f.c=f.e=null,L=0}}(),M.absoluteValue=M.abs=function(){var t=new e(this);return t.s<0&&(t.s=1),t},M.ceil=function(){return F(new e(this),this.e+1,2)},M.comparedTo=M.cmp=function(t,n){return L=1,f(this,new e(t,n))},M.decimalPlaces=M.dp=function(){var t,e,n=this.c;if(!n)return null;if(t=((e=n.length-1)-i(this.e/I))*I,e=n[e])for(;e%10==0;e/=10,t--);return 0>t&&(t=0),t},M.dividedBy=M.div=function(t,n){return L=3,G(this,new e(t,n),B,H)},M.dividedToIntegerBy=M.divToInt=function(t,n){return L=4,G(this,new e(t,n),0,1)},M.equals=M.eq=function(t,n){return L=5,0===f(this,new e(t,n))},M.floor=function(){return F(new e(this),this.e+1,3)},M.greaterThan=M.gt=function(t,n){return L=6,f(this,new e(t,n))>0},M.greaterThanOrEqualTo=M.gte=function(t,n){return L=7,1===(n=f(this,new e(t,n)))||0===n},M.isFinite=function(){return!!this.c},M.isInteger=M.isInt=function(){return!!this.c&&i(this.e/I)>this.c.length-2},M.isNaN=function(){return!this.s},M.isNegative=M.isNeg=function(){return this.s<0},M.isZero=function(){return!!this.c&&0==this.c[0]},M.lessThan=M.lt=function(t,n){return L=8,f(this,new e(t,n))<0},M.lessThanOrEqualTo=M.lte=function(t,n){return L=9,-1===(n=f(this,new e(t,n)))||0===n},M.minus=M.sub=function(t,n){var r,o,f,u,s=this,c=s.s;if(L=10,t=new e(t,n),n=t.s,!c||!n)return new e(0/0);if(c!=n)return t.s=-n,s.plus(t);var a=s.e/I,l=t.e/I,p=s.c,h=t.c;if(!a||!l){if(!p||!h)return p?(t.s=-n,t):new e(h?s:0/0);if(!p[0]||!h[0])return h[0]?(t.s=-n,t):new e(p[0]?s:3==H?-0:0)}if(a=i(a),l=i(l),p=p.slice(),c=a-l){for((u=0>c)?(c=-c,f=p):(l=a,f=h),f.reverse(),n=c;n--;f.push(0));f.reverse()}else for(o=(u=(c=p.length)<(n=h.length))?c:n,c=n=0;o>n;n++)if(p[n]!=h[n]){u=p[n]0)for(;n--;p[r++]=0);for(n=E-1;o>c;){if(p[--o]0?(s=u,r=a):(f=-f,r=c),r.reverse();f--;r.push(0));r.reverse()}for(f=c.length,n=a.length,0>f-n&&(r=a,a=c,c=r,n=f),f=0;n;)f=(c[--n]=c[n]+a[n]+f)/E|0,c[n]%=E;return f&&(c.unshift(f),++s),R(t,c,s)},M.precision=M.sd=function(t){var e,n,r=this,i=r.c;if(null!=t&&t!==!!t&&1!==t&&0!==t&&($&&U(13,"argument"+y,t),t!=!!t&&(t=null)),!i)return null;if(n=i.length-1,e=n*I+1,n=i[n]){for(;n%10==0;n/=10,e--);for(n=i[0];n>=10;n/=10,e++);}return t&&r.e+1>e&&(e=r.e+1),e},M.round=function(t,n){var r=new e(this);return(null==t||z(t,0,D,15))&&F(r,~~t+this.e+1,null!=n&&z(n,0,8,15,b)?0|n:H),r},M.shift=function(t){var n=this;return z(t,-x,x,16,"argument")?n.times("1e"+p(t)):new e(n.c&&n.c[0]&&(-x>t||t>x)?n.s*(0>t?0:1/0):n)},M.squareRoot=M.sqrt=function(){var t,n,r,f,u,s=this,c=s.c,a=s.s,l=s.e,p=B+4,h=new e("0.5");if(1!==a||!c||!c[0])return new e(!a||0>a&&(!c||c[0])?0/0:c?s:1/0);if(a=Math.sqrt(+s),0==a||a==1/0?(n=o(c),(n.length+l)%2==0&&(n+="0"),a=Math.sqrt(n),l=i((l+1)/2)-(0>l||l%2),a==1/0?n="1e"+l:(n=a.toExponential(),n=n.slice(0,n.indexOf("e")+1)+l),r=new e(n)):r=new e(a+""),r.c[0])for(l=r.e,a=l+p,3>a&&(a=0);;)if(u=r,r=h.times(u.plus(G(s,u,p,1))),o(u.c).slice(0,a)===(n=o(r.c)).slice(0,a)){if(r.ea&&(d=b,b=O,O=d,f=a,a=h,h=f),f=a+h,d=[];f--;d.push(0));for(v=E,w=T,f=h;--f>=0;){for(r=0,g=O[f]%w,m=O[f]/w|0,s=a,u=f+s;u>f;)l=b[--s]%w,p=b[s]/w|0,c=m*l+p*g,l=g*l+c%w*w+d[u]+r,r=(l/v|0)+(c/w|0)+m*p,d[u--]=l%v;d[u]=r}return r?++o:d.shift(),R(t,d,o)},M.toDigits=function(t,n){var r=new e(this);return t=null!=t&&z(t,1,D,18,"precision")?0|t:null,n=null!=n&&z(n,0,8,18,b)?0|n:H,t?F(r,t,n):r},M.toExponential=function(t,e){return h(this,null!=t&&z(t,0,D,19)?~~t+1:null,e,19)},M.toFixed=function(t,e){return h(this,null!=t&&z(t,0,D,20)?~~t+this.e+1:null,e,20)},M.toFormat=function(t,e){var n=h(this,null!=t&&z(t,0,D,21)?~~t+this.e+1:null,e,21);if(this.c){var r,i=n.split("."),o=+Z.groupSize,f=+Z.secondaryGroupSize,u=Z.groupSeparator,s=i[0],c=i[1],a=this.s<0,l=a?s.slice(1):s,p=l.length;if(f&&(r=o,o=f,f=r,p-=r),o>0&&p>0){for(r=p%o||o,s=l.substr(0,r);p>r;r+=o)s+=u+l.substr(r,o);f>0&&(s+=u+l.slice(r)),a&&(s="-"+s)}n=c?s+Z.decimalSeparator+((f=+Z.fractionGroupSize)?c.replace(new RegExp("\\d{"+f+"}\\B","g"),"$&"+Z.fractionGroupSeparator):c):s}return n},M.toFraction=function(t){var n,r,i,f,u,s,c,a,l,p=$,h=this,g=h.c,m=new e(P),d=r=new e(P),v=c=new e(P);if(null!=t&&($=!1,s=new e(t),$=p,(!(p=s.isInt())||s.lt(P))&&($&&U(22,"max denominator "+(p?"out of range":"not an integer"),t),t=!p&&s.c&&F(s,s.e+1,1).gte(P)?s:null)),!g)return h.toString();for(l=o(g),f=m.e=l.length-h.e-1,m.c[0]=_[(u=f%I)<0?I+u:u],t=!t||s.cmp(m)>0?f>0?m:d:s,u=q,q=1/0,s=new e(l),c.c[0]=0;a=G(s,m,0,1),i=r.plus(a.times(v)),1!=i.cmp(t);)r=v,v=i,d=c.plus(a.times(i=d)),c=i,m=s.minus(a.times(i=m)),s=i;return i=G(t.minus(r),v,0,1),c=c.plus(i.times(d)),r=r.plus(i.times(v)),c.s=d.s=h.s,f*=2,n=G(d,v,f,H).minus(h).abs().cmp(G(c,r,f,H).minus(h).abs())<1?[d.toString(),v.toString()]:[c.toString(),r.toString()],q=u,n},M.toNumber=function(){var t=this;return+t||(t.s?0*t.s:0/0)},M.toPower=M.pow=function(t){var n,r,i=w(0>t?-t:+t),o=this;if(!z(t,-x,x,23,"exponent")&&(!isFinite(t)||i>x&&(t/=0)||parseFloat(t)!=t&&!(t=0/0)))return new e(Math.pow(+o,t));for(n=Y?v(Y/I+2):0,r=new e(P);;){if(i%2){if(r=r.times(o),!r.c)break;n&&r.c.length>n&&(r.c.length=n)}if(i=w(i/2),!i)break;o=o.times(o),n&&o.c&&o.c.length>n&&(o.c.length=n)}return 0>t&&(r=P.div(r)),n?F(r,Y,H):r},M.toPrecision=function(t,e){return h(this,null!=t&&z(t,1,D,24,"precision")?0|t:null,e,24)},M.toString=function(t){var e,r=this,i=r.s,f=r.e;return null===f?i?(e="Infinity",0>i&&(e="-"+e)):e="NaN":(e=o(r.c),e=null!=t&&z(t,2,64,25,"base")?n(l(e,f),0|t,10,i):C>=f||f>=j?a(e,f):l(e,f),0>i&&r.c[0]&&(e="-"+e)),e},M.truncated=M.trunc=function(){return F(new e(this),this.e+1,1)},M.valueOf=M.toJSON=function(){return this.toString()},null!=t&&e.config(t),e}function i(t){var e=0|t;return t>0||t===e?e:e-1}function o(t){for(var e,n,r=1,i=t.length,o=t[0]+"";i>r;){for(e=t[r++]+"",n=I-e.length;n--;e="0"+e);o+=e}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function f(t,e){var n,r,i=t.c,o=e.c,f=t.s,u=e.s,s=t.e,c=e.e;if(!f||!u)return null;if(n=i&&!i[0],r=o&&!o[0],n||r)return n?r?0:-u:f;if(f!=u)return f;if(n=0>f,r=s==c,!i||!o)return r?0:!i^n?1:-1;if(!r)return s>c^n?1:-1;for(u=(s=i.length)<(c=o.length)?s:c,f=0;u>f;f++)if(i[f]!=o[f])return i[f]>o[f]^n?1:-1;return s==c?0:s>c^n?1:-1}function u(t,e,n){return(t=p(t))>=e&&n>=t}function s(t){return"[object Array]"==Object.prototype.toString.call(t)}function c(t,e,n){for(var r,i,o=[0],f=0,u=t.length;u>f;){for(i=o.length;i--;o[i]*=e);for(o[r=0]+=N.indexOf(t.charAt(f++));rn-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/n|0,o[r]%=n)}return o.reverse()}function a(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(0>e?"e":"e+")+e}function l(t,e){var n,r;if(0>e){for(r="0.";++e;r+="0");t=r+t}else if(n=t.length,++e>n){for(r="0",e-=n;--e;r+="0");t+=r}else n>e&&(t=t.slice(0,e)+"."+t.slice(e));return t}function p(t){return t=parseFloat(t),0>t?v(t):w(t)}var h,g,m,d=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,v=Math.ceil,w=Math.floor,y=" not a boolean or binary digit",b="rounding mode",O="number type has more than 15 significant digits",N="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",E=1e14,I=14,x=9007199254740991,_=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],T=1e7,D=1e9;if(h=r(),"function"==typeof define&&define.amd)define(function(){return h});else if("undefined"!=typeof e&&e.exports){if(e.exports=h,!g)try{g=t("crypto")}catch(S){}}else n.BigNumber=h}(this)},{crypto:1}],natspec:[function(t,e){var n=t("./node_modules/ethereum.js/lib/abi.js"),r=function(){var t=function(t,e){Object.keys(t).forEach(function(n){e[n]=t[n]})},e=function(t){return Object.keys(t).reduce(function(t,e){return t+"var "+e+" = context['"+e+"'];\n"},"")},r=function(t,e){return t.filter(function(t){return t.name===e})[0]},i=function(t,e){var r=n.formatOutput(t.inputs,"0x"+e.params[0].data.slice(10));return t.inputs.reduce(function(t,e,n){return t[e.name]=r[n],t},{})},o=function(t,e){var n,r="",i=/\`(?:\\.|[^`\\])*\`/gim,o=0;try{for(;null!==(n=i.exec(t));){var f=i.lastIndex-n[0].length,u=n[0].slice(1,n[0].length-1);r+=t.slice(o,f);var s=e(u);r+=s,o=i.lastIndex}r+=t.slice(o)}catch(c){throw new Error("Natspec evaluation failed, wrong input params")}return r},f=function(n,f){var u={};if(f)try{var s=r(f.abi,f.method),c=i(s,f.transaction);t(c,u)}catch(a){throw new Error("Natspec evaluation failed, method does not exist")}var l=e(u),p=o(n,function(t){var e=new Function("context",l+"return "+t+";");return e(u).toString()});return p},u=function(t,e){try{return f(t,e)}catch(n){return n.message}};return{evaluateExpression:f,evaluateExpressionSafe:u}}();e.exports=r},{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); +//# sourceMappingURL=natspec.js.map \ No newline at end of file diff --git a/libnatspec/natspecjs/example/example.html b/libnatspec/natspecjs/example/example.html new file mode 100644 index 000000000..26388bee3 --- /dev/null +++ b/libnatspec/natspecjs/example/example.html @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/libnatspec/natspecjs/natspec.js b/libnatspec/natspecjs/natspec.js new file mode 100644 index 000000000..277cad2aa --- /dev/null +++ b/libnatspec/natspecjs/natspec.js @@ -0,0 +1,186 @@ +/* + This file is part of natspec.js. + + natspec.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + natspec.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with natspec.js. If not, see . +*/ +/** @file natspec.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var abi = require('./node_modules/ethereum.js/lib/abi.js'); + +/** + * This object should be used to evaluate natspec expression + * It has one method evaluateExpression which shoul be used + */ +var natspec = (function () { + /** + * Helper method + * Should be called to copy values from object to global context + * + * @method copyToContext + * @param {Object} object from which we want to copy properties + * @param {Object} object to which we copy + */ + var copyToContext = function (obj, context) { + Object.keys(obj).forEach(function (key) { + context[key] = obj[key]; + }); + } + + /** + * Should be used to generate codes, which will be evaluated + * + * @method generateCode + * @param {Object} object from which code will be generated + * @return {String} javascript code which is used to initalized variables + */ + var generateCode = function (obj) { + return Object.keys(obj).reduce(function (acc, key) { + return acc + "var " + key + " = context['" + key + "'];\n"; + }, ""); + }; + + /** + * Helper method + * Should be called to get method with given name from the abi + * + * @method getMethodWithName + * @param {Array} contract's abi + * @param {String} name of the method that we are looking for + * @return {Object} abi for method with name + */ + var getMethodWithName = function(abi, name) { + return abi.filter(function (method) { + return method.name === name; + })[0]; + }; + + /** + * Should be used to get all contract method input variables + * + * @method getMethodInputParams + * @param {Object} abi for certain method + * @param {Object} transaction object + * @return {Object} object with all contract's method input variables + */ + var getMethodInputParams = function (method, transaction) { + // do it with output formatter (cause we have to decode) + var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); + + return method.inputs.reduce(function (acc, current, index) { + acc[current.name] = params[index]; + return acc; + }, {}); + }; + + /** + * Should be called when we want to evaluate natspec expression + * Replaces all natspec 'subexpressions' with evaluated value + * + * @method mapExpressionToEvaluate + * @param {String} expression to evaluate + * @param {Function} callback which is called to evaluate te expression + * @return {String} evaluated expression + */ + var mapExpressionsToEvaluate = function (expression, cb) { + var evaluatedExpression = ""; + + // match everything in `` quotes + var pattern = /\`(?:\\.|[^`\\])*\`/gim + var match; + var lastIndex = 0; + try { + while ((match = pattern.exec(expression)) !== null) { + var startIndex = pattern.lastIndex - match[0].length; + var toEval = match[0].slice(1, match[0].length - 1); + evaluatedExpression += expression.slice(lastIndex, startIndex); + var evaluatedPart = cb(toEval); + evaluatedExpression += evaluatedPart; + lastIndex = pattern.lastIndex; + } + + evaluatedExpression += expression.slice(lastIndex); + } + catch (err) { + throw new Error("Natspec evaluation failed, wrong input params"); + } + + return evaluatedExpression; + }; + + /** + * Should be called to evaluate single expression + * Is internally using javascript's 'eval' method + * + * @method evaluateExpression + * @param {String} expression which should be evaluated + * @param {Object} [call] object containing contract abi, transaction, called method + * @return {String} evaluated expression + * @throws exception if method is not found or we are trying to evaluate input params that does not exists + */ + var evaluateExpression = function (expression, call) { + //var self = this; + var context = {}; + + if (!!call) { + try { + var method = getMethodWithName(call.abi, call.method); + var params = getMethodInputParams(method, call.transaction); + copyToContext(params, context); + } + catch (err) { + throw new Error("Natspec evaluation failed, method does not exist"); + } + } + + var code = generateCode(context); + + var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) { + var fn = new Function("context", code + "return " + toEval + ";"); + return fn(context).toString(); + }); + + return evaluatedExpression; + }; + + /** + * Safe version of evaluateExpression + * Instead of throwing an exception it returns it as a string + * + * @method evaluateExpressionSafe + * @param {String} expression which should be evaluated + * @param {Object} [call] object containing contract abi, transaction, called method + * @return {String} evaluated expression + */ + var evaluateExpressionSafe = function (expression, call) { + try { + return evaluateExpression(expression, call); + } + catch (err) { + return err.message; + } + }; + + return { + evaluateExpression: evaluateExpression, + evaluateExpressionSafe: evaluateExpressionSafe + }; + +})(); + +module.exports = natspec; + diff --git a/libnatspec/natspecjs/package.json b/libnatspec/natspecjs/package.json new file mode 100644 index 000000000..ed2f252db --- /dev/null +++ b/libnatspec/natspecjs/package.json @@ -0,0 +1,24 @@ +{ + "name": "natspec.js", + "version": "0.0.1", + "description": "Javascript Library used to evaluate natspec expressions", + "main": "natspec.js", + "scripts": { + "build": "cd dist && browserify -r ../natspec.js:natspec -i crypto -o natspec.js && uglifyjs natspec.js --source-map natspec.js.map -c -m -o natspec.min.js", + "test": "mocha", + "test-coveralls": "istanbul cover _mocha -- -R spec && cat coverage/lcov.info | coveralls --verbose" + }, + "author": "", + "dependencies": { + "ethereum.js": "ethereum/ethereum.js#master" + }, + "devDependencies": { + "browserify": "^9.0.3", + "chai": "^2.1.0", + "coveralls": "^2.11.2", + "istanbul": "^0.3.6", + "mocha": "^2.1.0", + "uglify-js": "^2.4.16" + }, + "license": "LGPL-3.0" +} diff --git a/libnatspec/natspecjs/test/test.js b/libnatspec/natspecjs/test/test.js new file mode 100644 index 000000000..59f46cdab --- /dev/null +++ b/libnatspec/natspecjs/test/test.js @@ -0,0 +1,149 @@ +var chai = require('chai'); +var natspec = require('../natspec.js'); +var assert = chai.assert; + +describe('natspec', function () { + it('should evaluate simple expression', function () { + // given + var expression = "`x = 1` + `y = 2` will be equal `x + y`"; + + // when + var result = natspec.evaluateExpression(expression); + var result2 = natspec.evaluateExpressionSafe(expression); + + // then + assert.equal(result, "1 + 2 will be equal 3"); + assert.equal(result2, "1 + 2 will be equal 3"); + }); + + it('should evalute expression using input params', function () { + //given + var expression = "Will multiply `a` by 7 and return `a * 7`."; + var method = 'multiply'; + var abi = [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }]; + + var transaction = { + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + }; + + var call = { + method: method, + abi: abi, + transaction: transaction + }; + + // when + var result = natspec.evaluateExpression(expression, call); + var result2 = natspec.evaluateExpressionSafe(expression, call); + + // then + assert.equal(result, "Will multiply 122 by 7 and return 854."); + assert.equal(result2, "Will multiply 122 by 7 and return 854."); + }); + + it('should evalute expression using input params', function () { + //given + var expression = "Will multiply `a` by 7 and return `a * 7`."; + var method = 'multiply'; + var abi = [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "b", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }]; + + var transaction = { + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + }; + + var call = { + method: method, + abi: abi, + transaction: transaction + }; + + // when + var exceptionThrow = function () { natspec.evaluateExpression(expression, call);} + var result = natspec.evaluateExpressionSafe(expression, call); + + // then + assert.equal(result, "Natspec evaluation failed, wrong input params"); + assert.throws(exceptionThrow, "Natspec evaluation failed, wrong input params"); + }); + + it('should evalute expression using input params', function () { + //given + var expression = "Will multiply `a` by 7 and return `a * 7`."; + var method = 'multiply2'; + var abi = [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }]; + + var transaction = { + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + }; + + var call = { + method: method, + abi: abi, + transaction: transaction + }; + + // when + var exceptionThrow = function () { natspec.evaluateExpression(expression, call);} + var result = natspec.evaluateExpressionSafe(expression, call); + + // then + assert.equal(result, "Natspec evaluation failed, method does not exist"); + assert.throws(exceptionThrow, "Natspec evaluation failed, method does not exist"); + }); +}); + + diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index 5ef70bbc3..dccc130cd 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -45,7 +45,7 @@ void Capability::disable(std::string const& _problem) RLPStream& Capability::prep(RLPStream& _s, unsigned _id, unsigned _args) { - return Session::prep(_s).appendList(_args + 1).append(_id + m_idOffset); + return _s.appendRaw(bytes(1, _id + m_idOffset)).appendList(_args); } void Capability::sealAndSend(RLPStream& _s) @@ -53,16 +53,6 @@ void Capability::sealAndSend(RLPStream& _s) m_session->sealAndSend(_s); } -void Capability::send(bytesConstRef _msg) -{ - m_session->send(_msg); -} - -void Capability::send(bytes&& _msg) -{ - m_session->send(move(_msg)); -} - void Capability::addRating(unsigned _r) { m_session->addRating(_r); diff --git a/libp2p/Capability.h b/libp2p/Capability.h index 04b116aa8..ad8127bb5 100644 --- a/libp2p/Capability.h +++ b/libp2p/Capability.h @@ -52,9 +52,6 @@ protected: RLPStream& prep(RLPStream& _s, unsigned _id, unsigned _args = 0); void sealAndSend(RLPStream& _s); - void send(bytes&& _msg); - void send(bytesConstRef _msg); - void addRating(unsigned _r); private: diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index ed0ee653e..19df5ee2e 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -24,9 +24,8 @@ #include #include #include - +#include #include - #include #include #include @@ -36,6 +35,7 @@ #include "Common.h" #include "Capability.h" #include "UPnP.h" +#include "RLPxHandshake.h" #include "Host.h" using namespace std; using namespace dev; @@ -121,6 +121,23 @@ void Host::doneWorking() for (auto const& h: m_capabilities) h.second->onStopping(); + // disconnect pending handshake, before peers, as a handshake may create a peer + for (unsigned n = 0;; n = 0) + { + { + Guard l(x_connecting); + for (auto i: m_connecting) + if (auto h = i.lock()) + { + h->cancel(); + n++; + } + } + if (!n) + break; + m_ioService.poll(); + } + // disconnect peers for (unsigned n = 0;; n = 0) { @@ -157,32 +174,65 @@ unsigned Host::protocolVersion() const return 3; } -void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) +void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) { + /// Get or create Peer + shared_ptr p; + p = m_peers[_id]; + if (!p) + { + p.reset(new Peer()); // this maybe redundant + p->id = _id; + } + p->m_lastDisconnect = NoDisconnect; + if (p->isOffline()) + p->m_lastConnected = std::chrono::system_clock::now(); + p->m_failedAttempts = 0; + p->endpoint.tcp.address(_endpoint.address()); + + auto protocolVersion = _rlp[0].toInt(); + auto clientVersion = _rlp[1].toString(); + auto caps = _rlp[2].toVector(); + auto listenPort = _rlp[3].toInt(); + + // clang error (previously: ... << hex << caps ...) + // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" + stringstream capslog; + for (auto cap: caps) + capslog << "(" << cap.first << "," << dec << cap.second << ")"; + clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort; + + // create session so disconnects are managed + auto ps = make_shared(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet(), 0, map()})); + if (protocolVersion != this->protocolVersion()) + { + ps->disconnect(IncompatibleProtocol); + return; + } + { - clog(NetNote) << "p2p.host.peer.register" << _s->m_peer->id.abridged(); - StructuredLogger::p2pConnected( - _s->m_peer->id.abridged(), - _s->m_peer->peerEndpoint(), - _s->m_peer->m_lastConnected, - _s->m_info.clientVersion, - peerCount() - ); RecursiveGuard l(x_sessions); - // TODO: temporary loose-coupling; if m_peers already has peer, - // it is same as _s->m_peer. (fixing next PR) - if (!m_peers.count(_s->m_peer->id)) - m_peers[_s->m_peer->id] = _s->m_peer; - m_sessions[_s->m_peer->id] = _s; + if (m_sessions.count(_id) && !!m_sessions[_id].lock()) + if (auto s = m_sessions[_id].lock()) + if(s->isConnected()) + { + // Already connected. + clog(NetWarn) << "Session already exists for peer with id" << _id.abridged(); + ps->disconnect(DuplicatePeer); + return; + } + m_sessions[_id] = ps; } - + ps->start(); unsigned o = (unsigned)UserPacket; - for (auto const& i: _caps) + for (auto const& i: caps) if (haveCapability(i)) { - _s->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(_s.get(), o)); + ps->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(ps.get(), o)); o += m_capabilities[i]->messageCount(); } + clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); + StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount()); } void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) @@ -235,19 +285,6 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) } } -void Host::seal(bytes& _b) -{ - _b[0] = 0x22; - _b[1] = 0x40; - _b[2] = 0x08; - _b[3] = 0x91; - uint32_t len = (uint32_t)_b.size() - 8; - _b[4] = (len >> 24) & 0xff; - _b[5] = (len >> 16) & 0xff; - _b[6] = (len >> 8) & 0xff; - _b[7] = len & 0xff; -} - void Host::determinePublic(string const& _publicAddress, bool _upnp) { m_peerAddresses.clear(); @@ -320,34 +357,19 @@ void Host::runAcceptor() clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_tcpPublic << ")"; m_accepting = true; - // socket is created outside of acceptor-callback - // An allocated socket is necessary as asio can use the socket - // until the callback succeeds or fails. - // - // Until callback succeeds or fails, we can't dealloc it. - // - // Callback is guaranteed to be called via asio or when - // m_tcp4Acceptor->stop() is called by Host. - // - // All exceptions are caught so they don't halt asio and so the - // socket is deleted. - // - // It's possible for an accepted connection to return an error in which - // case the socket may be open and must be closed to prevent asio from - // processing socket events after socket is deallocated. - - bi::tcp::socket *s = new bi::tcp::socket(m_ioService); - m_tcp4Acceptor.async_accept(*s, [=](boost::system::error_code ec) + auto socket = make_shared(new bi::tcp::socket(m_ioService)); + m_tcp4Acceptor.async_accept(socket->ref(), [=](boost::system::error_code ec) { - // if no error code, doHandshake takes ownership + // if no error code bool success = false; if (!ec) { try { - // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - doHandshake(s, NodeId()); + auto handshake = make_shared(this, socket); + m_connecting.push_back(handshake); + handshake->start(); success = true; } catch (Exception const& _e) @@ -360,41 +382,16 @@ void Host::runAcceptor() } } - // asio doesn't close socket on error - if (!success && s->is_open()) - { - boost::system::error_code ec; - s->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - s->close(); - } - + if (!success) + socket->ref().close(); + m_accepting = false; - delete s; - if (ec.value() < 1) runAcceptor(); }); } } -void Host::doHandshake(bi::tcp::socket* _socket, NodeId _nodeId) -{ - try { - clog(NetConnect) << "Accepting connection for " << _socket->remote_endpoint(); - } catch (...){} - - shared_ptr p; - if (_nodeId) - p = m_peers[_nodeId]; - - if (!p) - p.reset(new Peer()); - p->endpoint.tcp.address(_socket->remote_endpoint().address()); - - auto ps = std::make_shared(this, std::move(*_socket), p); - ps->start(); -} - string Host::pocHost() { vector strs; @@ -440,15 +437,14 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short void Host::connect(std::shared_ptr const& _p) { - for (unsigned i = 0; i < 200; i++) - if (isWorking() && !m_run) - this_thread::sleep_for(chrono::milliseconds(50)); if (!m_run) return; + _p->m_lastAttempted = std::chrono::system_clock::now(); + if (havePeerSession(_p->id)) { - clog(NetWarn) << "Aborted connect. Node already connected."; + clog(NetConnect) << "Aborted connect. Node already connected."; return; } @@ -469,8 +465,8 @@ void Host::connect(std::shared_ptr const& _p) } clog(NetConnect) << "Attempting connection to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "from" << id().abridged(); - bi::tcp::socket* s = new bi::tcp::socket(m_ioService); - s->async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec) + auto socket = make_shared(new bi::tcp::socket(m_ioService)); + socket->ref().async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec) { if (ec) { @@ -480,16 +476,15 @@ void Host::connect(std::shared_ptr const& _p) } else { - clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - _p->m_lastDisconnect = NoDisconnect; - _p->m_lastConnected = std::chrono::system_clock::now(); - _p->m_failedAttempts = 0; - - auto ps = make_shared(this, std::move(*s), _p); - ps->start(); - + clog(NetConnect) << "Connecting to" << _p->id.abridged() << "@" << _p->peerEndpoint(); + auto handshake = make_shared(this, socket, _p->id); + { + Guard l(x_connecting); + m_connecting.push_back(handshake); + } + handshake->start(); } - delete s; + Guard l(x_pendingNodeConns); m_pendingPeerConns.erase(nptr); }); @@ -538,26 +533,42 @@ void Host::run(boost::system::error_code const&) m_nodeTable->processEvents(); + // cleanup zombies + { + Guard l(x_connecting); + m_connecting.remove_if([](std::weak_ptr h){ return h.lock(); }); + } + for (auto p: m_sessions) if (auto pp = p.second.lock()) pp->serviceNodesRequest(); keepAlivePeers(); - disconnectLatePeers(); - - auto c = peerCount(); - if (m_idealPeerCount && !c) - for (auto p: m_peers) - if (p.second->shouldReconnect()) - { - // TODO p2p: fixme - p.second->m_lastAttempted = std::chrono::system_clock::now(); - connect(p.second); + + // At this time peers will be disconnected based on natural TCP timeout. + // disconnectLatePeers needs to be updated for the assumption that Session + // is always live and to ensure reputation and fallback timers are properly + // updated. // disconnectLatePeers(); + + auto openSlots = m_idealPeerCount - peerCount(); + if (openSlots > 0) + { + list> toConnect; + { + RecursiveGuard l(x_sessions); + for (auto p: m_peers) + if (p.second->shouldReconnect()) + toConnect.push_back(p.second); + } + + for (auto p: toConnect) + if (openSlots--) + connect(p); + else break; - } - - if (c < m_idealPeerCount) + m_nodeTable->discover(); + } auto runcb = [this](boost::system::error_code const& error) { run(error); }; m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); diff --git a/libp2p/Host.h b/libp2p/Host.h index ce4dfa50c..219316c58 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -35,10 +35,12 @@ #include #include #include +#include #include "NodeTable.h" #include "HostCapability.h" #include "Network.h" #include "Peer.h" +#include "RLPxFrameIO.h" #include "Common.h" namespace ba = boost::asio; namespace bi = ba::ip; @@ -68,19 +70,16 @@ private: * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. * - * @todo exceptions when nodeTable not set (prior to start) - * @todo onNodeTableEvent: move peer-connection logic into ensurePeers - * @todo handshake: gracefully disconnect peer if peer already connected - * @todo abstract socket -> IPConnection + * @todo cleanup startPeerSession * @todo determinePublic: ipv6, udp * @todo handle conflict if addNode/requireNode called and Node already exists w/conflicting tcp or udp port - * @todo write host identifier to disk w/nodes * @todo per-session keepalive/ping instead of broadcast; set ping-timeout via median-latency - * @todo configuration-management (NetworkPrefs+Keys+Topology) */ class Host: public Worker { friend class HostNodeTableHandler; + friend class RLPXHandshake; + friend class Session; friend class HostCapabilityFace; @@ -114,8 +113,6 @@ public: CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } } - bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } - void addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPort, unsigned short _udpPort); /// Set ideal number of peers. @@ -153,7 +150,8 @@ public: NodeId id() const { return m_alias.pub(); } - void registerPeer(std::shared_ptr _s, CapDescs const& _caps); + /// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error. + void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); @@ -162,6 +160,8 @@ protected: void restoreNetwork(bytesConstRef _b); private: + bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } + /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); @@ -176,11 +176,6 @@ private: /// Called only from startedWorking(). void runAcceptor(); - /// Handler for verifying handshake siganture before creating session. _nodeId is passed for outbound connections. If successful, socket is moved to Session via std::move. - void doHandshake(bi::tcp::socket* _socket, NodeId _nodeId = NodeId()); - - void seal(bytes& _b); - /// Called by Worker. Not thread-safe; to be called only by worker. virtual void startedWorking(); /// Called by startedWorking. Not thread-safe; to be called only be Worker. @@ -229,6 +224,9 @@ private: /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. mutable std::map> m_sessions; mutable RecursiveMutex x_sessions; + + std::list> m_connecting; ///< Pending connections. + Mutex x_connecting; ///< Mutex for m_connecting. unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. diff --git a/libp2p/HostCapability.cpp b/libp2p/HostCapability.cpp index 9437cd45c..b2acdcd1b 100644 --- a/libp2p/HostCapability.cpp +++ b/libp2p/HostCapability.cpp @@ -27,11 +27,6 @@ using namespace std; using namespace dev; using namespace dev::p2p; -void HostCapabilityFace::seal(bytes& _b) -{ - m_host->seal(_b); -} - std::vector,std::shared_ptr>> HostCapabilityFace::peerSessions() const { RecursiveGuard l(m_host->x_sessions); diff --git a/libp2p/HostCapability.h b/libp2p/HostCapability.h index 9122ca1fa..93086b1c9 100644 --- a/libp2p/HostCapability.h +++ b/libp2p/HostCapability.h @@ -57,8 +57,6 @@ protected: virtual void onStarting() {} virtual void onStopping() {} - void seal(bytes& _b); - private: Host* m_host = nullptr; }; diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 704e5c2b4..8774b6578 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -55,6 +55,8 @@ class Peer: public Node friend class Session; /// Allows Session to update score and rating. friend class Host; /// For Host: saveNetwork(), restoreNetwork() + friend class RLPXHandshake; + public: bool isOffline() const { return !m_session.lock(); } diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp new file mode 100644 index 000000000..cb768bfeb --- /dev/null +++ b/libp2p/RLPxFrameIO.cpp @@ -0,0 +1,209 @@ +/* + 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 RLPXFrameIO.cpp + * @author Alex Leverington + * @date 2015 + */ + +#include "Host.h" +#include "Session.h" +#include "Peer.h" +#include "RLPxHandshake.h" +#include "RLPxFrameIO.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace CryptoPP; + +RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) +{ + // we need: + // originated? + // Secret == output of ecdhe agreement + // authCipher + // ackCipher + + bytes keyMaterialBytes(64); + bytesRef keyMaterial(&keyMaterialBytes); + + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + _init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared); + ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); + h512 nonceMaterial; + h256 const& leftNonce = _init.m_originated ? _init.m_remoteNonce : _init.m_nonce; + h256 const& rightNonce = _init.m_originated ? _init.m_nonce : _init.m_remoteNonce; + leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); + rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); + auto outRef(keyMaterial.cropped(h256::size, h256::size)); + sha3(nonceMaterial.ref(), outRef); // output h(nonces) + + sha3(keyMaterial, outRef); // output shared-secret + // token: sha3(outRef, bytesRef(&token)); -> m_host (to be saved to disk) + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + m_frameEncKey.resize(h256::size); + memcpy(m_frameEncKey.data(), outRef.data(), h256::size); + m_frameDecKey.resize(h256::size); + memcpy(m_frameDecKey.data(), outRef.data(), h256::size); + h128 iv; + m_frameEnc.SetKeyWithIV(m_frameEncKey, h256::size, iv.data()); + m_frameDec.SetKeyWithIV(m_frameDecKey, h256::size, iv.data()); + + // mac-secret = sha3(ecdhe-shared-secret || aes-secret) + sha3(keyMaterial, outRef); // output mac-secret + m_macEncKey.resize(h256::size); + memcpy(m_macEncKey.data(), outRef.data(), h256::size); + m_macEnc.SetKey(m_macEncKey, h256::size); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + (*(h256*)outRef.data() ^ _init.m_remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = _init.m_originated ? _init.m_authCipher : _init.m_ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); + + // recover mac-secret by re-xoring remoteNonce + (*(h256*)keyMaterial.data() ^ _init.m_remoteNonce ^ _init.m_nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = _init.m_originated ? _init.m_ackCipher : _init.m_authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); +} + +void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) +{ + // _packet = type || rlpList() + + RLPStream header; + uint32_t len = (uint32_t)_packet.size(); + header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)})); + // zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize). + header.appendRaw(bytes({0xc2,0x80,0x80})); + + // TODO: SECURITY check that header is <= 16 bytes + + bytes headerWithMac(h256::size); + bytesConstRef(&header.out()).copyTo(bytesRef(&headerWithMac)); + m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16); + updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16)); + egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size)); + + auto padding = (16 - (_packet.size() % 16)) % 16; + o_bytes.swap(headerWithMac); + o_bytes.resize(32 + _packet.size() + padding + h128::size); + bytesRef packetRef(o_bytes.data() + 32, _packet.size()); + m_frameEnc.ProcessData(packetRef.data(), _packet.data(), _packet.size()); + bytesRef paddingRef(o_bytes.data() + 32 + _packet.size(), padding); + if (padding) + m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding); + bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding); + updateEgressMACWithFrame(packetWithPaddingRef); + bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size); + egressDigest().ref().copyTo(macRef); +} + +bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) +{ + asserts(io.size() == h256::size); + updateIngressMACWithHeader(io); + bytesConstRef macRef = io.cropped(h128::size, h128::size); + h128 expected = ingressDigest(); + if (*(h128*)macRef.data() != expected) + return false; + m_frameDec.ProcessData(io.data(), io.data(), h128::size); + return true; +} + +bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) +{ + bytesRef cipherText(io.cropped(0, io.size() - h128::size)); + updateIngressMACWithFrame(cipherText); + bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size); + if (*(h128*)frameMac.data() != ingressDigest()) + return false; + m_frameDec.ProcessData(io.data(), io.data(), io.size() - h128::size); + return true; +} + +h128 RLPXFrameIO::egressDigest() +{ + SHA3_256 h(m_egressMac); + h128 digest; + h.TruncatedFinal(digest.data(), h128::size); + return move(digest); +} + +h128 RLPXFrameIO::ingressDigest() +{ + SHA3_256 h(m_ingressMac); + h128 digest; + h.TruncatedFinal(digest.data(), h128::size); + return move(digest); +} + +void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) +{ + updateMAC(m_egressMac, _headerCipher.cropped(0, 16)); +} + +void RLPXFrameIO::updateEgressMACWithFrame(bytesConstRef _cipher) +{ + m_egressMac.Update(_cipher.data(), _cipher.size()); + updateMAC(m_egressMac); +} + +void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) +{ + updateMAC(m_ingressMac, _headerCipher.cropped(0, 16)); +} + +void RLPXFrameIO::updateIngressMACWithFrame(bytesConstRef _cipher) +{ + m_ingressMac.Update(_cipher.data(), _cipher.size()); + updateMAC(m_ingressMac); +} + +void RLPXFrameIO::updateMAC(SHA3_256& _mac, bytesConstRef _seed) +{ + if (_seed.size() && _seed.size() != h128::size) + asserts(false); + + SHA3_256 prevDigest(_mac); + h128 encDigest(h128::size); + prevDigest.TruncatedFinal(encDigest.data(), h128::size); + h128 prevDigestOut = encDigest; + + { + Guard l(x_macEnc); + m_macEnc.ProcessData(encDigest.data(), encDigest.data(), 16); + } + if (_seed.size()) + encDigest ^= *(h128*)_seed.data(); + else + encDigest ^= *(h128*)prevDigestOut.data(); + + // update mac for final digest + _mac.Update(encDigest.data(), h128::size); +} diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h new file mode 100644 index 000000000..0f0504e48 --- /dev/null +++ b/libp2p/RLPxFrameIO.h @@ -0,0 +1,133 @@ +/* + 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 RLPXFrameIO.h + * @author Alex Leverington + * @date 2015 + */ + + +#pragma once + +#include +#include +#include +#include +#include +#include "Common.h" +namespace ba = boost::asio; +namespace bi = boost::asio::ip; + +namespace dev +{ +namespace p2p +{ + +class RLPXHandshake; + +/** + * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. + * Managed (via shared_ptr) socket for use by RLPXHandshake and RLPXFrameIO. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + * * an instance method must not be called concurrently + * * a writeSingleFramePacket can be called concurrent to authAndDecryptHeader OR authAndDecryptFrame + */ +class RLPXSocket: public std::enable_shared_from_this +{ +public: + RLPXSocket(bi::tcp::socket* _socket): m_socket(std::move(*_socket)) {} + ~RLPXSocket() { close(); } + + bool isConnected() const { return m_socket.is_open(); } + void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} } + bi::tcp::endpoint remoteEndpoint() { try { return m_socket.remote_endpoint(); } catch (...){ return bi::tcp::endpoint(); } } + bi::tcp::socket& ref() { return m_socket; } + +protected: + bi::tcp::socket m_socket; +}; + +/** + * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + */ +class RLPXFrameIO +{ + friend class Session; +public: + /// Constructor. + /// Requires instance of RLPXHandshake which has completed first two phases of handshake. + RLPXFrameIO(RLPXHandshake const& _init); + ~RLPXFrameIO() {} + + /// Encrypt _packet as RLPx frame. + void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); + + /// Authenticate and decrypt header in-place. + bool authAndDecryptHeader(bytesRef io_cipherWithMac); + + /// Authenticate and decrypt frame in-place. + bool authAndDecryptFrame(bytesRef io_cipherWithMac); + + /// Return first 16 bytes of current digest from egress mac. + h128 egressDigest(); + + /// Return first 16 bytes of current digest from ingress mac. + h128 ingressDigest(); + +protected: + /// Update state of egress MAC with frame header. + void updateEgressMACWithHeader(bytesConstRef _headerCipher); + + /// Update state of egress MAC with frame. + void updateEgressMACWithFrame(bytesConstRef _cipher); + + /// Update state of ingress MAC with frame header. + void updateIngressMACWithHeader(bytesConstRef _headerCipher); + + /// Update state of ingress MAC with frame. + void updateIngressMACWithFrame(bytesConstRef _cipher); + + bi::tcp::socket& socket() { return m_socket->ref(); } + +private: + /// Update state of _mac. + void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); + + CryptoPP::SecByteBlock m_frameEncKey; ///< Key for m_frameEnc + CryptoPP::CTR_Mode::Encryption m_frameEnc; ///< Encoder for egress plaintext. + + CryptoPP::SecByteBlock m_frameDecKey; ///< Key for m_frameDec + CryptoPP::CTR_Mode::Encryption m_frameDec; ///< Decoder for egress plaintext. + + CryptoPP::SecByteBlock m_macEncKey; /// Key for m_macEnd + CryptoPP::ECB_Mode::Encryption m_macEnc; /// One-way coder used by updateMAC for ingress and egress MAC updates. + Mutex x_macEnc; /// Mutex + + CryptoPP::SHA3_256 m_egressMac; ///< State of MAC for egress ciphertext. + CryptoPP::SHA3_256 m_ingressMac; ///< State of MAC for ingress ciphertext. + + std::shared_ptr m_socket; +}; + +} +} \ No newline at end of file diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp new file mode 100644 index 000000000..4f5c70802 --- /dev/null +++ b/libp2p/RLPxHandshake.cpp @@ -0,0 +1,266 @@ +/* + 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 RLPXHandshake.cpp + * @author Alex Leverington + * @date 2015 + */ + +#include "Host.h" +#include "Session.h" +#include "Peer.h" +#include "RLPxHandshake.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace CryptoPP; + +void RLPXHandshake::writeAuth() +{ + clog(NetConnect) << "p2p.connect.egress sending auth to " << m_socket->remoteEndpoint(); + m_auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); + bytesRef sig(&m_auth[0], Signature::size); + bytesRef hepubk(&m_auth[Signature::size], h256::size); + bytesRef pubk(&m_auth[Signature::size + h256::size], Public::size); + bytesRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size); + + // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) + Secret staticShared; + crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, staticShared); + sign(m_ecdhe.seckey(), staticShared ^ m_nonce).ref().copyTo(sig); + sha3(m_ecdhe.pubkey().ref(), hepubk); + m_host->m_alias.pub().ref().copyTo(pubk); + m_nonce.ref().copyTo(nonce); + m_auth[m_auth.size() - 1] = 0x0; + encryptECIES(m_remote, &m_auth, m_authCipher); + + auto self(shared_from_this()); + ba::async_write(m_socket->ref(), ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t) + { + transition(ec); + }); +} + +void RLPXHandshake::writeAck() +{ + clog(NetConnect) << "p2p.connect.ingress sending ack to " << m_socket->remoteEndpoint(); + m_ack.resize(Public::size + h256::size + 1); + bytesRef epubk(&m_ack[0], Public::size); + bytesRef nonce(&m_ack[Public::size], h256::size); + m_ecdhe.pubkey().ref().copyTo(epubk); + m_nonce.ref().copyTo(nonce); + m_ack[m_ack.size() - 1] = 0x0; + encryptECIES(m_remote, &m_ack, m_ackCipher); + + auto self(shared_from_this()); + ba::async_write(m_socket->ref(), ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t) + { + transition(ec); + }); +} + +void RLPXHandshake::readAuth() +{ + clog(NetConnect) << "p2p.connect.ingress recving auth from " << m_socket->remoteEndpoint(); + m_authCipher.resize(307); + auto self(shared_from_this()); + ba::async_read(m_socket->ref(), ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) + { + if (ec) + transition(ec); + else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_authCipher), m_auth)) + { + bytesConstRef sig(&m_auth[0], Signature::size); + bytesConstRef hepubk(&m_auth[Signature::size], h256::size); + bytesConstRef pubk(&m_auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size); + pubk.copyTo(m_remote.ref()); + nonce.copyTo(m_remoteNonce.ref()); + + Secret sharedSecret; + crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, sharedSecret); + m_remoteEphemeral = recover(*(Signature*)sig.data(), sharedSecret ^ m_remoteNonce); + assert(sha3(m_remoteEphemeral) == *(h256*)hepubk.data()); + transition(); + } + else + { + clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << m_socket->remoteEndpoint(); + m_nextState = Error; + transition(); + } + }); +} + +void RLPXHandshake::readAck() +{ + clog(NetConnect) << "p2p.connect.egress recving ack from " << m_socket->remoteEndpoint(); + m_ackCipher.resize(210); + auto self(shared_from_this()); + ba::async_read(m_socket->ref(), ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) + { + if (ec) + transition(ec); + else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_ackCipher), m_ack)) + { + bytesConstRef(&m_ack).cropped(0, Public::size).copyTo(m_remoteEphemeral.ref()); + bytesConstRef(&m_ack).cropped(Public::size, h256::size).copyTo(m_remoteNonce.ref()); + transition(); + } + else + { + clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remoteEndpoint(); + m_nextState = Error; + transition(); + } + }); +} + +void RLPXHandshake::error() +{ + clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; + m_socket->close(); + if (m_io != nullptr) + delete m_io; +} + +void RLPXHandshake::transition(boost::system::error_code _ech) +{ + if (_ech || m_nextState == Error) + return error(); + + auto self(shared_from_this()); + if (m_nextState == New) + { + m_nextState = AckAuth; + if (m_originated) + writeAuth(); + else + readAuth(); + } + else if (m_nextState == AckAuth) + { + m_nextState = WriteHello; + if (m_originated) + readAck(); + else + writeAck(); + } + else if (m_nextState == WriteHello) + { + m_nextState = ReadHello; + + if (m_originated) + clog(NetConnect) << "p2p.connect.egress sending capabilities handshake"; + else + clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; + + /// This pointer will be freed if there is an error otherwise + /// it will be passed to Host which will take ownership. + m_io = new RLPXFrameIO(*this); + + // old packet format + // 5 arguments, HelloPacket + RLPStream s; + s.append((unsigned)0).appendList(5) + << m_host->protocolVersion() + << m_host->m_clientVersion + << m_host->caps() + << m_host->listenPort() + << m_host->id(); + bytes packet; + s.swapOut(packet); + m_io->writeSingleFramePacket(&packet, m_handshakeOutBuffer); + ba::async_write(m_socket->ref(), ba::buffer(m_handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t) + { + transition(ec); + }); + } + else if (m_nextState == ReadHello) + { + // Authenticate and decrypt initial hello frame with initial RLPXFrameIO + // and request m_host to start session. + m_nextState = StartSession; + + // read frame header + m_handshakeInBuffer.resize(h256::size); + ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this, self](boost::system::error_code ec, std::size_t) + { + if (ec) + transition(ec); + else + { + /// authenticate and decrypt header + if (!m_io->authAndDecryptHeader(bytesRef(m_handshakeInBuffer.data(), h256::size))) + { + m_nextState = Error; + transition(); + return; + } + + clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header"; + + /// check frame size + bytes& header = m_handshakeInBuffer; + uint32_t frameSize = (uint32_t)(header[2]) | (uint32_t)(header[1])<<8 | (uint32_t)(header[0])<<16; + if (frameSize > 1024) + { + // all future frames: 16777216 + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large" << frameSize; + m_nextState = Error; + transition(); + return; + } + + /// rlp of header has protocol-type, sequence-id[, total-packet-size] + bytes headerRLP(header.size() - 3 - h128::size); + bytesConstRef(&header).cropped(3).copyTo(&headerRLP); + + /// read padded frame and mac + m_handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size); + ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t) + { + if (ec) + transition(ec); + else + { + bytesRef frame(&m_handshakeInBuffer); + if (!m_io->authAndDecryptFrame(frame)) + { + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; + m_nextState = Error; + transition(); + return; + } + + PacketType packetType = (PacketType)(frame[0] == 0x80 ? 0x0 : frame[0]); + if (packetType != 0) + { + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; + m_nextState = Error; + transition(); + return; + } + + clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session."; + RLP rlp(frame.cropped(1)); + m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); + } + }); + } + }); + } +} diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h new file mode 100644 index 000000000..aac8f4b5a --- /dev/null +++ b/libp2p/RLPxHandshake.h @@ -0,0 +1,126 @@ +/* + 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 RLPXHandshake.h + * @author Alex Leverington + * @date 2015 + */ + + +#pragma once + +#include +#include +#include +#include "RLPxFrameIO.h" +#include "Common.h" +namespace ba = boost::asio; +namespace bi = boost::asio::ip; + +namespace dev +{ +namespace p2p +{ + +/** + * @brief Setup inbound or outbound connection for communication over RLPXFrameIO. + * RLPx Spec: https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake + * + * @todo Implement StartSession transition via lambda which is passed to constructor. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + */ +class RLPXHandshake: public std::enable_shared_from_this +{ + friend class RLPXFrameIO; + + /// Sequential states of handshake + enum State + { + Error = -1, + New, + AckAuth, + WriteHello, + ReadHello, + StartSession + }; + +public: + /// Setup incoming connection. + RLPXHandshake(Host* _host, std::shared_ptr const& _socket): m_host(_host), m_originated(false), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } + + /// Setup outbound connection. + RLPXHandshake(Host* _host, std::shared_ptr const& _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_originated(true), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } + + ~RLPXHandshake() {} + + /// Start handshake. + void start() { transition(); } + + void cancel() { m_nextState = Error; } + +protected: + /// Write Auth message to socket and transitions to AckAuth. + void writeAuth(); + + /// Reads Auth message from socket and transitions to AckAuth. + void readAuth(); + + /// Write Ack message to socket and transitions to WriteHello. + void writeAck(); + + /// Reads Auth message from socket and transitions to WriteHello. + void readAck(); + + /// Closes connection and ends transitions. + void error(); + + /// Performs transition for m_nextState. + void transition(boost::system::error_code _ech = boost::system::error_code()); + + State m_nextState = New; ///< Current or expected state of transition. + + Host* m_host; ///< Host which provides m_alias, protocolVersion(), m_clientVersion, caps(), and TCP listenPort(). + + /// Node id of remote host for socket. + NodeId m_remote; ///< Public address of remote host. + bool m_originated = false; ///< True if connection is outbound. + + /// Buffers for encoded and decoded handshake phases + bytes m_auth; ///< Plaintext of egress or ingress Auth message. + bytes m_authCipher; ///< Ciphertext of egress or ingress Auth message. + bytes m_ack; ///< Plaintext of egress or ingress Ack message. + bytes m_ackCipher; ///< Ciphertext of egress or ingress Ack message. + bytes m_handshakeOutBuffer; ///< Frame buffer for egress Hello packet. + bytes m_handshakeInBuffer; ///< Frame buffer for ingress Hello packet. + + crypto::ECDHE m_ecdhe; ///< Ephemeral ECDH secret and agreement. + h256 m_nonce; ///< Nonce generated by this host for handshake. + + Public m_remoteEphemeral; ///< Remote ephemeral public key. + h256 m_remoteNonce; ///< Nonce generated by remote host for handshake. + + /// Used to read and write RLPx encrypted frames for last step of handshake authentication. + /// Passed onto Host which will take ownership. + RLPXFrameIO* m_io = nullptr; + + std::shared_ptr m_socket; ///< Socket. +}; + +} +} \ No newline at end of file diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index ec0a2d0d7..2c6530a2d 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -16,6 +16,7 @@ */ /** @file Session.cpp * @author Gav Wood + * @author Alex Leverington * @date 2014 */ @@ -37,11 +38,12 @@ using namespace dev::p2p; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const& _n): +Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info): m_server(_s), - m_socket(std::move(_socket)), + m_io(_io), + m_socket(m_io->socket()), m_peer(_n), - m_info({NodeId(), "?", m_socket.remote_endpoint().address().to_string(), 0, chrono::steady_clock::duration(0), CapDescSet(), 0, map()}), + m_info(_info), m_ping(chrono::steady_clock::time_point::max()) { m_lastReceived = m_connect = chrono::steady_clock::now(); @@ -65,6 +67,7 @@ Session::~Session() } } catch (...){} + delete m_io; } NodeId Session::id() const @@ -142,101 +145,21 @@ void Session::serviceNodesRequest() addNote("peers", "done"); } -bool Session::interpret(RLP const& _r) +bool Session::interpret(PacketType _t, RLP const& _r) { m_lastReceived = chrono::steady_clock::now(); - clogS(NetRight) << _r; + clogS(NetRight) << _t << _r; try // Generic try-catch block designed to capture RLP format errors - TODO: give decent diagnostics, make a bit more specific over what is caught. { - switch ((PacketType)_r[0].toInt()) + switch (_t) { - case HelloPacket: - { - // TODO: P2P first pass, implement signatures. if signature fails, drop connection. if egress, flag node's endpoint as stale. - // Move auth to Host so we consolidate authentication logic and eschew peer deduplication logic. - // Move all node-lifecycle information into Host. - // Finalize peer-lifecycle properties vs node lifecycle. - - m_protocolVersion = _r[1].toInt(); - auto clientVersion = _r[2].toString(); - auto caps = _r[3].toVector(); - auto listenPort = _r[4].toInt(); - auto id = _r[5].toHash(); - - // clang error (previously: ... << hex << caps ...) - // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" - stringstream capslog; - for (auto cap: caps) - capslog << "(" << cap.first << "," << dec << cap.second << ")"; - - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << id.abridged() << showbase << capslog.str() << dec << listenPort; - - if (m_server->id() == id) - { - // Already connected. - clogS(NetWarn) << "Connected to ourself under a false pretext. We were told this peer was id" << id.abridged(); - disconnect(LocalIdentity); - return true; - } - - // if peer and connection have id, check for UnexpectedIdentity - if (!id) - { - disconnect(NullIdentity); - return true; - } - else if (!m_peer->id) - { - m_peer->id = id; - m_peer->endpoint.tcp.port(listenPort); - } - else if (m_peer->id != id) - { - // TODO p2p: FIXME. Host should catch this and reattempt adding node to table. - m_peer->id = id; - m_peer->m_score = 0; - m_peer->m_rating = 0; -// disconnect(UnexpectedIdentity); -// return true; - } - - if (m_server->havePeerSession(id)) - { - // Already connected. - clogS(NetWarn) << "Already connected to a peer with id" << id.abridged(); - // Possible that two nodes continually connect to each other with exact same timing. - this_thread::sleep_for(chrono::milliseconds(rand() % 100)); - disconnect(DuplicatePeer); - return true; - } - - if (m_peer->isOffline()) - m_peer->m_lastConnected = chrono::system_clock::now(); - - if (m_protocolVersion != m_server->protocolVersion()) - { - disconnect(IncompatibleProtocol); - return true; - } - - m_info.clientVersion = clientVersion; - m_info.host = m_socket.remote_endpoint().address().to_string(); - m_info.port = listenPort; - m_info.lastPing = std::chrono::steady_clock::duration(); - m_info.caps = _r[3].toSet(); - m_info.socket = (unsigned)m_socket.native_handle(); - m_info.notes = map(); - - m_server->registerPeer(shared_from_this(), caps); - break; - } case DisconnectPacket: { string reason = "Unspecified"; - auto r = (DisconnectReason)_r[1].toInt(); - if (!_r[1].isInt()) + auto r = (DisconnectReason)_r[0].toInt(); + if (!_r[0].isInt()) drop(BadProtocol); else { @@ -275,7 +198,7 @@ bool Session::interpret(RLP const& _r) clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; m_weRequestedNodes = false; - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { bi::address peerAddress; if (_r[i][0].size() == 16) @@ -325,12 +248,11 @@ bool Session::interpret(RLP const& _r) break; default: { - auto id = _r[0].toInt(); for (auto const& i: m_capabilities) - if (id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount()) + if (_t >= i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount()) { if (i.second->m_enabled) - return i.second->interpret(id - i.second->m_idOffset, _r); + return i.second->interpret(_t - i.second->m_idOffset, _r); else return true; } @@ -356,47 +278,34 @@ void Session::ping() RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) { - return prep(_s).appendList(_args + 1).append((unsigned)_id); -} - -RLPStream& Session::prep(RLPStream& _s) -{ - return _s.appendRaw(bytes(8, 0)); + return _s.append((unsigned)_id).appendList(_args); } void Session::sealAndSend(RLPStream& _s) { bytes b; _s.swapOut(b); - m_server->seal(b); send(move(b)); } bool Session::checkPacket(bytesConstRef _msg) { - if (_msg.size() < 8) + if (_msg.size() < 2) return false; - if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91)) + if (_msg[0] > 0x7f) return false; - uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7]; - if (_msg.size() != len + 8) - return false; - RLP r(_msg.cropped(8)); - if (r.actualSize() != len) + RLP r(_msg.cropped(1)); + if (r.actualSize() + 1 != _msg.size()) return false; return true; } -void Session::send(bytesConstRef _msg) -{ - send(_msg.toBytes()); -} - void Session::send(bytes&& _msg) { - clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); + clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1)); - if (!checkPacket(bytesConstRef(&_msg))) + bytesConstRef msg(&_msg); + if (!checkPacket(msg)) clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!"; if (!m_socket.is_open()) @@ -416,6 +325,7 @@ void Session::send(bytes&& _msg) void Session::write() { const bytes& bytes = m_writeQueue[0]; + m_io->writeSingleFramePacket(&bytes, m_writeQueue[0]); auto self(shared_from_this()); ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/) { @@ -484,95 +394,88 @@ void Session::disconnect(DisconnectReason _reason) void Session::start() { - RLPStream s; - prep(s, HelloPacket, 5) - << m_server->protocolVersion() - << m_server->m_clientVersion - << m_server->caps() - << m_server->m_tcpPublic.port() - << m_server->id(); - sealAndSend(s); ping(); doRead(); } void Session::doRead() { - // ignore packets received while waiting to disconnect + // ignore packets received while waiting to disconnect. if (m_dropped) return; - + auto self(shared_from_this()); - m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length) { - // If error is end of file, ignore if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) { - // got here with length of 1241... clogS(NetWarn) << "Error reading: " << ec.message(); drop(TCPError); } else if (ec && length == 0) - { return; - } else { - try + /// authenticate and decrypt header + bytesRef header(m_data.data(), h256::size); + if (!m_io->authAndDecryptHeader(header)) { - m_incoming.resize(m_incoming.size() + length); - memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); - while (m_incoming.size() > 8) + clog(NetWarn) << "header decrypt failed"; + drop(BadProtocol); // todo: better error + return; + } + + /// check frame size + uint32_t frameSize = (m_data[0] * 256 + m_data[1]) * 256 + m_data[2]; + if (frameSize >= (uint32_t)1 << 24) + { + clog(NetWarn) << "frame size too large"; + drop(BadProtocol); + return; + } + + /// rlp of header has protocol-type, sequence-id[, total-packet-size] + bytes headerRLP(13); + bytesConstRef(m_data.data(), h128::size).cropped(3).copyTo(&headerRLP); + + /// read padded frame and mac + auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size; + ba::async_read(m_socket, boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length) + { + if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) + { + clogS(NetWarn) << "Error reading: " << ec.message(); + drop(TCPError); + } + else if (ec && length == 0) + return; + else { - if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) + if (!m_io->authAndDecryptFrame(bytesRef(m_data.data(), tlen))) + { + clog(NetWarn) << "frame decrypt failed"; + drop(BadProtocol); // todo: better error + return; + } + + bytesConstRef frame(m_data.data(), frameSize); + if (!checkPacket(frame)) { - clogS(NetWarn) << "INVALID SYNCHRONISATION TOKEN; expected = 22400891; received = " << toHex(bytesConstRef(m_incoming.data(), 4)); + cerr << "Received " << frame.size() << ": " << toHex(frame) << endl; + clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; disconnect(BadProtocol); return; } else { - uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data() + 4, 4)); - uint32_t tlen = len + 8; - if (m_incoming.size() < tlen) - break; - - // enough has come in. - auto data = bytesConstRef(m_incoming.data(), tlen); - if (!checkPacket(data)) - { - cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; - clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; - disconnect(BadProtocol); - return; - } - else - { - RLP r(data.cropped(8)); - if (!interpret(r)) - { - // error - bad protocol - clogS(NetWarn) << "Couldn't interpret packet." << RLP(r); - // Just wasting our bandwidth - perhaps reduce rating? - //return; - } - } - memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); - m_incoming.resize(m_incoming.size() - tlen); + auto packetType = (PacketType)RLP(frame.cropped(0, 1)).toInt(); + RLP r(frame.cropped(1)); + if (!interpret(packetType, r)) + clogS(NetWarn) << "Couldn't interpret packet." << RLP(r); } + doRead(); } - doRead(); - } - catch (Exception const& _e) - { - clogS(NetWarn) << "ERROR: " << diagnostic_information(_e); - drop(BadProtocol); - } - catch (std::exception const& _e) - { - clogS(NetWarn) << "ERROR: " << _e.what(); - drop(BadProtocol); - } + }); } }); } diff --git a/libp2p/Session.h b/libp2p/Session.h index a5347cddb..51db5adc3 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -16,6 +16,7 @@ */ /** @file Session.h * @author Gav Wood + * @author Alex Leverington * @date 2014 */ @@ -32,6 +33,7 @@ #include #include #include +#include "RLPxHandshake.h" #include "Common.h" namespace dev @@ -52,7 +54,7 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr const& _n); + Session(Host* _server, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info); virtual ~Session(); void start(); @@ -63,16 +65,13 @@ public: bool isConnected() const { return m_socket.is_open(); } NodeId id() const; - unsigned socketId() const { return m_socket.native_handle(); } + unsigned socketId() const { return m_info.socket; } template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } } static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0); - static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); - void send(bytes&& _msg); - void send(bytesConstRef _msg); int rating() const; void addRating(unsigned _r); @@ -85,6 +84,8 @@ public: void serviceNodesRequest(); private: + void send(bytes&& _msg); + /// Drop the connection for the reason @a _r. void drop(DisconnectReason _r); @@ -95,17 +96,18 @@ private: void write(); /// Interpret an incoming message. - bool interpret(RLP const& _r); + bool interpret(PacketType _t, RLP const& _r); /// @returns true iff the _msg forms a valid message for sending or receiving on the network. static bool checkPacket(bytesConstRef _msg); Host* m_server; ///< The host that owns us. Never null. - mutable bi::tcp::socket m_socket; ///< Socket for the peer's connection. Mutable to ask for native_handle(). + RLPXFrameIO* m_io; ///< Transport over which packets are sent. + bi::tcp::socket& m_socket; ///< Socket for the peer's connection. Mutex x_writeQueue; ///< Mutex for the write queue. std::deque m_writeQueue; ///< The write queue. - std::array m_data; ///< Buffer for ingress packet data. + std::array m_data; ///< Buffer for ingress packet data. bytes m_incoming; ///< Read buffer for ingress bytes. unsigned m_protocolVersion = 0; ///< The protocol version of the peer. diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 7ff846bdb..dc6e2c5a8 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -378,8 +378,9 @@ bool Compiler::visit(FunctionDefinition const& _function) m_context.removeVariable(*localVariable); m_context.adjustStackOffset(-(int)c_returnValuesSize); + if (!_function.isConstructor()) - m_context << eth::Instruction::JUMP; + m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); return false; } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 76f16f3ab..4b1e1b4d6 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -94,8 +94,8 @@ private: std::vector m_continueTags; ///< tag to jump to for a "continue" statement eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement unsigned m_modifierDepth = 0; - FunctionDefinition const* m_currentFunction; - unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag + FunctionDefinition const* m_currentFunction = nullptr; + unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag // arguments for base constructors, filled in derived-to-base order std::map> const*> m_baseArguments; }; diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index 1dea62e93..f2bb1de20 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -177,6 +177,13 @@ u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declarati return it->second; } +CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpType) +{ + eth::AssemblyItem item(eth::Instruction::JUMP); + item.setJumpType(_jumpType); + return *this << item; +} + void CompilerContext::resetVisitedNodes(ASTNode const* _node) { stack newStack; diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 4d63d8ba0..76923a77a 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -91,7 +91,7 @@ public: /// Appends a JUMP to a new tag and @returns the tag eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); } /// Appends a JUMP to a tag already on the stack - CompilerContext& appendJump() { return *this << eth::Instruction::JUMP; } + CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); /// Appends a JUMP to a specific tag CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; } /// Appends pushing of a new tag and @returns the new tag. @@ -120,7 +120,7 @@ public: eth::Assembly const& getAssembly() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.streamRLP(_stream, "", _sourceCodes); } + void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); } bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index b02aecf5f..129261120 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -108,7 +108,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& retSizeOnStack = returnType->getSizeOnStack(); } solAssert(retSizeOnStack <= 15, "Stack too deep."); - m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP; + m_context << eth::dupInstruction(retSizeOnStack + 1); + m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); } void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded) @@ -405,7 +406,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } _functionCall.getExpression().accept(*this); - m_context.appendJump(); + m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction); m_context << returnLabel; unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes()); @@ -825,10 +826,20 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) Declaration const* declaration = _identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { - if (magicVar->getType()->getCategory() == Type::Category::Contract) + switch (magicVar->getType()->getCategory()) + { + case Type::Category::Contract: // "this" or "super" if (!dynamic_cast(*magicVar->getType()).isSuper()) m_context << eth::Instruction::ADDRESS; + break; + case Type::Category::Integer: + // "now" + m_context << eth::Instruction::TIMESTAMP; + break; + default: + break; + } } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index 60de5105f..411e99abb 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -37,6 +37,7 @@ GlobalContext::GlobalContext(): m_magicVariables(vector>{make_shared("block", make_shared(MagicType::Kind::Block)), make_shared("msg", make_shared(MagicType::Kind::Message)), make_shared("tx", make_shared(MagicType::Kind::Transaction)), + make_shared("now", make_shared(256)), make_shared("suicide", make_shared(strings{"address"}, strings{}, FunctionType::Location::Suicide)), make_shared("sha3", diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 87eb2f8ff..cc0b2ca17 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -34,6 +34,8 @@ class Scanner; class Parser { public: + Parser() {} + ASTPointer parse(std::shared_ptr const& _scanner); std::shared_ptr const& getSourceName() const; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 70c8ca9de..09c34feb0 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -128,10 +128,6 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to filter.withEarliest(_json["earliest"].asInt()); if (_json["latest"].isInt()) filter.withLatest(_json["lastest"].asInt()); - if (_json["max"].isInt()) - filter.withMax(_json["max"].asInt()); - if (_json["skip"].isInt()) - filter.withSkip(_json["skip"].asInt()); if (!_json["address"].empty()) { if (_json["address"].isArray()) diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index 7480a104e..53ea91a9e 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -55,7 +55,7 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r) { case StatusPacket: { - auto protocolVersion = _r[1].toInt(); + auto protocolVersion = _r[0].toInt(); clogS(NetMessageSummary) << "Status: " << protocolVersion; diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index 29124a39a..991fd334a 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -37,6 +37,7 @@ #include "QVariableDefinition.h" #include "HttpServer.h" #include "AppContext.h" +#include "SortFilterProxyModel.h" using namespace dev; using namespace dev::eth; @@ -74,6 +75,7 @@ void AppContext::load() qmlRegisterType("org.ethereum.qml.QBoolType", 1, 0, "QBoolType"); qmlRegisterType("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration"); qmlRegisterType("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry"); + qmlRegisterType("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel"); QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml")); QObject* projectModel = projectModelComponent.create(); if (projectModelComponent.isError()) @@ -86,6 +88,7 @@ void AppContext::load() m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel); qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType("HttpServer", 1, 0, "HttpServer"); + m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); QWindow *window = qobject_cast(m_applicationEngine->rootObjects().at(0)); window->setIcon(QIcon(":/res/mix_256x256x32.png")); diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 97b808eb2..7f1f5d216 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -51,14 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) return; } -void CodeEditorExtensionManager::initExtensions() -{ - std::shared_ptr output = std::make_shared(m_appContext); - QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); - - initExtension(output); -} - void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) { if (!_ext->contentUrl().isEmpty()) @@ -93,5 +85,4 @@ void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView) void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView) { m_headerView = _headerView; - initExtensions(); //TODO: move this to a proper place } diff --git a/mix/CodeEditorExtensionManager.h b/mix/CodeEditorExtensionManager.h index fe6fbb33a..ebfe2d8a3 100644 --- a/mix/CodeEditorExtensionManager.h +++ b/mix/CodeEditorExtensionManager.h @@ -49,8 +49,6 @@ class CodeEditorExtensionManager: public QObject public: CodeEditorExtensionManager(); ~CodeEditorExtensionManager(); - /// Initialize all extensions. - void initExtensions(); /// Initialize extension. void initExtension(std::shared_ptr); /// Set current tab view diff --git a/mix/CodeModel.h b/mix/CodeModel.h index de9a07bab..92111996c 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -164,6 +164,8 @@ public: /// Find a contract by document id /// @returns CompiledContract object or null if not found Q_INVOKABLE CompiledContract* contractByDocumentId(QString _documentId) const; + /// Reset code model + Q_INVOKABLE void reset() { reset(QVariantMap()); } signals: /// Emited on compilation state change diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp index 449512975..bf0351ee3 100644 --- a/mix/FileIo.cpp +++ b/mix/FileIo.cpp @@ -158,6 +158,7 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder) } rlpStr.appendList(k); + manifest["entries"] = entries; std::stringstream jsonStr; jsonStr << manifest; QByteArray b = QString::fromStdString(jsonStr.str()).toUtf8(); @@ -166,7 +167,6 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder) for (unsigned int k = 0; k < files.size(); k++) rlpStr.append(files.at(k)); - manifest["entries"] = entries; bytes dapp = rlpStr.out(); dev::h256 dappHash = dev::sha3(dapp); //encrypt diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 061057bb4..a06bbb02d 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -331,7 +331,6 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const unsigned lastBlock = bc().number(); unsigned block = std::min(lastBlock, (unsigned)_f.latest()); unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest())); - unsigned skip = _f.skip(); // Pending transactions if (block > bc().number()) { @@ -341,9 +340,8 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const // Might have a transaction that contains a matching log. TransactionReceipt const& tr = m_state.receipt(i); LogEntries logEntries = _f.matches(tr); - for (unsigned entry = 0; entry < logEntries.size() && ret.size() != _f.max(); ++entry) + for (unsigned entry = 0; entry < logEntries.size(); ++entry) ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); - skip -= std::min(skip, static_cast(logEntries.size())); } block = bc().number(); } @@ -355,12 +353,8 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const if (_f.matches(bc().info(h).logBloom)) for (TransactionReceipt receipt: bc().receipts(h).receipts) if (_f.matches(receipt.bloom())) - { - LogEntries logEntries = _f.matches(receipt); - for (unsigned entry = skip; entry < logEntries.size() && ret.size() != _f.max(); ++entry) - ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); - skip -= std::min(skip, static_cast(logEntries.size())); - } + for (auto const& e: _f.matches(receipt)) + ret.insert(ret.begin(), LocalisedLogEntry(e, block)); h = bc().details(h).parent; } return ret; diff --git a/mix/SortFilterProxyModel.cpp b/mix/SortFilterProxyModel.cpp new file mode 100644 index 000000000..6fb2cca0c --- /dev/null +++ b/mix/SortFilterProxyModel.cpp @@ -0,0 +1,156 @@ +/* + 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 . +*/ +/** + * @author Yann + * @date 2015 + * Proxy used to filter a QML TableView. + */ + + +#include "SortFilterProxyModel.h" +#include +#include + +using namespace dev::mix; + +SortFilterProxyModel::SortFilterProxyModel(QObject* _parent) : QSortFilterProxyModel(_parent) +{ + connect(this, &SortFilterProxyModel::rowsInserted, this, &SortFilterProxyModel::countChanged); + connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged); +} + +int SortFilterProxyModel::count() const +{ + return rowCount(); +} + +QObject* SortFilterProxyModel::source() const +{ + return sourceModel(); +} + +void SortFilterProxyModel::setSource(QObject* _source) +{ + setSourceModel(qobject_cast(_source)); +} + +QByteArray SortFilterProxyModel::sortRole() const +{ + return roleNames().value(QSortFilterProxyModel::sortRole()); +} + +void SortFilterProxyModel::setSortRole(QByteArray const& _role) +{ + QSortFilterProxyModel::setSortRole(roleKey(_role)); +} + +void SortFilterProxyModel::setSortOrder(Qt::SortOrder _order) +{ + QSortFilterProxyModel::sort(0, _order); +} + +QString SortFilterProxyModel::filterString() const +{ + return filterRegExp().pattern(); +} + +void SortFilterProxyModel::setFilterString(QString const& _filter) +{ + setFilterRegExp(QRegExp(_filter, filterCaseSensitivity(), static_cast(filterSyntax()))); +} + +SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const +{ + return static_cast(filterRegExp().patternSyntax()); +} + +void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax _syntax) +{ + setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast(_syntax))); +} + +QJSValue SortFilterProxyModel::get(int _idx) const +{ + QJSEngine *engine = qmlEngine(this); + QJSValue value = engine->newObject(); + if (_idx >= 0 && _idx < count()) + { + QHash roles = roleNames(); + QHashIterator it(roles); + while (it.hasNext()) + { + it.next(); + value.setProperty(QString::fromUtf8(it.value()), data(index(_idx, 0), it.key()).toString()); + } + } + return value; +} + +int SortFilterProxyModel::roleKey(QByteArray const& _role) const +{ + QHash roles = roleNames(); + QHashIterator it(roles); + while (it.hasNext()) + { + it.next(); + if (it.value() == _role) + return it.key(); + } + return -1; +} + +QHash SortFilterProxyModel::roleNames() const +{ + if (QAbstractItemModel* source = sourceModel()) + return source->roleNames(); + return QHash(); +} + +bool SortFilterProxyModel::filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const +{ + QAbstractItemModel* model = sourceModel(); + QModelIndex sourceIndex = model->index(_sourceRow, 0, _sourceParent); + if (!sourceIndex.isValid()) + return true; + + QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString(); + QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString(); + return keyType.contains(m_filterType) && keyContent.contains(m_filterContent); +} + +void SortFilterProxyModel::setFilterType(QString const& _type) +{ + m_filterType = QRegExp(_type, filterCaseSensitivity(), static_cast(filterSyntax())); + setFilterRegExp(_type); +} + +QString SortFilterProxyModel::filterType() const +{ + return m_filterType.pattern(); +} + +void SortFilterProxyModel::setFilterContent(QString const& _content) +{ + m_filterContent = QRegExp(_content, filterCaseSensitivity(), static_cast(filterSyntax())); + setFilterRegExp(_content); +} + +QString SortFilterProxyModel::filterContent() const +{ + return m_filterContent.pattern(); +} + diff --git a/mix/SortFilterProxyModel.h b/mix/SortFilterProxyModel.h new file mode 100644 index 000000000..de9a2005f --- /dev/null +++ b/mix/SortFilterProxyModel.h @@ -0,0 +1,97 @@ +/* + 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 . +*/ +/** + * @author Yann + * @date 2015 + * Proxy used to filter a QML TableView. + */ + + +#pragma once + +#include +#include + +namespace dev +{ +namespace mix +{ + +class SortFilterProxyModel: public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QObject* source READ source WRITE setSource) + + Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole) + Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) + + Q_PROPERTY(QString filterContent READ filterContent WRITE setFilterContent) + Q_PROPERTY(QString filterType READ filterType WRITE setFilterType) + Q_PROPERTY(QString filterString READ filterString WRITE setFilterString) + Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax) + + Q_ENUMS(FilterSyntax) + +public: + explicit SortFilterProxyModel(QObject* _parent = 0); + + QObject* source() const; + void setSource(QObject* _source); + + QByteArray sortRole() const; + void setSortRole(QByteArray const& _role); + + void setSortOrder(Qt::SortOrder _order); + + QString filterContent() const; + void setFilterContent(QString const& _content); + QString filterType() const; + void setFilterType(QString const& _type); + + QString filterString() const; + void setFilterString(QString const& _filter); + + enum FilterSyntax { + RegExp, + Wildcard, + FixedString + }; + + FilterSyntax filterSyntax() const; + void setFilterSyntax(FilterSyntax _syntax); + + int count() const; + Q_INVOKABLE QJSValue get(int _index) const; + +signals: + void countChanged(); + +protected: + int roleKey(QByteArray const& _role) const; + QHash roleNames() const; + bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const; + +private: + QRegExp m_filterType; + QRegExp m_filterContent; + const QString type = "type"; + const QString content = "content"; +}; + +} +} diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml new file mode 100644 index 000000000..9d03b2af2 --- /dev/null +++ b/mix/qml/LogsPane.qml @@ -0,0 +1,302 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.3 +import org.ethereum.qml.SortFilterProxyModel 1.0 +import "." + +Rectangle +{ + function push(_level, _type, _content) + { + _content = _content.replace(/\n/g, " ") + logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level }); + } + + anchors.fill: parent + radius: 5 + color: LogsPaneStyle.generic.layout.backgroundColor + border.color: LogsPaneStyle.generic.layout.borderColor + border.width: LogsPaneStyle.generic.layout.borderWidth + ColumnLayout { + z: 2 + height: parent.height + width: parent.width + spacing: 0 + Row + { + id: rowAction + Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight + height: LogsPaneStyle.generic.layout.headerHeight + anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin + anchors.left: parent.left + spacing: LogsPaneStyle.generic.layout.headerButtonSpacing + Button + { + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + action: clearAction + iconSource: "qrc:/qml/img/broom.png" + } + + Action { + id: clearAction + enabled: logsModel.count > 0 + tooltip: qsTr("Clear") + onTriggered: { + logsModel.clear() + } + } + + Button + { + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + action: copytoClipBoardAction + iconSource: "qrc:/qml/img/copy.png" + } + + Action { + id: copytoClipBoardAction + enabled: logsModel.count > 0 + tooltip: qsTr("Copy to Clipboard") + onTriggered: { + var content = ""; + for (var k = 0; k < logsModel.count; k++) + { + var log = logsModel.get(k); + content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; + } + appContext.toClipboard(content); + } + } + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 1; + height: parent.height - 10 + color : "#808080" + } + + ToolButton { + id: javascriptButton + checkable: true + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + checked: true + onCheckedChanged: { + proxyModel.toogleFilter("javascript") + } + tooltip: qsTr("JavaScript") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + color: LogsPaneStyle.generic.layout.logLabelColor + anchors.centerIn: parent + text: qsTr("JavaScript") + } + } + } + } + + ToolButton { + id: runButton + checkable: true + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + checked: true + onCheckedChanged: { + proxyModel.toogleFilter("run") + } + tooltip: qsTr("Run") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + color: LogsPaneStyle.generic.layout.logLabelColor + anchors.centerIn: parent + text: qsTr("Run") + } + } + } + } + + ToolButton { + id: stateButton + checkable: true + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + checked: true + onCheckedChanged: { + proxyModel.toogleFilter("state") + } + tooltip: qsTr("State") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + color: "#5391d8" + anchors.centerIn: parent + text: qsTr("State") + } + } + } + } + + ToolButton { + id: compilationButton + checkable: true + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + checked: false + onCheckedChanged: { + proxyModel.toogleFilter("compilation") + } + tooltip: qsTr("Compilation") + style: + ButtonStyle { + label: + Item { + DefaultLabel { + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + color: "#5391d8" + anchors.centerIn: parent + text: qsTr("Compilation") + } + } + } + } + + DefaultTextField + { + id: searchBox + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + width: LogsPaneStyle.generic.layout.headerInputWidth + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-3) + font.italic: true + onTextChanged: { + proxyModel.search(text); + } + } + } + + ListModel { + id: logsModel + } + + TableView { + id: logsTable + clip: true + Layout.fillWidth: true + Layout.preferredHeight: parent.height - rowAction.height + headerVisible : false + onDoubleClicked: + { + var log = logsModel.get((logsTable.currentRow)); + if (log) + appContext.toClipboard(log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); + } + + model: SortFilterProxyModel { + id: proxyModel + source: logsModel + property var roles: ["-", "javascript", "run", "state"] + + Component.onCompleted: { + filterType = regEx(proxyModel.roles); + } + + function search(_value) + { + filterContent = _value; + } + + function toogleFilter(_value) + { + var count = roles.length; + for (var i in roles) + { + if (roles[i] === _value) + { + roles.splice(i, 1); + break; + } + } + if (count === roles.length) + roles.push(_value); + + filterType = regEx(proxyModel.roles); + } + + function regEx(_value) + { + return "(?:" + roles.join('|') + ")"; + } + + filterType: "(?:javascript|run|state)" + filterContent: "" + filterSyntax: SortFilterProxyModel.RegExp + filterCaseSensitivity: Qt.CaseInsensitive + } + TableViewColumn + { + role: "date" + title: qsTr("date") + width: LogsPaneStyle.generic.layout.dateWidth + delegate: itemDelegate + } + TableViewColumn + { + role: "type" + title: qsTr("type") + width: LogsPaneStyle.generic.layout.typeWidth + delegate: itemDelegate + } + TableViewColumn + { + role: "content" + title: qsTr("content") + width: LogsPaneStyle.generic.layout.contentWidth + delegate: itemDelegate + } + + rowDelegate: Item { + Rectangle { + width: logsTable.width - 4 + height: 17 + color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor + } + } + } + + Component { + id: itemDelegate + DefaultLabel { + text: styleData.value; + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-1) + color: { + if (proxyModel.get(styleData.row).level === "error") + return "red"; + else if (proxyModel.get(styleData.row).level === "warning") + return "orange"; + else + return "#808080"; + } + } + } + } +} diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml new file mode 100644 index 000000000..59b80653a --- /dev/null +++ b/mix/qml/LogsPaneStyle.qml @@ -0,0 +1,29 @@ +pragma Singleton +import QtQuick 2.0 + +QtObject { + + function absoluteSize(rel) + { + return systemPointSize + rel; + } + + property QtObject generic: QtObject { + property QtObject layout: QtObject { + property string backgroundColor: "#f7f7f7" + property string borderColor: "#5391d8" + property int borderWidth: 1 + property int headerHeight: 35 + property int headerButtonSpacing: 5 + property int leftMargin: 10 + property int headerButtonHeight: 30 + property string logLabelColor: "#5391d8" + property string logLabelFont: "sans serif" + property int headerInputWidth: 200 + property int dateWidth: 150 + property int typeWidth: 80 + property int contentWidth: 700 + property string logAlternateColor: "#f0f0f0" + } + } +} diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 081e0cd95..7ac751a79 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -35,8 +35,8 @@ Rectangle { onCompilationComplete: { if (firstCompile) { firstCompile = false; - if (runOnProjectLoad) - startQuickDebugging(); + if (runOnProjectLoad) + startQuickDebugging(); } } } @@ -102,7 +102,6 @@ Rectangle { } CodeEditorExtensionManager { - headerView: headerPaneTabs; } Settings { @@ -116,6 +115,7 @@ Rectangle { ColumnLayout { + id: mainColumn anchors.fill: parent spacing: 0 Rectangle { @@ -133,21 +133,15 @@ Rectangle { } id: headerPaneContainer anchors.fill: parent - TabView { - id: headerPaneTabs - tabsVisible: false - antialiasing: true + StatusPane + { anchors.fill: parent - style: TabViewStyle { - frameOverlap: 1 - tab: Rectangle {} - frame: Rectangle { color: "transparent" } - } + webPreview: webPreview } } } - Rectangle{ + Rectangle { Layout.fillWidth: true height: 1 color: "#8c8c8c" @@ -168,9 +162,9 @@ Rectangle { { anchors.fill: parent handleDelegate: Rectangle { - width: 1 - height: 1 - color: "#8c8c8c" + width: 1 + height: 1 + color: "#8c8c8c" } orientation: Qt.Horizontal @@ -180,16 +174,18 @@ Rectangle { Layout.minimumWidth: 250 Layout.fillHeight: true } + Rectangle { id: contentView Layout.fillHeight: true Layout.fillWidth: true + SplitView { - handleDelegate: Rectangle { + handleDelegate: Rectangle { width: 1 height: 1 color: "#8c8c8c" - } + } id: codeWebSplitter anchors.fill: parent orientation: Qt.Vertical diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index d3b4070aa..cca2c834b 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -64,7 +64,7 @@ Item { Connections { target: appContext onAppLoaded: { - if (projectSettings.lastProjectPath) + if (projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "") projectModel.loadProject(projectSettings.lastProjectPath) } } @@ -130,6 +130,14 @@ Item { id: projectStateListModel } + Connections + { + target: projectModel + onProjectClosed: { + projectSettings.lastProjectPath = ""; + } + } + Settings { id: projectSettings property string lastProjectPath; diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 61307826c..6deeb9790 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -134,6 +134,7 @@ Item { onProjectClosed: { stateListModel.clear(); stateList = []; + codeModel.reset(); } onProjectLoading: stateListModel.loadStatesFromProject(projectData); onProjectSaving: { @@ -148,6 +149,7 @@ Item { state.title = qsTr("Default"); projectData.states = [ state ]; projectData.defaultStateIndex = 0; + stateListModel.loadStatesFromProject(projectData); } } @@ -264,6 +266,7 @@ Item { defaultStateIndex--; save(); + } function save() { @@ -284,6 +287,8 @@ Item { else defaultStateIndex = 0; var items = projectData.states; + stateListModel.clear(); + stateList = []; for(var i = 0; i < items.length; i++) { var item = fromPlainStateItem(items[i]); stateListModel.append(item); diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 60a9a051e..20b30ce3b 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -8,6 +8,7 @@ import "." Rectangle { id: statusHeader objectName: "statusPane" + property variant webPreview function updateStatus(message) { @@ -15,7 +16,6 @@ Rectangle { { status.state = ""; status.text = qsTr("Compile successfully."); - logslink.visible = false; debugImg.state = "active"; } else @@ -23,39 +23,76 @@ Rectangle { status.state = "error"; var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true); status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; - logslink.visible = true; debugImg.state = ""; + errorMessage(status.text, "Compilation"); } debugRunActionIcon.enabled = codeModel.hasContract; } - function infoMessage(text) + function infoMessage(text, type) { status.state = ""; status.text = text - logslink.visible = false; + logPane.push("info", type, text); } - function errorMessage(text) + function warningMessage(text, type) + { + status.state = "warning"; + status.text = text + logPane.push("warning", type, text); + } + + function errorMessage(text, type) { status.state = "error"; status.text = text - logslink.visible = false; + logPane.push("error", type, text); + } + + Connections { + target: webPreview + onJavaScriptMessage: + { + if (_level === 0) + infoMessage(_content, "JavaScript") + else + { + var message = _sourceId.substring(_sourceId.lastIndexOf("/") + 1) + " - " + qsTr("line") + " " + _lineNb + " - " + _content; + if (_level === 1) + warningMessage(message, "JavaScript") + else + errorMessage(message, "JavaScript") + } + } } Connections { target:clientModel - onRunStarted: infoMessage(qsTr("Running transactions...")); - onRunFailed: errorMessage(qsTr("Error running transactions: " + _message)); - onRunComplete: infoMessage(qsTr("Run complete")); - onNewBlock: infoMessage(qsTr("New block created")); + onRunStarted: infoMessage(qsTr("Running transactions..."), "Run"); + onRunFailed: errorMessage(format(_message), "Run"); + onRunComplete: infoMessage(qsTr("Run complete"), "Run"); + onNewBlock: infoMessage(qsTr("New block created"), "State"); + + function format(_message) + { + var formatted = _message.match(/(?:)/); + if (formatted.length > 1) + formatted = formatted[1] + ": "; + else + return _message; + var exceptionInfos = _message.match(/(?:tag_)(.+)/g); + for (var k in exceptionInfos) + formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", "").replace("=", ""); + return formatted; + } } Connections { target:projectModel - onDeploymentStarted: infoMessage(qsTr("Running deployment...")); - onDeploymentError: errorMessage(error); - onDeploymentComplete: infoMessage(qsTr("Deployment complete")); - onDeploymentStepChanged: infoMessage(message); + onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment"); + onDeploymentError: errorMessage(error, "Deployment"); + onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "Deployment"); + onDeploymentStepChanged: infoMessage(message, "Deployment"); } Connections { target: codeModel @@ -74,6 +111,24 @@ Rectangle { width: 500 height: 30 color: "#fcfbfc" + states: [ + State { + name: "logsOpened" + PropertyChanges { + target: statusContainer + border.color: "#5391d8" + border.width: 1 + } + }, + State { + name: "logsClosed" + PropertyChanges { + target: statusContainer + border.color: "#5391d8" + border.width: 0 + } + } + ] Text { anchors.verticalCenter: parent.verticalCenter @@ -98,6 +153,17 @@ Rectangle { target: statusContainer color: "#fffcd5" } + }, + State { + name: "warning" + PropertyChanges { + target: status + color: "orange" + } + PropertyChanges { + target: statusContainer + color: "#fffcd5" + } } ] onTextChanged: @@ -127,30 +193,73 @@ Rectangle { color: "transparent" } } + MouseArea { + anchors.fill: parent + onClicked: { + logsContainer.toggle(); + } + } } Action { id: toolTipInfo tooltip: "" } - } - Button - { - id: logslink - anchors.left: statusContainer.right - anchors.leftMargin: 9 - visible: false - anchors.verticalCenter: parent.verticalCenter - action: displayLogAction - iconSource: "qrc:/qml/img/search_filled.png" - } + Rectangle + { + function toggle() + { + if (logsContainer.state === "opened") + { + statusContainer.state = "logsClosed"; + logsContainer.state = "closed" + } + else + { + statusContainer.state = "logsOpened"; + logsContainer.state = "opened"; + logsContainer.focus = true; + forceActiveFocus(); + } + } - Action { - id: displayLogAction - tooltip: qsTr("Display Log") - onTriggered: { - mainContent.displayCompilationErrorIfAny(); + id: logsContainer + width: 1000 + height: 0 + anchors.topMargin: 10 + anchors.top: statusContainer.bottom + anchors.horizontalCenter: parent.horizontalCenter + visible: false + radius: 5 + Component.onCompleted: + { + var top = logsContainer; + while (top.parent) + top = top.parent + var coordinates = logsContainer.mapToItem(top, 0, 0) + logsContainer.parent = top; + logsContainer.x = coordinates.x + logsContainer.y = coordinates.y + } + LogsPane + { + id: logPane + } + states: [ + State { + name: "opened"; + PropertyChanges { target: logsContainer; height: 500; visible: true } + }, + State { + name: "closed"; + PropertyChanges { target: logsContainer; height: 0; visible: false } + } + ] + transitions: Transition { + NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 } + NumberAnimation { properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 } + } } } @@ -168,7 +277,6 @@ Rectangle { { color: "transparent" anchors.fill: parent - Button { anchors.right: parent.right diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 6f03088a4..ba2975453 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -12,6 +12,7 @@ Item { id: webPreview property string pendingPageUrl: "" property bool initialized: false + signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content) function setPreviewUrl(url) { if (!initialized) @@ -198,7 +199,6 @@ Item { { setPreviewUrl(text); } - focus: true } @@ -216,7 +216,9 @@ Item { anchors.verticalCenter: parent.verticalCenter width: 21 height: 21 + focus: true } + CheckBox { id: autoReloadOnSave checked: true @@ -227,6 +229,7 @@ Item { text: qsTr("Auto reload on save") } } + focus: true } } } @@ -240,7 +243,7 @@ Item { id: webView experimental.settings.localContentCanAccessRemoteUrls: true onJavaScriptConsoleMessage: { - console.log(sourceID + ":" + lineNumber + ":" + message); + webPreview.javaScriptMessage(level, sourceID, lineNumber, message); } onLoadingChanged: { if (!loading) { diff --git a/mix/qml/img/broom.png b/mix/qml/img/broom.png new file mode 100644 index 000000000..76a9a0e0c Binary files /dev/null and b/mix/qml/img/broom.png differ diff --git a/mix/qml/img/copy.png b/mix/qml/img/copy.png new file mode 100644 index 000000000..72a1e62bc Binary files /dev/null and b/mix/qml/img/copy.png differ diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index d308119ae..413e61b85 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -105,7 +105,6 @@ function loadProject(path) { contractSources[doc.documentId] = fileIo.readFile(doc.path); } codeModel.reset(contractSources); - } function addFile(fileName) { @@ -377,8 +376,6 @@ function finalizeDeployment(deploymentId, addresses) { else insertAt += 6; html = html.substr(0, insertAt) + - "" + - "" + "" + html.substr(insertAt); fileIo.writeFile(deploymentDir + doc.fileName, html); @@ -388,9 +385,8 @@ function finalizeDeployment(deploymentId, addresses) { } //write deployment js var deploymentJs = - "// Autogenerated by Mix\n" + - "web3 = require(\"web3\");\n" + - "contracts = {};\n"; + "// Autogenerated by Mix\n" + + "contracts = {};\n"; for (var c in codeModel.contracts) { var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]"; deploymentJs += contractAccessor + " = {\n" + @@ -400,9 +396,6 @@ function finalizeDeployment(deploymentId, addresses) { contractAccessor + ".contract = web3.eth.contract(" + contractAccessor + ".address, " + contractAccessor + ".interface);\n"; } fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs); - //copy scripts - fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js"); - fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js"); deploymentAddresses = addresses; saveProject(); @@ -435,7 +428,7 @@ function checkEthPath(dappUrl, callBack) //register() jsonrpc: "2.0", method: "eth_call", - params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ], + params: [ { "gas": 150000, "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ], id: jsonRpcRequestId++ }); rpcCall(requests, function (httpRequest, response) { @@ -472,7 +465,7 @@ function checkRegistration(dappUrl, addr, callBack) //getOwner() jsonrpc: "2.0", method: "eth_call", - params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ], + params: [ { "gas" : 2000, "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ], id: jsonRpcRequestId++ }); @@ -537,7 +530,7 @@ function checkRegistration(dappUrl, addr, callBack) //setRegister() jsonrpc: "2.0", method: "eth_transact", - params: [ { "from": deploymentDialog.currentAccount, "gas": 2000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ], + params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ], id: jsonRpcRequestId++ }); @@ -570,7 +563,7 @@ function registerContentHash(registrar, callBack) //setContent() jsonrpc: "2.0", method: "eth_transact", - params: [ { "from": deploymentDialog.currentAccount, "gas": 2000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ], + params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ], id: jsonRpcRequestId++ }); rpcCall(requests, function (httpRequest, response) { @@ -587,7 +580,7 @@ function registerToUrlHint() //urlHint => suggestUrl jsonrpc: "2.0", method: "eth_transact", - params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 2000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ], + params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ], id: jsonRpcRequestId++ }); diff --git a/mix/qml/qmldir b/mix/qml/qmldir index 0f3fcd88c..73c117941 100644 --- a/mix/qml/qmldir +++ b/mix/qml/qmldir @@ -5,3 +5,4 @@ singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml singleton StateStyle 1.0 StateStyle.qml singleton StatusPaneStyle 1.0 StatusPaneStyle.qml singleton WebPreviewStyle 1.0 WebPreviewStyle.qml +singleton LogsPaneStyle 1.0 LogsPaneStyle.qml diff --git a/mix/res.qrc b/mix/res.qrc index 515c5fadf..e953b3e35 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -111,5 +111,9 @@ qml/img/exit.png qml/img/run.png qml/img/note.png + qml/LogsPane.qml + qml/img/copy.png + qml/img/broom.png + qml/LogsPaneStyle.qml diff --git a/pullSubtree.sh b/pullSubtree.sh new file mode 100755 index 000000000..5f20f5123 --- /dev/null +++ b/pullSubtree.sh @@ -0,0 +1,32 @@ +# usage +# ./pullsubtree [repository branch] [repository2 branch2] +# +# example +# ./pullSubtree evmjit master +# ./pullSubtree ethereumjs develop +# ./pullSubtree evmjit master ethereumjs master + +evmjit_repo="https://github.com/ethereum/evmjit" +evmjit_location="evmjit" + +ethereumjs_repo="https://github.com/ethereum/ethereum.js" +ethereumjs_location="libjsqrc/ethereumjs" + +natspecjs_repo="https://github.com/ethereum/natspec.js" +natspecjs_location="libnatspec/natspecjs" + +while [ "$1" != "" ]; do + case $1 in + evmjit | ethereumjs | natspecjs ) + REPO="${1}_repo" + REPO=${!REPO} + LOCATION="${1}_location" + LOCATION=${!LOCATION} + shift + BRANCH=$1 + git subtree pull --prefix=${LOCATION} ${REPO} ${BRANCH} --squash + ;; + esac + shift +done + diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 08acb5ebd..6ed90cdea 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -272,7 +272,7 @@ bool CommandLineInterface::processInput() while (!cin.eof()) { getline(cin, s); - m_sourceCodes[""].append(s); + m_sourceCodes[""].append(s + '\n'); } } else @@ -345,18 +345,11 @@ bool CommandLineInterface::processInput() void CommandLineInterface::handleAst(string const& _argStr) { string title; - string suffix; if (_argStr == g_argAstStr) - { title = "Syntax trees:"; - suffix = ".ast"; - } else if (_argStr == g_argAstJson) - { title = "JSON AST:"; - suffix = ".json"; - } else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal argStr for AST")); diff --git a/solc/docker_emscripten/Dockerfile b/solc/docker_emscripten/Dockerfile index b593cbf74..06467a2c5 100644 --- a/solc/docker_emscripten/Dockerfile +++ b/solc/docker_emscripten/Dockerfile @@ -10,6 +10,7 @@ RUN apt-get install -qy automake libtool yasm scons RUN useradd -ms /bin/bash user USER user +ENV HOME /home/user WORKDIR /home/user # Emscripten SDK diff --git a/test/Assembly.cpp b/test/Assembly.cpp index 3869919e7..fbc8e47b5 100644 --- a/test/Assembly.cpp +++ b/test/Assembly.cpp @@ -74,17 +74,19 @@ eth::AssemblyItems compileContract(const string& _sourceCode) return AssemblyItems(); } -void checkAssemblyLocations(AssemblyItems const& _items, std::vector _locations) +void checkAssemblyLocations(AssemblyItems const& _items, vector const& _locations) { - size_t i = 0; BOOST_CHECK_EQUAL(_items.size(), _locations.size()); - for (auto const& it: _items) + for (size_t i = 0; i < min(_items.size(), _locations.size()); ++i) { - BOOST_CHECK_MESSAGE(it.getLocation() == _locations[i], - std::string("Location mismatch for assembly item ") + std::to_string(i)); - ++i; + BOOST_CHECK_MESSAGE( + _items[i].getLocation() == _locations[i], + "Location mismatch for assembly item " + to_string(i) + ". Found: " + + to_string(_items[i].getLocation().start) + "-" + + to_string(_items[i].getLocation().end) + ", expected: " + + to_string(_locations[i].start) + "-" + + to_string(_locations[i].end)); } - } } // end anonymous namespace @@ -93,31 +95,21 @@ BOOST_AUTO_TEST_SUITE(Assembly) BOOST_AUTO_TEST_CASE(location_test) { - char const* sourceCode = "contract test {\n" - " function f() returns (uint256 a)\n" - " {\n" - " return 16;\n" - " }\n" - "}\n"; - std::shared_ptr n = make_shared("source"); + char const* sourceCode = R"( + contract test { + function f() returns (uint256 a) { + return 16; + } + } + )"; + shared_ptr n = make_shared("source"); AssemblyItems items = compileContract(sourceCode); - std::vector locations { - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(), SourceLocation(), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(), SourceLocation(), SourceLocation(), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), SourceLocation(0, 77, n), - SourceLocation(0, 77, n), - SourceLocation(18, 75, n), SourceLocation(40, 49, n), - SourceLocation(61, 70, n), SourceLocation(61, 70, n), SourceLocation(61, 70, n), - SourceLocation(), SourceLocation(), - SourceLocation(61, 70, n), SourceLocation(61, 70, n), SourceLocation(61, 70, n) - }; + vector locations = + vector(11, SourceLocation(2, 75, n)) + + vector(12, SourceLocation(20, 72, n)) + + vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + + vector(4, SourceLocation(58, 67, n)) + + vector(3, SourceLocation(20, 72, n)); checkAssemblyLocations(items, locations); } diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index ae2417052..3205c038a 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1031,6 +1031,17 @@ BOOST_AUTO_TEST_CASE(blockchain) BOOST_CHECK(callContractFunctionWithValue("someInfo()", 28) == encodeArgs(28, 0, 1)); } +BOOST_AUTO_TEST_CASE(now) +{ + char const* sourceCode = "contract test {\n" + " function someInfo() returns (bool success) {\n" + " return block.timestamp == now && now > 0;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("someInfo()") == encodeArgs(true)); +} + BOOST_AUTO_TEST_CASE(function_types) { char const* sourceCode = "contract test {\n" diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index befd571ea..9ac64107d 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -181,46 +181,35 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost) m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); // export post state - json_spirit::mObject postState; - - for (auto const& a: _statePost.addresses()) - { - json_spirit::mObject o; - o["balance"] = toString(_statePost.balance(a.first)); - o["nonce"] = toString(_statePost.transactionsFrom(a.first)); - { - json_spirit::mObject store; - for (auto const& s: _statePost.storage(a.first)) - store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second)); - o["storage"] = store; - } - o["code"] = "0x" + toHex(_statePost.code(a.first)); - - postState[toString(a.first)] = o; - } - m_TestObject["post"] = json_spirit::mValue(postState); + m_TestObject["post"] = fillJsonWithState(_statePost); m_TestObject["postStateRoot"] = toHex(_statePost.rootHash().asBytes()); // export pre state - json_spirit::mObject preState; + m_TestObject["pre"] = fillJsonWithState(m_statePre); +} + +json_spirit::mObject fillJsonWithState(State _state) +{ + // export pre state + json_spirit::mObject oState; - for (auto const& a: m_statePre.addresses()) + for (auto const& a: _state.addresses()) { json_spirit::mObject o; - o["balance"] = toString(m_statePre.balance(a.first)); - o["nonce"] = toString(m_statePre.transactionsFrom(a.first)); + o["balance"] = toString(_state.balance(a.first)); + o["nonce"] = toString(_state.transactionsFrom(a.first)); { json_spirit::mObject store; - for (auto const& s: m_statePre.storage(a.first)) + for (auto const& s: _state.storage(a.first)) store["0x"+toHex(toCompactBigEndian(s.first))] = "0x"+toHex(toCompactBigEndian(s.second)); o["storage"] = store; } - o["code"] = "0x" + toHex(m_statePre.code(a.first)); + o["code"] = "0x" + toHex(_state.code(a.first)); - preState[toString(a.first)] = o; + oState[toString(a.first)] = o; } - m_TestObject["pre"] = json_spirit::mValue(preState); + return oState; } u256 toInt(json_spirit::mValue const& _v) diff --git a/test/TestHelper.h b/test/TestHelper.h index 0f23f945c..d378848a2 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -117,6 +117,13 @@ private: json_spirit::mObject& m_TestObject; }; +class ZeroGasPricer: public eth::GasPricer +{ +protected: + u256 ask(eth::State const&) const override { return 0; } + u256 bid(eth::TransactionPriority = eth::TransactionPriority::Medium) const override { return 0; } +}; + // helping functions u256 toInt(json_spirit::mValue const& _v); byte toByte(json_spirit::mValue const& _v); @@ -136,6 +143,7 @@ void userDefinedTest(std::string testTypeFlag, std::function void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) diff --git a/test/bcUncleTestFiller.json b/test/bcUncleTestFiller.json index 639051f2b..73d1e5487 100644 --- a/test/bcUncleTestFiller.json +++ b/test/bcUncleTestFiller.json @@ -265,23 +265,7 @@ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" }, { - "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "coinbase" : "0000000000000000000000000000000000000000", - "difficulty" : "131072", - "extraData" : "0x", - "gasLimit" : "99902343", - "gasUsed" : "0", - "hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04", - "mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770", - "nonce" : "18a524c1790fa83b", - "number" : "2", - "parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae", - "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "seedHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd", - "timestamp" : "0x54c98c82", - "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + "sameAsPreviousSibling" : "1" } ] } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 0e1128ef9..eca08d09e 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -52,6 +52,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) ImportTest importer(o["pre"].get_obj()); State state(Address(), OverlayDB(), BaseState::Empty); importer.importState(o["pre"].get_obj(), state); + o["pre"] = fillJsonWithState(state); state.commit(); if (_fillin) @@ -87,7 +88,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) // get txs TransactionQueue txs; - TrivialGasPricer gp; + ZeroGasPricer gp; BOOST_REQUIRE(blObj.count("transactions")); for (auto const& txObj: blObj["transactions"].get_array()) { @@ -101,10 +102,18 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) BlockQueue uncleBlockQueue; mArray aUncleList; vector vBiUncles; + mObject uncleHeaderObj_pre; for (auto const& uHObj: blObj["uncleHeaders"].get_array()) { mObject uncleHeaderObj = uHObj.get_obj(); + if (uncleHeaderObj.count("sameAsPreviousSibling")) + { + writeBlockHeaderToJson(uncleHeaderObj_pre, vBiUncles[vBiUncles.size()-1]); + aUncleList.push_back(uncleHeaderObj_pre); + vBiUncles.push_back(vBiUncles[vBiUncles.size()-1]); + continue; + } BlockInfo uncleBlockFromFields = constructBlock(uncleHeaderObj); // make uncle header valid @@ -123,6 +132,8 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) cnote << "import uncle in blockQueue"; RLPStream uncle = createFullBlockFromHeader(uncleBlockFromFields); uncleBlockQueue.import(&uncle.out(), bc); + + uncleHeaderObj_pre = uncleHeaderObj; } blObj["uncleHeaders"] = aUncleList; @@ -579,6 +590,7 @@ void updatePoW(BlockInfo& _bi) ret = pow.mine(_bi, 10000, true, true); // tie(ret, blockFromFields.nonce) Ethash::assignResult(ret.second, _bi); } + _bi.hash = _bi.headerHash(WithNonce); } void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi) diff --git a/test/crypto.cpp b/test/crypto.cpp index 291893f59..dbbc2dfa0 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -15,6 +15,7 @@ along with cpp-ethereum. If not, see . */ /** @file crypto.cpp + * @author Alex Leverington * @author Gav Wood * @date 2014 * Crypto test functions. @@ -228,6 +229,82 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } +BOOST_AUTO_TEST_CASE(sha3_norestart) +{ + CryptoPP::SHA3_256 ctx; + bytes input(asBytes("test")); + ctx.Update(input.data(), 4); + CryptoPP::SHA3_256 ctxCopy(ctx); + bytes interimDigest(32); + ctx.Final(interimDigest.data()); + ctx.Update(input.data(), 4); + bytes firstDigest(32); + ctx.Final(firstDigest.data()); + BOOST_REQUIRE(interimDigest == firstDigest); + + ctxCopy.Update(input.data(), 4); + bytes finalDigest(32); + ctxCopy.Final(interimDigest.data()); + BOOST_REQUIRE(interimDigest != finalDigest); + + // we can do this another way -- copy the context for final + ctxCopy.Update(input.data(), 4); + ctxCopy.Update(input.data(), 4); + CryptoPP::SHA3_256 finalCtx(ctxCopy); + bytes finalDigest2(32); + finalCtx.Final(finalDigest2.data()); + BOOST_REQUIRE(finalDigest2 == interimDigest); + ctxCopy.Update(input.data(), 4); + bytes finalDigest3(32); + finalCtx.Final(finalDigest3.data()); + BOOST_REQUIRE(finalDigest2 != finalDigest3); +} + +BOOST_AUTO_TEST_CASE(ecies_kdf) +{ + KeyPair local = KeyPair::create(); + KeyPair remote = KeyPair::create(); + // nonce + Secret z1; + ecdh::agree(local.sec(), remote.pub(), z1); + auto key1 = s_secp256k1.eciesKDF(z1, bytes(), 64); + bytesConstRef eKey1 = bytesConstRef(&key1).cropped(0, 32); + bytesRef mKey1 = bytesRef(&key1).cropped(32, 32); + sha3(mKey1, mKey1); + + Secret z2; + ecdh::agree(remote.sec(), local.pub(), z2); + auto key2 = s_secp256k1.eciesKDF(z2, bytes(), 64); + bytesConstRef eKey2 = bytesConstRef(&key2).cropped(0, 32); + bytesRef mKey2 = bytesRef(&key2).cropped(32, 32); + sha3(mKey2, mKey2); + + BOOST_REQUIRE(eKey1.toBytes() == eKey2.toBytes()); + BOOST_REQUIRE(mKey1.toBytes() == mKey2.toBytes()); + + BOOST_REQUIRE((u256)h256(z1) > 0); + BOOST_REQUIRE(z1 == z2); + + BOOST_REQUIRE(key1.size() > 0 && ((u512)h512(key1)) > 0); + BOOST_REQUIRE(key1 == key2); +} + +BOOST_AUTO_TEST_CASE(ecies_standard) +{ + KeyPair k = KeyPair::create(); + + string message("Now is the time for all good persons to come to the aid of humanity."); + string original = message; + bytes b = asBytes(message); + + s_secp256k1.encryptECIES(k.pub(), b); + BOOST_REQUIRE(b != asBytes(original)); + BOOST_REQUIRE(b.size() > 0 && b[0] == 0x04); + + s_secp256k1.decryptECIES(k.sec(), b); + BOOST_REQUIRE(bytesConstRef(&b).cropped(0, original.size()).toBytes() == asBytes(original)); +} + BOOST_AUTO_TEST_CASE(ecies_eckeypair) { KeyPair k = KeyPair::create(); @@ -316,14 +393,233 @@ BOOST_AUTO_TEST_CASE(ecdhe) BOOST_REQUIRE_EQUAL(sremote, slocal); } -BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) +BOOST_AUTO_TEST_CASE(handshakeNew) { - // New connections require new ECDH keypairs - // Every new connection requires a new EC keypair - // Every new trust requires a new EC keypair - // All connections should share seed for PRF (or PRNG) for nonces + // authInitiator -> E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) + // authRecipient -> E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) + + h256 base(sha3("privacy")); + sha3(base.ref(), base.ref()); + Secret nodeAsecret(base); + KeyPair nodeA(nodeAsecret); + BOOST_REQUIRE(nodeA.pub()); + + sha3(base.ref(), base.ref()); + Secret nodeBsecret(base); + KeyPair nodeB(nodeBsecret); + BOOST_REQUIRE(nodeB.pub()); + + BOOST_REQUIRE_NE(nodeA.sec(), nodeB.sec()); + + // Initiator is Alice (nodeA) + ECDHE eA; + bytes nAbytes(fromHex("0xAAAA")); + h256 nonceA(sha3(nAbytes)); + bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); + Secret ssA; + { + bytesRef sig(&auth[0], Signature::size); + bytesRef hepubk(&auth[Signature::size], h256::size); + bytesRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + + crypto::ecdh::agree(nodeA.sec(), nodeB.pub(), ssA); + sign(eA.seckey(), ssA ^ nonceA).ref().copyTo(sig); + sha3(eA.pubkey().ref(), hepubk); + nodeA.pub().ref().copyTo(pubk); + nonceA.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + } + bytes authcipher; + encrypt(nodeB.pub(), &auth, authcipher); + BOOST_REQUIRE_EQUAL(authcipher.size(), 279); + + // Receipient is Bob (nodeB) + ECDHE eB; + bytes nBbytes(fromHex("0xBBBB")); + h256 nonceB(sha3(nAbytes)); + bytes ack(Public::size + h256::size + 1); + { + // todo: replace nodeA.pub() in encrypt() + // decrypt public key from auth + bytes authdecrypted; + decrypt(nodeB.sec(), &authcipher, authdecrypted); + Public node; + bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); + pubk.copyTo(node.ref()); + + bytesRef epubk(&ack[0], Public::size); + bytesRef nonce(&ack[Public::size], h256::size); + + eB.pubkey().ref().copyTo(epubk); + nonceB.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + } + bytes ackcipher; + encrypt(nodeA.pub(), &ack, ackcipher); + BOOST_REQUIRE_EQUAL(ackcipher.size(), 182); + + BOOST_REQUIRE(eA.pubkey()); + BOOST_REQUIRE(eB.pubkey()); + BOOST_REQUIRE_NE(eA.seckey(), eB.seckey()); + + /// Alice (after receiving ack) + Secret aEncryptK; + Secret aMacK; + Secret aEgressMac; + Secret aIngressMac; + { + bytes ackdecrypted; + decrypt(nodeA.sec(), &ackcipher, ackdecrypted); + BOOST_REQUIRE(ackdecrypted.size()); + bytesConstRef ackRef(&ackdecrypted); + Public eBAck; + h256 nonceBAck; + ackRef.cropped(0, Public::size).copyTo(bytesRef(eBAck.data(), Public::size)); + ackRef.cropped(Public::size, h256::size).copyTo(nonceBAck.ref()); + BOOST_REQUIRE_EQUAL(eBAck, eB.pubkey()); + BOOST_REQUIRE_EQUAL(nonceBAck, nonceB); + + // TODO: export ess and require equal to b + + bytes keyMaterialBytes(512); + bytesRef keyMaterial(&keyMaterialBytes); + + h256 ess; + // todo: ecdh-agree should be able to output bytes + eA.agree(eBAck, ess); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// auto token = sha3(ssA); + aEncryptK = sha3(keyMaterial); + aEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + aMacK = sha3(keyMaterial); + + keyMaterialBytes.resize(h256::size + authcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (aMacK ^ nonceBAck).ref().copyTo(keyMaterial); + bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); + aEgressMac = sha3(keyMaterial); + + keyMaterialBytes.resize(h256::size + ackcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (aMacK ^ nonceA).ref().copyTo(keyMaterial); + bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); + aIngressMac = sha3(keyMaterial); + } + + + /// Bob (after sending ack) + Secret ssB; + crypto::ecdh::agree(nodeB.sec(), nodeA.pub(), ssB); + BOOST_REQUIRE_EQUAL(ssA, ssB); + Secret bEncryptK; + Secret bMacK; + Secret bEgressMac; + Secret bIngressMac; + { + bytes authdecrypted; + decrypt(nodeB.sec(), &authcipher, authdecrypted); + BOOST_REQUIRE(authdecrypted.size()); + bytesConstRef ackRef(&authdecrypted); + Signature sigAuth; + h256 heA; + Public eAAuth; + Public nodeAAuth; + h256 nonceAAuth; + bytesConstRef sig(&authdecrypted[0], Signature::size); + bytesConstRef hepubk(&authdecrypted[Signature::size], h256::size); + bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&authdecrypted[Signature::size + h256::size + Public::size], h256::size); + + nonce.copyTo(nonceAAuth.ref()); + pubk.copyTo(nodeAAuth.ref()); + BOOST_REQUIRE(nonceAAuth); + BOOST_REQUIRE_EQUAL(nonceA, nonceAAuth); + BOOST_REQUIRE(nodeAAuth); + BOOST_REQUIRE_EQUAL(nodeA.pub(), nodeAAuth); // bad test, bad!!! + hepubk.copyTo(heA.ref()); + sig.copyTo(sigAuth.ref()); + + Secret ss; + s_secp256k1.agree(nodeB.sec(), nodeAAuth, ss); + eAAuth = recover(sigAuth, ss ^ nonceAAuth); + // todo: test when this fails; means remote is bad or packet bits were flipped + BOOST_REQUIRE_EQUAL(heA, sha3(eAAuth)); + BOOST_REQUIRE_EQUAL(eAAuth, eA.pubkey()); + + bytes keyMaterialBytes(512); + bytesRef keyMaterial(&keyMaterialBytes); + + h256 ess; + // todo: ecdh-agree should be able to output bytes + eB.agree(eAAuth, ess); +// s_secp256k1.agree(eB.seckey(), eAAuth, ess); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// auto token = sha3(ssA); + bEncryptK = sha3(keyMaterial); + bEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + bMacK = sha3(keyMaterial); + + // todo: replace nonceB with decrypted nonceB + keyMaterialBytes.resize(h256::size + ackcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (bMacK ^ nonceAAuth).ref().copyTo(keyMaterial); + bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); + bEgressMac = sha3(keyMaterial); + + keyMaterialBytes.resize(h256::size + authcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (bMacK ^ nonceB).ref().copyTo(keyMaterial); + bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); + bIngressMac = sha3(keyMaterial); + } + + BOOST_REQUIRE_EQUAL(aEncryptK, bEncryptK); + BOOST_REQUIRE_EQUAL(aMacK, bMacK); + BOOST_REQUIRE_EQUAL(aEgressMac, bIngressMac); + BOOST_REQUIRE_EQUAL(bEgressMac, aIngressMac); + + + +} + +BOOST_AUTO_TEST_CASE(ecies_aes128_ctr_unaligned) +{ + Secret encryptK(sha3("...")); + h256 egressMac(sha3("+++")); + // TESTING: send encrypt magic sequence + bytes magic {0x22,0x40,0x08,0x91}; + bytes magicCipherAndMac; + encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h128()); + + magicCipherAndMac.resize(magicCipherAndMac.size() + 32); + sha3mac(egressMac.ref(), &magic, egressMac.ref()); + egressMac.ref().copyTo(bytesRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); + + bytes plaintext; + bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32); + decryptSymNoAuth(encryptK, h128(), cipher, plaintext); + + plaintext.resize(magic.size()); + BOOST_REQUIRE(plaintext.size() > 0); + BOOST_REQUIRE(magic == plaintext); +} + +BOOST_AUTO_TEST_CASE(ecies_aes128_ctr) +{ + Secret k(sha3("0xAAAA")); + string m = "AAAAAAAAAAAAAAAA"; + bytesConstRef msg((byte*)m.data(), m.size()); + + bytes ciphertext; + auto iv = encryptSymNoAuth(k, msg, ciphertext); + bytes plaintext; + decryptSymNoAuth(k, iv, &ciphertext, plaintext); + BOOST_REQUIRE_EQUAL(asString(plaintext), m); } BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) @@ -357,14 +653,14 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) // 68 % 255 should be difference of counter e.ProcessData(out, in, text.size()); - ctr = h128(u128(ctr) + text.size() % 16); + ctr = h128(u128(ctr) + text.size() / 16); BOOST_REQUIRE(text != original); cipherCopy = text; } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } try @@ -374,9 +670,9 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) d.ProcessData(out, in, text.size()); BOOST_REQUIRE(text == original); } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } @@ -394,9 +690,9 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) // yep, ctr mode. BOOST_REQUIRE(cipherCopy == original); } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } } diff --git a/test/natspec.cpp b/test/natspec.cpp index 8ba660418..cdcedca46 100644 --- a/test/natspec.cpp +++ b/test/natspec.cpp @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(natspec_eval_function_exists) // given NatspecExpressionEvaluator e; // when - string result = e.evalExpression("`typeof evaluateExpression`").toStdString(); + string result = e.evalExpression("`typeof natspec.evaluateExpression`").toStdString(); // then BOOST_CHECK_EQUAL(result, "function"); } @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_input_params) // given char const* abi = R"([ { - "name": "f", + "name": "multiply", "constant": false, "type": "function", "inputs": [ @@ -94,7 +94,18 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_input_params) ] } ])"; - NatspecExpressionEvaluator e(abi, "'f'", "[4]"); + + char const* transaction = R"({ + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000004" + }], + "id": 6 + })"; + + NatspecExpressionEvaluator e(abi, transaction , "multiply"); // when string result = e.evalExpression("Will multiply `a` by 7 and return `a * 7`.").toStdString(); // then @@ -108,7 +119,7 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_error) // when string result = e.evalExpression("`test(`").toStdString(); // then - BOOST_CHECK_EQUAL(result, "`test(`"); + BOOST_CHECK_EQUAL(result, "Natspec evaluation failed, wrong input params"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/peer.cpp b/test/peer.cpp index 7f3c19e1e..14712d4f1 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -32,6 +32,9 @@ BOOST_AUTO_TEST_SUITE(p2p) BOOST_AUTO_TEST_CASE(host) { + auto oldLogVerbosity = g_logVerbosity; + g_logVerbosity = 10; + NetworkPreferences host1prefs(30301, "127.0.0.1", true, true); NetworkPreferences host2prefs(30302, "127.0.0.1", true, true); @@ -44,10 +47,14 @@ BOOST_AUTO_TEST_CASE(host) host1.addNode(node2, "127.0.0.1", host2prefs.listenPort, host2prefs.listenPort); - this_thread::sleep_for(chrono::seconds(1)); + this_thread::sleep_for(chrono::seconds(3)); + + auto host1peerCount = host1.peerCount(); + auto host2peerCount = host2.peerCount(); + BOOST_REQUIRE_EQUAL(host1peerCount, 1); + BOOST_REQUIRE_EQUAL(host2peerCount, 1); - BOOST_REQUIRE_EQUAL(host1.peerCount(), 1); - BOOST_REQUIRE_EQUAL(host2.peerCount(), host1.peerCount()); + g_logVerbosity = oldLogVerbosity; } BOOST_AUTO_TEST_CASE(save_nodes) @@ -56,6 +63,7 @@ BOOST_AUTO_TEST_CASE(save_nodes) for (auto i:{0,1,2,3,4,5}) { Host* h = new Host("Test", NetworkPreferences(30300 + i, "127.0.0.1", true, true)); + h->setIdealPeerCount(10); // starting host is required so listenport is available h->start(); while (!h->isStarted()) @@ -71,7 +79,7 @@ BOOST_AUTO_TEST_CASE(save_nodes) for (auto const& h: hosts) host2.addNode(h->id(), "127.0.0.1", h->listenPort(), h->listenPort()); - this_thread::sleep_for(chrono::milliseconds(1000)); + this_thread::sleep_for(chrono::milliseconds(2000)); bytes firstHostNetwork(host.saveNetwork()); bytes secondHostNetwork(host.saveNetwork()); diff --git a/test/rlpx.cpp b/test/rlpx.cpp new file mode 100644 index 000000000..6a86652fb --- /dev/null +++ b/test/rlpx.cpp @@ -0,0 +1,455 @@ +/* + 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 crypto.cpp + * @author Alex Leverington + * @date 2015 + * RLPx test functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::crypto; +using namespace CryptoPP; + +BOOST_AUTO_TEST_SUITE(rlpx) + +static Secp256k1 s_secp256k1; +static CryptoPP::AutoSeededRandomPool s_rng; +static CryptoPP::OID s_curveOID(CryptoPP::ASN1::secp256k1()); +static CryptoPP::DL_GroupParameters_EC s_params(s_curveOID); +static CryptoPP::DL_GroupParameters_EC::EllipticCurve s_curve(s_params.GetCurve()); + +BOOST_AUTO_TEST_CASE(test_secrets_cpp_vectors) +{ + KeyPair init(Secret(sha3("initiator"))); + KeyPair initR(Secret(sha3("initiator-random"))); + h256 initNonce(sha3("initiator-nonce")); + + KeyPair recv(Secret(sha3("remote-recv"))); + KeyPair recvR(Secret(sha3("remote-recv-random"))); + h256 recvNonce(sha3("remote-recv-nonce")); + + bytes authCipher(fromHex("")); + bytes ackCipher(fromHex("")); + + CryptoPP::CTR_Mode::Encryption m_frameEnc; + CryptoPP::CTR_Mode::Encryption m_frameDec; + CryptoPP::ECB_Mode::Encryption m_macEnc; + CryptoPP::SHA3_256 m_egressMac; + CryptoPP::SHA3_256 m_ingressMac; + + // when originated is true, agreement is with init secrets + // when originated is true, remoteNonce = recvNonce + // when originated is true, nonce = initNonce + bool originated = true; + auto remoteNonce = recvNonce; + auto nonce = initNonce; + bytes keyMaterialBytes(64); + bytesRef keyMaterial(&keyMaterialBytes); + + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + s_secp256k1.agree(initR.sec(), recvR.pub(), ephemeralShared); + Secret expected(fromHex("20d82c1092f351dc217bd66fa183e801234af14ead40423b6ee25112201c6e5a")); + BOOST_REQUIRE(expected == ephemeralShared); + + ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); + h512 nonceMaterial; + h256 const& leftNonce = originated ? remoteNonce : nonce; + h256 const& rightNonce = originated ? nonce : remoteNonce; + leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); + rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); + auto outRef(keyMaterial.cropped(h256::size, h256::size)); + sha3(nonceMaterial.ref(), outRef); // output h(nonces) + + // test that keyMaterial = ecdhe-shared-secret || sha3(nonce || initiator-nonce) + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(leftNonce.data(), h256::size); + ctx.Update(rightNonce.data(), h256::size); + bytes expected(32); + ctx.Final(expected.data()); + bytes given(32); + outRef.copyTo(&given); + BOOST_REQUIRE(expected == given); + } + bytes preImage(keyMaterialBytes); + + // shared-secret <- sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + // keyMaterial = ecdhe-shared-secret || shared-secret + sha3(keyMaterial, outRef); + bytes sharedSecret(32); + outRef.copyTo(&sharedSecret); + BOOST_REQUIRE(sharedSecret == fromHex("b65319ce56e00f3be75c4d0da92b5957d5583ca25eeeedac8e29b6dfc8b1ddf7")); + + // test that keyMaterial = ecdhe-shared-secret || shared-secret + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(preImage.data(), preImage.size()); + bytes expected(32); + ctx.Final(expected.data()); + bytes test(32); + outRef.copyTo(&test); + BOOST_REQUIRE(expected == test); + } + + // token: sha3(outRef) + bytes token(32); + sha3(outRef, bytesRef(&token)); + BOOST_REQUIRE(token == fromHex("db41fe0180f372983cf19fca7ee890f7fb5481079d44683d2c027be9e71bbca2")); + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + bytes aesSecret(32); + outRef.copyTo(&aesSecret); + BOOST_REQUIRE(aesSecret == fromHex("12347b4784bcb4e74b84637940482852fe25d78e328cf5c6f7a396bf96cc20bb")); + m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + + // mac-secret = sha3(ecdhe-shared-secret || aes-secret) + sha3(keyMaterial, outRef); // output mac-secret + bytes macSecret(32); + outRef.copyTo(&macSecret); + BOOST_REQUIRE(macSecret == fromHex("2ec149072353d54437422837c886b0538a9206e6c559f6b4a55f65a866867723")); + m_macEnc.SetKey(outRef.data(), h128::size); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + (*(h256*)outRef.data() ^ remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = originated ? authCipher : ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); + + { + bytes egressMac; + SHA3_256 h(m_egressMac); + bytes digest(16); + h.TruncatedFinal(digest.data(), 16); + BOOST_REQUIRE(digest == fromHex("23e5e8efb6e3765ecae1fca9160b18df")); + } + + // recover mac-secret by re-xoring remoteNonce + (*(h256*)keyMaterial.data() ^ remoteNonce ^ nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = originated ? ackCipher : authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); + + { + bytes ingressMac; + SHA3_256 h(m_ingressMac); + bytes digest(16); + h.TruncatedFinal(digest.data(), 16); + BOOST_REQUIRE(digest == fromHex("ceed64135852064cbdde86e7ea05e8f5")); + } +} + +BOOST_AUTO_TEST_CASE(test_secrets_from_go) +{ + KeyPair init(Secret(fromHex("0x5e173f6ac3c669587538e7727cf19b782a4f2fda07c1eaa662c593e5e85e3051"))); + KeyPair initR(Secret(fromHex("0x19c2185f4f40634926ebed3af09070ca9e029f2edd5fae6253074896205f5f6c"))); + h256 initNonce(fromHex("0xcd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb11")); + + KeyPair recv(Secret(fromHex("0xc45f950382d542169ea207959ee0220ec1491755abe405cd7498d6b16adb6df8"))); + KeyPair recvR(Secret(fromHex("0xd25688cf0ab10afa1a0e2dba7853ed5f1e5bf1c631757ed4e103b593ff3f5620"))); + h256 recvNonce(fromHex("0xf37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a7")); + + bytes authCipher(fromHex("0x04a0274c5951e32132e7f088c9bdfdc76c9d91f0dc6078e848f8e3361193dbdc43b94351ea3d89e4ff33ddcefbc80070498824857f499656c4f79bbd97b6c51a514251d69fd1785ef8764bd1d262a883f780964cce6a14ff206daf1206aa073a2d35ce2697ebf3514225bef186631b2fd2316a4b7bcdefec8d75a1025ba2c5404a34e7795e1dd4bc01c6113ece07b0df13b69d3ba654a36e35e69ff9d482d88d2f0228e7d96fe11dccbb465a1831c7d4ad3a026924b182fc2bdfe016a6944312021da5cc459713b13b86a686cf34d6fe6615020e4acf26bf0d5b7579ba813e7723eb95b3cef9942f01a58bd61baee7c9bdd438956b426a4ffe238e61746a8c93d5e10680617c82e48d706ac4953f5e1c4c4f7d013c87d34a06626f498f34576dc017fdd3d581e83cfd26cf125b6d2bda1f1d56")); + bytes ackCipher(fromHex("0x049934a7b2d7f9af8fd9db941d9da281ac9381b5740e1f64f7092f3588d4f87f5ce55191a6653e5e80c1c5dd538169aa123e70dc6ffc5af1827e546c0e958e42dad355bcc1fcb9cdf2cf47ff524d2ad98cbf275e661bf4cf00960e74b5956b799771334f426df007350b46049adb21a6e78ab1408d5e6ccde6fb5e69f0f4c92bb9c725c02f99fa72b9cdc8dd53cff089e0e73317f61cc5abf6152513cb7d833f09d2851603919bf0fbe44d79a09245c6e8338eb502083dc84b846f2fee1cc310d2cc8b1b9334728f97220bb799376233e113")); + + bytes authPlainExpected(fromHex("0x884c36f7ae6b406637c1f61b2f57e1d2cab813d24c6559aaf843c3f48962f32f46662c066d39669b7b2e3ba14781477417600e7728399278b1b5d801a519aa570034fdb5419558137e0d44cd13d319afe5629eeccb47fd9dfe55cc6089426e46cc762dd8a0636e07a54b31169eba0c7a20a1ac1ef68596f1f283b5c676bae4064abfcce24799d09f67e392632d3ffdc12e3d6430dcb0ea19c318343ffa7aae74d4cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb1100")); + bytes ackPlainExpected(fromHex("0x802b052f8b066640bba94a4fc39d63815c377fced6fcb84d27f791c9921ddf3e9bf0108e298f490812847109cbd778fae393e80323fd643209841a3b7f110397f37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a700")); + + bytes authPlain = authCipher; + BOOST_REQUIRE(s_secp256k1.decryptECIES(recv.sec(), authPlain)); + bytes ackPlain = ackCipher; + BOOST_REQUIRE(s_secp256k1.decryptECIES(init.sec(), ackPlain)); + + CryptoPP::CTR_Mode::Encryption m_frameEnc; + CryptoPP::CTR_Mode::Encryption m_frameDec; + CryptoPP::ECB_Mode::Encryption m_macEnc; + CryptoPP::SHA3_256 m_egressMac; + CryptoPP::SHA3_256 m_ingressMac; + + // when originated is true, agreement is with init secrets + // when originated is true, remoteNonce = recvNonce + // when originated is true, nonce = initNonce + bool originated = true; + auto remoteNonce = recvNonce; + auto nonce = initNonce; + bytes keyMaterialBytes(64); + bytesRef keyMaterial(&keyMaterialBytes); + + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + s_secp256k1.agree(initR.sec(), recvR.pub(), ephemeralShared); + Secret expected(fromHex("0xe3f407f83fc012470c26a93fdff534100f2c6f736439ce0ca90e9914f7d1c381")); + BOOST_REQUIRE(expected == ephemeralShared); + + ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); + h512 nonceMaterial; + h256 const& leftNonce = originated ? remoteNonce : nonce; + h256 const& rightNonce = originated ? nonce : remoteNonce; + leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size)); + rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size)); + auto outRef(keyMaterial.cropped(h256::size, h256::size)); + sha3(nonceMaterial.ref(), outRef); // output h(nonces) + + // test that keyMaterial = ecdhe-shared-secret || sha3(nonce || initiator-nonce) + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(leftNonce.data(), h256::size); + ctx.Update(rightNonce.data(), h256::size); + bytes expected(32); + ctx.Final(expected.data()); + bytes given(32); + outRef.copyTo(&given); + BOOST_REQUIRE(expected == given); + } + bytes preImage(keyMaterialBytes); + + // shared-secret <- sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + // keyMaterial = ecdhe-shared-secret || shared-secret + sha3(keyMaterial, outRef); + + // test that keyMaterial = ecdhe-shared-secret || shared-secret + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(preImage.data(), preImage.size()); + bytes expected(32); + ctx.Final(expected.data()); + bytes test(32); + outRef.copyTo(&test); + BOOST_REQUIRE(expected == test); + } + + // token: sha3(outRef) + bytes token(32); + sha3(outRef, bytesRef(&token)); + BOOST_REQUIRE(token == fromHex("0x3f9ec2592d1554852b1f54d228f042ed0a9310ea86d038dc2b401ba8cd7fdac4")); + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + bytes aesSecret(32); + outRef.copyTo(&aesSecret); + BOOST_REQUIRE(aesSecret == fromHex("0xc0458fa97a5230830e05f4f20b7c755c1d4e54b1ce5cf43260bb191eef4e418d")); + m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data()); + + // mac-secret = sha3(ecdhe-shared-secret || aes-secret) + sha3(keyMaterial, outRef); // output mac-secret + bytes macSecret(32); + outRef.copyTo(&macSecret); + BOOST_REQUIRE(macSecret == fromHex("0x48c938884d5067a1598272fcddaa4b833cd5e7d92e8228c0ecdfabbe68aef7f1")); + m_macEnc.SetKey(outRef.data(), h256::size); + + // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) + // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) + // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) + // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) + + (*(h256*)outRef.data() ^ remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = originated ? authCipher : ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + m_egressMac.Update(keyMaterialBytes.data(), keyMaterialBytes.size()); + + { + bytes egressMac; + SHA3_256 h(m_egressMac); + bytes digest(32); + h.Final(digest.data()); + BOOST_REQUIRE(digest == fromHex("0x09771e93b1a6109e97074cbe2d2b0cf3d3878efafe68f53c41bb60c0ec49097e")); + } + + // recover mac-secret by re-xoring remoteNonce + bytes recoverMacSecretTest(32); + (*(h256*)keyMaterial.data() ^ remoteNonce).ref().copyTo(&recoverMacSecretTest); + BOOST_REQUIRE(recoverMacSecretTest == macSecret); + + (*(h256*)keyMaterial.data() ^ remoteNonce ^ nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = originated ? ackCipher : authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); + + { + bytes ingressMac; + SHA3_256 h(m_ingressMac); + bytes digest(32); + h.Final(digest.data()); + BOOST_CHECK(digest == fromHex("0x75823d96e23136c89666ee025fb21a432be906512b3dd4a3049e898adb433847")); + } + + bytes initHello(fromHex("6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9")); + + bytes recvHello(fromHex("6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8")); + + /// test macs of frame headers + { + SHA3_256 egressmac(m_egressMac); + SHA3_256 prevDigest(egressmac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + h128 encDigest; + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= *(h128*)initHello.data(); + egressmac.Update(encDigest.data(), h128::size); + egressmac.TruncatedFinal(encDigest.data(), h128::size); + + bytes provided(16); + bytesConstRef(&initHello).cropped(16, 16).copyTo(bytesRef(&provided)); + BOOST_REQUIRE(*(h128*)encDigest.data() == *(h128*)provided.data()); + } + + { + SHA3_256 ingressmac(m_ingressMac); + SHA3_256 prevDigest(ingressmac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + h128 encDigest; + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= *(h128*)recvHello.data(); + ingressmac.Update(encDigest.data(), h128::size); + ingressmac.TruncatedFinal(encDigest.data(), h128::size); + + bytes provided(16); + bytesConstRef(&recvHello).cropped(16, 16).copyTo(bytesRef(&provided)); + BOOST_REQUIRE(*(h128*)encDigest.data() == *(h128*)provided.data()); + } + + // test decrypt of frame headers for recvHello + bytes plaintext(16); + m_frameDec.ProcessData(plaintext.data(), recvHello.data(), h128::size); + +} + +BOOST_AUTO_TEST_CASE(ecies_interop_test_primitives) +{ + CryptoPP::SHA256 sha256ctx; + bytes emptyExpected(fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + bytes empty; + sha256ctx.Update(empty.data(), 0); + bytes emptyTestOut(32); + sha256ctx.Final(emptyTestOut.data()); + BOOST_REQUIRE(emptyExpected == emptyTestOut); + + bytes hash1Expected(fromHex("0x8949b278bbafb8da1aaa18cb724175c5952280f74be5d29ab4b37d1b45c84b08")); + bytes hash1input(fromHex("0x55a53b55afb12affff3c")); + sha256ctx.Update(hash1input.data(), hash1input.size()); + bytes hash1Out(32); + sha256ctx.Final(hash1Out.data()); + BOOST_REQUIRE(hash1Out == hash1Expected); + + h128 hmack(fromHex("0x07a4b6dfa06369a570f2dcba2f11a18f")); + CryptoPP::HMAC hmacctx(hmack.data(), h128::size); + bytes input(fromHex("0x4dcb92ed4fc67fe86832")); + hmacctx.Update(input.data(), input.size()); + bytes hmacExpected(fromHex("0xc90b62b1a673b47df8e395e671a68bfa68070d6e2ef039598bb829398b89b9a9")); + bytes hmacOut(hmacExpected.size()); + hmacctx.Final(hmacOut.data()); + BOOST_REQUIRE(hmacExpected == hmacOut); + + // go messageTag + bytes tagSecret(fromHex("0xaf6623e52208c596e17c72cea6f1cb09")); + bytes tagInput(fromHex("0x3461282bcedace970df2")); + bytes tagExpected(fromHex("0xb3ce623bce08d5793677ba9441b22bb34d3e8a7de964206d26589df3e8eb5183")); + CryptoPP::HMAC hmactagctx(tagSecret.data(), tagSecret.size()); + hmactagctx.Update(tagInput.data(), tagInput.size()); + h256 mac; + hmactagctx.Final(mac.data()); + BOOST_REQUIRE(mac.asBytes() == tagExpected); + + Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); + bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); + bytes test1; + test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); + BOOST_REQUIRE(test1 == expect1); + + Secret kdfInput2(fromHex("0x961c065873443014e0371f1ed656c586c6730bf927415757f389d92acf8268df")); + bytes kdfExpect2(fromHex("0x4050c52e6d9c08755e5a818ac66fabe478b825b1836fd5efc4d44e40d04dabcc")); + bytes kdfTest2; + kdfTest2 = s_secp256k1.eciesKDF(kdfInput2, bytes(), 32); + BOOST_REQUIRE(kdfTest2 == kdfExpect2); + + KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); + Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); + Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); + Secret agreeTest; + s_secp256k1.agree(k.sec(), p, agreeTest); + BOOST_REQUIRE(agreeExpected == agreeTest); + + KeyPair kmK(Secret(fromHex("0x57baf2c62005ddec64c357d96183ebc90bf9100583280e848aa31d683cad73cb"))); + bytes kmCipher(fromHex("0x04ff2c874d0a47917c84eea0b2a4141ca95233720b5c70f81a8415bae1dc7b746b61df7558811c1d6054333907333ef9bb0cc2fbf8b34abb9730d14e0140f4553f4b15d705120af46cf653a1dc5b95b312cf8444714f95a4f7a0425b67fc064d18f4d0a528761565ca02d97faffdac23de10")); + bytes kmPlain = kmCipher; + bytes kmExpected(asBytes("a")); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kmK.sec(), kmPlain)); + BOOST_REQUIRE(kmExpected == kmPlain); + + KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); + Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); + BOOST_REQUIRE(penc == kenc.pub()); + + bytes cipher1(fromHex("0x046f647e1bd8a5cd1446d31513bac233e18bdc28ec0e59d46de453137a72599533f1e97c98154343420d5f16e171e5107999a7c7f1a6e26f57bcb0d2280655d08fb148d36f1d4b28642d3bb4a136f0e33e3dd2e3cffe4b45a03fb7c5b5ea5e65617250fdc89e1a315563c20504b9d3a72555")); + bytes plainTest1 = cipher1; + bytes expectedPlain1 = asBytes("a"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest1)); + BOOST_REQUIRE(plainTest1 == expectedPlain1); + + bytes cipher2(fromHex("0x0443c24d6ccef3ad095140760bb143078b3880557a06392f17c5e368502d79532bc18903d59ced4bbe858e870610ab0d5f8b7963dd5c9c4cf81128d10efd7c7aa80091563c273e996578403694673581829e25a865191bdc9954db14285b56eb0043b6288172e0d003c10f42fe413222e273d1d4340c38a2d8344d7aadcbc846ee")); + bytes plainTest2 = cipher2; + bytes expectedPlain2 = asBytes("aaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest2)); + BOOST_REQUIRE(plainTest2 == expectedPlain2); + + bytes cipher3(fromHex("0x04c4e40c86bb5324e017e598c6d48c19362ae527af8ab21b077284a4656c8735e62d73fb3d740acefbec30ca4c024739a1fcdff69ecaf03301eebf156eb5f17cca6f9d7a7e214a1f3f6e34d1ee0ec00ce0ef7d2b242fbfec0f276e17941f9f1bfbe26de10a15a6fac3cda039904ddd1d7e06e7b96b4878f61860e47f0b84c8ceb64f6a900ff23844f4359ae49b44154980a626d3c73226c19e")); + bytes plainTest3 = cipher3; + bytes expectedPlain3 = asBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest3)); + BOOST_REQUIRE(plainTest3 == expectedPlain3); +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/test/stBlockHashTestFiller.json b/test/stBlockHashTestFiller.json index 5ecc5b1c7..32745e89b 100644 --- a/test/stBlockHashTestFiller.json +++ b/test/stBlockHashTestFiller.json @@ -3,7 +3,7 @@ "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "5", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000000", "currentDifficulty" : "256", "currentTimestamp" : 1, "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" @@ -16,7 +16,7 @@ "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "nonce" : "0", "code" : "", "storage": {} @@ -25,7 +25,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "28500", + "gasLimit" : "285000", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -37,7 +37,7 @@ "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "257", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentDifficulty" : "256", "currentTimestamp" : 1, "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" @@ -50,7 +50,7 @@ "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "nonce" : "0", "code" : "", "storage": {} @@ -59,7 +59,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "28500", + "gasLimit" : "285000", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -71,7 +71,7 @@ "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "257", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentDifficulty" : "256", "currentTimestamp" : 1, "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" @@ -84,7 +84,7 @@ "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "nonce" : "0", "code" : "", "storage": {} @@ -93,7 +93,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "28500", + "gasLimit" : "285000", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stInitCodeTestFiller.json b/test/stInitCodeTestFiller.json index 902290121..2c155468f 100644 --- a/test/stInitCodeTestFiller.json +++ b/test/stInitCodeTestFiller.json @@ -1,5 +1,5 @@ { - "TransactionContractCreation" : { + "TransactionCreateRandomInitCode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -58,7 +58,7 @@ "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "", - "value" : "1" + "value" : "100" } }, @@ -66,7 +66,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -74,7 +74,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "70000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -84,7 +84,7 @@ "transaction" : { "data" : "0x600a80600c6000396000f200600160008035811a8100", - "gasLimit" : "21590", + "gasLimit" : "22000", "gasPrice" : "3", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -123,7 +123,7 @@ } }, - "TransactionSuicideInitCode" : { + "TransactionCreateAutoSuicideContract" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -150,11 +150,11 @@ "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "", - "value" : "1" + "value" : "15" } }, - "TransactionStopInitCode" : { + "TransactionCreateStopInInitcode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -185,7 +185,7 @@ } }, - "TransactionCreateSuicideContract" : { + "TransactionCreateSuicideInInitcode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -220,7 +220,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -235,7 +235,7 @@ }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -245,7 +245,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "40000", + "gasLimit" : "100000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -258,7 +258,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -273,7 +273,7 @@ }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -283,7 +283,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "40000", + "gasLimit" : "400000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -347,7 +347,47 @@ "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 500 (SLOAD 0) 1 0 0 0 0)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 50000 (SLOAD 0) 1 0 0 0 0)}", + "storage": {} + }, + + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000", + "code" : "", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : + { + "data" : "0x00", + "gasLimit" : "20000000", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "0" + } + }, + + "CallContractToCreateContractOOGBonusGas" : { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "45678256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "095e7baea6a6c7c4c2dfeb977efac326af552d87": { + "balance": "112", + "nonce": "0", + "//": "(CREATE 0 64 32)", + "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 0 (SLOAD 0) 12 0 0 0 0)}", "storage": {} }, @@ -385,6 +425,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87": { "balance": "0", "nonce": "0", + "//": "Create should fail. So CALL goes to 0x0...0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 0 (SLOAD 0) 0 0 0 0 0)}", @@ -427,7 +468,7 @@ "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 0 (SLOAD 0) 1 0 0 0 0)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 100000 (SLOAD 0) 1 0 0 0 0)}", "storage": {} }, @@ -442,7 +483,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "20000000", + "gasLimit" : "1000000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -463,11 +504,11 @@ "pre" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87": { - "balance": "1000", + "balance": "10000", "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1001 11 21)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 100000 11 21)}", "storage": {} }, @@ -534,7 +575,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "100000000", + "currentGasLimit" : "1000000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -542,7 +583,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "{(MSTORE 0 0x15)(CALL 7000 0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b 0 0 32 32 32) (RETURN 0 64)}", "nonce" : "0", "storage" : { @@ -560,7 +601,7 @@ "transaction" : { "data" : "", - "gasLimit" : "25000", + "gasLimit" : "250000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stMemoryTestFiller.json b/test/stMemoryTestFiller.json index f34307443..676a88feb 100644 --- a/test/stMemoryTestFiller.json +++ b/test/stMemoryTestFiller.json @@ -25,7 +25,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "22000", + "gasLimit" : "220000", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stPreCompiledContractsFiller.json b/test/stPreCompiledContractsFiller.json index d571dfccc..9e5fa862a 100644 --- a/test/stPreCompiledContractsFiller.json +++ b/test/stPreCompiledContractsFiller.json @@ -12,7 +12,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -25,7 +25,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "365224", + "gasLimit" : "3652240", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "100000", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -46,7 +46,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 64 32) [[ 0 ]] (MOD (MLOAD 64) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 128 64 32) [[ 0 ]] (MOD (MLOAD 64) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -81,7 +81,41 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MLOAD 128) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MLOAD 128) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "365224", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "CallEcrecover0_gas3000": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : "0", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 3000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -102,7 +136,7 @@ } }, - "CallEcrecover0_gas500": { + "CallEcrecover0_BonusGas": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -115,7 +149,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 500 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 0 1 1 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -136,7 +170,7 @@ } }, - "CallEcrecover0_Gas499": { + "CallEcrecover0_Gas2999": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -149,7 +183,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 499 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 2999 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -183,7 +217,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) }", + "code": "{ [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -217,7 +251,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 1) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 1) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -251,7 +285,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 33 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 65 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 97 97 32) [[ 0 ]] (MOD (MLOAD 97) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 33 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 65 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 100000 1 0 0 97 97 32) [[ 0 ]] (MOD (MLOAD 97) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -285,7 +319,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code": "{ (MSTORE 0 0x2f380a2dea7e778d81affc2443403b8fe4644db442ae4862ff5bb3732829cdb9) (MSTORE 32 27) (MSTORE 64 0x6b65ccb0558806e9b097f27a396d08f964e37b8b7af6ceeb516ff86739fbea0a) (MSTORE 96 0x37cbc8d883e129a4b1ef9d5f1df53c4f21a3ef147cf2a50a4ede0eb06ce092d4) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "code": "{ (MSTORE 0 0x2f380a2dea7e778d81affc2443403b8fe4644db442ae4862ff5bb3732829cdb9) (MSTORE 32 27) (MSTORE 64 0x6b65ccb0558806e9b097f27a396d08f964e37b8b7af6ceeb516ff86739fbea0a) (MSTORE 96 0x37cbc8d883e129a4b1ef9d5f1df53c4f21a3ef147cf2a50a4ede0eb06ce092d4) [[ 2 ]] (CALL 100000 1 0 0 128 128 32) [[ 0 ]] (MOD (MLOAD 128) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -625,7 +659,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ [[ 2 ]] (CALL 500 3 0 0 0 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ [[ 2 ]] (CALL 600 3 0 0 0 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -659,7 +693,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 5 0xf34578907f) [[ 2 ]] (CALL 500 3 0 0 37 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 5 0xf34578907f) [[ 2 ]] (CALL 600 3 0 0 37 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -693,7 +727,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 0 0xf34578907f) [[ 2 ]] (CALL 500 3 0 0 37 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 0 0xf34578907f) [[ 2 ]] (CALL 600 3 0 0 37 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -727,7 +761,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 100 3 0 0 32 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 120 3 0 0 32 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -761,7 +795,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 99 3 0 0 32 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 119 3 0 0 32 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -795,7 +829,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "20000000", "nonce" : "0", - "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 500 3 0 0 1000000 0 32) [[ 0 ]] (MLOAD 0)}", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) [[ 2 ]] (CALL 600 3 0 0 1000000 0 32) [[ 0 ]] (MLOAD 0)}", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { diff --git a/test/stRefundTestFiller.json b/test/stRefundTestFiller.json index 9e867a508..b0d0e1747 100644 --- a/test/stRefundTestFiller.json +++ b/test/stRefundTestFiller.json @@ -304,6 +304,95 @@ "data" : "" } }, + + "refund_CallToSuicide" : { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ [[ 0 ]] (CALL 500 0xaaae7baea6a6c7c4c2dfeb977efac326af552aaa 0 0 0 0 0 )}", + "storage" : { + "0x01" : "0x01" + } + }, + "aaae7baea6a6c7c4c2dfeb977efac326af552aaa" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ (SUICIDE 0x095e7baea6a6c7c4c2dfeb977efac326af552d87) }", + "storage" : { + "0x01" : "0x01" + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "refund_CallToSuicideTwice" : { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ [[ 0 ]] (CALL 500 0xaaae7baea6a6c7c4c2dfeb977efac326af552aaa 0 0 0 0 0 ) (CALL 500 0xaaae7baea6a6c7c4c2dfeb977efac326af552aaa 0 0 0 0 0 )}", + "storage" : { + "0x01" : "0x01" + } + }, + "aaae7baea6a6c7c4c2dfeb977efac326af552aaa" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ (SUICIDE 0x095e7baea6a6c7c4c2dfeb977efac326af552d87) }", + "storage" : { + "0x01" : "0x01" + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "refund_CallA" : { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/stSolidityTestFiller.json b/test/stSolidityTestFiller.json index 948dc5f7a..f2a2a0aa0 100644 --- a/test/stSolidityTestFiller.json +++ b/test/stSolidityTestFiller.json @@ -3,7 +3,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000000000000000000", + "currentGasLimit" : "1000000000000000000000000", "currentNumber" : "120", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -11,7 +11,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "10000000000000000", + "balance" : "1000000000000000000", "code" : "", "nonce" : "0", "storage" : { @@ -19,7 +19,7 @@ }, "d94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "//" : " ", "//" : "contract TestContract ", "//" : "{ ", @@ -203,7 +203,7 @@ "//" : " a = new TestContract(); ", "//" : " } ", "//" : "} ", - "code" : "0x60003560e060020a900480630c4c9a8014610084578063296df0df146100965780632a9afb83146100a8578063380e4396146100ba5780634893d88a146100cc5780637ee17e12146100da578063981a3165146100e8578063a60eedda146100fa578063e0a9fd281461010c578063e97384dc1461011e578063ed973fe91461013057005b61008c6102c0565b8060005260206000f35b61009e61067b565b8060005260206000f35b6100b06101ba565b8060005260206000f35b6100c261049b565b8060005260206000f35b6100d461087d565b60006000f35b6100e26101a4565b60006000f35b6100f06102ab565b8060005260206000f35b610102610695565b8060005260206000f35b610114610732565b8060005260206000f35b61012661055a565b8060005260206000f35b610138610142565b8060005260206000f35b600060006060610889600039606060006000f0905080600160a060020a031663b9c3d0a5602060008260e060020a026000526004600060008660155a03f150505060005160e1146101925761019b565b600191506101a0565b600091505b5090565b60006060610889600039606060006000f0905090565b60006001905060005460ff14156101d0576101d9565b600090506102a8565b60025460005414156101ea576101f3565b600090506102a8565b600154600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b141561021f57610228565b600090506102a8565b6003547f676c6f62616c2064617461203332206c656e67746820737472696e6700000000141561025757610260565b600090506102a8565b600460006000815260200190815260200160002054600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b141561029e576102a7565b600090506102a8565b5b90565b6000600090506102b961087d565b6001905090565b6000600090506102ce6101a4565b506102d761049b565b156102e157610307565b7ff000000000000000000000000000000000000000000000000000000000000000810190505b61030f610142565b156103195761033f565b7f0f00000000000000000000000000000000000000000000000000000000000000810190505b610347610695565b1561035157610376565b7ef0000000000000000000000000000000000000000000000000000000000000810190505b61037e61055a565b15610388576103ad565b7e0f000000000000000000000000000000000000000000000000000000000000810190505b60ff60008190555073a94f5374fce5edbc8e2a8697c15331677e6ebf0b60018190555060ff6002819055507f676c6f62616c2064617461203332206c656e67746820737472696e670000000060038190555073a94f5374fce5edbc8e2a8697c15331677e6ebf0b6004600060008152602001908152602001600020819055506104346101ba565b1561043e57610462565b7df00000000000000000000000000000000000000000000000000000000000810190505b61046a610732565b1561047457610498565b7d0f0000000000000000000000000000000000000000000000000000000000810190505b90565b6000600060006000915060009250816000146104b65761053a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7821315610530575b600a8212156104f55781806001019250506104df565b81600a146105025761052b565b600a90505b60008160ff16111561052a57818060019003925050808060019003915050610507565b5b610539565b60009250610555565b5b8160001461054757610550565b60019250610555565b600092505b505090565b60006001905041600160a060020a0316732adc25665018aa1fe0e6bc666dac8fc2697ff9ba141561058a57610593565b60009050610678565b446302b8feb014156105a4576105ad565b60009050610678565b45683635c9adc5dea0000014156105c3576105cc565b60009050610678565b43607814156105da576105e3565b60009050610678565b33600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b141561060d57610616565b60009050610678565b34606414156106245761062d565b60009050610678565b3a6001141561063b57610644565b60009050610678565b32600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b141561066e57610677565b60009050610678565b5b90565b6000600090505b60011561068e57610682565b6001905090565b60006000600191506060610889600039606060006000f0905080600160a060020a031662f55d9d600060008260e060020a02600052600441600160a060020a03168152602001600060008660155a03f150505080600160a060020a031663b9c3d0a5602060008260e060020a026000526004600060008660155a03f150505060005160e114156107245761072d565b6000915061072e565b5b5090565b60006001905060007f74657374737472696e67000000000000000000000000000000000000000000008152600a016000207f43c4b4524adb81e4e9a5c4648a98e9d320e3908ac5b6c889144b642cd08ae16d141561078f57610798565b6000905061087a565b60026020600060007f74657374737472696e67000000000000000000000000000000000000000000008152600a01600060008560155a03f150506000517f3c8727e019a42b444667a587b6001251becadabbb36bfed8087a92c18882d11114156108015761080a565b6000905061087a565b60036020600060007f74657374737472696e67000000000000000000000000000000000000000000008152600a01600060008560155a03f15050600051600160a060020a031673cd566972b5e50104011a92b59fa8e0b1234851ae141561087057610879565b6000905061087a565b5b90565b6108856102ab565b505600605480600c6000396000f30060003560e060020a90048062f55d9d14601e578063b9c3d0a514602d57005b60276004356046565b60006000f35b6033603d565b8060005260206000f35b600060e1905090565b80600160a060020a0316ff5056", + "code" : "0x60003560e060020a900480630c4c9a80146100635780632a9afb8314610075578063380e4396146100875780637ee17e1214610099578063a60eedda146100a7578063e0a9fd28146100b9578063e97384dc146100cb578063ed973fe9146100dd57005b61006b610473565b8060005260206000f35b61007d61064e565b8060005260206000f35b61008f61073f565b8060005260206000f35b6100a16107fe565b60006000f35b6100af6100ef565b8060005260206000f35b6100c1610196565b8060005260206000f35b6100d3610352565b8060005260206000f35b6100e56102eb565b8060005260206000f35b60006000600191506060610815600039606060006000f0905080600160a060020a031662f55d9d600060008260e060020a02600052600441600160a060020a03168152602001600060008660325a03f161014557005b505080600160a060020a031663b9c3d0a5602060008260e060020a026000526004600060008660325a03f161017657005b505060005160e1141561018857610191565b60009150610192565b5b5090565b60006001905060007f74657374737472696e67000000000000000000000000000000000000000000008152600a016000207f43c4b4524adb81e4e9a5c4648a98e9d320e3908ac5b6c889144b642cd08ae16d14156101f3576101fc565b600090506102e8565b60026020600060007f74657374737472696e67000000000000000000000000000000000000000000008152600a01600060008560325a03f161023a57005b506000517f3c8727e019a42b444667a587b6001251becadabbb36bfed8087a92c18882d111141561026a57610273565b600090506102e8565b60036020600060007f74657374737472696e67000000000000000000000000000000000000000000008152600a01600060008560325a03f16102b157005b50600051600160a060020a031673cd566972b5e50104011a92b59fa8e0b1234851ae14156102de576102e7565b600090506102e8565b5b90565b600060006060610815600039606060006000f0905080600160a060020a031663b9c3d0a5602060008260e060020a026000526004600060008660325a03f161032f57005b505060005160e11461034057610349565b6001915061034e565b600091505b5090565b60006001905041600160a060020a0316732adc25665018aa1fe0e6bc666dac8fc2697ff9ba14156103825761038b565b60009050610470565b446302b8feb0141561039c576103a5565b60009050610470565b45683635c9adc5dea0000014156103bb576103c4565b60009050610470565b43607814156103d2576103db565b60009050610470565b33600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b14156104055761040e565b60009050610470565b346064141561041c57610425565b60009050610470565b3a600114156104335761043c565b60009050610470565b32600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b14156104665761046f565b60009050610470565b5b90565b6000600090506104816107fe565b5061048a61073f565b15610494576104ba565b7ff000000000000000000000000000000000000000000000000000000000000000810190505b6104c26102eb565b156104cc576104f2565b7f0f00000000000000000000000000000000000000000000000000000000000000810190505b6104fa6100ef565b1561050457610529565b7ef0000000000000000000000000000000000000000000000000000000000000810190505b610531610352565b1561053b57610560565b7e0f000000000000000000000000000000000000000000000000000000000000810190505b60ff60008190555073a94f5374fce5edbc8e2a8697c15331677e6ebf0b60018190555060ff6002819055507f676c6f62616c2064617461203332206c656e67746820737472696e670000000060038190555073a94f5374fce5edbc8e2a8697c15331677e6ebf0b6004600060008152602001908152602001600020819055506105e761064e565b156105f157610615565b7df00000000000000000000000000000000000000000000000000000000000810190505b61061d610196565b156106275761064b565b7d0f0000000000000000000000000000000000000000000000000000000000810190505b90565b60006001905060005460ff14156106645761066d565b6000905061073c565b600254600054141561067e57610687565b6000905061073c565b600154600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b14156106b3576106bc565b6000905061073c565b6003547f676c6f62616c2064617461203332206c656e67746820737472696e670000000014156106eb576106f4565b6000905061073c565b600460006000815260200190815260200160002054600160a060020a031673a94f5374fce5edbc8e2a8697c15331677e6ebf0b14156107325761073b565b6000905061073c565b5b90565b60006000600060009150600092508160001461075a576107de565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe78213156107d4575b600a821215610799578180600101925050610783565b81600a146107a6576107cf565b600a90505b60008160ff1611156107ce578180600190039250508080600190039150506107ab565b5b6107dd565b600092506107f9565b5b816000146107eb576107f4565b600192506107f9565b600092505b505090565b60006060610815600039606060006000f09050905600605480600c6000396000f30060003560e060020a90048062f55d9d14601e578063b9c3d0a514602d57005b60276004356046565b60006000f35b6033603d565b8060005260206000f35b600060e1905090565b80600160a060020a0316ff5056", "nonce" : "0", "storage" : { } @@ -216,7 +216,7 @@ "data" : "0x7ee17e12", "//" : "runSolidityTests()", "data" : "0x0c4c9a80", - "gasLimit" : "465224", + "gasLimit" : "15000000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -249,11 +249,13 @@ "//": "contract main ", "//": "{ ", "//": " uint data; ", + "//": " address msgsender; ", "//": " function run() returns (uint) ", "//": " { ", "//": " data = 1; ", + "//": " msgsender = 0x095e7baea6a6c7c4c2dfeb977efac326af552d87; ", "//": " subcaller a = new subcaller(); ", - "//": " a.init(msg.sender); ", + "//": " a.init(msgsender); ", "//": " return data; ", "//": " } ", "//": " ", @@ -262,13 +264,13 @@ "//": " data = _data; ", "//": " } ", "//": "}", - "code" : "0x60e060020a60003504806330debb4214610020578063c04062261461003157005b61002b6004356100a4565b60006000f35b610039610043565b8060005260206000f35b60006000600160008190555060656100af600039606560006000f0905080600160a060020a03166319ab453c600060008260e060020a02600052600433600160a060020a03168152602001600060008660155a03f150505060005491505090565b80600081905550505600605980600c6000396000f30060e060020a60003504806319ab453c14601457005b601d6004356023565b60006000f35b80600160a060020a03166330debb42600060008260e060020a02600052600460e18152602001600060008660155a03f15050505056", + "code" : "0x60003560e060020a9004806330debb4214610021578063c04062261461003257005b61002c6004356100c7565b60006000f35b61003a610044565b8060005260206000f35b60006000600160008190555073095e7baea6a6c7c4c2dfeb977efac326af552d87600181905550606a6100d2600039606a60006000f0905080600160a060020a03166319ab453c600060008260e060020a026000526004600154600160a060020a03168152602001600060008660325a03f16100bc57005b505060005491505090565b80600081905550505600605e80600c6000396000f30060003560e060020a9004806319ab453c14601557005b601e6004356024565b60006000f35b80600160a060020a03166330debb42600060008260e060020a02600052600460e18152602001600060008660325a03f1605957005b50505056", "nonce" : "0", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "nonce" : "0", "code" : "", "storage": {} @@ -278,7 +280,7 @@ { "//" : "run()", "data" : "0xc0406226", - "gasLimit" : "35000", + "gasLimit" : "350000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -287,7 +289,7 @@ } }, - "CallRecursiveMethods" : { + "CallRecursiveMethods" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -317,13 +319,13 @@ "//" : " testRecursiveMethods(); ", "//" : " } ", "//" : "}", - "code" : "0x60e060020a600035048063296df0df1460285780634893d88a146034578063981a316514604057005b602e604c565b60006000f35b603a6061565b60006000f35b60466059565b60006000f35b5b600115605757604d565b565b605f6061565b565b60676059565b56", + "code" : "0x60003560e060020a90048063296df0df1460295780634893d88a146035578063981a316514604157005b602f604d565b60006000f35b603b6062565b60006000f35b6047605a565b60006000f35b5b600115605857604e565b565b60606062565b565b6068605a565b56", "nonce" : "0", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "500000", "nonce" : "0", "code" : "", "storage": {} @@ -333,7 +335,7 @@ { "//" : "testRecursiveMethods()", "data" : "0x981a3165", - "gasLimit" : "25000", + "gasLimit" : "30000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -372,13 +374,13 @@ "//" : " testRecursiveMethods(); ", "//" : " } ", "//" : "}", - "code" : "0x60e060020a600035048063296df0df1460285780634893d88a146034578063981a316514604057005b602e604c565b60006000f35b603a6061565b60006000f35b60466059565b60006000f35b5b600115605757604d565b565b605f6061565b565b60676059565b56", + "code" : "0x60003560e060020a90048063296df0df1460295780634893d88a146035578063981a316514604157005b602f604d565b60006000f35b603b6062565b60006000f35b6047605a565b60006000f35b5b600115605857604e565b565b60606062565b565b6068605a565b56", "nonce" : "0", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "50000", + "balance" : "500000", "nonce" : "0", "code" : "", "storage": {} @@ -388,7 +390,86 @@ { "//" : "testInfiniteLoop()", "data" : "0x296df0df", - "gasLimit" : "30000", + "gasLimit" : "300000", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "1" + } + }, + + "RecursiveCreateContractsCreate4Contracts" : { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "45678256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000000000000000000", + "//" : "contract recursiveCreate1 ", + "//" : "{ ", + "//" : " uint depp; ", + "//" : " function recursiveCreate1(address a, uint depth) ", + "//" : " { ", + "//" : " depth = depth - 1; ", + "//" : " depp = depth; ", + "//" : " if(depth > 0) ", + "//" : " main(a).create2(depth); ", + "//" : " } ", + "//" : "} ", + "//" : " ", + "//" : "contract recursiveCreate2 ", + "//" : "{ ", + "//" : " uint depp; ", + "//" : " function recursiveCreate2(address a, uint depth) ", + "//" : " { ", + "//" : " depth = depth - 1; ", + "//" : " depp = depth; ", + "//" : " if(depth > 0) ", + "//" : " recursiveCreate1 rec1 = new recursiveCreate1(a, depth); ", + "//" : " } ", + "//" : "} ", + "//" : " ", + "//" : "contract main ", + "//" : "{ ", + "//" : " address maincontract; ", + "//" : " uint depp; ", + "//" : " function run(uint depth) ", + "//" : " { ", + "//" : " maincontract = 0x095e7baea6a6c7c4c2dfeb977efac326af552d87; ", + "//" : " depp = depth; ", + "//" : " recursiveCreate1 rec1 = new recursiveCreate1(maincontract, depth); ", + "//" : " } ", + "//" : " ", + "//" : " function create2(uint depth) ", + "//" : " { ", + "//" : " recursiveCreate2 rec2 = new recursiveCreate2(maincontract, depth); ", + "//" : " address(rec2).send(2); ", + "//" : " } ", + "//" : "}", + "code" : "0x60003560e060020a90048063820b13f614610021578063a444f5e91461003257005b61002c600435610093565b60006000f35b61003d600435610043565b60006000f35b600073095e7baea6a6c7c4c2dfeb977efac326af552d8760008190555081600181905550606b6101ad600039606b600054600160a060020a0316815260200182815260200160006000f090505050565b600060c86100e560003960c8600054600160a060020a0316815260200182815260200160006000f0905080600160a060020a0316600060026000600060006000848787f16100dd57005b50505050505600604060c860043960045160245160006001820391508160008190555060008211602657604c565b606b605d600039606b83600160a060020a0316815260200182815260200160006000f090505b505050600180605c6000396000f300006040606b6004396004516024516001810390508060008190555060008111602457605b565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660325a03f1605857005b50505b5050600180606a6000396000f300006040606b6004396004516024516001810390508060008190555060008111602457605b565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660325a03f1605857005b50505b5050600180606a6000396000f30000", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "5000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : + { + "//" : "run(uint256)", + "data" : "0xa444f5e90000000000000000000000000000000000000000000000000000000000000004", + "gasLimit" : "300000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -409,12 +490,14 @@ "pre" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { - "balance" : "1000000", + "balance" : "1000000000000000000000000000000000", "//" : "contract recursiveCreate1 ", "//" : "{ ", + "//" : " uint depp; ", "//" : " function recursiveCreate1(address a, uint depth) ", "//" : " { ", "//" : " depth = depth - 1; ", + "//" : " depp = depth; ", "//" : " if(depth > 0) ", "//" : " main(a).create2(depth); ", "//" : " } ", @@ -422,9 +505,11 @@ "//" : " ", "//" : "contract recursiveCreate2 ", "//" : "{ ", + "//" : " uint depp; ", "//" : " function recursiveCreate2(address a, uint depth) ", "//" : " { ", "//" : " depth = depth - 1; ", + "//" : " depp = depth; ", "//" : " if(depth > 0) ", "//" : " recursiveCreate1 rec1 = new recursiveCreate1(a, depth); ", "//" : " } ", @@ -436,7 +521,7 @@ "//" : " uint depp; ", "//" : " function run(uint depth) ", "//" : " { ", - "//" : " maincontract = msg.sender; ", + "//" : " maincontract = 0x095e7baea6a6c7c4c2dfeb977efac326af552d87; ", "//" : " depp = depth; ", "//" : " recursiveCreate1 rec1 = new recursiveCreate1(maincontract, depth); ", "//" : " } ", @@ -447,13 +532,13 @@ "//" : " address(rec2).send(2); ", "//" : " } ", "//" : "}", - "code" : "0x60003560e060020a90048063820b13f614610021578063a444f5e91461003257005b61002c600435610043565b60006000f35b61003d60043561008f565b60006000f35b600060c66100cc60003960c6600054600160a060020a0316815260200182815260200160006000f0905080600160a060020a0316600060026000600060006000848787f1505050505050565b6000336000819055508160018190555060686101926000396068600054600160a060020a0316815260200182815260200160006000f09050505056006012604060c6600439600451602451601e565b60018060c56000396000f35b6000600182039150600082116031576057565b6068605d600039606883600160a060020a0316815260200182815260200160006000f090505b5050505600601260406068600439600451602451601e565b60018060676000396000f35b60018103905060008111602f576062565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660155a03f15050505b505056000000601260406068600439600451602451601e565b60018060676000396000f35b60018103905060008111602f576062565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660155a03f15050505b5050560000", + "code" : "0x60003560e060020a90048063820b13f614610021578063a444f5e91461003257005b61002c600435610093565b60006000f35b61003d600435610043565b60006000f35b600073095e7baea6a6c7c4c2dfeb977efac326af552d8760008190555081600181905550606b6101ad600039606b600054600160a060020a0316815260200182815260200160006000f090505050565b600060c86100e560003960c8600054600160a060020a0316815260200182815260200160006000f0905080600160a060020a0316600060026000600060006000848787f16100dd57005b50505050505600604060c860043960045160245160006001820391508160008190555060008211602657604c565b606b605d600039606b83600160a060020a0316815260200182815260200160006000f090505b505050600180605c6000396000f300006040606b6004396004516024516001810390508060008190555060008111602457605b565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660325a03f1605857005b50505b5050600180606a6000396000f300006040606b6004396004516024516001810390508060008190555060008111602457605b565b81600160a060020a031663820b13f6600060008260e060020a026000526004858152602001600060008660325a03f1605857005b50505b5050600180606a6000396000f30000", "nonce" : "0", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "500000", + "balance" : "5000000", "nonce" : "0", "code" : "", "storage": {} @@ -462,8 +547,8 @@ "transaction" : { "//" : "run(uint256)", - "data" : "0xa444f5e900000000000000000000000000000000000000000000000000000000000204", - "gasLimit" : "30000", + "data" : "0xa444f5e90000000000000000000000000000000000000000000000000000000000000204", + "gasLimit" : "300000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -508,7 +593,7 @@ } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "50000", + "balance" : "500000", "nonce" : "0", "code" : "", "storage": {} @@ -518,7 +603,7 @@ { "//" : "run()", "data" : "0xc0406226", - "gasLimit" : "30000", + "gasLimit" : "300000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stTransactionTestFiller.json b/test/stTransactionTestFiller.json index 9bd7436bc..26a69f187 100644 --- a/test/stTransactionTestFiller.json +++ b/test/stTransactionTestFiller.json @@ -514,7 +514,7 @@ "transaction" : { "data" : "", - "gasLimit" : "21100", + "gasLimit" : "23000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -535,7 +535,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "22000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -545,7 +545,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", "//" : "gas = 19 going OOG, gas = 20 fine", - "code" : "{ (CALL 19 0 1 0 0 0 0) }", + "code" : "{ (CALL 40000 0 1 0 0 0 0) }", "nonce" : "0", "storage" : { } @@ -574,7 +574,67 @@ "transaction" : { "data" : "", - "gasLimit" : "21100", + "gasLimit" : "160000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "InternlCallStoreClearsSucces" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "//" : "gas = 19 going OOG, gas = 20 fine", + "code" : "{ (CALL 100000 0 1 0 0 0 0) }", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0)(SSTORE 4 0)(SSTORE 5 0)(SSTORE 6 0)(SSTORE 7 0)(SSTORE 8 0)(SSTORE 9 0)}", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c", + "0x05" : "0x0c", + "0x06" : "0x0c", + "0x07" : "0x0c", + "0x08" : "0x0c", + "0x09" : "0x0c" + } + } + + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "160000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -605,7 +665,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", "//" : "gas = 19 going OOG, gas = 20 fine", - "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 19 0 1 0 0 0 0) }", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 20000 0 1 0 0 0 0) }", "nonce" : "0", "storage" : { "0x" : "0x0c", @@ -639,7 +699,7 @@ "transaction" : { "data" : "", - "gasLimit" : "23000", + "gasLimit" : "200000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -648,7 +708,72 @@ } }, - "SuicidesAndInternlCallSuicidesOOG" : { + "StoreClearsAndInternlCallStoreClearsSuccess" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "500000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "//" : "gas = 19 going OOG, gas = 20 fine", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 50000 0 1 0 0 0 0) }", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c" + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0)(SSTORE 4 0)(SSTORE 5 0)(SSTORE 6 0)(SSTORE 7 0)(SSTORE 8 0)(SSTORE 9 0)}", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c", + "0x05" : "0x0c", + "0x06" : "0x0c", + "0x07" : "0x0c", + "0x08" : "0x0c", + "0x09" : "0x0c" + } + } + + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "200000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "SuicidesAndInternlCallSuicidesBonusGasAtCall" : { "env" : { "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", "currentDifficulty" : "45678256", @@ -669,7 +794,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", - "code" : "{(CALL 20000 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", + "code" : "{(CALL 0 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", "nonce" : "0", "storage" : { } @@ -682,7 +807,54 @@ "storage" : { } } + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + "SuicidesAndInternlCallSuicidesBonusGasAtCallFailed" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "code" : "{(CALL 0 0x0000000000000000000000000000000000000000 0 0 0 0 0) (SUICIDE 0)}", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SUICIDE 0x0000000000000000000000000000000000000001)}", + "nonce" : "0", + "storage" : { + } + } }, "transaction" : @@ -697,7 +869,55 @@ } }, - "SuicidesAndInternlCallSuicides" : { + "SuicidesAndInternlCallSuicidesOOG" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "code" : "{(CALL 22000 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SUICIDE 0x0000000000000000000000000000000000000001)}", + "nonce" : "0", + "storage" : { + } + } + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "SuicidesAndInternlCallSuicidesSuccess" : { "env" : { "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", "currentDifficulty" : "45678256", @@ -709,7 +929,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "1000000", + "balance" : "180000", "code" : "", "nonce" : "0", "storage" : { @@ -737,7 +957,7 @@ "transaction" : { "data" : "", - "gasLimit" : "50000", + "gasLimit" : "150000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -767,7 +987,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10000", - "code" : "{(SUICIDE 0) (CALL 2000 0x0000000000000000000000000000000000000000 0 0 0 0 0) }", + "code" : "{(SUICIDE 0) (CALL 30000 0x0000000000000000000000000000000000000000 0 0 0 0 0) }", "nonce" : "0", "storage" : { } @@ -785,7 +1005,7 @@ "transaction" : { "data" : "", - "gasLimit" : "33700", + "gasLimit" : "83700", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -957,6 +1177,7 @@ }, "transaction" : { + "data" : "cost (0 - 4 !0 - 68) 10*4 + 9*68 = 652", "data" : "0x00000000000000000000112233445566778f32", "gasLimit" : "22000", "gasPrice" : "1", diff --git a/test/ttTransactionTestFiller.json b/test/ttTransactionTestFiller.json index 96e4c78a6..105b64abc 100644 --- a/test/ttTransactionTestFiller.json +++ b/test/ttTransactionTestFiller.json @@ -14,7 +14,7 @@ } }, - "WrongVRSTestVl26" : { + "WrongVRSTestVEqual26" : { "transaction" : { "data" : "", @@ -29,7 +29,7 @@ } }, - "WrongVRSTestVl29" : { + "WrongVRSTestVEqual29" : { "transaction" : { "data" : "", @@ -44,7 +44,7 @@ } }, - "WrongVRSTestVge31" : { + "WrongVRSTestVEqual31" : { "transaction" : { "data" : "", diff --git a/test/vm.cpp b/test/vm.cpp index eb7c174ec..a39f0005f 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -533,33 +533,43 @@ BOOST_AUTO_TEST_CASE(vmPerformanceTest) } } -//BOOST_AUTO_TEST_CASE(vmInputLimitsTest1) -//{ -// for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) -// { -// string arg = boost::unit_test::framework::master_test_suite().argv[i]; -// if (arg == "--inputlimits" || arg == "--all") -// { -// auto start = chrono::steady_clock::now(); - -// dev::test::executeTests("vmInputLimitsTest1", "/VMTests", dev::test::doVMTests); - -// auto end = chrono::steady_clock::now(); -// auto duration(chrono::duration_cast(end - start)); -// cnote << "test duration: " << duration.count() << " milliseconds.\n"; -// } -// } -//} - -//BOOST_AUTO_TEST_CASE(vmInputLimitsTest2) -//{ -// for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) -// { -// string arg = boost::unit_test::framework::master_test_suite().argv[i]; -// if (arg == "--inputlimits" || arg == "--all") -// dev::test::executeTests("vmInputLimitsTest2", "/VMTests", dev::test::doVMTests); -// } -//} +BOOST_AUTO_TEST_CASE(vmInputLimitsTest1) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--inputlimits" || arg == "--all") + { + auto start = chrono::steady_clock::now(); + + dev::test::executeTests("vmInputLimits1", "/VMTests", dev::test::doVMTests); + + auto end = chrono::steady_clock::now(); + auto duration(chrono::duration_cast(end - start)); + cnote << "test duration: " << duration.count() << " milliseconds.\n"; + } + } +} + +BOOST_AUTO_TEST_CASE(vmInputLimitsTest2) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--inputlimits" || arg == "--all") + dev::test::executeTests("vmInputLimits2", "/VMTests", dev::test::doVMTests); + } +} + +BOOST_AUTO_TEST_CASE(vmInputLimitsLightTest) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--inputlimits" || arg == "--all") + dev::test::executeTests("vmInputLimitsLight", "/VMTests", dev::test::doVMTests); + } +} BOOST_AUTO_TEST_CASE(vmRandom) { diff --git a/test/vmArithmeticTestFiller.json b/test/vmArithmeticTestFiller.json index 3e453f8e9..81dbf052b 100644 --- a/test/vmArithmeticTestFiller.json +++ b/test/vmArithmeticTestFiller.json @@ -976,8 +976,36 @@ "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "1000000000000000000", "data" : "", - "gasPrice" : "100000000000000", - "gas" : "10000" + "gasPrice" : "1", + "gas" : "1000000" + } + }, + + "sdiv_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "{ asm PUSH1 0x05 PUSH1 0x09 PUSH1 0x00 SUB SDIV DUP PUSH1 0 SSTORE }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "10000000" } }, diff --git a/test/vmIOandFlowOperationsTestFiller.json b/test/vmIOandFlowOperationsTestFiller.json index 995b9d6da..84a5188a6 100644 --- a/test/vmIOandFlowOperationsTestFiller.json +++ b/test/vmIOandFlowOperationsTestFiller.json @@ -283,7 +283,7 @@ "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", - "currentGasLimit" : "1000000", + "currentGasLimit" : "8390000000", "currentDifficulty" : "256", "currentTimestamp" : "1", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" @@ -306,6 +306,229 @@ "gas" : "100000" } }, + "mstore8MemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60f1630fffffff53", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000" + } + }, + + "mloadMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x630fffffff51", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000" + } + }, + + "mstoreMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60f1630fffffff52", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "log1MemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff60ff630fffffffa1", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "extcodecopyMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff60ff630fffffff630fffffff3c", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "codecopyMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff60ff630fffffff630fffffff39", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "calldatacopyMemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff60ff630fffffff630fffffff37", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, + + "sha3MemExp": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "8390000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "0x60ff630fffffff20", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1", + "gas" : "8390000000" + } + }, "mstore8WordToBigError": { "env" : {