Browse Source

Merge branch 'develop' into no_warnings

cl-refactor
Paweł Bylica 10 years ago
parent
commit
9a8408eacc
  1. 23
      CMakeLists.txt
  2. 3
      alethzero/CMakeLists.txt
  3. 123
      alethzero/GetPassword.ui
  4. 20
      alethzero/Main.ui
  5. 144
      alethzero/MainWin.cpp
  6. 7
      alethzero/MainWin.h
  7. 30
      alethzero/Transact.cpp
  8. 11
      alethzero/Transact.h
  9. 3
      eth/main.cpp
  10. 33
      ethkey/CMakeLists.txt
  11. 413
      ethkey/KeyAux.h
  12. 84
      ethkey/main.cpp
  13. 4
      ethminer/MinerAux.h
  14. 2
      evmjit/CMakeLists.txt
  15. 4
      evmjit/include/evmjit/JIT.h
  16. 25
      evmjit/libevmjit-cpp/Env.cpp
  17. 6
      evmjit/libevmjit-cpp/JitVM.h
  18. 2
      evmjit/libevmjit/ExecutionEngine.cpp
  19. 8
      evmjit/libevmjit/JIT.cpp
  20. 22
      exp/main.cpp
  21. 3
      libdevcore/Common.h
  22. 9
      libdevcore/CommonData.h
  23. 1
      libdevcrypto/CMakeLists.txt
  24. 22
      libdevcrypto/Common.cpp
  25. 15
      libdevcrypto/Common.h
  26. 213
      libdevcrypto/SecretStore.cpp
  27. 31
      libdevcrypto/SecretStore.h
  28. 2
      libethash-cl/ethash_cl_miner.cpp
  29. 2
      libethash/internal.c
  30. 8
      libethcore/Ethash.cpp
  31. 6
      libethcore/EthashAux.cpp
  32. 2
      libethcore/ICAP.h
  33. 78
      libethcore/KeyManager.cpp
  34. 26
      libethcore/KeyManager.h
  35. 2
      libethcore/Miner.h
  36. 2
      libethereum/AccountDiff.h
  37. 4
      libevm/VM.cpp
  38. 1
      libevmasm/Assembly.cpp
  39. 6
      libevmasm/AssemblyItem.h
  40. 132
      libevmasm/GasMeter.cpp
  41. 27
      libevmasm/GasMeter.h
  42. 13
      libevmasm/KnownState.cpp
  43. 4
      libevmasm/KnownState.h
  44. 24
      libscrypt/CMakeLists.txt
  45. 9
      libscrypt/LICENSE
  46. 313
      libscrypt/b64.c
  47. 10
      libscrypt/b64.h
  48. 73
      libscrypt/crypto-mcf.c
  49. 0
      libscrypt/crypto-scrypt-saltgen.c
  50. 100
      libscrypt/crypto_scrypt-check.c
  51. 0
      libscrypt/crypto_scrypt-hash.c
  52. 35
      libscrypt/crypto_scrypt-hexconvert.c
  53. 9
      libscrypt/crypto_scrypt-hexconvert.h
  54. 342
      libscrypt/crypto_scrypt-nosse.c
  55. 56
      libscrypt/libscrypt.h
  56. 8
      libscrypt/libscrypt.version
  57. 411
      libscrypt/sha256.c
  58. 70
      libscrypt/sha256.h
  59. 26
      libscrypt/slowequals.c
  60. 5
      libscrypt/slowequals.h
  61. 144
      libscrypt/sysendian.h
  62. 20
      libsolidity/ASTVisitor.h
  63. 38
      libsolidity/CompilerStack.cpp
  64. 5
      libsolidity/CompilerStack.h
  65. 28
      libsolidity/InterfaceHandler.cpp
  66. 39
      libsolidity/StructuralGasEstimator.cpp
  67. 4
      libsolidity/StructuralGasEstimator.h
  68. 22
      libsolidity/Token.h
  69. 3
      libweb3jsonrpc/AccountHolder.cpp
  70. 15
      mix/ClientModel.cpp
  71. 123
      mix/CodeModel.cpp
  72. 5
      mix/CodeModel.h
  73. 4
      mix/qml/CodeEditorView.qml
  74. 29
      mix/qml/QBoolTypeView.qml
  75. 15
      mix/qml/StateDialog.qml
  76. 2
      mix/qml/StateListModel.qml
  77. 3
      mix/qml/StructView.qml
  78. 3
      mix/qml/TransactionDialog.qml
  79. 22
      mix/qml/TransactionLog.qml
  80. 19
      mix/qml/WebCodeEditor.qml
  81. 23
      mix/qml/html/cm/errorannotation.js
  82. 7
      mix/qml/html/cm/inkpot.css
  83. 1
      mix/qml/html/cm/solarized.css
  84. 57
      mix/qml/html/codeeditor.js
  85. 9
      test/TestHelper.cpp
  86. 64
      test/libdevcrypto/SecretStore.cpp
  87. 4
      test/libethereum/StateTestsFiller/stMemoryStressTestFiller.json
  88. 2
      test/libethereum/state.cpp
  89. 2
      test/libevm/vm.cpp
  90. 60
      test/libsolidity/GasMeter.cpp
  91. 86
      test/libsolidity/SolidityEndToEndTest.cpp
  92. 11
      test/libsolidity/solidityExecutionFramework.h

23
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) option(PROFILING "Build in support for profiling" OFF)
set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).") 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(SOLIDITY "Build the Solidity language components" ON)
option(SERPENT "Build the Serpent language components" ON) option(SERPENT "Build the Serpent language components" ON)
option(TOOLS "Build the tools components" ON) option(TOOLS "Build the tools components" ON)
@ -251,6 +252,17 @@ elseif (BUNDLE STREQUAL "user")
set(NCURSES ${DECENT_PLATFORM}) set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON) set(TOOLS ON)
set(TESTS OFF) 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") elseif (BUNDLE STREQUAL "miner")
set(SERPENT OFF) set(SERPENT OFF)
set(SOLIDITY OFF) set(SOLIDITY OFF)
@ -259,6 +271,7 @@ elseif (BUNDLE STREQUAL "miner")
set(NCURSES OFF) set(NCURSES OFF)
set(TOOLS OFF) set(TOOLS OFF)
set(TESTS OFF) set(TESTS OFF)
set(ETHKEY OFF)
set(MINER ON) set(MINER ON)
set(ETHASHCL ON) set(ETHASHCL ON)
endif () endif ()
@ -296,6 +309,7 @@ message("-- JSONRPC JSON-RPC support ${JSONRPC}
message("-- USENPM Javascript source building ${USENPM}") message("-- USENPM Javascript source building ${USENPM}")
message("------------------------------------------------------------- components") message("------------------------------------------------------------- components")
message("-- MINER Build miner ${MINER}") message("-- MINER Build miner ${MINER}")
message("-- ETHKEY Build wallet tools ${ETHKEY}")
message("-- TOOLS Build basic tools ${TOOLS}") message("-- TOOLS Build basic tools ${TOOLS}")
message("-- SOLIDITY Build Solidity language components ${SOLIDITY}") message("-- SOLIDITY Build Solidity language components ${SOLIDITY}")
message("-- SERPENT Build Serpent language components ${SERPENT}") message("-- SERPENT Build Serpent language components ${SERPENT}")
@ -364,6 +378,7 @@ if (JSCONSOLE)
endif () endif ()
add_subdirectory(secp256k1) add_subdirectory(secp256k1)
add_subdirectory(libscrypt)
add_subdirectory(libdevcrypto) add_subdirectory(libdevcrypto)
if (GENERAL) if (GENERAL)
@ -386,10 +401,14 @@ if (GENERAL)
add_subdirectory(libwebthree) add_subdirectory(libwebthree)
endif () endif ()
if (MINER) if (MINER OR TOOLS)
add_subdirectory(ethminer) add_subdirectory(ethminer)
endif () endif ()
if (ETHKEY OR TOOLS)
add_subdirectory(ethkey)
endif ()
if (TESTS) if (TESTS)
add_subdirectory(libtestutils) add_subdirectory(libtestutils)
add_subdirectory(test) add_subdirectory(test)

3
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_Debugger.h Debugger.ui)
qt5_wrap_ui(ui_Transact.h Transact.ui) qt5_wrap_ui(ui_Transact.h Transact.ui)
qt5_wrap_ui(ui_ExportState.h ExportState.ui) qt5_wrap_ui(ui_ExportState.h ExportState.ui)
qt5_wrap_ui(ui_GetPassword.h GetPassword.ui)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
@ -35,7 +36,7 @@ endif ()
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake # eth_add_executable is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE} eth_add_executable(${EXECUTABLE}
ICON alethzero 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 WIN_RESOURCES alethzero.rc
) )

123
alethzero/GetPassword.ui

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GetPassword</class>
<widget class="QDialog" name="GetPassword">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>187</height>
</rect>
</property>
<property name="windowTitle">
<string>Enter Password</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="entry">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>GetPassword</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>GetPassword</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

20
alethzero/Main.ui

@ -157,7 +157,10 @@
<addaction name="newAccount"/> <addaction name="newAccount"/>
<addaction name="importKey"/> <addaction name="importKey"/>
<addaction name="importKeyFile"/> <addaction name="importKeyFile"/>
<addaction name="claimPresale"/>
<addaction name="exportKey"/> <addaction name="exportKey"/>
<addaction name="reencryptAll"/>
<addaction name="reencryptKey"/>
<addaction name="killAccount"/> <addaction name="killAccount"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="loadJS"/> <addaction name="loadJS"/>
@ -1656,7 +1659,7 @@ font-size: 14pt</string>
<string>&amp;Enable Local Addresses</string> <string>&amp;Enable Local Addresses</string>
</property> </property>
</action> </action>
<action name="importKeyFile"> <action name="claimPresale">
<property name="enabled"> <property name="enabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -1745,6 +1748,21 @@ font-size: 14pt</string>
<string>Co&amp;nfirm Transactions</string> <string>Co&amp;nfirm Transactions</string>
</property> </property>
</action> </action>
<action name="importKeyFile">
<property name="text">
<string>Import &amp;Secret Key File...</string>
</property>
</action>
<action name="reencryptKey">
<property name="text">
<string>&amp;Re-Encrypt Key</string>
</property>
</action>
<action name="reencryptAll">
<property name="text">
<string>Re-Encrypt All Keys...</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

144
alethzero/MainWin.cpp

@ -75,6 +75,7 @@
#include "WebPage.h" #include "WebPage.h"
#include "ExportState.h" #include "ExportState.h"
#include "ui_Main.h" #include "ui_Main.h"
#include "ui_GetPassword.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
@ -467,7 +468,9 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered() void Main::on_newTransaction_triggered()
{ {
m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); 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() void Main::on_loadJS_triggered()
@ -698,12 +701,17 @@ Secret Main::retrieveSecret(Address const& _a) const
auto info = m_keyManager.accountDetails()[_a]; auto info = m_keyManager.accountDetails()[_a];
while (true) while (true)
{ {
if (Secret s = m_keyManager.secret(_a, [&](){ Secret s = m_keyManager.secret(_a, [&](){
return QInputDialog::getText(const_cast<Main*>(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(); 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; return s;
else if (QMessageBox::warning(const_cast<Main*>(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(); 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; QString password;
while (true) 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()); QString confirm = QInputDialog::getText(nullptr, QString::fromStdString(_title), "Confirm this password by typing it again", QLineEdit::Password, QString());
if (password == confirm) if (password == confirm)
break; break;
QMessageBox::warning(nullptr, QString::fromStdString(_title), "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok); 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(); 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"); 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) 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!"); bool ok;
std::string hint = QInputDialog::getText(this, "Import Account Key", "Enter a hint to help you remember this password.").toStdString(); 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); m_keyManager.import(k.secret(), s.toStdString(), password, hint);
} }
else else
@ -813,6 +839,33 @@ void Main::on_importKey_triggered()
} }
void Main::on_importKeyFile_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 (*)"); QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)");
try try
@ -1976,8 +2029,11 @@ void Main::on_newAccount_triggered()
QString s = QInputDialog::getText(this, "Create Account", "Enter this account's name"); 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) 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!"); bool ok = false;
std::string hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString(); 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); m_keyManager.import(p.secret(), s.toStdString(), password, hint);
} }
else else
@ -1989,15 +2045,14 @@ void Main::on_killAccount_triggered()
{ {
if (ui->ourAccounts->currentRow() >= 0) 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); Address h((byte const*)hba.data(), Address::ConstructFromPointer);
auto k = m_keyManager.accountDetails()[h]; auto k = m_keyManager.accountDetails()[h];
if ( QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + k.first + "?!"),
ethereum()->balanceAt(h) != 0 && 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"
QMessageBox::critical(this, QString::fromStdString("Kill Account " + k.first + "?!"), "Are you sure you want to continue? \r\n If so, type 'YES' to confirm."),
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" QLineEdit::Normal, "NO");
"Are you sure you want to continue?"), if (s != "YES")
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
return; return;
m_keyManager.kill(h); m_keyManager.kill(h);
if (m_keyManager.accounts().empty()) 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() void Main::on_go_triggered()
{ {
if (!ui->net->isChecked()) if (!ui->net->isChecked())

7
alethzero/MainWin.h

@ -33,9 +33,9 @@
#include <QtWidgets/QMainWindow> #include <QtWidgets/QMainWindow>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include <libethcore/KeyManager.h>
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libethereum/KeyManager.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include "Context.h" #include "Context.h"
@ -137,7 +137,10 @@ private slots:
void on_newAccount_triggered(); void on_newAccount_triggered();
void on_killAccount_triggered(); void on_killAccount_triggered();
void on_importKey_triggered(); void on_importKey_triggered();
void on_reencryptKey_triggered();
void on_reencryptAll_triggered();
void on_importKeyFile_triggered(); void on_importKeyFile_triggered();
void on_claimPresale_triggered();
void on_exportKey_triggered(); void on_exportKey_triggered();
// Account pane // Account pane
@ -246,7 +249,7 @@ private:
void refreshBalances(); void refreshBalances();
void setBeneficiary(dev::Address const& _b); 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::Main> ui; std::unique_ptr<Ui::Main> ui;

30
alethzero/Transact.cpp

@ -39,7 +39,8 @@
#include <libnatspec/NatspecExpressionEvaluator.h> #include <libnatspec/NatspecExpressionEvaluator.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/Utility.h> #include <libethereum/Utility.h>
#include <libethereum/KeyManager.h> #include <libethcore/KeyManager.h>
#if ETH_SERPENT #if ETH_SERPENT
#include <libserpent/funcs.h> #include <libserpent/funcs.h>
#include <libserpent/util.h> #include <libserpent/util.h>
@ -76,6 +77,7 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
m_ethereum = _eth; m_ethereum = _eth;
m_natSpecDB = _natSpecDB; m_natSpecDB = _natSpecDB;
auto old = ui->from->currentIndex();
ui->from->clear(); ui->from->clear();
for (auto const& i: m_accounts) 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)); 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); 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 bool Transact::isCreation() const
@ -301,6 +307,9 @@ void Transact::rejigData()
// Determine how much balance we have to play with... // Determine how much balance we have to play with...
//findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice()); //findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice());
auto s = fromAccount(); auto s = fromAccount();
if (!s)
return;
auto b = ethereum()->balanceAt(s, PendingBlock); auto b = ethereum()->balanceAt(s, PendingBlock);
m_allGood = true; m_allGood = true;
@ -344,7 +353,7 @@ void Transact::rejigData()
if (b < value() + baseGas * gasPrice()) if (b < value() + baseGas * gasPrice())
{ {
// Not enough - bail. // Not enough - bail.
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> No single account contains enough for paying even the basic amount of gas required.</div>"); bail("<div class=\"error\"><span class=\"icon\">ERROR</span> Account doesn't contain enough for paying even the basic amount of gas required.</div>");
return; return;
} }
else else
@ -417,6 +426,8 @@ Secret Transact::findSecret(u256 _totalReq) const
Address Transact::fromAccount() Address Transact::fromAccount()
{ {
if (ui->from->currentIndex() < 0 || ui->from->currentIndex() >= (int)m_accounts.size())
return Address();
auto it = m_accounts.begin(); auto it = m_accounts.begin();
std::advance(it, ui->from->currentIndex()); std::advance(it, ui->from->currentIndex());
return *it; return *it;
@ -425,14 +436,19 @@ Address Transact::fromAccount()
void Transact::on_send_clicked() void Transact::on_send_clicked()
{ {
// Secret s = findSecret(value() + fee()); // Secret s = findSecret(value() + fee());
Secret s = m_context->retrieveSecret(fromAccount()); auto a = fromAccount();
auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock); auto b = ethereum()->balanceAt(a, PendingBlock);
if (!s || b < value() + fee())
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; return;
} }
Secret s = m_context->retrieveSecret(a);
if (!s)
return;
if (isCreation()) if (isCreation())
{ {
// If execution is a contract creation, add Natspec to // If execution is a contract creation, add Natspec to
@ -467,7 +483,7 @@ void Transact::on_debug_clicked()
auto b = ethereum()->balanceAt(from, PendingBlock); auto b = ethereum()->balanceAt(from, PendingBlock);
if (!from || b < value() + fee()) 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; return;
} }

11
alethzero/Transact.h

@ -44,12 +44,13 @@ public:
void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots: private slots:
void on_from_currentIndexChanged(int) { rejigData(); rejigData(); }
void on_destination_currentTextChanged(QString); void on_destination_currentTextChanged(QString);
void on_value_valueChanged(int) { updateFee(); } void on_value_valueChanged(int) { updateFee(); rejigData(); }
void on_gas_valueChanged(int) { updateFee(); } void on_gas_valueChanged(int) { updateFee(); rejigData(); }
void on_valueUnits_currentIndexChanged(int) { updateFee(); } void on_valueUnits_currentIndexChanged(int) { updateFee(); rejigData(); }
void on_gasPriceUnits_currentIndexChanged(int) { updateFee(); } void on_gasPriceUnits_currentIndexChanged(int) { updateFee(); rejigData(); }
void on_gasPrice_valueChanged(int) { updateFee(); } void on_gasPrice_valueChanged(int) { updateFee(); rejigData(); }
void on_data_textChanged() { rejigData(); } void on_data_textChanged() { rejigData(); }
void on_optimize_clicked() { rejigData(); } void on_optimize_clicked() { rejigData(); }
void on_send_clicked(); void on_send_clicked();

3
eth/main.cpp

@ -37,7 +37,8 @@
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <libethereum/All.h> #include <libethereum/All.h>
#include <libethereum/KeyManager.h> #include <libethcore/KeyManager.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#if ETH_JSCONSOLE || !ETH_TRUE #if ETH_JSCONSOLE || !ETH_TRUE
#include <libjsconsole/JSConsole.h> #include <libjsconsole/JSConsole.h>

33
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()

413
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 <http://www.gnu.org/licenses/>.
*/
/** @file KeyAux.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* CLI module for key management.
*/
#include <thread>
#include <chrono>
#include <fstream>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim_all.hpp>
#include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h>
#include <libethcore/KeyManager.h>
#include <libethcore/ICAP.h>
#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<string, string> 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<h128>() + 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<u128> bare;
vector<u128> nonIcap;
for (auto const& u: wallet.store().keys())
if (Address a = wallet.address(u))
if (a[0])
nonIcap.push_back(u);
else
{
std::pair<std::string, std::string> 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<std::string, std::string> 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 [ <file>|<secret-hex> , ... ] Import keys from given sources." << endl
<< " --recode-bare [ <uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl
// << " --export-bare [ <uuid> , ... ] Export given keys." << endl
<< " --kill-bare [ <uuid> , ... ] Delete given keys." << endl
<< "Secret-store configuration:" << endl
<< " --secrets-path <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 <name> Create a new key with given name and add it in the wallet." << endl
<< " -i,--import [<uuid>|<file>|<secret-hex>] <name> Import keys from given source and place in wallet." << endl
<< " -e,--export [ <address>|<uuid> , ... ] Export given keys." << endl
<< " -r,--recode [ <address>|<uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl
<< "Wallet configuration:" << endl
<< " --create-wallet Create an Ethereum master wallet." << endl
<< " --wallet-path <path> Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl
<< " -m, --master <password> Specify wallet (master) password." << endl
<< endl
<< "Encryption configuration:" << endl
<< " --kdf <kdfname> Specify KDF to use when encrypting (default: sc rypt)" << endl
<< " --kdf-param <name> <value> Specify a parameter for the KDF." << endl
// << " --cipher <ciphername> Specify cipher to use when encrypting (default: aes-128-ctr)" << endl
// << " --cipher-param <name> <value> Specify a parameter for the cipher." << endl
<< " --lock <password> Specify password for when encrypting a (the) key." << endl
<< " --hint <hint> Specify hint for the --lock password." << endl
<< endl
<< "Decryption configuration:" << endl
<< " --unlock <password> 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<string, string> m_kdfParams;
// string m_cipher;
// map<string, string> m_cipherParams;
};

84
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 <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* Ethereum client.
*/
#include <thread>
#include <chrono>
#include <fstream>
#include <iostream>
#include <libdevcore/FileSystem.h>
#include <libdevcore/Log.h>
#include <libethcore/KeyManager.h>
#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;
}

4
ethminer/MinerAux.h

@ -16,10 +16,10 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file main.cpp /** @file MinerAux.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>
* @date 2014 * @date 2014
* Ethereum client. * CLI module for mining.
*/ */
#include <thread> #include <thread>

2
evmjit/CMakeLists.txt

@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC OFF)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else() 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() endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")

4
evmjit/include/evmjit/JIT.h

@ -28,8 +28,8 @@ public:
private: private:
friend class dev::eth::jit::ExecutionEngine; friend class dev::eth::jit::ExecutionEngine;
static void* getCode(h256 _codeHash); static uint64_t getCode(h256 _codeHash);
static void mapCode(h256 _codeHash, void* _funcAddr); static void mapCode(h256 _codeHash, uint64_t _funcAddr);
}; };
} }

25
evmjit/libevmjit-cpp/Env.cpp

@ -64,19 +64,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) 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 = llvm2eth(*_value); CallParameters params;
auto receiveAddress = right160(*_receiveAddress); params.value = llvm2eth(*_value);
auto codeAddress = right160(*_codeAddress); params.senderAddress = _env->myAddress;
const auto isCall = receiveAddress == codeAddress; // OPT: The same address pointer can be used if not CODECALL 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; *io_gas -= _callGas;
if (*io_gas < 0) if (*io_gas < 0)
return false; return false;
if (isCall && !_env->exists(receiveAddress)) if (isCall && !_env->exists(params.receiveAddress))
*io_gas -= static_cast<int64_t>(c_callNewAccountGas); // no underflow, *io_gas non-negative before *io_gas -= static_cast<int64_t>(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"); /*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible");
*io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow *io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow
@ -87,11 +92,11 @@ extern "C"
return false; return false;
auto ret = false; auto ret = false;
auto callGas = u256{_callGas}; params.gas = u256{_callGas};
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024)
ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress); ret = _env->call(params);
*io_gas += static_cast<int64_t>(callGas); // it is never more than initial _callGas *io_gas += static_cast<int64_t>(params.gas); // it is never more than initial _callGas
return ret; return ret;
} }

6
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 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: private:
friend class VMFactory; friend class VMFactory;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} explicit JitVM(u256 _gas = 0): m_gas(_gas) {}
u256 m_gas;
jit::RuntimeData m_data; jit::RuntimeData m_data;
jit::ExecutionEngine m_engine; jit::ExecutionEngine m_engine;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT

2
evmjit/libevmjit/ExecutionEngine.cpp

@ -183,7 +183,7 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!CHECK(entryFuncPtr)) if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError; return ReturnCode::LLVMLinkError;
JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast
} }
listener->stateChanged(ExecState::Execution); listener->stateChanged(ExecState::Execution);

8
evmjit/libevmjit/JIT.cpp

@ -12,7 +12,7 @@ namespace
class JITImpl: JIT class JITImpl: JIT
{ {
public: public:
std::unordered_map<h256, void*> codeMap; std::unordered_map<h256, uint64_t> codeMap;
static JITImpl& instance() static JITImpl& instance()
{ {
@ -28,16 +28,16 @@ bool JIT::isCodeReady(h256 _codeHash)
return JITImpl::instance().codeMap.count(_codeHash) != 0; return JITImpl::instance().codeMap.count(_codeHash) != 0;
} }
void* JIT::getCode(h256 _codeHash) uint64_t JIT::getCode(h256 _codeHash)
{ {
auto& codeMap = JITImpl::instance().codeMap; auto& codeMap = JITImpl::instance().codeMap;
auto it = codeMap.find(_codeHash); auto it = codeMap.find(_codeHash);
if (it != codeMap.end()) if (it != codeMap.end())
return it->second; return it->second;
return nullptr; return 0;
} }
void JIT::mapCode(h256 _codeHash, void* _funcAddr) void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr)
{ {
JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr));
} }

22
exp/main.cpp

@ -36,7 +36,6 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <libdevcore/TrieDB.h> #include <libdevcore/TrieDB.h>
#include <libdevcore/TrieHash.h> #include <libdevcore/TrieHash.h>
/*
#include <libdevcore/RangeMask.h> #include <libdevcore/RangeMask.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
@ -50,24 +49,37 @@
#include <libethcore/Farm.h> #include <libethcore/Farm.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <libethereum/All.h> #include <libethereum/All.h>
#include <libethereum/KeyManager.h> #include <libethcore/KeyManager.h>
#include <libethereum/AccountDiff.h> #include <libethereum/AccountDiff.h>
#include <libethereum/DownloadMan.h> #include <libethereum/DownloadMan.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <liblll/All.h> #include <liblll/All.h>
#include <libwhisper/WhisperPeer.h> #include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h> #include <libwhisper/WhisperHost.h>
#include <test/JsonSpiritHeaders.h>*/ #include <test/JsonSpiritHeaders.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
/*using namespace dev::eth; using namespace dev::eth;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
namespace js = json_spirit; namespace js = json_spirit;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
*/
#if 1 #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() int main()
{ {
cdebug << "EXP"; cdebug << "EXP";

3
libdevcore/Common.h

@ -39,6 +39,7 @@
#include <set> #include <set>
#include <unordered_set> #include <unordered_set>
#include <functional> #include <functional>
#include <string>
#include <boost/timer.hpp> #include <boost/timer.hpp>
#include <boost/functional/hash.hpp> #include <boost/functional/hash.hpp>
#pragma warning(push) #pragma warning(push)
@ -63,6 +64,8 @@ namespace dev
extern char const* Version; extern char const* Version;
static const std::string EmptyString;
// Binary data types. // Binary data types.
using bytes = std::vector<byte>; using bytes = std::vector<byte>;
using bytesRef = vector_ref<byte>; using bytesRef = vector_ref<byte>;

9
libdevcore/CommonData.h

@ -323,4 +323,13 @@ std::vector<T> keysOf(std::map<T, U> const& _m)
return ret; return ret;
} }
template<class T, class U>
std::vector<T> keysOf(std::unordered_map<T, U> const& _m)
{
std::vector<T> ret;
for (auto const& i: _m)
ret.push_back(i.first);
return ret;
}
} }

1
libdevcrypto/CMakeLists.txt

@ -22,6 +22,7 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} scrypt)
target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} devcore)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )

22
libdevcrypto/Common.cpp

@ -25,6 +25,7 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <libscrypt/libscrypt.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
@ -119,10 +120,11 @@ std::pair<bytes, h128> dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plai
return make_pair(encryptSymNoAuth(_k, iv, _plain), iv); 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; if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
SecByteBlock key(_k.data(), c_aesKeyLen); return bytes();
SecByteBlock key(_k.data(), _k.size());
try try
{ {
CTR_Mode<AES>::Encryption e; CTR_Mode<AES>::Encryption e;
@ -138,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; if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
SecByteBlock key(_k.data(), c_aesKeyLen); return bytes();
SecByteBlock key(_k.data(), _k.size());
try try
{ {
CTR_Mode<AES>::Decryption d; CTR_Mode<AES>::Decryption d;
@ -180,6 +183,13 @@ bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations,
return ret; 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() KeyPair KeyPair::create()
{ {
static boost::thread_specific_ptr<mt19937_64> s_eng; static boost::thread_specific_ptr<mt19937_64> s_eng;

15
libdevcrypto/Common.h

@ -106,10 +106,18 @@ bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
std::pair<bytes, h128> encryptSymNoAuth(h128 const& _k, bytesConstRef _plain); std::pair<bytes, h128> encryptSymNoAuth(h128 const& _k, bytesConstRef _plain);
/// Encrypts payload with specified IV/ctr using AES128-CTR. /// 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. /// 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. /// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _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. /// Derive key via PBKDF2.
bytes pbkdf2(std::string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen = 32); 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". /// Simple class that represents a "key pair".
/// All of the data of the class can be regenerated from the secret key (m_secret) alone. /// 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). /// Actually stores a tuplet of secret, public and address (the right 160-bits of the public).

213
libdevcrypto/SecretStore.cpp

@ -22,6 +22,7 @@
#include "SecretStore.h" #include "SecretStore.h"
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
@ -33,7 +34,57 @@ using namespace dev;
namespace js = json_spirit; namespace js = json_spirit;
namespace fs = boost::filesystem; 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(); load();
} }
@ -42,10 +93,11 @@ SecretStore::~SecretStore()
{ {
} }
bytes SecretStore::secret(h128 const& _uuid, function<std::string()> const& _pass) const bytes SecretStore::secret(h128 const& _uuid, function<std::string()> const& _pass, bool _useCache) const
{ {
(void)_pass;
auto rit = m_cached.find(_uuid); auto rit = m_cached.find(_uuid);
if (rit != m_cached.end()) if (_useCache && rit != m_cached.end())
return rit->second; return rit->second;
auto it = m_keys.find(_uuid); auto it = m_keys.find(_uuid);
if (it == m_keys.end()) if (it == m_keys.end())
@ -93,7 +145,7 @@ void SecretStore::save(std::string const& _keysPath)
js::read_string(k.second.first, crypto); js::read_string(k.second.first, crypto);
v["crypto"] = crypto; v["crypto"] = crypto;
v["id"] = uuid; v["id"] = uuid;
v["version"] = 2; v["version"] = c_keyFileVersion;
writeFile(filename, js::write_string(js::mValue(v), true)); writeFile(filename, js::write_string(js::mValue(v), true));
if (!k.second.second.empty() && k.second.second != filename) if (!k.second.second.empty() && k.second.second != filename)
boost::filesystem::remove(k.second.second); boost::filesystem::remove(k.second.second);
@ -105,48 +157,88 @@ void SecretStore::load(std::string const& _keysPath)
{ {
fs::path p(_keysPath); fs::path p(_keysPath);
boost::filesystem::create_directories(p); boost::filesystem::create_directories(p);
js::mValue v;
for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it)
if (is_regular_file(it->path())) if (is_regular_file(it->path()))
{ readKey(it->path().string(), true);
cdebug << "Reading" << it->path(); }
js::read_string(contentsString(it->path().string()), v);
if (v.type() == js::obj_type) h128 SecretStore::readKey(std::string const& _file, bool _deleteFile)
{ {
js::mObject o = v.get_obj(); cdebug << "Reading" << _file;
int version = o.count("Version") ? stoi(o["Version"].get_str()) : o.count("version") ? o["version"].get_int() : 0; return readKeyContent(contentsString(_file), _deleteFile ? _file : string());
if (version == 2) }
m_keys[fromUUID(o["id"].get_str())] = make_pair(js::write_string(o["crypto"], false), it->path().string());
else h128 SecretStore::readKeyContent(std::string const& _content, std::string const& _file)
cwarn << "Cannot read key version" << version; {
} js::mValue u = upgraded(_content);
// else if (u.type() == js::obj_type)
// cwarn << "Invalid JSON in key file" << it->path().string(); {
} 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<std::string()> 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; js::mObject ret;
// KDF info // KDF info
unsigned dklen = 16; unsigned dklen = 32;
unsigned iterations = 262144;
bytes salt = h256::random().asBytes(); bytes salt = h256::random().asBytes();
ret["kdf"] = "pbkdf2"; bytes derivedKey;
if (_kdf == KDF::Scrypt)
{ {
js::mObject params; unsigned iterations = 262144;
params["prf"] = "hmac-sha256"; unsigned p = 1;
params["c"] = (int)iterations; unsigned r = 8;
params["salt"] = toHex(salt); ret["kdf"] = "scrypt";
params["dklen"] = (int)dklen; {
ret["kdfparams"] = params; 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 // cipher info
ret["cipher"] = "aes-128-cbc"; ret["cipher"] = "aes-128-ctr";
h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); h128 key(derivedKey, h128::AlignLeft);
// cdebug << "cipherKey" << key.hex();
h128 iv = h128::random(); h128 iv = h128::random();
{ {
js::mObject params; js::mObject params;
@ -159,7 +251,9 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass)
ret["ciphertext"] = toHex(cipherText); ret["ciphertext"] = toHex(cipherText);
// and mac. // 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()); ret["mac"] = toHex(mac.ref());
return js::write_string((js::mValue)ret, true); 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()); bytes salt = fromHex(params["salt"].get_str());
derivedKey = pbkdf2(_pass, salt, iterations, params["dklen"].get_int()); 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 else
{ {
cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported."; cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported.";
return bytes(); 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()); bytes cipherText = fromHex(o["ciphertext"].get_str());
// check MAC // check MAC
h256 mac(o["mac"].get_str()); if (o.count("mac"))
h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText);
if (mac != macExp)
{ {
cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); h256 mac(o["mac"].get_str());
return bytes(); 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 // decrypt
if (o["cipher"].get_str() == "aes-128-cbc") if (o["cipher"].get_str() == "aes-128-ctr")
{ {
auto params = o["cipherparams"].get_obj(); auto params = o["cipherparams"].get_obj();
h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight);
h128 iv(params["iv"].get_str()); 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 else
{ {

31
libdevcrypto/SecretStore.h

@ -30,27 +30,48 @@
namespace dev namespace dev
{ {
enum class KDF {
PBKDF2_SHA256,
Scrypt,
};
class SecretStore class SecretStore
{ {
public: public:
SecretStore(); SecretStore(std::string const& _path = defaultPath());
~SecretStore(); ~SecretStore();
bytes secret(h128 const& _uuid, std::function<std::string()> const& _pass) const; bytes secret(h128 const& _uuid, std::function<std::string()> 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); h128 importSecret(bytes const& _s, std::string const& _pass);
bool recode(h128 const& _uuid, std::string const& _newPass, std::function<std::string()> const& _pass, KDF _kdf = KDF::Scrypt);
void kill(h128 const& _uuid); void kill(h128 const& _uuid);
std::vector<h128> keys() const { return keysOf(m_keys); }
// Clear any cached keys. // Clear any cached keys.
void clearCache() const; 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: private:
void save(std::string const& _keysPath = getDataDir("web3") + "/keys"); void load(std::string const& _keysPath);
void load(std::string const& _keysPath = getDataDir("web3") + "/keys"); void load() { load(m_path); }
static std::string encrypt(bytes const& _v, std::string const& _pass); 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); static bytes decrypt(std::string const& _v, std::string const& _pass);
mutable std::unordered_map<h128, bytes> m_cached; mutable std::unordered_map<h128, bytes> m_cached;
std::unordered_map<h128, std::pair<std::string, std::string>> m_keys; std::unordered_map<h128, std::pair<std::string, std::string>> m_keys;
std::string m_path;
}; };
} }

2
libethash-cl/ethash_cl_miner.cpp

@ -304,7 +304,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
}; };
std::queue<pending_batch> pending; std::queue<pending_batch> pending;
static uint32_t const c_zero = 0; uint32_t const c_zero = 0;
// update header constant buffer // update header constant buffer
m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header);

2
libethash/internal.c

@ -368,7 +368,7 @@ static bool ethash_mmap(struct ethash_full* ret, FILE* f)
if ((fd = ethash_fileno(ret->file)) == -1) { if ((fd = ethash_fileno(ret->file)) == -1) {
return false; return false;
} }
mmapped_data= mmap( mmapped_data = mmap(
NULL, NULL,
(size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE, (size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE,
PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,

8
libethcore/Ethash.cpp

@ -142,8 +142,12 @@ void Ethash::CPUMiner::workLoop()
WorkPackage w = work(); WorkPackage w = work();
EthashAux::FullType dag; EthashAux::FullType dag;
while (!shouldStop() && !(dag = EthashAux::full(w.seedHash))) while (!shouldStop() && !dag)
this_thread::sleep_for(chrono::milliseconds(500)); {
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; h256 boundary = w.boundary;
unsigned hashCount = 1; unsigned hashCount = 1;

6
libethcore/EthashAux.cpp

@ -133,7 +133,9 @@ bytesConstRef EthashAux::LightAllocation::data() const
EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb) EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb)
{ {
// cdebug << "About to call ethash_full_new...";
full = ethash_full_new(_light, _cb); full = ethash_full_new(_light, _cb);
// cdebug << "Called OK.";
if (!full) if (!full)
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new()")); BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new()"));
} }
@ -170,9 +172,9 @@ EthashAux::FullType EthashAux::full(h256 const& _seedHash, bool _createIfMissing
if (_createIfMissing || computeFull(_seedHash, false) == 100) if (_createIfMissing || computeFull(_seedHash, false) == 100)
{ {
s_dagCallback = _f; s_dagCallback = _f;
cnote << "Loading from libethash..."; // cnote << "Loading from libethash...";
ret = make_shared<FullAllocation>(l->light, dagCallbackShim); ret = make_shared<FullAllocation>(l->light, dagCallbackShim);
cnote << "Done loading."; // cnote << "Done loading.";
DEV_GUARDED(get()->x_fulls) DEV_GUARDED(get()->x_fulls)
get()->m_fulls[_seedHash] = get()->m_lastUsedFull = ret; get()->m_fulls[_seedHash] = get()->m_lastUsedFull = ret;

2
libethcore/ICAP.h

@ -38,8 +38,6 @@ namespace eth
struct InvalidICAP: virtual public dev::Exception {}; struct InvalidICAP: virtual public dev::Exception {};
static const std::string EmptyString;
/** /**
* @brief Encapsulation of an ICAP address. * @brief Encapsulation of an ICAP address.
* Can be encoded, decoded, looked-up and inspected. * Can be encoded, decoded, looked-up and inspected.

78
libethereum/KeyManager.cpp → libethcore/KeyManager.cpp

@ -31,8 +31,8 @@ using namespace dev;
using namespace eth; using namespace eth;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
KeyManager::KeyManager(std::string const& _keysFile): KeyManager::KeyManager(std::string const& _keysFile, std::string const& _secretsPath):
m_keysFile(_keysFile) m_keysFile(_keysFile), m_store(_secretsPath)
{} {}
KeyManager::~KeyManager() KeyManager::~KeyManager()
@ -49,6 +49,32 @@ void KeyManager::create(std::string const& _pass)
write(_pass, m_keysFile); write(_pass, m_keysFile);
} }
bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function<string()> 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<string()> 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) bool KeyManager::load(std::string const& _pass)
{ {
try { try {
@ -70,8 +96,12 @@ bool KeyManager::load(std::string const& _pass)
m_passwordInfo[(h256)i[0]] = (std::string)i[1]; m_passwordInfo[(h256)i[0]] = (std::string)i[1];
m_password = (string)s[3]; m_password = (string)s[3];
} }
cdebug << hashPassword(m_password) << toHex(m_password);
m_cachedPasswords[hashPassword(m_password)] = m_password; m_cachedPasswords[hashPassword(m_password)] = m_password;
m_cachedPasswords[hashPassword(defaultPassword())] = defaultPassword(); 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; return true;
} }
catch (...) { catch (...) {
@ -89,18 +119,35 @@ Secret KeyManager::secret(Address const& _address, function<std::string()> const
Secret KeyManager::secret(h128 const& _uuid, function<std::string()> const& _pass) const Secret KeyManager::secret(h128 const& _uuid, function<std::string()> const& _pass) const
{ {
return Secret(m_store.secret(_uuid, [&](){ return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); }));
auto kit = m_keyInfo.find(_uuid); }
if (kit != m_keyInfo.end())
std::string KeyManager::getPassword(h128 const& _uuid, function<std::string()> 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<std::string()> 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); m_cachedPasswords[hashPassword(p)] = p;
if (it != m_cachedPasswords.end()) return p;
return it->second;
} }
std::string p = _pass(); }
m_cachedPasswords[hashPassword(p)] = p; return string();
return p;
}));
} }
h128 KeyManager::uuid(Address const& _a) const h128 KeyManager::uuid(Address const& _a) const
@ -171,7 +218,7 @@ std::unordered_map<Address, std::pair<std::string, std::string>> KeyManager::acc
std::unordered_map<Address, std::pair<std::string, std::string>> ret; std::unordered_map<Address, std::pair<std::string, std::string>> ret;
for (auto const& i: m_addrLookup) for (auto const& i: m_addrLookup)
if (m_keyInfo.count(i.second) > 0) 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; return ret;
} }
@ -194,6 +241,9 @@ void KeyManager::write(std::string const& _pass, std::string const& _keysFile) c
bytes salt = h256::random().asBytes(); bytes salt = h256::random().asBytes();
writeFile(_keysFile + ".salt", salt); writeFile(_keysFile + ".salt", salt);
auto key = h128(pbkdf2(_pass, salt, 262144, 16)); auto key = h128(pbkdf2(_pass, salt, 262144, 16));
m_cachedPasswords[hashPassword(_pass)] = _pass;
m_master = hashPassword(_pass);
write(key, _keysFile); write(key, _keysFile);
} }

26
libethereum/KeyManager.h → libethcore/KeyManager.h

@ -42,6 +42,12 @@ struct KeyInfo
static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::string(); }; 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: This one is specifically for Ethereum, but we can make it generic in due course.
// TODO: hidden-partition style key-store. // TODO: hidden-partition style key-store.
/** /**
@ -55,7 +61,7 @@ static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::str
class KeyManager class KeyManager
{ {
public: public:
KeyManager(std::string const& _keysFile = getDataDir("ethereum") + "/keys.info"); KeyManager(std::string const& _keysFile = defaultPath(), std::string const& _secretsPath = SecretStore::defaultPath());
~KeyManager(); ~KeyManager();
void setKeysFile(std::string const& _keysFile) { m_keysFile = _keysFile; } void setKeysFile(std::string const& _keysFile) { m_keysFile = _keysFile; }
@ -67,9 +73,12 @@ public:
void save(std::string const& _pass) const { write(_pass, m_keysFile); } void save(std::string const& _pass) const { write(_pass, m_keysFile); }
void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; } 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; AddressHash accounts() const;
std::unordered_map<Address, std::pair<std::string, std::string>> accountDetails() const; std::unordered_map<Address, std::pair<std::string, std::string>> 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; h128 uuid(Address const& _a) const;
Address address(h128 const& _uuid) const; Address address(h128 const& _uuid) const;
@ -84,15 +93,23 @@ public:
Secret secret(Address const& _address, std::function<std::string()> const& _pass = DontKnowThrow) const; Secret secret(Address const& _address, std::function<std::string()> const& _pass = DontKnowThrow) const;
Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const; Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const;
bool recode(Address const& _address, SemanticPassword _newPass, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
bool recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
void kill(h128 const& _id) { kill(address(_id)); } void kill(h128 const& _id) { kill(address(_id)); }
void kill(Address const& _a); void kill(Address const& _a);
static std::string defaultPath() { return getDataDir("ethereum") + "/keys.info"; }
private: private:
std::string defaultPassword() const { return asString(m_key.ref()); } std::string getPassword(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const;
std::string getPassword(h256 const& _passHash, std::function<std::string()> const& _pass = DontKnowThrow) const;
std::string defaultPassword(std::function<std::string()> const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); }
h256 hashPassword(std::string const& _pass) const; h256 hashPassword(std::string const& _pass) const;
// Only use if previously loaded ok. // Only use if previously loaded ok.
// @returns false if wasn't 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; bool write(std::string const& _keysFile) const;
void write(std::string const& _pass, 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; void write(h128 const& _key, std::string const& _keysFile) const;
@ -112,9 +129,10 @@ private:
// we have an upgrade strategy. // we have an upgrade strategy.
std::string m_password; std::string m_password;
SecretStore m_store;
mutable h128 m_key;
mutable std::string m_keysFile; mutable std::string m_keysFile;
mutable h128 m_key;
mutable h256 m_master;
SecretStore m_store;
}; };
} }

2
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; } // 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 hashes = 0; ///< Total number of hashes computed.
uint64_t ms = 0; ///< Total number of milliseconds of mining thus far. 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 {}; struct MineInfo: public MiningProgress {};

2
libethereum/AccountDiff.h

@ -62,7 +62,7 @@ struct AccountDiff
Diff<bool> exist; ///< The account's existance; was it created/deleted or not? Diff<bool> exist; ///< The account's existance; was it created/deleted or not?
Diff<u256> balance; ///< The account's balance; did it alter? Diff<u256> balance; ///< The account's balance; did it alter?
Diff<u256> nonce; ///< The account's nonce; did it alter? Diff<u256> nonce; ///< The account's nonce; did it alter?
std::unordered_map<u256, Diff<u256>> storage; ///< The account's storage addresses; each has its own Diff. std::map<u256, Diff<u256>> storage; ///< The account's storage addresses; each has its own Diff.
Diff<bytes> code; ///< The account's code; in general this should only have changed if exist also changed. Diff<bytes> code; ///< The account's code; in general this should only have changed if exist also changed.
}; };

4
libevm/VM.cpp

@ -635,7 +635,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
if (m_stack[m_stack.size() - 3] > 0) if (m_stack[m_stack.size() - 3] > 0)
callParams->gas += c_callStipend; callParams->gas += c_callStipend;
m_stack.pop_back(); m_stack.pop_back();
callParams->receiveAddress = asAddress(m_stack.back()); callParams->codeAddress = asAddress(m_stack.back());
m_stack.pop_back(); m_stack.pop_back();
callParams->value = m_stack.back(); callParams->value = m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
@ -653,7 +653,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{ {
callParams->onOp = _onOp; callParams->onOp = _onOp;
callParams->senderAddress = _ext.myAddress; callParams->senderAddress = _ext.myAddress;
callParams->codeAddress = inst == Instruction::CALL ? callParams->receiveAddress : callParams->senderAddress; callParams->receiveAddress = inst == Instruction::CALL ? callParams->codeAddress : callParams->senderAddress;
callParams->data = bytesConstRef(m_temp.data() + inOff, inSize); callParams->data = bytesConstRef(m_temp.data() + inOff, inSize);
callParams->out = bytesRef(m_temp.data() + outOff, outSize); callParams->out = bytesRef(m_temp.data() + outOff, outSize);
m_stack.push_back(_ext.call(*callParams)); m_stack.push_back(_ext.call(*callParams));

1
libevmasm/Assembly.cpp

@ -431,6 +431,7 @@ bytes Assembly::assemble() const
case PushSubSize: case PushSubSize:
{ {
auto s = m_data[i.data()].size(); auto s = m_data[i.data()].size();
i.setPushedValue(u256(s));
byte b = max<unsigned>(1, dev::bytesRequired(s)); byte b = max<unsigned>(1, dev::bytesRequired(s));
ret.push_back((byte)Instruction::PUSH1 - 1 + b); ret.push_back((byte)Instruction::PUSH1 - 1 + b);
ret.resize(ret.size() + b); ret.resize(ret.size() + b);

6
libevmasm/AssemblyItem.h

@ -84,11 +84,17 @@ public:
JumpType getJumpType() const { return m_jumpType; } JumpType getJumpType() const { return m_jumpType; }
std::string getJumpTypeAsString() const; std::string getJumpTypeAsString() const;
void setPushedValue(u256 const& _value) const { m_pushedValue = std::make_shared<u256>(_value); }
u256 const* pushedValue() const { return m_pushedValue.get(); }
private: private:
AssemblyItemType m_type; AssemblyItemType m_type;
u256 m_data; u256 m_data;
SourceLocation m_location; SourceLocation m_location;
JumpType m_jumpType = JumpType::Ordinary; 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<u256> m_pushedValue;
}; };
using AssemblyItems = std::vector<AssemblyItem>; using AssemblyItems = std::vector<AssemblyItem>;

132
libevmasm/GasMeter.cpp

@ -20,6 +20,7 @@
*/ */
#include "GasMeter.h" #include "GasMeter.h"
#include <libevmasm/KnownState.h>
#include <libevmcore/Params.h> #include <libevmcore/Params.h>
using namespace std; using namespace std;
@ -41,55 +42,162 @@ GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption co
GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item) GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item)
{ {
switch (_item.type()) { GasConsumption gas;
switch (_item.type())
{
case Push: case Push:
case PushTag: case PushTag:
return runGas(Instruction::PUSH1); case PushData:
case PushString:
case PushSub:
case PushSubSize:
case PushProgramSize:
gas = runGas(Instruction::PUSH1);
break;
case Tag: case Tag:
return runGas(Instruction::JUMPDEST); gas = runGas(Instruction::JUMPDEST);
break;
case Operation: case Operation:
{ {
GasConsumption gas = runGas(_item.instruction()); ExpressionClasses& classes = m_state->expressionClasses();
gas = runGas(_item.instruction());
switch (_item.instruction()) switch (_item.instruction())
{ {
case Instruction::SSTORE: 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; break;
}
case Instruction::SLOAD: case Instruction::SLOAD:
gas += c_sloadGas; gas += c_sloadGas;
break; break;
case Instruction::RETURN:
gas += memoryGas(0, -1);
break;
case Instruction::MLOAD:
case Instruction::MSTORE: case Instruction::MSTORE:
gas += memoryGas(classes.find(eth::Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(32))
}));
break;
case Instruction::MSTORE8: case Instruction::MSTORE8:
case Instruction::MLOAD: gas += memoryGas(classes.find(eth::Instruction::ADD, {
case Instruction::RETURN: m_state->relativeStackElement(0),
classes.find(AssemblyItem(1))
}));
break;
case Instruction::SHA3: case Instruction::SHA3:
gas = c_sha3Gas;
gas += wordGas(c_sha3WordGas, m_state->relativeStackElement(-1));
gas += memoryGas(0, -1);
break;
case Instruction::CALLDATACOPY: case Instruction::CALLDATACOPY:
case Instruction::CODECOPY: case Instruction::CODECOPY:
gas += memoryGas(0, -2);
gas += wordGas(c_copyGas, m_state->relativeStackElement(-2));
break;
case Instruction::EXTCODECOPY: case Instruction::EXTCODECOPY:
gas += memoryGas(-1, -3);
gas += wordGas(c_copyGas, m_state->relativeStackElement(-3));
break;
case Instruction::LOG0: case Instruction::LOG0:
case Instruction::LOG1: case Instruction::LOG1:
case Instruction::LOG2: case Instruction::LOG2:
case Instruction::LOG3: case Instruction::LOG3:
case Instruction::LOG4: 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::CALL:
case Instruction::CALLCODE: 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: case Instruction::CREATE:
gas = c_createGas;
gas += memoryGas(-1, -2);
break;
case Instruction::EXP: case Instruction::EXP:
// @todo logic can be improved gas = c_expGas;
gas = GasConsumption::infinite(); if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
gas += c_expByteGas * (32 - (h256(*value).firstBitSet() / 8));
else
gas = GasConsumption::infinite();
break; break;
default: default:
break; break;
} }
return gas;
break; break;
} }
default: default:
gas = GasConsumption::infinite();
break; 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) GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction)

27
libevmasm/GasMeter.h

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <ostream> #include <ostream>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/AssemblyItem.h> #include <libevmasm/AssemblyItem.h>
namespace dev namespace dev
@ -29,8 +30,13 @@ namespace dev
namespace eth namespace eth
{ {
class KnownState;
/** /**
* Class that helps computing the maximum gas consumption for instructions. * 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 class GasMeter
{ {
@ -47,11 +53,28 @@ public:
bool isInfinite; 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<KnownState> 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); GasConsumption estimateMax(AssemblyItem const& _item);
private: 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); static GasConsumption runGas(Instruction _instruction);
std::shared_ptr<KnownState> 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) 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) if (_consumption.isInfinite)
return _str << "inf"; return _str << "inf";
else else
return _str << _consumption.value; return _str << std::dec << _consumption.value;
} }

13
libevmasm/KnownState.cpp

@ -92,7 +92,11 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
else if (_item.type() != Operation) else if (_item.type() != Operation)
{ {
assertThrow(_item.deposit() == 1, InvalidDeposit, ""); 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 else
{ {
@ -230,7 +234,12 @@ ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation
return m_stackElements.at(_stackHeight); return m_stackElements.at(_stackHeight);
// Stack element not found (not assigned yet), create new unknown equivalence class. // Stack element not found (not assigned yet), create new unknown equivalence class.
return m_stackElements[_stackHeight] = 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() void KnownState::clearTagUnions()

4
libevmasm/KnownState.h

@ -111,6 +111,8 @@ public:
/// Retrieves the current equivalence class fo the given stack element (or generates a new /// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet). /// one if it does not exist yet).
Id stackElement(int _stackHeight, SourceLocation const& _location); 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 /// @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. /// containing the tag if it is a PushTag expression and the empty set otherwise.
@ -123,6 +125,8 @@ public:
std::map<int, Id> const& stackElements() const { return m_stackElements; } std::map<int, Id> const& stackElements() const { return m_stackElements; }
ExpressionClasses& expressionClasses() const { return *m_expressionClasses; } ExpressionClasses& expressionClasses() const { return *m_expressionClasses; }
std::map<Id, Id> const& storageContent() const { return m_storageContent; }
private: private:
/// Assigns a new equivalence class to the next sequence number of the given stack element. /// Assigns a new equivalence class to the next sequence number of the given stack element.
void setStackElement(int _stackHeight, Id _class); void setStackElement(int _stackHeight, Id _class);

24
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} )

9
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.

313
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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#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);
}

10
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);

73
libscrypt/crypto-mcf.c

@ -0,0 +1,73 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <float.h>
#include <stdint.h>
#include <math.h>
#ifndef S_SPLINT_S /* Including this here triggers a known bug in splint */
//#include <unistd.h>
#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;
}

0
libscrypt/crypto-scrypt-saltgen.c

100
libscrypt/crypto_scrypt-check.c

@ -0,0 +1,100 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#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 */
}

0
libscrypt/crypto_scrypt-hash.c

35
libscrypt/crypto_scrypt-hexconvert.c

@ -0,0 +1,35 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
/* 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;
}

9
libscrypt/crypto_scrypt-hexconvert.h

@ -0,0 +1,9 @@
#include <stdint.h>
/**
* 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);

342
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 <sys/types.h>
#ifndef _WIN32
#include <sys/mman.h>
#endif
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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);
}

56
libscrypt/libscrypt.h

@ -0,0 +1,56 @@
/*-
*/
#ifndef _CRYPTO_SCRYPT_H_
#define _CRYPTO_SCRYPT_H_
#include <stdint.h>
#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_ */

8
libscrypt/libscrypt.version

@ -0,0 +1,8 @@
libscrypt {
global: libscrypt_check;
libscrypt_hash;
libscrypt_mcf;
libscrypt_salt_gen;
libscrypt_scrypt;
local: *;
};

411
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 <sys/types.h>
#include <stdint.h>
#include <string.h>
#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));
}

70
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 <sys/types.h>
#include <stdint.h>
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_ */

26
libscrypt/slowequals.c

@ -0,0 +1,26 @@
#include <string.h>
/* 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<lena && i<lenb; i++)
{
diff |= a[i] ^ b[i];
}
if (diff == 0)
{
return 1;
}
else
{
return 0;
}
}

5
libscrypt/slowequals.h

@ -0,0 +1,5 @@
/* 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);

144
libscrypt/sysendian.h

@ -0,0 +1,144 @@
/*-
* Copyright 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.
*
* This file was originally written by Colin Percival as part of the Tarsnap
* online backup system.
*/
#ifndef _SYSENDIAN_H_
#define _SYSENDIAN_H_
/* If we don't have be64enc, the <sys/endian.h> we have isn't usable. */
#if !HAVE_DECL_BE64ENC
#undef HAVE_SYS_ENDIAN_H
#endif
#ifdef HAVE_SYS_ENDIAN_H
#include <sys/endian.h>
#else
#include <stdint.h>
#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_ */

20
libsolidity/ASTVisitor.h

@ -220,6 +220,26 @@ protected:
virtual void endVisitNode(ASTNode const&) { } 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<bool(ASTNode const&)> _onVisit,
std::function<void(ASTNode const&)> _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<bool(ASTNode const&)> m_onVisit;
std::function<void(ASTNode const&)> m_onEndVisit;
};
/** /**
* Utility class that visits the AST in depth-first order and calls a function on each node and each edge. * 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. * Child nodes are only visited if the node callback of the parent returns true.

38
libsolidity/CompilerStack.cpp

@ -55,12 +55,29 @@ const map<string, string> StandardSources = map<string, string>{
}; };
CompilerStack::CompilerStack(bool _addStandardSources): 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 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 CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
{ {
bool existed = m_sources.count(_name) != 0; bool existed = m_sources.count(_name) != 0;
@ -269,23 +286,6 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn); 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() void CompilerStack::resolveImports()
{ {
// topological sorting (depth first search) of the import graph, cutting potential cycles // topological sorting (depth first search) of the import graph, cutting potential cycles

5
libsolidity/CompilerStack.h

@ -72,6 +72,9 @@ public:
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources. /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
explicit CompilerStack(bool _addStandardSources = true); 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. /// 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. /// @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); } 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(); Contract();
}; };
void reset(bool _keepSources = false);
void resolveImports(); void resolveImports();
Contract const& getContract(std::string const& _contractName = "") const; Contract const& getContract(std::string const& _contractName = "") const;
Source const& getSource(std::string const& _sourceName = "") const; Source const& getSource(std::string const& _sourceName = "") const;
bool m_addStandardSources; ///< If true, standard sources are added.
bool m_parseSuccessful; bool m_parseSuccessful;
std::map<std::string const, Source> m_sources; std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext; std::shared_ptr<GlobalContext> m_globalContext;

28
libsolidity/InterfaceHandler.cpp

@ -107,17 +107,27 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef) unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
{ {
string ret = "contract " + _contractDef.getName() + "{"; string ret = "contract " + _contractDef.getName() + "{";
auto populateParameters = [](vector<string> const& _paramNames, vector<string> 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()) for (auto const& it: _contractDef.getInterfaceFunctions())
{ {
auto populateParameters = [](vector<string> const& _paramNames,
vector<string> 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() + ret += "function " + it.second->getDeclaration().getName() +
populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) + populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
(it.second->isConstant() ? "constant " : ""); (it.second->isConstant() ? "constant " : "");

39
libsolidity/StructuralGasEstimator.cpp

@ -23,6 +23,9 @@
#include "StructuralGasEstimator.h" #include "StructuralGasEstimator.h"
#include <map> #include <map>
#include <functional> #include <functional>
#include <memory>
#include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/KnownState.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
@ -38,14 +41,23 @@ StructuralGasEstimator::ASTGasConsumptionSelfAccumulated StructuralGasEstimator:
{ {
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, ""); solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
map<SourceLocation, GasMeter::GasConsumption> particularCosts; map<SourceLocation, GasMeter::GasConsumption> particularCosts;
GasMeter meter;
for (auto const& item: _items) ControlFlowGraph cfg(_items);
particularCosts[item.getLocation()] += meter.estimateMax(item); 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<ASTNode const*> finestNodes = finestNodesAtLocation(_ast);
ASTGasConsumptionSelfAccumulated gasCosts; ASTGasConsumptionSelfAccumulated gasCosts;
auto onNode = [&](ASTNode const& _node) auto onNode = [&](ASTNode const& _node)
{ {
if (!finestNodes.count(&_node))
return true;
gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.getLocation()]; gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.getLocation()];
return true; return true;
}; };
@ -108,3 +120,24 @@ map<ASTNode const*, GasMeter::GasConsumption> StructuralGasEstimator::breakToSta
// gasCosts should only contain non-overlapping locations // gasCosts should only contain non-overlapping locations
return gasCosts; return gasCosts;
} }
set<ASTNode const*> StructuralGasEstimator::finestNodesAtLocation(
vector<ASTNode const*> const& _roots
)
{
map<SourceLocation, ASTNode const*> locations;
set<ASTNode const*> nodes;
SimpleASTVisitor visitor(function<bool(ASTNode const&)>(), [&](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;
}

4
libsolidity/StructuralGasEstimator.h

@ -56,6 +56,10 @@ public:
ASTGasConsumptionSelfAccumulated const& _gasCosts, ASTGasConsumptionSelfAccumulated const& _gasCosts,
std::vector<ASTNode const*> const& _roots std::vector<ASTNode const*> const& _roots
); );
private:
/// @returns the set of AST nodes which are the finest nodes at their location.
std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
}; };
} }

22
libsolidity/Token.h

@ -142,34 +142,34 @@ namespace solidity
K(Delete, "delete", 0) \ K(Delete, "delete", 0) \
\ \
/* Keywords */ \ /* Keywords */ \
K(Anonymous, "anonymous", 0) \
K(Break, "break", 0) \ K(Break, "break", 0) \
K(Const, "constant", 0) \ K(Const, "constant", 0) \
K(Anonymous, "anonymous", 0) \
K(Continue, "continue", 0) \ K(Continue, "continue", 0) \
K(Contract, "contract", 0) \ K(Contract, "contract", 0) \
K(Default, "default", 0) \ K(Default, "default", 0) \
K(Do, "do", 0) \ K(Do, "do", 0) \
K(Else, "else", 0) \ K(Else, "else", 0) \
K(Enum, "enum", 0) \
K(Event, "event", 0) \ K(Event, "event", 0) \
K(External, "external", 0) \ K(External, "external", 0) \
K(Is, "is", 0) \
K(Indexed, "indexed", 0) \
K(For, "for", 0) \ K(For, "for", 0) \
K(Function, "function", 0) \ K(Function, "function", 0) \
K(If, "if", 0) \ K(If, "if", 0) \
K(Indexed, "indexed", 0) \
K(Internal, "internal", 0) \
K(Import, "import", 0) \ K(Import, "import", 0) \
K(Is, "is", 0) \
K(Mapping, "mapping", 0) \ K(Mapping, "mapping", 0) \
K(Modifier, "modifier", 0) \ K(Modifier, "modifier", 0) \
K(New, "new", 0) \ K(New, "new", 0) \
K(Public, "public", 0) \ K(Public, "public", 0) \
K(Private, "private", 0) \ K(Private, "private", 0) \
K(Internal, "internal", 0) \
K(Return, "return", 0) \ K(Return, "return", 0) \
K(Returns, "returns", 0) \ K(Returns, "returns", 0) \
K(Struct, "struct", 0) \ K(Struct, "struct", 0) \
K(Var, "var", 0) \ K(Var, "var", 0) \
K(While, "while", 0) \ K(While, "while", 0) \
K(Enum, "enum", 0) \
\ \
/* Ether subdenominations */ \ /* Ether subdenominations */ \
K(SubWei, "wei", 0) \ K(SubWei, "wei", 0) \
@ -304,15 +304,21 @@ namespace solidity
T(Identifier, NULL, 0) \ T(Identifier, NULL, 0) \
\ \
/* Keywords reserved for future. use*/ \ /* Keywords reserved for future. use*/ \
T(String, "string", 0) \ K(As, "as", 0) \
K(Case, "case", 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(Switch, "switch", 0) \
K(Throw, "throw", 0) \ K(Throw, "throw", 0) \
K(Try, "try", 0) \ K(Try, "try", 0) \
K(Catch, "catch", 0) \
K(Using, "using", 0) \
K(Type, "type", 0) \ K(Type, "type", 0) \
K(TypeOf, "typeof", 0) \ K(TypeOf, "typeof", 0) \
K(Using, "using", 0) \
/* Illegal token - not able to scan. */ \ /* Illegal token - not able to scan. */ \
T(Illegal, "ILLEGAL", 0) \ T(Illegal, "ILLEGAL", 0) \
\ \

3
libweb3jsonrpc/AccountHolder.cpp

@ -26,7 +26,8 @@
#include <ctime> #include <ctime>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/KeyManager.h> #include <libethcore/KeyManager.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;

15
mix/ClientModel.cpp

@ -387,24 +387,21 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId) std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId)
{ {
std::pair<QString, int> ret; std::pair<QString, int> ret = std::make_pair(_contractId, 0);
ret.first = _contractId;
ret.second = -1;
if (_contractId.startsWith("<") && _contractId.endsWith(">")) if (_contractId.startsWith("<") && _contractId.endsWith(">"))
{ {
QStringList values = ret.first.remove("<").remove(">").split(" - "); QStringList values = ret.first.remove("<").remove(">").split(" - ");
ret.first = values[0]; ret = std::make_pair(values[0], values[1].toUInt());
ret.second = values[1].toUInt();
} }
return ret; return ret;
} }
QString ClientModel::resolveToken(std::pair<QString, int> const& _value, vector<Address> const& _contracts) QString ClientModel::resolveToken(std::pair<QString, int> const& _value, vector<Address> const& _contracts)
{ {
if (_value.second != -1) if (_contracts.size() > 0)
return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref())); return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref()));
else else
return _value.first; return _value.first;
} }
std::pair<QString, int> ClientModel::retrieveToken(QString const& _value, vector<Address> const& _contracts) std::pair<QString, int> ClientModel::retrieveToken(QString const& _value, vector<Address> const& _contracts)

123
mix/CodeModel.cpp

@ -47,7 +47,7 @@
using namespace dev::mix; using namespace dev::mix;
const std::set<std::string> c_predefinedContracts = const std::set<std::string> 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 namespace
@ -59,7 +59,7 @@ class CollectLocalsVisitor: public ASTConstVisitor
{ {
public: public:
CollectLocalsVisitor(QHash<LocationPair, SolidityDeclaration>* _locals): CollectLocalsVisitor(QHash<LocationPair, SolidityDeclaration>* _locals):
m_locals(_locals), m_functionScope(false) {} m_locals(_locals), m_functionScope(false) {}
private: private:
LocationPair nodeLocation(ASTNode const& _node) LocationPair nodeLocation(ASTNode const& _node)
@ -99,7 +99,7 @@ class CollectLocationsVisitor: public ASTConstVisitor
{ {
public: public:
CollectLocationsVisitor(SourceMap* _sourceMap): CollectLocationsVisitor(SourceMap* _sourceMap):
m_sourceMap(_sourceMap) {} m_sourceMap(_sourceMap) {}
private: private:
LocationPair nodeLocation(ASTNode const& _node) LocationPair nodeLocation(ASTNode const& _node)
@ -197,6 +197,8 @@ CodeModel::~CodeModel()
stop(); stop();
disconnect(this); disconnect(this);
releaseContracts(); releaseContracts();
if (m_gasCostsMaps)
delete m_gasCostsMaps;
} }
void CodeModel::stop() void CodeModel::stop()
@ -313,29 +315,52 @@ void CodeModel::runCompilationJob(int _jobId)
} }
catch (dev::Exception const& _exception) catch (dev::Exception const& _exception)
{ {
std::ostringstream error; std::stringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs); solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs);
QString message = QString::fromStdString(error.str()); QString message = QString::fromStdString(error.str());
QString sourceName; QVariantMap firstLocation;
if (SourceLocation const* location = boost::get_error_info<solidity::errinfo_sourceLocation>(_exception)) QVariantList secondLocations;
if (SourceLocation const* first = boost::get_error_info<solidity::errinfo_sourceLocation>(_exception))
firstLocation = resolveCompilationErrorLocation(cs, *first);
if (SecondarySourceLocation const* second = boost::get_error_info<solidity::errinfo_secondarySourceLocation>(_exception))
{ {
if (location->sourceName) for (auto const& c: second->infos)
sourceName = QString::fromStdString(*location->sourceName); secondLocations.push_back(resolveCompilationErrorLocation(cs, c.second));
if (!sourceName.isEmpty())
if (CompiledContract* contract = contractByDocumentId(sourceName))
message = message.replace(sourceName, contract->contract()->name()); //substitute the location to match our contract names
} }
compilationError(message, sourceName); compilationError(message, firstLocation, secondLocations);
} }
m_compiling = false; m_compiling = false;
emit stateChanged(); emit stateChanged();
} }
QVariantMap CodeModel::resolveCompilationErrorLocation(CompilerStack const& _compiler, SourceLocation const& _location)
{
std::tuple<int, int, int, int> 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) void CodeModel::gasEstimation(solidity::CompilerStack const& _cs)
{ {
if (m_gasCostsMaps) if (m_gasCostsMaps)
m_gasCostsMaps->deleteLater(); m_gasCostsMaps->deleteLater();
m_gasCostsMaps = new GasMapWrapper(this); m_gasCostsMaps = new GasMapWrapper;
for (std::string n: _cs.getContractNames()) for (std::string n: _cs.getContractNames())
{ {
ContractDefinition const& contractDefinition = _cs.getContractDefinition(n); ContractDefinition const& contractDefinition = _cs.getContractDefinition(n);
@ -411,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 //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(); })) 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()) if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface())
@ -461,59 +486,59 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
switch (_type->getCategory()) switch (_type->getCategory())
{ {
case Type::Category::Integer: case Type::Category::Integer:
{ {
IntegerType const* it = dynamic_cast<IntegerType const*>(_type); IntegerType const* it = dynamic_cast<IntegerType const*>(_type);
r.size = it->getNumBits() / 8; r.size = it->getNumBits() / 8;
r.type = it->isAddress() ? SolidityType::Type::Address : it->isSigned() ? SolidityType::Type::SignedInteger : SolidityType::Type::UnsignedInteger; r.type = it->isAddress() ? SolidityType::Type::Address : it->isSigned() ? SolidityType::Type::SignedInteger : SolidityType::Type::UnsignedInteger;
} }
break; break;
case Type::Category::Bool: case Type::Category::Bool:
r.type = SolidityType::Type::Bool; r.type = SolidityType::Type::Bool;
break; break;
case Type::Category::FixedBytes: case Type::Category::FixedBytes:
{ {
FixedBytesType const* b = dynamic_cast<FixedBytesType const*>(_type); FixedBytesType const* b = dynamic_cast<FixedBytesType const*>(_type);
r.type = SolidityType::Type::Bytes; r.type = SolidityType::Type::Bytes;
r.size = static_cast<unsigned>(b->getNumBytes()); r.size = static_cast<unsigned>(b->getNumBytes());
} }
break; break;
case Type::Category::Contract: case Type::Category::Contract:
r.type = SolidityType::Type::Address; r.type = SolidityType::Type::Address;
break; break;
case Type::Category::Array: case Type::Category::Array:
{
ArrayType const* array = dynamic_cast<ArrayType const*>(_type);
if (array->isByteArray())
r.type = SolidityType::Type::Bytes;
else
{ {
ArrayType const* array = dynamic_cast<ArrayType const*>(_type); SolidityType elementType = nodeType(array->getBaseType().get());
if (array->isByteArray()) elementType.name = r.name;
r.type = SolidityType::Type::Bytes; r = elementType;
else
{
SolidityType elementType = nodeType(array->getBaseType().get());
elementType.name = r.name;
r = elementType;
}
r.count = static_cast<unsigned>(array->getLength());
r.dynamicSize = _type->isDynamicallySized();
r.array = true;
} }
r.count = static_cast<unsigned>(array->getLength());
r.dynamicSize = _type->isDynamicallySized();
r.array = true;
}
break; break;
case Type::Category::Enum: case Type::Category::Enum:
{ {
r.type = SolidityType::Type::Enum; r.type = SolidityType::Type::Enum;
EnumType const* e = dynamic_cast<EnumType const*>(_type); EnumType const* e = dynamic_cast<EnumType const*>(_type);
for(auto const& enumValue: e->getEnumDefinition().getMembers()) for(auto const& enumValue: e->getEnumDefinition().getMembers())
r.enumNames.push_back(QString::fromStdString(enumValue->getName())); r.enumNames.push_back(QString::fromStdString(enumValue->getName()));
} }
break; break;
case Type::Category::Struct: case Type::Category::Struct:
{
r.type = SolidityType::Type::Struct;
StructType const* s = dynamic_cast<StructType const*>(_type);
for(auto const& structMember: s->getMembers())
{ {
r.type = SolidityType::Type::Struct; auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.name);
StructType const* s = dynamic_cast<StructType const*>(_type); r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.name), nodeType(structMember.type.get()), slotAndOffset.first, slotAndOffset.second });
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 });
}
} }
}
break; break;
case Type::Category::Function: case Type::Category::Function:
case Type::Category::IntegerConstant: case Type::Category::IntegerConstant:

5
mix/CodeModel.h

@ -137,7 +137,7 @@ class GasMapWrapper: public QObject
Q_PROPERTY(GasCostsMaps gasMaps MEMBER m_gasMaps CONSTANT) Q_PROPERTY(GasCostsMaps gasMaps MEMBER m_gasMaps CONSTANT)
public: public:
GasMapWrapper(QObject* _parent): QObject(_parent){} GasMapWrapper(QObject* _parent = nullptr): QObject(_parent){}
void push(QString _source, int _start, int _end, QString _value, bool _isInfinite); void push(QString _source, int _start, int _end, QString _value, bool _isInfinite);
bool contains(QString _key); bool contains(QString _key);
void insert(QString _source, QVariantList _variantList); void insert(QString _source, QVariantList _variantList);
@ -216,7 +216,7 @@ signals:
/// Emitted on compilation complete /// Emitted on compilation complete
void compilationComplete(); void compilationComplete();
/// Emitted on compilation error /// 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 /// Internal signal used to transfer compilation job to background thread
void scheduleCompilationJob(int _jobId); void scheduleCompilationJob(int _jobId);
/// Emitted if there are any changes in the code model /// Emitted if there are any changes in the code model
@ -239,6 +239,7 @@ private:
void stop(); void stop();
void releaseContracts(); void releaseContracts();
void collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames); void collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames);
QVariantMap resolveCompilationErrorLocation(dev::solidity::CompilerStack const& _cs, dev::SourceLocation const& _location);
std::atomic<bool> m_compiling; std::atomic<bool> m_compiling;
mutable dev::Mutex x_contractMap; mutable dev::Mutex x_contractMap;

4
mix/qml/CodeEditorView.qml

@ -74,8 +74,8 @@ Item {
}); });
} }
editor.document = document; editor.document = document;
editor.sourceName = document.documentId;
editor.setFontSize(editorSettings.fontSize); editor.setFontSize(editorSettings.fontSize);
editor.sourceName = document.documentId;
editor.setText(data, document.syntaxMode); editor.setText(data, document.syntaxMode);
editor.changeGeneration(); editor.changeGeneration();
} }
@ -180,7 +180,7 @@ Item {
Connections { Connections {
target: codeModel target: codeModel
onCompilationError: { onCompilationError: {
sourceInError = _sourceName; sourceInError = _firstErrorLoc.source;
} }
onCompilationComplete: { onCompilationComplete: {
sourceInError = ""; sourceInError = "";

29
mix/qml/QBoolTypeView.qml

@ -6,22 +6,37 @@ Item
id: editRoot id: editRoot
property string value property string value
property string defaultValue property string defaultValue
property alias readOnly: !boolCombo.enabled property bool readOnly: !boolCombo.enabled
height: 20 height: 20
width: 150 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 { Rectangle {
anchors.fill: parent anchors.fill: parent
ComboBox ComboBox
{ {
property bool inited: false property bool inited;
Component.onCompleted: Component.onCompleted:
{ {
if (value === "") init();
currentIndex = parseInt(defaultValue); inited = true;
else
currentIndex = parseInt(value);
inited = true
} }
id: boolCombo id: boolCombo

15
mix/qml/StateDialog.qml

@ -507,8 +507,13 @@ Dialog {
Button { Button {
text: qsTr("OK") text: qsTr("OK")
onClicked: { onClicked: {
close() if (titleField.text === "")
accepted() alertDialog.open()
else
{
close()
accepted()
}
} }
} }
Button { Button {
@ -517,6 +522,12 @@ Dialog {
} }
} }
MessageDialog
{
id: alertDialog
text: qsTr("Please provide a name.")
}
ListModel { ListModel {
id: accountsModel id: accountsModel

2
mix/qml/StateListModel.qml

@ -225,6 +225,7 @@ Item {
var ctorTr = defaultTransactionItem(); var ctorTr = defaultTransactionItem();
ctorTr.functionId = c; ctorTr.functionId = c;
ctorTr.contractId = c; ctorTr.contractId = c;
ctorTr.label = qsTr("Deploy") + " " + ctorTr.contractId;
ctorTr.sender = item.accounts[0].secret; ctorTr.sender = item.accounts[0].secret;
item.transactions.push(ctorTr); item.transactions.push(ctorTr);
} }
@ -265,6 +266,7 @@ Item {
var ctorTr = defaultTransactionItem(); var ctorTr = defaultTransactionItem();
ctorTr.functionId = c; ctorTr.functionId = c;
ctorTr.contractId = c; ctorTr.contractId = c;
ctorTr.label = qsTr("Deploy") + " " + ctorTr.contractId;
ctorTr.sender = state.accounts[0].secret; ctorTr.sender = state.accounts[0].secret;
state.transactions.push(ctorTr); state.transactions.push(ctorTr);
changed = true; changed = true;

3
mix/qml/StructView.qml

@ -93,6 +93,9 @@ Column
else else
item.value = getValue(); item.value = getValue();
if (ptype.category === QSolidityType.Bool)
item.init();
item.onValueChanged.connect(function() { item.onValueChanged.connect(function() {
vals[pname] = item.value; vals[pname] = item.value;
valueChanged(); valueChanged();

3
mix/qml/TransactionDialog.qml

@ -94,7 +94,6 @@ Dialog {
function loadCtorParameters(contractId) function loadCtorParameters(contractId)
{ {
paramsModel = []; paramsModel = [];
console.log(contractId);
var contract = codeModel.contracts[contractId]; var contract = codeModel.contracts[contractId];
if (contract) { if (contract) {
var params = contract.contract.constructor.parameters; var params = contract.contract.constructor.parameters;
@ -154,7 +153,7 @@ Dialog {
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) { if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var contract = codeModel.contracts[contractFromToken(recipients.currentValue())]; var contract = codeModel.contracts[contractFromToken(recipients.currentValue())];
if (contract) { if (contract) {
var func = contract.contract.functions[functionComboBox.currentIndex]; var func = contract.contract.functions[functionComboBox.currentIndex - 1];
if (func) { if (func) {
var parameters = func.parameters; var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++) for (var p = 0; p < parameters.length; p++)

22
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 Button
{ {
id: mineBtn
anchors.rightMargin: 9 anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
action: mineAction action: mineAction

19
mix/qml/WebCodeEditor.qml

@ -152,17 +152,20 @@ Item {
} }
function compilationError(error, sourceName) function compilationError(error, firstLocation, secondLocations)
{ {
if (sourceName !== parent.sourceName)
return;
if (!editorBrowser || !error) if (!editorBrowser || !error)
return; return;
var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); var detail = error.split('\n')[0];
if (errorInfo.line && errorInfo.column) var reg = detail.match(/:\d+:\d+:/g);
editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); if (reg !== null)
else detail = detail.replace(reg[0], "");
editorBrowser.runJavaScript("compilationComplete()", function(result) { }); displayErrorAnnotations(detail, firstLocation, secondLocations);
}
function displayErrorAnnotations(detail, location, secondaryErrors)
{
editorBrowser.runJavaScript("compilationError('" + sourceName + "', '" + JSON.stringify(location) + "', '" + detail + "', '" + JSON.stringify(secondaryErrors) + "')", function(result){});
} }
Timer Timer

23
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.opened = false;
this.line = line; this.rawContent = content;
this.column = column;
this.content = content.replace("Contract Error:", ""); this.content = content.replace("Contract Error:", "");
this.editor = editor; this.editor = editor;
this.errorMark = null; this.errorMark = null;
this.lineWidget = null; this.lineWidget = null;
this.init(); this.init();
this.open(); if (this.content)
this.open();
} }
ErrorAnnotation.prototype.init = function() ErrorAnnotation.prototype.init = function()
{ {
var separators = [';', ',', '\\\(', '\\\{', '\\\}', '\\\)', ':']; 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 });
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 });
} }
ErrorAnnotation.prototype.open = function() ErrorAnnotation.prototype.open = function()
{ {
if (this.line) if (this.location.start.line)
{ {
var node = document.createElement("div"); var node = document.createElement("div");
node.id = "annotation" node.id = "annotation"
node.innerHTML = this.content; node.innerHTML = this.content;
node.className = "CodeMirror-errorannotation-context"; 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; this.opened = true;
} }
} }
ErrorAnnotation.prototype.close = function() ErrorAnnotation.prototype.close = function()
{ {
this.lineWidget.clear(); if (this.lineWidget)
this.lineWidget.clear();
this.opened = false; this.opened = false;
} }

7
mix/qml/html/cm/inkpot.css

@ -52,7 +52,7 @@ span.CodeMirror-selectedtext { color: #ffffff !important; }
.CodeMirror-errorannotation { .CodeMirror-errorannotation {
border-bottom: 1px solid #DD3330; border-bottom: 1px solid #DD3330;
margin-bottom: 4px; margin-bottom: 4px;
} }
.CodeMirror-errorannotation-context { .CodeMirror-errorannotation-context {
font-family: monospace; font-family: monospace;
@ -63,3 +63,8 @@ span.CodeMirror-selectedtext { color: #ffffff !important; }
border-top: solid 2px #063742; border-top: solid 2px #063742;
} }
.CodeMirror-search-field
{
font-size: 12px;
}

1
mix/qml/html/cm/solarized.css

@ -194,4 +194,3 @@ span.CodeMirror-selectedtext { color: #586e75 !important; }
.CodeMirror-gasCosts { .CodeMirror-gasCosts {
border-bottom: double 1px #2aa198; border-bottom: double 1px #2aa198;
} }

57
mix/qml/html/codeeditor.js

@ -157,44 +157,49 @@ showWarning = function(content)
debugWarning = editor.addLineWidget(0, node, { coverGutter: false, above: true }); debugWarning = editor.addLineWidget(0, node, { coverGutter: false, above: true });
} }
var annotation = null; var annotations = [];
var compilationCompleteBool = true; var compilationCompleteBool = true;
compilationError = function(line, column, content) compilationError = function(currentSourceName, location, error, secondaryErrors)
{ {
compilationCompleteBool = false; compilationCompleteBool = false;
window.setTimeout(function(){ if (compilationCompleteBool)
if (compilationCompleteBool) return;
return; clearAnnotations();
line = parseInt(line); location = JSON.parse(location);
column = parseInt(column); if (location.source === currentSourceName)
if (line > 0) ensureAnnotation(location, error, "first");
line = line - 1; var lineError = location.start.line + 1;
if (column > 0) var errorOrigin = "Source " + location.contractName + " line " + lineError;
column = column - 1; secondaryErrors = JSON.parse(secondaryErrors);
for(var i in secondaryErrors)
if (annotation == null) {
annotation = new ErrorAnnotation(editor, line, column, content); if (secondaryErrors[i].source === currentSourceName)
else if (annotation.line !== line || annotation.column !== column || annotation.content !== content) ensureAnnotation(secondaryErrors[i], errorOrigin, "second");
{ }
annotation.destroy(); }
annotation = new ErrorAnnotation(editor, line, column, content);
} ensureAnnotation = function(location, error, type)
}, 500) {
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() compilationComplete = function()
{ {
if (annotation !== null) clearAnnotations();
{
annotation.destroy();
annotation = null;
}
compilationCompleteBool = true; compilationCompleteBool = true;
} }
goToCompilationError = function() 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) setFontSize = function(size)

9
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) void ImportTest::exportTest(bytes const& _output, State const& _statePost)
{ {
// export output // 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 // export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); 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) void checkOutput(bytes const& _output, json_spirit::mObject& _o)
{ {
int j = 0; 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()) for (auto const& d: _o["out"].get_array())
{ {
BOOST_CHECK_MESSAGE(_output[j] == toInt(d), "Output byte [" << j << "] different!"); BOOST_CHECK_MESSAGE(_output[j] == toInt(d), "Output byte [" << j << "] different!");

64
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 <http://www.gnu.org/licenses/>.
*/
/** @file SecretStore.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2015
* Secret store test functions.
*/
#include <fstream>
#include <random>
#include <boost/test/unit_test.hpp>
#include "../JsonSpiritHeaders.h"
#include <libdevcrypto/SecretStore.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/TrieDB.h>
#include <libdevcore/TrieHash.h>
#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()

4
test/libethereum/StateTestsFiller/stMemoryStressTestFiller.json

@ -58,7 +58,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000", "balance" : "1000000000000000000",
"nonce" : "0", "nonce" : "0",
"code" : "{ (RETURN 0 4294967297) } ", "code" : "{ (RETURN 0 4294967295) } ",
"storage": {} "storage": {}
}, },
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -98,7 +98,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000", "balance" : "1000000000000000000",
"nonce" : "0", "nonce" : "0",
"code" : "{[ 0 ] 1 (RETURN 0 4294967296) } ", "code" : "{[ 0 ] 1 (RETURN 0 4294967295) } ",
"storage": {} "storage": {}
}, },
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {

2
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); dev::test::executeTests("stMemoryStressTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
} }
#if ETH_SOLIDITY
BOOST_AUTO_TEST_CASE(stSolidityTest) BOOST_AUTO_TEST_CASE(stSolidityTest)
{ {
dev::test::executeTests("stSolidityTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); dev::test::executeTests("stSolidityTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
} }
#endif
BOOST_AUTO_TEST_CASE(stMemoryTest) BOOST_AUTO_TEST_CASE(stMemoryTest)
{ {

2
test/libevm/vm.cpp

@ -390,7 +390,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
} }
o["callcreates"] = fev.exportCallCreates(); 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["gas"] = toCompactHex(gas, HexPrefix::Add, 1);
o["logs"] = exportLog(fev.sub.logs); o["logs"] = exportLog(fev.sub.logs);
} }

60
test/GasMeter.cpp → test/libsolidity/GasMeter.cpp

@ -21,6 +21,8 @@
*/ */
#include <test/libsolidity/solidityExecutionFramework.h> #include <test/libsolidity/solidityExecutionFramework.h>
#include <libevmasm/GasMeter.h>
#include <libevmasm/KnownState.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/StructuralGasEstimator.h> #include <libsolidity/StructuralGasEstimator.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
@ -55,8 +57,21 @@ public:
); );
} }
void testCreationTimeGas(string const& _sourceCode, string const& _contractName = "")
{
compileAndRun(_sourceCode);
auto state = make_shared<KnownState>();
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: protected:
dev::solidity::CompilerStack m_compiler;
map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts; map<ASTNode const*, eth::GasMeter::GasConsumption> 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() BOOST_AUTO_TEST_SUITE_END()
} }

86
test/libsolidity/SolidityEndToEndTest.cpp

@ -4023,6 +4023,92 @@ BOOST_AUTO_TEST_CASE(overwriting_inheritance)
BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(6)); 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() BOOST_AUTO_TEST_SUITE_END()

11
test/libsolidity/solidityExecutionFramework.h

@ -44,11 +44,11 @@ public:
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
{ {
dev::solidity::CompilerStack compiler(m_addStandardSources); m_compiler.reset(false, m_addStandardSources);
compiler.addSource("", _sourceCode); m_compiler.addSource("", _sourceCode);
ETH_TEST_REQUIRE_NO_THROW(compiler.compile(m_optimize), "Compiling contract failed"); 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); sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty()); BOOST_REQUIRE(!m_output.empty());
return m_output; return m_output;
@ -160,12 +160,14 @@ protected:
BOOST_REQUIRE(executive.go()); BOOST_REQUIRE(executive.go());
m_state.noteSending(m_sender); m_state.noteSending(m_sender);
executive.finalize(); executive.finalize();
m_gasUsed = executive.gasUsed();
m_output = executive.out().toVector(); m_output = executive.out().toVector();
m_logs = executive.logs(); m_logs = executive.logs();
} }
bool m_optimize = false; bool m_optimize = false;
bool m_addStandardSources = false; bool m_addStandardSources = false;
dev::solidity::CompilerStack m_compiler;
Address m_sender; Address m_sender;
Address m_contractAddress; Address m_contractAddress;
eth::State m_state; eth::State m_state;
@ -173,6 +175,7 @@ protected:
u256 const m_gas = 100000000; u256 const m_gas = 100000000;
bytes m_output; bytes m_output;
eth::LogEntries m_logs; eth::LogEntries m_logs;
u256 m_gasUsed;
}; };
} }

Loading…
Cancel
Save