Browse Source

Merge branch 'develop' into netFix

cl-refactor
subtly 10 years ago
parent
commit
4d45c82019
  1. 1
      .gitignore
  2. 23
      CMakeLists.txt
  3. 3
      alethzero/CMakeLists.txt
  4. 4
      alethzero/Debugger.cpp
  5. 123
      alethzero/GetPassword.ui
  6. 20
      alethzero/Main.ui
  7. 150
      alethzero/MainWin.cpp
  8. 7
      alethzero/MainWin.h
  9. 30
      alethzero/Transact.cpp
  10. 11
      alethzero/Transact.h
  11. 6
      cmake/EthCompilerSettings.cmake
  12. 7
      cmake/EthDependencies.cmake
  13. 19
      cmake/FindMiniupnpc.cmake
  14. 322
      cmake/FindWindowsSDK.cmake
  15. 75
      eth/main.cpp
  16. 33
      ethkey/CMakeLists.txt
  17. 431
      ethkey/KeyAux.h
  18. 84
      ethkey/main.cpp
  19. 16
      ethminer/MinerAux.h
  20. 31
      evmjit/libevmjit-cpp/Env.cpp
  21. 16
      evmjit/libevmjit-cpp/JitVM.cpp
  22. 6
      evmjit/libevmjit-cpp/JitVM.h
  23. 22
      exp/main.cpp
  24. 1
      extdep/getstuff.bat
  25. 35
      libdevcore/Base64.cpp
  26. 1
      libdevcore/Base64.h
  27. 7
      libdevcore/Common.h
  28. 9
      libdevcore/CommonData.h
  29. 18
      libdevcore/TransientDirectory.cpp
  30. 2
      libdevcore/TrieHash.cpp
  31. 520
      libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp
  32. 1
      libdevcrypto/CMakeLists.txt
  33. 22
      libdevcrypto/Common.cpp
  34. 19
      libdevcrypto/Common.h
  35. 213
      libdevcrypto/SecretStore.cpp
  36. 31
      libdevcrypto/SecretStore.h
  37. 65
      libethash-cl/ethash_cl_miner.cpp
  38. 1
      libethash-cl/ethash_cl_miner.h
  39. 15
      libethash/internal.c
  40. 21
      libethash/io.c
  41. 17
      libethash/io.h
  42. 25
      libethcore/Common.cpp
  43. 5
      libethcore/Common.h
  44. 29
      libethcore/Ethash.cpp
  45. 2
      libethcore/Ethash.h
  46. 16
      libethcore/EthashAux.cpp
  47. 15
      libethcore/Exceptions.h
  48. 2
      libethcore/ICAP.h
  49. 78
      libethcore/KeyManager.cpp
  50. 26
      libethcore/KeyManager.h
  51. 2
      libethcore/Miner.h
  52. 2
      libethereum/AccountDiff.h
  53. 2
      libethereum/BlockChain.cpp
  54. 64
      libethereum/BlockQueue.cpp
  55. 2
      libethereum/BlockQueue.h
  56. 1
      libethereum/CanonBlockChain.h
  57. 36
      libethereum/Client.cpp
  58. 5
      libethereum/CommonNet.h
  59. 50
      libethereum/DownloadMan.cpp
  60. 113
      libethereum/DownloadMan.h
  61. 553
      libethereum/EthereumHost.cpp
  62. 44
      libethereum/EthereumHost.h
  63. 537
      libethereum/EthereumPeer.cpp
  64. 50
      libethereum/EthereumPeer.h
  65. 114
      libethereum/Executive.cpp
  66. 14
      libethereum/Executive.h
  67. 4
      libethereum/ExtVM.cpp
  68. 141
      libethereum/State.cpp
  69. 18
      libethereum/State.h
  70. 21
      libethereum/Transaction.cpp
  71. 3
      libethereum/Transaction.h
  72. 2
      libevm/ExtVMFace.h
  73. 10
      libevm/SmartVM.cpp
  74. 8
      libevm/SmartVM.h
  75. 43
      libevm/VM.cpp
  76. 17
      libevm/VM.h
  77. 5
      libevm/VMFace.h
  78. 14
      libevm/VMFactory.cpp
  79. 4
      libevm/VMFactory.h
  80. 42
      libevmasm/Assembly.cpp
  81. 6
      libevmasm/Assembly.h
  82. 2
      libevmasm/AssemblyItem.h
  83. 75
      libevmasm/BlockDeduplicator.cpp
  84. 16
      libevmasm/BlockDeduplicator.h
  85. 8
      libevmasm/ControlFlowGraph.cpp
  86. 27
      libevmasm/ExpressionClasses.cpp
  87. 5
      libevmasm/ExpressionClasses.h
  88. 9
      libevmasm/GasMeter.cpp
  89. 14
      libevmasm/GasMeter.h
  90. 128
      libevmasm/PathGasMeter.cpp
  91. 66
      libevmasm/PathGasMeter.h
  92. 2
      libevmasm/SemanticInformation.cpp
  93. 6
      libp2p/Host.cpp
  94. 1
      libp2p/Host.h
  95. 7
      libp2p/HostCapability.cpp
  96. 5
      libp2p/HostCapability.h
  97. 2
      libp2p/Session.h
  98. 10
      libp2p/UDP.h
  99. 24
      libscrypt/CMakeLists.txt
  100. 9
      libscrypt/LICENSE

1
.gitignore

@ -35,6 +35,7 @@ build_xc
# build system
build.*/
extdep/install
extdep/download
*.pyc

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

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

4
alethzero/Debugger.cpp

@ -82,7 +82,7 @@ bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transacti
bytesConstRef lastData;
h256 lastHash;
h256 lastDataHash;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
auto onOp = [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
VM& vm = *voidVM;
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
@ -104,7 +104,7 @@ bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transacti
levels.push_back(&history.back());
else
levels.resize(ext.depth);
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, static_cast<u256>(gas), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
_executive.go(onOp);
_executive.finalize();

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="importKey"/>
<addaction name="importKeyFile"/>
<addaction name="claimPresale"/>
<addaction name="exportKey"/>
<addaction name="reencryptAll"/>
<addaction name="reencryptKey"/>
<addaction name="killAccount"/>
<addaction name="separator"/>
<addaction name="loadJS"/>
@ -1656,7 +1659,7 @@ font-size: 14pt</string>
<string>&amp;Enable Local Addresses</string>
</property>
</action>
<action name="importKeyFile">
<action name="claimPresale">
<property name="enabled">
<bool>true</bool>
</property>
@ -1745,6 +1748,21 @@ font-size: 14pt</string>
<string>Co&amp;nfirm Transactions</string>
</property>
</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>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

150
alethzero/MainWin.cpp

@ -75,6 +75,7 @@
#include "WebPage.h"
#include "ExportState.h"
#include "ui_Main.h"
#include "ui_GetPassword.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
@ -467,7 +468,9 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered()
{
m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB);
m_transact.exec();
m_transact.setWindowFlags(Qt::Dialog);
m_transact.setWindowModality(Qt::WindowModal);
m_transact.show();
}
void Main::on_loadJS_triggered()
@ -698,12 +701,17 @@ Secret Main::retrieveSecret(Address const& _a) const
auto info = m_keyManager.accountDetails()[_a];
while (true)
{
if (Secret s = m_keyManager.secret(_a, [&](){
return QInputDialog::getText(const_cast<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();
}))
Secret s = m_keyManager.secret(_a, [&](){
QDialog d;
Ui_GetPassword gp;
gp.setupUi(&d);
d.setWindowTitle("Unlock Account");
gp.label->setText(QString("Enter the password for the account %2 (%1).").arg(QString::fromStdString(_a.abridged())).arg(QString::fromStdString(info.first)));
gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(info.second));
return d.exec() == QDialog::Accepted ? gp.entry->text().toStdString() : string();
});
if (s || QMessageBox::warning(nullptr, "Unlock Account", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel)
return s;
else if (QMessageBox::warning(const_cast<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();
}
std::string Main::getPassword(std::string const& _title, std::string const& _for)
std::string Main::getPassword(std::string const& _title, std::string const& _for, std::string* _hint, bool* _ok)
{
QString password;
while (true)
{
password = QInputDialog::getText(nullptr, QString::fromStdString(_title), QString::fromStdString(_for), QLineEdit::Password, QString());
bool ok;
password = QInputDialog::getText(nullptr, QString::fromStdString(_title), QString::fromStdString(_for), QLineEdit::Password, QString(), &ok);
if (!ok)
{
if (_ok)
*_ok = false;
return string();
}
if (password.isEmpty())
break;
QString confirm = QInputDialog::getText(nullptr, QString::fromStdString(_title), "Confirm this password by typing it again", QLineEdit::Password, QString());
if (password == confirm)
break;
QMessageBox::warning(nullptr, QString::fromStdString(_title), "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok);
}
if (!password.isEmpty() && _hint && !m_keyManager.haveHint(password.toStdString()))
*_hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString();
if (_ok)
*_ok = true;
return password.toStdString();
}
@ -797,8 +820,11 @@ void Main::on_importKey_triggered()
QString s = QInputDialog::getText(this, "Import Account Key", "Enter this account's name");
if (QMessageBox::question(this, "Additional Security?", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
{
std::string password = getPassword("Import Account Key", "Enter the password you would like to use for this key. Don't forget it!");
std::string hint = QInputDialog::getText(this, "Import Account Key", "Enter a hint to help you remember this password.").toStdString();
bool ok;
std::string hint;
std::string password = getPassword("Import Account Key", "Enter the password you would like to use for this key. Don't forget it!", &hint, &ok);
if (!ok)
return;
m_keyManager.import(k.secret(), s.toStdString(), password, hint);
}
else
@ -813,6 +839,33 @@ void Main::on_importKey_triggered()
}
void Main::on_importKeyFile_triggered()
{
QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)");
h128 uuid = m_keyManager.store().importKey(s.toStdString());
if (!uuid)
{
QMessageBox::warning(this, "Key File Invalid", "Could not find secret key definition. This is probably not an Web3 key file.");
return;
}
QString info = QInputDialog::getText(this, "Import Key File", "Enter a description of this key to help you recognise it in the future.");
QString pass;
for (Secret s; !s;)
{
s = Secret(m_keyManager.store().secret(uuid, [&](){
pass = QInputDialog::getText(this, "Import Key File", "Enter the password for the key to complete the import.", QLineEdit::Password);
return pass.toStdString();
}, false));
if (!s && QMessageBox::question(this, "Import Key File", "The password you provided is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel)
return;
}
QString hint = QInputDialog::getText(this, "Import Key File", "Enter a hint for this password to help you remember it.");
m_keyManager.importExisting(uuid, info.toStdString(), pass.toStdString(), hint.toStdString());
}
void Main::on_claimPresale_triggered()
{
QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)");
try
@ -1750,7 +1803,7 @@ void Main::on_accounts_doubleClicked()
}
}
static shh::FullTopic topicFromText(QString _s)
static shh::Topics topicFromText(QString _s)
{
shh::BuildTopic ret;
while (_s.size())
@ -1976,8 +2029,11 @@ void Main::on_newAccount_triggered()
QString s = QInputDialog::getText(this, "Create Account", "Enter this account's name");
if (QMessageBox::question(this, "Create Account", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
{
std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!");
std::string hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString();
bool ok = false;
std::string hint;
std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!", &hint, &ok);
if (!ok)
return;
m_keyManager.import(p.secret(), s.toStdString(), password, hint);
}
else
@ -1989,15 +2045,14 @@ void Main::on_killAccount_triggered()
{
if (ui->ourAccounts->currentRow() >= 0)
{
auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray();
auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray();
Address h((byte const*)hba.data(), Address::ConstructFromPointer);
auto k = m_keyManager.accountDetails()[h];
if (
ethereum()->balanceAt(h) != 0 &&
QMessageBox::critical(this, QString::fromStdString("Kill Account " + k.first + "?!"),
QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it. It, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n"
"Are you sure you want to continue?"),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + k.first + "?!"),
QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it.\r\nIt, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n"
"Are you sure you want to continue? \r\n If so, type 'YES' to confirm."),
QLineEdit::Normal, "NO");
if (s != "YES")
return;
m_keyManager.kill(h);
if (m_keyManager.accounts().empty())
@ -2009,6 +2064,57 @@ void Main::on_killAccount_triggered()
}
}
void Main::on_reencryptKey_triggered()
{
if (ui->ourAccounts->currentRow() >= 0)
{
auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray();
Address a((byte const*)hba.data(), Address::ConstructFromPointer);
QStringList kdfs = {"PBKDF2-SHA256", "Scrypt"};
bool ok = true;
KDF kdf = (KDF)kdfs.indexOf(QInputDialog::getItem(this, "Re-Encrypt Key", "Select a key derivation function to use for storing your key:", kdfs, kdfs.size() - 1, false, &ok));
if (!ok)
return;
std::string hint;
std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!\nEnter nothing to use your Master password.", &hint, &ok);
if (!ok)
return;
try {
auto pw = [&](){
auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.hint(a)), QLineEdit::Password, QString()).toStdString();
if (p.empty())
throw UnknownPassword();
return p;
};
while (!(password.empty() ? m_keyManager.recode(a, SemanticPassword::Master, pw, kdf) : m_keyManager.recode(a, password, hint, pw, kdf)))
if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel)
return;
}
catch (UnknownPassword&) {}
}
}
void Main::on_reencryptAll_triggered()
{
QStringList kdfs = {"PBKDF2-SHA256", "Scrypt"};
bool ok = false;
QString kdf = QInputDialog::getItem(this, "Re-Encrypt Key", "Select a key derivation function to use for storing your keys:", kdfs, kdfs.size() - 1, false, &ok);
if (!ok)
return;
try {
for (Address const& a: m_keyManager.accounts())
while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){
auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.hint(a))), QLineEdit::Password, QString()).toStdString();
if (p.empty())
throw UnknownPassword();
return p;
}, (KDF)kdfs.indexOf(kdf)))
if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel)
return;
}
catch (UnknownPassword&) {}
}
void Main::on_go_triggered()
{
if (!ui->net->isChecked())
@ -2081,10 +2187,10 @@ void Main::refreshWhispers()
shh::Envelope const& e = w.second;
shh::Message m;
for (pair<Public, Secret> const& i: m_server->ids())
if (!!(m = e.open(shh::FullTopic(), i.second)))
if (!!(m = e.open(shh::Topics(), i.second)))
break;
if (!m)
m = e.open(shh::FullTopic());
m = e.open(shh::Topics());
QString msg;
if (m.from())

7
alethzero/MainWin.h

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

30
alethzero/Transact.cpp

@ -39,7 +39,8 @@
#include <libnatspec/NatspecExpressionEvaluator.h>
#include <libethereum/Client.h>
#include <libethereum/Utility.h>
#include <libethereum/KeyManager.h>
#include <libethcore/KeyManager.h>
#if ETH_SERPENT
#include <libserpent/funcs.h>
#include <libserpent/util.h>
@ -76,6 +77,7 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
m_ethereum = _eth;
m_natSpecDB = _natSpecDB;
auto old = ui->from->currentIndex();
ui->from->clear();
for (auto const& i: m_accounts)
{
@ -84,6 +86,10 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(i))).arg(QString::fromStdString(d.first));
ui->from->addItem(s);
}
if (old > -1 && old < ui->from->count())
ui->from->setCurrentIndex(old);
else if (ui->from->count())
ui->from->setCurrentIndex(0);
}
bool Transact::isCreation() const
@ -301,6 +307,9 @@ void Transact::rejigData()
// Determine how much balance we have to play with...
//findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice());
auto s = fromAccount();
if (!s)
return;
auto b = ethereum()->balanceAt(s, PendingBlock);
m_allGood = true;
@ -344,7 +353,7 @@ void Transact::rejigData()
if (b < value() + baseGas * gasPrice())
{
// Not enough - bail.
bail("<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;
}
else
@ -417,6 +426,8 @@ Secret Transact::findSecret(u256 _totalReq) const
Address Transact::fromAccount()
{
if (ui->from->currentIndex() < 0 || ui->from->currentIndex() >= (int)m_accounts.size())
return Address();
auto it = m_accounts.begin();
std::advance(it, ui->from->currentIndex());
return *it;
@ -425,14 +436,19 @@ Address Transact::fromAccount()
void Transact::on_send_clicked()
{
// Secret s = findSecret(value() + fee());
Secret s = m_context->retrieveSecret(fromAccount());
auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock);
if (!s || b < value() + fee())
auto a = fromAccount();
auto b = ethereum()->balanceAt(a, PendingBlock);
if (!a || b < value() + fee())
{
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
QMessageBox::critical(nullptr, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount.", QMessageBox::Ok);
return;
}
Secret s = m_context->retrieveSecret(a);
if (!s)
return;
if (isCreation())
{
// If execution is a contract creation, add Natspec to
@ -467,7 +483,7 @@ void Transact::on_debug_clicked()
auto b = ethereum()->balanceAt(from, PendingBlock);
if (!from || b < value() + fee())
{
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount.");
return;
}

11
alethzero/Transact.h

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

6
cmake/EthCompilerSettings.cmake

@ -37,13 +37,15 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests)
# declare Windows XP requirement
# undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions
add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501 /DNOMINMAX)
# define miniupnp static library
add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501 /DNOMINMAX /DMINIUPNP_STATICLIB)
# disable empty object file warning
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
# warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification
# warning LNK4099: pdb was not found with lib
# stack size 16MB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:16777216")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:33554432")
# windows likes static
if (NOT ETH_STATIC)
message("Forcing static linkage for MSVC.")

7
cmake/EthDependencies.cmake

@ -23,10 +23,11 @@ set(ETH_SCRIPTS_DIR ${CMAKE_SOURCE_DIR}/cmake/scripts)
# TODO use proper version of windows SDK (32 vs 64)
# TODO make it possible to use older versions of windows SDK (7.0+ should also work)
# TODO it windows SDK is NOT FOUND, throw ERROR
# from https://github.com/rpavlik/cmake-modules/blob/master/FindWindowsSDK.cmake
if (WIN32)
set (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:/Program Files/Windows Kits/8.1/Lib/winv6.3/um/x86")
message(" - Found windows 8.1 SDK")
#set (CMAKE_PREFIX_PATH "C:/Program Files/Windows Kits/8.1/Lib/winv6.3/um/x64")
find_package(WINDOWSSDK REQUIRED)
message(" - WindowsSDK dirs: ${WINDOWSSDK_DIRS}")
set (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOWSSDK_DIRS})
endif()
# homebrew installs qts in opt

19
cmake/FindMiniupnpc.cmake

@ -14,17 +14,32 @@ find_path(
MINIUPNPC_INCLUDE_DIR
NAMES miniupnpc/miniupnpc.h
DOC "miniupnpc include dir"
)
)
find_library(
MINIUPNPC_LIBRARY
NAMES miniupnpc
DOC "miniupnpc library"
)
)
set(MINIUPNPC_INCLUDE_DIRS ${MINIUPNPC_INCLUDE_DIR})
set(MINIUPNPC_LIBRARIES ${MINIUPNPC_LIBRARY})
# debug library on windows
# same naming convention as in QT (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library(
MINIUPNPC_LIBRARY_DEBUG
NAMES miniupnpcd
DOC "miniupnpc debug library"
)
set(MINIUPNPC_LIBRARIES "iphlpapi" optimized ${MINIUPNPC_LIBRARIES} debug ${MINIUPNPC_LIBRARY_DEBUG})
endif()
# handle the QUIETLY and REQUIRED arguments and set MINIUPNPC_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)

322
cmake/FindWindowsSDK.cmake

@ -0,0 +1,322 @@
# - Find the Windows SDK aka Platform SDK
#
# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK
#
# Variables:
# WINDOWSSDK_FOUND - if any version of the windows or platform SDK was found that is usable with the current version of visual studio
# WINDOWSSDK_LATEST_DIR
# WINDOWSSDK_LATEST_NAME
# WINDOWSSDK_FOUND_PREFERENCE - if we found an entry indicating a "preferred" SDK listed for this visual studio version
# WINDOWSSDK_PREFERRED_DIR
# WINDOWSSDK_PREFERRED_NAME
#
# WINDOWSSDK_DIRS - contains no duplicates, ordered most recent first.
# WINDOWSSDK_PREFERRED_FIRST_DIRS - contains no duplicates, ordered with preferred first, followed by the rest in descending recency
#
# Functions:
# windowssdk_name_lookup(<directory> <output variable>) - Find the name corresponding with the SDK directory you pass in, or
# NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work.
#
# get_windowssdk_from_component(<file or dir> <output variable>) - Given a library or include dir,
# find the Windows SDK root dir corresponding to it, or NOTFOUND if unrecognized.
#
# get_windowssdk_library_dirs(<directory> <output variable>) - Find the architecture-appropriate
# library directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
#
# get_windowssdk_include_dirs(<directory> <output variable>) - Find the
# include directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
#
# Requires these CMake modules:
# FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
#
# Original Author:
# 2012 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2012.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(_preferred_sdk_dirs)
set(_win_sdk_dirs)
set(_win_sdk_versanddirs)
if(MSVC_VERSION GREATER 1310) # Newer than VS .NET/VS Toolkit 2003
# Environment variable for SDK dir
if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL ""))
message(STATUS "Got $ENV{WindowsSDKDir} - Windows/Platform SDK directories: ${_win_sdk_dirs}")
list(APPEND _preferred_sdk_dirs "$ENV{WindowsSDKDir}")
endif()
if(MSVC_VERSION LESS 1600)
# Per-user current Windows SDK for VS2005/2008
get_filename_component(_sdkdir
"[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
ABSOLUTE)
if(EXISTS "${_sdkdir}")
list(APPEND _preferred_sdk_dirs "${_sdkdir}")
endif()
# System-wide current Windows SDK for VS2005/2008
get_filename_component(_sdkdir
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
ABSOLUTE)
if(EXISTS "${_sdkdir}")
list(APPEND _preferred_sdk_dirs "${_sdkdir}")
endif()
endif()
if(MSVC_VERSION LESS 1700)
# VC 10 and older has broad target support
set(_winsdk_vistaonly)
else()
# VC 11 by default targets Vista and later only, so we can add a few more SDKs that (might?) only work on vista+
if("${CMAKE_VS_PLATFORM_TOOLSET}" MATCHES "_xp")
# This is the XP-compatible v110 toolset
elseif("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v100")
# This is the VS2010 toolset
else()
if(NOT WINDOWSSDK_FOUND AND NOT WindowsSDK_FIND_QUIETLY)
message(STATUS "FindWindowsSDK: Detected Visual Studio 2012 or newer, not using the _xp toolset variant: including SDK versions that drop XP support in search!")
endif()
# These versions have no XP (and possibly Vista pre-SP1) support
set(_winsdk_vistaonly)
if(NOT MSVC_VERSION LESS 1800)
list(APPEND _winsdk_vistaonly
# Windows Software Development Kit (SDK) for Windows 8.1
# http://msdn.microsoft.com/en-gb/windows/desktop/bg162891
v8.1)
endif()
list(APPEND _winsdk_vistaonly
# Included in Visual Studio 2012
v8.0A
# Microsoft Windows SDK for Windows 8 and .NET Framework 4.5
# This is the first version to also include the DirectX SDK
# http://msdn.microsoft.com/en-US/windows/desktop/hh852363.aspx
v8.0
# Microsoft Windows SDK for Windows 7 and .NET Framework 4
# http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b
v7.1
)
endif()
endif()
foreach(_winsdkver
${_winsdk_vistaonly}
# Included in Visual Studio 2013
# Includes the v120_xp toolset
v8.1A
# Included with VS 2012 Update 1 or later
# Introduces v110_xp toolset
v7.1A
# Included with VS 2010
v7.0A
# Windows SDK for Windows 7 and .NET Framework 3.5 SP1
# Works with VC9
#http://www.microsoft.com/en-us/download/details.aspx?id=18950
v7.0
# Two versions call themselves "v6.1":
# Older:
# Windows Vista Update & .NET 3.0 SDK
# http://www.microsoft.com/en-us/download/details.aspx?id=14477
# Newer:
# Windows Server 2008 & .NET 3.5 SDK
# may have broken VS9SP1? they recommend v7.0 instead, or a KB...
# http://www.microsoft.com/en-us/download/details.aspx?id=24826
v6.1
# Included in VS 2008
v6.0A
# Microsoft Windows Software Development Kit for Windows Vista and .NET Framework 3.0 Runtime Components
# http://blogs.msdn.com/b/stanley/archive/2006/11/08/microsoft-windows-software-development-kit-for-windows-vista-and-net-framework-3-0-runtime-components.aspx
v6.0)
get_filename_component(_sdkdir
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\${_winsdkver};InstallationFolder]"
ABSOLUTE)
if(EXISTS "${_sdkdir}")
list(APPEND _win_sdk_dirs "${_sdkdir}")
list(APPEND
_win_sdk_versanddirs
"Windows SDK ${_winsdkver}"
"${_sdkdir}")
endif()
endforeach()
endif()
if(MSVC_VERSION GREATER 1200)
foreach(_platformsdkinfo
"D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1_Microsoft Platform SDK for Windows Server 2003 R2"
"8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3_Microsoft Platform SDK for Windows Server 2003 SP1")
string(SUBSTRING "${_platformsdkinfo}" 0 36 _platformsdkguid)
string(SUBSTRING "${_platformsdkinfo}" 37 -1 _platformsdkname)
foreach(HIVE HKEY_LOCAL_MACHINE HKEY_CURRENT_USER)
get_filename_component(_sdkdir
"[${HIVE}\\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\${_platformsdkguid};Install Dir]"
ABSOLUTE)
if(EXISTS "${_sdkdir}")
list(APPEND _win_sdk_dirs "${_sdkdir}")
list(APPEND _win_sdk_versanddirs "${_platformsdkname}" "${_sdkdir}")
endif()
endforeach()
endforeach()
endif()
set(_win_sdk_versanddirs
"${_win_sdk_versanddirs}"
CACHE
INTERNAL
"mapping between windows sdk version locations and names"
FORCE)
function(windowssdk_name_lookup _dir _outvar)
list(FIND _win_sdk_versanddirs "${_dir}" _diridx)
math(EXPR _nameidx "${_diridx} - 1")
if(${_nameidx} GREATER -1)
list(GET _win_sdk_versanddirs ${_nameidx} _sdkname)
else()
set(_sdkname "NOTFOUND")
endif()
set(${_outvar} "${_sdkname}" PARENT_SCOPE)
endfunction()
if(_win_sdk_dirs)
# Remove duplicates
list(REMOVE_DUPLICATES _win_sdk_dirs)
list(GET _win_sdk_dirs 0 WINDOWSSDK_LATEST_DIR)
windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}"
WINDOWSSDK_LATEST_NAME)
set(WINDOWSSDK_DIRS ${_win_sdk_dirs})
endif()
if(_preferred_sdk_dirs)
list(GET _preferred_sdk_dirs 0 WINDOWSSDK_PREFERRED_DIR)
windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}"
WINDOWSSDK_PREFERRED_NAME)
set(WINDOWSSDK_PREFERRED_FIRST_DIRS
${_preferred_sdk_dirs}
${_win_sdk_dirs})
list(REMOVE_DUPLICATES WINDOWSSDK_PREFERRED_FIRST_DIRS)
set(WINDOWSSDK_FOUND_PREFERENCE ON)
# In case a preferred dir was found that isn't found otherwise
#set(WINDOWSSDK_DIRS ${WINDOWSSDK_DIRS} ${WINDOWSSDK_PREFERRED_FIRST_DIRS})
#list(REMOVE_DUPLICATES WINDOWSSDK_DIRS)
else()
set(WINDOWSSDK_PREFERRED_DIR "${WINDOWSSDK_LATEST_DIR}")
set(WINDOWSSDK_PREFERRED_NAME "${WINDOWSSDK_LATEST_NAME}")
set(WINDOWSSDK_PREFERRED_FIRST_DIRS ${WINDOWSSDK_DIRS})
set(WINDOWSSDK_FOUND_PREFERENCE OFF)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WindowsSDK
"No compatible version of the Windows SDK or Platform SDK found."
WINDOWSSDK_DIRS)
if(WINDOWSSDK_FOUND)
if(NOT _winsdk_remembered_dirs STREQUAL WINDOWSSDK_DIRS)
set(_winsdk_remembered_dirs
"${WINDOWSSDK_DIRS}"
CACHE
INTERNAL
""
FORCE)
if(NOT WindowsSDK_FIND_QUIETLY)
foreach(_sdkdir ${WINDOWSSDK_DIRS})
windowssdk_name_lookup("${_sdkdir}" _sdkname)
message(STATUS " - Found ${_sdkname} at ${_sdkdir}")
endforeach()
endif()
endif()
# Internal: Architecture-appropriate library directory names.
if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")
set(_winsdk_archbare /arm) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch arm) # what the architecture used to be called
set(_winsdk_arch8 arm) # what the WDK for Win8+ calls this architecture
else()
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
set(_winsdk_archbare /x64) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch amd64) # what the architecture used to be called
set(_winsdk_arch8 x64) # what the WDK for Win8+ calls this architecture
else()
set(_winsdk_archbare ) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch i386) # what the architecture used to be called
set(_winsdk_arch8 x86) # what the WDK for Win8+ calls this architecture
endif()
endif()
function(get_windowssdk_from_component _component _var)
get_filename_component(_component "${_component}" ABSOLUTE)
file(TO_CMAKE_PATH "${_component}" _component)
foreach(_sdkdir ${WINDOWSSDK_DIRS})
get_filename_component(_sdkdir "${_sdkdir}" ABSOLUTE)
string(LENGTH "${_sdkdir}" _sdklen)
file(RELATIVE_PATH _rel "${_sdkdir}" "${_component}")
# If we don't have any "parent directory" items...
if(NOT "${_rel}" MATCHES "[.][.]")
set(${_var} "${_sdkdir}" PARENT_SCOPE)
return()
endif()
endforeach()
# Fail.
set(${_var} "NOTFOUND" PARENT_SCOPE)
endfunction()
function(get_windowssdk_library_dirs _winsdk_dir _var)
set(_result)
foreach(_suffix
"lib${_winsdk_archbare}" # SDKs like 7.1A
"lib/w2k/${_winsdk_arch}" # Win2k min requirement
"lib/wxp/${_winsdk_arch}" # WinXP min requirement
"lib/wnet/${_winsdk_arch}" # Win Server 2003 min requirement
"lib/wlh/${_winsdk_arch}" # Win Vista ("Long Horn") min requirement
"lib/wlh/um/${_winsdk_arch8}" # Win Vista ("Long Horn") min requirement
"lib/win7/${_winsdk_arch}" # Win 7 min requirement
"lib/win7/um/${_winsdk_arch8}" # Win 7 min requirement
"lib/win8/um/${_winsdk_arch8}" # Win 8 min requirement
"lib/win8/km/${_winsdk_arch8}" # Win 8 min requirement
"lib/winv6.3/km/${_winsdk_arch8}" # Win 8.1 min requirement
"lib/winv6.3/um/${_winsdk_arch8}" # Win 8.1 min requirement
)
# Check to see if a library actually exists here.
file(GLOB _libs "${_winsdk_dir}/${_suffix}/*.lib")
if(_libs)
list(APPEND _result "${_winsdk_dir}/${_suffix}")
endif()
endforeach()
if(NOT _result)
set(_result NOTFOUND)
endif()
set(${_var} ${_result} PARENT_SCOPE)
endfunction()
function(get_windowssdk_include_dirs _winsdk_dir _var)
set(_result)
foreach(_suffix
"Include"
"Include/shared"
"Include/um"
"Include/winrt"
"Include/km"
"Include/wdf"
)
# Check to see if a header file actually exists here.
file(GLOB _headers "${_winsdk_dir}/${_suffix}/*.h")
if(_headers)
list(APPEND _result "${_winsdk_dir}/${_suffix}")
endif()
endforeach()
if(NOT _result)
set(_result NOTFOUND)
endif()
set(${_var} ${_result} PARENT_SCOPE)
endfunction()
endif()

75
eth/main.cpp

@ -37,7 +37,8 @@
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libethereum/KeyManager.h>
#include <libethcore/KeyManager.h>
#include <libwebthree/WebThree.h>
#if ETH_JSCONSOLE || !ETH_TRUE
#include <libjsconsole/JSConsole.h>
@ -123,9 +124,11 @@ void help()
<< " --password <password> Give a password for a private key." << endl
<< endl
<< "Client transacting:" << endl
<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
/*<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (default: 30.679)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl*/
<< " --ask <wei> Set the minimum ask gas price under which no transactions will be mined (default 500000000000)." << endl
<< " --bid <wei> Set the bid gas price for to pay for transactions (default 500000000000)." << endl
<< endl
<< "Client mining:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
@ -298,8 +301,10 @@ int main(int argc, char** argv)
/// Transaction params
TransactionPriority priority = TransactionPriority::Medium;
double etherPrice = 30.679;
double blockFees = 15.0;
// double etherPrice = 30.679;
// double blockFees = 15.0;
u256 askPrice("500000000000");
u256 bidPrice("500000000000");
// javascript console
bool useConsole = false;
@ -463,7 +468,7 @@ int main(int argc, char** argv)
}
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
/* else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
{
@ -486,6 +491,30 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}*/
else if (arg == "--ask" && i + 1 < argc)
{
try
{
askPrice = u256(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
else if (arg == "--bid" && i + 1 < argc)
{
try
{
bidPrice = u256(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
else if ((arg == "-P" || arg == "--priority") && i + 1 < argc)
{
@ -627,7 +656,7 @@ int main(int argc, char** argv)
nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(),
netPrefs,
&nodesState);
auto toNumber = [&](string const& s) -> unsigned {
if (s == "latest")
return web3.ethereum()->number();
@ -692,15 +721,16 @@ int main(int argc, char** argv)
}
if (keyManager.exists())
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter your MASTER password: ");
if (!keyManager.load(masterPassword))
{
if (masterPassword.empty() || !keyManager.load(masterPassword))
while (true)
{
masterPassword = getPassword("Please enter your MASTER password: ");
if (keyManager.load(masterPassword))
break;
cout << "Password invalid. Try again." << endl;
masterPassword.clear();
}
}
}
else
{
while (masterPassword.empty())
@ -728,7 +758,8 @@ int main(int argc, char** argv)
cout << ethCredits();
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
// std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
std::shared_ptr<eth::TrivialGasPricer> gasPricer = make_shared<eth::TrivialGasPricer>(askPrice, bidPrice);
eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr;
StructuredLogger::starting(clientImplString, dev::Version);
if (c)
@ -827,7 +858,7 @@ int main(int argc, char** argv)
iss >> enable;
c->setForceMining(isTrue(enable));
}
else if (c && cmd == "setblockfees")
/* else if (c && cmd == "setblockfees")
{
iss >> blockFees;
try
@ -882,7 +913,7 @@ int main(int argc, char** argv)
cerr << "Unknown priority: " << m << endl;
}
cout << "Priority: " << (int)priority << "/8" << endl;
}
}*/
else if (cmd == "verbosity")
{
if (iss.peek() != -1)
@ -1185,7 +1216,7 @@ int main(int argc, char** argv)
{
OnOpFunc oof;
if (format == "pretty")
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
@ -1196,24 +1227,24 @@ int main(int argc, char** argv)
f << " STORAGE" << endl;
for (auto const& i: ext->state().storage(ext->myAddress))
f << showbase << hex << i.first << ": " << i.second << endl;
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32";
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << gas << " | -" << dec << gasCost << " | " << newMemSize << "x32";
};
else if (format == "standard")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
};
else if (format == "standard+")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress))
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
};
e.initialize(t);
if (!e.execute())

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

431
ethkey/KeyAux.h

@ -0,0 +1,431 @@
#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,
InspectBare,
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 == "--inspect-bare")
m_mode = OperationMode::InspectBare;
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::InspectBare || 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()));
if (!u)
{
cerr << "Cannot import " << i << " not a file or secret." << endl;
continue;
}
cout << "Successfully imported " << i << " as " << toUUID(u);
}
break;
case OperationMode::InspectBare:
for (auto const& i: m_inputs)
if (!contents(i).empty())
{
h128 u = store.readKey(i, false);
bytes s = store.secret(u, [&](){ return getPassword("Enter password for key " + i + ": "); });
cout << "Key " << i << ":" << endl;
cout << " UUID: " << toUUID(u) << ":" << endl;
cout << " Address: " << toAddress(Secret(s)).hex() << endl;
cout << " Secret: " << Secret(s).abridged() << endl;
}
else if (h128 u = fromUUID(i))
{
bytes s = store.secret(u, [&](){ return getPassword("Enter password for key " + toUUID(u) + ": "); });
cout << "Key " << i << ":" << endl;
cout << " Address: " << toAddress(Secret(s)).hex() << endl;
cout << " Secret: " << Secret(s).abridged() << endl;
}
else
cerr << "Couldn't inspect " << i << "; not found." << endl;
break;
case OperationMode::ExportBare: break;
case OperationMode::RecodeBare:
for (auto const& i: m_inputs)
if (h128 u = fromUUID(i))
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 " << i << "; not found." << endl;
case OperationMode::KillBare:
for (auto const& i: m_inputs)
if (h128 u = fromUUID(i))
store.kill(u);
else
cerr << "Couldn't kill " << i << "; 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;
}

16
ethminer/MinerAux.h

@ -16,10 +16,10 @@
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
/** @file MinerAux.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* Ethereum client.
* CLI module for mining.
*/
#include <thread>
@ -171,8 +171,16 @@ public:
m_minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--opencl")
{
m_minerType = MinerType::GPU;
miningThreads = 1;
if (!ProofOfWork::GPUMiner::haveSufficientGPUMemory())
{
cout << "No GPU device with sufficient memory was found. Defaulting to CPU" << endl;
m_minerType = MinerType::CPU;
}
else
{
m_minerType = MinerType::GPU;
miningThreads = 1;
}
}
else if (arg == "--no-precompute")
{

31
evmjit/libevmjit-cpp/Env.cpp

@ -54,7 +54,7 @@ extern "C"
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
u256 gas = *io_gas;
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight);
*io_gas = static_cast<int64_t>(gas);
*o_address = address;
}
@ -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)
{
auto value = llvm2eth(*_value);
auto receiveAddress = right160(*_receiveAddress);
auto codeAddress = right160(*_codeAddress);
const auto isCall = receiveAddress == codeAddress; // OPT: The same address pointer can be used if not CODECALL
CallParameters params;
params.value = llvm2eth(*_value);
params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress);
params.data = {_inBeg, (size_t)_inSize};
params.out = {_outBeg, (size_t)_outSize};
params.onOp = {};
const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL
*io_gas -= _callGas;
if (*io_gas < 0)
return false;
if (isCall && !_env->exists(receiveAddress))
if (isCall && !_env->exists(params.receiveAddress))
*io_gas -= static_cast<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");
*io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow
@ -87,17 +92,17 @@ extern "C"
return false;
auto ret = false;
auto callGas = u256{_callGas};
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress);
params.gas = u256{_callGas};
if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024)
ret = _env->call(params);
*io_gas += static_cast<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;
}
EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash)
{
auto hash = sha3({_begin, _size});
auto hash = sha3({_begin, (size_t)_size});
*o_hash = hash;
}
@ -125,7 +130,7 @@ extern "C"
if (_topic4)
topics.push_back(*_topic4);
_env->log(std::move(topics), {_beg, _size});
_env->log(std::move(topics), {_beg, (size_t)_size});
}
}

16
evmjit/libevmjit-cpp/JitVM.cpp

@ -18,27 +18,25 @@ namespace eth
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
{
using namespace jit;
auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (rejected)
{
cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter";
m_fallbackVM = VMFactory::create(VMKind::Interpreter, m_gas);
auto&& output = m_fallbackVM->go(_ext, _onOp, _step);
m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it
return output;
cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter";
m_fallbackVM = VMFactory::create(VMKind::Interpreter);
return m_fallbackVM->go(io_gas, _ext, _onOp, _step);
}
m_data.gas = static_cast<decltype(m_data.gas)>(m_gas);
m_data.gas = static_cast<decltype(m_data.gas)>(io_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size();
@ -78,7 +76,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
break;
}
m_gas = m_data.gas; // TODO: Remove m_gas field
io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)};
}

6
evmjit/libevmjit-cpp/JitVM.h

@ -10,12 +10,10 @@ namespace eth
class JitVM: public VMFace
{
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
public:
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
private:
friend class VMFactory;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {}
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT

22
exp/main.cpp

@ -36,7 +36,6 @@
#include <boost/algorithm/string.hpp>
#include <libdevcore/TrieDB.h>
#include <libdevcore/TrieHash.h>
/*
#include <libdevcore/RangeMask.h>
#include <libdevcore/Log.h>
#include <libdevcore/Common.h>
@ -50,24 +49,37 @@
#include <libethcore/Farm.h>
#include <libdevcore/FileSystem.h>
#include <libethereum/All.h>
#include <libethereum/KeyManager.h>
#include <libethcore/KeyManager.h>
#include <libethereum/AccountDiff.h>
#include <libethereum/DownloadMan.h>
#include <libethereum/Client.h>
#include <liblll/All.h>
#include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h>
#include <test/JsonSpiritHeaders.h>*/
#include <test/JsonSpiritHeaders.h>
using namespace std;
using namespace dev;
/*using namespace dev::eth;
using namespace dev::eth;
using namespace dev::p2p;
using namespace dev::shh;
namespace js = json_spirit;
namespace fs = boost::filesystem;
*/
#if 1
int main()
{
cdebug << pbkdf2("password", asBytes("salt"), 1, 32);
cdebug << pbkdf2("password", asBytes("salt"), 1, 16);
cdebug << pbkdf2("password", asBytes("salt"), 2, 16);
cdebug << pbkdf2("testpassword", fromHex("de5742f1f1045c402296422cee5a8a9ecf0ac5bf594deca1170d22aef33a79cf"), 262144, 16);
return 0;
}
#elif 0
int main()
{
cdebug << "EXP";

1
extdep/getstuff.bat

@ -13,6 +13,7 @@ call :download json-rpc-cpp 0.5.0
call :download leveldb 1.2
call :download microhttpd 0.9.2
call :download qt 5.4.1
call :download miniupnpc 1.9
goto :EOF

35
libdevcore/Base64.cpp

@ -27,20 +27,27 @@
/// Originally by René Nyffenegger, modified by some other guy and then devified by Gav Wood.
#include "Base64.h"
#include <iostream>
using namespace std;
using namespace dev;
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(byte c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
static inline byte find_base64_char_index(byte c) {
if ('A' <= c && c <= 'Z') return c - 'A';
else if ('a' <= c && c <= 'z') return c - 'a' + 1 + find_base64_char_index('Z');
else if ('0' <= c && c <= '9') return c - '0' + 1 + find_base64_char_index('z');
else if (c == '+') return 1 + find_base64_char_index('9');
else if (c == '/') return 1 + find_base64_char_index('+');
else return 1 + find_base64_char_index('/');
}
std::string dev::toBase64(bytesConstRef _in) {
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string ret;
int i = 0;
int j = 0;
@ -85,7 +92,7 @@ std::string dev::toBase64(bytesConstRef _in) {
}
bytes dev::fromBase64(std::string const& encoded_string) {
int in_len = encoded_string.size();
auto in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
@ -94,9 +101,9 @@ bytes dev::fromBase64(std::string const& encoded_string) {
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = find_base64_char_index(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
@ -109,11 +116,11 @@ bytes dev::fromBase64(std::string const& encoded_string) {
}
if (i) {
for (j = i; j <4; j++)
for (j = i; j < 4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
for (j = 0; j < 4; j++)
char_array_4[j] = find_base64_char_index(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);

1
libdevcore/Base64.h

@ -28,7 +28,6 @@
/// DEVified by Gav Wood.
#pragma once
#include <vector>
#include <string>
#include "Common.h"
#include "FixedHash.h"

7
libdevcore/Common.h

@ -39,11 +39,16 @@
#include <set>
#include <unordered_set>
#include <functional>
#include <string>
#include <boost/timer.hpp>
#include <boost/functional/hash.hpp>
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <boost/version.hpp>
#if (BOOST_VERSION == 105800)
#include "boost_multiprecision_number_compare_bug_workaround.hpp"
#endif
#include <boost/multiprecision/cpp_int.hpp>
#pragma warning(pop)
#pragma GCC diagnostic pop
@ -63,6 +68,8 @@ namespace dev
extern char const* Version;
static const std::string EmptyString;
// Binary data types.
using bytes = std::vector<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;
}
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;
}
}

18
libdevcore/TransientDirectory.cpp

@ -19,10 +19,12 @@
* @date 2015
*/
#include <thread>
#include <boost/filesystem.hpp>
#include "Exceptions.h"
#include "TransientDirectory.h"
#include "CommonIO.h"
#include "Log.h"
using namespace std;
using namespace dev;
@ -42,5 +44,19 @@ TransientDirectory::TransientDirectory(std::string const& _path):
TransientDirectory::~TransientDirectory()
{
boost::filesystem::remove_all(m_path);
boost::system::error_code ec;
boost::filesystem::remove_all(m_path, ec);
if (!ec)
return;
// In some cases, antivirus runnig on Windows will scan all the newly created directories.
// As a consequence, directory is locked and can not be deleted immediately.
// Retry after 10 milliseconds usually is successful.
// This will help our tests run smoothly in such environment.
this_thread::sleep_for(chrono::milliseconds(10));
ec.clear();
boost::filesystem::remove_all(m_path, ec);
if (!ec)
cwarn << "Failed to delete directory '" << m_path << "': " << ec.message();
}

2
libdevcore/TrieHash.cpp

@ -23,10 +23,8 @@
#include <libdevcore/TrieCommon.h>
#include <libdevcore/TrieDB.h> // @TODO replace ASAP!
#include <libdevcore/SHA3.h>
#include <libethcore/Common.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace dev
{

520
libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp

@ -0,0 +1,520 @@
// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58.
#ifdef BOOST_MP_COMPARE_HPP
#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp
#endif
///////////////////////////////////////////////////////////////////////////////
// Copyright 2012 John Maddock. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_MP_COMPARE_HPP
#define BOOST_MP_COMPARE_HPP
// A copy of boost/multiprecision/traits/is_backend.hpp
#ifndef BOOST_MP_IS_BACKEND_HPP
#define BOOST_MP_IS_BACKEND_HPP
#include <boost/mpl/has_xxx.hpp>
#include <boost/type_traits/conditional.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/multiprecision/detail/number_base.hpp>
#include <boost/multiprecision/detail/generic_interconvert.hpp>
namespace boost{ namespace multiprecision{ namespace detail{
BOOST_MPL_HAS_XXX_TRAIT_DEF(signed_types)
BOOST_MPL_HAS_XXX_TRAIT_DEF(unsigned_types)
BOOST_MPL_HAS_XXX_TRAIT_DEF(float_types)
template <class T>
struct is_backend
{
static const bool value = has_signed_types<T>::value && has_unsigned_types<T>::value && has_float_types<T>::value;
};
template <class Backend>
struct other_backend
{
typedef typename boost::conditional<
boost::is_same<number<Backend>, number<Backend, et_on> >::value,
number<Backend, et_off>, number<Backend, et_on> >::type type;
};
template <class B, class V>
struct number_from_backend
{
typedef typename boost::conditional <
boost::is_convertible<V, number<B> >::value,
number<B>,
typename other_backend<B>::type > ::type type;
};
template <bool b, class T, class U>
struct is_first_backend_imp{ static const bool value = false; };
template <class T, class U>
struct is_first_backend_imp<true, T, U>{ static const bool value = is_convertible<U, number<T, et_on> >::value || is_convertible<U, number<T, et_off> >::value; };
template <class T, class U>
struct is_first_backend : is_first_backend_imp<is_backend<T>::value, T, U> {};
template <bool b, class T, class U>
struct is_second_backend_imp{ static const bool value = false; };
template <class T, class U>
struct is_second_backend_imp<true, T, U>{ static const bool value = is_convertible<T, number<U> >::value || is_convertible<T, number<U, et_off> >::value; };
template <class T, class U>
struct is_second_backend : is_second_backend_imp<is_backend<U>::value, T, U> {};
}
}
}
#endif // BOOST_MP_IS_BACKEND_HPP
//
// Comparison operators for number.
//
namespace boost{ namespace multiprecision{
namespace default_ops{
template <class B>
inline bool eval_eq(const B& a, const B& b)
{
return a.compare(b) == 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_eq(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_eq(t.backend(), b);
}
template <class B>
inline bool eval_lt(const B& a, const B& b)
{
return a.compare(b) < 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_lt(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_lt(t.backend(), b);
}
template <class B>
inline bool eval_gt(const B& a, const B& b)
{
return a.compare(b) > 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_gt(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_gt(t.backend(), b);
}
} // namespace default_ops
namespace detail{
template <class Num, class Val>
struct is_valid_mixed_compare : public mpl::false_ {};
template <class B, expression_template_option ET, class Val>
struct is_valid_mixed_compare<number<B, ET>, Val> : public is_convertible<Val, number<B, ET> > {};
template <class B, expression_template_option ET>
struct is_valid_mixed_compare<number<B, ET>, number<B, ET> > : public mpl::false_ {};
template <class B, expression_template_option ET, class tag, class Arg1, class Arg2, class Arg3, class Arg4>
struct is_valid_mixed_compare<number<B, ET>, expression<tag, Arg1, Arg2, Arg3, Arg4> >
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
template <class tag, class Arg1, class Arg2, class Arg3, class Arg4, class B, expression_template_option ET>
struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
template <class Backend, expression_template_option ExpressionTemplates>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value != number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>&)
{
return false;
}
template <class Backend, expression_template_option ExpressionTemplates>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value == number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>& a)
{
using default_ops::eval_fpclassify;
return eval_fpclassify(a.backend()) == FP_NAN;
}
template <class Arithmetic>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value != number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic&)
{
return false;
}
template <class Arithmetic>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value == number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic& a)
{
return (boost::math::isnan)(a);
}
template <class T, class U>
inline BOOST_CONSTEXPR bool is_unordered_comparison(const T& a, const U& b)
{
return is_unordered_value(a) || is_unordered_value(b);
}
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator == (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator == (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator == (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator == (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return eval_eq(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return eval_eq(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_eq;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return eval_eq(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator != (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator != (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(a.backend(), number<Backend, et_on>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator != (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(b.backend(), number<Backend, et_on>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator != (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return true;
return !eval_eq(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return true;
return !eval_eq(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_eq;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return true;
return !eval_eq(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator < (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator < (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator < (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator < (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return eval_gt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return eval_lt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_lt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return eval_lt(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator > (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator > (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator > (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator > (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return a > t;
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return t > b;
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_gt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return t > t2;
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator <= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator <= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator <= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator <= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
if(detail::is_unordered_value(a) || detail::is_unordered_value(b))
return false;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return !eval_lt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return !eval_gt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_gt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return !eval_gt(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator >= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator >= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator >= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator >= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return !eval_gt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return !eval_lt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_lt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return !eval_lt(t.backend(), t2.backend());
}
}} // namespaces
#endif // BOOST_MP_COMPARE_HPP

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} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} scrypt)
target_link_libraries(${EXECUTABLE} devcore)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )

22
libdevcrypto/Common.cpp

@ -25,6 +25,7 @@
#include <chrono>
#include <thread>
#include <mutex>
#include <libscrypt/libscrypt.h>
#include <libdevcore/Guards.h>
#include <libdevcore/SHA3.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);
}
bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain)
bytes dev::encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain)
{
const int c_aesKeyLen = 16;
SecByteBlock key(_k.data(), c_aesKeyLen);
if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
return bytes();
SecByteBlock key(_k.data(), _k.size());
try
{
CTR_Mode<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;
SecByteBlock key(_k.data(), c_aesKeyLen);
if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
return bytes();
SecByteBlock key(_k.data(), _k.size());
try
{
CTR_Mode<AES>::Decryption d;
@ -180,6 +183,13 @@ bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations,
return ret;
}
bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen)
{
bytes ret(_dkLen);
libscrypt_scrypt((uint8_t const*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _n, _r, _p, ret.data(), ret.size());
return ret;
}
KeyPair KeyPair::create()
{
static boost::thread_specific_ptr<mt19937_64> s_eng;

19
libdevcrypto/Common.h

@ -98,18 +98,26 @@ bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Encrypt payload using ECIES standard with AES128-CTR.
void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypt payload using ECIES standard with AES128-CTR.
bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Encrypts payload with random IV/ctr using AES128-CTR.
std::pair<bytes, h128> encryptSymNoAuth(h128 const& _k, bytesConstRef _plain);
/// Encrypts payload with specified IV/ctr using AES128-CTR.
bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain);
bytes encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain);
/// Decrypts payload with specified IV/ctr using AES128-CTR.
bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher);
bytes decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher);
/// Encrypts payload with specified IV/ctr using AES128-CTR.
inline bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }
inline bytes encryptSymNoAuth(h256 const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }
/// Decrypts payload with specified IV/ctr using AES128-CTR.
inline bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }
inline bytes decryptSymNoAuth(h256 const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }
/// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash);
@ -123,6 +131,9 @@ bool verify(Public const& _k, Signature const& _s, h256 const& _hash);
/// Derive key via PBKDF2.
bytes pbkdf2(std::string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen = 32);
/// Derive key via Scrypt.
bytes scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen);
/// Simple class that represents a "key pair".
/// All of the data of the class can be regenerated from the secret key (m_secret) alone.
/// Actually stores a tuplet of secret, public and address (the right 160-bits of the public).

213
libdevcrypto/SecretStore.cpp

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

31
libdevcrypto/SecretStore.h

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

65
libethash-cl/ethash_cl_miner.cpp

@ -35,6 +35,7 @@
#include "ethash_cl_miner_kernel.h"
#define ETHASH_BYTES 32
#define ETHASH_CL_MINIMUM_MEMORY 2000000000
// workaround lame platforms
#if !CL_VERSION_1_2
@ -47,6 +48,9 @@
using namespace std;
// TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel
#define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
static void add_definition(std::string& source, char const* id, unsigned value)
{
char buf[256];
@ -72,7 +76,7 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic
cl::Platform::get(&platforms);
if (platforms.empty())
{
cout << "No OpenCL platforms found." << endl;
ETHCL_LOG("No OpenCL platforms found.");
return std::string();
}
@ -82,7 +86,7 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
cout << "No OpenCL devices found." << endl;
ETHCL_LOG("No OpenCL devices found.");
return std::string();
}
@ -107,7 +111,7 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
cl::Platform::get(&platforms);
if (platforms.empty())
{
cout << "No OpenCL platforms found." << endl;
ETHCL_LOG("No OpenCL platforms found.");
return 0;
}
@ -116,12 +120,53 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
cout << "No OpenCL devices found." << endl;
ETHCL_LOG("No OpenCL devices found.");
return 0;
}
return devices.size();
}
bool ethash_cl_miner::haveSufficientGPUMemory(unsigned _platformId)
{
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return false;
}
std::vector<cl::Device> devices;
unsigned platform_num = std::min<unsigned>(_platformId, platforms.size() - 1);
platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
ETHCL_LOG("No OpenCL devices found.");
return false;
}
for (cl::Device const& device: devices)
{
cl_ulong result;
device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
if (result >= ETHASH_CL_MINIMUM_MEMORY)
{
ETHCL_LOG(
"Found suitable OpenCL device [" << device.getInfo<CL_DEVICE_NAME>()
<< "] with " << result << " bytes of GPU memory"
);
return true;
}
else
ETHCL_LOG(
"OpenCL device " << device.getInfo<CL_DEVICE_NAME>()
<< " has insufficient GPU memory." << result <<
" bytes of memory found < " << ETHASH_CL_MINIMUM_MEMORY << " bytes of memory required"
);
}
return false;
}
void ethash_cl_miner::finish()
{
if (m_queue())
@ -135,7 +180,7 @@ bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned work
cl::Platform::get(&platforms);
if (platforms.empty())
{
cout << "No OpenCL platforms found." << endl;
ETHCL_LOG("No OpenCL platforms found.");
return false;
}
@ -143,25 +188,25 @@ bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned work
_platformId = std::min<unsigned>(_platformId, platforms.size() - 1);
cout << "Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str() << endl;
ETHCL_LOG("Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str());
// get GPU device of the default platform
std::vector<cl::Device> devices;
platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
cout << "No OpenCL devices found." << endl;
ETHCL_LOG("No OpenCL devices found.");
return false;
}
// use selected device
cl::Device& device = devices[std::min<unsigned>(_deviceId, devices.size() - 1)];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
cout << "Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")" << endl;
ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")");
if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
{
cout << "OpenCL 1.0 is not supported." << endl;
ETHCL_LOG("OpenCL 1.0 is not supported.");
return false;
}
if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
@ -193,7 +238,7 @@ bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned work
}
catch (cl::Error err)
{
cout << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str();
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false;
}
m_hash_kernel = cl::Kernel(program, "ethash_hash");

1
libethash-cl/ethash_cl_miner.h

@ -35,6 +35,7 @@ public:
static unsigned get_num_platforms();
static unsigned get_num_devices(unsigned _platformId = 0);
static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
static bool haveSufficientGPUMemory(unsigned _platformId = 0);
bool init(uint8_t const* _dag, uint64_t _dagSize, unsigned workgroup_size = 64, unsigned _platformId = 0, unsigned _deviceId = 0);
void finish();

15
libethash/internal.c

@ -364,11 +364,12 @@ static bool ethash_mmap(struct ethash_full* ret, FILE* f)
{
int fd;
char* mmapped_data;
errno = 0;
ret->file = f;
if ((fd = ethash_fileno(ret->file)) == -1) {
return false;
}
mmapped_data= mmap(
mmapped_data = mmap(
NULL,
(size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE,
PROT_READ | PROT_WRITE,
@ -400,38 +401,48 @@ ethash_full_t ethash_full_new_internal(
ret->file_size = (size_t)full_size;
switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) {
case ETHASH_IO_FAIL:
// ethash_io_prepare will do all ETHASH_CRITICAL() logging in fail case
goto fail_free_full;
case ETHASH_IO_MEMO_MATCH:
if (!ethash_mmap(ret, f)) {
ETHASH_CRITICAL("mmap failure()");
goto fail_close_file;
}
return ret;
case ETHASH_IO_MEMO_SIZE_MISMATCH:
// if a DAG of same filename but unexpected size is found, silently force new file creation
if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) {
ETHASH_CRITICAL("Could not recreate DAG file after finding existing DAG with unexpected size.");
goto fail_free_full;
}
// fallthrough to the mismatch case here, DO NOT go through match
case ETHASH_IO_MEMO_MISMATCH:
if (!ethash_mmap(ret, f)) {
ETHASH_CRITICAL("mmap failure()");
goto fail_close_file;
}
break;
}
if (!ethash_compute_full_data(ret->data, full_size, light, callback)) {
ETHASH_CRITICAL("Failure at computing DAG data.");
goto fail_free_full_data;
}
// after the DAG has been filled then we finalize it by writting the magic number at the beginning
if (fseek(f, 0, SEEK_SET) != 0) {
ETHASH_CRITICAL("Could not seek to DAG file start to write magic number.");
goto fail_free_full_data;
}
uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM;
if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
ETHASH_CRITICAL("Could not write magic number to DAG's beginning.");
goto fail_free_full_data;
}
if (fflush(f) != 0) {// make sure the magic number IS there
ETHASH_CRITICAL("Could not flush memory mapped data to DAG file. Insufficient space?");
goto fail_free_full_data;
}
fflush(f); // make sure the magic number IS there
return ret;
fail_free_full_data:

21
libethash/io.c

@ -21,6 +21,7 @@
#include "io.h"
#include <string.h>
#include <stdio.h>
#include <errno.h>
enum ethash_io_rc ethash_io_prepare(
char const* dirname,
@ -32,15 +33,19 @@ enum ethash_io_rc ethash_io_prepare(
{
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// reset errno before io calls
errno = 0;
// assert directory exists
if (!ethash_mkdir(dirname)) {
ETHASH_CRITICAL("Could not create the ethash directory");
goto end;
}
ethash_io_mutable_name(ETHASH_REVISION, &seedhash, mutable_name);
char* tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name));
if (!tmpfile) {
ETHASH_CRITICAL("Could not create the full DAG pathname");
goto end;
}
@ -52,6 +57,7 @@ enum ethash_io_rc ethash_io_prepare(
size_t found_size;
if (!ethash_file_size(f, &found_size)) {
fclose(f);
ETHASH_CRITICAL("Could not query size of DAG file: \"%s\"", tmpfile);
goto free_memo;
}
if (file_size != found_size - ETHASH_DAG_MAGIC_NUM_SIZE) {
@ -64,6 +70,7 @@ enum ethash_io_rc ethash_io_prepare(
if (fread(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
// I/O error
fclose(f);
ETHASH_CRITICAL("Could not read from DAG file: \"%s\"", tmpfile);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
@ -80,15 +87,25 @@ enum ethash_io_rc ethash_io_prepare(
// file does not exist, will need to be created
f = ethash_fopen(tmpfile, "wb+");
if (!f) {
ETHASH_CRITICAL("Could not create DAG file: \"%s\"", tmpfile);
goto free_memo;
}
// make sure it's of the proper size
if (fseek(f, (long int)(file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1), SEEK_SET) != 0) {
fclose(f);
ETHASH_CRITICAL("Could not seek to the end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo;
}
if (fputc('\n', f) == EOF) {
fclose(f);
ETHASH_CRITICAL("Could not write in the end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo;
}
if (fflush(f) != 0) {
fclose(f);
ETHASH_CRITICAL("Could not flush at end of DAG file: \"%s\". Insufficient space?", tmpfile);
goto free_memo;
}
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
goto set_file;

17
libethash/io.h

@ -54,6 +54,23 @@ enum ethash_io_rc {
#define snprintf(...) sprintf_s(__VA_ARGS__)
#endif
/**
* Logs a critical error in important parts of ethash. Should mostly help
* figure out what kind of problem (I/O, memory e.t.c.) causes a NULL
* ethash_full_t
*/
#ifdef ETHASH_PRINT_CRITICAL_OUTPUT
#define ETHASH_CRITICAL(...) \
do \
{ \
printf("ETHASH CRITICAL ERROR: "__VA_ARGS__); \
printf("\n"); \
fflush(stdout); \
} while (0)
#else
#define ETHASH_CRITICAL(...)
#endif
/**
* Prepares io for ethash
*

25
libethcore/Common.cpp

@ -35,7 +35,7 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 60;
const unsigned c_protocolVersion = 61;
#if ETH_FATDB
const unsigned c_minorProtocolVersion = 3;
const unsigned c_databaseBaseVersion = 9;
@ -104,5 +104,28 @@ std::string formatBalance(bigint const& _b)
return ret.str();
}
static void badBlockInfo(BlockInfo const& _bi, string const& _err)
{
cwarn << EthRedBold << "========================================================================";
cwarn << EthRedBold << "== Software Failure " + _err + string(max<int>(0, 44 - _err.size()), ' ') + " ==";
string bin = toString(_bi.number);
cwarn << EthRedBold << ("== Guru Meditation #" + string(max<int>(0, 8 - bin.size()), '0') + bin + "." + _bi.hash().abridged() + " ==");
cwarn << EthRedBold << "========================================================================";
}
void badBlock(bytesConstRef _block, string const& _err)
{
badBlockInfo(BlockInfo(_block, CheckNothing), _err);
cwarn << " Block:" << toHex(_block);
cwarn << " Block RLP:" << RLP(_block);
}
void badBlockHeader(bytesConstRef _header, string const& _err)
{
badBlockInfo(BlockInfo::fromHeader(_header, CheckNothing), _err);
cwarn << " Header:" << toHex(_header);
cwarn << " Header RLP:" << RLP(_header);;
}
}
}

5
libethcore/Common.h

@ -148,5 +148,10 @@ struct TransactionSkeleton
u256 gasPrice = UndefinedU256;
};
void badBlockHeader(bytesConstRef _header, std::string const& _err);
inline void badBlockHeader(bytes const& _header, std::string const& _err) { badBlockHeader(&_header, _err); }
void badBlock(bytesConstRef _header, std::string const& _err);
inline void badBlock(bytes const& _header, std::string const& _err) { badBlock(&_header, _err); }
}
}

29
libethcore/Ethash.cpp

@ -94,11 +94,13 @@ bool Ethash::preVerify(BlockInfo const& _header)
h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
return !!ethash_quick_check_difficulty(
(ethash_h256_t const*)_header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce,
(ethash_h256_t const*)_header.mixHash.data(),
(ethash_h256_t const*)boundary.data());
bool ret = !!ethash_quick_check_difficulty(
(ethash_h256_t const*)_header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce,
(ethash_h256_t const*)_header.mixHash.data(),
(ethash_h256_t const*)boundary.data());
return ret;
}
bool Ethash::verify(BlockInfo const& _header)
@ -112,6 +114,10 @@ bool Ethash::verify(BlockInfo const& _header)
auto result = EthashAux::eval(_header);
bool slow = result.value <= _header.boundary() && result.mixHash == _header.mixHash;
// cdebug << (slow ? "VERIFY" : "VERYBAD");
// cdebug << result.value.hex() << _header.boundary().hex();
// cdebug << result.mixHash.hex() << _header.mixHash.hex();
#if ETH_DEBUG || !ETH_TRUE
if (!pre && slow)
{
@ -142,8 +148,12 @@ void Ethash::CPUMiner::workLoop()
WorkPackage w = work();
EthashAux::FullType dag;
while (!shouldStop() && !(dag = EthashAux::full(w.seedHash)))
this_thread::sleep_for(chrono::milliseconds(500));
while (!shouldStop() && !dag)
{
while (!shouldStop() && EthashAux::computeFull(w.seedHash, true) != 100)
this_thread::sleep_for(chrono::milliseconds(500));
dag = EthashAux::full(w.seedHash, false);
}
h256 boundary = w.boundary;
unsigned hashCount = 1;
@ -353,6 +363,11 @@ void Ethash::GPUMiner::pause()
stopWorking();
}
bool Ethash::GPUMiner::haveSufficientGPUMemory()
{
return ethash_cl_miner::haveSufficientGPUMemory(s_platformId);
}
std::string Ethash::GPUMiner::platformInfo()
{
return ethash_cl_miner::platform_info(s_platformId, s_deviceId);

2
libethcore/Ethash.h

@ -87,6 +87,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo();
static bool haveSufficientGPUMemory() { return false; }
static void setDefaultPlatform(unsigned) {}
static void setDefaultDevice(unsigned) {}
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
@ -115,6 +116,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : 1; }
static std::string platformInfo();
static bool haveSufficientGPUMemory();
static unsigned getNumDevices();
static void setDefaultPlatform(unsigned _id) { s_platformId = _id; }
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }

16
libethcore/EthashAux.cpp

@ -133,9 +133,14 @@ bytesConstRef EthashAux::LightAllocation::data() const
EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb)
{
// cdebug << "About to call ethash_full_new...";
full = ethash_full_new(_light, _cb);
// cdebug << "Called OK.";
if (!full)
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new()"));
{
clog(DAGChannel) << "DAG Generation Failure. Reason: " << strerror(errno);
BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new"));
}
}
EthashAux::FullAllocation::~FullAllocation()
@ -170,9 +175,9 @@ EthashAux::FullType EthashAux::full(h256 const& _seedHash, bool _createIfMissing
if (_createIfMissing || computeFull(_seedHash, false) == 100)
{
s_dagCallback = _f;
cnote << "Loading from libethash...";
// cnote << "Loading from libethash...";
ret = make_shared<FullAllocation>(l->light, dagCallbackShim);
cnote << "Done loading.";
// cnote << "Done loading.";
DEV_GUARDED(get()->x_fulls)
get()->m_fulls[_seedHash] = get()->m_lastUsedFull = ret;
@ -238,8 +243,9 @@ Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{
if (FullType dag = get()->m_fulls[_seedHash].lock())
return dag->compute(_headerHash, _nonce);
DEV_GUARDED(get()->x_fulls)
if (FullType dag = get()->m_fulls[_seedHash].lock())
return dag->compute(_headerHash, _nonce);
DEV_IF_THROWS(return EthashAux::get()->light(_seedHash)->compute(_headerHash, _nonce))
{
return Ethash::Result{ ~h256(), h256() };

15
libethcore/Exceptions.h

@ -49,27 +49,28 @@ struct FeeTooSmall: virtual dev::Exception {};
struct TooMuchGasUsed: virtual dev::Exception {};
struct ExtraDataTooBig: virtual dev::Exception {};
struct InvalidSignature: virtual dev::Exception {};
class InvalidBlockFormat: virtual public dev::Exception {};
struct InvalidBlockFormat: virtual dev::Exception {};
struct InvalidUnclesHash: virtual dev::Exception {};
struct InvalidUncle: virtual dev::Exception {};
struct TooManyUncles: virtual dev::Exception {};
struct UncleTooOld: virtual dev::Exception {};
class UncleInChain: virtual public dev::Exception {};
struct UncleIsBrother: virtual dev::Exception {};
struct UncleInChain: virtual dev::Exception {};
struct DuplicateUncleNonce: virtual dev::Exception {};
struct InvalidStateRoot: virtual dev::Exception {};
struct InvalidGasUsed: virtual dev::Exception {};
class InvalidTransactionsHash: virtual public dev::Exception {};
struct InvalidTransactionsHash: virtual dev::Exception {};
struct InvalidTransaction: virtual dev::Exception {};
struct InvalidDifficulty: virtual dev::Exception {};
class InvalidGasLimit: virtual public dev::Exception {};
struct InvalidGasLimit: virtual dev::Exception {};
struct InvalidTransactionGasUsed: virtual dev::Exception {};
struct InvalidTransactionsStateRoot: virtual dev::Exception {};
struct InvalidReceiptsStateRoot: virtual dev::Exception {};
struct InvalidTimestamp: virtual dev::Exception {};
struct InvalidLogBloom: virtual dev::Exception {};
class InvalidNonce: virtual public dev::Exception {};
class InvalidBlockHeaderItemCount: virtual public dev::Exception {};
class InvalidBlockNonce: virtual public dev::Exception {};
struct InvalidNonce: virtual dev::Exception {};
struct InvalidBlockHeaderItemCount: virtual dev::Exception {};
struct InvalidBlockNonce: virtual dev::Exception {};
struct InvalidParentHash: virtual dev::Exception {};
struct InvalidNumber: virtual dev::Exception {};
struct InvalidContractAddress: virtual public dev::Exception {};

2
libethcore/ICAP.h

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

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

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

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

@ -42,6 +42,12 @@ struct KeyInfo
static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::string(); };
enum class SemanticPassword
{
Existing,
Master
};
// TODO: This one is specifically for Ethereum, but we can make it generic in due course.
// TODO: hidden-partition style key-store.
/**
@ -55,7 +61,7 @@ static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::str
class KeyManager
{
public:
KeyManager(std::string const& _keysFile = getDataDir("ethereum") + "/keys.info");
KeyManager(std::string const& _keysFile = defaultPath(), std::string const& _secretsPath = SecretStore::defaultPath());
~KeyManager();
void setKeysFile(std::string const& _keysFile) { m_keysFile = _keysFile; }
@ -67,9 +73,12 @@ public:
void save(std::string const& _pass) const { write(_pass, m_keysFile); }
void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; }
void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordInfo[hashPassword(_pass)] = _hint; }
bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); }
AddressHash accounts() const;
std::unordered_map<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;
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(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(Address const& _a);
static std::string defaultPath() { return getDataDir("ethereum") + "/keys.info"; }
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;
// Only use if previously loaded ok.
// @returns false if wasn't previously loaded ok.
bool write() const { return write(m_keysFile); }
bool write(std::string const& _keysFile) const;
void write(std::string const& _pass, std::string const& _keysFile) const;
void write(h128 const& _key, std::string const& _keysFile) const;
@ -112,9 +129,10 @@ private:
// we have an upgrade strategy.
std::string m_password;
SecretStore m_store;
mutable h128 m_key;
mutable std::string m_keysFile;
mutable h128 m_key;
mutable h256 m_master;
SecretStore m_store;
};
}

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; }
uint64_t hashes = 0; ///< Total number of hashes computed.
uint64_t ms = 0; ///< Total number of milliseconds of mining thus far.
uint64_t rate() const { return hashes * 1000 / ms; }
uint64_t rate() const { return ms == 0 ? 0 : hashes * 1000 / ms; }
};
struct MineInfo: public MiningProgress {};

2
libethereum/AccountDiff.h

@ -62,7 +62,7 @@ struct AccountDiff
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> 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.
};

2
libethereum/BlockChain.cpp

@ -317,7 +317,7 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
{
try
{
// Nonce & uncle nonces already verified thread at this point.
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE(Block import, 500)
r = import(block.first, block.second, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);

64
libethereum/BlockQueue.cpp

@ -22,6 +22,7 @@
#include "BlockQueue.h"
#include <thread>
#include <libdevcore/Log.h>
#include <libethcore/EthashAux.h>
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
#include "BlockChain.h"
@ -76,11 +77,56 @@ void BlockQueue::verifierBody()
std::pair<BlockInfo, bytes> res;
swap(work.second, res.second);
try {
res.first.populate(res.second, CheckEverything, work.first);
res.first.verifyInternals(&res.second);
try {
res.first.populate(res.second, CheckEverything, work.first);
res.first.verifyInternals(&res.second);
}
catch (InvalidNonce&)
{
badBlock(res.second, "Invalid block nonce");
cwarn << " Nonce:" << res.first.nonce.hex();
cwarn << " PoWHash:" << res.first.headerHash(WithoutNonce).hex();
cwarn << " SeedHash:" << res.first.seedHash().hex();
cwarn << " Target:" << res.first.boundary().hex();
cwarn << " MixHash:" << res.first.mixHash.hex();
Ethash::Result er = EthashAux::eval(res.first.seedHash(), res.first.headerHash(WithoutNonce), res.first.nonce);
cwarn << " Ethash v:" << er.value.hex();
cwarn << " Ethash mH:" << er.mixHash.hex();
throw;
}
catch (Exception& _e)
{
badBlock(res.second, _e.what());
throw;
}
RLP r(&res.second);
for (auto const& uncle: r[2])
BlockInfo().populateFromHeader(RLP(uncle.data()), CheckEverything);
{
try
{
BlockInfo().populateFromHeader(RLP(uncle.data()), CheckEverything);
}
catch (InvalidNonce&)
{
badBlockHeader(uncle.data(), "Invalid uncle nonce");
BlockInfo bi = BlockInfo::fromHeader(uncle.data(), CheckNothing);
cwarn << " Nonce:" << bi.nonce.hex();
cwarn << " PoWHash:" << bi.headerHash(WithoutNonce).hex();
cwarn << " SeedHash:" << bi.seedHash().hex();
cwarn << " Target:" << bi.boundary().hex();
cwarn << " MixHash:" << bi.mixHash.hex();
Ethash::Result er = EthashAux::eval(bi.seedHash(), bi.headerHash(WithoutNonce), bi.nonce);
cwarn << " Ethash v:" << er.value.hex();
cwarn << " Ethash mH:" << er.mixHash.hex();
throw;
}
catch (Exception& _e)
{
badBlockHeader(uncle.data(), _e.what());
throw;
}
}
}
catch (...)
{
@ -372,3 +418,15 @@ void BlockQueue::retryAllUnknown()
m_unknown.clear();
m_moreToVerify.notify_all();
}
std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _bqs)
{
_out << "verified: " << _bqs.verified << endl;
_out << "verifying: " << _bqs.verifying << endl;
_out << "unverified: " << _bqs.unverified << endl;
_out << "future: " << _bqs.future << endl;
_out << "unknown: " << _bqs.unknown << endl;
_out << "bad: " << _bqs.bad << endl;
return _out;
}

2
libethereum/BlockQueue.h

@ -136,5 +136,7 @@ private:
bool m_deleting = false; ///< Exit condition for verifiers.
};
std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);
}
}

1
libethereum/CanonBlockChain.h

@ -34,7 +34,6 @@
#include <libdevcore/Guards.h>
#include "BlockDetails.h"
#include "Account.h"
#include "BlockQueue.h"
#include "BlockChain.h"
namespace ldb = leveldb;

36
libethereum/Client.cpp

@ -44,10 +44,11 @@ VersionChecker::VersionChecker(string const& _dbPath):
try
{
auto protocolVersion = (unsigned)status[0];
(void)protocolVersion;
auto minorProtocolVersion = (unsigned)status[1];
auto databaseVersion = (unsigned)status[2];
m_action =
protocolVersion != eth::c_protocolVersion || databaseVersion != c_databaseVersion ?
databaseVersion != c_databaseVersion ?
WithExisting::Kill
: minorProtocolVersion != eth::c_minorProtocolVersion ?
WithExisting::Verify
@ -164,28 +165,8 @@ const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; }
#endif
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth", 0),
m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(new TrivialGasPricer),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(m_stateDB)
Client(_extNet, make_shared<TrivialGasPricer>(), _dbPath, _forceAction, _networkId)
{
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
doWork();
startWorking();
}
@ -195,7 +176,7 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string c
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(_gp),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(m_stateDB),
m_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(m_stateDB)
{
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
@ -205,7 +186,9 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string c
m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
auto host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
m_host = host;
_extNet->addCapability(host, EthereumHost::staticName(), EthereumHost::c_oldProtocolVersion); //TODO: remove this one v61+ protocol is common
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
@ -286,9 +269,11 @@ void Client::killChain()
{
WriteGuard l(x_postMine);
WriteGuard l2(x_preMine);
WriteGuard l3(x_working);
m_preMine = State();
m_postMine = State();
m_working = State();
m_stateDB = OverlayDB();
m_stateDB = State::openDB(Defaults::dbPath(), WithExisting::Kill);
@ -301,6 +286,7 @@ void Client::killChain()
if (auto h = m_host.lock())
h->reset();
startedWorking();
doWork();
startWorking();
@ -440,7 +426,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp, LastHashes(), 0);
if (!e.call(_dest, _dest, _from, _value, _gasPrice, &_data, _gas, _from))
if (!e.call(_dest, _from, _value, _gasPrice, &_data, _gas))
e.go();
ret = e.executionResult();
}

5
libethereum/CommonNet.h

@ -38,12 +38,12 @@ namespace eth
#if ETH_DEBUG
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
#else
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
#endif
@ -63,6 +63,7 @@ enum
GetBlocksPacket,
BlocksPacket,
NewBlockPacket,
GetBlockHashesByNumberPacket,
PacketCount
};

50
libethereum/DownloadMan.cpp

@ -75,3 +75,53 @@ bool DownloadSub::noteBlock(h256 _hash)
m_remaining.erase(_hash);
return ret;
}
HashDownloadSub::HashDownloadSub(HashDownloadMan& _man): m_man(&_man)
{
WriteGuard l(m_man->x_subs);
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_attempted = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_man->m_subs.insert(this);
}
HashDownloadSub::~HashDownloadSub()
{
if (m_man)
{
WriteGuard l(m_man->x_subs);
m_man->m_subs.erase(this);
}
}
void HashDownloadSub::resetFetch()
{
Guard l(m_fetch);
m_remaining = 0;
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_attempted = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
}
unsigned HashDownloadSub::nextFetch(unsigned _n)
{
Guard l(m_fetch);
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
if (!m_man || m_man->chainEmpty())
return 0;
m_asked = (~(m_man->taken() + m_attempted)).lowest(_n);
if (m_asked.empty())
m_asked = (~(m_man->taken(true) + m_attempted)).lowest(_n);
m_attempted += m_asked;
return *m_asked.begin();
}
void HashDownloadSub::noteHash(unsigned _index, unsigned _size)
{
Guard l(m_fetch);
if (m_man)
for(unsigned i = _index; i < _index + _size; ++i)
if (i >= m_man->m_got.all().first && i < m_man->m_got.all().second)
m_man->m_got += i;
}

113
libethereum/DownloadMan.h

@ -88,6 +88,13 @@ public:
i->m_man = nullptr;
}
void appendToChain(h256s const& _hashes)
{
WriteGuard l(m_lock);
m_chain.insert(m_chain.end(), _hashes.cbegin(), _hashes.cend());
m_blocksGot = RangeMask<unsigned>(0, m_chain.size());
}
void resetToChain(h256s const& _chain)
{
{
@ -158,6 +165,112 @@ private:
std::unordered_set<DownloadSub*> m_subs;
};
class HashDownloadMan;
class HashDownloadSub
{
friend class HashDownloadMan;
public:
HashDownloadSub(HashDownloadMan& _man);
~HashDownloadSub();
/// Finished last fetch - grab the next hash index to download
unsigned nextFetch(unsigned _n);
/// Note that we've received a particular hash range.
void noteHash(unsigned _index, unsigned count);
/// Nothing doing here.
void doneFetch() { resetFetch(); }
bool askedContains(unsigned _i) const { Guard l(m_fetch); return m_asked.contains(_i); }
RangeMask<unsigned> const& asked() const { return m_asked; }
RangeMask<unsigned> const& attemped() const { return m_attempted; }
private:
void resetFetch(); // Called by DownloadMan when we need to reset the download.
HashDownloadMan* m_man = nullptr;
mutable Mutex m_fetch;
unsigned m_remaining;
RangeMask<unsigned> m_asked;
RangeMask<unsigned> m_attempted;
};
class HashDownloadMan
{
friend class HashDownloadSub;
public:
~HashDownloadMan()
{
for (auto i: m_subs)
i->m_man = nullptr;
}
void resetToRange(unsigned _start, unsigned _count)
{
{
ReadGuard l(x_subs);
for (auto i: m_subs)
i->resetFetch();
}
WriteGuard l(m_lock);
m_chainStart = _start;
m_chainCount = _count;
m_got += RangeMask<unsigned>(_start, _start + _count);
{
ReadGuard l(x_subs);
for (auto i: m_subs)
i->resetFetch();
}
}
void reset(unsigned _start)
{
WriteGuard l(m_lock);
m_chainStart = _start;
m_chainCount = 0;
m_got = RangeMask<unsigned>(_start, _start);
}
RangeMask<unsigned> taken(bool _desperate = false) const
{
ReadGuard l(m_lock);
auto ret = m_got;
if (!_desperate)
{
ReadGuard l(x_subs);
for (auto i: m_subs)
ret += i->m_asked;
}
return ret;
}
bool isComplete() const
{
ReadGuard l(m_lock);
return m_got.full();
}
size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; }
size_t chainEmpty() const { ReadGuard l(m_lock); return m_chainCount == 0; }
void foreachSub(std::function<void(HashDownloadSub const&)> const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); }
unsigned subCount() const { ReadGuard l(x_subs); return m_subs.size(); }
RangeMask<unsigned> hashesGot() const { ReadGuard l(m_lock); return m_got; }
private:
mutable SharedMutex m_lock;
unsigned m_chainStart = 0;
unsigned m_chainCount = 0;
RangeMask<unsigned> m_got;
mutable SharedMutex x_subs;
std::unordered_set<HashDownloadSub*> m_subs;
};
}
}

553
libethereum/EthereumHost.cpp

@ -27,6 +27,7 @@
#include <libp2p/Host.h>
#include <libp2p/Session.h>
#include <libethcore/Exceptions.h>
#include <libethcore/Params.h>
#include "BlockChain.h"
#include "TransactionQueue.h"
#include "BlockQueue.h"
@ -37,6 +38,8 @@ using namespace dev;
using namespace dev::eth;
using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(),
Worker ("ethsync"),
@ -46,12 +49,12 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
m_networkId (_networkId)
{
m_latestBlockSent = _ch.currentHash();
m_hashMan.reset(m_chain.number() + 1);
}
EthereumHost::~EthereumHost()
{
for (auto i: peerSessions())
i.first->cap<EthereumPeer>().get()->abortSync();
foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
}
bool EthereumHost::ensureInitialised()
@ -69,88 +72,18 @@ bool EthereumHost::ensureInitialised()
return false;
}
void EthereumHost::noteNeedsSyncing(EthereumPeer* _who)
{
// if already downloading hash-chain, ignore.
if (isSyncing())
{
clog(NetAllDetail) << "Sync in progress: Just set to help out.";
if (m_syncer->m_asking == Asking::Blocks)
_who->transition(Asking::Blocks);
}
else
// otherwise check to see if we should be downloading...
_who->attemptSync();
}
void EthereumHost::changeSyncer(EthereumPeer* _syncer, bool _needHelp)
{
if (_syncer)
clog(NetAllDetail) << "Changing syncer to" << _syncer->session()->socketId();
else
clog(NetAllDetail) << "Clearing syncer.";
m_syncer = _syncer;
if (isSyncing())
{
if (_needHelp && _syncer->m_asking == Asking::Blocks)
for (auto j: peerSessions())
{
clog(NetNote) << "Getting help with downloading blocks";
auto e = j.first->cap<EthereumPeer>().get();
if (e != _syncer && e->m_asking == Asking::Nothing)
e->transition(Asking::Blocks);
}
}
else
{
// start grabbing next hash chain if there is one.
for (auto j: peerSessions())
{
j.first->cap<EthereumPeer>()->attemptSync();
if (isSyncing())
return;
}
clog(NetNote) << "No more peers to sync with.";
}
}
void EthereumHost::noteDoneBlocks(EthereumPeer* _who, bool _clemency)
{
if (m_man.isComplete())
{
// Done our chain-get.
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_who->addRating(m_man.chainSize() / 100);
m_man.reset();
}
else if (_who->isSyncing())
{
if (_clemency)
clog(NetNote) << "Chain download failed. Aborted while incomplete.";
else
{
// Done our chain-get.
clog(NetWarn) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished.";
clog(NetWarn) << m_man.remaining();
clog(NetWarn) << "WOULD BAN.";
// m_banned.insert(_who->session()->id()); // We know who you are!
// _who->disable("Peer sent hashes but was unable to provide the blocks.");
}
m_man.reset();
}
}
void EthereumHost::reset()
{
if (m_syncer)
m_syncer->abortSync();
foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
m_man.resetToChain(h256s());
m_hashMan.reset(m_chain.number() + 1);
m_needSyncBlocks = true;
m_needSyncHashes = true;
m_syncingLatestHash = h256();
m_syncingTotalDifficulty = 0;
m_latestBlockSent = h256();
m_transactionsSent.clear();
m_hashes.clear();
}
void EthereumHost::doWork()
@ -158,7 +91,7 @@ void EthereumHost::doWork()
bool netChange = ensureInitialised();
auto h = m_chain.currentHash();
// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
if (isSyncing() && m_chain.isKnown(m_latestBlockSent))
{
if (m_newTransactions)
{
@ -172,9 +105,7 @@ void EthereumHost::doWork()
}
}
for (auto p: peerSessions())
if (shared_ptr<EthereumPeer> const& ep = p.first->cap<EthereumPeer>())
ep->tick();
foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange;
// TODO: Figure out what to do with netChange.
@ -194,28 +125,44 @@ void EthereumHost::maintainTransactions()
}
for (auto const& t: ts)
m_transactionsSent.insert(t.first);
for (auto p: peerSessions())
if (auto ep = p.first->cap<EthereumPeer>())
foreachPeerPtr([&](shared_ptr<EthereumPeer> _p)
{
bytes b;
unsigned n = 0;
for (auto const& h: peerTransactions[_p])
{
bytes b;
unsigned n = 0;
for (auto const& h: peerTransactions[ep])
{
ep->m_knownTransactions.insert(h);
b += ts[h].rlp();
++n;
}
_p->m_knownTransactions.insert(h);
b += ts[h].rlp();
++n;
}
ep->clearKnownTransactions();
_p->clearKnownTransactions();
if (n || ep->m_requireTransactions)
{
RLPStream ts;
ep->prep(ts, TransactionsPacket, n).appendRaw(b, n);
ep->sealAndSend(ts);
}
ep->m_requireTransactions = false;
if (n || _p->m_requireTransactions)
{
RLPStream ts;
_p->prep(ts, TransactionsPacket, n).appendRaw(b, n);
_p->sealAndSend(ts);
}
_p->m_requireTransactions = false;
});
}
void EthereumHost::foreachPeer(std::function<void(EthereumPeer*)> const& _f) const
{
foreachPeerPtr([&](std::shared_ptr<EthereumPeer> _p)
{
if (_p)
_f(_p.get());
});
}
void EthereumHost::foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const
{
for (auto s: peerSessions())
_f(s.first->cap<EthereumPeer>());
for (auto s: peerSessions(c_oldProtocolVersion)) //TODO: remove once v61+ is common
_f(s.first->cap<EthereumPeer>(c_oldProtocolVersion));
}
tuple<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<Session>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
@ -286,3 +233,407 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
m_latestBlockSent = _currentHash;
}
}
void EthereumHost::onPeerStatus(EthereumPeer* _peer)
{
Guard l(x_sync);
if (_peer->m_genesisHash != m_chain.genesisHash())
_peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != protocolVersion() && _peer->m_protocolVersion != c_oldProtocolVersion)
_peer->disable("Invalid protocol version.");
else if (_peer->m_networkId != networkId())
_peer->disable("Invalid network identifier.");
else if (_peer->session()->info().clientVersion.find("/v0.7.0/") != string::npos)
_peer->disable("Blacklisted client version.");
else if (isBanned(_peer->session()->id()))
_peer->disable("Peer banned for previous bad behaviour.");
else
{
if (_peer->m_protocolVersion != protocolVersion())
estimatePeerHashes(_peer);
else if (_peer->m_latestBlockNumber > m_chain.number())
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number();
if (m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
continueSync(_peer);
}
}
void EthereumHost::estimatePeerHashes(EthereumPeer* _peer)
{
BlockInfo block = m_chain.info();
time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp;
time_t now = time(0);
unsigned blockCount = 1000;
if (lastBlockTime > now)
clog(NetWarn) << "Clock skew? Latest block is in the future";
else
blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit;
clog(NetAllDetail) << "Estimated hashes: " << blockCount;
_peer->m_expectedHashes = blockCount;
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
onPeerHashes(_peer, _hashes, false);
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete)
{
if (_hashes.empty())
{
onPeerDoneHashes(_peer, true);
return;
}
unsigned knowns = 0;
unsigned unknowns = 0;
h256s neededBlocks;
bool syncByNumber = !m_syncingLatestHash;
for (unsigned i = 0; i < _hashes.size(); ++i)
{
_peer->addRating(1);
auto h = _hashes[i];
auto status = m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || m_chain.isKnown(h))
{
clog(NetMessageSummary) << "Block hash already known:" << h;
if (!syncByNumber)
{
m_hashes += neededBlocks;
clog(NetMessageSummary) << "Start blocks download...";
onPeerDoneHashes(_peer, true);
return;
}
}
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
_peer->setIdle();
return;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
neededBlocks.push_back(h);
}
else
knowns++;
if (!syncByNumber)
m_syncingLatestHash = h;
}
if (syncByNumber)
{
m_man.appendToChain(neededBlocks); // Append to download manager immediatelly
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
}
else
{
m_hashes += neededBlocks; // Append to local list
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLatestHash;
}
if (_complete)
{
m_needSyncBlocks = true;
continueSync(_peer);
}
else if (syncByNumber && m_hashMan.isComplete())
{
// Done our chain-get.
m_needSyncHashes = false;
clog(NetNote) << "Hashes download complete.";
// 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_hashMan.reset(m_chain.number() + 1);
continueSync();
}
else if (m_hashes.size() > _peer->m_expectedHashes)
{
_peer->disable("Too many hashes");
m_hashes.clear();
m_syncingLatestHash = h256();
continueSync(); ///Try with some other peer, keep the chain
}
else
continueSync(_peer); /// Grab next hashes
}
void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
{
assert(_peer->m_asking == Asking::Nothing);
m_needSyncHashes = false;
if (_peer->m_protocolVersion != protocolVersion() || _localChain)
{
m_man.resetToChain(m_hashes);
m_hashes.clear();
}
continueSync();
}
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
if (itemCount == 0)
{
// Got to this peer's latest block - just give up.
clog(NetNote) << "Finishing blocks fetch...";
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
_peer->setIdle();
return;
}
unsigned success = 0;
unsigned future = 0;
unsigned unknown = 0;
unsigned got = 0;
unsigned repeated = 0;
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = BlockInfo::headerHash(_r[i].data());
if (_peer->m_sub.noteBlock(h))
{
_peer->addRating(10);
switch (m_bq.import(_r[i].data(), m_chain))
{
case ImportResult::Success:
success++;
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
_peer->disable("Malformed block received.");
return;
case ImportResult::FutureTime:
future++;
break;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
got++;
break;
case ImportResult::UnknownParent:
unknown++;
break;
default:;
}
}
else
{
_peer->addRating(0); // -1?
repeated++;
}
}
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_man.isComplete() && !m_needSyncHashes)
{
// Done our chain-get.
m_needSyncBlocks = false;
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_man.reset();
}
continueSync(_peer);
}
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{
Guard l(x_sync);
if (isSyncing_UNSAFE())
{
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return;
}
clog(NetNote) << "New block hash discovered: syncing without help.";
onPeerHashes(_peer, _hashes, true);
}
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{
Guard l(x_sync);
if (isSyncing_UNSAFE())
{
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return;
}
auto h = BlockInfo::headerHash(_r[0].data());
clog(NetMessageSummary) << "NewBlock: " << h;
if (_r.itemCount() != 2)
_peer->disable("NewBlock without 2 data fields.");
else
{
bool sync = false;
switch (m_bq.import(_r[0].data(), m_chain))
{
case ImportResult::Success:
_peer->addRating(100);
break;
case ImportResult::FutureTime:
//TODO: Rating dependent on how far in future it is.
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
_peer->disable("Malformed block received.");
return;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
break;
case ImportResult::UnknownParent:
if (h)
{
u256 difficulty = _r[1].toInt<u256>();
if (m_syncingTotalDifficulty < difficulty)
{
clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
_peer->m_latestHash = h;
_peer->m_totalDifficulty = difficulty;
m_needSyncHashes = true;
m_needSyncBlocks = true;
m_syncingLatestHash = h;
sync = true;
}
}
break;
default:;
}
DEV_GUARDED(_peer->x_knownBlocks)
_peer->m_knownBlocks.insert(h);
if (sync)
continueSync(_peer);
}
}
void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
{
unsigned itemCount = _r.itemCount();
clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)";
Guard l(_peer->x_knownTransactions);
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = sha3(_r[i].data());
_peer->m_knownTransactions.insert(h);
ImportResult ir = m_tq.import(_r[i].data());
switch (ir)
{
case ImportResult::Malformed:
_peer->addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
m_transactionsSent.insert(h);
_peer->addRating(0);
break;
case ImportResult::Success:
_peer->addRating(100);
break;
default:;
}
}
}
void EthereumHost::continueSync()
{
clog(NetAllDetail) << "Getting help with downloading hashes and blocks";
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking == Asking::Nothing)
continueSync(_p);
});
}
void EthereumHost::continueSync(EthereumPeer* _peer)
{
assert(_peer->m_asking == Asking::Nothing);
bool otherPeerSync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer))
{
foreachPeer([&](EthereumPeer* _p)
{
if (_p != _peer && _p->m_asking == Asking::Hashes && _p->m_protocolVersion != protocolVersion())
otherPeerSync = true; // Already have a peer downloading hash chain with old protocol, do nothing
});
if (otherPeerSync)
{
/// Downloading from other peer with v60 protocol, nothing ese we can do
_peer->setIdle();
return;
}
if (_peer->m_protocolVersion == protocolVersion() && !m_syncingLatestHash)
_peer->requestHashes(); /// v61+ and not catching up to a particular hash
else
{
// Restart/continue sync in single peer mode
if (!m_syncingLatestHash)
{
m_syncingLatestHash =_peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
}
_peer->requestHashes(m_syncingLatestHash);
}
}
else if (m_needSyncBlocks && peerShouldGrabBlocks(_peer)) // Check if this peer can help with downloading blocks
_peer->requestBlocks();
else
_peer->setIdle();
}
bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const
{
auto td = _peer->m_totalDifficulty;
auto lh = m_syncingLatestHash;
auto ctd = m_chain.details().totalDifficulty;
clog(NetAllDetail) << "Should grab blocks? " << td << "vs" << ctd;
if (td < ctd || (td == ctd && m_chain.currentHash() == lh))
return false;
return true;
}
bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
{
h256 c = m_chain.currentHash();
unsigned n = m_chain.number();
u256 td = m_chain.details().totalDifficulty;
clog(NetAllDetail) << "Attempt chain-grab? Latest:" << c << ", number:" << n << ", TD:" << td << " versus " << _peer->m_totalDifficulty;
if (td >= _peer->m_totalDifficulty)
{
clog(NetAllDetail) << "No. Our chain is better.";
return false;
}
else
{
clog(NetAllDetail) << "Yes. Their chain is better.";
return true;
}
}
bool EthereumHost::isSyncing_UNSAFE() const
{
/// We need actual peer information here to handle the case when we are the first ever peer on the network to mine.
/// I.e. on a new private network the first node mining has noone to sync with and should start block propogation immediately.
bool syncing = false;
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking != Asking::Nothing)
syncing = true;
});
return syncing;
}

44
libethereum/EthereumHost.h

@ -56,8 +56,6 @@ class BlockQueue;
*/
class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker
{
friend class EthereumPeer;
public:
/// Start server, but don't listen.
EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId);
@ -72,21 +70,31 @@ public:
void reset();
DownloadMan const& downloadMan() const { return m_man; }
bool isSyncing() const { return !!m_syncer; }
bool isSyncing() const { Guard l(x_sync); return isSyncing_UNSAFE(); }
bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; }
void noteNewBlocks() { m_newBlocks = true; }
void onPeerStatus(EthereumPeer* _peer); ///< Called by peer to report status
void onPeerBlocks(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks during syn
void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks
void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has new hashes
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has another sequential block of hashes during sync
void onPeerTransactions(EthereumPeer* _peer, RLP const& _r); ///< Called by peer when it has new transactions
DownloadMan& downloadMan() { return m_man; }
HashDownloadMan& hashDownloadMan() { return m_hashMan; }
BlockChain const& chain() { return m_chain; }
static unsigned const c_oldProtocolVersion;
private:
std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
/// Session is tell us that we may need (re-)syncing with the peer.
void noteNeedsSyncing(EthereumPeer* _who);
/// Called when the peer can no longer provide us with any needed blocks.
void noteDoneBlocks(EthereumPeer* _who, bool _clemency);
void foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const;
bool isSyncing_UNSAFE() const;
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork();
@ -108,7 +116,13 @@ private:
virtual void onStarting() { startWorking(); }
virtual void onStopping() { stopWorking(); }
void changeSyncer(EthereumPeer* _ignore, bool _needHelp = true);
void continueSync(); /// Find something to do for all peers
void continueSync(EthereumPeer* _peer); /// Find some work to do for a peer
void onPeerDoneHashes(EthereumPeer* _peer, bool _new); /// Called when done downloading hashes from peer
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete);
bool peerShouldGrabBlocks(EthereumPeer* _peer) const;
bool peerShouldGrabChain(EthereumPeer* _peer) const;
void estimatePeerHashes(EthereumPeer* _peer);
BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
@ -116,9 +130,8 @@ private:
u256 m_networkId;
EthereumPeer* m_syncer = nullptr; // TODO: switch to weak_ptr
DownloadMan m_man;
HashDownloadMan m_hashMan;
h256 m_latestBlockSent;
h256Hash m_transactionsSent;
@ -127,6 +140,13 @@ private:
bool m_newTransactions = false;
bool m_newBlocks = false;
mutable Mutex x_sync;
bool m_needSyncHashes = true; ///< Indicates if need to downlad hashes
bool m_needSyncBlocks = true; ///< Indicates if we still need to download some blocks
h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync.
h256s m_hashes; ///< List of hashes with unknown block numbers. Used for v60 chain downloading and catching up to a particular unknown
};
}

537
libethereum/EthereumPeer.cpp

@ -34,11 +34,14 @@ using namespace dev;
using namespace dev::eth;
using namespace p2p;
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i):
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, CapDesc const& _cap):
Capability(_s, _h, _i),
m_sub(host()->m_man)
m_sub(host()->downloadMan()),
m_hashSub(host()->hashDownloadMan()),
m_peerCapabilityVersion(_cap.second)
{
transition(Asking::State);
m_syncHashNumber = host()->chain().number() + 1;
requestStatus();
}
EthereumPeer::~EthereumPeer()
@ -50,7 +53,7 @@ EthereumPeer::~EthereumPeer()
void EthereumPeer::abortSync()
{
if (isSyncing())
transition(Asking::Nothing, true);
setIdle();
}
EthereumHost* EthereumPeer::host() const
@ -74,164 +77,77 @@ string toString(Asking _a)
return "?";
}
void EthereumPeer::transition(Asking _a, bool _force, bool _needHelp)
void EthereumPeer::setIdle()
{
clog(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
if (m_asking == Asking::State && _a != Asking::State)
m_requireTransactions = true;
m_sub.doneFetch();
m_hashSub.doneFetch();
setAsking(Asking::Nothing);
}
void EthereumPeer::requestStatus()
{
assert(m_asking == Asking::Nothing);
setAsking(Asking::State);
RLPStream s;
bool latest = m_peerCapabilityVersion == host()->protocolVersion();
prep(s, StatusPacket, latest ? 6 : 5)
<< (latest ? host()->protocolVersion() : EthereumHost::c_oldProtocolVersion)
<< host()->networkId()
<< host()->chain().details().totalDifficulty
<< host()->chain().currentHash()
<< host()->chain().genesisHash();
if (latest)
s << u256(host()->chain().number());
sealAndSend(s);
}
if (_a == Asking::State)
{
if (m_asking == Asking::Nothing)
{
setAsking(Asking::State, false);
prep(s, StatusPacket, 5)
<< host()->protocolVersion()
<< host()->networkId()
<< host()->m_chain.details().totalDifficulty
<< host()->m_chain.currentHash()
<< host()->m_chain.genesisHash();
sealAndSend(s);
return;
}
}
else if (_a == Asking::Hashes)
{
if (m_asking == Asking::State || m_asking == Asking::Nothing)
{
if (isSyncing())
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
m_syncingLatestHash = m_latestHash;
m_syncingTotalDifficulty = m_totalDifficulty;
resetNeedsSyncing();
setAsking(_a, true);
prep(s, GetBlockHashesPacket, 2) << m_syncingLatestHash << c_maxHashesAsk;
m_syncingNeededBlocks = h256s(1, m_syncingLatestHash);
sealAndSend(s);
return;
}
else if (m_asking == Asking::Hashes)
{
if (!isSyncing())
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
void EthereumPeer::requestHashes()
{
assert(m_asking == Asking::Nothing);
m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk);
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes for numbers " << m_syncHashNumber << "-" << m_syncHashNumber + c_maxHashesAsk - 1;
sealAndSend(s);
}
setAsking(_a, true);
prep(s, GetBlockHashesPacket, 2) << m_syncingLastReceivedHash << c_maxHashesAsk;
sealAndSend(s);
return;
}
}
else if (_a == Asking::Blocks)
{
if (m_asking == Asking::Hashes)
{
if (!isSyncing())
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
if (shouldGrabBlocks())
{
clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash << ", was" << host()->m_latestBlockSent << "]";
void EthereumPeer::requestHashes(h256 const& _lastHash)
{
assert(m_asking == Asking::Nothing);
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes staring from " << _lastHash;
sealAndSend(s);
}
host()->m_man.resetToChain(m_syncingNeededBlocks);
// host()->m_latestBlockSent = m_syncingLatestHash;
}
else
{
clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring.";
m_syncingLatestHash = h256();
setAsking(Asking::Nothing, false);
return;
}
}
// run through into...
if (m_asking == Asking::Nothing || m_asking == Asking::Hashes || m_asking == Asking::Blocks)
{
// Looks like it's the best yet for total difficulty. Set to download.
setAsking(Asking::Blocks, isSyncing(), _needHelp); // will kick off other peers to help if available.
auto blocks = m_sub.nextFetch(c_maxBlocksAsk);
if (blocks.size())
{
prep(s, GetBlocksPacket, blocks.size());
for (auto const& i: blocks)
s << i;
sealAndSend(s);
}
else
transition(Asking::Nothing);
return;
}
}
else if (_a == Asking::Nothing)
void EthereumPeer::requestBlocks()
{
setAsking(Asking::Blocks);
auto blocks = m_sub.nextFetch(c_maxBlocksAsk);
if (blocks.size())
{
if (m_asking == Asking::Blocks)
{
clog(NetNote) << "Finishing blocks fetch...";
// a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry.
if (isSyncing())
host()->noteDoneBlocks(this, _force);
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
m_sub.doneFetch();
setAsking(Asking::Nothing, false);
}
else if (m_asking == Asking::Hashes)
{
clog(NetNote) << "Finishing hashes fetch...";
setAsking(Asking::Nothing, false);
}
else if (m_asking == Asking::State)
{
setAsking(Asking::Nothing, false);
// Just got the state - should check to see if we can be of help downloading the chain if any.
// Otherwise, should put ourselves up for sync.
setNeedsSyncing(m_latestHash, m_totalDifficulty);
}
// Otherwise it's fine. We don't care if it's Nothing->Nothing.
return;
RLPStream s;
prep(s, GetBlocksPacket, blocks.size());
for (auto const& i: blocks)
s << i;
sealAndSend(s);
}
clog(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
else
setIdle();
return;
}
void EthereumPeer::setAsking(Asking _a, bool _isSyncing, bool _needHelp)
void EthereumPeer::setAsking(Asking _a)
{
bool changedAsking = (m_asking != _a);
m_asking = _a;
if (_isSyncing != (host()->m_syncer == this) || (_isSyncing && changedAsking))
host()->changeSyncer(_isSyncing ? this : nullptr, _needHelp);
if (!_isSyncing)
{
m_syncingLatestHash = h256();
m_syncingTotalDifficulty = 0;
m_syncingNeededBlocks.clear();
}
m_lastAsk = chrono::system_clock::now();
session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?");
session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : ""));
}
void EthereumPeer::setNeedsSyncing(h256 _latestHash, u256 _td)
{
m_latestHash = _latestHash;
m_totalDifficulty = _td;
if (m_latestHash)
host()->noteNeedsSyncing(this);
session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : ""));
}
void EthereumPeer::tick()
{
if (chrono::system_clock::now() - m_lastAsk > chrono::seconds(10) && m_asking != Asking::Nothing)
@ -241,57 +157,7 @@ void EthereumPeer::tick()
bool EthereumPeer::isSyncing() const
{
return host()->m_syncer == this;
}
bool EthereumPeer::shouldGrabBlocks() const
{
auto td = m_syncingTotalDifficulty;
auto lh = m_syncingLatestHash;
auto ctd = host()->m_chain.details().totalDifficulty;
if (m_syncingNeededBlocks.empty())
return false;
clog(NetNote) << "Should grab blocks? " << td << "vs" << ctd << ";" << m_syncingNeededBlocks.size() << " blocks, ends" << m_syncingNeededBlocks.back();
if (td < ctd || (td == ctd && host()->m_chain.currentHash() == lh))
return false;
return true;
}
void EthereumPeer::attemptSync()
{
if (m_asking != Asking::Nothing)
{
clog(NetAllDetail) << "Can't synced with this peer - outstanding asks.";
return;
}
// if already done this, then ignore.
if (!needsSyncing())
{
clog(NetAllDetail) << "Already synced with this peer.";
return;
}
h256 c = host()->m_chain.currentHash();
unsigned n = host()->m_chain.number();
u256 td = host()->m_chain.details().totalDifficulty;
clog(NetAllDetail) << "Attempt chain-grab? Latest:" << c << ", number:" << n << ", TD:" << td << " versus " << m_totalDifficulty;
if (td >= m_totalDifficulty)
{
clog(NetAllDetail) << "No. Our chain is better.";
resetNeedsSyncing();
transition(Asking::Nothing);
}
else
{
clog(NetAllDetail) << "Yes. Their chain is better.";
transition(Asking::Hashes);
}
return m_asking != Asking::Nothing;
}
bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
@ -304,54 +170,23 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
m_protocolVersion = _r[0].toInt<unsigned>();
m_networkId = _r[1].toInt<u256>();
// a bit dirty as we're misusing these to communicate the values to transition, but harmless.
m_totalDifficulty = _r[2].toInt<u256>();
m_latestHash = _r[3].toHash<h256>();
auto genesisHash = _r[4].toHash<h256>();
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
m_genesisHash = _r[4].toHash<h256>();
if (m_peerCapabilityVersion == host()->protocolVersion())
{
m_protocolVersion = host()->protocolVersion();
m_latestBlockNumber = _r[5].toInt<u256>();
}
if (genesisHash != host()->m_chain.genesisHash())
disable("Invalid genesis hash");
else if (m_protocolVersion != host()->protocolVersion())
disable("Invalid protocol version.");
else if (m_networkId != host()->networkId())
disable("Invalid network identifier.");
else if (session()->info().clientVersion.find("/v0.7.0/") != string::npos)
disable("Blacklisted client version.");
else if (host()->isBanned(session()->id()))
disable("Peer banned for previous bad behaviour.");
else
transition(Asking::Nothing);
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << m_genesisHash << "/" << m_latestBlockNumber << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
setAsking(Asking::Nothing);
host()->onPeerStatus(this);
break;
}
case TransactionsPacket:
{
unsigned itemCount = _r.itemCount();
clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)";
Guard l(x_knownTransactions);
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = sha3(_r[i].data());
m_knownTransactions.insert(h);
ImportResult ir = host()->m_tq.import(_r[i].data());
switch (ir)
{
case ImportResult::Malformed:
addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
host()->m_transactionsSent.insert(h);
addRating(0);
break;
case ImportResult::Success:
addRating(100);
break;
default:;
}
}
host()->onPeerTransactions(this, _r);
break;
}
case GetBlockHashesPacket:
@ -359,18 +194,39 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
h256 later = _r[0].toHash<h256>();
unsigned limit = _r[1].toInt<unsigned>();
clog(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later << ")";
unsigned c = min<unsigned>(host()->m_chain.number(later), limit);
unsigned c = min<unsigned>(host()->chain().number(later), limit);
RLPStream s;
prep(s, BlockHashesPacket, c);
h256 p = host()->m_chain.details(later).parent;
for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent)
h256 p = host()->chain().details(later).parent;
for (unsigned i = 0; i < c && p; ++i, p = host()->chain().details(p).parent)
s << p;
sealAndSend(s);
addRating(0);
break;
}
case GetBlockHashesByNumberPacket:
{
u256 number256 = _r[0].toInt<u256>();
unsigned number = (unsigned) number256;
unsigned limit = _r[1].toInt<unsigned>();
clog(NetMessageSummary) << "GetBlockHashesByNumber (" << number << "-" << number + limit - 1 << ")";
RLPStream s;
if (number <= host()->chain().number())
{
unsigned c = min<unsigned>(host()->chain().number() - number + 1, limit);
prep(s, BlockHashesPacket, c);
for (unsigned n = number; n < number + c; n++)
{
h256 p = host()->chain().numberHash(n);
s << p;
}
}
else
prep(s, BlockHashesPacket, 0);
sealAndSend(s);
addRating(0);
break;
}
case BlockHashesPacket:
{
unsigned itemCount = _r.itemCount();
@ -378,45 +234,19 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
if (m_asking != Asking::Hashes)
{
cwarn << "Peer giving us hashes when we didn't ask for them.";
clog(NetWarn) << "Peer giving us hashes when we didn't ask for them.";
break;
}
if (itemCount == 0)
{
transition(Asking::Blocks);
return true;
}
unsigned knowns = 0;
unsigned unknowns = 0;
setAsking(Asking::Nothing);
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
{
addRating(1);
auto h = _r[i].toHash<h256>();
auto status = host()->m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h))
{
clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download...";
transition(Asking::Blocks);
return true;
}
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
transition(Asking::Nothing);
return true;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
m_syncingNeededBlocks.push_back(h);
}
else
knowns++;
m_syncingLastReceivedHash = h;
hashes[i] = _r[i].toHash<h256>();
m_hashSub.noteHash(m_syncHashNumber + i, 1);
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLastReceivedHash;
// run through - ask for more.
transition(Asking::Hashes);
m_syncHashNumber += itemCount;
host()->onPeerHashes(this, hashes);
break;
}
case GetBlocksPacket:
@ -436,9 +266,9 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
for (unsigned i = 0; i < min(count, c_maxBlocks); ++i)
{
auto h = _r[i].toHash<h256>();
if (host()->m_chain.isKnown(h))
if (host()->chain().isKnown(h))
{
rlp += host()->m_chain.block(_r[i].toHash<h256>());
rlp += host()->chain().block(_r[i].toHash<h256>());
++n;
}
}
@ -455,157 +285,30 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case BlocksPacket:
{
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
if (m_asking != Asking::Blocks)
clog(NetWarn) << "Unexpected Blocks received!";
if (itemCount == 0)
{
// Got to this peer's latest block - just give up.
transition(Asking::Nothing);
break;
}
unsigned success = 0;
unsigned future = 0;
unsigned unknown = 0;
unsigned got = 0;
unsigned repeated = 0;
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = BlockInfo::headerHash(_r[i].data());
if (m_sub.noteBlock(h))
{
addRating(10);
switch (host()->m_bq.import(_r[i].data(), host()->m_chain))
{
case ImportResult::Success:
success++;
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
disable("Malformed block received.");
return true;
case ImportResult::FutureTime:
future++;
break;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
got++;
break;
case ImportResult::UnknownParent:
unknown++;
break;
default:;
}
}
else
{
addRating(0); // -1?
repeated++;
}
}
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_asking == Asking::Blocks)
clog(NetWarn) << "Peer giving us blocks when we didn't ask for them.";
else
{
if (!got)
transition(Asking::Blocks);
else
transition(Asking::Nothing);
setAsking(Asking::Nothing);
host()->onPeerBlocks(this, _r);
}
break;
}
case NewBlockPacket:
{
auto h = BlockInfo::headerHash(_r[0].data());
clog(NetMessageSummary) << "NewBlock: " << h;
if (_r.itemCount() != 2)
disable("NewBlock without 2 data fields.");
else
{
switch (host()->m_bq.import(_r[0].data(), host()->m_chain))
{
case ImportResult::Success:
addRating(100);
break;
case ImportResult::FutureTime:
//TODO: Rating dependent on how far in future it is.
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
disable("Malformed block received.");
return true;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
break;
case ImportResult::UnknownParent:
clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
setNeedsSyncing(h, _r[1].toInt<u256>());
break;
default:;
}
DEV_GUARDED(x_knownBlocks)
m_knownBlocks.insert(h);
}
host()->onPeerNewBlock(this, _r);
break;
}
case NewBlockHashesPacket:
{
clog(NetMessageSummary) << "NewBlockHashes";
if (host()->isSyncing())
clog(NetMessageSummary) << "Ignoring since we're already downloading.";
else
{
unsigned knowns = 0;
unsigned unknowns = 0;
unsigned itemCount = _r.itemCount();
for (unsigned i = 0; i < itemCount; ++i)
{
addRating(1);
auto h = _r[i].toHash<h256>();
DEV_GUARDED(x_knownBlocks)
m_knownBlocks.insert(h);
auto status = host()->m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h))
knowns++;
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
return true;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
m_syncingNeededBlocks.push_back(h);
}
else
knowns++;
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
if (unknowns > 0)
{
clog(NetNote) << "Not syncing and new block hash discovered: syncing without help.";
host()->m_man.resetToChain(m_syncingNeededBlocks);
host()->changeSyncer(this, false);
transition(Asking::Blocks, false, false); // TODO: transaction(Asking::NewBlocks, false)
}
return true;
}
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "BlockHashes (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreHashes");
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
hashes[i] = _r[i].toHash<h256>();
host()->onPeerNewHashes(this, hashes);
break;
}
default:

50
libethereum/EthereumPeer.h

@ -49,11 +49,11 @@ namespace eth
*/
class EthereumPeer: public p2p::Capability
{
friend class EthereumHost;
friend class EthereumHost; //TODO: remove this
public:
/// Basic constructor.
EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h, unsigned _i);
EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h, unsigned _i, p2p::CapDesc const& _cap);
/// Basic destructor.
virtual ~EthereumPeer();
@ -70,17 +70,26 @@ public:
/// What is the ethereum subprotocol host object.
EthereumHost* host() const;
/// Abort sync and reset fetch
void setIdle();
/// Request hashes. Uses hash download manager to get hash number. v61+ protocol version only
void requestHashes();
/// Request hashes for given parent hash.
void requestHashes(h256 const& _lastHash);
/// Request blocks. Uses block download manager.
void requestBlocks();
private:
using p2p::Capability::sealAndSend;
/// Interpret an incoming message.
virtual bool interpret(unsigned _id, RLP const& _r);
/// Transition state in a particular direction.
void transition(Asking _wantState, bool _force = false, bool _needHelp = true);
/// Attempt to begin syncing with this peer; first check the peer has a more difficlult chain to download, then start asking for hashes, then move to blocks.
void attemptSync();
/// Request status. Called from constructor
void requestStatus();
/// Abort the sync operation.
void abortSync();
@ -89,11 +98,7 @@ private:
void clearKnownTransactions() { std::lock_guard<std::mutex> l(x_knownTransactions); m_knownTransactions.clear(); }
/// Update our asking state.
void setAsking(Asking _g, bool _isSyncing, bool _needHelp = true);
/// Update our syncing requirements state.
void setNeedsSyncing(h256 _latestHash, u256 _td);
void resetNeedsSyncing() { setNeedsSyncing(h256(), 0); }
void setAsking(Asking _g);
/// Do we presently need syncing with this peer?
bool needsSyncing() const { return !!m_latestHash; }
@ -101,14 +106,12 @@ private:
/// Are we presently syncing with this peer?
bool isSyncing() const;
/// Check whether the session should bother grabbing the peer's blocks.
bool shouldGrabBlocks() const;
/// Runs period checks to check up on the peer.
void tick();
/// Peer's protocol version.
unsigned m_protocolVersion;
/// Peer's network id.
u256 m_networkId;
@ -117,24 +120,24 @@ private:
/// When we asked for it. Allows a time out.
std::chrono::system_clock::time_point m_lastAsk;
/// Whether this peer is in the process of syncing or not. Only one peer can be syncing at once.
bool m_isSyncing = false;
/// These are determined through either a Status message or from NewBlock.
h256 m_latestHash; ///< Peer's latest block's hash that we know about or default null value if no need to sync.
u256 m_totalDifficulty; ///< Peer's latest block's total difficulty.
/// Once a sync is started on this peer, they are cleared and moved into m_syncing*.
h256 m_genesisHash; ///< Peer's genesis hash
u256 m_latestBlockNumber; ///< Number of the latest block this peer has
/// This is built as we ask for hashes. Once no more hashes are given, we present this to the
/// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks.
h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer.
h256 m_syncingLastReceivedHash; ///< Hash most recently received from peer.
h256 m_syncingLatestHash; ///< Peer's latest block's hash, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Peer's latest block's total difficulty, as of the current sync.
unsigned m_expectedHashes = 0; ///< Estimated upper bound of hashes to expect from this peer.
unsigned m_syncHashNumber = 0; ///< Number of latest hash we sync to
/// Once we're asking for blocks, this becomes in use.
DownloadSub m_sub;
/// Once we're asking for hashes, this becomes in use.
HashDownloadSub m_hashSub;
u256 m_peerCapabilityVersion; ///< Protocol version this peer supports received as capability
/// Have we received a GetTransactions packet that we haven't yet answered?
bool m_requireTransactions = false;
@ -142,7 +145,6 @@ private:
h256Hash m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them).
Mutex x_knownTransactions;
h256Hash m_knownTransactions; ///< Transactions that the peer already knows of.
};
}

114
libethereum/Executive.cpp

@ -32,6 +32,7 @@ using namespace dev;
using namespace dev::eth;
const char* VMTraceChannel::name() { return "EVM"; }
const char* ExecutiveWarnChannel::name() { return WarnChannel::name(); }
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
m_s(_s),
@ -41,7 +42,7 @@ Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
u256 Executive::gasUsed() const
{
return m_t.gas() - m_endGas;
return m_t.gas() - m_gas;
}
ExecutionResult Executive::executionResult() const
@ -63,7 +64,7 @@ void Executive::initialize(Transaction const& _transaction)
u256 startGasUsed = m_s.gasUsed();
if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit)
{
clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas();
clog(ExecutiveWarnChannel) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas();
m_excepted = TransactionException::BlockGasLimitReached;
BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas()));
}
@ -71,7 +72,7 @@ void Executive::initialize(Transaction const& _transaction)
// Check gas cost is enough.
if (!m_t.checkPayment())
{
clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas();
clog(ExecutiveWarnChannel) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas();
m_excepted = TransactionException::OutOfGas;
BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_t.gasRequired(), (bigint)m_t.gas()));
}
@ -84,13 +85,13 @@ void Executive::initialize(Transaction const& _transaction)
}
catch (...)
{
clog(StateDetail) << "Invalid Signature";
clog(ExecutiveWarnChannel) << "Invalid Signature";
m_excepted = TransactionException::InvalidSignature;
throw;
}
if (m_t.nonce() != nonceReq)
{
clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
clog(ExecutiveWarnChannel) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
m_excepted = TransactionException::InvalidNonce;
BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));
}
@ -100,7 +101,7 @@ void Executive::initialize(Transaction const& _transaction)
m_totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < m_totalCost)
{
clog(StateDetail) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender());
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender());
m_excepted = TransactionException::NotEnoughCash;
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender())));
}
@ -120,75 +121,45 @@ bool Executive::execute()
if (m_t.isCreation())
return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.gasRequired(), &m_t.data(), m_t.sender());
else
return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired(), m_t.sender());
return call(m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired());
}
bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas)
{
m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
auto it = !(_codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_codeAddress) : precompiled().end();
if (it != precompiled().end())
{
bigint g = it->second.gas(_data);
if (_gas < g)
{
m_endGas = 0;
m_excepted = TransactionException::OutOfGasBase;
// Bail from exception.
return true; // true actually means "all finished - nothing more to be done regarding go().
}
else
{
m_endGas = (u256)(_gas - g);
m_precompiledOut = it->second.exec(_data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_codeAddress))
{
m_vm = VMFactory::create(_gas);
bytes const& c = m_s.code(_codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
}
else
m_endGas = _gas;
m_s.transferBalance(_senderAddress, _receiveAddress, _value);
return !m_ext;
CallParameters params{_senderAddress, _receiveAddress, _receiveAddress, _gas, _value, _data, {}, {}};
return call(params, _gasPrice, _senderAddress);
}
bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin)
{
m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
auto it = !(_p.codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_p.codeAddress) : precompiled().end();
if (it != precompiled().end())
{
bigint g = it->second.gas(_p.data);
if (_p.gas < g)
{
m_endGas = 0;
m_excepted = TransactionException::OutOfGasBase;
// Bail from exception.
return true; // true actually means "all finished - nothing more to be done regarding go().
}
else
{
m_endGas = (u256)(_p.gas - g);
m_gas = (u256)(_p.gas - g);
m_precompiledOut = it->second.exec(_p.data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_p.codeAddress))
else
{
m_vm = VMFactory::create(_p.gas);
bytes const& c = m_s.code(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth);
m_gas = _p.gas;
if (m_s.addressHasCode(_p.codeAddress))
{
m_vm = VMFactory::create();
bytes const& c = m_s.code(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth);
}
}
else
m_endGas = _p.gas;
m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.value);
@ -202,11 +173,12 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
// we delete it explicitly if we decide we need to revert.
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
m_gas = _gas;
// Execute _init.
if (!_init.empty())
{
m_vm = VMFactory::create(_gas);
m_vm = VMFactory::create();
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
}
@ -214,17 +186,14 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
m_s.transferBalance(_sender, m_newAddress, _endowment);
if (_init.empty())
{
m_s.m_cache[m_newAddress].setCode({});
m_endGas = _gas;
}
return !m_ext;
}
OnOpFunc Executive::simpleTrace()
{
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
@ -238,7 +207,25 @@ OnOpFunc Executive::simpleTrace()
for (auto const& i: ext.state().storage(ext.myAddress))
o << showbase << hex << i.first << ": " << i.second << endl;
dev::LogOutputStream<VMTraceChannel, false>() << o.str();
dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << vm.gas() << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
OnOpFunc Executive::standardTrace(ostream& o_output)
{
return [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
o_output << endl << " STACK" << endl;
for (auto i: vm.stack())
o_output << (h256)i << endl;
o_output << " MEMORY" << endl << ((vm.memory().size() > 1000) ? " mem size greater than 1000 bytes " : memDump(vm.memory()));
o_output << " STORAGE" << endl;
for (auto const& i: ext.state().storage(ext.myAddress))
o_output << showbase << hex << i.first << ": " << i.second << endl;
o_output << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
@ -251,17 +238,16 @@ bool Executive::go(OnOpFunc const& _onOp)
#endif
try
{
m_out = m_vm->go(*m_ext, _onOp);
m_endGas = m_vm->gas();
m_out = m_vm->go(m_gas, *m_ext, _onOp);
if (m_isCreation)
{
m_gasForDeposit = m_endGas;
m_gasForDeposit = m_gas;
m_depositSize = m_out.size();
if (m_out.size() * c_createDataGas <= m_endGas)
if (m_out.size() * c_createDataGas <= m_gas)
{
m_codeDeposit = CodeDeposit::Success;
m_endGas -= m_out.size() * c_createDataGas;
m_gas -= m_out.size() * c_createDataGas;
}
else
{
@ -279,7 +265,7 @@ bool Executive::go(OnOpFunc const& _onOp)
catch (VMException const& _e)
{
clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
m_endGas = 0;
m_gas = 0;
m_excepted = toTransactionException(_e);
m_ext->revert();
@ -312,12 +298,12 @@ void Executive::finalize()
// SSTORE refunds...
// must be done before the miner gets the fees.
if (m_ext)
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
m_gas += min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds);
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_t.sender(), m_endGas * m_t.gasPrice());
m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());
u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice();
u256 feesEarned = (m_t.gas() - m_gas) * m_t.gasPrice();
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
// Suicides...

14
libethereum/Executive.h

@ -36,6 +36,7 @@ class ExtVM;
struct Manifest;
struct VMTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 11; };
struct ExecutiveWarnChannel: public LogChannel { static const char* name(); static const int verbosity = 6; };
/**
* @brief Message-call/contract-creation executor; useful for executing transactions.
@ -94,20 +95,23 @@ public:
bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
/// Set up the executive for evaluating a bare CALL (message call) operation.
/// @returns false iff go() must be called (and thus a VM execution in required).
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool call(Address _receiveAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas);
bool call(CallParameters const& _cp, u256 const& _gasPrice, Address const& _origin);
/// Finalise an operation through accruing the substate into the parent context.
void accrueSubState(SubState& _parentContext);
/// Executes (or continues execution of) the VM.
/// @returns false iff go() must be called again to finish the transction.
/// @returns false iff go() must be called again to finish the transaction.
bool go(OnOpFunc const& _onOp = OnOpFunc());
/// Operation function for providing a simple trace of the VM execution.
static OnOpFunc simpleTrace();
/// @returns gas remaining after the transaction/operation.
u256 endGas() const { return m_endGas; }
/// Operation function for providing a simple trace of the VM execution.
static OnOpFunc standardTrace(std::ostream& o_output);
/// @returns gas remaining after the transaction/operation. Valid after the transaction has been executed.
u256 gas() const { return m_gas; }
/// @returns output data of the transaction/operation.
bytesConstRef out() const { return m_out; }
/// @returns the new address for the created contract in the CREATE operation.
@ -133,7 +137,7 @@ private:
u256 m_gasForDeposit; ///< Amount of gas remaining for the code deposit phase.
CodeDeposit m_codeDeposit = CodeDeposit::None; ///< True if an attempted deposit failed due to lack of gas.
TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception.
u256 m_endGas; ///< The final amount of gas for the transaction.
u256 m_gas = 0; ///< The gas for EVM code execution. Initial amount before go() execution, final amount after go() execution.
Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().

4
libethereum/ExtVM.cpp

@ -34,7 +34,7 @@ bool ExtVM::call(CallParameters& _p)
e.go(_p.onOp);
e.accrueSubState(sub);
}
_p.gas = e.endGas();
_p.gas = e.gas();
e.out().copyTo(_p.out);
return !e.excepted();
@ -51,7 +51,7 @@ h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc
e.go(_onOp);
e.accrueSubState(sub);
}
io_gas = e.endGas();
io_gas = e.gas();
return e.newAddress();
}

141
libethereum/State.cpp

@ -564,6 +564,52 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
return ret;
}
string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
RLP rlp(_block);
cleanup(false);
BlockInfo bi(_block, (_ir & ImportRequirements::ValidNonce) ? CheckEverything : IgnoreNonce);
m_currentBlock = bi;
m_currentBlock.verifyInternals(_block);
m_currentBlock.noteDirty();
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number);
vector<bytes> receipts;
ostringstream ss;
unsigned i = 0;
for (auto const& tr: rlp[1])
{
ss << " VM Execution of transaction" << i << ":" << endl;
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, Executive::standardTrace(ss));
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
++i;
ss << endl;
}
return ss.str();
}
template <class Channel>
class LogOverride
{
public:
LogOverride(bool _value): m_old(g_logOverride.count(&typeid(Channel)) ? (int)g_logOverride[&typeid(Channel)] : c_null) { g_logOverride[&typeid(Channel)] = _value; }
~LogOverride()
{
if (m_old == c_null)
g_logOverride.erase(&typeid(Channel));
else
g_logOverride[&typeid(Channel)] = (bool)m_old;
}
private:
static const int c_null = -1;
int m_old;
};
u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
// m_currentBlock is assumed to be prepopulated and reset.
@ -587,14 +633,6 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
// cnote << "playback begins:" << m_state.root();
// cnote << m_state;
MemoryDB tm;
GenericTrieDB<MemoryDB> transactionsTrie(&tm);
transactionsTrie.init();
MemoryDB rm;
GenericTrieDB<MemoryDB> receiptsTrie(&rm);
receiptsTrie.init();
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number);
RLP rlp(_block);
@ -604,7 +642,19 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
unsigned i = 0;
for (auto const& tr: rlp[1])
{
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
try {
LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
}
catch (...)
{
badBlock(_block, "Invalid transaction");
cwarn << " Transaction Index:" << i;
LogOverride<ExecutiveWarnChannel> o(true);
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
throw;
}
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
@ -612,40 +662,33 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
}
auto receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot)
{
cwarn << "Bad receipts state root.";
cwarn << "Expected: " << toString(receiptsRoot) << " received: " << toString(m_currentBlock.receiptsRoot);
cwarn << "Block:" << toHex(_block);
cwarn << "Block RLP:" << rlp;
cwarn << "Calculated: " << receiptsRoot;
badBlock(_block, "Bad receipts state root");
cwarn << " Received: " << toString(m_currentBlock.receiptsRoot);
cwarn << " Expected: " << toString(receiptsRoot) << " which is:";
for (unsigned j = 0; j < i; ++j)
{
RLPStream k;
k << j;
auto b = receipts[j];
cwarn << j << ": ";
cwarn << "RLP: " << RLP(b);
cwarn << "Hex: " << toHex(b);
cwarn << TransactionReceipt(&b);
}
cwarn << "Recorded: " << m_currentBlock.receiptsRoot;
auto rs = _bc.receipts(m_currentBlock.hash());
for (unsigned j = 0; j < rs.receipts.size(); ++j)
{
auto b = rs.receipts[j].rlp();
cwarn << j << ": ";
cwarn << "RLP: " << RLP(b);
cwarn << "Hex: " << toHex(b);
cwarn << rs.receipts[j];
cwarn << " RLP: " << RLP(b);
cwarn << " Hex: " << toHex(b);
cwarn << " " << TransactionReceipt(&b);
}
cwarn << " VMTrace:\n" << vmTrace(_block, _bc, _ir);
BOOST_THROW_EXCEPTION(InvalidReceiptsStateRoot());
}
if (m_currentBlock.logBloom != logBloom())
{
cwarn << "Bad log bloom!";
badBlock(_block, "Bad log bloom");
cwarn << " Receipt blooms:";
for (unsigned j = 0; j < i; ++j)
{
auto b = receipts[j];
cwarn << " " << j << ":" << TransactionReceipt(&b).bloom().hex();
}
cwarn << " Final bloom:" << m_currentBlock.logBloom.hex();
BOOST_THROW_EXCEPTION(InvalidLogBloom());
}
@ -654,7 +697,10 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
// Check uncles & apply their rewards to state.
if (rlp[2].itemCount() > 2)
{
badBlock(_block, "Too many uncles");
BOOST_THROW_EXCEPTION(TooManyUncles());
}
vector<BlockInfo> rewarded;
h256Hash excluded = _bc.allKinFrom(m_currentBlock.parentHash, 6);
@ -664,13 +710,30 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
{
auto h = sha3(i.data());
if (excluded.count(h))
{
badBlock(_block, "Invalid uncle included");
BOOST_THROW_EXCEPTION(UncleInChain() << errinfo_comment("Uncle in block already mentioned") << errinfo_data(toString(excluded)) << errinfo_hash256(sha3(i.data())));
}
excluded.insert(h);
BlockInfo uncle = BlockInfo::fromHeader(i.data(), (_ir & ImportRequirements::CheckUncles) ? CheckEverything : IgnoreNonce, h);
BlockInfo uncleParent(_bc.block(uncle.parentHash));
if ((bigint)uncleParent.number < (bigint)m_currentBlock.number - 7)
{
badBlock(_block, "Uncle too old");
cwarn << " Uncle number: " << uncle.number;
cwarn << " Uncle parent number: " << uncleParent.number;
cwarn << " Block number: " << m_currentBlock.number;
BOOST_THROW_EXCEPTION(UncleTooOld());
}
else if (uncle.number == m_currentBlock.number)
{
badBlock(_block, "Uncle is brother");
cwarn << " Uncle number: " << uncle.number;
cwarn << " Uncle parent number: " << uncleParent.number;
cwarn << " Block number: " << m_currentBlock.number;
BOOST_THROW_EXCEPTION(UncleIsBrother());
}
uncle.verifyParent(uncleParent);
// tdIncrease += uncle.difficulty;
@ -685,13 +748,13 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
// Hash the state trie and check against the state_root hash in m_currentBlock.
if (m_currentBlock.stateRoot != m_previousBlock.stateRoot && m_currentBlock.stateRoot != rootHash())
{
cwarn << "Bad state root!";
cnote << "Given to be:" << m_currentBlock.stateRoot;
badBlock(_block, "Bad state root");
cnote << " Given to be:" << m_currentBlock.stateRoot;
// TODO: Fix
// cnote << SecureTrieDB<Address, OverlayDB>(&m_db, m_currentBlock.stateRoot);
cnote << "Calculated to be:" << rootHash();
cnote << " Calculated to be:" << rootHash();
cwarn << " VMTrace:\n" << vmTrace(_block, _bc, _ir);
// cnote << m_state;
cnote << *this;
// Rollback the trie.
m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidStateRoot());
@ -700,6 +763,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
if (m_currentBlock.gasUsed != gasUsed())
{
// Rollback the trie.
badBlock(_block, "Invalid gas used");
m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidGasUsed() << RequirementError(bigint(gasUsed()), bigint(m_currentBlock.gasUsed)));
}
@ -1114,7 +1178,7 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const
return true;
}
ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p)
ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp)
{
#if ETH_PARANOIA
paranoia("start of execution.", true);
@ -1139,9 +1203,12 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
#endif
if (!e.execute())
#if ETH_VMTRACE
{
(void)_onOp;
e.go(e.simpleTrace());
}
#else
e.go();
e.go(_onOp);
#endif
e.finalize();

18
libethereum/State.h

@ -84,9 +84,16 @@ public:
class TrivialGasPricer: public GasPricer
{
protected:
u256 ask(State const&) const override { return 10 * szabo; }
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return 10 * szabo; }
public:
TrivialGasPricer() = default;
TrivialGasPricer(u256 const& _ask, u256 const& _bid): m_ask(_ask), m_bid(_bid) {}
u256 ask(State const&) const override { return m_ask; }
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return m_bid; }
private:
u256 m_ask = 10 * szabo;
u256 m_bid = 10 * szabo;
};
enum class Permanence
@ -190,7 +197,7 @@ public:
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
ExecutionResult execute(LastHashes const& _lh, Transaction const& _t, Permanence _p = Permanence::Committed);
ExecutionResult execute(LastHashes const& _lh, Transaction const& _t, Permanence _p = Permanence::Committed, OnOpFunc const& _onOp = OnOpFunc());
/// Get the remaining gas limit in this block.
u256 gasLimitRemaining() const { return m_currentBlock.gasLimit - gasUsed(); }
@ -351,6 +358,9 @@ private:
/// Debugging only. Good for checking the Trie is in shape.
void paranoia(std::string const& _when, bool _enforceRefs = false) const;
/// Provide a standard VM trace for debugging purposes.
std::string vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir);
OverlayDB m_db; ///< Our overlay for the state tree.
SecureTrieDB<Address, OverlayDB> m_state; ///< Our state tree, as an OverlayDB DB.
Transactions m_transactions; ///< The current list of transactions that we've included in the state.

21
libethereum/Transaction.cpp

@ -39,6 +39,27 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, ExecutionResult const& _e
return _out;
}
std::string badTransaction(bytesConstRef _tx, string const& _err)
{
stringstream ret;
ret << "========================================================================" << endl;
ret << "== Software Failure " << (_err + string(max<int>(0, 44 - _err.size()), ' ')) << " ==" << endl;
ret << "== Guru Meditation " << sha3(_tx).abridged() << " ==" << endl;
ret << "========================================================================" << endl;
ret << " Transaction: " << toHex(_tx) << endl;
ret << " Transaction RLP: ";
try {
ret << RLP(_tx);
}
catch (Exception& _e)
{
ret << "Invalid: " << _e.what();
}
ret << endl;
return ret.str();
}
TransactionException dev::eth::toTransactionException(VMException const& _e)
{
if (!!dynamic_cast<BadInstruction const*>(&_e))

3
libethereum/Transaction.h

@ -235,5 +235,8 @@ inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t)
return _out;
}
void badTransaction(bytesConstRef _tx, std::string const& _err);
inline void badTransaction(bytes const& _tx, std::string const& _err) { badTransaction(&_tx, _err); }
}
}

2
libevm/ExtVMFace.h

@ -106,7 +106,7 @@ class VM;
using LastHashes = std::vector<h256>;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, VM*, ExtVMFace const*)>;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, bigint /*gas*/, VM*, ExtVMFace const*)>;
struct CallParameters
{

10
libevm/SmartVM.cpp

@ -41,7 +41,7 @@ namespace
}
}
bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
bytesConstRef SmartVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
auto codeHash = sha3(_ext.code);
auto vmKind = VMKind::Interpreter; // default VM
@ -66,11 +66,9 @@ bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step
}
// TODO: Selected VM must be kept only because it returns reference to its internal memory.
// VM implementations should be stateless, without gas counter and escaping memory reference.
m_selectedVM = VMFactory::create(vmKind, gas());
auto out = m_selectedVM->go(_ext, _onOp, _steps);
m_gas = m_selectedVM->gas();
return out;
// VM implementations should be stateless, without escaping memory reference.
m_selectedVM = VMFactory::create(vmKind);
return m_selectedVM->go(io_gas, _ext, _onOp, _steps);
}
}

8
libevm/SmartVM.h

@ -31,16 +31,10 @@ namespace eth
class SmartVM: public VMFace
{
public:
SmartVM(u256 const& _gas): m_gas(_gas) {}
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void reset(u256 const& _gas = 0) noexcept override { m_gas = _gas; }
u256 gas() const noexcept override { return (u256)m_gas; }
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
private:
std::unique_ptr<VMFace> m_selectedVM;
bigint m_gas;
};
}

43
libevm/VM.cpp

@ -25,13 +25,6 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
void VM::reset(u256 const& _gas) noexcept
{
m_gas = _gas;
m_curPC = 0;
m_jumpDests.clear();
}
struct InstructionMetric
{
int gasPriceTier;
@ -52,8 +45,12 @@ static array<InstructionMetric, 256> metrics()
return s_ret;
}
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
bytesConstRef VM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
// Reset leftovers from possible previous run
m_curPC = 0;
m_jumpDests.clear();
m_stack.reserve((unsigned)c_stackLimit);
unique_ptr<CallParameters> callParams;
@ -71,7 +68,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
for (unsigned i = 0; i < _ext.code.size(); ++i)
{
if (_ext.code[i] == (byte)Instruction::JUMPDEST)
m_jumpDests.insert(i);
m_jumpDests.push_back(i);
else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
}
@ -100,7 +97,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
auto onOperation = [&]()
{
if (_onOp)
_onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext);
_onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
};
switch (inst)
@ -198,17 +195,11 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
runGas += c_copyGas * ((copySize + 31) / 32);
onOperation();
// if (_onOp)
// _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext);
if (m_gas < runGas)
{
// Out of gas!
m_gas = 0;
if (io_gas < runGas)
BOOST_THROW_EXCEPTION(OutOfGas());
}
m_gas -= runGas;
io_gas -= (u256)runGas;
if (newTempSize > m_temp.size())
m_temp.resize((size_t)newTempSize);
@ -546,7 +537,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
break;
case Instruction::JUMP:
nextPC = m_stack.back();
if (!m_jumpDests.count(nextPC))
if (find(m_jumpDests.begin(), m_jumpDests.end(), (uint64_t)nextPC) == m_jumpDests.end() || nextPC > numeric_limits<uint64_t>::max() )
BOOST_THROW_EXCEPTION(BadJumpDestination());
m_stack.pop_back();
break;
@ -554,7 +545,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
if (m_stack[m_stack.size() - 2])
{
nextPC = m_stack.back();
if (!m_jumpDests.count(nextPC))
if (find(m_jumpDests.begin(), m_jumpDests.end(), (uint64_t)nextPC) == m_jumpDests.end() || nextPC > numeric_limits<uint64_t>::max() )
BOOST_THROW_EXCEPTION(BadJumpDestination());
}
m_stack.pop_back();
@ -567,7 +558,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.push_back(m_temp.size());
break;
case Instruction::GAS:
m_stack.push_back((u256)m_gas);
m_stack.push_back(io_gas);
break;
case Instruction::JUMPDEST:
break;
@ -616,11 +607,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back();
if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024)
{
u256 g(m_gas);
m_stack.push_back((u160)_ext.create(endowment, g, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
m_gas = g;
}
m_stack.push_back((u160)_ext.create(endowment, io_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
else
m_stack.push_back(0);
break;
@ -661,7 +648,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
else
m_stack.push_back(0);
m_gas += callParams->gas;
io_gas += callParams->gas;
break;
}
case Instruction::RETURN:
@ -670,7 +657,6 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back();
unsigned s = (unsigned)m_stack.back();
m_stack.pop_back();
return bytesConstRef(m_temp.data() + b, s);
}
case Instruction::SUICIDE:
@ -683,6 +669,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
return bytesConstRef();
}
}
if (_steps == (uint64_t)-1)
BOOST_THROW_EXCEPTION(StepsDone());
return bytesConstRef();

17
libevm/VM.h

@ -52,31 +52,22 @@ inline u256 fromAddress(Address _a)
class VM: public VMFace
{
public:
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
u256 curPC() const { return m_curPC; }
bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; }
void reset(u256 const& _gas = 0) noexcept override;
u256 gas() const noexcept override { return (u256)m_gas; }
private:
friend class VMFactory;
/// Construct VM object.
explicit VM(u256 _gas): m_gas(_gas) {}
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 m_curPC = 0;
bytes m_temp;
u256s m_stack;
std::set<u256> m_jumpDests;
std::vector<uint64_t> m_jumpDests;
std::function<void()> m_onFail;
bigint m_gas = 0;
};
}

5
libevm/VMFace.h

@ -43,10 +43,7 @@ public:
VMFace(VMFace const&) = delete;
VMFace& operator=(VMFace const&) = delete;
virtual void reset(u256 const& _gas = 0) noexcept = 0;
virtual u256 gas() const noexcept = 0;
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
};
}

14
libevm/VMFactory.cpp

@ -38,27 +38,27 @@ void VMFactory::setKind(VMKind _kind)
g_kind = _kind;
}
std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
std::unique_ptr<VMFace> VMFactory::create()
{
return create(g_kind, _gas);
return create(g_kind);
}
std::unique_ptr<VMFace> VMFactory::create(VMKind _kind, u256 _gas)
std::unique_ptr<VMFace> VMFactory::create(VMKind _kind)
{
#if ETH_EVMJIT
switch (_kind)
{
default:
case VMKind::Interpreter:
return std::unique_ptr<VMFace>(new VM(_gas));
return std::unique_ptr<VMFace>(new VM);
case VMKind::JIT:
return std::unique_ptr<VMFace>(new JitVM(_gas));
return std::unique_ptr<VMFace>(new JitVM);
case VMKind::Smart:
return std::unique_ptr<VMFace>(new SmartVM(_gas));
return std::unique_ptr<VMFace>(new SmartVM);
}
#else
asserts(_kind == VMKind::Interpreter && "JIT disabled in build configuration");
return std::unique_ptr<VMFace>(new VM(_gas));
return std::unique_ptr<VMFace>(new VM);
#endif
}

4
libevm/VMFactory.h

@ -36,10 +36,10 @@ public:
VMFactory() = delete;
/// Creates a VM instance of global kind (controlled by setKind() function).
static std::unique_ptr<VMFace> create(u256 _gas);
static std::unique_ptr<VMFace> create();
/// Creates a VM instance of kind provided.
static std::unique_ptr<VMFace> create(VMKind _kind, u256 _gas);
static std::unique_ptr<VMFace> create(VMKind _kind);
/// Set global VM kind
static void setKind(VMKind _kind);

42
libevmasm/Assembly.cpp

@ -127,7 +127,10 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
_out << " PUSH \"" << m_strings.at((h256)i.data()) << "\"";
break;
case PushTag:
_out << " PUSH [tag" << dec << i.data() << "]";
if (i.data() == 0)
_out << " PUSH [ErrorTag]";
else
_out << " PUSH [tag" << dec << i.data() << "]";
break;
case PushSub:
_out << " PUSH [$" << h256(i.data()).abridged() << "]";
@ -207,8 +210,12 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
createJsonValue("PUSH tag", i.getLocation().start, i.getLocation().end, m_strings.at((h256)i.data())));
break;
case PushTag:
collection.append(
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, string(i.data())));
if (i.data() == 0)
collection.append(
createJsonValue("PUSH [ErrorTag]", i.getLocation().start, i.getLocation().end, ""));
else
collection.append(
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, string(i.data())));
break;
case PushSub:
collection.append(
@ -226,7 +233,7 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
collection.append(
createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data())));
collection.append(
createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end));
createJsonValue("JUMPDEST", i.getLocation().start, i.getLocation().end));
break;
case PushData:
collection.append(createJsonValue("PUSH data", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
@ -307,6 +314,11 @@ Assembly& Assembly::optimise(bool _enable)
count = 0;
copt << "Performing optimisation...";
// This only modifies PushTags, we have to run again to actually remove code.
BlockDeduplicator dedup(m_items);
if (dedup.deduplicate())
count++;
{
ControlFlowGraph cfg(m_items);
AssemblyItems optimisedItems;
@ -349,11 +361,6 @@ Assembly& Assembly::optimise(bool _enable)
m_items = move(optimisedItems);
count++;
}
// This only modifies PushTags, we have to run again to actually remove code.
BlockDeduplicator dedup(m_items);
if (dedup.deduplicate())
count++;
}
}
@ -387,6 +394,11 @@ bytes Assembly::assemble() const
// m_data must not change from here on
for (AssemblyItem const& i: m_items)
{
// store position of the invalid jump destination
if (i.type() != Tag && tagPos[0] == 0)
tagPos[0] = ret.size();
switch (i.type())
{
case Operation:
@ -448,17 +460,23 @@ bytes Assembly::assemble() const
}
case Tag:
tagPos[(unsigned)i.data()] = ret.size();
assertThrow(i.data() != 0, AssemblyException, "");
ret.push_back((byte)Instruction::JUMPDEST);
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
}
for (auto const& i: tagRef)
{
bytesRef r(ret.data() + i.first, bytesPerTag);
//@todo in the failure case, we could use the position of the invalid jumpdest
toBigEndian(i.second < tagPos.size() ? tagPos[i.second] : (1 << (8 * bytesPerTag)) - 1, r);
auto tag = i.second;
if (tag >= tagPos.size())
tag = 0;
if (tag == 0)
assertThrow(tagPos[tag] != 0, AssemblyException, "");
toBigEndian(tagPos[tag], r);
}
if (!m_data.empty())

6
libevmasm/Assembly.h

@ -67,6 +67,8 @@ public:
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem errorTag() { return AssemblyItem(PushTag, 0); }
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
AssemblyItems const& getItems() const { return m_items; }
AssemblyItem const& back() const { return m_items.back(); }
@ -97,7 +99,6 @@ public:
const StringMap &_sourceCodes = StringMap(),
bool _inJsonFormat = false
) const;
protected:
std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@ -109,7 +110,8 @@ private:
Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const;
protected:
unsigned m_usedTags = 0;
// 0 is reserved for exception
unsigned m_usedTags = 1;
AssemblyItems m_items;
mutable std::map<h256, bytes> m_data;
std::vector<Assembly> m_subs;

2
libevmasm/AssemblyItem.h

@ -65,7 +65,7 @@ public:
/// @returns the instruction of this item (only valid if type() == Operation)
Instruction instruction() const { return Instruction(byte(m_data)); }
/// @returns true iff the type and data of the items are equal.
/// @returns true if the type and data of the items are equal.
bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; }
bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); }
/// Less-than operator compatible with operator==.

75
libevmasm/BlockDeduplicator.cpp

@ -35,13 +35,33 @@ bool BlockDeduplicator::deduplicate()
{
// Compares indices based on the suffix that starts there, ignoring tags and stopping at
// opcodes that stop the control flow.
// Virtual tag that signifies "the current block" and which is used to optimise loops.
// We abort if this virtual tag actually exists.
AssemblyItem pushSelf(PushTag, u256(-4));
if (
std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag())
)
return false;
function<bool(size_t, size_t)> comparator = [&](size_t _i, size_t _j)
{
if (_i == _j)
return false;
BlockIterator first(m_items.begin() + _i, m_items.end());
BlockIterator second(m_items.begin() + _j, m_items.end());
// To compare recursive loops, we have to already unify PushTag opcodes of the
// block's own tag.
AssemblyItem pushFirstTag(pushSelf);
AssemblyItem pushSecondTag(pushSelf);
if (_i < m_items.size() && m_items.at(_i).type() == Tag)
pushFirstTag = m_items.at(_i).pushTag();
if (_j < m_items.size() && m_items.at(_j).type() == Tag)
pushSecondTag = m_items.at(_j).pushTag();
BlockIterator first(m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf);
BlockIterator second(m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf);
BlockIterator end(m_items.end(), m_items.end());
if (first != end && (*first).type() == Tag)
@ -52,27 +72,34 @@ bool BlockDeduplicator::deduplicate()
return std::lexicographical_compare(first, end, second, end);
};
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
map<u256, u256> tagReplacement;
for (size_t i = 0; i < m_items.size(); ++i)
size_t iterations = 0;
for (; ; ++iterations)
{
if (m_items.at(i).type() != Tag)
continue;
auto it = blocksSeen.find(i);
if (it == blocksSeen.end())
blocksSeen.insert(i);
else
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
}
bool ret = false;
for (AssemblyItem& item: m_items)
if (item.type() == PushTag && tagReplacement.count(item.data()))
//@todo this should probably be optimized.
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
map<u256, u256> tagReplacement;
for (size_t i = 0; i < m_items.size(); ++i)
{
ret = true;
item.setData(tagReplacement.at(item.data()));
if (m_items.at(i).type() != Tag)
continue;
auto it = blocksSeen.find(i);
if (it == blocksSeen.end())
blocksSeen.insert(i);
else
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
}
return ret;
bool changed = false;
for (AssemblyItem& item: m_items)
if (item.type() == PushTag && tagReplacement.count(item.data()))
{
changed = true;
item.setData(tagReplacement.at(item.data()));
}
if (!changed)
break;
}
return iterations > 0;
}
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
@ -89,3 +116,11 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
}
return *this;
}
AssemblyItem const& BlockDeduplicator::BlockIterator::operator*() const
{
if (replaceItem && replaceWith && *it == *replaceItem)
return *replaceWith;
else
return *it;
}

16
libevmasm/BlockDeduplicator.h

@ -47,19 +47,27 @@ public:
bool deduplicate();
private:
/// Iterator that skips tags skips to the end if (all branches of) the control
/// Iterator that skips tags and skips to the end if (all branches of) the control
/// flow does not continue to the next instruction.
/// If the arguments are supplied to the constructor, replaces items on the fly.
struct BlockIterator: std::iterator<std::forward_iterator_tag, AssemblyItem const>
{
public:
BlockIterator(AssemblyItems::const_iterator _it, AssemblyItems::const_iterator _end):
it(_it), end(_end) { }
BlockIterator(
AssemblyItems::const_iterator _it,
AssemblyItems::const_iterator _end,
AssemblyItem const* _replaceItem = nullptr,
AssemblyItem const* _replaceWith = nullptr
):
it(_it), end(_end), replaceItem(_replaceItem), replaceWith(_replaceWith) {}
BlockIterator& operator++();
bool operator==(BlockIterator const& _other) const { return it == _other.it; }
bool operator!=(BlockIterator const& _other) const { return it != _other.it; }
AssemblyItem const& operator*() const { return *it; }
AssemblyItem const& operator*() const;
AssemblyItems::const_iterator it;
AssemblyItems::const_iterator end;
AssemblyItem const* replaceItem;
AssemblyItem const* replaceWith;
};
AssemblyItems& m_items;

8
libevmasm/ControlFlowGraph.cpp

@ -226,7 +226,10 @@ void ControlFlowGraph::gatherKnowledge()
//@todo we might have to do something like incrementing the sequence number for each JUMPDEST
assertThrow(!!workQueue.back().first, OptimizerException, "");
if (!m_blocks.count(workQueue.back().first))
{
workQueue.pop_back();
continue; // too bad, we do not know the tag, probably an invalid jump
}
BasicBlock& block = m_blocks.at(workQueue.back().first);
KnownStatePointer state = workQueue.back().second;
workQueue.pop_back();
@ -257,10 +260,7 @@ void ControlFlowGraph::gatherKnowledge()
);
state->feedItem(m_items.at(pc++));
if (tags.empty() || std::any_of(tags.begin(), tags.end(), [&](u256 const& _tag)
{
return !m_blocks.count(BlockId(_tag));
}))
if (tags.empty())
{
if (!unknownJumpEncountered)
{

27
libevmasm/ExpressionClasses.cpp

@ -57,11 +57,11 @@ ExpressionClasses::Id ExpressionClasses::find(
exp.arguments = _arguments;
exp.sequenceNumber = _sequenceNumber;
if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end());
if (SemanticInformation::isDeterministic(_item))
{
if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end());
auto it = m_expressions.find(exp);
if (it != m_expressions.end())
return it->id;
@ -82,6 +82,27 @@ ExpressionClasses::Id ExpressionClasses::find(
return exp.id;
}
void ExpressionClasses::forceEqual(
ExpressionClasses::Id _id,
AssemblyItem const& _item,
ExpressionClasses::Ids const& _arguments,
bool _copyItem
)
{
Expression exp;
exp.id = _id;
exp.item = &_item;
exp.arguments = _arguments;
if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end());
if (_copyItem)
exp.item = storeItem(_item);
m_expressions.insert(exp);
}
ExpressionClasses::Id ExpressionClasses::newClass(SourceLocation const& _location)
{
Expression exp;

5
libevmasm/ExpressionClasses.h

@ -74,6 +74,11 @@ public:
/// @returns the number of classes.
Id size() const { return m_representatives.size(); }
/// Forces the given @a _item with @a _arguments to the class @a _id. This can be used to
/// add prior knowledge e.g. about CALLDATA, but has to be used with caution. Will not work as
/// expected if @a _item applied to @a _arguments already exists.
void forceEqual(Id _id, AssemblyItem const& _item, Ids const& _arguments, bool _copyItem = true);
/// @returns the id of a new class which is different to all other classes.
Id newClass(SourceLocation const& _location);

9
libevmasm/GasMeter.cpp

@ -29,12 +29,13 @@ using namespace dev::eth;
GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other)
{
isInfinite = isInfinite || _other.isInfinite;
if (_other.isInfinite && !isInfinite)
*this = infinite();
if (isInfinite)
return *this;
bigint v = bigint(value) + _other.value;
if (v > std::numeric_limits<u256>::max())
isInfinite = true;
if (v > numeric_limits<u256>::max())
*this = infinite();
else
value = u256(v);
return *this;
@ -147,7 +148,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item)
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
gas += c_expByteGas * (32 - (h256(*value).firstBitSet() / 8));
else
gas = GasConsumption::infinite();
gas += c_expByteGas * 32;
break;
default:
break;

14
libevmasm/GasMeter.h

@ -22,6 +22,7 @@
#pragma once
#include <ostream>
#include <tuple>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/AssemblyItem.h>
@ -46,20 +47,25 @@ public:
GasConsumption(u256 _value = 0, bool _infinite = false): value(_value), isInfinite(_infinite) {}
static GasConsumption infinite() { return GasConsumption(0, true); }
GasConsumption& operator+=(GasConsumption const& _otherS);
std::ostream& operator<<(std::ostream& _str) const;
GasConsumption& operator+=(GasConsumption const& _other);
bool operator<(GasConsumption const& _other) const { return this->tuple() < _other.tuple(); }
std::tuple<bool const&, u256 const&> tuple() const { return std::tie(isInfinite, value); }
u256 value;
bool isInfinite;
};
/// Constructs a new gas meter given the current state.
GasMeter(std::shared_ptr<KnownState> const& _state): m_state(_state) {}
explicit GasMeter(std::shared_ptr<KnownState> const& _state, u256 const& _largestMemoryAccess = 0):
m_state(_state), m_largestMemoryAccess(_largestMemoryAccess) {}
/// @returns an upper bound on the gas consumed by the given instruction and updates
/// the state.
GasConsumption estimateMax(AssemblyItem const& _item);
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
@ -80,7 +86,7 @@ private:
inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption const& _consumption)
{
if (_consumption.isInfinite)
return _str << "inf";
return _str << "[???]";
else
return _str << std::dec << _consumption.value;
}

128
libevmasm/PathGasMeter.cpp

@ -0,0 +1,128 @@
/*
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 PathGasMeter.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#include "PathGasMeter.h"
#include <libevmasm/KnownState.h>
#include <libevmasm/SemanticInformation.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
PathGasMeter::PathGasMeter(AssemblyItems const& _items):
m_items(_items)
{
for (size_t i = 0; i < m_items.size(); ++i)
if (m_items[i].type() == Tag)
m_tagPositions[m_items[i].data()] = i;
}
GasMeter::GasConsumption PathGasMeter::estimateMax(
size_t _startIndex,
shared_ptr<KnownState> const& _state
)
{
auto path = unique_ptr<GasPath>(new GasPath());
path->index = _startIndex;
path->state = _state->copy();
m_queue.push_back(move(path));
GasMeter::GasConsumption gas;
while (!m_queue.empty() && !gas.isInfinite)
gas = max(gas, handleQueueItem());
return gas;
}
GasMeter::GasConsumption PathGasMeter::handleQueueItem()
{
assertThrow(!m_queue.empty(), OptimizerException, "");
unique_ptr<GasPath> path = move(m_queue.back());
m_queue.pop_back();
shared_ptr<KnownState> state = path->state;
GasMeter meter(state, path->largestMemoryAccess);
ExpressionClasses& classes = state->expressionClasses();
GasMeter::GasConsumption gas = path->gas;
size_t index = path->index;
if (index >= m_items.size() || (index > 0 && m_items.at(index).type() != Tag))
// Invalid jump usually provokes an out-of-gas exception, but we want to give an upper
// bound on the gas that is needed without changing the behaviour, so it is fine to
// return the current gas value.
return gas;
set<u256> jumpTags;
for (; index < m_items.size() && !gas.isInfinite; ++index)
{
bool branchStops = false;
jumpTags.clear();
AssemblyItem const& item = m_items.at(index);
if (item.type() == Tag || item == AssemblyItem(eth::Instruction::JUMPDEST))
{
// Do not allow any backwards jump. This is quite restrictive but should work for
// the simplest things.
if (path->visitedJumpdests.count(index))
return GasMeter::GasConsumption::infinite();
path->visitedJumpdests.insert(index);
}
else if (item == AssemblyItem(eth::Instruction::JUMP))
{
branchStops = true;
jumpTags = state->tagsInExpression(state->relativeStackElement(0));
if (jumpTags.empty()) // unknown jump destination
return GasMeter::GasConsumption::infinite();
}
else if (item == AssemblyItem(eth::Instruction::JUMPI))
{
ExpressionClasses::Id condition = state->relativeStackElement(-1);
if (classes.knownNonZero(condition) || !classes.knownZero(condition))
{
jumpTags = state->tagsInExpression(state->relativeStackElement(0));
if (jumpTags.empty()) // unknown jump destination
return GasMeter::GasConsumption::infinite();
}
branchStops = classes.knownNonZero(condition);
}
else if (SemanticInformation::altersControlFlow(item))
branchStops = true;
gas += meter.estimateMax(item);
for (u256 const& tag: jumpTags)
{
auto newPath = unique_ptr<GasPath>(new GasPath());
newPath->index = m_items.size();
if (m_tagPositions.count(tag))
newPath->index = m_tagPositions.at(tag);
newPath->gas = gas;
newPath->largestMemoryAccess = meter.largestMemoryAccess();
newPath->state = state->copy();
newPath->visitedJumpdests = path->visitedJumpdests;
m_queue.push_back(move(newPath));
}
if (branchStops)
break;
}
return gas;
}

66
libevmasm/PathGasMeter.h

@ -0,0 +1,66 @@
/*
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 PathGasMeter.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#pragma once
#include <set>
#include <vector>
#include <memory>
#include <libevmasm/GasMeter.h>
namespace dev
{
namespace eth
{
class KnownState;
struct GasPath
{
size_t index = 0;
std::shared_ptr<KnownState> state;
u256 largestMemoryAccess;
GasMeter::GasConsumption gas;
std::set<size_t> visitedJumpdests;
};
/**
* Computes an upper bound on the gas usage of a computation starting at a certain position in
* a list of AssemblyItems in a given state until the computation stops.
* Can be used to estimate the gas usage of functions on any given input.
*/
class PathGasMeter
{
public:
PathGasMeter(AssemblyItems const& _items);
GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state);
private:
GasMeter::GasConsumption handleQueueItem();
std::vector<std::unique_ptr<GasPath>> m_queue;
std::map<u256, size_t> m_tagPositions;
AssemblyItems const& m_items;
};
}
}

2
libevmasm/SemanticInformation.cpp

@ -111,7 +111,7 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
switch (_item.instruction())
{
// note that CALL, CALLCODE and CREATE do not really alter the control flow, because we
// continue on the next instruction (unless an exception happens which can always happen)
// continue on the next instruction
case Instruction::JUMP:
case Instruction::JUMPI:
case Instruction::RETURN:

6
libp2p/Host.cpp

@ -202,6 +202,10 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
// clang error (previously: ... << hex << caps ...)
// "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments"
stringstream capslog;
if (caps.size() > 1)
caps.erase(remove_if(caps.begin(), caps.end(), [&](CapDesc const& _r){ return any_of(caps.begin(), caps.end(), [&](CapDesc const& _o){ return _r.first == _o.first && _o.second > _r.second; }); }), caps.end());
for (auto cap: caps)
capslog << "(" << cap.first << "," << dec << cap.second << ")";
clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id << showbase << capslog.str() << dec << listenPort;
@ -237,7 +241,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
for (auto const& i: caps)
if (haveCapability(i))
{
ps->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(ps.get(), o));
ps->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(ps.get(), o, i));
o += m_capabilities[i]->messageCount();
}
ps->start();

1
libp2p/Host.h

@ -99,6 +99,7 @@ public:
/// Register a peer-capability; all new peer connections will have this capability.
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; std::shared_ptr<T> ret(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; }
template <class T> void addCapability(std::shared_ptr<T> const & _p, std::string const& _name, u256 const& _version) { m_capabilities[std::make_pair(_name, _version)] = _p; }
bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; }
CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; }

7
libp2p/HostCapability.cpp

@ -28,12 +28,17 @@ using namespace dev;
using namespace dev::p2p;
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions() const
{
return peerSessions(version());
}
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions(u256 const& _version) const
{
RecursiveGuard l(m_host->x_sessions);
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> ret;
for (auto const& i: m_host->m_sessions)
if (std::shared_ptr<Session> s = i.second.lock())
if (s->m_capabilities.count(capDesc()))
if (s->m_capabilities.count(std::make_pair(name(), _version)))
ret.push_back(make_pair(s,s->m_peer));
return ret;
}

5
libp2p/HostCapability.h

@ -46,13 +46,14 @@ public:
Host* host() const { return m_host; }
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> peerSessions() const;
std::vector<std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>>> peerSessions(u256 const& _version) const;
protected:
virtual std::string name() const = 0;
virtual u256 version() const = 0;
CapDesc capDesc() const { return std::make_pair(name(), version()); }
virtual unsigned messageCount() const = 0;
virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset) = 0;
virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset, CapDesc const& _cap) = 0;
virtual void onStarting() {}
virtual void onStopping() {}
@ -76,7 +77,7 @@ protected:
virtual std::string name() const { return PeerCap::name(); }
virtual u256 version() const { return PeerCap::version(); }
virtual unsigned messageCount() const { return PeerCap::messageCount(); }
virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset) { return new PeerCap(_s, this, _idOffset); }
virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset, CapDesc const& _cap) { return new PeerCap(_s, this, _idOffset, _cap); }
};
}

2
libp2p/Session.h

@ -69,6 +69,8 @@ public:
template <class PeerCap>
std::shared_ptr<PeerCap> cap() const { try { return std::static_pointer_cast<PeerCap>(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } }
template <class PeerCap>
std::shared_ptr<PeerCap> cap(u256 const& _version) const { try { return std::static_pointer_cast<PeerCap>(m_capabilities.at(std::make_pair(PeerCap::name(), _version))); } catch (...) { return nullptr; } }
static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0);
void sealAndSend(RLPStream& _s);

10
libp2p/UDP.h

@ -65,8 +65,8 @@ protected:
*/
struct RLPXDatagramFace: public UDPDatagram
{
static uint32_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); }
static uint32_t secondsSinceEpoch() { return std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now()).time_since_epoch()).count(); }
static uint32_t futureFromEpoch(std::chrono::seconds _sec) { return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + _sec).time_since_epoch()).count()); }
static uint32_t secondsSinceEpoch() { return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now()).time_since_epoch()).count()); }
static Public authenticate(bytesConstRef _sig, bytesConstRef _rlp);
virtual uint8_t packetType() = 0;
@ -99,7 +99,7 @@ struct UDPSocketFace
*/
struct UDPSocketEvents
{
virtual void onDisconnected(UDPSocketFace*) {};
virtual void onDisconnected(UDPSocketFace*) {}
virtual void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packetData) = 0;
};
@ -115,7 +115,7 @@ class UDPSocket: UDPSocketFace, public std::enable_shared_from_this<UDPSocket<Ha
{
public:
enum { maxDatagramSize = MaxDatagramSize };
static_assert(maxDatagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes");
static_assert((unsigned)maxDatagramSize < 65507u, "UDP datagrams cannot be larger than 65507 bytes");
/// Create socket for specific endpoint.
UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, bi::udp::endpoint _endpoint): m_host(_host), m_endpoint(_endpoint), m_socket(_io) { m_started.store(false); m_closed.store(true); };
@ -283,4 +283,4 @@ void UDPSocket<Handler, MaxDatagramSize>::disconnectWithError(boost::system::err
}
}
}
}

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.

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save