diff --git a/CMakeLists.txt b/CMakeLists.txt index aafdad099..a5c4fe930 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,8 @@ option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF) option(PROFILING "Build in support for profiling" OFF) set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).") -option(MINER "Build the miner component" ON) +option(MINER "Build the CLI miner component" ON) +option(ETHKEY "Build the CLI key manager component" ON) option(SOLIDITY "Build the Solidity language components" ON) option(SERPENT "Build the Serpent language components" ON) option(TOOLS "Build the tools components" ON) @@ -251,6 +252,17 @@ elseif (BUNDLE STREQUAL "user") set(NCURSES ${DECENT_PLATFORM}) set(TOOLS ON) set(TESTS OFF) +elseif (BUNDLE STREQUAL "wallet") + set(SERPENT OFF) + set(SOLIDITY OFF) + set(USENPM OFF) + set(GUI OFF) + set(NCURSES OFF) + set(TOOLS OFF) + set(TESTS OFF) + set(ETHKEY ON) + set(MINER OFF) + set(ETHASHCL ON) elseif (BUNDLE STREQUAL "miner") set(SERPENT OFF) set(SOLIDITY OFF) @@ -259,6 +271,7 @@ elseif (BUNDLE STREQUAL "miner") set(NCURSES OFF) set(TOOLS OFF) set(TESTS OFF) + set(ETHKEY OFF) set(MINER ON) set(ETHASHCL ON) endif () @@ -296,6 +309,7 @@ message("-- JSONRPC JSON-RPC support ${JSONRPC} message("-- USENPM Javascript source building ${USENPM}") message("------------------------------------------------------------- components") message("-- MINER Build miner ${MINER}") +message("-- ETHKEY Build wallet tools ${ETHKEY}") message("-- TOOLS Build basic tools ${TOOLS}") message("-- SOLIDITY Build Solidity language components ${SOLIDITY}") message("-- SERPENT Build Serpent language components ${SERPENT}") @@ -364,6 +378,7 @@ if (JSCONSOLE) endif () add_subdirectory(secp256k1) +add_subdirectory(libscrypt) add_subdirectory(libdevcrypto) if (GENERAL) @@ -386,10 +401,14 @@ if (GENERAL) add_subdirectory(libwebthree) endif () -if (MINER) +if (MINER OR TOOLS) add_subdirectory(ethminer) endif () +if (ETHKEY OR TOOLS) + add_subdirectory(ethkey) +endif () + if (TESTS) add_subdirectory(libtestutils) add_subdirectory(test) diff --git a/abi/main.cpp b/abi/main.cpp index 27c5eea1b..df7fa8811 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -26,7 +26,7 @@ #include "../test/JsonSpiritHeaders.h" #include #include -#include +#include #include using namespace std; using namespace dev; diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 41d9ea10f..595668cd1 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -23,6 +23,7 @@ qt5_wrap_ui(ui_Connect.h Connect.ui) qt5_wrap_ui(ui_Debugger.h Debugger.ui) qt5_wrap_ui(ui_Transact.h Transact.ui) qt5_wrap_ui(ui_ExportState.h ExportState.ui) +qt5_wrap_ui(ui_GetPassword.h GetPassword.ui) file(GLOB HEADERS "*.h") @@ -35,7 +36,7 @@ endif () # eth_add_executable is defined in cmake/EthExecutableHelper.cmake eth_add_executable(${EXECUTABLE} ICON alethzero - UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui ExportState.ui + UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui ExportState.ui GetPassword.ui WIN_RESOURCES alethzero.rc ) diff --git a/alethzero/DappLoader.cpp b/alethzero/DappLoader.cpp index a91beb2f7..b2249ae5b 100644 --- a/alethzero/DappLoader.cpp +++ b/alethzero/DappLoader.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/alethzero/DownloadView.cpp b/alethzero/DownloadView.cpp index 009e2dc83..210649edb 100644 --- a/alethzero/DownloadView.cpp +++ b/alethzero/DownloadView.cpp @@ -52,7 +52,10 @@ void DownloadView::paintEvent(QPaintEvent*) QPointF pos(0, 0); auto bg = m_man->blocksGot(); - + unsigned subCount = m_man->subCount(); + if (subCount == 0) + return; + unsigned dh = 360 / subCount; for (unsigned i = bg.all().first, ei = bg.all().second; i < ei; ++i) { int s = -2; @@ -68,7 +71,6 @@ void DownloadView::paintEvent(QPaintEvent*) h++; }); } - unsigned dh = 360 / m_man->subCount(); if (s == -2) p.fillRect(QRectF(QPointF(pos) + QPointF(3 * area.width() / 8, 3 * area.height() / 8), area / 4), Qt::black); else if (s == -1) diff --git a/alethzero/GetPassword.ui b/alethzero/GetPassword.ui new file mode 100644 index 000000000..753bca565 --- /dev/null +++ b/alethzero/GetPassword.ui @@ -0,0 +1,123 @@ + + + GetPassword + + + + 0 + 0 + 400 + 187 + + + + Enter Password + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Qt::RichText + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QLineEdit::Password + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + GetPassword + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + GetPassword + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/alethzero/Main.ui b/alethzero/Main.ui index b5e22409d..ecdc07ab6 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -157,7 +157,10 @@ + + + @@ -1656,7 +1659,7 @@ font-size: 14pt &Enable Local Addresses - + true @@ -1745,6 +1748,21 @@ font-size: 14pt Co&nfirm Transactions + + + Import &Secret Key File... + + + + + &Re-Encrypt Key + + + + + Re-Encrypt All Keys... + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 9486f9f01..6a11736a4 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -44,7 +44,7 @@ #include #include #endif -#include +#include #include #include #include @@ -75,6 +75,7 @@ #include "WebPage.h" #include "ExportState.h" #include "ui_Main.h" +#include "ui_GetPassword.h" using namespace std; using namespace dev; using namespace dev::p2p; @@ -467,7 +468,9 @@ void Main::load(QString _s) void Main::on_newTransaction_triggered() { m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); - m_transact.exec(); + m_transact.setWindowFlags(Qt::Dialog); + m_transact.setWindowModality(Qt::WindowModal); + m_transact.show(); } void Main::on_loadJS_triggered() @@ -698,12 +701,17 @@ Secret Main::retrieveSecret(Address const& _a) const auto info = m_keyManager.accountDetails()[_a]; while (true) { - if (Secret s = m_keyManager.secret(_a, [&](){ - return QInputDialog::getText(const_cast(this), "Import Account Key", QString("Enter the password for the account %2 (%1). The hint is:\n%3").arg(QString::fromStdString(_a.abridged())).arg(QString::fromStdString(info.first)).arg(QString::fromStdString(info.second)), QLineEdit::Password).toStdString(); - })) + Secret s = m_keyManager.secret(_a, [&](){ + QDialog d; + Ui_GetPassword gp; + gp.setupUi(&d); + d.setWindowTitle("Unlock Account"); + gp.label->setText(QString("Enter the password for the account %2 (%1).").arg(QString::fromStdString(_a.abridged())).arg(QString::fromStdString(info.first))); + gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(info.second)); + return d.exec() == QDialog::Accepted ? gp.entry->text().toStdString() : string(); + }); + if (s || QMessageBox::warning(nullptr, "Unlock Account", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) return s; - else if (QMessageBox::warning(const_cast(this), "Incorrect Password", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) - return Secret(); } } @@ -771,17 +779,32 @@ void Main::readSettings(bool _skipGeometry) on_urlEdit_returnPressed(); } -std::string Main::getPassword(std::string const& _title, std::string const& _for) +std::string Main::getPassword(std::string const& _title, std::string const& _for, std::string* _hint, bool* _ok) { QString password; while (true) { - password = QInputDialog::getText(nullptr, QString::fromStdString(_title), QString::fromStdString(_for), QLineEdit::Password, QString()); + bool ok; + password = QInputDialog::getText(nullptr, QString::fromStdString(_title), QString::fromStdString(_for), QLineEdit::Password, QString(), &ok); + if (!ok) + { + if (_ok) + *_ok = false; + return string(); + } + if (password.isEmpty()) + break; QString confirm = QInputDialog::getText(nullptr, QString::fromStdString(_title), "Confirm this password by typing it again", QLineEdit::Password, QString()); if (password == confirm) break; QMessageBox::warning(nullptr, QString::fromStdString(_title), "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok); } + + if (!password.isEmpty() && _hint && !m_keyManager.haveHint(password.toStdString())) + *_hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString(); + + if (_ok) + *_ok = true; return password.toStdString(); } @@ -797,8 +820,11 @@ void Main::on_importKey_triggered() QString s = QInputDialog::getText(this, "Import Account Key", "Enter this account's name"); if (QMessageBox::question(this, "Additional Security?", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - std::string password = getPassword("Import Account Key", "Enter the password you would like to use for this key. Don't forget it!"); - std::string hint = QInputDialog::getText(this, "Import Account Key", "Enter a hint to help you remember this password.").toStdString(); + bool ok; + std::string hint; + std::string password = getPassword("Import Account Key", "Enter the password you would like to use for this key. Don't forget it!", &hint, &ok); + if (!ok) + return; m_keyManager.import(k.secret(), s.toStdString(), password, hint); } else @@ -813,6 +839,33 @@ void Main::on_importKey_triggered() } void Main::on_importKeyFile_triggered() +{ + QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); + h128 uuid = m_keyManager.store().importKey(s.toStdString()); + if (!uuid) + { + QMessageBox::warning(this, "Key File Invalid", "Could not find secret key definition. This is probably not an Web3 key file."); + return; + } + + QString info = QInputDialog::getText(this, "Import Key File", "Enter a description of this key to help you recognise it in the future."); + + QString pass; + for (Secret s; !s;) + { + s = Secret(m_keyManager.store().secret(uuid, [&](){ + pass = QInputDialog::getText(this, "Import Key File", "Enter the password for the key to complete the import.", QLineEdit::Password); + return pass.toStdString(); + }, false)); + if (!s && QMessageBox::question(this, "Import Key File", "The password you provided is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) + return; + } + + QString hint = QInputDialog::getText(this, "Import Key File", "Enter a hint for this password to help you remember it."); + m_keyManager.importExisting(uuid, info.toStdString(), pass.toStdString(), hint.toStdString()); +} + +void Main::on_claimPresale_triggered() { QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); try @@ -1166,10 +1219,10 @@ void Main::on_turboMining_triggered() void Main::refreshBlockChain() { - if (!ui->blocks->isVisible()) + if (!ui->blocks->isVisible() && isVisible()) return; - DEV_TIMED_FUNCTION; + DEV_TIMED_FUNCTION_ABOVE(500); cwatch << "refreshBlockChain()"; // TODO: keep the same thing highlighted. @@ -1353,7 +1406,7 @@ void Main::timerEvent(QTimerEvent*) auto ls = ethereum()->checkWatchSafe(i.first); if (ls.size()) { - cnote << "FIRING WATCH" << i.first << ls.size(); +// cnote << "FIRING WATCH" << i.first << ls.size(); i.second(ls); } } @@ -1976,8 +2029,11 @@ void Main::on_newAccount_triggered() QString s = QInputDialog::getText(this, "Create Account", "Enter this account's name"); if (QMessageBox::question(this, "Create Account", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!"); - std::string hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString(); + bool ok = false; + std::string hint; + std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!", &hint, &ok); + if (!ok) + return; m_keyManager.import(p.secret(), s.toStdString(), password, hint); } else @@ -1989,15 +2045,14 @@ void Main::on_killAccount_triggered() { if (ui->ourAccounts->currentRow() >= 0) { - auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); + auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); Address h((byte const*)hba.data(), Address::ConstructFromPointer); auto k = m_keyManager.accountDetails()[h]; - if ( - ethereum()->balanceAt(h) != 0 && - QMessageBox::critical(this, QString::fromStdString("Kill Account " + k.first + "?!"), - QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it. It, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" - "Are you sure you want to continue?"), - QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) + QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + k.first + "?!"), + QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it.\r\nIt, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" + "Are you sure you want to continue? \r\n If so, type 'YES' to confirm."), + QLineEdit::Normal, "NO"); + if (s != "YES") return; m_keyManager.kill(h); if (m_keyManager.accounts().empty()) @@ -2009,6 +2064,57 @@ void Main::on_killAccount_triggered() } } +void Main::on_reencryptKey_triggered() +{ + if (ui->ourAccounts->currentRow() >= 0) + { + auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); + Address a((byte const*)hba.data(), Address::ConstructFromPointer); + QStringList kdfs = {"PBKDF2-SHA256", "Scrypt"}; + bool ok = true; + KDF kdf = (KDF)kdfs.indexOf(QInputDialog::getItem(this, "Re-Encrypt Key", "Select a key derivation function to use for storing your key:", kdfs, kdfs.size() - 1, false, &ok)); + if (!ok) + return; + std::string hint; + std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!\nEnter nothing to use your Master password.", &hint, &ok); + if (!ok) + return; + try { + auto pw = [&](){ + auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.hint(a)), QLineEdit::Password, QString()).toStdString(); + if (p.empty()) + throw UnknownPassword(); + return p; + }; + while (!(password.empty() ? m_keyManager.recode(a, SemanticPassword::Master, pw, kdf) : m_keyManager.recode(a, password, hint, pw, kdf))) + if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) + return; + } + catch (UnknownPassword&) {} + } +} + +void Main::on_reencryptAll_triggered() +{ + QStringList kdfs = {"PBKDF2-SHA256", "Scrypt"}; + bool ok = false; + QString kdf = QInputDialog::getItem(this, "Re-Encrypt Key", "Select a key derivation function to use for storing your keys:", kdfs, kdfs.size() - 1, false, &ok); + if (!ok) + return; + try { + for (Address const& a: m_keyManager.accounts()) + while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){ + auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.hint(a))), QLineEdit::Password, QString()).toStdString(); + if (p.empty()) + throw UnknownPassword(); + return p; + }, (KDF)kdfs.indexOf(kdf))) + if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) + return; + } + catch (UnknownPassword&) {} +} + void Main::on_go_triggered() { if (!ui->net->isChecked()) diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 9f2082dd0..29cd0dbf3 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -33,9 +33,9 @@ #include #include #include +#include #include #include -#include #include #include #include "Context.h" @@ -137,7 +137,10 @@ private slots: void on_newAccount_triggered(); void on_killAccount_triggered(); void on_importKey_triggered(); + void on_reencryptKey_triggered(); + void on_reencryptAll_triggered(); void on_importKeyFile_triggered(); + void on_claimPresale_triggered(); void on_exportKey_triggered(); // Account pane @@ -246,7 +249,7 @@ private: void refreshBalances(); void setBeneficiary(dev::Address const& _b); - std::string getPassword(std::string const& _title, std::string const& _for); + std::string getPassword(std::string const& _title, std::string const& _for, std::string* _hint = nullptr, bool* _ok = nullptr); std::unique_ptr ui; diff --git a/alethzero/NatspecHandler.cpp b/alethzero/NatspecHandler.cpp index a7cad6853..d00abc44f 100644 --- a/alethzero/NatspecHandler.cpp +++ b/alethzero/NatspecHandler.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include using namespace dev; diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 1336c3f05..b485091d9 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -39,7 +39,8 @@ #include #include #include -#include +#include + #if ETH_SERPENT #include #include @@ -76,6 +77,7 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e m_ethereum = _eth; m_natSpecDB = _natSpecDB; + auto old = ui->from->currentIndex(); ui->from->clear(); for (auto const& i: m_accounts) { @@ -84,6 +86,10 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(i))).arg(QString::fromStdString(d.first)); ui->from->addItem(s); } + if (old > -1 && old < ui->from->count()) + ui->from->setCurrentIndex(old); + else if (ui->from->count()) + ui->from->setCurrentIndex(0); } bool Transact::isCreation() const @@ -301,6 +307,9 @@ void Transact::rejigData() // Determine how much balance we have to play with... //findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice()); auto s = fromAccount(); + if (!s) + return; + auto b = ethereum()->balanceAt(s, PendingBlock); m_allGood = true; @@ -344,7 +353,7 @@ void Transact::rejigData() if (b < value() + baseGas * gasPrice()) { // Not enough - bail. - bail("
ERROR No single account contains enough for paying even the basic amount of gas required.
"); + bail("
ERROR Account doesn't contain enough for paying even the basic amount of gas required.
"); return; } else @@ -417,6 +426,8 @@ Secret Transact::findSecret(u256 _totalReq) const Address Transact::fromAccount() { + if (ui->from->currentIndex() < 0 || ui->from->currentIndex() >= (int)m_accounts.size()) + return Address(); auto it = m_accounts.begin(); std::advance(it, ui->from->currentIndex()); return *it; @@ -425,14 +436,19 @@ Address Transact::fromAccount() void Transact::on_send_clicked() { // Secret s = findSecret(value() + fee()); - Secret s = m_context->retrieveSecret(fromAccount()); - auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock); - if (!s || b < value() + fee()) + auto a = fromAccount(); + auto b = ethereum()->balanceAt(a, PendingBlock); + + if (!a || b < value() + fee()) { - QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); + QMessageBox::critical(nullptr, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount.", QMessageBox::Ok); return; } + Secret s = m_context->retrieveSecret(a); + if (!s) + return; + if (isCreation()) { // If execution is a contract creation, add Natspec to @@ -467,7 +483,7 @@ void Transact::on_debug_clicked() auto b = ethereum()->balanceAt(from, PendingBlock); if (!from || b < value() + fee()) { - QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); + QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount."); return; } diff --git a/alethzero/Transact.h b/alethzero/Transact.h index cd62c0e20..c14fcc7e1 100644 --- a/alethzero/Transact.h +++ b/alethzero/Transact.h @@ -44,12 +44,13 @@ public: void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); private slots: + void on_from_currentIndexChanged(int) { rejigData(); rejigData(); } void on_destination_currentTextChanged(QString); - void on_value_valueChanged(int) { updateFee(); } - void on_gas_valueChanged(int) { updateFee(); } - void on_valueUnits_currentIndexChanged(int) { updateFee(); } - void on_gasPriceUnits_currentIndexChanged(int) { updateFee(); } - void on_gasPrice_valueChanged(int) { updateFee(); } + void on_value_valueChanged(int) { updateFee(); rejigData(); } + void on_gas_valueChanged(int) { updateFee(); rejigData(); } + void on_valueUnits_currentIndexChanged(int) { updateFee(); rejigData(); } + void on_gasPriceUnits_currentIndexChanged(int) { updateFee(); rejigData(); } + void on_gasPrice_valueChanged(int) { updateFee(); rejigData(); } void on_data_textChanged() { rejigData(); } void on_optimize_clicked() { rejigData(); } void on_send_clicked(); diff --git a/eth/main.cpp b/eth/main.cpp index 1cf918346..a57928e72 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include @@ -37,7 +37,8 @@ #include #include #include -#include +#include + #include #if ETH_JSCONSOLE || !ETH_TRUE #include diff --git a/ethkey/CMakeLists.txt b/ethkey/CMakeLists.txt new file mode 100644 index 000000000..5575acbd0 --- /dev/null +++ b/ethkey/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_policy(SET CMP0015 NEW) +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ..) +include_directories(${Boost_INCLUDE_DIRS}) +include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) + +if (JSCONSOLE) + include_directories(${V8_INCLUDE_DIRS}) +endif() + +set(EXECUTABLE ethkey) + +file(GLOB HEADERS "*.h") + +add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + +add_dependencies(${EXECUTABLE} BuildInfo.h) + +target_link_libraries(${EXECUTABLE} devcrypto) +target_link_libraries(${EXECUTABLE} ethcore) + +if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) + eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) +endif() + +if (APPLE) + install(TARGETS ${EXECUTABLE} DESTINATION bin) +else() + eth_install_executable(${EXECUTABLE}) +endif() diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h new file mode 100644 index 000000000..df2ba5a5a --- /dev/null +++ b/ethkey/KeyAux.h @@ -0,0 +1,413 @@ +#pragma once + +/* + 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 KeyAux.cpp + * @author Gav Wood + * @date 2014 + * CLI module for key management. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BuildInfo.h" +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace boost::algorithm; + +#undef RETURN + +class BadArgument: public Exception {}; + +string getAccountPassword(KeyManager& keyManager, Address const& a) +{ + return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); +} + +string createPassword(std::string const& _prompt) +{ + string ret; + while (true) + { + ret = getPassword(_prompt); + string confirm = getPassword("Please confirm the password by entering it again: "); + if (ret == confirm) + break; + cout << "Passwords were different. Try again." << endl; + } + return ret; +// cout << "Enter a hint to help you remember this password: " << flush; +// cin >> hint; +// return make_pair(ret, hint); +} + +pair createPassword(KeyManager& _keyManager, std::string const& _prompt, std::string const& _pass = std::string(), std::string const& _hint = std::string()) +{ + string pass = _pass; + if (pass.empty()) + while (true) + { + pass = getPassword(_prompt); + string confirm = getPassword("Please confirm the password by entering it again: "); + if (pass == confirm) + break; + cout << "Passwords were different. Try again." << endl; + } + string hint = _hint; + if (hint.empty() && !pass.empty() && !_keyManager.haveHint(pass)) + { + cout << "Enter a hint to help you remember this password: " << flush; + getline(cin, hint); + } + return make_pair(pass, hint); +} + +class KeyCLI +{ +public: + enum class OperationMode + { + None, + ListBare, + NewBare, + ImportBare, + ExportBare, + RecodeBare, + KillBare, + CreateWallet, + List, + New, + Import, + Export, + Recode, + Kill + }; + + KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {} + + bool interpretOption(int& i, int argc, char** argv) + { + string arg = argv[i]; + if (arg == "--wallet-path" && i + 1 < argc) + m_walletPath = argv[++i]; + else if (arg == "--secrets-path" && i + 1 < argc) + m_secretsPath = argv[++i]; + else if ((arg == "-m" || arg == "--master") && i + 1 < argc) + m_masterPassword = argv[++i]; + else if (arg == "--unlock" && i + 1 < argc) + m_unlocks.push_back(argv[++i]); + else if (arg == "--lock" && i + 1 < argc) + m_lock = argv[++i]; + else if (arg == "--kdf" && i + 1 < argc) + m_kdf = argv[++i]; + else if (arg == "--kdf-param" && i + 2 < argc) + { + auto n = argv[++i]; + auto v = argv[++i]; + m_kdfParams[n] = v; + } + else if (arg == "--new-bare") + m_mode = OperationMode::NewBare; + else if (arg == "--import-bare") + m_mode = OperationMode::ImportBare; + else if (arg == "--list-bare") + m_mode = OperationMode::ListBare; + else if (arg == "--export-bare") + m_mode = OperationMode::ExportBare; + else if (arg == "--recode-bare") + m_mode = OperationMode::RecodeBare; + else if (arg == "--kill-bare") + m_mode = OperationMode::KillBare; + else if (arg == "--create-wallet") + m_mode = OperationMode::CreateWallet; + else if (arg == "--list") + m_mode = OperationMode::List; + else if ((arg == "-n" || arg == "--new") && i + 1 < argc) + { + m_mode = OperationMode::New; + m_name = argv[++i]; + } + else if ((arg == "-i" || arg == "--import") && i + 2 < argc) + { + m_mode = OperationMode::Import; + m_inputs = strings(1, argv[++i]); + m_name = argv[++i]; + } + else if (arg == "--export") + m_mode = OperationMode::Export; + else if (arg == "--recode") + m_mode = OperationMode::Recode; + else if (arg == "--no-icap") + m_icap = false; + else if (m_mode == OperationMode::ImportBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare) + m_inputs.push_back(arg); + else + return false; + return true; + } + + KeyPair makeKey() const + { + KeyPair k(Secret::random()); + while (m_icap && k.address()[0]) + k = KeyPair(sha3(k.secret())); + return k; + } + + void execute() + { + if (m_mode == OperationMode::CreateWallet) + { + KeyManager wallet(m_walletPath, m_secretsPath); + if (m_masterPassword.empty()) + m_masterPassword = createPassword("Please enter a MASTER password to protect your key store (make it strong!): "); + if (m_masterPassword.empty()) + cerr << "Aborted (empty password not allowed)." << endl; + else + wallet.create(m_masterPassword); + } + else if (m_mode < OperationMode::CreateWallet) + { + SecretStore store(m_secretsPath); + switch (m_mode) + { + case OperationMode::ListBare: + for (h128 const& u: std::set() + store.keys()) + cout << toUUID(u) << endl; + break; + case OperationMode::NewBare: + { + if (m_lock.empty()) + m_lock = createPassword("Enter a password with which to secure this account: "); + auto k = makeKey(); + h128 u = store.importSecret(k.secret().asBytes(), m_lock); + cout << "Created key " << toUUID(u) << endl; + cout << " Address: " << k.address().hex() << endl; + cout << " ICAP: " << ICAP(k.address()).encoded() << endl; + break; + } + case OperationMode::ImportBare: + for (string const& i: m_inputs) + { + h128 u; + bytes b; + b = fromHex(i); + if (b.size() != 32) + { + std::string s = contentsString(i); + b = fromHex(s); + if (b.size() != 32) + u = store.importKey(i); + } + if (!u && b.size() == 32) + u = store.importSecret(b, lockPassword(toAddress(Secret(b)).abridged())); + else + { + cerr << "Cannot import " << i << " not a file or secret." << endl; + continue; + } + cout << "Successfully imported " << i << " as " << toUUID(u); + } + break; + case OperationMode::ExportBare: break; + case OperationMode::RecodeBare: + for (auto const& i: m_inputs) + { + h128 u = fromUUID(i); + if (u) + if (store.recode(u, lockPassword(toUUID(u)), [&](){ return getPassword("Enter password for key " + toUUID(u) + ": "); }, kdf())) + cerr << "Re-encoded " << toUUID(u) << endl; + else + cerr << "Couldn't re-encode " << toUUID(u) << "; key corrupt or incorrect password supplied." << endl; + else + cerr << "Couldn't re-encode " << toUUID(u) << "; not found." << endl; + } + case OperationMode::KillBare: + for (auto const& i: m_inputs) + { + h128 u = fromUUID(i); + if (u) + store.kill(u); + else + cerr << "Couldn't kill " << toUUID(u) << "; not found." << endl; + } + break; + default: break; + } + } + else + { + KeyManager wallet(m_walletPath, m_secretsPath); + if (wallet.exists()) + while (true) + { + if (wallet.load(m_masterPassword)) + break; + if (!m_masterPassword.empty()) + { + cout << "Password invalid. Try again." << endl; + m_masterPassword.clear(); + } + m_masterPassword = getPassword("Please enter your MASTER password: "); + } + else + { + cerr << "Couldn't open wallet. Does it exist?" << endl; + exit(-1); + } + switch (m_mode) + { + case OperationMode::New: + { + tie(m_lock, m_lockHint) = createPassword(wallet, "Enter a password with which to secure this account (or nothing to use the master password): ", m_lock, m_lockHint); + auto k = makeKey(); + bool usesMaster = m_lock.empty(); + h128 u = usesMaster ? wallet.import(k.secret(), m_name) : wallet.import(k.secret(), m_name, m_lock, m_lockHint); + cout << "Created key " << toUUID(u) << endl; + cout << " Name: " << m_name << endl; + if (usesMaster) + cout << " Uses master password." << endl; + else + cout << " Password hint: " << m_lockHint << endl; + cout << " Address: " << k.address().hex() << endl; + cout << " ICAP: " << ICAP(k.address()).encoded() << endl; + break; + } + case OperationMode::List: + { + vector bare; + vector nonIcap; + for (auto const& u: wallet.store().keys()) + if (Address a = wallet.address(u)) + if (a[0]) + nonIcap.push_back(u); + else + { + std::pair info = wallet.accountDetails()[a]; + cout << toUUID(u) << " " << a.abridged(); + cout << " " << ICAP(a).encoded(); + cout << " " << info.first << endl; + } + else + bare.push_back(u); + for (auto const& u: nonIcap) + if (Address a = wallet.address(u)) + { + std::pair info = wallet.accountDetails()[a]; + cout << toUUID(u) << " " << a.abridged(); + cout << " (Not ICAP) "; + cout << " " << info.first << endl; + } + for (auto const& u: bare) + cout << toUUID(u) << " (Bare)" << endl; + } + default: break; + } + } + } + + std::string lockPassword(std::string const& _accountName) + { + return m_lock.empty() ? createPassword("Enter a password with which to secure account " + _accountName + ": ") : m_lock; + } + + static void streamHelp(ostream& _out) + { + _out + << "Secret-store (\"bare\") operation modes:" << endl + << " --list-bare List all secret available in secret-store." << endl + << " --new-bare Generate and output a key without interacting with wallet and dump the JSON." << endl + << " --import-bare [ | , ... ] Import keys from given sources." << endl + << " --recode-bare [ | , ... ] Decrypt and re-encrypt given keys." << endl +// << " --export-bare [ , ... ] Export given keys." << endl + << " --kill-bare [ , ... ] Delete given keys." << endl + << "Secret-store configuration:" << endl + << " --secrets-path Specify Web3 secret-store path (default: " << SecretStore::defaultPath() << ")" << endl + << endl + << "Wallet operating modes:" << endl + << " -l,--list List all keys available in wallet." << endl + << " -n,--new Create a new key with given name and add it in the wallet." << endl + << " -i,--import [||] Import keys from given source and place in wallet." << endl + << " -e,--export [
| , ... ] Export given keys." << endl + << " -r,--recode [
|| , ... ] Decrypt and re-encrypt given keys." << endl + << "Wallet configuration:" << endl + << " --create-wallet Create an Ethereum master wallet." << endl + << " --wallet-path Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl + << " -m, --master Specify wallet (master) password." << endl + << endl + << "Encryption configuration:" << endl + << " --kdf Specify KDF to use when encrypting (default: sc rypt)" << endl + << " --kdf-param Specify a parameter for the KDF." << endl +// << " --cipher Specify cipher to use when encrypting (default: aes-128-ctr)" << endl +// << " --cipher-param Specify a parameter for the cipher." << endl + << " --lock Specify password for when encrypting a (the) key." << endl + << " --hint Specify hint for the --lock password." << endl + << endl + << "Decryption configuration:" << endl + << " --unlock Specify password for a (the) key." << endl + << "Key generation configuration:" << endl + << " --no-icap Don't bother to make a direct-ICAP capable key." << endl + ; + } + + static bool isTrue(std::string const& _m) + { + return _m == "on" || _m == "yes" || _m == "true" || _m == "1"; + } + + static bool isFalse(std::string const& _m) + { + return _m == "off" || _m == "no" || _m == "false" || _m == "0"; + } + +private: + KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; } + + /// Operating mode. + OperationMode m_mode; + + /// Wallet stuff + string m_secretsPath = SecretStore::defaultPath(); + string m_walletPath = KeyManager::defaultPath(); + + /// Wallet password stuff + string m_masterPassword; + strings m_unlocks; + string m_lock; + string m_lockHint; + bool m_icap = true; + + /// Creating + string m_name; + + /// Importing + strings m_inputs; + + string m_kdf = "scrypt"; + map m_kdfParams; +// string m_cipher; +// map m_cipherParams; +}; diff --git a/ethkey/main.cpp b/ethkey/main.cpp new file mode 100644 index 000000000..53781a38a --- /dev/null +++ b/ethkey/main.cpp @@ -0,0 +1,84 @@ +/* + 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 main.cpp + * @author Gav Wood + * @date 2014 + * Ethereum client. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "BuildInfo.h" +#include "KeyAux.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +void help() +{ + cout + << "Usage ethkey [OPTIONS]" << endl + << "Options:" << endl << endl; + KeyCLI::streamHelp(cout); + cout + << "General Options:" << endl + << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl + << " -V,--version Show the version and exit." << endl + << " -h,--help Show this help message and exit." << endl + ; + exit(0); +} + +void version() +{ + cout << "ethkey version " << dev::Version << endl; + cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; + exit(0); +} + +int main(int argc, char** argv) +{ + KeyCLI m(KeyCLI::OperationMode::ListBare); + g_logVerbosity = 0; + + for (int i = 1; i < argc; ++i) + { + string arg = argv[i]; + if (m.interpretOption(i, argc, argv)) {} + else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) + g_logVerbosity = atoi(argv[++i]); + else if (arg == "-h" || arg == "--help") + help(); + else if (arg == "-V" || arg == "--version") + version(); + else + { + cerr << "Invalid argument: " << arg << endl; + exit(-1); + } + } + + m.execute(); + + return 0; +} + diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index 040bd945c..47fd2e2ae 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -16,10 +16,10 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file main.cpp +/** @file MinerAux.cpp * @author Gav Wood * @date 2014 - * Ethereum client. + * CLI module for mining. */ #include @@ -31,11 +31,11 @@ #include #include -#include +#include #include #include #include -#include +#include #include #include #include @@ -174,6 +174,10 @@ public: m_minerType = MinerType::GPU; miningThreads = 1; } + else if (arg == "--no-precompute") + { + precompute = false; + } else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc) { string m = boost::to_lower_copy(string(argv[++i])); @@ -268,6 +272,7 @@ public: << "Work farming mode:" << endl << " -F,--farm Put into mining farm mode with the work server at URL (default: http://127.0.0.1:8545)" << endl << " --farm-recheck Leave n ms between checks for changed work (default: 500)." << endl + << " --no-precompute Don't precompute the next epoch's DAG." << endl #endif << "Ethash verify mode:" << endl << " -w,--check-pow Check PoW credentials for validity." << endl @@ -388,7 +393,7 @@ private: (void)_m; (void)_remote; (void)_recheckPeriod; - #if ETH_JSONRPC || !ETH_TRUE +#if ETH_JSONRPC || !ETH_TRUE jsonrpc::HttpClient client(_remote); Farm rpc(client); @@ -399,7 +404,7 @@ private: f.startGPU(); ProofOfWork::WorkPackage current; - EthashAux::FullType dag; + EthashAux::FullType dag; while (true) try { @@ -418,9 +423,13 @@ private: cnote << "Getting work package..."; Json::Value v = rpc.eth_getWork(); h256 hh(v[0].asString()); - h256 newSeedHash(v[1].asString()); - if (!(dag = EthashAux::full(newSeedHash, true))) - BOOST_THROW_EXCEPTION(DAGCreationFailure()); + h256 newSeedHash(v[1].asString()); + if (current.seedHash != newSeedHash) + cnote << "Grabbing DAG for" << newSeedHash; + if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; }))) + BOOST_THROW_EXCEPTION(DAGCreationFailure()); + if (precompute) + EthashAux::computeFull(sha3(newSeedHash), true); if (hh != current.headerHash) { current.headerHash = hh; @@ -459,7 +468,7 @@ private: cerr << "JSON-RPC problem. Probably couldn't connect. Retrying in " << i << "... \r"; cerr << endl; } - #endif +#endif exit(0); } @@ -484,5 +493,5 @@ private: /// Farm params string farmURL = "http://127.0.0.1:8545"; unsigned farmRecheckPeriod = 500; - + bool precompute = true; }; diff --git a/ethminer/main.cpp b/ethminer/main.cpp index 5b82db471..4deba38d1 100644 --- a/ethminer/main.cpp +++ b/ethminer/main.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include "MinerAux.h" using namespace std; using namespace dev; diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index c57188bbb..4cb46fe3c 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC OFF) if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") else() - set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas") + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}") endif() if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index b2382b8ba..3901b4fdf 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -1,6 +1,6 @@ #pragma GCC diagnostic ignored "-Wconversion" -#include +#include #include #include @@ -63,19 +63,24 @@ extern "C" EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) { - auto value = jit2eth(*_value); - auto receiveAddress = right160(*_receiveAddress); - auto codeAddress = right160(*_codeAddress); - const auto isCall = receiveAddress == codeAddress; // OPT: The same address pointer can be used if not CODECALL + CallParameters params; + params.value = jit2eth(*_value); + params.senderAddress = _env->myAddress; + params.receiveAddress = right160(*_receiveAddress); + params.codeAddress = right160(*_codeAddress); + params.data = {_inBeg, _inSize}; + params.out = {_outBeg, _outSize}; + params.onOp = {}; + const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL *io_gas -= _callGas; if (*io_gas < 0) return false; - if (isCall && !_env->exists(receiveAddress)) + if (isCall && !_env->exists(params.receiveAddress)) *io_gas -= static_cast(c_callNewAccountGas); // no underflow, *io_gas non-negative before - if (value > 0) // value transfer + if (params.value > 0) // value transfer { /*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible"); *io_gas -= static_cast(c_callValueTransferGas); // no underflow @@ -86,11 +91,11 @@ extern "C" return false; auto ret = false; - auto callGas = u256{_callGas}; - if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) - ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress); + params.gas = u256{_callGas}; + if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024) + ret = _env->call(params); - *io_gas += static_cast(callGas); // it is never more than initial _callGas + *io_gas += static_cast(params.gas); // it is never more than initial _callGas return ret; } diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index 3bdbc37b8..f834fad1a 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -4,7 +4,7 @@ #include "JitVM.h" #include -#include +#include #include #include diff --git a/evmjit/libevmjit-cpp/JitVM.h b/evmjit/libevmjit-cpp/JitVM.h index fdc87248a..979c8000c 100644 --- a/evmjit/libevmjit-cpp/JitVM.h +++ b/evmjit/libevmjit-cpp/JitVM.h @@ -12,10 +12,14 @@ class JitVM: public VMFace { virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual u256 gas() const noexcept { return m_gas; } + virtual void reset(u256 const& _gas = 0) noexcept { m_gas = _gas; } + private: friend class VMFactory; - explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} + explicit JitVM(u256 _gas = 0): m_gas(_gas) {} + u256 m_gas; evmjit::RuntimeData m_data; evmjit::ExecutionContext m_context; std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT diff --git a/exp/main.cpp b/exp/main.cpp index 138ae76a4..f0574fa7c 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include #include @@ -41,14 +43,14 @@ #include #include #include -#include #include #include #include #include -#include +#include #include -#include +#include + #include #include #include @@ -66,6 +68,44 @@ namespace fs = boost::filesystem; #if 1 +int main() +{ + cdebug << pbkdf2("password", asBytes("salt"), 1, 32); + cdebug << pbkdf2("password", asBytes("salt"), 1, 16); + cdebug << pbkdf2("password", asBytes("salt"), 2, 16); + cdebug << pbkdf2("testpassword", fromHex("de5742f1f1045c402296422cee5a8a9ecf0ac5bf594deca1170d22aef33a79cf"), 262144, 16); + return 0; +} + + +#elif 0 + +int main() +{ + cdebug << "EXP"; + vector data; + for (unsigned i = 0; i < 10000; ++i) + data.push_back(rlp(i)); + + h256 ret; + DEV_TIMED(triedb) + { + MemoryDB mdb; + GenericTrieDB t(&mdb); + t.init(); + unsigned i = 0; + for (auto const& d: data) + t.insert(rlp(i++), d); + ret = t.root(); + } + cdebug << ret; + DEV_TIMED(hash256) + ret = orderedTrieRoot(data); + cdebug << ret; +} + +#elif 0 + int main() { KeyManager keyman; diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 95817e41c..ae98861c1 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #pragma warning(push) @@ -63,6 +64,8 @@ namespace dev extern char const* Version; +static const std::string EmptyString; + // Binary data types. using bytes = std::vector; using bytesRef = vector_ref; @@ -86,8 +89,9 @@ extern const u256 UndefinedU256; // Map types. using StringMap = std::map; +using BytesMap = std::map; using u256Map = std::map; -using HexMap = std::map; +using HexMap = std::map; // Hash types. using StringHashMap = std::unordered_map; @@ -200,7 +204,7 @@ private: #endif #define DEV_TIMED_ABOVE(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(#S, MS), true); __eth_t.second; __eth_t.second = false) -#define DEV_TIMED_SCOPE_ABOVE(S) ::dev::TimerHelper __eth_t(S, MS) +#define DEV_TIMED_SCOPE_ABOVE(S, MS) ::dev::TimerHelper __eth_t(S, MS) #if WIN32 #define DEV_TIMED_FUNCTION_ABOVE(MS) DEV_TIMED_SCOPE_ABOVE(__FUNCSIG__, MS) #else diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index 6cad29952..f8d8c172f 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -115,7 +115,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) return ret; } -bytes dev::asNibbles(std::string const& _s) +bytes dev::asNibbles(bytesConstRef const& _s) { std::vector ret; ret.reserve(_s.size() * 2); diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 6c1f34667..e1d8d7bdb 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -95,7 +95,7 @@ inline bytes asBytes(std::string const& _b) /// Converts a string into the big-endian base-16 stream of integers (NOT ASCII). /// @example asNibbles("A")[0] == 4 && asNibbles("A")[1] == 1 -bytes asNibbles(std::string const& _s); +bytes asNibbles(bytesConstRef const& _s); // Big-endian to/from host endian conversion functions. @@ -323,4 +323,13 @@ std::vector keysOf(std::map const& _m) return ret; } +template +std::vector keysOf(std::unordered_map const& _m) +{ + std::vector ret; + for (auto const& i: _m) + ret.push_back(i.first); + return ret; +} + } diff --git a/libdevcrypto/FileSystem.cpp b/libdevcore/FileSystem.cpp similarity index 97% rename from libdevcrypto/FileSystem.cpp rename to libdevcore/FileSystem.cpp index adf3a2946..dfda891f5 100644 --- a/libdevcrypto/FileSystem.cpp +++ b/libdevcore/FileSystem.cpp @@ -22,8 +22,8 @@ */ #include "FileSystem.h" -#include -#include +#include "Common.h" +#include "Log.h" #if defined(_WIN32) #include diff --git a/libdevcrypto/FileSystem.h b/libdevcore/FileSystem.h similarity index 100% rename from libdevcrypto/FileSystem.h rename to libdevcore/FileSystem.h diff --git a/libdevcore/Hash.cpp b/libdevcore/Hash.cpp new file mode 100644 index 000000000..c6b917b90 --- /dev/null +++ b/libdevcore/Hash.cpp @@ -0,0 +1,440 @@ +/* + 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 Hash.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Hash.h" +#include +#include +#include +#include "picosha2.h" +using namespace std; +using namespace dev; + +namespace dev +{ + +h256 sha256(bytesConstRef _input) +{ + h256 ret; + picosha2::hash256(_input.begin(), _input.end(), ret.data(), ret.data() + 32); + return ret; +} + +namespace rmd160 +{ + +/********************************************************************\ + * + * FILE: rmd160.h + * FILE: rmd160.c + * + * CONTENTS: Header file for a sample C-implementation of the + * RIPEMD-160 hash-function. + * TARGET: any computer with an ANSI C compiler + * + * AUTHOR: Antoon Bosselaers, ESAT-COSIC + * DATE: 1 March 1996 + * VERSION: 1.0 + * + * Copyright (c) Katholieke Universiteit Leuven + * 1996, All Rights Reserved + * + \********************************************************************/ + +// Adapted into "header-only" format by Gav Wood. + +/* macro definitions */ + +#define RMDsize 160 + +/* collect four bytes into one word: */ +#define BYTES_TO_DWORD(strptr) \ +(((uint32_t) *((strptr)+3) << 24) | \ +((uint32_t) *((strptr)+2) << 16) | \ +((uint32_t) *((strptr)+1) << 8) | \ +((uint32_t) *(strptr))) + +/* ROL(x, n) cyclically rotates x over n bits to the left */ +/* x must be of an unsigned 32 bits type and 0 <= n < 32. */ +#define ROL(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* the five basic functions F(), G() and H() */ +#define F(x, y, z) ((x) ^ (y) ^ (z)) +#define G(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define H(x, y, z) (((x) | ~(y)) ^ (z)) +#define I(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define J(x, y, z) ((x) ^ ((y) | ~(z))) + +/* the ten basic operations FF() through III() */ +#define FF(a, b, c, d, e, x, s) {\ +(a) += F((b), (c), (d)) + (x);\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define GG(a, b, c, d, e, x, s) {\ +(a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define HH(a, b, c, d, e, x, s) {\ +(a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define II(a, b, c, d, e, x, s) {\ +(a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define JJ(a, b, c, d, e, x, s) {\ +(a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define FFF(a, b, c, d, e, x, s) {\ +(a) += F((b), (c), (d)) + (x);\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define GGG(a, b, c, d, e, x, s) {\ +(a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define HHH(a, b, c, d, e, x, s) {\ +(a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define III(a, b, c, d, e, x, s) {\ +(a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define JJJ(a, b, c, d, e, x, s) {\ +(a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} + +void MDinit(uint32_t *MDbuf) +{ + MDbuf[0] = 0x67452301UL; + MDbuf[1] = 0xefcdab89UL; + MDbuf[2] = 0x98badcfeUL; + MDbuf[3] = 0x10325476UL; + MDbuf[4] = 0xc3d2e1f0UL; + + return; +} + +/********************************************************************/ + +void MDcompress(uint32_t *MDbuf, uint32_t *X) +{ + uint32_t aa = MDbuf[0], bb = MDbuf[1], cc = MDbuf[2], + dd = MDbuf[3], ee = MDbuf[4]; + uint32_t aaa = MDbuf[0], bbb = MDbuf[1], ccc = MDbuf[2], + ddd = MDbuf[3], eee = MDbuf[4]; + + /* round 1 */ + FF(aa, bb, cc, dd, ee, X[ 0], 11); + FF(ee, aa, bb, cc, dd, X[ 1], 14); + FF(dd, ee, aa, bb, cc, X[ 2], 15); + FF(cc, dd, ee, aa, bb, X[ 3], 12); + FF(bb, cc, dd, ee, aa, X[ 4], 5); + FF(aa, bb, cc, dd, ee, X[ 5], 8); + FF(ee, aa, bb, cc, dd, X[ 6], 7); + FF(dd, ee, aa, bb, cc, X[ 7], 9); + FF(cc, dd, ee, aa, bb, X[ 8], 11); + FF(bb, cc, dd, ee, aa, X[ 9], 13); + FF(aa, bb, cc, dd, ee, X[10], 14); + FF(ee, aa, bb, cc, dd, X[11], 15); + FF(dd, ee, aa, bb, cc, X[12], 6); + FF(cc, dd, ee, aa, bb, X[13], 7); + FF(bb, cc, dd, ee, aa, X[14], 9); + FF(aa, bb, cc, dd, ee, X[15], 8); + + /* round 2 */ + GG(ee, aa, bb, cc, dd, X[ 7], 7); + GG(dd, ee, aa, bb, cc, X[ 4], 6); + GG(cc, dd, ee, aa, bb, X[13], 8); + GG(bb, cc, dd, ee, aa, X[ 1], 13); + GG(aa, bb, cc, dd, ee, X[10], 11); + GG(ee, aa, bb, cc, dd, X[ 6], 9); + GG(dd, ee, aa, bb, cc, X[15], 7); + GG(cc, dd, ee, aa, bb, X[ 3], 15); + GG(bb, cc, dd, ee, aa, X[12], 7); + GG(aa, bb, cc, dd, ee, X[ 0], 12); + GG(ee, aa, bb, cc, dd, X[ 9], 15); + GG(dd, ee, aa, bb, cc, X[ 5], 9); + GG(cc, dd, ee, aa, bb, X[ 2], 11); + GG(bb, cc, dd, ee, aa, X[14], 7); + GG(aa, bb, cc, dd, ee, X[11], 13); + GG(ee, aa, bb, cc, dd, X[ 8], 12); + + /* round 3 */ + HH(dd, ee, aa, bb, cc, X[ 3], 11); + HH(cc, dd, ee, aa, bb, X[10], 13); + HH(bb, cc, dd, ee, aa, X[14], 6); + HH(aa, bb, cc, dd, ee, X[ 4], 7); + HH(ee, aa, bb, cc, dd, X[ 9], 14); + HH(dd, ee, aa, bb, cc, X[15], 9); + HH(cc, dd, ee, aa, bb, X[ 8], 13); + HH(bb, cc, dd, ee, aa, X[ 1], 15); + HH(aa, bb, cc, dd, ee, X[ 2], 14); + HH(ee, aa, bb, cc, dd, X[ 7], 8); + HH(dd, ee, aa, bb, cc, X[ 0], 13); + HH(cc, dd, ee, aa, bb, X[ 6], 6); + HH(bb, cc, dd, ee, aa, X[13], 5); + HH(aa, bb, cc, dd, ee, X[11], 12); + HH(ee, aa, bb, cc, dd, X[ 5], 7); + HH(dd, ee, aa, bb, cc, X[12], 5); + + /* round 4 */ + II(cc, dd, ee, aa, bb, X[ 1], 11); + II(bb, cc, dd, ee, aa, X[ 9], 12); + II(aa, bb, cc, dd, ee, X[11], 14); + II(ee, aa, bb, cc, dd, X[10], 15); + II(dd, ee, aa, bb, cc, X[ 0], 14); + II(cc, dd, ee, aa, bb, X[ 8], 15); + II(bb, cc, dd, ee, aa, X[12], 9); + II(aa, bb, cc, dd, ee, X[ 4], 8); + II(ee, aa, bb, cc, dd, X[13], 9); + II(dd, ee, aa, bb, cc, X[ 3], 14); + II(cc, dd, ee, aa, bb, X[ 7], 5); + II(bb, cc, dd, ee, aa, X[15], 6); + II(aa, bb, cc, dd, ee, X[14], 8); + II(ee, aa, bb, cc, dd, X[ 5], 6); + II(dd, ee, aa, bb, cc, X[ 6], 5); + II(cc, dd, ee, aa, bb, X[ 2], 12); + + /* round 5 */ + JJ(bb, cc, dd, ee, aa, X[ 4], 9); + JJ(aa, bb, cc, dd, ee, X[ 0], 15); + JJ(ee, aa, bb, cc, dd, X[ 5], 5); + JJ(dd, ee, aa, bb, cc, X[ 9], 11); + JJ(cc, dd, ee, aa, bb, X[ 7], 6); + JJ(bb, cc, dd, ee, aa, X[12], 8); + JJ(aa, bb, cc, dd, ee, X[ 2], 13); + JJ(ee, aa, bb, cc, dd, X[10], 12); + JJ(dd, ee, aa, bb, cc, X[14], 5); + JJ(cc, dd, ee, aa, bb, X[ 1], 12); + JJ(bb, cc, dd, ee, aa, X[ 3], 13); + JJ(aa, bb, cc, dd, ee, X[ 8], 14); + JJ(ee, aa, bb, cc, dd, X[11], 11); + JJ(dd, ee, aa, bb, cc, X[ 6], 8); + JJ(cc, dd, ee, aa, bb, X[15], 5); + JJ(bb, cc, dd, ee, aa, X[13], 6); + + /* parallel round 1 */ + JJJ(aaa, bbb, ccc, ddd, eee, X[ 5], 8); + JJJ(eee, aaa, bbb, ccc, ddd, X[14], 9); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 7], 9); + JJJ(ccc, ddd, eee, aaa, bbb, X[ 0], 11); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 9], 13); + JJJ(aaa, bbb, ccc, ddd, eee, X[ 2], 15); + JJJ(eee, aaa, bbb, ccc, ddd, X[11], 15); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 4], 5); + JJJ(ccc, ddd, eee, aaa, bbb, X[13], 7); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 6], 7); + JJJ(aaa, bbb, ccc, ddd, eee, X[15], 8); + JJJ(eee, aaa, bbb, ccc, ddd, X[ 8], 11); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 1], 14); + JJJ(ccc, ddd, eee, aaa, bbb, X[10], 14); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 3], 12); + JJJ(aaa, bbb, ccc, ddd, eee, X[12], 6); + + /* parallel round 2 */ + III(eee, aaa, bbb, ccc, ddd, X[ 6], 9); + III(ddd, eee, aaa, bbb, ccc, X[11], 13); + III(ccc, ddd, eee, aaa, bbb, X[ 3], 15); + III(bbb, ccc, ddd, eee, aaa, X[ 7], 7); + III(aaa, bbb, ccc, ddd, eee, X[ 0], 12); + III(eee, aaa, bbb, ccc, ddd, X[13], 8); + III(ddd, eee, aaa, bbb, ccc, X[ 5], 9); + III(ccc, ddd, eee, aaa, bbb, X[10], 11); + III(bbb, ccc, ddd, eee, aaa, X[14], 7); + III(aaa, bbb, ccc, ddd, eee, X[15], 7); + III(eee, aaa, bbb, ccc, ddd, X[ 8], 12); + III(ddd, eee, aaa, bbb, ccc, X[12], 7); + III(ccc, ddd, eee, aaa, bbb, X[ 4], 6); + III(bbb, ccc, ddd, eee, aaa, X[ 9], 15); + III(aaa, bbb, ccc, ddd, eee, X[ 1], 13); + III(eee, aaa, bbb, ccc, ddd, X[ 2], 11); + + /* parallel round 3 */ + HHH(ddd, eee, aaa, bbb, ccc, X[15], 9); + HHH(ccc, ddd, eee, aaa, bbb, X[ 5], 7); + HHH(bbb, ccc, ddd, eee, aaa, X[ 1], 15); + HHH(aaa, bbb, ccc, ddd, eee, X[ 3], 11); + HHH(eee, aaa, bbb, ccc, ddd, X[ 7], 8); + HHH(ddd, eee, aaa, bbb, ccc, X[14], 6); + HHH(ccc, ddd, eee, aaa, bbb, X[ 6], 6); + HHH(bbb, ccc, ddd, eee, aaa, X[ 9], 14); + HHH(aaa, bbb, ccc, ddd, eee, X[11], 12); + HHH(eee, aaa, bbb, ccc, ddd, X[ 8], 13); + HHH(ddd, eee, aaa, bbb, ccc, X[12], 5); + HHH(ccc, ddd, eee, aaa, bbb, X[ 2], 14); + HHH(bbb, ccc, ddd, eee, aaa, X[10], 13); + HHH(aaa, bbb, ccc, ddd, eee, X[ 0], 13); + HHH(eee, aaa, bbb, ccc, ddd, X[ 4], 7); + HHH(ddd, eee, aaa, bbb, ccc, X[13], 5); + + /* parallel round 4 */ + GGG(ccc, ddd, eee, aaa, bbb, X[ 8], 15); + GGG(bbb, ccc, ddd, eee, aaa, X[ 6], 5); + GGG(aaa, bbb, ccc, ddd, eee, X[ 4], 8); + GGG(eee, aaa, bbb, ccc, ddd, X[ 1], 11); + GGG(ddd, eee, aaa, bbb, ccc, X[ 3], 14); + GGG(ccc, ddd, eee, aaa, bbb, X[11], 14); + GGG(bbb, ccc, ddd, eee, aaa, X[15], 6); + GGG(aaa, bbb, ccc, ddd, eee, X[ 0], 14); + GGG(eee, aaa, bbb, ccc, ddd, X[ 5], 6); + GGG(ddd, eee, aaa, bbb, ccc, X[12], 9); + GGG(ccc, ddd, eee, aaa, bbb, X[ 2], 12); + GGG(bbb, ccc, ddd, eee, aaa, X[13], 9); + GGG(aaa, bbb, ccc, ddd, eee, X[ 9], 12); + GGG(eee, aaa, bbb, ccc, ddd, X[ 7], 5); + GGG(ddd, eee, aaa, bbb, ccc, X[10], 15); + GGG(ccc, ddd, eee, aaa, bbb, X[14], 8); + + /* parallel round 5 */ + FFF(bbb, ccc, ddd, eee, aaa, X[12] , 8); + FFF(aaa, bbb, ccc, ddd, eee, X[15] , 5); + FFF(eee, aaa, bbb, ccc, ddd, X[10] , 12); + FFF(ddd, eee, aaa, bbb, ccc, X[ 4] , 9); + FFF(ccc, ddd, eee, aaa, bbb, X[ 1] , 12); + FFF(bbb, ccc, ddd, eee, aaa, X[ 5] , 5); + FFF(aaa, bbb, ccc, ddd, eee, X[ 8] , 14); + FFF(eee, aaa, bbb, ccc, ddd, X[ 7] , 6); + FFF(ddd, eee, aaa, bbb, ccc, X[ 6] , 8); + FFF(ccc, ddd, eee, aaa, bbb, X[ 2] , 13); + FFF(bbb, ccc, ddd, eee, aaa, X[13] , 6); + FFF(aaa, bbb, ccc, ddd, eee, X[14] , 5); + FFF(eee, aaa, bbb, ccc, ddd, X[ 0] , 15); + FFF(ddd, eee, aaa, bbb, ccc, X[ 3] , 13); + FFF(ccc, ddd, eee, aaa, bbb, X[ 9] , 11); + FFF(bbb, ccc, ddd, eee, aaa, X[11] , 11); + + /* combine results */ + ddd += cc + MDbuf[1]; /* final result for MDbuf[0] */ + MDbuf[1] = MDbuf[2] + dd + eee; + MDbuf[2] = MDbuf[3] + ee + aaa; + MDbuf[3] = MDbuf[4] + aa + bbb; + MDbuf[4] = MDbuf[0] + bb + ccc; + MDbuf[0] = ddd; + + return; +} + +void MDfinish(uint32_t *MDbuf, byte const *strptr, uint32_t lswlen, uint32_t mswlen) +{ + unsigned int i; /* counter */ + uint32_t X[16]; /* message words */ + + memset(X, 0, 16*sizeof(uint32_t)); + + /* put bytes from strptr into X */ + for (i=0; i<(lswlen&63); i++) { + /* byte i goes into word X[i div 4] at pos. 8*(i mod 4) */ + X[i>>2] ^= (uint32_t) *strptr++ << (8 * (i&3)); + } + + /* append the bit m_n == 1 */ + X[(lswlen>>2)&15] ^= (uint32_t)1 << (8*(lswlen&3) + 7); + + if ((lswlen & 63) > 55) { + /* length goes to next block */ + MDcompress(MDbuf, X); + memset(X, 0, 16*sizeof(uint32_t)); + } + + /* append length in bits*/ + X[14] = lswlen << 3; + X[15] = (lswlen >> 29) | (mswlen << 3); + MDcompress(MDbuf, X); + + return; +} + +#undef ROL +#undef F +#undef G +#undef H +#undef I +#undef J +#undef FF +#undef GG +#undef HH +#undef II +#undef JJ +#undef FFF +#undef GGG +#undef HHH +#undef III +#undef JJJ + +} + +/* + * @returns RMD(_input) + */ +h160 ripemd160(bytesConstRef _input) +{ + h160 hashcode; + uint32_t buffer[RMDsize / 32]; // contains (A, B, C, D(, E)) + uint32_t current[16]; // current 16-word chunk + + // initialize + rmd160::MDinit(buffer); + byte const* message = _input.data(); + uint32_t remaining = _input.size(); // # of bytes not yet processed + + // process message in 16x 4-byte chunks + for (; remaining >= 64; remaining -= 64) + { + for (unsigned i = 0; i < 16; i++) + { + current[i] = BYTES_TO_DWORD(message); + message += 4; + } + rmd160::MDcompress(buffer, current); + } + // length mod 64 bytes left + + // finish: + rmd160::MDfinish(buffer, message, _input.size(), 0); + + for (unsigned i = 0; i < RMDsize / 8; i += 4) + { + hashcode[i] = buffer[i >> 2]; // implicit cast to byte + hashcode[i + 1] = (buffer[i >> 2] >> 8); //extracts the 8 least + hashcode[i + 2] = (buffer[i >> 2] >> 16); // significant bits. + hashcode[i + 3] = (buffer[i >> 2] >> 24); + } + + return hashcode; +} + +#undef BYTES_TO_DWORD +#undef RMDsize + +} diff --git a/libdevcore/Hash.h b/libdevcore/Hash.h new file mode 100644 index 000000000..7c5fcd67a --- /dev/null +++ b/libdevcore/Hash.h @@ -0,0 +1,38 @@ +/* + 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 Hash.h + * @author Gav Wood + * @date 2014 + * + * The FixedHash fixed-size "hash" container type. + */ + +#pragma once + +#include +#include +#include +#include "SHA3.h" + +namespace dev +{ + +h256 sha256(bytesConstRef _input); + +h160 ripemd160(bytesConstRef _input); + +} diff --git a/libdevcrypto/MemoryDB.cpp b/libdevcore/MemoryDB.cpp similarity index 100% rename from libdevcrypto/MemoryDB.cpp rename to libdevcore/MemoryDB.cpp diff --git a/libdevcrypto/MemoryDB.h b/libdevcore/MemoryDB.h similarity index 100% rename from libdevcrypto/MemoryDB.h rename to libdevcore/MemoryDB.h diff --git a/libdevcore/SHA3.cpp b/libdevcore/SHA3.cpp new file mode 100644 index 000000000..880f23d6e --- /dev/null +++ b/libdevcore/SHA3.cpp @@ -0,0 +1,223 @@ +/* + 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 SHA3.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "SHA3.h" +#include +#include +#include +#include +#include +#include "picosha2.h" +using namespace std; +using namespace dev; + +namespace dev +{ + +h256 EmptySHA3 = sha3(bytesConstRef()); +h256 EmptyListSHA3 = sha3(rlpList()); + +namespace keccak +{ + +/** libkeccak-tiny + * + * A single-file implementation of SHA-3 and SHAKE. + * + * Implementor: David Leon Gil + * License: CC0, attribution kindly requested. Blame taken too, + * but not liability. + */ + +#define decshake(bits) \ + int shake##bits(uint8_t*, size_t, const uint8_t*, size_t); + +#define decsha3(bits) \ + int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t); + +decshake(128) +decshake(256) +decsha3(224) +decsha3(256) +decsha3(384) +decsha3(512) + +/******** The Keccak-f[1600] permutation ********/ + +/*** Constants. ***/ +static const uint8_t rho[24] = \ + { 1, 3, 6, 10, 15, 21, + 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, + 62, 18, 39, 61, 20, 44}; +static const uint8_t pi[24] = \ + {10, 7, 11, 17, 18, 3, + 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, + 20, 14, 22, 9, 6, 1}; +static const uint64_t RC[24] = \ + {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, + 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, + 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; + +/*** Helper macros to unroll the permutation. ***/ +#define rol(x, s) (((x) << s) | ((x) >> (64 - s))) +#define REPEAT6(e) e e e e e e +#define REPEAT24(e) REPEAT6(e e e e) +#define REPEAT5(e) e e e e e +#define FOR5(v, s, e) \ + v = 0; \ + REPEAT5(e; v += s;) + +/*** Keccak-f[1600] ***/ +static inline void keccakf(void* state) { + uint64_t* a = (uint64_t*)state; + uint64_t b[5] = {0}; + uint64_t t = 0; + uint8_t x, y; + + for (int i = 0; i < 24; i++) { + // Theta + FOR5(x, 1, + b[x] = 0; + FOR5(y, 5, + b[x] ^= a[x + y]; )) + FOR5(x, 1, + FOR5(y, 5, + a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) + // Rho and pi + t = a[1]; + x = 0; + REPEAT24(b[0] = a[pi[x]]; + a[pi[x]] = rol(t, rho[x]); + t = b[0]; + x++; ) + // Chi + FOR5(y, + 5, + FOR5(x, 1, + b[x] = a[y + x];) + FOR5(x, 1, + a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) + // Iota + a[0] ^= RC[i]; + } +} + +/******** The FIPS202-defined functions. ********/ + +/*** Some helper macros. ***/ + +#define _(S) do { S } while (0) +#define FOR(i, ST, L, S) \ + _(for (size_t i = 0; i < L; i += ST) { S; }) +#define mkapply_ds(NAME, S) \ + static inline void NAME(uint8_t* dst, \ + const uint8_t* src, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } +#define mkapply_sd(NAME, S) \ + static inline void NAME(const uint8_t* src, \ + uint8_t* dst, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } + +mkapply_ds(xorin, dst[i] ^= src[i]) // xorin +mkapply_sd(setout, dst[i] = src[i]) // setout + +#define P keccakf +#define Plen 200 + +// Fold P*F over the full blocks of an input. +#define foldP(I, L, F) \ + while (L >= rate) { \ + F(a, I, rate); \ + P(a); \ + I += rate; \ + L -= rate; \ + } + +/** The sponge-based hash construction. **/ +static inline int hash(uint8_t* out, size_t outlen, + const uint8_t* in, size_t inlen, + size_t rate, uint8_t delim) { + if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) { + return -1; + } + uint8_t a[Plen] = {0}; + // Absorb input. + foldP(in, inlen, xorin); + // Xor in the DS and pad frame. + a[inlen] ^= delim; + a[rate - 1] ^= 0x80; + // Xor in the last block. + xorin(a, in, inlen); + // Apply P + P(a); + // Squeeze output. + foldP(out, outlen, setout); + setout(a, out, outlen); + memset(a, 0, 200); + return 0; +} + +/*** Helper macros to define SHA3 and SHAKE instances. ***/ +#define defshake(bits) \ + int shake##bits(uint8_t* out, size_t outlen, \ + const uint8_t* in, size_t inlen) { \ + return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x1f); \ + } +#define defsha3(bits) \ + int sha3_##bits(uint8_t* out, size_t outlen, \ + const uint8_t* in, size_t inlen) { \ + if (outlen > (bits/8)) { \ + return -1; \ + } \ + return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ + } + +/*** FIPS202 SHAKE VOFs ***/ +defshake(128) +defshake(256) + +/*** FIPS202 SHA3 FOFs ***/ +defsha3(224) +defsha3(256) +defsha3(384) +defsha3(512) + +} + +h256 sha3(bytesConstRef _input) +{ + // FIXME: What with unaligned memory? + h256 ret; + keccak::sha3_256(ret.data(), 32, _input.data(), _input.size()); +// keccak::keccak(ret.data(), 32, (uint64_t const*)_input.data(), _input.size()); + return ret; +} + +} diff --git a/libdevcrypto/SHA3.h b/libdevcore/SHA3.h similarity index 66% rename from libdevcrypto/SHA3.h rename to libdevcore/SHA3.h index 66b8efe11..c3ef524fe 100644 --- a/libdevcrypto/SHA3.h +++ b/libdevcore/SHA3.h @@ -32,46 +32,29 @@ namespace dev // SHA-3 convenience routines. -/// Calculate SHA3-256 hash of the given input and load it into the given output. -void sha3(bytesConstRef _input, bytesRef _output); - -/// Calculate SHA3-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data. -std::string sha3(std::string const& _input, bool _isNibbles); - -/// Calculate SHA3-256 hash of the given input, returning as a byte array. -bytes sha3Bytes(bytesConstRef _input); - -/// Calculate SHA3-256 hash of the given input (presented as a binary string), returning as a byte array. -inline bytes sha3Bytes(std::string const& _input) { return sha3Bytes((std::string*)&_input); } - -/// Calculate SHA3-256 hash of the given input, returning as a byte array. -inline bytes sha3Bytes(bytes const& _input) { return sha3Bytes((bytes*)&_input); } - /// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. h256 sha3(bytesConstRef _input); +/// Calculate SHA3-256 hash of the given input and load it into the given output. +inline void sha3(bytesConstRef _input, bytesRef _output) { sha3(_input).ref().populate(_output); } + /// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. -inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef((bytes*)&_input)); } +inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef(&_input)); } /// Calculate SHA3-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash. inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); } - -/// Calculate SHA3-256 MAC -void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output); /// Calculate SHA3-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash. template inline h256 sha3(FixedHash const& _input) { return sha3(_input.ref()); } -extern h256 EmptySHA3; - -extern h256 EmptyListSHA3; - -// Other crypto convenience routines +/// Calculate SHA3-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data. +inline std::string sha3(std::string const& _input, bool _isNibbles) { return asString((_isNibbles ? sha3(fromHex(_input)) : sha3(bytesConstRef(&_input))).asBytes()); } -bytes aesDecrypt(bytesConstRef _cipher, std::string const& _password, unsigned _rounds = 2000, bytesConstRef _salt = bytesConstRef()); +/// Calculate SHA3-256 MAC +inline void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output) { sha3(_secret.toBytes() + _plain.toBytes()).ref().populate(_output); } -void sha256(bytesConstRef _input, bytesRef _output); +extern h256 EmptySHA3; -void ripemd160(bytesConstRef _input, bytesRef _output); +extern h256 EmptyListSHA3; } diff --git a/libdevcrypto/TrieCommon.cpp b/libdevcore/TrieCommon.cpp similarity index 100% rename from libdevcrypto/TrieCommon.cpp rename to libdevcore/TrieCommon.cpp diff --git a/libdevcrypto/TrieCommon.h b/libdevcore/TrieCommon.h similarity index 100% rename from libdevcrypto/TrieCommon.h rename to libdevcore/TrieCommon.h diff --git a/libdevcrypto/TrieDB.cpp b/libdevcore/TrieDB.cpp similarity index 100% rename from libdevcrypto/TrieDB.cpp rename to libdevcore/TrieDB.cpp diff --git a/libdevcrypto/TrieDB.h b/libdevcore/TrieDB.h similarity index 99% rename from libdevcrypto/TrieDB.h rename to libdevcore/TrieDB.h index cf85148d2..f9d7bff5f 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcore/TrieDB.h @@ -30,9 +30,8 @@ #include #include #include -#include +#include #include "MemoryDB.h" -#include "OverlayDB.h" #include "TrieCommon.h" namespace ldb = leveldb; diff --git a/libdevcrypto/TrieHash.cpp b/libdevcore/TrieHash.cpp similarity index 80% rename from libdevcrypto/TrieHash.cpp rename to libdevcore/TrieHash.cpp index 0b02ce77f..cff3464b5 100644 --- a/libdevcrypto/TrieHash.cpp +++ b/libdevcore/TrieHash.cpp @@ -20,9 +20,9 @@ */ #include "TrieHash.h" -#include -#include // @TODO replace ASAP! -#include +#include +#include // @TODO replace ASAP! +#include #include using namespace std; using namespace dev; @@ -158,74 +158,40 @@ void hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i } } -h256 hash256(StringMap const& _s) -{ - // build patricia tree. - if (_s.empty()) - return sha3(rlp("")); - HexMap hexMap; - for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[asNibbles(i->first)] = i->second; - RLPStream s; - hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); - return sha3(s.out()); -} - -bytes rlp256(StringMap const& _s) +bytes rlp256(BytesMap const& _s) { // build patricia tree. if (_s.empty()) return rlp(""); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[asNibbles(i->first)] = i->second; + hexMap[asNibbles(bytesConstRef(&i->first))] = i->second; RLPStream s; - hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); + hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); return s.out(); } -h256 hash256(u256Map const& _s) +h256 hash256(BytesMap const& _s) { - // build patricia tree. - if (_s.empty()) - return sha3(rlp("")); - HexMap hexMap; - for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[asNibbles(toBigEndianString(i->first))] = asString(rlp(i->second)); - RLPStream s; - hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); - return sha3(s.out()); + return sha3(rlp256(_s)); } -/*h256 orderedTrieRoot(std::vector const& _data) +h256 orderedTrieRoot(std::vector const& _data) { - StringMap m; + BytesMap m; unsigned j = 0; for (auto i: _data) - m[asString(rlp(j++))] = asString(i); + m[rlp(j++)] = i; return hash256(m); -}*/ - -h256 orderedTrieRoot(std::vector const& _data) -{ - MemoryDB db; - GenericTrieDB t(&db); - t.init(); - unsigned j = 0; - for (auto i: _data) - t.insert(rlp(j++), i.toBytes()); - return t.root(); } -h256 orderedTrieRoot(std::vector const& _data) +h256 orderedTrieRoot(std::vector const& _data) { - MemoryDB db; - GenericTrieDB t(&db); - t.init(); + BytesMap m; unsigned j = 0; for (auto i: _data) - t.insert(rlp(j++), i); - return t.root(); + m[rlp(j++)] = i.toBytes(); + return hash256(m); } } diff --git a/libdevcrypto/TrieHash.h b/libdevcore/TrieHash.h similarity index 80% rename from libdevcrypto/TrieHash.h rename to libdevcore/TrieHash.h index b0588fc38..9649ef0c7 100644 --- a/libdevcrypto/TrieHash.h +++ b/libdevcore/TrieHash.h @@ -27,21 +27,18 @@ namespace dev { -bytes rlp256(StringMap const& _s); -h256 hash256(StringMap const& _s); -h256 hash256(u256Map const& _s); +bytes rlp256(BytesMap const& _s); +h256 hash256(BytesMap const& _s); -/*h256 orderedTrieRoot(std::vector const& _data); +h256 orderedTrieRoot(std::vector const& _data); template inline h256 trieRootOver(unsigned _itemCount, T const& _getKey, U const& _getValue) { - StringMap m; + BytesMap m; for (unsigned i = 0; i < _itemCount; ++i) - m[asString(_getKey(i))] = asString(_getValue(i)); + m[_getKey(i)] = _getValue(i); return hash256(m); -}*/ - -using bytesMap = std::unordered_map; +} h256 orderedTrieRoot(std::vector const& _data); h256 orderedTrieRoot(std::vector const& _data); diff --git a/libdevcore/picosha2.h b/libdevcore/picosha2.h new file mode 100644 index 000000000..44b6bee59 --- /dev/null +++ b/libdevcore/picosha2.h @@ -0,0 +1,360 @@ +/* +The MIT License (MIT) + +Copyright (C) 2014 okdshin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#ifndef PICOSHA2_H +#define PICOSHA2_H +//picosha2:20140213 +#include +#include +#include +#include +#include +#include +#include + +namespace picosha2 +{ + +namespace detail +{ + +inline uint8_t mask_8bit(uint8_t x){ + return x&0xff; +} + +inline uint32_t mask_32bit(uint32_t x){ + return x&0xffffffff; +} + +static const uint32_t add_constant[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static const uint32_t initial_message_digest[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z){ + return (x&y)^((~x)&z); +} + +inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z){ + return (x&y)^(x&z)^(y&z); +} + +inline uint32_t rotr(uint32_t x, std::size_t n){ + assert(n < 32); + return mask_32bit((x>>n)|(x<<(32-n))); +} + +inline uint32_t bsig0(uint32_t x){ + return rotr(x, 2)^rotr(x, 13)^rotr(x, 22); +} + +inline uint32_t bsig1(uint32_t x){ + return rotr(x, 6)^rotr(x, 11)^rotr(x, 25); +} + +inline uint32_t shr(uint32_t x, std::size_t n){ + assert(n < 32); + return x >> n; +} + +inline uint32_t ssig0(uint32_t x){ + return rotr(x, 7)^rotr(x, 18)^shr(x, 3); +} + +inline uint32_t ssig1(uint32_t x){ + return rotr(x, 17)^rotr(x, 19)^shr(x, 10); +} + +template +void hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 last){ + (void)last; // FIXME: check this is valid + uint32_t w[64]; + std::fill(w, w+64, 0); + for(std::size_t i = 0; i < 16; ++i){ + w[i] = (static_cast(mask_8bit(*(first+i*4)))<<24) + |(static_cast(mask_8bit(*(first+i*4+1)))<<16) + |(static_cast(mask_8bit(*(first+i*4+2)))<<8) + |(static_cast(mask_8bit(*(first+i*4+3)))); + } + for(std::size_t i = 16; i < 64; ++i){ + w[i] = mask_32bit(ssig1(w[i-2])+w[i-7]+ssig0(w[i-15])+w[i-16]); + } + + uint32_t a = *message_digest; + uint32_t b = *(message_digest+1); + uint32_t c = *(message_digest+2); + uint32_t d = *(message_digest+3); + uint32_t e = *(message_digest+4); + uint32_t f = *(message_digest+5); + uint32_t g = *(message_digest+6); + uint32_t h = *(message_digest+7); + + for(std::size_t i = 0; i < 64; ++i){ + uint32_t temp1 = h+bsig1(e)+ch(e,f,g)+add_constant[i]+w[i]; + uint32_t temp2 = bsig0(a)+maj(a,b,c); + h = g; + g = f; + f = e; + e = mask_32bit(d+temp1); + d = c; + c = b; + b = a; + a = mask_32bit(temp1+temp2); + } + *message_digest += a; + *(message_digest+1) += b; + *(message_digest+2) += c; + *(message_digest+3) += d; + *(message_digest+4) += e; + *(message_digest+5) += f; + *(message_digest+6) += g; + *(message_digest+7) += h; + for(std::size_t i = 0; i < 8; ++i){ + *(message_digest+i) = mask_32bit(*(message_digest+i)); + } +} + +}//namespace detail + +template +void output_hex(InIter first, InIter last, std::ostream& os){ + os.setf(std::ios::hex, std::ios::basefield); + while(first != last){ + os.width(2); + os.fill('0'); + os << static_cast(*first); + ++first; + } + os.setf(std::ios::dec, std::ios::basefield); +} + +template +void bytes_to_hex_string(InIter first, InIter last, std::string& hex_str){ + std::ostringstream oss; + output_hex(first, last, oss); + hex_str.assign(oss.str()); +} + +template +void bytes_to_hex_string(const InContainer& bytes, std::string& hex_str){ + bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str); +} + +template +std::string bytes_to_hex_string(InIter first, InIter last){ + std::string hex_str; + bytes_to_hex_string(first, last, hex_str); + return hex_str; +} + +template +std::string bytes_to_hex_string(const InContainer& bytes){ + std::string hex_str; + bytes_to_hex_string(bytes, hex_str); + return hex_str; +} + +class hash256_one_by_one { +public: + hash256_one_by_one(){ + init(); + } + + void init(){ + buffer_.clear(); + std::fill(data_length_digits_, data_length_digits_+4, 0); + std::copy(detail::initial_message_digest, detail::initial_message_digest+8, h_); + } + + template + void process(RaIter first, RaIter last){ + add_to_data_length(std::distance(first, last)); + std::copy(first, last, std::back_inserter(buffer_)); + std::size_t i = 0; + for(;i+64 <= buffer_.size(); i+=64){ + detail::hash256_block(h_, buffer_.begin()+i, buffer_.begin()+i+64); + } + buffer_.erase(buffer_.begin(), buffer_.begin()+i); + } + + void finish(){ + uint8_t temp[64]; + std::fill(temp, temp+64, 0); + std::size_t remains = buffer_.size(); + std::copy(buffer_.begin(), buffer_.end(), temp); + temp[remains] = 0x80; + + if(remains > 55){ + std::fill(temp+remains+1, temp+64, 0); + detail::hash256_block(h_, temp, temp+64); + std::fill(temp, temp+64-4, 0); + } + else { + std::fill(temp+remains+1, temp+64-4, 0); + } + + write_data_bit_length(&(temp[56])); + detail::hash256_block(h_, temp, temp+64); + } + + template + void get_hash_bytes(OutIter first, OutIter last)const{ + for(const uint32_t* iter = h_; iter != h_+8; ++iter){ + for(std::size_t i = 0; i < 4 && first != last; ++i){ + *(first++) = detail::mask_8bit(static_cast((*iter >> (24-8*i)))); + } + } + } + +private: + void add_to_data_length(uint32_t n) { + uint32_t carry = 0; + data_length_digits_[0] += n; + for(std::size_t i = 0; i < 4; ++i) { + data_length_digits_[i] += carry; + if(data_length_digits_[i] >= 65536u) { + data_length_digits_[i] -= 65536u; + carry = 1; + } + else { + break; + } + } + } + void write_data_bit_length(uint8_t* begin) { + uint32_t data_bit_length_digits[4]; + std::copy( + data_length_digits_, data_length_digits_+4, + data_bit_length_digits + ); + + // convert byte length to bit length (multiply 8 or shift 3 times left) + uint32_t carry = 0; + for(std::size_t i = 0; i < 4; ++i) { + uint32_t before_val = data_bit_length_digits[i]; + data_bit_length_digits[i] <<= 3; + data_bit_length_digits[i] |= carry; + data_bit_length_digits[i] &= 65535u; + carry = (before_val >> (16-3)) & 65535u; + } + + // write data_bit_length + for(int i = 3; i >= 0; --i) { + (*begin++) = static_cast(data_bit_length_digits[i] >> 8); + (*begin++) = static_cast(data_bit_length_digits[i]); + } + } + std::vector buffer_; + uint32_t data_length_digits_[4]; //as 64bit integer (16bit x 4 integer) + uint32_t h_[8]; +}; + +inline void get_hash_hex_string(const hash256_one_by_one& hasher, std::string& hex_str){ + uint8_t hash[32]; + hasher.get_hash_bytes(hash, hash+32); + return bytes_to_hex_string(hash, hash+32, hex_str); +} + +inline std::string get_hash_hex_string(const hash256_one_by_one& hasher){ + std::string hex_str; + get_hash_hex_string(hasher, hex_str); + return hex_str; +} + +template +void hash256(RaIter first, RaIter last, OutIter first2, OutIter last2){ + hash256_one_by_one hasher; + //hasher.init(); + hasher.process(first, last); + hasher.finish(); + hasher.get_hash_bytes(first2, last2); +} + +template +void hash256(RaIter first, RaIter last, OutContainer& dst){ + hash256(first, last, dst.begin(), dst.end()); +} + +template +void hash256(const RaContainer& src, OutIter first, OutIter last){ + hash256(src.begin(), src.end(), first, last); +} + +template +void hash256(const RaContainer& src, OutContainer& dst){ + hash256(src.begin(), src.end(), dst.begin(), dst.end()); +} + + +template +void hash256_hex_string(RaIter first, RaIter last, std::string& hex_str){ + uint8_t hashed[32]; + hash256(first, last, hashed, hashed+32); + std::ostringstream oss; + output_hex(hashed, hashed+32, oss); + hex_str.assign(oss.str()); +} + +template +std::string hash256_hex_string(RaIter first, RaIter last){ + std::string hex_str; + hash256_hex_string(first, last, hex_str); + return hex_str; +} + +inline void hash256_hex_string(const std::string& src, std::string& hex_str){ + hash256_hex_string(src.begin(), src.end(), hex_str); +} + +template +void hash256_hex_string(const RaContainer& src, std::string& hex_str){ + hash256_hex_string(src.begin(), src.end(), hex_str); +} + +template +std::string hash256_hex_string(const RaContainer& src){ + return hash256_hex_string(src.begin(), src.end()); +} + +}//namespace picosha2 + +#endif //PICOSHA2_H diff --git a/libdevcrypto/AES.cpp b/libdevcrypto/AES.cpp index 56885ae36..e9edac0d3 100644 --- a/libdevcrypto/AES.cpp +++ b/libdevcrypto/AES.cpp @@ -19,9 +19,9 @@ * @date 2014 */ -#include "CryptoPP.h" #include "AES.h" - +#include +#include "CryptoPP.h" using namespace std; using namespace dev; using namespace dev::crypto; @@ -58,3 +58,31 @@ size_t Stream::streamOut(bytes&) return 0; } +bytes dev::aesDecrypt(bytesConstRef _ivCipher, std::string const& _password, unsigned _rounds, bytesConstRef _salt) +{ + bytes pw = asBytes(_password); + + if (!_salt.size()) + _salt = &pw; + + bytes target(64); + CryptoPP::PKCS5_PBKDF2_HMAC().DeriveKey(target.data(), target.size(), 0, pw.data(), pw.size(), _salt.data(), _salt.size(), _rounds); + + try + { + CryptoPP::AES::Decryption aesDecryption(target.data(), 16); + auto cipher = _ivCipher.cropped(16); + auto iv = _ivCipher.cropped(0, 16); + CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data()); + std::string decrypted; + CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(decrypted)); + stfDecryptor.Put(cipher.data(), cipher.size()); + stfDecryptor.MessageEnd(); + return asBytes(decrypted); + } + catch (exception const& e) + { + cerr << e.what() << endl; + return bytes(); + } +} diff --git a/libdevcrypto/AES.h b/libdevcrypto/AES.h index f0646eb85..32d1880dc 100644 --- a/libdevcrypto/AES.h +++ b/libdevcrypto/AES.h @@ -86,4 +86,7 @@ private: } } -} \ No newline at end of file + +bytes aesDecrypt(bytesConstRef _cipher, std::string const& _password, unsigned _rounds = 2000, bytesConstRef _salt = bytesConstRef()); + +} diff --git a/libdevcrypto/CMakeLists.txt b/libdevcrypto/CMakeLists.txt index 295dd257a..7df1149b0 100644 --- a/libdevcrypto/CMakeLists.txt +++ b/libdevcrypto/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES}) +target_link_libraries(${EXECUTABLE} scrypt) target_link_libraries(${EXECUTABLE} devcore) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index a5c176fe6..814f8309e 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -25,9 +25,11 @@ #include #include #include +#include #include -#include "SHA3.h" -#include "FileSystem.h" +#include +#include +#include "AES.h" #include "CryptoPP.h" using namespace std; using namespace dev; @@ -118,10 +120,11 @@ std::pair dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plai return make_pair(encryptSymNoAuth(_k, iv, _plain), iv); } -bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain) +bytes dev::encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain) { - const int c_aesKeyLen = 16; - SecByteBlock key(_k.data(), c_aesKeyLen); + if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32) + return bytes(); + SecByteBlock key(_k.data(), _k.size()); try { CTR_Mode::Encryption e; @@ -137,10 +140,11 @@ bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plai } } -bytes dev::decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher) +bytes dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher) { - const size_t c_aesKeyLen = 16; - SecByteBlock key(_k.data(), c_aesKeyLen); + if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32) + return bytes(); + SecByteBlock key(_k.data(), _k.size()); try { CTR_Mode::Decryption d; @@ -179,6 +183,13 @@ bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, return ret; } +bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen) +{ + bytes ret(_dkLen); + libscrypt_scrypt((uint8_t const*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _n, _r, _p, ret.data(), ret.size()); + return ret; +} + KeyPair KeyPair::create() { static boost::thread_specific_ptr s_eng; diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 6464c7ede..10bcdd067 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -98,18 +98,26 @@ 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. std::pair encryptSymNoAuth(h128 const& _k, bytesConstRef _plain); /// Encrypts payload with specified IV/ctr using AES128-CTR. -bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain); +bytes encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain); /// Decrypts payload with specified IV/ctr using AES128-CTR. -bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher); +bytes decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher); + +/// Encrypts payload with specified IV/ctr using AES128-CTR. +inline bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); } +inline bytes encryptSymNoAuth(h256 const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); } + +/// Decrypts payload with specified IV/ctr using AES128-CTR. +inline bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); } +inline bytes decryptSymNoAuth(h256 const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); } /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); @@ -123,6 +131,9 @@ bool verify(Public const& _k, Signature const& _s, h256 const& _hash); /// Derive key via PBKDF2. bytes pbkdf2(std::string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen = 32); +/// Derive key via Scrypt. +bytes scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen); + /// Simple class that represents a "key pair". /// All of the data of the class can be regenerated from the secret key (m_secret) alone. /// Actually stores a tuplet of secret, public and address (the right 160-bits of the public). diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index 4991e3713..ca8a2e6b5 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -49,7 +49,7 @@ #include #pragma warning(pop) #pragma GCC diagnostic pop -#include "SHA3.h" +#include #include "Common.h" namespace dev diff --git a/libdevcrypto/ECDHE.cpp b/libdevcrypto/ECDHE.cpp index a00a92872..a5aaf3984 100644 --- a/libdevcrypto/ECDHE.cpp +++ b/libdevcrypto/ECDHE.cpp @@ -19,9 +19,9 @@ * @date 2014 */ -#include "SHA3.h" -#include "CryptoPP.h" #include "ECDHE.h" +#include +#include "CryptoPP.h" using namespace std; using namespace dev; diff --git a/libdevcrypto/OverlayDB.h b/libdevcrypto/OverlayDB.h index 2e5428bdf..b37d2c11b 100644 --- a/libdevcrypto/OverlayDB.h +++ b/libdevcrypto/OverlayDB.h @@ -29,7 +29,7 @@ #include #include #include -#include "MemoryDB.h" +#include namespace ldb = leveldb; namespace dev diff --git a/libdevcrypto/SHA3.cpp b/libdevcrypto/SHA3.cpp deleted file mode 100644 index b7a47b745..000000000 --- a/libdevcrypto/SHA3.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - 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 SHA3.cpp - * @author Gav Wood - * @date 2014 - */ - -#include "SHA3.h" - -#include -#include "CryptoPP.h" -using namespace std; -using namespace dev; - -namespace dev -{ - -h256 EmptySHA3 = sha3(bytesConstRef()); -h256 EmptyListSHA3 = sha3(rlpList()); - -std::string sha3(std::string const& _input, bool _hex) -{ - if (!_hex) - { - string ret(32, '\0'); - sha3(bytesConstRef((byte const*)_input.data(), _input.size()), bytesRef((byte*)ret.data(), 32)); - return ret; - } - - uint8_t buf[32]; - sha3(bytesConstRef((byte const*)_input.data(), _input.size()), bytesRef((byte*)&(buf[0]), 32)); - std::string ret(64, '\0'); - for (unsigned int i = 0; i < 32; i++) - sprintf((char*)(ret.data())+i*2, "%02x", buf[i]); - return ret; -} - -void sha3(bytesConstRef _input, bytesRef _output) -{ - CryptoPP::SHA3_256 ctx; - ctx.Update((byte*)_input.data(), _input.size()); - assert(_output.size() >= 32); - ctx.Final(_output.data()); -} - -void ripemd160(bytesConstRef _input, bytesRef _output) -{ - CryptoPP::RIPEMD160 ctx; - ctx.Update((byte*)_input.data(), _input.size()); - assert(_output.size() >= 32); - ctx.Final(_output.data()); -} - -void sha256(bytesConstRef _input, bytesRef _output) -{ - CryptoPP::SHA256 ctx; - ctx.Update((byte*)_input.data(), _input.size()); - assert(_output.size() >= 32); - ctx.Final(_output.data()); -} - -bytes sha3Bytes(bytesConstRef _input) -{ - bytes ret(32); - sha3(_input, &ret); - return ret; -} - -h256 sha3(bytesConstRef _input) -{ - h256 ret; - sha3(_input, bytesRef(&ret[0], 32)); - return ret; -} - -void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output) -{ - CryptoPP::SHA3_256 ctx; - assert(_secret.size() > 0); - ctx.Update((byte*)_secret.data(), _secret.size()); - ctx.Update((byte*)_plain.data(), _plain.size()); - assert(_output.size() >= 32); - ctx.Final(_output.data()); -} - -bytes aesDecrypt(bytesConstRef _ivCipher, std::string const& _password, unsigned _rounds, bytesConstRef _salt) -{ - bytes pw = asBytes(_password); - - if (!_salt.size()) - _salt = &pw; - - bytes target(64); - CryptoPP::PKCS5_PBKDF2_HMAC().DeriveKey(target.data(), target.size(), 0, pw.data(), pw.size(), _salt.data(), _salt.size(), _rounds); - - try - { - CryptoPP::AES::Decryption aesDecryption(target.data(), 16); - auto cipher = _ivCipher.cropped(16); - auto iv = _ivCipher.cropped(0, 16); - CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data()); - std::string decrypted; - CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(decrypted)); - stfDecryptor.Put(cipher.data(), cipher.size()); - stfDecryptor.MessageEnd(); - return asBytes(decrypted); - } - catch (exception const& e) - { - cerr << e.what() << endl; - return bytes(); - } -} - -} diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 9be0b89e8..11ff98bf6 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -22,18 +22,69 @@ #include "SecretStore.h" #include #include +#include #include #include #include +#include +#include #include -#include "SHA3.h" -#include "FileSystem.h" using namespace std; using namespace dev; namespace js = json_spirit; namespace fs = boost::filesystem; -SecretStore::SecretStore() +static const int c_keyFileVersion = 3; + +static js::mValue upgraded(std::string const& _s) +{ + js::mValue v; + js::read_string(_s, v); + if (v.type() != js::obj_type) + return js::mValue(); + js::mObject ret = v.get_obj(); + unsigned version = ret.count("Version") ? stoi(ret["Version"].get_str()) : ret.count("version") ? ret["version"].get_int() : 0; + if (version == 1) + { + // upgrade to version 2 + js::mObject old; + swap(old, ret); + + ret["id"] = old["Id"]; + js::mObject c; + c["ciphertext"] = old["Crypto"].get_obj()["CipherText"]; + c["cipher"] = "aes-128-cbc"; + { + js::mObject cp; + cp["iv"] = old["Crypto"].get_obj()["IV"]; + c["cipherparams"] = cp; + } + c["kdf"] = old["Crypto"].get_obj()["KeyHeader"].get_obj()["Kdf"]; + { + js::mObject kp; + kp["salt"] = old["Crypto"].get_obj()["Salt"]; + for (auto const& i: old["Crypto"].get_obj()["KeyHeader"].get_obj()["KdfParams"].get_obj()) + if (i.first != "SaltLen") + kp[boost::to_lower_copy(i.first)] = i.second; + c["kdfparams"] = kp; + } + c["sillymac"] = old["Crypto"].get_obj()["MAC"]; + c["sillymacjson"] = _s; + ret["crypto"] = c; + version = 2; + } + if (version == 2) + { + ret["crypto"].get_obj()["cipher"] = "aes-128-ctr"; + ret["crypto"].get_obj()["compat"] = "2"; + version = 3; + } + if (version == c_keyFileVersion) + return ret; + return js::mValue(); +} + +SecretStore::SecretStore(std::string const& _path): m_path(_path) { load(); } @@ -42,10 +93,11 @@ SecretStore::~SecretStore() { } -bytes SecretStore::secret(h128 const& _uuid, function const& _pass) const +bytes SecretStore::secret(h128 const& _uuid, function const& _pass, bool _useCache) const { + (void)_pass; auto rit = m_cached.find(_uuid); - if (rit != m_cached.end()) + if (_useCache && rit != m_cached.end()) return rit->second; auto it = m_keys.find(_uuid); if (it == m_keys.end()) @@ -93,7 +145,7 @@ void SecretStore::save(std::string const& _keysPath) js::read_string(k.second.first, crypto); v["crypto"] = crypto; v["id"] = uuid; - v["version"] = 2; + v["version"] = c_keyFileVersion; writeFile(filename, js::write_string(js::mValue(v), true)); if (!k.second.second.empty() && k.second.second != filename) boost::filesystem::remove(k.second.second); @@ -105,48 +157,88 @@ void SecretStore::load(std::string const& _keysPath) { fs::path p(_keysPath); boost::filesystem::create_directories(p); - js::mValue v; for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) if (is_regular_file(it->path())) - { - cdebug << "Reading" << it->path(); - js::read_string(contentsString(it->path().string()), v); - if (v.type() == js::obj_type) - { - js::mObject o = v.get_obj(); - int version = o.count("Version") ? stoi(o["Version"].get_str()) : o.count("version") ? o["version"].get_int() : 0; - if (version == 2) - m_keys[fromUUID(o["id"].get_str())] = make_pair(js::write_string(o["crypto"], false), it->path().string()); - else - cwarn << "Cannot read key version" << version; - } -// else -// cwarn << "Invalid JSON in key file" << it->path().string(); - } + readKey(it->path().string(), true); +} + +h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) +{ + cdebug << "Reading" << _file; + return readKeyContent(contentsString(_file), _deleteFile ? _file : string()); +} + +h128 SecretStore::readKeyContent(std::string const& _content, std::string const& _file) +{ + js::mValue u = upgraded(_content); + if (u.type() == js::obj_type) + { + js::mObject& o = u.get_obj(); + auto uuid = fromUUID(o["id"].get_str()); + m_keys[uuid] = make_pair(js::write_string(o["crypto"], false), _file); + return uuid; + } + else + cwarn << "Invalid JSON in key file" << _file; + return h128(); } -std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) +bool SecretStore::recode(h128 const& _uuid, string const& _newPass, std::function const& _pass, KDF _kdf) +{ +// cdebug << "recode:" << toUUID(_uuid); + bytes s = secret(_uuid, _pass, true); + if (s.empty()) + return false; + m_keys[_uuid].first = encrypt(s, _newPass, _kdf); + save(); + return true; +} + +std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF _kdf) { js::mObject ret; // KDF info - unsigned dklen = 16; - unsigned iterations = 262144; + unsigned dklen = 32; bytes salt = h256::random().asBytes(); - ret["kdf"] = "pbkdf2"; + bytes derivedKey; + if (_kdf == KDF::Scrypt) { - js::mObject params; - params["prf"] = "hmac-sha256"; - params["c"] = (int)iterations; - params["salt"] = toHex(salt); - params["dklen"] = (int)dklen; - ret["kdfparams"] = params; + unsigned iterations = 262144; + unsigned p = 1; + unsigned r = 8; + ret["kdf"] = "scrypt"; + { + js::mObject params; + params["n"] = (int64_t)iterations; + params["r"] = (int)r; + params["p"] = (int)p; + params["dklen"] = (int)dklen; + params["salt"] = toHex(salt); + ret["kdfparams"] = params; + } + derivedKey = scrypt(_pass, salt, iterations, r, p, dklen); + } + else + { + unsigned iterations = 262144; + ret["kdf"] = "pbkdf2"; + { + js::mObject params; + params["prf"] = "hmac-sha256"; + params["c"] = (int)iterations; + params["salt"] = toHex(salt); + params["dklen"] = (int)dklen; + ret["kdfparams"] = params; + } + derivedKey = pbkdf2(_pass, salt, iterations, dklen); } - bytes derivedKey = pbkdf2(_pass, salt, iterations, dklen); +// cdebug << "derivedKey" << toHex(derivedKey); // cipher info - ret["cipher"] = "aes-128-cbc"; - h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); + ret["cipher"] = "aes-128-ctr"; + h128 key(derivedKey, h128::AlignLeft); +// cdebug << "cipherKey" << key.hex(); h128 iv = h128::random(); { js::mObject params; @@ -159,7 +251,9 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) ret["ciphertext"] = toHex(cipherText); // and mac. - h256 mac = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + h256 mac = sha3(ref(derivedKey).cropped(16, 16).toBytes() + cipherText); +// cdebug << "macBody" << toHex(ref(derivedKey).cropped(16, 16).toBytes() + cipherText); +// cdebug << "mac" << mac.hex(); ret["mac"] = toHex(mac.ref()); return js::write_string((js::mValue)ret, true); @@ -188,30 +282,65 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) bytes salt = fromHex(params["salt"].get_str()); derivedKey = pbkdf2(_pass, salt, iterations, params["dklen"].get_int()); } + else if (o["kdf"].get_str() == "scrypt") + { + auto p = o["kdfparams"].get_obj(); + derivedKey = scrypt(_pass, fromHex(p["salt"].get_str()), p["n"].get_int(), p["r"].get_int(), p["p"].get_int(), p["dklen"].get_int()); + } else { cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported."; return bytes(); } + if (derivedKey.size() < 32 && !(o.count("compat") && o["compat"].get_str() == "2")) + { + cwarn << "Derived key's length too short (<32 bytes)"; + return bytes(); + } + bytes cipherText = fromHex(o["ciphertext"].get_str()); // check MAC - h256 mac(o["mac"].get_str()); - h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); - if (mac != macExp) + if (o.count("mac")) { - cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); - return bytes(); + h256 mac(o["mac"].get_str()); + h256 macExp; + if (o.count("compat") && o["compat"].get_str() == "2") + macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + else + macExp = sha3(bytesConstRef(&derivedKey).cropped(16, 16).toBytes() + cipherText); + if (mac != macExp) + { + cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); + return bytes(); + } + } + else if (o.count("sillymac")) + { + h256 mac(o["sillymac"].get_str()); + h256 macExp = sha3(asBytes(o["sillymacjson"].get_str()) + bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + if (mac != macExp) + { + cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); + return bytes(); + } } + else + cwarn << "No MAC. Proceeding anyway."; // decrypt - if (o["cipher"].get_str() == "aes-128-cbc") + if (o["cipher"].get_str() == "aes-128-ctr") { auto params = o["cipherparams"].get_obj(); - h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); h128 iv(params["iv"].get_str()); - return decryptSymNoAuth(key, iv, &cipherText); + if (o.count("compat") && o["compat"].get_str() == "2") + { + h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); + return decryptSymNoAuth(key, iv, &cipherText); + } + else + return decryptSymNoAuth(h128(derivedKey, h128::AlignLeft), iv, &cipherText); } else { diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 1fb6adf4a..4474212b1 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -24,33 +24,54 @@ #include #include #include +#include #include "Common.h" -#include "FileSystem.h" namespace dev { +enum class KDF { + PBKDF2_SHA256, + Scrypt, +}; + class SecretStore { public: - SecretStore(); + SecretStore(std::string const& _path = defaultPath()); ~SecretStore(); - bytes secret(h128 const& _uuid, std::function const& _pass) const; + bytes secret(h128 const& _uuid, std::function const& _pass, bool _useCache = true) const; + h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; } + h128 importKeyContent(std::string const& _content) { auto ret = readKeyContent(_content, std::string()); if (ret) save(); return ret; } h128 importSecret(bytes const& _s, std::string const& _pass); + bool recode(h128 const& _uuid, std::string const& _newPass, std::function const& _pass, KDF _kdf = KDF::Scrypt); void kill(h128 const& _uuid); + std::vector keys() const { return keysOf(m_keys); } + // Clear any cached keys. void clearCache() const; + // Doesn't save(). + h128 readKey(std::string const& _file, bool _deleteFile); + h128 readKeyContent(std::string const& _content, std::string const& _file = std::string()); + + void save(std::string const& _keysPath); + void save() { save(m_path); } + + static std::string defaultPath() { return getDataDir("web3") + "/keys"; } + private: - void save(std::string const& _keysPath = getDataDir("web3") + "/keys"); - void load(std::string const& _keysPath = getDataDir("web3") + "/keys"); - static std::string encrypt(bytes const& _v, std::string const& _pass); + void load(std::string const& _keysPath); + void load() { load(m_path); } + static std::string encrypt(bytes const& _v, std::string const& _pass, KDF _kdf = KDF::Scrypt); static bytes decrypt(std::string const& _v, std::string const& _pass); mutable std::unordered_map m_cached; std::unordered_map> m_keys; + + std::string m_path; }; } diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 3891f2ef6..93ce9ab22 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -206,14 +206,15 @@ bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned work m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32); // compute dag on CPU - { + try { m_queue.enqueueWriteBuffer(m_dag, CL_TRUE, 0, _dagSize, _dag); - - // if this throws then it's because we probably need to subdivide the dag uploads for compatibility -// void* dag_ptr = m_queue.enqueueMapBuffer(m_dag, true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, _dagSize); - // memcpying 1GB: horrible... really. horrible. but necessary since we can't mmap *and* gpumap. -// _fillDAG(dag_ptr); -// m_queue.enqueueUnmapMemObject(m_dag, dag_ptr); + } + catch (...) + { + // didn't work. shitty driver. try allocating in CPU RAM and manually memcpying it. + void* dag_ptr = m_queue.enqueueMapBuffer(m_dag, true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, _dagSize); + memcpy(dag_ptr, _dag, _dagSize); + m_queue.enqueueUnmapMemObject(m_dag, dag_ptr); } // create mining buffers @@ -303,7 +304,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook }; std::queue pending; - static uint32_t const c_zero = 0; + uint32_t const c_zero = 0; // update header constant buffer m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); diff --git a/libethash/internal.c b/libethash/internal.c index e881e0c7b..2a12163f7 100644 --- a/libethash/internal.c +++ b/libethash/internal.c @@ -368,7 +368,7 @@ static bool ethash_mmap(struct ethash_full* ret, FILE* f) if ((fd = ethash_fileno(ret->file)) == -1) { return false; } - mmapped_data= mmap( + mmapped_data = mmap( NULL, (size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE, PROT_READ | PROT_WRITE, diff --git a/libethcore/ABI.h b/libethcore/ABI.h index 7fca09c9a..09aca6754 100644 --- a/libethcore/ABI.h +++ b/libethcore/ABI.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include namespace dev { diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 00a5108c4..0e125b607 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -21,8 +21,8 @@ #include #include -#include -#include +#include +#include #include #include #include "EthashAux.h" @@ -191,22 +191,12 @@ void BlockInfo::populate(bytesConstRef _block, Strictness _s, h256 const& _h) struct BlockInfoDiagnosticsChannel: public LogChannel { static const char* name() { return EthBlue "▧" EthWhite " ◌"; } static const int verbosity = 9; }; -template h256 trieRootOver(unsigned _itemCount, T const& _getKey, U const& _getValue) -{ - MemoryDB db; - GenericTrieDB t(&db); - t.init(); - for (unsigned i = 0; i < _itemCount; ++i) - t.insert(_getKey(i), _getValue(i)); - return t.root(); -} - void BlockInfo::verifyInternals(bytesConstRef _block) const { RLP root(_block); auto txList = root[1]; - auto expectedRoot = trieRootOver(txList.itemCount(), [&](unsigned i){ return rlp(i); }, [&](unsigned i){ return txList[i].data(); }); + auto expectedRoot = trieRootOver(txList.itemCount(), [&](unsigned i){ return rlp(i); }, [&](unsigned i){ return txList[i].data().toBytes(); }); clog(BlockInfoDiagnosticsChannel) << "Expected trie root:" << toString(expectedRoot); if (transactionsRoot != expectedRoot) diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index c74aeba5e..56db647f3 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "Exceptions.h" #include "ProofOfWork.h" using namespace std; diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 7609d8b6b..60585a162 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #if ETH_ETHASHCL || !ETH_TRUE @@ -75,6 +75,13 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) return ret; } +void Ethash::ensurePrecomputed(unsigned _number) +{ + if (_number % ETHASH_EPOCH_LENGTH > ETHASH_EPOCH_LENGTH * 9 / 10) + // 90% of the way to the new epoch + EthashAux::computeFull(EthashAux::seedHash(_number + ETHASH_EPOCH_LENGTH), true); +} + void Ethash::prep(BlockInfo const& _header, std::function const& _f) { EthashAux::full(_header.seedHash(), true, _f); @@ -135,8 +142,12 @@ void Ethash::CPUMiner::workLoop() WorkPackage w = work(); EthashAux::FullType dag; - while (!shouldStop() && !(dag = EthashAux::full(w.seedHash))) - this_thread::sleep_for(chrono::milliseconds(500)); + while (!shouldStop() && !dag) + { + while (!shouldStop() && EthashAux::computeFull(w.seedHash, true) != 100) + this_thread::sleep_for(chrono::milliseconds(500)); + dag = EthashAux::full(w.seedHash, false); + } h256 boundary = w.boundary; unsigned hashCount = 1; @@ -306,6 +317,7 @@ void Ethash::GPUMiner::workLoop() cnote << "workLoop" << !!m_miner << m_minerSeed << w.seedHash; if (!m_miner || m_minerSeed != w.seedHash) { + cnote << "Initialising miner..."; m_minerSeed = w.seedHash; delete m_miner; @@ -333,9 +345,9 @@ void Ethash::GPUMiner::workLoop() uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook); } - catch (...) + catch (cl::Error const& _e) { - cwarn << "Error GPU mining. GPU memory fragmentation?"; + cwarn << "Error GPU mining: " << _e.what() << "(" << _e.err() << ")"; } } diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 81f842e4f..86540678f 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -74,6 +74,7 @@ public: static std::string name(); static unsigned revision(); static void prep(BlockInfo const& _header, std::function const& _f = std::function()); + static void ensurePrecomputed(unsigned _number); static bool verify(BlockInfo const& _header); static bool preVerify(BlockInfo const& _header); static WorkPackage package(BlockInfo const& _header); diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index eccb986a7..06da22f98 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -31,8 +31,8 @@ #include #include #include -#include -#include +#include +#include #include #include "BlockInfo.h" #include "Exceptions.h" @@ -133,7 +133,9 @@ bytesConstRef EthashAux::LightAllocation::data() const EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb) { +// cdebug << "About to call ethash_full_new..."; full = ethash_full_new(_light, _cb); +// cdebug << "Called OK."; if (!full) BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new()")); } @@ -167,12 +169,12 @@ EthashAux::FullType EthashAux::full(h256 const& _seedHash, bool _createIfMissing return ret; } - if (_createIfMissing || computeFull(_seedHash) == 100) + if (_createIfMissing || computeFull(_seedHash, false) == 100) { s_dagCallback = _f; - cnote << "Loading from libethash..."; +// cnote << "Loading from libethash..."; ret = make_shared(l->light, dagCallbackShim); - cnote << "Done loading."; +// cnote << "Done loading."; DEV_GUARDED(get()->x_fulls) get()->m_fulls[_seedHash] = get()->m_lastUsedFull = ret; @@ -183,7 +185,7 @@ EthashAux::FullType EthashAux::full(h256 const& _seedHash, bool _createIfMissing #define DEV_IF_THROWS(X) try { X; } catch (...) -unsigned EthashAux::computeFull(h256 const& _seedHash) +unsigned EthashAux::computeFull(h256 const& _seedHash, bool _createIfMissing) { Guard l(get()->x_fulls); uint64_t blockNumber; @@ -199,7 +201,7 @@ unsigned EthashAux::computeFull(h256 const& _seedHash) return 100; } - if (!get()->m_fullGenerator || !get()->m_fullGenerator->joinable()) + if (_createIfMissing && (!get()->m_fullGenerator || !get()->m_fullGenerator->joinable())) { get()->m_fullProgress = 0; get()->m_generatingFullNumber = blockNumber / ETHASH_EPOCH_LENGTH * ETHASH_EPOCH_LENGTH; diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index b09f6465f..e6fed519f 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -71,7 +71,7 @@ public: static const uint64_t NotGenerating = (uint64_t)-1; /// Kicks off generation of DAG for @a _seedHash and @returns false or @returns true if ready. - static unsigned computeFull(h256 const& _seedHash); + static unsigned computeFull(h256 const& _seedHash, bool _createIfMissing = true); /// Information on the generation progress. static std::pair fullGeneratingProgress() { return std::make_pair(get()->m_generatingFullNumber, get()->m_fullProgress); } /// Kicks off generation of DAG for @a _blocknumber and blocks until ready; @returns result or empty pointer if not existing and _createIfMissing is false. diff --git a/libethcore/ICAP.cpp b/libethcore/ICAP.cpp index 6fce19a62..158c297f8 100644 --- a/libethcore/ICAP.cpp +++ b/libethcore/ICAP.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "Exceptions.h" #include "ABI.h" using namespace std; diff --git a/libethcore/ICAP.h b/libethcore/ICAP.h index b4229e1f5..a2456bd40 100644 --- a/libethcore/ICAP.h +++ b/libethcore/ICAP.h @@ -38,8 +38,6 @@ namespace eth struct InvalidICAP: virtual public dev::Exception {}; -static const std::string EmptyString; - /** * @brief Encapsulation of an ICAP address. * Can be encoded, decoded, looked-up and inspected. diff --git a/libethereum/KeyManager.cpp b/libethcore/KeyManager.cpp similarity index 68% rename from libethereum/KeyManager.cpp rename to libethcore/KeyManager.cpp index 11b2cb2a6..182201301 100644 --- a/libethereum/KeyManager.cpp +++ b/libethcore/KeyManager.cpp @@ -31,8 +31,8 @@ using namespace dev; using namespace eth; namespace fs = boost::filesystem; -KeyManager::KeyManager(std::string const& _keysFile): - m_keysFile(_keysFile) +KeyManager::KeyManager(std::string const& _keysFile, std::string const& _secretsPath): + m_keysFile(_keysFile), m_store(_secretsPath) {} KeyManager::~KeyManager() @@ -49,6 +49,32 @@ void KeyManager::create(std::string const& _pass) write(_pass, m_keysFile); } +bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass, KDF _kdf) +{ + noteHint(_newPass, _hint); + h128 u = uuid(_address); + if (!store().recode(u, _newPass, [&](){ return getPassword(u, _pass); }, _kdf)) + return false; + + m_keyInfo[u].passHash = hashPassword(_newPass); + write(); + return true; +} + +bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass, KDF _kdf) +{ + h128 u = uuid(_address); + std::string p; + if (_newPass == SemanticPassword::Existing) + p = getPassword(u, _pass); + else if (_newPass == SemanticPassword::Master) + p = defaultPassword(); + else + return false; + + return recode(_address, p, string(), _pass, _kdf); +} + bool KeyManager::load(std::string const& _pass) { try { @@ -61,12 +87,21 @@ bool KeyManager::load(std::string const& _pass) if (version == 1) { for (auto const& i: s[1]) + { m_keyInfo[m_addrLookup[(Address)i[0]] = (h128)i[1]] = KeyInfo((h256)i[2], (std::string)i[3]); + cdebug << toString((Address)i[0]) << toString((h128)i[1]) << toString((h256)i[2]) << (std::string)i[3]; + } + for (auto const& i: s[2]) m_passwordInfo[(h256)i[0]] = (std::string)i[1]; m_password = (string)s[3]; } + cdebug << hashPassword(m_password) << toHex(m_password); m_cachedPasswords[hashPassword(m_password)] = m_password; + cdebug << hashPassword(asString(m_key.ref())) << m_key.hex(); + m_cachedPasswords[hashPassword(asString(m_key.ref()))] = asString(m_key.ref()); + cdebug << hashPassword(_pass) << _pass; + m_cachedPasswords[m_master = hashPassword(_pass)] = _pass; return true; } catch (...) { @@ -84,18 +119,35 @@ Secret KeyManager::secret(Address const& _address, function const Secret KeyManager::secret(h128 const& _uuid, function const& _pass) const { - return Secret(m_store.secret(_uuid, [&](){ - auto kit = m_keyInfo.find(_uuid); - if (kit != m_keyInfo.end()) + return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); })); +} + +std::string KeyManager::getPassword(h128 const& _uuid, function const& _pass) const +{ + auto kit = m_keyInfo.find(_uuid); + h256 ph; + if (kit != m_keyInfo.end()) + ph = kit->second.passHash; + return getPassword(ph, _pass); +} + +std::string KeyManager::getPassword(h256 const& _passHash, function const& _pass) const +{ + auto it = m_cachedPasswords.find(_passHash); + if (it != m_cachedPasswords.end()) + return it->second; + for (unsigned i = 0; i< 10; ++i) + { + std::string p = _pass(); + if (p.empty()) + break; + if (hashPassword(p) == _passHash || !_passHash) { - auto it = m_cachedPasswords.find(kit->second.passHash); - if (it != m_cachedPasswords.end()) - return it->second; + m_cachedPasswords[hashPassword(p)] = p; + return p; } - std::string p = _pass(); - m_cachedPasswords[hashPassword(p)] = p; - return p; - })); + } + return string(); } h128 KeyManager::uuid(Address const& _a) const @@ -166,7 +218,7 @@ std::unordered_map> KeyManager::acc std::unordered_map> ret; for (auto const& i: m_addrLookup) if (m_keyInfo.count(i.second) > 0) - ret[i.first] = make_pair(m_keyInfo.at(i.second).info, m_passwordInfo.at(m_keyInfo.at(i.second).passHash)); + ret[i.first] = make_pair(m_keyInfo.count(i.second) ? m_keyInfo.at(i.second).info : "", m_keyInfo.count(i.second) && m_passwordInfo.count(m_keyInfo.at(i.second).passHash) ? m_passwordInfo.at(m_keyInfo.at(i.second).passHash) : ""); return ret; } @@ -189,6 +241,9 @@ void KeyManager::write(std::string const& _pass, std::string const& _keysFile) c bytes salt = h256::random().asBytes(); writeFile(_keysFile + ".salt", salt); auto key = h128(pbkdf2(_pass, salt, 262144, 16)); + + m_cachedPasswords[hashPassword(_pass)] = _pass; + m_master = hashPassword(_pass); write(key, _keysFile); } @@ -210,4 +265,6 @@ void KeyManager::write(h128 const& _key, std::string const& _keysFile) const writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out())); m_key = _key; + m_cachedPasswords[hashPassword(defaultPassword())] = defaultPassword(); + } diff --git a/libethereum/KeyManager.h b/libethcore/KeyManager.h similarity index 64% rename from libethereum/KeyManager.h rename to libethcore/KeyManager.h index 38e8d8853..155805c5d 100644 --- a/libethereum/KeyManager.h +++ b/libethcore/KeyManager.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include namespace dev { @@ -42,6 +42,12 @@ struct KeyInfo static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::string(); }; +enum class SemanticPassword +{ + Existing, + Master +}; + // TODO: This one is specifically for Ethereum, but we can make it generic in due course. // TODO: hidden-partition style key-store. /** @@ -55,7 +61,7 @@ static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::str class KeyManager { public: - KeyManager(std::string const& _keysFile = getDataDir("ethereum") + "/keys.info"); + KeyManager(std::string const& _keysFile = defaultPath(), std::string const& _secretsPath = SecretStore::defaultPath()); ~KeyManager(); void setKeysFile(std::string const& _keysFile) { m_keysFile = _keysFile; } @@ -67,30 +73,43 @@ public: void save(std::string const& _pass) const { write(_pass, m_keysFile); } void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; } + void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordInfo[hashPassword(_pass)] = _hint; } + bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); } AddressHash accounts() const; std::unordered_map> accountDetails() const; + std::string const& hint(Address const& _a) const { try { return m_passwordInfo.at(m_keyInfo.at(m_addrLookup.at(_a)).passHash); } catch (...) { return EmptyString; } } h128 uuid(Address const& _a) const; Address address(h128 const& _uuid) const; h128 import(Secret const& _s, std::string const& _info, std::string const& _pass, std::string const& _passInfo); - h128 import(Secret const& _s, std::string const& _info) { return import(_s, _info, m_password, std::string()); } + h128 import(Secret const& _s, std::string const& _info) { return import(_s, _info, defaultPassword(), std::string()); } SecretStore& store() { return m_store; } void importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo); + void importExisting(h128 const& _uuid, std::string const& _info) { importExisting(_uuid, _info, defaultPassword(), std::string()); } Secret secret(Address const& _address, std::function const& _pass = DontKnowThrow) const; Secret secret(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; + bool recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); + bool recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); + void kill(h128 const& _id) { kill(address(_id)); } void kill(Address const& _a); + static std::string defaultPath() { return getDataDir("ethereum") + "/keys.info"; } + private: + std::string getPassword(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; + std::string getPassword(h256 const& _passHash, std::function const& _pass = DontKnowThrow) const; + std::string defaultPassword(std::function const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); } h256 hashPassword(std::string const& _pass) const; // Only use if previously loaded ok. // @returns false if wasn't previously loaded ok. + bool write() const { return write(m_keysFile); } bool write(std::string const& _keysFile) const; void write(std::string const& _pass, std::string const& _keysFile) const; void write(h128 const& _key, std::string const& _keysFile) const; @@ -103,12 +122,17 @@ private: // Passwords that we're storing. mutable std::unordered_map m_cachedPasswords; - // The default password for keys in the keystore - protected by the master password. + // DEPRECATED. + // Used to be the default password for keys in the keystore, stored in the keys file. + // Now the default password is based off the key of the keys file directly, so this is redundant + // except for the fact that people have existing keys stored with it. Leave for now until/unless + // we have an upgrade strategy. std::string m_password; - SecretStore m_store; - mutable h128 m_key; mutable std::string m_keysFile; + mutable h128 m_key; + mutable h256 m_master; + SecretStore m_store; }; } diff --git a/libethcore/Miner.h b/libethcore/Miner.h index 3a68491ff..11b9ae140 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -44,7 +44,7 @@ struct MiningProgress // MiningProgress& operator+=(MiningProgress const& _mp) { hashes += _mp.hashes; ms = std::max(ms, _mp.ms); return *this; } uint64_t hashes = 0; ///< Total number of hashes computed. uint64_t ms = 0; ///< Total number of milliseconds of mining thus far. - uint64_t rate() const { return hashes * 1000 / ms; } + uint64_t rate() const { return ms == 0 ? 0 : hashes * 1000 / ms; } }; struct MineInfo: public MiningProgress {}; @@ -107,12 +107,10 @@ public: } if (!!_work) { - boost::timer t; - pause(); - cdebug << "pause took" << t.elapsed(); - t.restart(); - kickOff(); - cdebug << "kickOff took" << t.elapsed(); + DEV_TIMED_ABOVE(pause, 250) + pause(); + DEV_TIMED_ABOVE(kickOff, 250) + kickOff(); } else if (!_work && !!old) pause(); diff --git a/libethereum/Account.h b/libethereum/Account.h index 660dc0a4c..87fc82b6c 100644 --- a/libethereum/Account.h +++ b/libethereum/Account.h @@ -23,8 +23,8 @@ #include #include -#include -#include +#include +#include namespace dev { diff --git a/libethereum/AccountDiff.h b/libethereum/AccountDiff.h index 22107b958..dd494c0a5 100644 --- a/libethereum/AccountDiff.h +++ b/libethereum/AccountDiff.h @@ -62,7 +62,7 @@ struct AccountDiff Diff exist; ///< The account's existance; was it created/deleted or not? Diff balance; ///< The account's balance; did it alter? Diff nonce; ///< The account's nonce; did it alter? - std::unordered_map> storage; ///< The account's storage addresses; each has its own Diff. + std::map> storage; ///< The account's storage addresses; each has its own Diff. Diff code; ///< The account's code; in general this should only have changed if exist also changed. }; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 73747b25b..e23fde6b6 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include @@ -96,7 +96,7 @@ ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) #endif } -#if ETH_DEBUG +#if ETH_DEBUG&&0 static const chrono::system_clock::duration c_collectionDuration = chrono::seconds(15); static const unsigned c_collectionQueueSize = 2; static const unsigned c_maxCacheSize = 1024 * 1024 * 1; @@ -318,7 +318,9 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st try { // Nonce & uncle nonces already verified thread at this point. - auto r = import(block.first, block.second, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles); + ImportRoute r; + DEV_TIMED_ABOVE(Block import, 500) + r = import(block.first, block.second, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles); fresh += r.first; dead += r.second; } diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index c865bac32..d0437739d 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -94,7 +94,7 @@ public: void retryAllUnknown(); /// Get information on the items queued. - std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_verified.size(), m_unknown.size()); } + std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_readySet.size(), m_unknownSet.size()); } /// Clear everything. void clear() { WriteGuard l(m_lock); DEV_INVARIANT_CHECK; Guard l2(m_verification); m_readySet.clear(); m_drainingSet.clear(); m_verified.clear(); m_unverified.clear(); m_unknownSet.clear(); m_unknown.clear(); m_future.clear(); } diff --git a/libethereum/CachedAddressState.cpp b/libethereum/CachedAddressState.cpp index a25017793..757aef466 100644 --- a/libethereum/CachedAddressState.cpp +++ b/libethereum/CachedAddressState.cpp @@ -21,8 +21,9 @@ #include "CachedAddressState.h" +#include #include -#include +#include #include "Account.h" using namespace std; using namespace dev; @@ -57,8 +58,8 @@ std::unordered_map CachedAddressState::storage() const if (m_r) { SecureTrieDB memdb(const_cast(m_o), m_r[2].toHash()); // promise we won't alter the overlay! :) -// for (auto const& j: memdb) -// ret[j.first] = RLP(j.second).toInt(); + for (auto const& j: memdb) + ret[j.first] = RLP(j.second).toInt(); } if (m_s) for (auto const& j: m_s->storageOverlay()) diff --git a/libethereum/CanonBlockChain.cpp b/libethereum/CanonBlockChain.cpp index f1de7292b..5dd7dc2ce 100644 --- a/libethereum/CanonBlockChain.cpp +++ b/libethereum/CanonBlockChain.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 97118a7a9..e372e611a 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -172,6 +172,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for m_preMine(m_stateDB, BaseState::CanonGenesis), m_postMine(m_stateDB) { + m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30); m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); @@ -197,6 +198,7 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string c m_preMine(m_stateDB), m_postMine(m_stateDB) { + m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30); m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); @@ -453,8 +455,15 @@ ProofOfWork::WorkPackage Client::getWork() { // lock the work so a later submission isn't invalidated by processing a transaction elsewhere. // this will be reset as soon as a new block arrives, allowing more transactions to be processed. + bool oldShould = shouldServeWork(); m_lastGetWork = chrono::system_clock::now(); - m_remoteWorking = true; + + // if this request has made us bother to serve work, prep it now. + if (!oldShould && shouldServeWork()) + onPostStateChanged(); + else + // otherwise, set this to true so that it gets prepped next time. + m_remoteWorking = true; return ProofOfWork::package(m_miningInfo); } @@ -484,7 +493,7 @@ void Client::syncBlockQueue() cwork << "BQ ==> CHAIN ==> STATE"; { - tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, 10); + tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 90 + 10); if (ir.first.empty()) return; } @@ -607,7 +616,8 @@ bool Client::remoteActive() const void Client::onPostStateChanged() { cnote << "Post state changed"; - if (isMining() || remoteActive()) + + if (m_bq.items().first == 0 && (isMining() || remoteActive())) { cnote << "Restarting mining..."; DEV_WRITE_GUARDED(x_working) @@ -619,6 +629,8 @@ void Client::onPostStateChanged() m_miningInfo = m_postMine.info(); } m_farm.setWork(m_miningInfo); + + Ethash::ensurePrecomputed(m_bc.number()); } m_remoteWorking = false; } @@ -636,7 +648,7 @@ void Client::noteChanged(h256Hash const& _filters) { Guard l(x_filtersWatches); if (_filters.size()) - filtersStreamOut(cnote << "noteChanged:", _filters); + filtersStreamOut(cwatch << "noteChanged:", _filters); // accrue all changes left in each filter into the watches. for (auto& w: m_watches) if (_filters.count(w.second.id)) diff --git a/libethereum/Client.h b/libethereum/Client.h index 7eeb7fb2d..c77cb6034 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -274,6 +274,9 @@ private: /// Ticks various system-level objects. void tick(); + /// @returns true only if it's worth bothering to prep the mining block. + bool shouldServeWork() const { return m_bq.items().first == 0 && (isMining() || remoteActive()); } + VersionChecker m_vc; ///< Dummy object to check & update the protocol version. CanonBlockChain m_bc; ///< Maintains block database. BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). @@ -289,7 +292,7 @@ private: BlockInfo m_miningInfo; ///< The header we're attempting to mine on (derived from m_postMine). bool remoteActive() const; ///< Is there an active and valid remote worker? bool m_remoteWorking = false; ///< Has the remote worker recently been reset? - std::chrono::system_clock::time_point m_lastGetWork = std::chrono::system_clock::time_point::min(); ///< Is there an active and valid remote worker? + std::chrono::system_clock::time_point m_lastGetWork; ///< Is there an active and valid remote worker? std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. diff --git a/libethereum/Defaults.cpp b/libethereum/Defaults.cpp index febe53d84..b839bbb5c 100644 --- a/libethereum/Defaults.cpp +++ b/libethereum/Defaults.cpp @@ -21,7 +21,7 @@ #include "Defaults.h" -#include +#include using namespace std; using namespace dev; using namespace dev::eth; diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index a1eee9440..4fbf51244 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -159,6 +159,42 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen return !m_ext; } +bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin) +{ + m_isCreation = false; +// cnote << "Transferring" << formatBalance(_value) << "to receiver."; + auto it = !(_p.codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_p.codeAddress) : precompiled().end(); + if (it != precompiled().end()) + { + bigint g = it->second.gas(_p.data); + if (_p.gas < g) + { + m_endGas = 0; + m_excepted = TransactionException::OutOfGasBase; + // Bail from exception. + return true; // true actually means "all finished - nothing more to be done regarding go(). + } + else + { + m_endGas = (u256)(_p.gas - g); + m_precompiledOut = it->second.exec(_p.data); + m_out = &m_precompiledOut; + } + } + else if (m_s.addressHasCode(_p.codeAddress)) + { + m_vm = VMFactory::create(_p.gas); + bytes const& c = m_s.code(_p.codeAddress); + m_ext = make_shared(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth); + } + else + m_endGas = _p.gas; + + m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.value); + + return !m_ext; +} + bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin) { m_isCreation = true; diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 3806221be..8bb0ab771 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -95,6 +95,7 @@ public: /// Set up the executive for evaluating a bare CALL (message call) operation. /// @returns false iff go() must be called (and thus a VM execution in required). bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress); + bool call(CallParameters const& _cp, u256 const& _gasPrice, Address const& _origin); /// Finalise an operation through accruing the substate into the parent context. void accrueSubState(SubState& _parentContext); diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 68d146ce1..c0591cef5 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -26,16 +26,16 @@ using namespace std; using namespace dev; using namespace dev::eth; -bool ExtVM::call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp, Address _myAddressOverride, Address _codeAddressOverride) +bool ExtVM::call(CallParameters& _p) { Executive e(m_s, lastHashes, depth + 1); - if (!e.call(_receiveAddress, _codeAddressOverride, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, io_gas, origin)) + if (!e.call(_p, gasPrice, origin)) { - e.go(_onOp); + e.go(_p.onOp); e.accrueSubState(sub); } - io_gas = e.endGas(); - e.out().copyTo(_out); + _p.gas = e.endGas(); + e.out().copyTo(_p.out); return !e.excepted(); } diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index 1a2d180dd..babff4edf 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -58,7 +58,7 @@ public: virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = {}) override final; /// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller. - virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final; + virtual bool call(CallParameters& _params) override final; /// Read address's balance. virtual u256 balance(Address _a) override final { return m_s.balance(_a); } diff --git a/libethereum/LogFilter.cpp b/libethereum/LogFilter.cpp index ab9848d59..21ba9d3ef 100644 --- a/libethereum/LogFilter.cpp +++ b/libethereum/LogFilter.cpp @@ -21,7 +21,7 @@ #include "LogFilter.h" -#include +#include #include "State.h" using namespace std; using namespace dev; diff --git a/libethereum/Precompiled.cpp b/libethereum/Precompiled.cpp index cdcb4a46a..0e80949fe 100644 --- a/libethereum/Precompiled.cpp +++ b/libethereum/Precompiled.cpp @@ -21,7 +21,9 @@ #include "Precompiled.h" -#include +#include +#include +#include #include #include #include @@ -61,19 +63,12 @@ static bytes ecrecoverCode(bytesConstRef _in) static bytes sha256Code(bytesConstRef _in) { - bytes ret(32); - sha256(_in, &ret); - return ret; + return sha256(_in).asBytes(); } static bytes ripemd160Code(bytesConstRef _in) { - bytes ret(32); - ripemd160(_in, &ret); - // leaves the 20-byte hash left-aligned. we want it right-aligned: - memmove(ret.data() + 12, ret.data(), 20); - memset(ret.data(), 0, 12); - return ret; + return h256(ripemd160(_in), h256::AlignRight).asBytes(); } static bytes identityCode(bytesConstRef _in) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 3bf7aaff7..c753f57ea 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -840,16 +840,8 @@ void State::commitToMine(BlockChain const& _bc) } } - // TODO: move over to using TrieHash - - - MemoryDB tm; - GenericTrieDB transactionsTrie(&tm); - transactionsTrie.init(); - - MemoryDB rm; - GenericTrieDB receiptsTrie(&rm); - receiptsTrie.init(); + BytesMap transactionsMap; + BytesMap receiptsMap; RLPStream txs; txs.appendList(m_transactions.size()); @@ -861,11 +853,11 @@ void State::commitToMine(BlockChain const& _bc) RLPStream receiptrlp; m_receipts[i].streamRLP(receiptrlp); - receiptsTrie.insert(&k.out(), &receiptrlp.out()); + receiptsMap.insert(std::make_pair(k.out(), receiptrlp.out())); RLPStream txrlp; m_transactions[i].streamRLP(txrlp); - transactionsTrie.insert(&k.out(), &txrlp.out()); + transactionsMap.insert(std::make_pair(k.out(), txrlp.out())); txs.appendRaw(txrlp.out()); } @@ -874,8 +866,8 @@ void State::commitToMine(BlockChain const& _bc) RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles); - m_currentBlock.transactionsRoot = transactionsTrie.root(); - m_currentBlock.receiptsRoot = receiptsTrie.root(); + m_currentBlock.transactionsRoot = hash256(transactionsMap); + m_currentBlock.receiptsRoot = hash256(receiptsMap); m_currentBlock.logBloom = logBloom(); m_currentBlock.sha3Uncles = sha3(m_currentUncles); diff --git a/libethereum/State.h b/libethereum/State.h index d9fc9d2b5..f46d0e222 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -25,7 +25,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 78852b7b8..09d6cd54c 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -22,7 +22,7 @@ #pragma once #include -#include +#include #include #include namespace dev diff --git a/libethereum/Utility.cpp b/libethereum/Utility.cpp index 7b0a961b2..adfea4a51 100644 --- a/libethereum/Utility.cpp +++ b/libethereum/Utility.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include using namespace std; using namespace dev; using namespace dev::eth; diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 48fbd0b01..ce259bef2 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -108,6 +108,18 @@ using LastHashes = std::vector; using OnOpFunc = std::function; +struct CallParameters +{ + Address senderAddress; + Address codeAddress; + Address receiveAddress; + u256 gas; + u256 value; + bytesConstRef data; + bytesRef out; + OnOpFunc onOp; +}; + /** * @brief Interface and null implementation of the class for specifying VM externalities. */ @@ -153,7 +165,7 @@ public: virtual h160 create(u256, u256&, bytesConstRef, OnOpFunc const&) { return h160(); } /// Make a new message call. - virtual bool call(Address, u256, bytesConstRef, u256&, bytesRef, OnOpFunc const&, Address, Address) { return false; } + virtual bool call(CallParameters&) { return false; } /// Revert any changes made (by any of the other calls). virtual void log(h256s&& _topics, bytesConstRef _data) { sub.logs.push_back(LogEntry(myAddress, std::move(_topics), _data.toBytes())); } diff --git a/libevm/SmartVM.cpp b/libevm/SmartVM.cpp index 6a2ff43c6..2d68fd84b 100644 --- a/libevm/SmartVM.cpp +++ b/libevm/SmartVM.cpp @@ -21,7 +21,7 @@ #include "SmartVM.h" #include #include -#include +#include #include #include #include "VMFactory.h" diff --git a/libevm/SmartVM.h b/libevm/SmartVM.h index 29f464ecd..e21af23b0 100644 --- a/libevm/SmartVM.h +++ b/libevm/SmartVM.h @@ -31,12 +31,16 @@ namespace eth class SmartVM: public VMFace { public: - SmartVM(u256 _gas): VMFace(_gas) {} + SmartVM(u256 const& _gas): m_gas(_gas) {} virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + void reset(u256 const& _gas = 0) noexcept override { m_gas = _gas; } + u256 gas() const noexcept override { return (u256)m_gas; } + private: std::unique_ptr m_selectedVM; + bigint m_gas; }; } diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 853ac25f6..ff771d21c 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -25,9 +25,9 @@ using namespace std; using namespace dev; using namespace dev::eth; -void VM::reset(u256 _gas) noexcept +void VM::reset(u256 const& _gas) noexcept { - VMFace::reset(_gas); + m_gas = _gas; m_curPC = 0; m_jumpDests.clear(); } @@ -56,6 +56,8 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) { m_stack.reserve((unsigned)c_stackLimit); + unique_ptr callParams; + static const array c_metrics = metrics(); auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; @@ -206,7 +208,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) BOOST_THROW_EXCEPTION(OutOfGas()); } - m_gas = (u256)((bigint)m_gas - runGas); + m_gas -= runGas; if (newTempSize > m_temp.size()) m_temp.resize((size_t)newTempSize); @@ -565,7 +567,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) m_stack.push_back(m_temp.size()); break; case Instruction::GAS: - m_stack.push_back(m_gas); + m_stack.push_back((u256)m_gas); break; case Instruction::JUMPDEST: break; @@ -614,7 +616,11 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) m_stack.pop_back(); if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024) - m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); + { + u256 g(m_gas); + m_stack.push_back((u160)_ext.create(endowment, g, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); + m_gas = g; + } else m_stack.push_back(0); break; @@ -622,13 +628,16 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) case Instruction::CALL: case Instruction::CALLCODE: { - u256 gas = m_stack.back(); + if (!callParams) + callParams.reset(new CallParameters); + + callParams->gas = m_stack.back(); if (m_stack[m_stack.size() - 3] > 0) - gas += c_callStipend; + callParams->gas += c_callStipend; m_stack.pop_back(); - Address receiveAddress = asAddress(m_stack.back()); + callParams->codeAddress = asAddress(m_stack.back()); m_stack.pop_back(); - u256 value = m_stack.back(); + callParams->value = m_stack.back(); m_stack.pop_back(); unsigned inOff = (unsigned)m_stack.back(); @@ -640,12 +649,19 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) unsigned outSize = (unsigned)m_stack.back(); m_stack.pop_back(); - if (_ext.balance(_ext.myAddress) >= value && _ext.depth < 1024) - m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress)); + if (_ext.balance(_ext.myAddress) >= callParams->value && _ext.depth < 1024) + { + callParams->onOp = _onOp; + callParams->senderAddress = _ext.myAddress; + callParams->receiveAddress = inst == Instruction::CALL ? callParams->codeAddress : callParams->senderAddress; + callParams->data = bytesConstRef(m_temp.data() + inOff, inSize); + callParams->out = bytesRef(m_temp.data() + outOff, outSize); + m_stack.push_back(_ext.call(*callParams)); + } else m_stack.push_back(0); - m_gas += gas; + m_gas += callParams->gas; break; } case Instruction::RETURN: diff --git a/libevm/VM.h b/libevm/VM.h index 30007e0b3..181fcdc2e 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include "VMFace.h" @@ -52,8 +52,6 @@ inline u256 fromAddress(Address _a) class VM: public VMFace { public: - virtual void reset(u256 _gas = 0) noexcept override final; - virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } } @@ -64,17 +62,21 @@ public: bytes const& memory() const { return m_temp; } u256s const& stack() const { return m_stack; } + void reset(u256 const& _gas = 0) noexcept override; + u256 gas() const noexcept override { return (u256)m_gas; } + private: friend class VMFactory; /// Construct VM object. - explicit VM(u256 _gas): VMFace(_gas) {} + explicit VM(u256 _gas): m_gas(_gas) {} u256 m_curPC = 0; bytes m_temp; u256s m_stack; std::set m_jumpDests; std::function m_onFail; + bigint m_gas = 0; }; } diff --git a/libevm/VMFace.h b/libevm/VMFace.h index d2689d13e..92e6d4c4f 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -38,18 +38,15 @@ struct StackUnderflow: virtual VMException {}; class VMFace { public: - explicit VMFace(u256 _gas): m_gas(_gas) {} + VMFace() = default; virtual ~VMFace() = default; VMFace(VMFace const&) = delete; VMFace& operator=(VMFace const&) = delete; - virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; } - u256 gas() const noexcept { return m_gas; } + virtual void reset(u256 const& _gas = 0) noexcept = 0; + virtual u256 gas() const noexcept = 0; virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; - -protected: - u256 m_gas = 0; }; } diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 6f38b0f42..5cf3b787a 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -431,6 +431,7 @@ bytes Assembly::assemble() const case PushSubSize: { auto s = m_data[i.data()].size(); + i.setPushedValue(u256(s)); byte b = max(1, dev::bytesRequired(s)); ret.push_back((byte)Instruction::PUSH1 - 1 + b); ret.resize(ret.size() + b); diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index b3012a7ea..7d8f3d9a4 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -84,11 +84,17 @@ public: JumpType getJumpType() const { return m_jumpType; } std::string getJumpTypeAsString() const; + void setPushedValue(u256 const& _value) const { m_pushedValue = std::make_shared(_value); } + u256 const* pushedValue() const { return m_pushedValue.get(); } + private: AssemblyItemType m_type; u256 m_data; SourceLocation m_location; JumpType m_jumpType = JumpType::Ordinary; + /// Pushed value for operations with data to be determined during assembly stage, + /// e.g. PushSubSize, PushTag, PushSub, etc. + mutable std::shared_ptr m_pushedValue; }; using AssemblyItems = std::vector; diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 9f6f9dd63..b2fa73116 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index e5fb0e09a..a8dc4dd58 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -20,6 +20,7 @@ */ #include "GasMeter.h" +#include #include using namespace std; @@ -41,55 +42,162 @@ GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption co GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item) { - switch (_item.type()) { + GasConsumption gas; + switch (_item.type()) + { case Push: case PushTag: - return runGas(Instruction::PUSH1); + case PushData: + case PushString: + case PushSub: + case PushSubSize: + case PushProgramSize: + gas = runGas(Instruction::PUSH1); + break; case Tag: - return runGas(Instruction::JUMPDEST); + gas = runGas(Instruction::JUMPDEST); + break; case Operation: { - GasConsumption gas = runGas(_item.instruction()); + ExpressionClasses& classes = m_state->expressionClasses(); + gas = runGas(_item.instruction()); switch (_item.instruction()) { case Instruction::SSTORE: - // @todo logic can be improved - gas += c_sstoreSetGas; + { + ExpressionClasses::Id slot = m_state->relativeStackElement(0); + ExpressionClasses::Id value = m_state->relativeStackElement(-1); + if (classes.knownZero(value) || ( + m_state->storageContent().count(slot) && + classes.knownNonZero(m_state->storageContent().at(slot)) + )) + gas += c_sstoreResetGas; //@todo take refunds into account + else + gas += c_sstoreSetGas; break; + } case Instruction::SLOAD: gas += c_sloadGas; break; + case Instruction::RETURN: + gas += memoryGas(0, -1); + break; + case Instruction::MLOAD: case Instruction::MSTORE: + gas += memoryGas(classes.find(eth::Instruction::ADD, { + m_state->relativeStackElement(0), + classes.find(AssemblyItem(32)) + })); + break; case Instruction::MSTORE8: - case Instruction::MLOAD: - case Instruction::RETURN: + gas += memoryGas(classes.find(eth::Instruction::ADD, { + m_state->relativeStackElement(0), + classes.find(AssemblyItem(1)) + })); + break; case Instruction::SHA3: + gas = c_sha3Gas; + gas += wordGas(c_sha3WordGas, m_state->relativeStackElement(-1)); + gas += memoryGas(0, -1); + break; case Instruction::CALLDATACOPY: case Instruction::CODECOPY: + gas += memoryGas(0, -2); + gas += wordGas(c_copyGas, m_state->relativeStackElement(-2)); + break; case Instruction::EXTCODECOPY: + gas += memoryGas(-1, -3); + gas += wordGas(c_copyGas, m_state->relativeStackElement(-3)); + break; case Instruction::LOG0: case Instruction::LOG1: case Instruction::LOG2: case Instruction::LOG3: case Instruction::LOG4: + { + unsigned n = unsigned(_item.instruction()) - unsigned(Instruction::LOG0); + gas = c_logGas + c_logTopicGas * n; + gas += memoryGas(0, -1); + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) + gas += c_logDataGas * (*value); + else + gas = GasConsumption::infinite(); + break; + } case Instruction::CALL: case Instruction::CALLCODE: + gas = c_callGas; + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0))) + gas += (*value); + else + gas = GasConsumption::infinite(); + if (_item.instruction() != Instruction::CALLCODE) + gas += c_callNewAccountGas; // We very rarely know whether the address exists. + if (!classes.knownZero(m_state->relativeStackElement(-2))) + gas += c_callValueTransferGas; + gas += memoryGas(-3, -4); + gas += memoryGas(-5, -6); + break; case Instruction::CREATE: + gas = c_createGas; + gas += memoryGas(-1, -2); + break; case Instruction::EXP: - // @todo logic can be improved - gas = GasConsumption::infinite(); + gas = c_expGas; + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) + gas += c_expByteGas * (32 - (h256(*value).firstBitSet() / 8)); + else + gas = GasConsumption::infinite(); break; default: break; } - return gas; break; } default: + gas = GasConsumption::infinite(); break; } - return GasConsumption::infinite(); + m_state->feedItem(_item); + return gas; +} + +GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionClasses::Id _position) +{ + u256 const* value = m_state->expressionClasses().knownConstant(_position); + if (!value) + return GasConsumption::infinite(); + return GasConsumption(_multiplier * ((*value + 31) / 32)); +} + +GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _position) +{ + u256 const* value = m_state->expressionClasses().knownConstant(_position); + if (!value) + return GasConsumption::infinite(); + if (*value < m_largestMemoryAccess) + return GasConsumption(u256(0)); + u256 previous = m_largestMemoryAccess; + m_largestMemoryAccess = *value; + auto memGas = [](u256 const& pos) -> u256 + { + u256 size = (pos + 31) / 32; + return c_memoryGas * size + size * size / c_quadCoeffDiv; + }; + return memGas(*value) - memGas(previous); +} + +GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosSize) +{ + ExpressionClasses& classes = m_state->expressionClasses(); + if (classes.knownZero(m_state->relativeStackElement(_stackPosSize))) + return GasConsumption(0); + else + return memoryGas(classes.find(eth::Instruction::ADD, { + m_state->relativeStackElement(_stackPosOffset), + m_state->relativeStackElement(_stackPosSize) + })); } GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction) diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 63dbc1380..ab6d5613b 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include namespace dev @@ -29,8 +30,13 @@ namespace dev namespace eth { +class KnownState; + /** * Class that helps computing the maximum gas consumption for instructions. + * Has to be initialized with a certain known state that will be automatically updated for + * each call to estimateMax. These calls have to supply strictly subsequent AssemblyItems. + * A new gas meter has to be constructed (with a new state) for control flow changes. */ class GasMeter { @@ -47,11 +53,28 @@ public: bool isInfinite; }; - /// Returns an upper bound on the gas consumed by the given instruction. + /// Constructs a new gas meter given the current state. + GasMeter(std::shared_ptr const& _state): m_state(_state) {} + + /// @returns an upper bound on the gas consumed by the given instruction and updates + /// the state. GasConsumption estimateMax(AssemblyItem const& _item); private: + /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. + GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value); + /// @returns the gas needed to access the given memory position. + /// @todo this assumes that memory was never accessed before and thus over-estimates gas usage. + GasConsumption memoryGas(ExpressionClasses::Id _position); + /// @returns the memory gas for accessing the memory at a specific offset for a number of bytes + /// given as values on the stack at the given relative positions. + GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize); + static GasConsumption runGas(Instruction _instruction); + + std::shared_ptr m_state; + /// Largest point where memory was accessed since the creation of this object. + u256 m_largestMemoryAccess; }; inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption const& _consumption) @@ -59,7 +82,7 @@ inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption con if (_consumption.isInfinite) return _str << "inf"; else - return _str << _consumption.value; + return _str << std::dec << _consumption.value; } diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 0aac9cedb..d62dbf17e 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -23,7 +23,7 @@ #include "KnownState.h" #include -#include +#include #include using namespace std; @@ -92,7 +92,11 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool else if (_item.type() != Operation) { assertThrow(_item.deposit() == 1, InvalidDeposit, ""); - setStackElement(++m_stackHeight, m_expressionClasses->find(_item, {}, _copyItem)); + if (_item.pushedValue()) + // only available after assembly stage, should not be used for optimisation + setStackElement(++m_stackHeight, m_expressionClasses->find(*_item.pushedValue())); + else + setStackElement(++m_stackHeight, m_expressionClasses->find(_item, {}, _copyItem)); } else { @@ -230,7 +234,12 @@ ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation return m_stackElements.at(_stackHeight); // Stack element not found (not assigned yet), create new unknown equivalence class. return m_stackElements[_stackHeight] = - m_expressionClasses->find(AssemblyItem(UndefinedItem, _stackHeight, _location)); + m_expressionClasses->find(AssemblyItem(UndefinedItem, _stackHeight, _location)); +} + +KnownState::Id KnownState::relativeStackElement(int _stackOffset, SourceLocation const& _location) +{ + return stackElement(m_stackHeight + _stackOffset, _location); } void KnownState::clearTagUnions() diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h index 3505df74f..9d28ef21a 100644 --- a/libevmasm/KnownState.h +++ b/libevmasm/KnownState.h @@ -111,6 +111,8 @@ public: /// Retrieves the current equivalence class fo the given stack element (or generates a new /// one if it does not exist yet). Id stackElement(int _stackHeight, SourceLocation const& _location); + /// @returns the stackElement relative to the current stack height. + Id relativeStackElement(int _stackOffset, SourceLocation const& _location = SourceLocation()); /// @returns its set of tags if the given expression class is a known tag union; returns a set /// containing the tag if it is a PushTag expression and the empty set otherwise. @@ -123,6 +125,8 @@ public: std::map const& stackElements() const { return m_stackElements; } ExpressionClasses& expressionClasses() const { return *m_expressionClasses; } + std::map const& storageContent() const { return m_storageContent; } + private: /// Assigns a new equivalence class to the next sequence number of the given stack element. void setStackElement(int _stackHeight, Id _class); diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index e2599c1d5..998579a90 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "Session.h" #include "Common.h" #include "Capability.h" diff --git a/libp2p/UDP.h b/libp2p/UDP.h index b09d556e7..a455b5057 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include "Common.h" diff --git a/libscrypt/CMakeLists.txt b/libscrypt/CMakeLists.txt new file mode 100644 index 000000000..8543244c5 --- /dev/null +++ b/libscrypt/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_policy(SET CMP0015 NEW) +# this policy was introduced in cmake 3.0 +# remove if, once 3.0 will be used on unix +if (${CMAKE_MAJOR_VERSION} GREATER 2) + # old policy do not use MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) +endif() +set(CMAKE_AUTOMOC OFF) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ..) + +set(EXECUTABLE scrypt) + +file(GLOB HEADERS "*.h") + +add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + +install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libscrypt/LICENSE b/libscrypt/LICENSE new file mode 100644 index 000000000..46a743175 --- /dev/null +++ b/libscrypt/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2013, Joshua Small + All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/libscrypt/b64.c b/libscrypt/b64.c new file mode 100644 index 000000000..b797dd0d9 --- /dev/null +++ b/libscrypt/b64.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Base64 encode/decode functions from OpenBSD (src/lib/libc/net/base64.c). + */ +#include +#include +#include +#include +#include + +#include "b64.h" + + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. +*/ + +int +libscrypt_b64_encode(src, srclength, target, targsize) + unsigned char const *src; + size_t srclength; + char *target; + size_t targsize; +{ + size_t datalength = 0; + unsigned char input[3]; + unsigned char output[4]; + unsigned int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (int)(datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +libscrypt_b64_decode(src, target, targsize) + char const *src; + unsigned char *target; + size_t targsize; +{ + int state, ch; + unsigned int tarindex; + unsigned char nextbyte; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = (unsigned char)*src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + nextbyte = ((pos - Base64) & 0x0f) << 4; + if (tarindex + 1 < targsize) + target[tarindex+1] = nextbyte; + else if (nextbyte) + return (-1); + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + nextbyte = ((pos - Base64) & 0x03) << 6; + if (tarindex + 1 < targsize) + target[tarindex+1] = nextbyte; + else if (nextbyte) + return (-1); + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = (unsigned char)*src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = (unsigned char)*src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = (unsigned char)*src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = (unsigned char)*src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && tarindex < targsize && + target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/libscrypt/b64.h b/libscrypt/b64.h new file mode 100644 index 000000000..2e271eb5b --- /dev/null +++ b/libscrypt/b64.h @@ -0,0 +1,10 @@ + +/* BASE64 libraries used internally - should not need to be packaged */ + +#define b64_encode_len(A) ((A+2)/3 * 4 + 1) +#define b64_decode_len(A) (A / 4 * 3 + 2) + +int libscrypt_b64_encode(unsigned char const *src, size_t srclength, + /*@out@*/ char *target, size_t targetsize); +int libscrypt_b64_decode(char const *src, /*@out@*/ unsigned char *target, + size_t targetsize); diff --git a/libscrypt/crypto-mcf.c b/libscrypt/crypto-mcf.c new file mode 100644 index 000000000..9f7ddc376 --- /dev/null +++ b/libscrypt/crypto-mcf.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifndef S_SPLINT_S /* Including this here triggers a known bug in splint */ +//#include +#endif + +#include "libscrypt.h" + +/* ilog2 for powers of two */ +static uint32_t scrypt_ilog2(uint32_t n) +{ +#ifndef S_SPLINT_S + + /* Check for a valid power of two */ + if (n < 2 || (n & (n - 1))) + return -1; +#endif + uint32_t t = 1; + while (((uint32_t)1 << t) < n) + { + if(t > SCRYPT_SAFE_N) + return (uint32_t) -1; /* Check for insanity */ + t++; + } + + return t; +} + +#ifdef _MSC_VER + #define SNPRINTF _snprintf +#else + #define SNPRINTF snprintf +#endif + +int libscrypt_mcf(uint32_t N, uint32_t r, uint32_t p, const char *salt, + const char *hash, char *mcf) +{ + + uint32_t t, params; + int s; + + if(!mcf || !hash) + return 0; + /* Although larger values of r, p are valid in scrypt, this mcf format + * limits to 8 bits. If your number is larger, current computers will + * struggle + */ + if(r > (uint8_t)(-1) || p > (uint8_t)(-1)) + return 0; + + t = scrypt_ilog2(N); + if (t < 1) + return 0; + + params = (r << 8) + p; + params += (uint32_t)t << 16; + + /* Using snprintf - not checking for overflows. We've already + * determined that mcf should be defined as at least SCRYPT_MCF_LEN + * in length + */ + s = SNPRINTF(mcf, SCRYPT_MCF_LEN, SCRYPT_MCF_ID "$%06x$%s$%s", (unsigned int)params, salt, hash); + if (s > SCRYPT_MCF_LEN) + return 0; + + return 1; +} diff --git a/libscrypt/crypto-scrypt-saltgen.c b/libscrypt/crypto-scrypt-saltgen.c new file mode 100644 index 000000000..e69de29bb diff --git a/libscrypt/crypto_scrypt-check.c b/libscrypt/crypto_scrypt-check.c new file mode 100644 index 000000000..99477ffe9 --- /dev/null +++ b/libscrypt/crypto_scrypt-check.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#include "b64.h" +#include "slowequals.h" +#include "libscrypt.h" + +#ifdef _WIN32 +/* On windows, strtok uses a thread-local static variable in strtok to + * make strtok thread-safe. It also neglects to provide a strtok_r. */ +#define strtok_r(str, val, saveptr) strtok((str), (val)) +#endif + +int libscrypt_check(char *mcf, const char *password) +{ + /* Return values: + * <0 error + * == 0 password incorrect + * >0 correct password + */ + +#ifndef _WIN32 + char *saveptr = NULL; +#endif + uint32_t params; + uint64_t N; + uint8_t r, p; + int retval; + uint8_t hashbuf[64]; + char outbuf[128]; + uint8_t salt[32]; + char *tok; + + if(memcmp(mcf, SCRYPT_MCF_ID, 3) != 0) + { + /* Only version 0 supported */ + return -1; + } + + tok = strtok_r(mcf, "$", &saveptr); + if ( !tok ) + return -1; + + tok = strtok_r(NULL, "$", &saveptr); + + if ( !tok ) + return -1; + + params = (uint32_t)strtoul(tok, NULL, 16); + if ( params == 0 ) + return -1; + + tok = strtok_r(NULL, "$", &saveptr); + + if ( !tok ) + return -1; + + p = params & 0xff; + r = (params >> 8) & 0xff; + N = params >> 16; + + if (N > SCRYPT_SAFE_N) + return -1; + + N = (uint64_t)1 << N; + + /* Useful debugging: + printf("We've obtained salt 'N' r p of '%s' %d %d %d\n", tok, N,r,p); + */ + + memset(salt, 0, sizeof(salt)); /* Keeps splint happy */ + retval = libscrypt_b64_decode(tok, (unsigned char*)salt, sizeof(salt)); + if (retval < 1) + return -1; + + retval = libscrypt_scrypt((uint8_t*)password, strlen(password), salt, + (uint32_t)retval, N, r, p, hashbuf, sizeof(hashbuf)); + + if (retval != 0) + return -1; + + retval = libscrypt_b64_encode((unsigned char*)hashbuf, sizeof(hashbuf), + outbuf, sizeof(outbuf)); + + if (retval == 0) + return -1; + + tok = strtok_r(NULL, "$", &saveptr); + + if ( !tok ) + return -1; + + if(slow_equals(tok, outbuf) == 0) + return 0; + + return 1; /* This is the "else" condition */ +} + diff --git a/libscrypt/crypto_scrypt-hash.c b/libscrypt/crypto_scrypt-hash.c new file mode 100644 index 000000000..e69de29bb diff --git a/libscrypt/crypto_scrypt-hexconvert.c b/libscrypt/crypto_scrypt-hexconvert.c new file mode 100644 index 000000000..3df12a023 --- /dev/null +++ b/libscrypt/crypto_scrypt-hexconvert.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +/* The hexconvert function is only used to test reference vectors against + * known answers. The contents of this file are therefore a component + * to assist with test harnesses only + */ + +int libscrypt_hexconvert(uint8_t *buf, size_t s, char *outbuf, size_t obs) +{ + + size_t i; + int len = 0; + + if (!buf || s < 1 || obs < (s * 2 + 1)) + return 0; + + memset(outbuf, 0, obs); + + + for(i=0; i<=(s-1); i++) + { + /* snprintf(outbuf, s,"%s...", outbuf....) has undefined results + * and can't be used. Using offests like this makes snprintf + * nontrivial. we therefore have use inescure sprintf() and + * lengths checked elsewhere (start of function) */ + /*@ -bufferoverflowhigh @*/ + len += sprintf(outbuf+len, "%02x", (unsigned int) buf[i]); + } + + return 1; +} + diff --git a/libscrypt/crypto_scrypt-hexconvert.h b/libscrypt/crypto_scrypt-hexconvert.h new file mode 100644 index 000000000..8175b24f1 --- /dev/null +++ b/libscrypt/crypto_scrypt-hexconvert.h @@ -0,0 +1,9 @@ + +#include + +/** + * Converts a binary string to a hex representation of that string + * outbuf must have size of at least buf * 2 + 1. + */ +int libscrypt_hexconvert(const uint8_t *buf, size_t s, char *outbuf, + size_t obs); diff --git a/libscrypt/crypto_scrypt-nosse.c b/libscrypt/crypto_scrypt-nosse.c new file mode 100644 index 000000000..12c860f2d --- /dev/null +++ b/libscrypt/crypto_scrypt-nosse.c @@ -0,0 +1,342 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include +#include + +#include "sha256.h" +#include "sysendian.h" + +#include "libscrypt.h" + +static void blkcpy(void *, void *, size_t); +static void blkxor(void *, void *, size_t); +static void salsa20_8(uint32_t[16]); +static void blockmix_salsa8(uint32_t *, uint32_t *, uint32_t *, size_t); +static uint64_t integerify(void *, size_t); +static void smix(uint8_t *, size_t, uint64_t, uint32_t *, uint32_t *); + +static void +blkcpy(void * dest, void * src, size_t len) +{ + size_t * D = dest; + size_t * S = src; + size_t L = len / sizeof(size_t); + size_t i; + + for (i = 0; i < L; i++) + D[i] = S[i]; +} + +static void +blkxor(void * dest, void * src, size_t len) +{ + size_t * D = dest; + size_t * S = src; + size_t L = len / sizeof(size_t); + size_t i; + + for (i = 0; i < L; i++) + D[i] ^= S[i]; +} + +/** + * salsa20_8(B): + * Apply the salsa20/8 core to the provided block. + */ +static void +salsa20_8(uint32_t B[16]) +{ + uint32_t x[16]; + size_t i; + + blkcpy(x, B, 64); + for (i = 0; i < 8; i += 2) { +#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) + /* Operate on columns. */ + x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); + x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); + + x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); + x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); + + x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); + x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); + + x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); + x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); + + /* Operate on rows. */ + x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); + x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); + + x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); + x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); + + x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); + x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); + + x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); + x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); +#undef R + } + for (i = 0; i < 16; i++) + B[i] += x[i]; +} + +/** + * blockmix_salsa8(Bin, Bout, X, r): + * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r + * bytes in length; the output Bout must also be the same size. The + * temporary space X must be 64 bytes. + */ +static void +blockmix_salsa8(uint32_t * Bin, uint32_t * Bout, uint32_t * X, size_t r) +{ + size_t i; + + /* 1: X <-- B_{2r - 1} */ + blkcpy(X, &Bin[(2 * r - 1) * 16], 64); + + /* 2: for i = 0 to 2r - 1 do */ + for (i = 0; i < 2 * r; i += 2) { + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 16], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 8], X, 64); + + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 16 + 16], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 8 + r * 16], X, 64); + } +} + +/** + * integerify(B, r): + * Return the result of parsing B_{2r-1} as a little-endian integer. + */ +static uint64_t +integerify(void * B, size_t r) +{ + uint32_t * X = (void *)((uintptr_t)(B) + (2 * r - 1) * 64); + + return (((uint64_t)(X[1]) << 32) + X[0]); +} + +/** + * smix(B, r, N, V, XY): + * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; + * the temporary storage V must be 128rN bytes in length; the temporary + * storage XY must be 256r + 64 bytes in length. The value N must be a + * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a + * multiple of 64 bytes. + */ +static void +smix(uint8_t * B, size_t r, uint64_t N, uint32_t * V, uint32_t * XY) +{ + uint32_t * X = XY; + uint32_t * Y = &XY[32 * r]; + uint32_t * Z = &XY[64 * r]; + uint64_t i; + uint64_t j; + size_t k; + + /* 1: X <-- B */ + for (k = 0; k < 32 * r; k++) + X[k] = le32dec(&B[4 * k]); + + /* 2: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 3: V_i <-- X */ + blkcpy(&V[i * (32 * r)], X, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(X, Y, Z, r); + + /* 3: V_i <-- X */ + blkcpy(&V[(i + 1) * (32 * r)], Y, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(Y, X, Z, r); + } + + /* 6: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 7: j <-- Integerify(X) mod N */ + j = integerify(X, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(X, &V[j * (32 * r)], 128 * r); + blockmix_salsa8(X, Y, Z, r); + + /* 7: j <-- Integerify(X) mod N */ + j = integerify(Y, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(Y, &V[j * (32 * r)], 128 * r); + blockmix_salsa8(Y, X, Z, r); + } + + /* 10: B' <-- X */ + for (k = 0; k < 32 * r; k++) + le32enc(&B[4 * k], X[k]); +} + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error + */ +int +libscrypt_scrypt(const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, + uint8_t * buf, size_t buflen) +{ + void * B0, * V0, * XY0; + uint8_t * B; + uint32_t * V; + uint32_t * XY; + uint32_t i; + + /* Sanity-check parameters. */ +#if SIZE_MAX > UINT32_MAX + if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { + errno = EFBIG; + goto err0; + } +#endif + if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { + errno = EFBIG; + goto err0; + } + if (r == 0 || p == 0) { + errno = EINVAL; + goto err0; + } + if (((N & (N - 1)) != 0) || (N < 2)) { + errno = EINVAL; + goto err0; + } + if ((r > SIZE_MAX / 128 / p) || +#if SIZE_MAX / 256 <= UINT32_MAX + (r > SIZE_MAX / 256) || +#endif + (N > SIZE_MAX / 128 / r)) { + errno = ENOMEM; + goto err0; + } + + /* Allocate memory. */ +#ifdef HAVE_POSIX_MEMALIGN + if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) + goto err0; + B = (uint8_t *)(B0); + if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) + goto err1; + XY = (uint32_t *)(XY0); +#ifndef MAP_ANON + if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) + goto err2; + V = (uint32_t *)(V0); +#endif +#else + if ((B0 = malloc(128 * r * p + 63)) == NULL) + goto err0; + B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); + if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) + goto err1; + XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); +#ifndef MAP_ANON + if ((V0 = malloc(128 * r * N + 63)) == NULL) + goto err2; + V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); +#endif +#endif +#ifdef MAP_ANON + if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, +#ifdef MAP_NOCORE + MAP_ANON | MAP_PRIVATE | MAP_NOCORE, +#else + MAP_ANON | MAP_PRIVATE, +#endif + -1, 0)) == MAP_FAILED) + goto err2; + V = (uint32_t *)(V0); +#endif + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ + libscrypt_PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { + /* 3: B_i <-- MF(B_i, N) */ + smix(&B[i * 128 * r], r, N, V, XY); + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ + libscrypt_PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); + + /* Free memory. */ +#ifdef MAP_ANON + if (munmap(V0, 128 * r * N)) + goto err2; +#else + free(V0); +#endif + free(XY0); + free(B0); + + /* Success! */ + return (0); + +err2: + free(XY0); +err1: + free(B0); +err0: + /* Failure! */ + return (-1); +} diff --git a/libscrypt/libscrypt.h b/libscrypt/libscrypt.h new file mode 100644 index 000000000..889ba13aa --- /dev/null +++ b/libscrypt/libscrypt.h @@ -0,0 +1,56 @@ +/*- + */ +#ifndef _CRYPTO_SCRYPT_H_ +#define _CRYPTO_SCRYPT_H_ + + +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * libscrypt_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * password; duh + * N: CPU AND RAM cost (first modifier) + * r: RAM Cost + * p: CPU cost (parallelisation) + * In short, N is your main performance modifier. Values of r = 8, p = 1 are + * standard unless you want to modify the CPU/RAM ratio. + * Return 0 on success; or -1 on error. + */ +int libscrypt_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, + uint32_t, uint32_t, /*@out@*/ uint8_t *, size_t); + +/* Converts a series of input parameters to a MCF form for storage */ +int libscrypt_mcf(uint32_t N, uint32_t r, uint32_t p, const char *salt, + const char *hash, char *mcf); + +/* Checks a given MCF against a password */ +int libscrypt_check(char *mcf, const char *password); + +#ifdef __cplusplus +} +#endif + +/* Sane default values */ +#define SCRYPT_HASH_LEN 64 /* This can be user defined - + *but 64 is the reference size + */ +#define SCRYPT_SAFE_N 30 /* This is much higher than you want. It's just + * a blocker for insane defines + */ +#define SCRYPT_SALT_LEN 16 /* This is just a recommended size */ +#define SCRYPT_MCF_LEN 125 /* mcf is 120 byte + nul */ +#define SCRYPT_MCF_ID "$s1" +#define SCRYPT_N 16384 +#define SCRYPT_r 8 +#define SCRYPT_p 16 +#endif /* !_CRYPTO_SCRYPT_H_ */ diff --git a/libscrypt/libscrypt.version b/libscrypt/libscrypt.version new file mode 100644 index 000000000..9cc574db2 --- /dev/null +++ b/libscrypt/libscrypt.version @@ -0,0 +1,8 @@ +libscrypt { + global: libscrypt_check; +libscrypt_hash; +libscrypt_mcf; +libscrypt_salt_gen; +libscrypt_scrypt; + local: *; +}; diff --git a/libscrypt/sha256.c b/libscrypt/sha256.c new file mode 100644 index 000000000..279e3cf8d --- /dev/null +++ b/libscrypt/sha256.c @@ -0,0 +1,411 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "sysendian.h" + +#include "sha256.h" + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + t0 = h + S1(e) + Ch(e, f, g) + k; \ + t1 = S0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, k) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i] + k) + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t * state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + uint32_t t0, t1; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + RNDr(S, W, 0, 0x428a2f98); + RNDr(S, W, 1, 0x71374491); + RNDr(S, W, 2, 0xb5c0fbcf); + RNDr(S, W, 3, 0xe9b5dba5); + RNDr(S, W, 4, 0x3956c25b); + RNDr(S, W, 5, 0x59f111f1); + RNDr(S, W, 6, 0x923f82a4); + RNDr(S, W, 7, 0xab1c5ed5); + RNDr(S, W, 8, 0xd807aa98); + RNDr(S, W, 9, 0x12835b01); + RNDr(S, W, 10, 0x243185be); + RNDr(S, W, 11, 0x550c7dc3); + RNDr(S, W, 12, 0x72be5d74); + RNDr(S, W, 13, 0x80deb1fe); + RNDr(S, W, 14, 0x9bdc06a7); + RNDr(S, W, 15, 0xc19bf174); + RNDr(S, W, 16, 0xe49b69c1); + RNDr(S, W, 17, 0xefbe4786); + RNDr(S, W, 18, 0x0fc19dc6); + RNDr(S, W, 19, 0x240ca1cc); + RNDr(S, W, 20, 0x2de92c6f); + RNDr(S, W, 21, 0x4a7484aa); + RNDr(S, W, 22, 0x5cb0a9dc); + RNDr(S, W, 23, 0x76f988da); + RNDr(S, W, 24, 0x983e5152); + RNDr(S, W, 25, 0xa831c66d); + RNDr(S, W, 26, 0xb00327c8); + RNDr(S, W, 27, 0xbf597fc7); + RNDr(S, W, 28, 0xc6e00bf3); + RNDr(S, W, 29, 0xd5a79147); + RNDr(S, W, 30, 0x06ca6351); + RNDr(S, W, 31, 0x14292967); + RNDr(S, W, 32, 0x27b70a85); + RNDr(S, W, 33, 0x2e1b2138); + RNDr(S, W, 34, 0x4d2c6dfc); + RNDr(S, W, 35, 0x53380d13); + RNDr(S, W, 36, 0x650a7354); + RNDr(S, W, 37, 0x766a0abb); + RNDr(S, W, 38, 0x81c2c92e); + RNDr(S, W, 39, 0x92722c85); + RNDr(S, W, 40, 0xa2bfe8a1); + RNDr(S, W, 41, 0xa81a664b); + RNDr(S, W, 42, 0xc24b8b70); + RNDr(S, W, 43, 0xc76c51a3); + RNDr(S, W, 44, 0xd192e819); + RNDr(S, W, 45, 0xd6990624); + RNDr(S, W, 46, 0xf40e3585); + RNDr(S, W, 47, 0x106aa070); + RNDr(S, W, 48, 0x19a4c116); + RNDr(S, W, 49, 0x1e376c08); + RNDr(S, W, 50, 0x2748774c); + RNDr(S, W, 51, 0x34b0bcb5); + RNDr(S, W, 52, 0x391c0cb3); + RNDr(S, W, 53, 0x4ed8aa4a); + RNDr(S, W, 54, 0x5b9cca4f); + RNDr(S, W, 55, 0x682e6ff3); + RNDr(S, W, 56, 0x748f82ee); + RNDr(S, W, 57, 0x78a5636f); + RNDr(S, W, 58, 0x84c87814); + RNDr(S, W, 59, 0x8cc70208); + RNDr(S, W, 60, 0x90befffa); + RNDr(S, W, 61, 0xa4506ceb); + RNDr(S, W, 62, 0xbef9a3f7); + RNDr(S, W, 63, 0xc67178f2); + + /* 4. Mix local working variables into global state */ + for (i = 0; i < 8; i++) + state[i] += S[i]; + + /* Clean the stack. */ + memset(W, 0, 256); + memset(S, 0, 32); + t0 = t1 = 0; +} + +static unsigned char PAD[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx) +{ + unsigned char len[8]; + uint32_t r, plen; + + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + be32enc_vect(len, ctx->count, 8); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64 */ + r = (ctx->count[1] >> 3) & 0x3f; + plen = (r < 56) ? (56 - r) : (120 - r); + libscrypt_SHA256_Update(ctx, PAD, (size_t)plen); + + /* Add the terminating bit-count */ + libscrypt_SHA256_Update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +void +libscrypt_SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far */ + ctx->count[0] = ctx->count[1] = 0; + + /* Magic initialization constants */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash */ +void +libscrypt_SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len) +{ + uint32_t bitlen[2]; + uint32_t r; + const unsigned char *src = in; + + /* Number of bytes left in the buffer from previous updates */ + r = (ctx->count[1] >> 3) & 0x3f; + + /* Convert the length into a number of bits */ + bitlen[1] = ((uint32_t)len) << 3; + bitlen[0] = (uint32_t)(len >> 29); + + /* Update number of bits */ + if ((ctx->count[1] += bitlen[1]) < bitlen[1]) + ctx->count[0]++; + ctx->count[0] += bitlen[0]; + + /* Handle the case where we don't need to perform any transforms */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks */ + while (len >= 64) { + SHA256_Transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +void +libscrypt_SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + + /* Add padding */ + SHA256_Pad(ctx); + + /* Write the hash */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state */ + memset((void *)ctx, 0, sizeof(*ctx)); +} + +/* Initialize an HMAC-SHA256 operation with the given key. */ +void +libscrypt_HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen) +{ + unsigned char pad[64]; + unsigned char khash[32]; + const unsigned char * K = _K; + size_t i; + + /* If Klen > 64, the key is really SHA256(K). */ + if (Klen > 64) { + libscrypt_SHA256_Init(&ctx->ictx); + libscrypt_SHA256_Update(&ctx->ictx, K, Klen); + libscrypt_SHA256_Final(khash, &ctx->ictx); + K = khash; + Klen = 32; + } + + /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ + libscrypt_SHA256_Init(&ctx->ictx); + memset(pad, 0x36, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + libscrypt_SHA256_Update(&ctx->ictx, pad, 64); + + /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ + libscrypt_SHA256_Init(&ctx->octx); + memset(pad, 0x5c, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + libscrypt_SHA256_Update(&ctx->octx, pad, 64); + + /* Clean the stack. */ + memset(khash, 0, 32); +} + +/* Add bytes to the HMAC-SHA256 operation. */ +void +libscrypt_HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void *in, size_t len) +{ + + /* Feed data to the inner SHA256 operation. */ + libscrypt_SHA256_Update(&ctx->ictx, in, len); +} + +/* Finish an HMAC-SHA256 operation. */ +void +libscrypt_HMAC_SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX * ctx) +{ + unsigned char ihash[32]; + + /* Finish the inner SHA256 operation. */ + libscrypt_SHA256_Final(ihash, &ctx->ictx); + + /* Feed the inner hash to the outer SHA256 operation. */ + libscrypt_SHA256_Update(&ctx->octx, ihash, 32); + + /* Finish the outer SHA256 operation. */ + libscrypt_SHA256_Final(digest, &ctx->octx); + + /* Clean the stack. */ + memset(ihash, 0, 32); +} + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void +libscrypt_PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, + size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) +{ + HMAC_SHA256_CTX PShctx, hctx; + size_t i; + uint8_t ivec[4]; + uint8_t U[32]; + uint8_t T[32]; + uint64_t j; + int k; + size_t clen; + + /* Compute HMAC state after processing P and S. */ + libscrypt_HMAC_SHA256_Init(&PShctx, passwd, passwdlen); + libscrypt_HMAC_SHA256_Update(&PShctx, salt, saltlen); + + /* Iterate through the blocks. */ + for (i = 0; i * 32 < dkLen; i++) { + /* Generate INT(i + 1). */ + be32enc(ivec, (uint32_t)(i + 1)); + + /* Compute U_1 = PRF(P, S || INT(i)). */ + memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); + libscrypt_HMAC_SHA256_Update(&hctx, ivec, 4); + libscrypt_HMAC_SHA256_Final(U, &hctx); + + /* T_i = U_1 ... */ + memcpy(T, U, 32); + + for (j = 2; j <= c; j++) { + /* Compute U_j. */ + libscrypt_HMAC_SHA256_Init(&hctx, passwd, passwdlen); + libscrypt_HMAC_SHA256_Update(&hctx, U, 32); + libscrypt_HMAC_SHA256_Final(U, &hctx); + + /* ... xor U_j ... */ + for (k = 0; k < 32; k++) + T[k] ^= U[k]; + } + + /* Copy as many bytes as necessary into buf. */ + clen = dkLen - i * 32; + if (clen > 32) + clen = 32; + memcpy(&buf[i * 32], T, clen); + } + + /* Clean PShctx, since we never called _Final on it. */ + memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX)); +} diff --git a/libscrypt/sha256.h b/libscrypt/sha256.h new file mode 100644 index 000000000..f7138b417 --- /dev/null +++ b/libscrypt/sha256.h @@ -0,0 +1,70 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $ + */ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#include + +#include + +typedef struct libscrypt_SHA256Context { + uint32_t state[8]; + uint32_t count[2]; + unsigned char buf[64]; +} SHA256_CTX; + +typedef struct libscrypt_HMAC_SHA256Context { + SHA256_CTX ictx; + SHA256_CTX octx; +} HMAC_SHA256_CTX; + +void libscrypt_SHA256_Init(/*@out@*/ SHA256_CTX *); +void libscrypt_SHA256_Update(SHA256_CTX *, const void *, size_t); + +/* Original declaration: + * void SHA256_Final(unsigned char [32], SHA256_CTX *); +*/ +void libscrypt_SHA256_Final(/*@out@*/ unsigned char [], SHA256_CTX *); +void libscrypt_HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); +void libscrypt_HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); + +/* Original declaration: + * void HMAC_SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *); +*/ +void libscrypt_HMAC_SHA256_Final(unsigned char [], HMAC_SHA256_CTX *); + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void libscrypt_PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, + uint64_t, uint8_t *, size_t); + +#endif /* !_SHA256_H_ */ diff --git a/libscrypt/slowequals.c b/libscrypt/slowequals.c new file mode 100644 index 000000000..48e488e4e --- /dev/null +++ b/libscrypt/slowequals.c @@ -0,0 +1,26 @@ +#include + +/* Implements a constant time version of strcmp() + * Will return 1 if a and b are equal, 0 if they are not */ +int slow_equals(const char* a, const char* b) +{ + size_t lena, lenb, diff, i; + lena = strlen(a); + lenb = strlen(b); + diff = strlen(a) ^ strlen(b); + + for(i=0; i we have isn't usable. */ +#if !HAVE_DECL_BE64ENC +#undef HAVE_SYS_ENDIAN_H +#endif + +#ifdef HAVE_SYS_ENDIAN_H + +#include + +#else + +#include +#ifdef _MSC_VER + #define INLINE __inline +#else + #define INLINE inline +#endif + +static INLINE uint32_t +be32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); +} + +static INLINE void +be32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} + +static INLINE uint64_t +be64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); +} + +static INLINE void +be64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[7] = x & 0xff; + p[6] = (x >> 8) & 0xff; + p[5] = (x >> 16) & 0xff; + p[4] = (x >> 24) & 0xff; + p[3] = (x >> 32) & 0xff; + p[2] = (x >> 40) & 0xff; + p[1] = (x >> 48) & 0xff; + p[0] = (x >> 56) & 0xff; +} + +static INLINE uint32_t +le32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); +} + +static INLINE void +le32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +static INLINE uint64_t +le64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); +} + +static INLINE void +le64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; + p[4] = (x >> 32) & 0xff; + p[5] = (x >> 40) & 0xff; + p[6] = (x >> 48) & 0xff; + p[7] = (x >> 56) & 0xff; +} +#endif /* !HAVE_SYS_ENDIAN_H */ + +#endif /* !_SYSENDIAN_H_ */ diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 9842901e7..14884254e 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include using namespace std; diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index fbda50791..f78472208 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -220,6 +220,26 @@ protected: virtual void endVisitNode(ASTNode const&) { } }; +/** + * Utility class that accepts std::functions and calls them for visitNode and endVisitNode. + */ +class SimpleASTVisitor: public ASTConstVisitor +{ +public: + SimpleASTVisitor( + std::function _onVisit, + std::function _onEndVisit + ): m_onVisit(_onVisit), m_onEndVisit(_onEndVisit) {} + +protected: + virtual bool visitNode(ASTNode const& _n) override { return m_onVisit ? m_onVisit(_n) : true; } + virtual void endVisitNode(ASTNode const& _n) override { m_onEndVisit(_n); } + +private: + std::function m_onVisit; + std::function m_onEndVisit; +}; + /** * Utility class that visits the AST in depth-first order and calls a function on each node and each edge. * Child nodes are only visited if the node callback of the parent returns true. diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index b3fedc45d..4f9764075 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include using namespace std; @@ -55,12 +55,29 @@ const map StandardSources = map{ }; CompilerStack::CompilerStack(bool _addStandardSources): - m_addStandardSources(_addStandardSources), m_parseSuccessful(false) + m_parseSuccessful(false) { - if (m_addStandardSources) + if (_addStandardSources) addSources(StandardSources, true); // add them as libraries } +void CompilerStack::reset(bool _keepSources, bool _addStandardSources) +{ + m_parseSuccessful = false; + if (_keepSources) + for (auto sourcePair: m_sources) + sourcePair.second.reset(); + else + { + m_sources.clear(); + if (_addStandardSources) + addSources(StandardSources, true); + } + m_globalContext.reset(); + m_sourceOrder.clear(); + m_contracts.clear(); +} + bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary) { bool existed = m_sources.count(_name) != 0; @@ -269,23 +286,6 @@ tuple CompilerStack::positionFromSourceLocation(SourceLocati return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn); } -void CompilerStack::reset(bool _keepSources) -{ - m_parseSuccessful = false; - if (_keepSources) - for (auto sourcePair: m_sources) - sourcePair.second.reset(); - else - { - m_sources.clear(); - if (m_addStandardSources) - addSources(StandardSources, true); - } - m_globalContext.reset(); - m_sourceOrder.clear(); - m_contracts.clear(); -} - void CompilerStack::resolveImports() { // topological sorting (depth first search) of the import graph, cutting potential cycles diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 2ad791f22..0bc109a26 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -72,6 +72,9 @@ public: /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources. explicit CompilerStack(bool _addStandardSources = true); + /// Resets the compiler to a state where the sources are not parsed or even removed. + void reset(bool _keepSources = false, bool _addStandardSources = true); + /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// @returns true if a source object by the name already existed and was replaced. void addSources(StringMap const& _nameContents, bool _isLibrary = false) { for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary); } @@ -165,13 +168,11 @@ private: Contract(); }; - void reset(bool _keepSources = false); void resolveImports(); Contract const& getContract(std::string const& _contractName = "") const; Source const& getSource(std::string const& _sourceName = "") const; - bool m_addStandardSources; ///< If true, standard sources are added. bool m_parseSuccessful; std::map m_sources; std::shared_ptr m_globalContext; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 838ee264e..2e513b7fc 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 85026ac12..9a2acd5ba 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -107,17 +107,27 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef) { string ret = "contract " + _contractDef.getName() + "{"; + + auto populateParameters = [](vector const& _paramNames, vector const& _paramTypes) + { + string r = ""; + solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); + for (unsigned i = 0; i < _paramNames.size(); ++i) + r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i]; + return r.size() ? r + ")" : "()"; + }; + if (_contractDef.getConstructor()) + { + auto externalFunction = FunctionType(*_contractDef.getConstructor()).externalFunctionType(); + solAssert(!!externalFunction, ""); + ret += + "function " + + _contractDef.getName() + + populateParameters(externalFunction->getParameterNames(), externalFunction->getParameterTypeNames()) + + ";"; + } for (auto const& it: _contractDef.getInterfaceFunctions()) { - auto populateParameters = [](vector const& _paramNames, - vector const& _paramTypes) - { - string r = ""; - solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); - for (unsigned i = 0; i < _paramNames.size(); ++i) - r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i]; - return r.size() ? r + ")" : "()"; - }; ret += "function " + it.second->getDeclaration().getName() + populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) + (it.second->isConstant() ? "constant " : ""); diff --git a/libsolidity/StructuralGasEstimator.cpp b/libsolidity/StructuralGasEstimator.cpp index ececd7116..9ce32ca54 100644 --- a/libsolidity/StructuralGasEstimator.cpp +++ b/libsolidity/StructuralGasEstimator.cpp @@ -23,6 +23,9 @@ #include "StructuralGasEstimator.h" #include #include +#include +#include +#include #include #include @@ -38,14 +41,23 @@ StructuralGasEstimator::ASTGasConsumptionSelfAccumulated StructuralGasEstimator: { solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, ""); map particularCosts; - GasMeter meter; - for (auto const& item: _items) - particularCosts[item.getLocation()] += meter.estimateMax(item); + ControlFlowGraph cfg(_items); + for (BasicBlock const& block: cfg.optimisedBlocks()) + { + assertThrow(!!block.startState, OptimizerException, ""); + GasMeter meter(block.startState->copy()); + auto const end = _items.begin() + block.end; + for (auto iter = _items.begin() + block.begin; iter != end; ++iter) + particularCosts[iter->getLocation()] += meter.estimateMax(*iter); + } + set finestNodes = finestNodesAtLocation(_ast); ASTGasConsumptionSelfAccumulated gasCosts; auto onNode = [&](ASTNode const& _node) { + if (!finestNodes.count(&_node)) + return true; gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.getLocation()]; return true; }; @@ -108,3 +120,24 @@ map StructuralGasEstimator::breakToSta // gasCosts should only contain non-overlapping locations return gasCosts; } + +set StructuralGasEstimator::finestNodesAtLocation( + vector const& _roots +) +{ + map locations; + set nodes; + SimpleASTVisitor visitor(function(), [&](ASTNode const& _n) + { + if (!locations.count(_n.getLocation())) + { + locations[_n.getLocation()] = &_n; + nodes.insert(&_n); + } + }); + + for (ASTNode const* root: _roots) + root->accept(visitor); + return nodes; +} + diff --git a/libsolidity/StructuralGasEstimator.h b/libsolidity/StructuralGasEstimator.h index df1ae509d..ddc7c186c 100644 --- a/libsolidity/StructuralGasEstimator.h +++ b/libsolidity/StructuralGasEstimator.h @@ -56,6 +56,10 @@ public: ASTGasConsumptionSelfAccumulated const& _gasCosts, std::vector const& _roots ); + +private: + /// @returns the set of AST nodes which are the finest nodes at their location. + std::set finestNodesAtLocation(std::vector const& _roots); }; } diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 1435dcc57..8a373da34 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -142,34 +142,34 @@ namespace solidity K(Delete, "delete", 0) \ \ /* Keywords */ \ + K(Anonymous, "anonymous", 0) \ K(Break, "break", 0) \ K(Const, "constant", 0) \ - K(Anonymous, "anonymous", 0) \ K(Continue, "continue", 0) \ K(Contract, "contract", 0) \ K(Default, "default", 0) \ K(Do, "do", 0) \ K(Else, "else", 0) \ + K(Enum, "enum", 0) \ K(Event, "event", 0) \ K(External, "external", 0) \ - K(Is, "is", 0) \ - K(Indexed, "indexed", 0) \ K(For, "for", 0) \ K(Function, "function", 0) \ K(If, "if", 0) \ + K(Indexed, "indexed", 0) \ + K(Internal, "internal", 0) \ K(Import, "import", 0) \ + K(Is, "is", 0) \ K(Mapping, "mapping", 0) \ K(Modifier, "modifier", 0) \ K(New, "new", 0) \ K(Public, "public", 0) \ K(Private, "private", 0) \ - K(Internal, "internal", 0) \ K(Return, "return", 0) \ K(Returns, "returns", 0) \ K(Struct, "struct", 0) \ K(Var, "var", 0) \ K(While, "while", 0) \ - K(Enum, "enum", 0) \ \ /* Ether subdenominations */ \ K(SubWei, "wei", 0) \ @@ -304,15 +304,21 @@ namespace solidity T(Identifier, NULL, 0) \ \ /* Keywords reserved for future. use*/ \ - T(String, "string", 0) \ + K(As, "as", 0) \ K(Case, "case", 0) \ + K(Catch, "catch", 0) \ + K(Final, "final", 0) \ + K(Let, "let", 0) \ + K(Match, "match", 0) \ + K(Of, "of", 0) \ + K(Relocatable, "relocatable", 0) \ + T(String, "string", 0) \ K(Switch, "switch", 0) \ K(Throw, "throw", 0) \ K(Try, "try", 0) \ - K(Catch, "catch", 0) \ - K(Using, "using", 0) \ K(Type, "type", 0) \ K(TypeOf, "typeof", 0) \ + K(Using, "using", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index d1f51decf..7577b83a1 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include diff --git a/libtestutils/Common.cpp b/libtestutils/Common.cpp index 5767be32c..cff21d464 100644 --- a/libtestutils/Common.cpp +++ b/libtestutils/Common.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "Common.h" using namespace std; diff --git a/libweb3jsonrpc/AccountHolder.cpp b/libweb3jsonrpc/AccountHolder.cpp index a73f20680..abd0a1adf 100644 --- a/libweb3jsonrpc/AccountHolder.cpp +++ b/libweb3jsonrpc/AccountHolder.cpp @@ -26,7 +26,8 @@ #include #include #include -#include +#include + using namespace std; using namespace dev; diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 5235b0c4f..44f7f521c 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include "WebThreeStubServer.h" using namespace std; diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index c29ac6bf6..dd9172c70 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -21,7 +21,7 @@ #include "Common.h" -#include +#include #include "Message.h" using namespace std; using namespace dev; diff --git a/libwhisper/Interface.h b/libwhisper/Interface.h index db595e21e..8b84193aa 100644 --- a/libwhisper/Interface.h +++ b/libwhisper/Interface.h @@ -29,7 +29,7 @@ #include #include -#include +#include #include "Common.h" #include "Message.h" diff --git a/libwhisper/Message.h b/libwhisper/Message.h index bd73df268..893602190 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "Common.h" namespace dev diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index ebbbcf8ed..cd427da35 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "Common.h" #include "WhisperPeer.h" #include "Interface.h" diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index 8542e987d..ab9c8222a 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -29,7 +29,7 @@ #include #include -#include +#include #include "Common.h" #include "Message.h" diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 54d5db2b5..6b5242084 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -387,24 +387,21 @@ void ClientModel::executeSequence(vector const& _sequence, std::pair ClientModel::resolvePair(QString const& _contractId) { - std::pair ret; - ret.first = _contractId; - ret.second = -1; + std::pair ret = std::make_pair(_contractId, 0); if (_contractId.startsWith("<") && _contractId.endsWith(">")) { QStringList values = ret.first.remove("<").remove(">").split(" - "); - ret.first = values[0]; - ret.second = values[1].toUInt(); + ret = std::make_pair(values[0], values[1].toUInt()); } return ret; } QString ClientModel::resolveToken(std::pair const& _value, vector
const& _contracts) { - if (_value.second != -1) - return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref())); - else - return _value.first; + if (_contracts.size() > 0) + return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref())); + else + return _value.first; } std::pair ClientModel::retrieveToken(QString const& _value, vector
const& _contracts) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 5c6ec07c0..d0f70683d 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include "QContractDefinition.h" @@ -45,18 +47,19 @@ using namespace dev::mix; const std::set c_predefinedContracts = - { "Config", "Coin", "CoinReg", "coin", "service", "owned", "mortal", "NameReg", "named", "std", "configUser" }; +{ "Config", "Coin", "CoinReg", "coin", "service", "owned", "mortal", "NameReg", "named", "std", "configUser" }; namespace { +using namespace dev::eth; using namespace dev::solidity; class CollectLocalsVisitor: public ASTConstVisitor { public: CollectLocalsVisitor(QHash* _locals): - m_locals(_locals), m_functionScope(false) {} + m_locals(_locals), m_functionScope(false) {} private: LocationPair nodeLocation(ASTNode const& _node) @@ -96,7 +99,7 @@ class CollectLocationsVisitor: public ASTConstVisitor { public: CollectLocationsVisitor(SourceMap* _sourceMap): - m_sourceMap(_sourceMap) {} + m_sourceMap(_sourceMap) {} private: LocationPair nodeLocation(ASTNode const& _node) @@ -194,6 +197,8 @@ CodeModel::~CodeModel() stop(); disconnect(this); releaseContracts(); + if (m_gasCostsMaps) + delete m_gasCostsMaps; } void CodeModel::stop() @@ -305,28 +310,90 @@ void CodeModel::runCompilationJob(int _jobId) } } cs.compile(false); + gasEstimation(cs); collectContracts(cs, sourceNames); } catch (dev::Exception const& _exception) { - std::ostringstream error; + std::stringstream error; solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs); QString message = QString::fromStdString(error.str()); - QString sourceName; - if (SourceLocation const* location = boost::get_error_info(_exception)) + QVariantMap firstLocation; + QVariantList secondLocations; + if (SourceLocation const* first = boost::get_error_info(_exception)) + firstLocation = resolveCompilationErrorLocation(cs, *first); + if (SecondarySourceLocation const* second = boost::get_error_info(_exception)) { - if (location->sourceName) - sourceName = QString::fromStdString(*location->sourceName); - if (!sourceName.isEmpty()) - if (CompiledContract* contract = contractByDocumentId(sourceName)) - message = message.replace(sourceName, contract->contract()->name()); //substitute the location to match our contract names + for (auto const& c: second->infos) + secondLocations.push_back(resolveCompilationErrorLocation(cs, c.second)); } - compilationError(message, sourceName); + compilationError(message, firstLocation, secondLocations); } m_compiling = false; emit stateChanged(); } +QVariantMap CodeModel::resolveCompilationErrorLocation(CompilerStack const& _compiler, SourceLocation const& _location) +{ + std::tuple pos = _compiler.positionFromSourceLocation(_location); + QVariantMap startError; + startError.insert("line", std::get<0>(pos) > 1 ? (std::get<0>(pos) - 1) : 1); + startError.insert("column", std::get<1>(pos) > 1 ? (std::get<1>(pos) - 1) : 1); + QVariantMap endError; + endError.insert("line", std::get<2>(pos) > 1 ? (std::get<2>(pos) - 1) : 1); + endError.insert("column", std::get<3>(pos) > 1 ? (std::get<3>(pos) - 1) : 1); + QVariantMap error; + error.insert("start", startError); + error.insert("end", endError); + QString sourceName; + if (_location.sourceName) + sourceName = QString::fromStdString(*_location.sourceName); + error.insert("source", sourceName); + if (!sourceName.isEmpty()) + if (CompiledContract* contract = contractByDocumentId(sourceName)) + sourceName = contract->contract()->name(); //substitute the location to match our contract names + error.insert("contractName", sourceName); + return error; +} + +void CodeModel::gasEstimation(solidity::CompilerStack const& _cs) +{ + if (m_gasCostsMaps) + m_gasCostsMaps->deleteLater(); + m_gasCostsMaps = new GasMapWrapper; + for (std::string n: _cs.getContractNames()) + { + ContractDefinition const& contractDefinition = _cs.getContractDefinition(n); + QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName); + + if (!m_gasCostsMaps->contains(sourceName)) + m_gasCostsMaps->insert(sourceName, QVariantList()); + + if (!contractDefinition.isFullyImplemented()) + continue; + dev::solidity::SourceUnit const& sourceUnit = _cs.getAST(*contractDefinition.getLocation().sourceName); + AssemblyItems const* items = _cs.getRuntimeAssemblyItems(n); + StructuralGasEstimator estimator; + std::map gasCosts = estimator.breakToStatementLevel(estimator.performEstimation(*items, std::vector({&sourceUnit})), {&sourceUnit}); + for (auto gasItem = gasCosts.begin(); gasItem != gasCosts.end(); ++gasItem) + { + SourceLocation const& location = gasItem->first->getLocation(); + GasMeter::GasConsumption cost = gasItem->second; + std::stringstream v; + v << cost.value; + m_gasCostsMaps->push(sourceName, location.start, location.end, QString::fromStdString(v.str()), cost.isInfinite); + } + } +} + +QVariantList CodeModel::gasCostByDocumentId(QString const& _documentId) const +{ + if (m_gasCostsMaps) + return m_gasCostsMaps->gasCostsByDocId(_documentId); + else + return QVariantList(); +} + void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs, std::vector const& _sourceNames) { Guard pl(x_pendingContracts); @@ -369,7 +436,7 @@ void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs, std::v { //make sure there are no other contracts in the same source, otherwise it is not a rename if (!std::any_of(result.begin(),result.end(), [=](ContractMap::const_iterator::value_type _v) { return _v != contract && _v->documentId() == contract->documentId(); })) - prevContract = c.value(); + prevContract = c.value(); } } if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface()) @@ -419,59 +486,59 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) switch (_type->getCategory()) { case Type::Category::Integer: - { - IntegerType const* it = dynamic_cast(_type); - r.size = it->getNumBits() / 8; - r.type = it->isAddress() ? SolidityType::Type::Address : it->isSigned() ? SolidityType::Type::SignedInteger : SolidityType::Type::UnsignedInteger; - } + { + IntegerType const* it = dynamic_cast(_type); + r.size = it->getNumBits() / 8; + r.type = it->isAddress() ? SolidityType::Type::Address : it->isSigned() ? SolidityType::Type::SignedInteger : SolidityType::Type::UnsignedInteger; + } break; case Type::Category::Bool: r.type = SolidityType::Type::Bool; break; case Type::Category::FixedBytes: - { - FixedBytesType const* b = dynamic_cast(_type); - r.type = SolidityType::Type::Bytes; - r.size = static_cast(b->getNumBytes()); - } + { + FixedBytesType const* b = dynamic_cast(_type); + r.type = SolidityType::Type::Bytes; + r.size = static_cast(b->getNumBytes()); + } break; case Type::Category::Contract: r.type = SolidityType::Type::Address; break; case Type::Category::Array: + { + ArrayType const* array = dynamic_cast(_type); + if (array->isByteArray()) + r.type = SolidityType::Type::Bytes; + else { - ArrayType const* array = dynamic_cast(_type); - if (array->isByteArray()) - r.type = SolidityType::Type::Bytes; - else - { - SolidityType elementType = nodeType(array->getBaseType().get()); - elementType.name = r.name; - r = elementType; - } - r.count = static_cast(array->getLength()); - r.dynamicSize = _type->isDynamicallySized(); - r.array = true; + SolidityType elementType = nodeType(array->getBaseType().get()); + elementType.name = r.name; + r = elementType; } + r.count = static_cast(array->getLength()); + r.dynamicSize = _type->isDynamicallySized(); + r.array = true; + } break; case Type::Category::Enum: - { - r.type = SolidityType::Type::Enum; - EnumType const* e = dynamic_cast(_type); - for(auto const& enumValue: e->getEnumDefinition().getMembers()) - r.enumNames.push_back(QString::fromStdString(enumValue->getName())); - } + { + r.type = SolidityType::Type::Enum; + EnumType const* e = dynamic_cast(_type); + for(auto const& enumValue: e->getEnumDefinition().getMembers()) + r.enumNames.push_back(QString::fromStdString(enumValue->getName())); + } break; case Type::Category::Struct: + { + r.type = SolidityType::Type::Struct; + StructType const* s = dynamic_cast(_type); + for(auto const& structMember: s->getMembers()) { - r.type = SolidityType::Type::Struct; - StructType const* s = dynamic_cast(_type); - for(auto const& structMember: s->getMembers()) - { - auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.name); - r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.name), nodeType(structMember.type.get()), slotAndOffset.first, slotAndOffset.second }); - } + auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.name); + r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.name), nodeType(structMember.type.get()), slotAndOffset.first, slotAndOffset.second }); } + } break; case Type::Category::Function: case Type::Category::IntegerConstant: @@ -516,3 +583,29 @@ QString CodeModel::resolveFunctionName(dev::SourceLocation const& _location) } return QString(); } + +void GasMapWrapper::push(QString _source, int _start, int _end, QString _value, bool _isInfinite) +{ + GasMap* gas = new GasMap(_start, _end, _value, _isInfinite, this); + m_gasMaps.find(_source).value().push_back(QVariant::fromValue(gas)); +} + +bool GasMapWrapper::contains(QString _key) +{ + return m_gasMaps.contains(_key); +} + +void GasMapWrapper::insert(QString _source, QVariantList _variantList) +{ + m_gasMaps.insert(_source, _variantList); +} + +QVariantList GasMapWrapper::gasCostsByDocId(QString _source) +{ + auto gasIter = m_gasMaps.find(_source); + if (gasIter != m_gasMaps.end()) + return gasIter.value(); + else + return QVariantList(); +} + diff --git a/mix/CodeModel.h b/mix/CodeModel.h index a0b03951f..dcf3d0c1e 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -32,6 +32,7 @@ #include #include #include "SolidityType.h" +#include "QBigInt.h" class QTextDocument; @@ -127,6 +128,42 @@ struct SourceMap }; using SourceMaps = QMap; //by source id +using GasCostsMaps = QMap; //gas cost by contract name + +class GasMapWrapper: public QObject +{ + Q_OBJECT + + Q_PROPERTY(GasCostsMaps gasMaps MEMBER m_gasMaps CONSTANT) + +public: + GasMapWrapper(QObject* _parent = nullptr): QObject(_parent){} + void push(QString _source, int _start, int _end, QString _value, bool _isInfinite); + bool contains(QString _key); + void insert(QString _source, QVariantList _variantList); + QVariantList gasCostsByDocId(QString _source); + +private: + GasCostsMaps m_gasMaps; +}; + +class GasMap: public QObject +{ + Q_OBJECT + + Q_PROPERTY(int start MEMBER m_start CONSTANT) + Q_PROPERTY(int end MEMBER m_end CONSTANT) + Q_PROPERTY(QString gas MEMBER m_gas CONSTANT) + Q_PROPERTY(bool isInfinite MEMBER m_isInfinite CONSTANT) + +public: + GasMap(int _start, int _end, QString _gas, bool _isInfinite, QObject* _parent): QObject(_parent), m_start(_start), m_end(_end), m_gas(_gas), m_isInfinite(_isInfinite) {} + + int m_start; + int m_end; + QString m_gas; + bool m_isInfinite; +}; /// Code compilation model. Compiles contracts in background an provides compiled contract data class CodeModel: public QObject @@ -168,6 +205,10 @@ public: bool isContractOrFunctionLocation(dev::SourceLocation const& _location); /// Get funciton name by location QString resolveFunctionName(dev::SourceLocation const& _location); + /// Gas estimation for compiled sources + void gasEstimation(solidity::CompilerStack const& _cs); + /// Gas cost by doc id + Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const; signals: /// Emited on compilation state change @@ -175,7 +216,7 @@ signals: /// Emitted on compilation complete void compilationComplete(); /// Emitted on compilation error - void compilationError(QString _error, QString _sourceName); + void compilationError(QString _error, QVariantMap _firstErrorLoc, QVariantList _secondErrorLoc); /// Internal signal used to transfer compilation job to background thread void scheduleCompilationJob(int _jobId); /// Emitted if there are any changes in the code model @@ -198,11 +239,13 @@ private: void stop(); void releaseContracts(); void collectContracts(dev::solidity::CompilerStack const& _cs, std::vector const& _sourceNames); + QVariantMap resolveCompilationErrorLocation(dev::solidity::CompilerStack const& _cs, dev::SourceLocation const& _location); std::atomic m_compiling; mutable dev::Mutex x_contractMap; ContractMap m_contractMap; SourceMaps m_sourceMaps; + GasMapWrapper* m_gasCostsMaps = 0; std::unique_ptr m_codeHighlighterSettings; QThread m_backgroundThread; BackgroundWorker m_backgroundWorker; @@ -216,3 +259,5 @@ private: } } + +//Q_DECLARE_METATYPE(dev::mix::GasMap) diff --git a/mix/FileIo.cpp b/mix/FileIo.cpp index 22538194c..cf8300677 100644 --- a/mix/FileIo.cpp +++ b/mix/FileIo.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "FileIo.h" using namespace dev; diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index 13dbd4821..e6764d712 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -20,7 +20,7 @@ */ #include -#include +#include #include #include "QVariableDeclaration.h" #include "QFunctionDefinition.h" diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml index fe62efe12..161f7141a 100644 --- a/mix/qml/Application.qml +++ b/mix/qml/Application.qml @@ -116,6 +116,10 @@ ApplicationWindow { MenuSeparator {} MenuItem { action: toggleAssemblyDebuggingAction } } + Menu { + title: qsTr("Tools") + MenuItem { action: gasEstimationAction } + } Menu { title: qsTr("Windows") MenuItem { action: openNextDocumentAction } @@ -409,4 +413,15 @@ ApplicationWindow { mainContent.codeEditor.goToCompilationError(); } } + + Action { + id: gasEstimationAction + text: qsTr("Display gas estimation") + shortcut: "Ctrl+G" + checkable: true + onTriggered: + { + mainContent.codeEditor.displayGasEstimation(checked); + } + } } diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index bb7e203bf..51950fec7 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -74,8 +74,8 @@ Item { }); } editor.document = document; - editor.sourceName = document.documentId; editor.setFontSize(editorSettings.fontSize); + editor.sourceName = document.documentId; editor.setText(data, document.syntaxMode); editor.changeGeneration(); } @@ -168,15 +168,26 @@ Item { editors.itemAt(i).item.setFontSize(size); } + function displayGasEstimation(checked) + { + var editor = getEditor(currentDocumentId); + if (editor) + editor.displayGasEstimation(checked); + } + Component.onCompleted: projectModel.codeEditor = codeEditorView; Connections { target: codeModel onCompilationError: { - sourceInError = _sourceName; + sourceInError = _firstErrorLoc.source; } onCompilationComplete: { sourceInError = ""; + var gasCosts = codeModel.gasCostByDocumentId(currentDocumentId); + var editor = getEditor(currentDocumentId); + if (editor) + editor.setGasCosts(gasCosts); } } @@ -280,6 +291,7 @@ Item { messageDialog.doc = editorListModel.get(index); messageDialog.open(); } + loader.item.displayGasEstimation(gasEstimationAction.checked); } } Component.onCompleted: { diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index c6283b60e..9decc91ae 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -4,7 +4,6 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 import Qt.labs.settings 1.0 -import QtGraphicalEffects 1.0 import "js/Debugger.js" as Debugger import "js/ErrorLocationFormater.js" as ErrorLocationFormater import "." diff --git a/mix/qml/QBoolTypeView.qml b/mix/qml/QBoolTypeView.qml index c68b4e133..a95c12040 100644 --- a/mix/qml/QBoolTypeView.qml +++ b/mix/qml/QBoolTypeView.qml @@ -6,22 +6,37 @@ Item id: editRoot property string value property string defaultValue - property alias readOnly: !boolCombo.enabled + property bool readOnly: !boolCombo.enabled height: 20 width: 150 + onReadOnlyChanged: { + boolCombo.enabled = !readOnly; + } + + function init() + { + value = value === true ? "1" : value + value = value === false ? "0" : value; + value = value === "true" ? "1" : value + value = value === "false" ? "0" : value; + + if (value === "") + boolCombo.currentIndex = parseInt(defaultValue); + else + boolCombo.currentIndex = parseInt(value); + boolCombo.enabled = !readOnly; + } + Rectangle { anchors.fill: parent ComboBox { - property bool inited: false + property bool inited; Component.onCompleted: { - if (value === "") - currentIndex = parseInt(defaultValue); - else - currentIndex = parseInt(value); - inited = true + init(); + inited = true; } id: boolCombo diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index f8da6dabd..404a524f4 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -507,8 +507,13 @@ Dialog { Button { text: qsTr("OK") onClicked: { - close() - accepted() + if (titleField.text === "") + alertDialog.open() + else + { + close() + accepted() + } } } Button { @@ -517,6 +522,12 @@ Dialog { } } + MessageDialog + { + id: alertDialog + text: qsTr("Please provide a name.") + } + ListModel { id: accountsModel diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index f21c93199..d3062af9e 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -225,6 +225,7 @@ Item { var ctorTr = defaultTransactionItem(); ctorTr.functionId = c; ctorTr.contractId = c; + ctorTr.label = qsTr("Deploy") + " " + ctorTr.contractId; ctorTr.sender = item.accounts[0].secret; item.transactions.push(ctorTr); } @@ -265,6 +266,7 @@ Item { var ctorTr = defaultTransactionItem(); ctorTr.functionId = c; ctorTr.contractId = c; + ctorTr.label = qsTr("Deploy") + " " + ctorTr.contractId; ctorTr.sender = state.accounts[0].secret; state.transactions.push(ctorTr); changed = true; diff --git a/mix/qml/StatesComboBox.qml b/mix/qml/StatesComboBox.qml index 907580ee7..3d2b08eef 100644 --- a/mix/qml/StatesComboBox.qml +++ b/mix/qml/StatesComboBox.qml @@ -23,7 +23,6 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 import QtQuick.Layouts 1.1 -import QtGraphicalEffects 1.0 import org.ethereum.qml.InverseMouseArea 1.0 Rectangle { diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 0c01caeb1..1c1453002 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -3,7 +3,6 @@ import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import QtQuick.Controls.Styles 1.3 import org.ethereum.qml.InverseMouseArea 1.0 -import QtGraphicalEffects 1.0 import "js/ErrorLocationFormater.js" as ErrorLocationFormater import "." diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 4feab2166..cb38ba5ed 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -93,6 +93,9 @@ Column else item.value = getValue(); + if (ptype.category === QSolidityType.Bool) + item.init(); + item.onValueChanged.connect(function() { vals[pname] = item.value; valueChanged(); diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 1437c2562..e7fe22e51 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -94,7 +94,6 @@ Dialog { function loadCtorParameters(contractId) { paramsModel = []; - console.log(contractId); var contract = codeModel.contracts[contractId]; if (contract) { var params = contract.contract.constructor.parameters; @@ -154,7 +153,7 @@ Dialog { if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) { var contract = codeModel.contracts[contractFromToken(recipients.currentValue())]; if (contract) { - var func = contract.contract.functions[functionComboBox.currentIndex]; + var func = contract.contract.functions[functionComboBox.currentIndex - 1]; if (func) { var parameters = func.parameters; for (var p = 0; p < parameters.length; p++) diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index 16ed3e9bf..d31fe0786 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -73,8 +73,30 @@ Item { } } + CheckBox + { + text: qsTr("Mine") + onCheckedChanged: { + mineAction.enabled = !checked; + mineTimer.running = checked; + } + } + + Timer + { + id: mineTimer + repeat: true; + interval: 12000 + running: false + onTriggered: + { + clientModel.mine(); + } + } + Button { + id: mineBtn anchors.rightMargin: 9 anchors.verticalCenter: parent.verticalCenter action: mineAction diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 38f2327b1..054a63bd7 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -83,6 +83,16 @@ Item { editorBrowser.runJavaScript("setFontSize(" + size + ")", function(result) {}); } + function setGasCosts(gasCosts) { + if (initialized && editorBrowser) + editorBrowser.runJavaScript("setGasCosts('" + JSON.stringify(gasCosts) + "')", function(result) {}); + } + + function displayGasEstimation(show) { + if (initialized && editorBrowser) + editorBrowser.runJavaScript("displayGasEstimation('" + show + "')", function(result) {}); + } + Clipboard { id: clipboard @@ -134,20 +144,28 @@ Item { function compilationComplete() { if (editorBrowser) + { editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + parent.displayGasEstimation(gasEstimationAction.checked); + } + + } - function compilationError(error, sourceName) + function compilationError(error, firstLocation, secondLocations) { - if (sourceName !== parent.sourceName) - return; if (!editorBrowser || !error) return; - var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); - if (errorInfo.line && errorInfo.column) - editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); - else - editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + var detail = error.split('\n')[0]; + var reg = detail.match(/:\d+:\d+:/g); + if (reg !== null) + detail = detail.replace(reg[0], ""); + displayErrorAnnotations(detail, firstLocation, secondLocations); + } + + function displayErrorAnnotations(detail, location, secondaryErrors) + { + editorBrowser.runJavaScript("compilationError('" + sourceName + "', '" + JSON.stringify(location) + "', '" + detail + "', '" + JSON.stringify(secondaryErrors) + "')", function(result){}); } Timer diff --git a/mix/qml/html/cm/errorannotation.js b/mix/qml/html/cm/errorannotation.js index 071f0e0d8..33837ed37 100644 --- a/mix/qml/html/cm/errorannotation.js +++ b/mix/qml/html/cm/errorannotation.js @@ -1,42 +1,39 @@ -function ErrorAnnotation(editor, line, column, content) +function ErrorAnnotation(editor, location, content) { + this.location = location; this.opened = false; - this.line = line; - this.column = column; + this.rawContent = content; this.content = content.replace("Contract Error:", ""); this.editor = editor; this.errorMark = null; this.lineWidget = null; this.init(); - this.open(); + if (this.content) + this.open(); } ErrorAnnotation.prototype.init = function() { - var separators = [';', ',', '\\\(', '\\\{', '\\\}', '\\\)', ':']; - var errorPart = editor.getLine(this.line).substring(this.column); - var incrMark = this.column + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; - if (incrMark === this.column) - incrMark = this.column + 1; - this.errorMark = editor.markText({ line: this.line, ch: this.column }, { line: this.line, ch: incrMark }, { className: "CodeMirror-errorannotation", inclusiveRight: true }); + this.errorMark = editor.markText({ line: this.location.start.line, ch: this.location.start.column }, { line: this.location.end.line, ch: this.location.end.column }, { className: "CodeMirror-errorannotation", inclusiveRight: true }); } ErrorAnnotation.prototype.open = function() { - if (this.line) + if (this.location.start.line) { var node = document.createElement("div"); node.id = "annotation" node.innerHTML = this.content; node.className = "CodeMirror-errorannotation-context"; - this.lineWidget = this.editor.addLineWidget(this.line, node, { coverGutter: false }); + this.lineWidget = this.editor.addLineWidget(this.location.start.line, node, { coverGutter: false }); this.opened = true; } } ErrorAnnotation.prototype.close = function() { - this.lineWidget.clear(); + if (this.lineWidget) + this.lineWidget.clear(); this.opened = false; } diff --git a/mix/qml/html/cm/inkpot.css b/mix/qml/html/cm/inkpot.css index c6863e624..6a2d8d63a 100644 --- a/mix/qml/html/cm/inkpot.css +++ b/mix/qml/html/cm/inkpot.css @@ -52,7 +52,7 @@ span.CodeMirror-selectedtext { color: #ffffff !important; } .CodeMirror-errorannotation { border-bottom: 1px solid #DD3330; margin-bottom: 4px; -} + } .CodeMirror-errorannotation-context { font-family: monospace; @@ -63,3 +63,8 @@ span.CodeMirror-selectedtext { color: #ffffff !important; } border-top: solid 2px #063742; } +.CodeMirror-search-field +{ + font-size: 12px; +} + diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index b8cede806..df0859d25 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -189,3 +189,8 @@ view-port } span.CodeMirror-selectedtext { color: #586e75 !important; } + +/* Gas Costs */ +.CodeMirror-gasCosts { + border-bottom: double 1px #2aa198; +} diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index d25fbd091..6af8ff131 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -157,44 +157,49 @@ showWarning = function(content) debugWarning = editor.addLineWidget(0, node, { coverGutter: false, above: true }); } -var annotation = null; +var annotations = []; var compilationCompleteBool = true; -compilationError = function(line, column, content) +compilationError = function(currentSourceName, location, error, secondaryErrors) { compilationCompleteBool = false; - window.setTimeout(function(){ - if (compilationCompleteBool) - return; - line = parseInt(line); - column = parseInt(column); - if (line > 0) - line = line - 1; - if (column > 0) - column = column - 1; - - if (annotation == null) - annotation = new ErrorAnnotation(editor, line, column, content); - else if (annotation.line !== line || annotation.column !== column || annotation.content !== content) - { - annotation.destroy(); - annotation = new ErrorAnnotation(editor, line, column, content); - } - }, 500) + if (compilationCompleteBool) + return; + clearAnnotations(); + location = JSON.parse(location); + if (location.source === currentSourceName) + ensureAnnotation(location, error, "first"); + var lineError = location.start.line + 1; + var errorOrigin = "Source " + location.contractName + " line " + lineError; + secondaryErrors = JSON.parse(secondaryErrors); + for(var i in secondaryErrors) + { + if (secondaryErrors[i].source === currentSourceName) + ensureAnnotation(secondaryErrors[i], errorOrigin, "second"); + } +} + +ensureAnnotation = function(location, error, type) +{ + annotations.push({ "type": type, "annotation": new ErrorAnnotation(editor, location, error)}); +} + +clearAnnotations = function() +{ + for (var k in annotations) + annotations[k].annotation.destroy(); + annotations.length = 0; } compilationComplete = function() { - if (annotation !== null) - { - annotation.destroy(); - annotation = null; - } + clearAnnotations(); compilationCompleteBool = true; } goToCompilationError = function() { - editor.setCursor(annotation.line, annotation.column) + if (annotations.length > 0) + editor.setCursor(annotations[0].annotation.location.start.line, annotations[0].annotation.location.start.column) } setFontSize = function(size) @@ -203,5 +208,112 @@ setFontSize = function(size) editor.refresh(); } +makeGasCostMarker = function(value) { + var marker = document.createElement("div"); + marker.style.color = "#822"; + marker.innerHTML = value; + marker.className = "CodeMirror-errorannotation-context"; + return marker; +}; + +var gasCosts = null; +setGasCosts = function(_gasCosts) +{ + gasCosts = JSON.parse(_gasCosts); + if (showingGasEstimation) + { + displayGasEstimation(false); + displayGasEstimation(true); + } +} + +var showingGasEstimation = false; +var gasMarkText = []; +var gasMarkRef = {}; +displayGasEstimation = function(show) +{ + show = JSON.parse(show); + showingGasEstimation = show; + if (show) + { + var maxGas = 20000; + var step = colorGradient.length / maxGas; // 20000 max gas + clearGasMark(); + gasMarkText = []; + gasMarkRef = {}; + for (var i in gasCosts) + { + if (gasCosts[i].gas !== "0") + { + var color; + var colorIndex = Math.round(step * gasCosts[i].gas); + if (gasCosts[i].isInfinite || colorIndex > colorGradient.length) + color = colorGradient[colorGradient.length - 1]; + else + color = colorGradient[colorIndex]; + var className = "CodeMirror-gasCosts" + i; + var line = editor.posFromIndex(gasCosts[i].start) + gasMarkText.push(editor.markText(line, editor.posFromIndex(gasCosts[i].end), { inclusiveLeft: true, inclusiveRight: true, handleMouseEvents: true, className: className, css: "background-color:" + color })); + gasMarkRef[className] = { line: line.line, value: gasCosts[i] }; + } + } + CodeMirror.on(editor.getWrapperElement(), "mouseover", listenMouseOver); + } + else + { + CodeMirror.off(editor.getWrapperElement(), "mouseover", listenMouseOver); + clearGasMark(); + if (gasAnnotation) + { + gasAnnotation.clear(); + gasAnnotation = null; + } + } +} + +function clearGasMark() +{ + if (gasMarkText) + for (var k in gasMarkText) + gasMarkText[k].clear(); +} + +var gasAnnotation; +function listenMouseOver(e) +{ + var node = e.target || e.srcElement; + if (node) + { + if (node.className && node.className.indexOf("CodeMirror-gasCosts") !== -1) + { + if (gasAnnotation) + gasAnnotation.clear(); + var cl = getGasCostClass(node); + var gasTitle = gasMarkRef[cl].value.isInfinite ? "infinite" : gasMarkRef[cl].value.gas; + gasTitle = gasTitle + " gas"; + gasAnnotation = editor.addLineWidget(gasMarkRef[cl].line + 1, makeGasCostMarker(gasTitle), { coverGutter: false, above: true }); + } + else if (gasAnnotation) + { + gasAnnotation.clear(); + gasAnnotation = null; + } + } +} + +function getGasCostClass(node) +{ + var classes = node.className.split(" "); + for (var k in classes) + { + if (classes[k].indexOf("CodeMirror-gasCosts") !== -1) + return classes[k]; + } + return ""; +} + +// blue => red ["#1515ED", "#1714EA", "#1914E8", "#1B14E6", "#1D14E4", "#1F14E2", "#2214E0", "#2414DE", "#2614DC", "#2813DA", "#2A13D8", "#2D13D6", "#2F13D4", "#3113D2", "#3313D0", "#3513CE", "#3713CC", "#3A12CA", "#3C12C8", "#3E12C6", "#4012C4", "#4212C2", "#4512C0", "#4712BE", "#4912BC", "#4B11BA", "#4D11B8", "#4F11B6", "#5211B4", "#5411B2", "#5611B0", "#5811AE", "#5A11AC", "#5D11AA", "#5F10A7", "#6110A5", "#6310A3", "#6510A1", "#67109F", "#6A109D", "#6C109B", "#6E1099", "#700F97", "#720F95", "#750F93", "#770F91", "#790F8F", "#7B0F8D", "#7D0F8B", "#7F0F89", "#820E87", "#840E85", "#860E83", "#880E81", "#8A0E7F", "#8D0E7D", "#8F0E7B", "#910E79", "#930D77", "#950D75", "#970D73", "#9A0D71", "#9C0D6F", "#9E0D6D", "#A00D6B", "#A20D69", "#A50D67", "#A70C64", "#A90C62", "#AB0C60", "#AD0C5E", "#AF0C5C", "#B20C5A", "#B40C58", "#B60C56", "#B80B54", "#BA0B52", "#BD0B50", "#BF0B4E", "#C10B4C", "#C30B4A", "#C50B48", "#C70B46", "#CA0A44", "#CC0A42", "#CE0A40", "#D00A3E", "#D20A3C", "#D50A3A", "#D70A38", "#D90A36", "#DB0934", "#DD0932", "#DF0930", "#E2092E", "#E4092C", "#E6092A", "#E80928", "#EA0926", "#ED0924"] +/* green => red */ var colorGradient = ["#429C27", "#439A26", "#449926", "#469726", "#479626", "#489525", "#4A9325", "#4B9225", "#4D9025", "#4E8F25", "#4F8E24", "#518C24", "#528B24", "#548924", "#558824", "#568723", "#588523", "#598423", "#5B8223", "#5C8122", "#5D8022", "#5F7E22", "#607D22", "#627B22", "#637A21", "#647921", "#667721", "#677621", "#697421", "#6A7320", "#6B7220", "#6D7020", "#6E6F20", "#706E20", "#716C1F", "#726B1F", "#74691F", "#75681F", "#76671E", "#78651E", "#79641E", "#7B621E", "#7C611E", "#7D601D", "#7F5E1D", "#805D1D", "#825B1D", "#835A1D", "#84591C", "#86571C", "#87561C", "#89541C", "#8A531B", "#8B521B", "#8D501B", "#8E4F1B", "#904D1B", "#914C1A", "#924B1A", "#94491A", "#95481A", "#97461A", "#984519", "#994419", "#9B4219", "#9C4119", "#9E4019", "#9F3E18", "#A03D18", "#A23B18", "#A33A18", "#A43917", "#A63717", "#A73617", "#A93417", "#AA3317", "#AB3216", "#AD3016", "#AE2F16", "#B02D16", "#B12C16", "#B22B15", "#B42915", "#B52815", "#B72615", "#B82514", "#B92414", "#BB2214", "#BC2114", "#BE1F14", "#BF1E13", "#C01D13", "#C21B13", "#C31A13", "#C51813", "#C61712", "#C71612", "#C91412", "#CA1312", "#CC1212"] + editor.setOption("extraKeys", extraKeys); diff --git a/neth/main.cpp b/neth/main.cpp index 7ce64cba5..a6e661d2e 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include diff --git a/rlp/main.cpp b/rlp/main.cpp index 5f2f7f358..3924f9c44 100644 --- a/rlp/main.cpp +++ b/rlp/main.cpp @@ -25,7 +25,7 @@ #include "../test/JsonSpiritHeaders.h" #include #include -#include +#include using namespace std; using namespace dev; namespace js = json_spirit; diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index aada83041..476d1ecf9 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -327,7 +327,8 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta void ImportTest::exportTest(bytes const& _output, State const& _statePost) { // export output - m_TestObject["out"] = toHex(_output, 2, HexPrefix::Add); + + m_TestObject["out"] = _output.size() > 4096 ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add); // export logs m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); @@ -489,7 +490,11 @@ LogEntries importLog(json_spirit::mArray& _a) void checkOutput(bytes const& _output, json_spirit::mObject& _o) { int j = 0; - if (_o["out"].type() == json_spirit::array_type) + + if (_o["out"].get_str().find("#") == 0) + BOOST_CHECK((u256)_output.size() == toInt(_o["out"].get_str().substr(1))); + + else if (_o["out"].type() == json_spirit::array_type) for (auto const& d: _o["out"].get_array()) { BOOST_CHECK_MESSAGE(_output[j] == toInt(d), "Output byte [" << j << "] different!"); diff --git a/test/deprecated/main.cpp b/test/deprecated/main.cpp index 6ec8885b3..47e96f337 100644 --- a/test/deprecated/main.cpp +++ b/test/deprecated/main.cpp @@ -20,10 +20,6 @@ * Main test functions. */ -#include -#include "TrieHash.h" -#include "MemTrie.h" - #include int trieTest(); diff --git a/test/libdevcrypto/MemTrie.cpp b/test/libdevcrypto/MemTrie.cpp index ab5a13b60..4507d1d80 100644 --- a/test/libdevcrypto/MemTrie.cpp +++ b/test/libdevcrypto/MemTrie.cpp @@ -21,8 +21,8 @@ #include "MemTrie.h" -#include -#include +#include +#include #include using namespace std; using namespace dev; diff --git a/test/libdevcrypto/SecretStore.cpp b/test/libdevcrypto/SecretStore.cpp new file mode 100644 index 000000000..1f927db5d --- /dev/null +++ b/test/libdevcrypto/SecretStore.cpp @@ -0,0 +1,64 @@ +/* + 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 SecretStore.cpp + * @author Gav Wood + * @date 2015 + * Secret store test functions. + */ + +#include +#include +#include +#include "../JsonSpiritHeaders.h" +#include +#include +#include +#include +#include "MemTrie.h" +#include "../TestHelper.h" +using namespace std; +using namespace dev; + +namespace js = json_spirit; + +BOOST_AUTO_TEST_SUITE(KeyStore) + +BOOST_AUTO_TEST_CASE(basic_tests) +{ + string testPath = test::getTestPath(); + + testPath += "/KeyStoreTests"; + + cnote << "Testing Key Store..."; + js::mValue v; + string s = asString(contents(testPath + "/basic_tests.json")); + BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'KeyStoreTests/basic_tests.json' is empty. Have you cloned the 'tests' repo branch develop?"); + js::read_string(s, v); + for (auto& i: v.get_obj()) + { + cnote << i.first; + js::mObject& o = i.second.get_obj(); + SecretStore store("."); + h128 u = store.readKeyContent(js::write_string(o["json"], false)); + cdebug << "read uuid" << u; + bytes s = store.secret(u, [&](){ return o["password"].get_str(); }); + cdebug << "got secret" << toHex(s); + BOOST_REQUIRE_EQUAL(toHex(s), o["priv"].get_str()); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libdevcrypto/crypto.cpp b/test/libdevcrypto/crypto.cpp index 88ff98965..497887145 100644 --- a/test/libdevcrypto/crypto.cpp +++ b/test/libdevcrypto/crypto.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -45,13 +45,19 @@ 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(emptySHA3Types) +BOOST_AUTO_TEST_CASE(sha3general) { - h256 emptyListSHA3(fromHex("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")); - BOOST_REQUIRE_EQUAL(emptyListSHA3, EmptyListSHA3); + BOOST_REQUIRE_EQUAL(sha3(""), h256("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + BOOST_REQUIRE_EQUAL(sha3("hello"), h256("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8")); +} +BOOST_AUTO_TEST_CASE(emptySHA3Types) +{ h256 emptySHA3(fromHex("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); BOOST_REQUIRE_EQUAL(emptySHA3, EmptySHA3); + + h256 emptyListSHA3(fromHex("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")); + BOOST_REQUIRE_EQUAL(emptyListSHA3, EmptyListSHA3); } BOOST_AUTO_TEST_CASE(cryptopp_patch) @@ -805,7 +811,7 @@ int cryptoTest() std::string hmsg = sha3(t.rlp(false), false); cout << "SHA256(RLP(TX w/o SIG)): 0x" << toHex(hmsg) << endl; - bytes privkey = sha3Bytes("123"); + bytes privkey = sha3("123").asBytes(); { bytes pubkey(65); diff --git a/test/libdevcrypto/hexPrefix.cpp b/test/libdevcrypto/hexPrefix.cpp index c72f24535..223f1ac7b 100644 --- a/test/libdevcrypto/hexPrefix.cpp +++ b/test/libdevcrypto/hexPrefix.cpp @@ -27,7 +27,7 @@ #include "../JsonSpiritHeaders.h" #include #include -#include +#include #include "../TestHelper.h" using namespace std; diff --git a/test/libdevcrypto/trie.cpp b/test/libdevcrypto/trie.cpp index ecfc515aa..b5d8662dc 100644 --- a/test/libdevcrypto/trie.cpp +++ b/test/libdevcrypto/trie.cpp @@ -27,8 +27,8 @@ #include "../JsonSpiritHeaders.h" #include -#include -#include +#include +#include #include "MemTrie.h" #include "../TestHelper.h" @@ -294,15 +294,25 @@ BOOST_AUTO_TEST_CASE(trie_tests_ordered) } } -inline h256 stringMapHash256(StringMap const& _s) +h256 stringMapHash256(StringMap const& _s) { - return hash256(_s); + BytesMap bytesMap; + for (auto const& _v: _s) + bytesMap.insert(std::make_pair(bytes(_v.first.begin(), _v.first.end()), bytes(_v.second.begin(), _v.second.end()))); + return hash256(bytesMap); +} + +bytes stringMapRlp256(StringMap const& _s) +{ + BytesMap bytesMap; + for (auto const& _v: _s) + bytesMap.insert(std::make_pair(bytes(_v.first.begin(), _v.first.end()), bytes(_v.second.begin(), _v.second.end()))); + return rlp256(bytesMap); } BOOST_AUTO_TEST_CASE(moreTrieTests) { cnote << "Testing Trie more..."; -#if 0 // More tests... { MemoryDB m; @@ -311,7 +321,7 @@ BOOST_AUTO_TEST_CASE(moreTrieTests) cout << t; cout << m; cout << t.root() << endl; - cout << hash256(StringMap()) << endl; + cout << stringMapHash256(StringMap()) << endl; t.insert(string("tesz"), string("test")); cout << t; @@ -336,7 +346,7 @@ BOOST_AUTO_TEST_CASE(moreTrieTests) t.remove(string("test")); cout << m; cout << t.root() << endl; - cout << hash256(StringMap()) << endl; + cout << stringMapHash256(StringMap()) << endl; } { MemoryDB m; @@ -348,20 +358,23 @@ BOOST_AUTO_TEST_CASE(moreTrieTests) cout << m; cout << t.root() << endl; cout << stringMapHash256({{"b", "B"}, {"a", "A"}}) << endl; - cout << RLP(rlp256({{"b", "B"}, {"a", "A"}})) << endl; + bytes r(stringMapRlp256({{"b", "B"}, {"a", "A"}})); + cout << RLP(r) << endl; } { MemTrie t; t.insert("dog", "puppy"); cout << hex << t.hash256() << endl; - cout << RLP(t.rlp()) << endl; + bytes r(t.rlp()); + cout << RLP(r) << endl; } { MemTrie t; t.insert("bed", "d"); t.insert("be", "e"); cout << hex << t.hash256() << endl; - cout << RLP(t.rlp()) << endl; + bytes r(t.rlp()); + cout << RLP(r) << endl; } { cout << hex << stringMapHash256({{"dog", "puppy"}, {"doe", "reindeer"}}) << endl; @@ -369,10 +382,10 @@ BOOST_AUTO_TEST_CASE(moreTrieTests) t.insert("dog", "puppy"); t.insert("doe", "reindeer"); cout << hex << t.hash256() << endl; - cout << RLP(t.rlp()) << endl; + bytes r(t.rlp()); + cout << RLP(r) << endl; cout << toHex(t.rlp()) << endl; } -#endif { MemoryDB m; EnforceRefs r(m, true); @@ -387,16 +400,16 @@ BOOST_AUTO_TEST_CASE(moreTrieTests) t.insert(a, b); s[a] = b; - /*cout << endl << "-------------------------------" << endl; + cout << endl << "-------------------------------" << endl; cout << a << " -> " << b << endl; cout << d; cout << m; cout << d.root() << endl; - cout << hash256(s) << endl;*/ + cout << stringMapHash256(s) << endl; BOOST_REQUIRE(d.check(true)); - BOOST_REQUIRE_EQUAL(t.hash256(), hash256(s)); - BOOST_REQUIRE_EQUAL(d.root(), hash256(s)); + BOOST_REQUIRE_EQUAL(t.hash256(), stringMapHash256(s)); + BOOST_REQUIRE_EQUAL(d.root(), stringMapHash256(s)); for (auto const& i: s) { (void)i; @@ -421,8 +434,8 @@ BOOST_AUTO_TEST_CASE(moreTrieTests) BOOST_REQUIRE(d.check(true)); BOOST_REQUIRE(t.at(a).empty()); BOOST_REQUIRE(d.at(string(a)).empty()); - BOOST_REQUIRE_EQUAL(t.hash256(), hash256(s)); - BOOST_REQUIRE_EQUAL(d.root(), hash256(s)); + BOOST_REQUIRE_EQUAL(t.hash256(), stringMapHash256(s)); + BOOST_REQUIRE_EQUAL(d.root(), stringMapHash256(s)); for (auto const& i: s) { (void)i; @@ -493,7 +506,6 @@ BOOST_AUTO_TEST_CASE(trieLowerBound) BOOST_AUTO_TEST_CASE(trieStess) { cnote << "Stress-testing Trie..."; - if (0) { MemoryDB m; MemoryDB dm; @@ -512,8 +524,8 @@ BOOST_AUTO_TEST_CASE(trieStess) m[k] = v; t.insert(k, v); d.insert(k, v); - BOOST_REQUIRE_EQUAL(hash256(m), t.hash256()); - BOOST_REQUIRE_EQUAL(hash256(m), d.root()); + BOOST_REQUIRE_EQUAL(stringMapHash256(m), t.hash256()); + BOOST_REQUIRE_EQUAL(stringMapHash256(m), d.root()); BOOST_REQUIRE(d.check(true)); } while (!m.empty()) @@ -557,8 +569,8 @@ BOOST_AUTO_TEST_CASE(trieStess) cwarn << "Good?" << d2.root(); } BOOST_REQUIRE(d.check(true)); - BOOST_REQUIRE_EQUAL(hash256(m), t.hash256()); - BOOST_REQUIRE_EQUAL(hash256(m), d.root()); + BOOST_REQUIRE_EQUAL(stringMapHash256(m), t.hash256()); + BOOST_REQUIRE_EQUAL(stringMapHash256(m), d.root()); } } } diff --git a/test/libethereum/StateTestsFiller/stMemoryStressTestFiller.json b/test/libethereum/StateTestsFiller/stMemoryStressTestFiller.json index 3893223eb..b5a531520 100644 --- a/test/libethereum/StateTestsFiller/stMemoryStressTestFiller.json +++ b/test/libethereum/StateTestsFiller/stMemoryStressTestFiller.json @@ -58,7 +58,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : "0", - "code" : "{ (RETURN 0 4294967297) } ", + "code" : "{ (RETURN 0 4294967295) } ", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -77,7 +77,7 @@ "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "data" : "" } - }, + }, "mload32bitBound_return2": { "env" : { @@ -98,7 +98,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : "0", - "code" : "{[ 0 ] 1 (RETURN 0 4294967296) } ", + "code" : "{[ 0 ] 1 (RETURN 0 4294967295) } ", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index 191b94cc3..4e98b4036 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include #include diff --git a/test/libethereum/state.cpp b/test/libethereum/state.cpp index fe0f10ca3..ed5cf14ac 100644 --- a/test/libethereum/state.cpp +++ b/test/libethereum/state.cpp @@ -176,12 +176,10 @@ BOOST_AUTO_TEST_CASE(stMemoryStressTest) dev::test::executeTests("stMemoryStressTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); } -#if ETH_SOLIDITY BOOST_AUTO_TEST_CASE(stSolidityTest) { dev::test::executeTests("stSolidityTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); } -#endif BOOST_AUTO_TEST_CASE(stMemoryTest) { diff --git a/test/libevm/vm.cpp b/test/libevm/vm.cpp index 10670bfe3..7b589d238 100644 --- a/test/libevm/vm.cpp +++ b/test/libevm/vm.cpp @@ -44,13 +44,10 @@ h160 FakeExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _init, OnOpF return na; } -bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256& io_gas, bytesRef _out, OnOpFunc const&, Address _myAddressOverride, Address _codeAddressOverride) +bool FakeExtVM::call(CallParameters& _p) { - Transaction t(_value, gasPrice, io_gas, _receiveAddress, _data.toVector()); + Transaction t(_p.value, gasPrice, _p.gas, _p.receiveAddress, _p.data.toVector()); callcreates.push_back(t); - (void)_out; - (void)_myAddressOverride; - (void)_codeAddressOverride; return true; } @@ -393,7 +390,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) } o["callcreates"] = fev.exportCallCreates(); - o["out"] = toHex(output, 2, HexPrefix::Add); + o["out"] = output.size() > 4096 ? "#" + toString(output.size()) : toHex(output, 2, HexPrefix::Add); o["gas"] = toCompactHex(gas, HexPrefix::Add, 1); o["logs"] = exportLog(fev.sub.logs); } diff --git a/test/libevm/vm.h b/test/libevm/vm.h index dff89d98d..18fa1ca25 100644 --- a/test/libevm/vm.h +++ b/test/libevm/vm.h @@ -59,7 +59,7 @@ public: virtual void suicide(Address _a) override { std::get<0>(addresses[_a]) += std::get<0>(addresses[myAddress]); addresses.erase(myAddress); } virtual bytes const& codeAt(Address _a) override { return std::get<3>(addresses[_a]); } virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _init, eth::OnOpFunc const&) override; - virtual bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256& io_gas, bytesRef _out, eth::OnOpFunc const&, Address, Address) override; + virtual bool call(eth::CallParameters&) override; void setTransaction(Address _caller, u256 _value, u256 _gasPrice, bytes const& _data); void setContract(Address _myAddress, u256 _myBalance, u256 _myNonce, std::map const& _storage, bytes const& _code); void set(Address _a, u256 _myBalance, u256 _myNonce, std::map const& _storage, bytes const& _code); diff --git a/test/libp2p/rlpx.cpp b/test/libp2p/rlpx.cpp index 6a86652fb..620ddd952 100644 --- a/test/libp2p/rlpx.cpp +++ b/test/libp2p/rlpx.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/test/GasMeter.cpp b/test/libsolidity/GasMeter.cpp similarity index 66% rename from test/GasMeter.cpp rename to test/libsolidity/GasMeter.cpp index 0ffe41712..43eb3f956 100644 --- a/test/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -21,6 +21,8 @@ */ #include +#include +#include #include #include #include @@ -55,8 +57,21 @@ public: ); } + void testCreationTimeGas(string const& _sourceCode, string const& _contractName = "") + { + compileAndRun(_sourceCode); + auto state = make_shared(); + GasMeter meter(state); + GasMeter::GasConsumption gas; + for (AssemblyItem const& item: *m_compiler.getAssemblyItems(_contractName)) + gas += meter.estimateMax(item); + u256 bytecodeSize(m_compiler.getRuntimeBytecode(_contractName).size()); + gas += bytecodeSize * c_createDataGas; + BOOST_REQUIRE(!gas.isInfinite); + BOOST_CHECK(gas.value == m_gasUsed); + } + protected: - dev::solidity::CompilerStack m_compiler; map m_gasCosts; }; @@ -91,6 +106,49 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) } } +BOOST_AUTO_TEST_CASE(simple_contract) +{ + // Tests a simple "deploy contract" code without constructor. The actual contract is not relevant. + char const* sourceCode = R"( + contract test { + bytes32 public shaValue; + function f(uint a) { + shaValue = sha3(a); + } + } + )"; + testCreationTimeGas(sourceCode); +} + +BOOST_AUTO_TEST_CASE(store_sha3) +{ + char const* sourceCode = R"( + contract test { + bytes32 public shaValue; + function test(uint a) { + shaValue = sha3(a); + } + } + )"; + testCreationTimeGas(sourceCode); +} + +BOOST_AUTO_TEST_CASE(updating_store) +{ + char const* sourceCode = R"( + contract test { + uint data; + uint data2; + function test() { + data = 1; + data = 2; + data2 = 0; + } + } + )"; + testCreationTimeGas(sourceCode); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 6713382fa..503615a5a 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include using namespace std; @@ -1501,9 +1501,7 @@ BOOST_AUTO_TEST_CASE(sha256) compileAndRun(sourceCode); auto f = [&](u256 const& _input) -> u256 { - h256 ret; - dev::sha256(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); - return ret; + return dev::sha256(dev::ref(toBigEndian(_input))); }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -1520,9 +1518,7 @@ BOOST_AUTO_TEST_CASE(ripemd) compileAndRun(sourceCode); auto f = [&](u256 const& _input) -> u256 { - h256 ret; - dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); - return u256(ret); + return h256(dev::ripemd160(h256(_input).ref()), h256::AlignLeft); // This should be aligned right. i guess it's fixed elsewhere? }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -4027,6 +4023,92 @@ BOOST_AUTO_TEST_CASE(overwriting_inheritance) BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(6)); } +BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct) +{ + char const* sourceCode = R"( + contract test { + struct testStruct + { + uint m_value; + } + testStruct data1; + testStruct data2; + testStruct data3; + function test() + { + data1.m_value = 2; + } + function assign() returns (uint ret_local, uint ret_global, uint ret_global3, uint ret_global1) + { + testStruct x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2 + data2 = data1; // should copy data. data2.m_value == 2 + + ret_local = x.m_value; // = 2 + ret_global = data2.m_value; // = 2 + + x.m_value = 3; + data3 = x; //should copy the data. data3.m_value == 3 + ret_global3 = data3.m_value; // = 3 + ret_global1 = data1.m_value; // = 3. Changed due to the assignment to x.m_value + } + } + )"; + compileAndRun(sourceCode, 0, "test"); + BOOST_CHECK(callContractFunction("assign()") == encodeArgs(2, 2, 3, 3)); +} + +BOOST_AUTO_TEST_CASE(struct_delete_member) +{ + char const* sourceCode = R"( + contract test { + struct testStruct + { + uint m_value; + } + testStruct data1; + function test() + { + data1.m_value = 2; + } + function deleteMember() returns (uint ret_value) + { + testStruct x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0 + x.m_value = 4; + delete x.m_value; + ret_value = data1.m_value; + } + } + )"; + compileAndRun(sourceCode, 0, "test"); + auto res = callContractFunction("deleteMember()"); + BOOST_CHECK(callContractFunction("deleteMember()") == encodeArgs(0)); +} + +BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping) +{ + char const* sourceCode = R"( + contract test { + struct testStruct + { + uint m_value; + } + mapping (uint => testStruct) campaigns; + + function test() + { + campaigns[0].m_value = 2; + } + function deleteIt() returns (uint) + { + delete campaigns[0]; + return campaigns[0].m_value; + } + } + )"; + compileAndRun(sourceCode, 0, "test"); + auto res = callContractFunction("deleteIt()"); + BOOST_CHECK(callContractFunction("deleteIt()") == encodeArgs(0)); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 4ec7b8bda..c52bbf9de 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/libsolidity/solidityExecutionFramework.h b/test/libsolidity/solidityExecutionFramework.h index f76465f23..fa25fb12c 100644 --- a/test/libsolidity/solidityExecutionFramework.h +++ b/test/libsolidity/solidityExecutionFramework.h @@ -44,11 +44,11 @@ public: bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") { - dev::solidity::CompilerStack compiler(m_addStandardSources); - compiler.addSource("", _sourceCode); - ETH_TEST_REQUIRE_NO_THROW(compiler.compile(m_optimize), "Compiling contract failed"); + m_compiler.reset(false, m_addStandardSources); + m_compiler.addSource("", _sourceCode); + ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed"); - bytes code = compiler.getBytecode(_contractName); + bytes code = m_compiler.getBytecode(_contractName); sendMessage(code, true, _value); BOOST_REQUIRE(!m_output.empty()); return m_output; @@ -160,12 +160,14 @@ protected: BOOST_REQUIRE(executive.go()); m_state.noteSending(m_sender); executive.finalize(); + m_gasUsed = executive.gasUsed(); m_output = executive.out().toVector(); m_logs = executive.logs(); } bool m_optimize = false; bool m_addStandardSources = false; + dev::solidity::CompilerStack m_compiler; Address m_sender; Address m_contractAddress; eth::State m_state; @@ -173,6 +175,7 @@ protected: u256 const m_gas = 100000000; bytes m_output; eth::LogEntries m_logs; + u256 m_gasUsed; }; } diff --git a/third/MainWin.cpp b/third/MainWin.cpp index 12625ffbc..3cfc016e3 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include