Browse Source

Merge branch 'develop' into client_ref

Conflicts:
	libevm/ExtVMFace.h
	libweb3jsonrpc/WebThreeStubServerBase.cpp
cl-refactor
Marek Kotewicz 10 years ago
parent
commit
4fd02cdd4c
  1. 33
      CMakeLists.txt
  2. 2
      abi/CMakeLists.txt
  3. 2
      alethzero/DappLoader.cpp
  4. 60
      alethzero/MainWin.cpp
  5. 2
      alethzero/MainWin.h
  6. 7
      alethzero/NatspecHandler.h
  7. 4
      alethzero/OurWebThreeStubServer.cpp
  8. 2
      alethzero/OurWebThreeStubServer.h
  9. 7
      alethzero/Transact.cpp
  10. 8
      appdmg.json.in
  11. BIN
      bg.png
  12. 18
      cmake/EthCompilerSettings.cmake
  13. 6
      cmake/EthDependencies.cmake
  14. 49
      cmake/FindRocksDB.cmake
  15. 106
      eth/main.cpp
  16. 20
      ethkey/KeyAux.h
  17. 32
      ethminer/MinerAux.h
  18. 2
      ethvm/CMakeLists.txt
  19. 23
      evmjit/CMakeLists.txt
  20. 15
      evmjit/libevmjit/Arith256.cpp
  21. 20
      evmjit/libevmjit/Array.cpp
  22. 17
      evmjit/libevmjit/Cache.cpp
  23. 6
      evmjit/libevmjit/Cache.h
  24. 10
      evmjit/libevmjit/Compiler.cpp
  25. 5
      evmjit/libevmjit/Endianness.cpp
  26. 28
      evmjit/libevmjit/ExecutionEngine.cpp
  27. 22
      evmjit/libevmjit/GasMeter.cpp
  28. 8
      evmjit/libevmjit/Optimizer.cpp
  29. 12
      evmjit/libevmjit/RuntimeManager.cpp
  30. 3
      evmjit/libevmjit/Type.h
  31. 2
      exp/CMakeLists.txt
  32. 63
      getcoverage.sh
  33. BIN
      install-folder-bg.png
  34. BIN
      install-folder-bg@2x.png
  35. 2
      libdevcore/CMakeLists.txt
  36. 2
      libdevcore/Common.cpp
  37. 19
      libdevcore/Common.h
  38. 4
      libdevcore/CommonData.cpp
  39. 14
      libdevcore/CommonData.h
  40. 83
      libdevcore/CommonIO.cpp
  41. 15
      libdevcore/CommonIO.h
  42. 7
      libdevcore/FixedHash.h
  43. 4
      libdevcore/RLP.cpp
  44. 2
      libdevcore/RLP.h
  45. 15
      libdevcore/TrieDB.h
  46. 36
      libdevcore/db.h
  47. 33
      libdevcore/vector_ref.h
  48. 2
      libdevcrypto/AES.h
  49. 4
      libdevcrypto/CMakeLists.txt
  50. 111
      libdevcrypto/Common.cpp
  51. 27
      libdevcrypto/Common.h
  52. 35
      libdevcrypto/Exceptions.h
  53. 3
      libdevcrypto/OverlayDB.cpp
  54. 7
      libdevcrypto/OverlayDB.h
  55. 120
      libdevcrypto/SecretStore.cpp
  56. 50
      libdevcrypto/SecretStore.h
  57. 21
      libethash-cl/CMakeLists.txt
  58. 60
      libethash-cl/bin2h.cmake
  59. 74
      libethash-cl/ethash_cl_miner.cpp
  60. 3
      libethash-cl/ethash_cl_miner.h
  61. 24
      libethash-cl/ethash_cl_miner_kernel.cl
  62. 3
      libethash/endian.h
  63. 4
      libethash/internal.c
  64. 21
      libethash/internal.h
  65. 4
      libethcore/BlockInfo.cpp
  66. 6
      libethcore/Common.h
  67. 6
      libethcore/Ethash.cpp
  68. 3
      libethcore/Ethash.h
  69. 3
      libethcore/Farm.h
  70. 16
      libethcore/ICAP.cpp
  71. 165
      libethcore/KeyManager.cpp
  72. 68
      libethcore/KeyManager.h
  73. 6
      libethcore/Params.cpp
  74. 16
      libethereum/BlockChain.cpp
  75. 9
      libethereum/BlockChain.h
  76. 800
      libethereum/BlockChainSync.cpp
  77. 278
      libethereum/BlockChainSync.h
  78. 7
      libethereum/BlockDetails.h
  79. 53
      libethereum/BlockQueue.cpp
  80. 5
      libethereum/BlockQueue.h
  81. 3
      libethereum/CMakeLists.txt
  82. 6
      libethereum/CanonBlockChain.h
  83. 71
      libethereum/Client.cpp
  84. 5
      libethereum/Client.h
  85. 6
      libethereum/CommonNet.h
  86. 593
      libethereum/EthereumHost.cpp
  87. 75
      libethereum/EthereumHost.h
  88. 56
      libethereum/EthereumPeer.cpp
  89. 16
      libethereum/EthereumPeer.h
  90. 2
      libethereum/Executive.h
  91. 24
      libethereum/State.cpp
  92. 14
      libethereum/State.h
  93. 3
      libethereum/TransactionQueue.cpp
  94. 3
      libethereum/TransactionQueue.h
  95. 28
      libevm/ExtVMFace.h
  96. 58
      libevm/VM.cpp
  97. 10
      libevmasm/Assembly.cpp
  98. 7
      libevmasm/AssemblyItem.cpp
  99. 9
      libevmasm/AssemblyItem.h
  100. 7
      libjsconsole/JSConsole.cpp

33
CMakeLists.txt

@ -30,6 +30,7 @@ option(JSONRPC "Build with jsonprc. default on" ON)
option(FATDB "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents." OFF) option(FATDB "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents." OFF)
option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF) option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF)
option(PROFILING "Build in support for profiling" OFF) option(PROFILING "Build in support for profiling" OFF)
option(ROCKSDB "Use rocksdb rather than leveldb" OFF)
set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).") set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).")
option(MINER "Build the CLI miner component" ON) option(MINER "Build the CLI miner component" ON)
@ -37,9 +38,10 @@ option(ETHKEY "Build the CLI key manager component" ON)
option(SOLIDITY "Build the Solidity language components" ON) option(SOLIDITY "Build the Solidity language components" ON)
option(SERPENT "Build the Serpent language components" ON) option(SERPENT "Build the Serpent language components" ON)
option(TOOLS "Build the tools components" ON) option(TOOLS "Build the tools components" ON)
option(NCURSES "Build the NCurses components" ON) option(NCURSES "Build the NCurses components" OFF)
option(GUI "Build GUI components (AlethZero, Mix)" ON) option(GUI "Build GUI components (AlethZero, Mix)" ON)
option(TESTS "Build the tests." ON) option(TESTS "Build the tests." ON)
option(NOBOOST "No use of boost macros in test functions" OFF)
option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF)
option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF) option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF)
option(JSCONSOLE "Build in javascript console" OFF) option(JSCONSOLE "Build in javascript console" OFF)
@ -82,6 +84,10 @@ function(configureProject)
add_definitions(-DETH_CURL) add_definitions(-DETH_CURL)
endif() endif()
if (NOBOOST)
add_definitions(-DNOBOOST)
endif()
add_definitions(-DETH_TRUE) add_definitions(-DETH_TRUE)
endfunction() endfunction()
@ -193,8 +199,10 @@ eth_format_option(MINER)
eth_format_option(USENPM) eth_format_option(USENPM)
eth_format_option(PROFILING) eth_format_option(PROFILING)
eth_format_option(SOLIDITY) eth_format_option(SOLIDITY)
eth_format_option(ROCKSDB)
eth_format_option(GUI) eth_format_option(GUI)
eth_format_option(TESTS) eth_format_option(TESTS)
eth_format_option(NOBOOST)
eth_format_option(TOOLS) eth_format_option(TOOLS)
eth_format_option(ETHASHCL) eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE) eth_format_option(JSCONSOLE)
@ -222,7 +230,7 @@ elseif (BUNDLE STREQUAL "full")
set(SOLIDITY ON) set(SOLIDITY ON)
set(USENPM ON) set(USENPM ON)
set(GUI ON) set(GUI ON)
set(NCURSES ${DECENT_PLATFORM}) # set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON) set(TOOLS ON)
set(TESTS ON) set(TESTS ON)
set(FATDB ON) set(FATDB ON)
@ -249,7 +257,7 @@ elseif (BUNDLE STREQUAL "user")
set(SOLIDITY OFF) set(SOLIDITY OFF)
set(USENPM OFF) set(USENPM OFF)
set(GUI ON) set(GUI ON)
set(NCURSES ${DECENT_PLATFORM}) # set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON) set(TOOLS ON)
set(TESTS OFF) set(TESTS OFF)
elseif (BUNDLE STREQUAL "wallet") elseif (BUNDLE STREQUAL "wallet")
@ -307,6 +315,7 @@ message("-- PROFILING Profiling support ${PROFILIN
message("-- FATDB Full database exploring ${FATDB}") message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}") message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}") message("-- USENPM Javascript source building ${USENPM}")
message("-- ROCKSDB Prefer rocksdb to leveldb ${ROCKSDB}")
message("------------------------------------------------------------- components") message("------------------------------------------------------------- components")
message("-- MINER Build miner ${MINER}") message("-- MINER Build miner ${MINER}")
message("-- ETHKEY Build wallet tools ${ETHKEY}") message("-- ETHKEY Build wallet tools ${ETHKEY}")
@ -316,6 +325,7 @@ message("-- SERPENT Build Serpent language components ${SERPENT}
message("-- GUI Build GUI components ${GUI}") message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}") message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}") message("-- TESTS Build tests ${TESTS}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}") message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}") message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}") message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
@ -332,6 +342,15 @@ include(EthExecutableHelper)
createBuildInfo() createBuildInfo()
if (ROCKSDB AND ROCKSDB_FOUND)
set(DB_INCLUDE_DIRS ${ROCKSDB_INCLUDE_DIRS})
set(DB_LIBRARIES ${ROCKSDB_LIBRARIES})
add_definitions(-DETH_ROCKSDB)
else()
set(DB_INCLUDE_DIRS ${LEVELDB_INCLUDE_DIRS})
set(DB_LIBRARIES ${LEVELDB_LIBRARIES})
endif()
if (EVMJIT) if (EVMJIT)
set(EVMJIT_CPP TRUE) # include CPP-JIT connector set(EVMJIT_CPP TRUE) # include CPP-JIT connector
add_subdirectory(evmjit) add_subdirectory(evmjit)
@ -430,9 +449,9 @@ if (TOOLS)
endif() endif()
if (NCURSES) #if (NCURSES)
add_subdirectory(neth) # add_subdirectory(neth)
endif () #endif ()
if (GUI) if (GUI)
@ -458,7 +477,7 @@ if (APPLE AND GUI)
-DAPP_DMG_EXE=${ETH_APP_DMG} -DAPP_DMG_EXE=${ETH_APP_DMG}
-DAPP_DMG_FILE=appdmg.json.in -DAPP_DMG_FILE=appdmg.json.in
-DAPP_DMG_ICON="alethzero/alethzero.icns" -DAPP_DMG_ICON="alethzero/alethzero.icns"
-DAPP_DMG_BACKGROUND="bg.png" -DAPP_DMG_BACKGROUND="install-folder-bg.png"
-DETH_BUILD_DIR="${CMAKE_BINARY_DIR}" -DETH_BUILD_DIR="${CMAKE_BINARY_DIR}"
-DETH_MIX_APP="$<TARGET_FILE_DIR:mix>" -DETH_MIX_APP="$<TARGET_FILE_DIR:mix>"
-DETH_ALETHZERO_APP="$<TARGET_FILE_DIR:AlethZero>" -DETH_ALETHZERO_APP="$<TARGET_FILE_DIR:AlethZero>"

2
abi/CMakeLists.txt

@ -4,7 +4,7 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE abi) set(EXECUTABLE abi)

2
alethzero/DappLoader.cpp

@ -193,6 +193,8 @@ QByteArray const& DappLoader::web3Content()
code += "\n"; code += "\n";
code += contentsOfQResource(":/js/setup.js"); code += contentsOfQResource(":/js/setup.js");
code += "\n"; code += "\n";
code += contentsOfQResource(":/js/admin.js");
code += "\n";
m_web3Js = code.toLatin1(); m_web3Js = code.toLatin1();
} }
return m_web3Js; return m_web3Js;

60
alethzero/MainWin.cpp

@ -212,7 +212,7 @@ Main::Main(QWidget *parent) :
m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads));
auto w3ss = new OurWebThreeStubServer(*m_httpConnector, this); auto w3ss = new OurWebThreeStubServer(*m_httpConnector, this);
m_server.reset(w3ss); m_server.reset(w3ss);
auto sessionKey = w3ss->newSession({true}); auto sessionKey = w3ss->newSession(SessionPermissions{{Priviledge::Admin}});
connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned())); m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening(); m_server->StartListening();
@ -235,7 +235,7 @@ Main::Main(QWidget *parent) :
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector(); // QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page); // inspector->setPage(page);
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(m_keyManager.accounts().front());
ethereum()->setDefault(LatestBlock); ethereum()->setDefault(LatestBlock);
@ -430,9 +430,9 @@ void Main::installBalancesWatch()
// TODO: Update for new currencies reg. // TODO: Update for new currencies reg.
for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i) for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i)
altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1)));
for (auto const& i: m_keyManager.accounts()) for (auto const& address: m_keyManager.accounts())
for (auto c: altCoins) for (auto c: altCoins)
tf.address(c).topic(0, h256(i, h256::AlignRight)); tf.address(c).topic(0, h256(address, h256::AlignRight));
uninstallWatch(m_balancesFilter); uninstallWatch(m_balancesFilter);
m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); }); m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); });
@ -501,7 +501,7 @@ void Main::load(QString _s)
void Main::on_newTransaction_triggered() void Main::on_newTransaction_triggered()
{ {
m_transact->setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); m_transact->setEnvironment(m_keyManager.accountsHash(), ethereum(), &m_natSpecDB);
m_transact->show(); m_transact->show();
} }
@ -735,18 +735,17 @@ void Main::writeSettings()
s.setValue("windowState", saveState()); s.setValue("windowState", saveState());
} }
Secret Main::retrieveSecret(Address const& _a) const Secret Main::retrieveSecret(Address const& _address) const
{ {
auto info = m_keyManager.accountDetails()[_a];
while (true) while (true)
{ {
Secret s = m_keyManager.secret(_a, [&](){ Secret s = m_keyManager.secret(_address, [&](){
QDialog d; QDialog d;
Ui_GetPassword gp; Ui_GetPassword gp;
gp.setupUi(&d); gp.setupUi(&d);
d.setWindowTitle("Unlock Account"); 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.label->setText(QString("Enter the password for the account %2 (%1).").arg(QString::fromStdString(_address.abridged())).arg(QString::fromStdString(m_keyManager.accountName(_address))));
gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(info.second)); gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(m_keyManager.passwordHint(_address)));
return d.exec() == QDialog::Accepted ? gp.entry->text().toStdString() : string(); 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) if (s || QMessageBox::warning(nullptr, "Unlock Account", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel)
@ -770,7 +769,7 @@ void Main::readSettings(bool _skipGeometry)
for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i)
{ {
memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret));
if (!m_keyManager.accounts().count(KeyPair(k).address())) if (!m_keyManager.hasAccount(KeyPair(k).address()))
m_keyManager.import(k, "Imported (UNSAFE) key."); m_keyManager.import(k, "Imported (UNSAFE) key.");
} }
} }
@ -858,7 +857,7 @@ void Main::on_importKey_triggered()
if (b.size() == 32) if (b.size() == 32)
{ {
auto k = KeyPair(h256(b)); auto k = KeyPair(h256(b));
if (!m_keyManager.accounts().count(k.address())) if (!m_keyManager.hasAccount(k.address()))
{ {
QString s = QInputDialog::getText(this, "Import Account Key", "Enter this account's name"); QString s = QInputDialog::getText(this, "Import Account Key", "Enter this account's name");
if (QMessageBox::question(this, "Additional Security?", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) if (QMessageBox::question(this, "Additional Security?", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
@ -939,7 +938,7 @@ void Main::on_claimPresale_triggered()
} }
cnote << k.address(); cnote << k.address();
if (!m_keyManager.accounts().count(k.address())) if (!m_keyManager.hasAccount(k.address()))
ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice()); ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice());
else else
QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account.");
@ -1110,13 +1109,13 @@ void Main::refreshBalances()
// cdebug << n << addr << denom << sha3(h256(n).asBytes()); // cdebug << n << addr << denom << sha3(h256(n).asBytes());
altCoins[addr] = make_tuple(fromRaw(n), 0, denom); altCoins[addr] = make_tuple(fromRaw(n), 0, denom);
}*/ }*/
for (pair<Address, std::pair<std::string, std::string>> const& i: m_keyManager.accountDetails()) for (auto const& address: m_keyManager.accounts())
{ {
u256 b = ethereum()->balanceAt(i.first); u256 b = ethereum()->balanceAt(address);
QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.first))).arg((unsigned)ethereum()->countAt(i.first)).arg(QString::fromStdString(i.second.first)), ui->ourAccounts); QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(address))).arg((unsigned)ethereum()->countAt(address)).arg(QString::fromStdString(m_keyManager.accountName(address))), ui->ourAccounts);
li->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); li->setData(Qt::UserRole, QByteArray((char const*)address.data(), Address::size));
li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
li->setCheckState(m_beneficiary == i.first ? Qt::Checked : Qt::Unchecked); li->setCheckState(m_beneficiary == address ? Qt::Checked : Qt::Unchecked);
totalBalance += b; totalBalance += b;
// for (auto& c: altCoins) // for (auto& c: altCoins)
@ -1158,7 +1157,7 @@ void Main::refreshNetwork()
auto ns = web3()->nodes(); auto ns = web3()->nodes();
for (p2p::Peer const& i: ns) for (p2p::Peer const& i: ns)
ui->nodes->insertItem(sessions.count(i.id) ? 0 : ui->nodes->count(), QString("[%1 %3] %2 - ( =%5s | /%4s%6 ) - *%7 $%8") ui->nodes->insertItem(sessions.count(i.id) ? 0 : ui->nodes->count(), QString("[%1 %3] %2 - ( %4 ) - *%5")
.arg(QString::fromStdString(i.id.abridged())) .arg(QString::fromStdString(i.id.abridged()))
.arg(QString::fromStdString(i.endpoint.address.to_string())) .arg(QString::fromStdString(i.endpoint.address.to_string()))
.arg(i.id == web3()->id() ? "self" : sessions.count(i.id) ? sessions[i.id] : "disconnected") .arg(i.id == web3()->id() ? "self" : sessions.count(i.id) ? sessions[i.id] : "disconnected")
@ -1254,7 +1253,7 @@ void Main::refreshBlockCount()
BlockQueueStatus b = ethereum()->blockQueueStatus(); BlockQueueStatus b = ethereum()->blockQueueStatus();
SyncStatus sync = ethereum()->syncStatus(); SyncStatus sync = ethereum()->syncStatus();
QString syncStatus = EthereumHost::stateName(sync.state); QString syncStatus = EthereumHost::stateName(sync.state);
if (sync.state == SyncState::HashesParallel || sync.state == SyncState::HashesSingle) if (sync.state == SyncState::Hashes)
syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal); syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal);
if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks) if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks)
syncStatus += QString(": %1/%2").arg(sync.blocksReceived).arg(sync.blocksTotal); syncStatus += QString(": %1/%2").arg(sync.blocksReceived).arg(sync.blocksTotal);
@ -1922,7 +1921,7 @@ void Main::on_clearPending_triggered()
void Main::on_retryUnknown_triggered() void Main::on_retryUnknown_triggered()
{ {
ethereum()->retryUnkonwn(); ethereum()->retryUnknown();
} }
void Main::on_killBlockchain_triggered() void Main::on_killBlockchain_triggered()
@ -1941,11 +1940,7 @@ void Main::on_net_triggered()
{ {
ui->port->setEnabled(!ui->net->isChecked()); ui->port->setEnabled(!ui->net->isChecked());
ui->clientName->setEnabled(!ui->net->isChecked()); ui->clientName->setEnabled(!ui->net->isChecked());
string n = string("AlethZero/v") + dev::Version; web3()->setClientVersion(WebThreeDirect::composeClientVersion("AlethZero", ui->clientName->text().toStdString()));
if (ui->clientName->text().size())
n += "/" + ui->clientName->text().toStdString();
n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM);
web3()->setClientVersion(n);
if (ui->net->isChecked()) if (ui->net->isChecked())
{ {
web3()->setIdealPeerCount(ui->idealPeers->value()); web3()->setIdealPeerCount(ui->idealPeers->value());
@ -2098,9 +2093,8 @@ void Main::on_killAccount_triggered()
{ {
auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray();
Address h((byte const*)hba.data(), Address::ConstructFromPointer); Address h((byte const*)hba.data(), Address::ConstructFromPointer);
auto k = m_keyManager.accountDetails()[h]; QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + m_keyManager.accountName(h) + "?!"),
QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + k.first + "?!"), QString::fromStdString("Account " + m_keyManager.accountName(h) + " (" + 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"
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."), "Are you sure you want to continue? \r\n If so, type 'YES' to confirm."),
QLineEdit::Normal, "NO"); QLineEdit::Normal, "NO");
if (s != "YES") if (s != "YES")
@ -2108,10 +2102,10 @@ void Main::on_killAccount_triggered()
m_keyManager.kill(h); m_keyManager.kill(h);
if (m_keyManager.accounts().empty()) if (m_keyManager.accounts().empty())
m_keyManager.import(Secret::random(), "Default account"); m_keyManager.import(Secret::random(), "Default account");
m_beneficiary = *m_keyManager.accounts().begin(); m_beneficiary = m_keyManager.accounts().front();
keysChanged(); keysChanged();
if (m_beneficiary == h) if (m_beneficiary == h)
setBeneficiary(*m_keyManager.accounts().begin()); setBeneficiary(m_keyManager.accounts().front());
} }
} }
@ -2132,7 +2126,7 @@ void Main::on_reencryptKey_triggered()
return; return;
try { try {
auto pw = [&](){ 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(); auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.passwordHint(a)), QLineEdit::Password, QString()).toStdString();
if (p.empty()) if (p.empty())
throw PasswordUnknown(); throw PasswordUnknown();
return p; return p;
@ -2155,7 +2149,7 @@ void Main::on_reencryptAll_triggered()
try { try {
for (Address const& a: m_keyManager.accounts()) for (Address const& a: m_keyManager.accounts())
while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){ 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(); 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.passwordHint(a))), QLineEdit::Password, QString()).toStdString();
if (p.empty()) if (p.empty())
throw PasswordUnknown(); throw PasswordUnknown();
return p; return p;

2
alethzero/MainWin.h

@ -96,7 +96,7 @@ public:
dev::eth::KeyManager& keyManager() override { return m_keyManager; } dev::eth::KeyManager& keyManager() override { return m_keyManager; }
bool doConfirm(); bool doConfirm();
dev::Secret retrieveSecret(dev::Address const& _a) const override; dev::Secret retrieveSecret(dev::Address const& _address) const override;
public slots: public slots:
void load(QString _file); void load(QString _file);

7
alethzero/NatspecHandler.h

@ -22,16 +22,11 @@
#pragma once #pragma once
#pragma warning(push) #include <libdevcore/db.h>
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <json/json.h> #include <json/json.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
#include "Context.h" #include "Context.h"
namespace ldb = leveldb;
class NatspecHandler: public NatSpecFace class NatspecHandler: public NatSpecFace
{ {
public: public:

4
alethzero/OurWebThreeStubServer.cpp

@ -33,7 +33,7 @@ OurWebThreeStubServer::OurWebThreeStubServer(
jsonrpc::AbstractServerConnector& _conn, jsonrpc::AbstractServerConnector& _conn,
Main* _main Main* _main
): ):
WebThreeStubServer(_conn, *_main->web3(), make_shared<OurAccountHolder>(_main), _main->owned().toVector().toStdVector(), _main->keyManager()), WebThreeStubServer(_conn, *_main->web3(), make_shared<OurAccountHolder>(_main), _main->owned().toVector().toStdVector(), _main->keyManager(), *static_cast<TrivialGasPricer*>(_main->ethereum()->gasPricer().get())),
m_main(_main) m_main(_main)
{ {
} }
@ -136,7 +136,7 @@ void OurAccountHolder::doValidations()
AddressHash OurAccountHolder::realAccounts() const AddressHash OurAccountHolder::realAccounts() const
{ {
return m_main->keyManager().accounts(); return m_main->keyManager().accountsHash();
} }
bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy) bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)

2
alethzero/OurWebThreeStubServer.h

@ -59,7 +59,7 @@ private:
Main* m_main; Main* m_main;
}; };
class OurWebThreeStubServer: public QObject, public WebThreeStubServer class OurWebThreeStubServer: public QObject, public dev::WebThreeStubServer
{ {
Q_OBJECT Q_OBJECT

7
alethzero/Transact.cpp

@ -77,11 +77,10 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e
auto old = ui->from->currentIndex(); auto old = ui->from->currentIndex();
ui->from->clear(); ui->from->clear();
for (auto const& i: m_accounts) for (auto const& address: m_accounts)
{ {
auto d = m_context->keyManager().accountDetails()[i]; u256 b = ethereum()->balanceAt(address, PendingBlock);
u256 b = ethereum()->balanceAt(i, PendingBlock); QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(address))).arg(QString::fromStdString(m_context->keyManager().accountName(address)));
QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(i))).arg(QString::fromStdString(d.first));
ui->from->addItem(s); ui->from->addItem(s);
} }
if (old > -1 && old < ui->from->count()) if (old > -1 && old < ui->from->count())

8
appdmg.json.in

@ -2,11 +2,11 @@
"title": "Ethereum", "title": "Ethereum",
"icon": "appdmg_icon.icns", "icon": "appdmg_icon.icns",
"background": "appdmg_background.png", "background": "appdmg_background.png",
"icon-size": 80, "icon-size": 55,
"contents": [ "contents": [
{ "x": 600, "y": 170, "type": "link", "path": "/Applications" }, { "x": 242, "y": 240, "type": "link", "path": "/Applications" },
{ "x": 150, "y": 90, "type": "file", "path": "${ETH_ALETHZERO_APP}" }, { "x": 145, "y": 125, "type": "file", "path": "${ETH_ALETHZERO_APP}" },
{ "x": 150, "y": 260, "type": "file", "path": "${ETH_MIX_APP}" } { "x": 339, "y": 125, "type": "file", "path": "${ETH_MIX_APP}" }
] ]
} }

BIN
bg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

18
cmake/EthCompilerSettings.cmake

@ -19,6 +19,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -DSHAREDLIB -fPIC") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -DSHAREDLIB -fPIC")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG")
set(CMAKE_CXX_FLAGS_DEBUGSAN "-O1 -g -fsanitize=address,integer,undefined -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/sanitizer-blacklist.txt -fno-omit-frame-pointer -DETH_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG -DETH_RELEASE") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG -DETH_RELEASE")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DETH_RELEASE") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -DETH_RELEASE")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DETH_RELEASE") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DETH_RELEASE")
@ -34,17 +35,21 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# disable unknown pragma warning (4068) # disable unknown pragma warning (4068)
# disable unsafe function warning (4996) # disable unsafe function warning (4996)
# disable decorated name length exceeded, name was truncated (4503) # disable decorated name length exceeded, name was truncated (4503)
# disable conversion from 'size_t' to 'type', possible loss of data (4267)
# disable qualifier applied to function type has no meaning; ignored (4180)
# disable C++ exception specification ignored except to indicate a function is not __declspec(nothrow) (4290)
# disable conversion from 'type1' to 'type2', possible loss of data (4244)
# disable forcing value to bool 'true' or 'false' (performance warning) (4800)
# disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests) # disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests)
# declare Windows XP requirement # declare Windows XP requirement
# undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions
# define miniupnp static library # define miniupnp static library
add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501 /DNOMINMAX /DMINIUPNP_STATICLIB) add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 /wd4267 /wd4180 /wd4290 /wd4244 /wd4800 -D_WIN32_WINNT=0x0501 /DNOMINMAX /DMINIUPNP_STATICLIB)
# disable empty object file warning # disable empty object file warning
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
# warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification
# warning LNK4099: pdb was not found with lib # warning LNK4099: pdb was not found with lib
# stack size 16MB set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:33554432")
# windows likes static # windows likes static
if (NOT ETH_STATIC) if (NOT ETH_STATIC)
@ -64,6 +69,13 @@ if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_C
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lprofiler") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lprofiler")
endif () endif ()
if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")))
set(CMAKE_CXX_FLAGS "-g --coverage ${CMAKE_CXX_FLAGS}")
set(CMAKE_C_FLAGS "-g --coverage ${CMAKE_C_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS} -lprofiler")
set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS} -lprofiler")
endif ()
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
option(USE_LD_GOLD "Use GNU gold linker" ON) option(USE_LD_GOLD "Use GNU gold linker" ON)
if (USE_LD_GOLD) if (USE_LD_GOLD)

6
cmake/EthDependencies.cmake

@ -49,6 +49,12 @@ find_package (LevelDB REQUIRED)
message(" - LevelDB header: ${LEVELDB_INCLUDE_DIRS}") message(" - LevelDB header: ${LEVELDB_INCLUDE_DIRS}")
message(" - LevelDB lib: ${LEVELDB_LIBRARIES}") message(" - LevelDB lib: ${LEVELDB_LIBRARIES}")
find_package (RocksDB)
if (ROCKSDB_FOUND)
message(" - RocksDB header: ${ROCKSDB_INCLUDE_DIRS}")
message(" - RocksDB lib: ${ROCKSDB_LIBRARIES}")
endif()
if (JSCONSOLE) if (JSCONSOLE)
find_package (v8 REQUIRED) find_package (v8 REQUIRED)
message(" - v8 header: ${V8_INCLUDE_DIRS}") message(" - v8 header: ${V8_INCLUDE_DIRS}")

49
cmake/FindRocksDB.cmake

@ -0,0 +1,49 @@
# Find rocksdb
#
# Find the rocksdb includes and library
#
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
#
# This module defines
# ROCKSDB_INCLUDE_DIRS, where to find header, etc.
# ROCKSDB_LIBRARIES, the libraries needed to use rocksdb.
# ROCKSDB_FOUND, If false, do not try to use rocksdb.
# only look in default directories
find_path(
ROCKSDB_INCLUDE_DIR
NAMES rocksdb/db.h
DOC "rocksdb include dir"
)
find_library(
ROCKSDB_LIBRARY
NAMES rocksdb
DOC "rocksdb library"
)
set(ROCKSDB_INCLUDE_DIRS ${ROCKSDB_INCLUDE_DIR})
set(ROCKSDB_LIBRARIES ${ROCKSDB_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(
ROCKSDB_LIBRARY_DEBUG
NAMES rocksdbd
DOC "rocksdb debug library"
)
set(ROCKSDB_LIBRARIES optimized ${ROCKSDB_LIBRARIES} debug ${ROCKSDB_LIBRARY_DEBUG})
endif()
# handle the QUIETLY and REQUIRED arguments and set ROCKSDB_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(rocksdb DEFAULT_MSG
ROCKSDB_INCLUDE_DIR ROCKSDB_LIBRARY)
mark_as_advanced (ROCKSDB_INCLUDE_DIR ROCKSDB_LIBRARY)

106
eth/main.cpp

@ -102,6 +102,9 @@ void interactiveHelp()
<< " listaccounts List the accounts on the network." << endl << " listaccounts List the accounts on the network." << endl
<< " listcontracts List the contracts on the network." << endl << " listcontracts List the contracts on the network." << endl
<< " balanceat <address> Gives the balance of the given account." << endl << " balanceat <address> Gives the balance of the given account." << endl
<< " balanceatblock <address> <blocknumber> Gives the balance of the given account." << endl
<< " storageat <address> Gives the storage of the given account." << endl
<< " storageatblock <address> <blocknumber> Gives the storahe of the given account at a given blocknumber." << endl
<< " codeat <address> Gives the code of the given account." << endl << " codeat <address> Gives the code of the given account." << endl
#endif #endif
<< " setsigningkey <addr> Set the address with which to sign transactions." << endl << " setsigningkey <addr> Set the address with which to sign transactions." << endl
@ -168,6 +171,9 @@ void help()
<< " --port <port> Connect to remote port (default: 30303)." << endl << " --port <port> Connect to remote port (default: 30303)." << endl
<< " --network-id <n> Only connect to other hosts with this network id (default:0)." << endl << " --network-id <n> Only connect to other hosts with this network id (default:0)." << endl
<< " --upnp <on/off> Use UPnP for NAT (default: on)." << endl << " --upnp <on/off> Use UPnP for NAT (default: on)." << endl
<< " --no-discovery Disable Node discovery. (experimental)" << endl
<< " --pin Only connect to required (trusted) peers. (experimental)" << endl
// << " --require-peers <peers.json> List of required (trusted) peers. (experimental)" << endl
<< endl; << endl;
MinerCLI::streamHelp(cout); MinerCLI::streamHelp(cout);
cout cout
@ -304,6 +310,8 @@ int main(int argc, char** argv)
unsigned short remotePort = 30303; unsigned short remotePort = 30303;
unsigned peers = 11; unsigned peers = 11;
bool bootstrap = false; bool bootstrap = false;
bool disableDiscovery = false;
bool pinning = false;
unsigned networkId = 0; unsigned networkId = 0;
/// Mining params /// Mining params
@ -592,6 +600,10 @@ int main(int argc, char** argv)
} }
else if (arg == "-b" || arg == "--bootstrap") else if (arg == "-b" || arg == "--bootstrap")
bootstrap = true; bootstrap = true;
else if (arg == "--no-discovery")
disableDiscovery = true;
else if (arg == "--pin")
pinning = true;
else if (arg == "-f" || arg == "--force-mining") else if (arg == "-f" || arg == "--force-mining")
forceMining = true; forceMining = true;
else if (arg == "-i" || arg == "--interactive") else if (arg == "-i" || arg == "--interactive")
@ -678,16 +690,17 @@ int main(int argc, char** argv)
return ret; return ret;
}; };
auto getAccountPassword = [&](Address const& a){ auto getAccountPassword = [&](Address const& a){
return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): ");
}; };
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
netPrefs.discovery = !disableDiscovery;
netPrefs.pin = pinning;
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : "");
dev::WebThreeDirect web3( dev::WebThreeDirect web3(
clientImplString, WebThreeDirect::composeClientVersion("++eth", clientName),
dbPath, dbPath,
killChain, killChain,
nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(), nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(),
@ -751,7 +764,8 @@ int main(int argc, char** argv)
case ImportResult::Success: good++; break; case ImportResult::Success: good++; break;
case ImportResult::AlreadyKnown: alreadyHave++; break; case ImportResult::AlreadyKnown: alreadyHave++; break;
case ImportResult::UnknownParent: unknownParent++; break; case ImportResult::UnknownParent: unknownParent++; break;
case ImportResult::FutureTime: futureTime++; break; case ImportResult::FutureTimeUnknown: unknownParent++; futureTime++; break;
case ImportResult::FutureTimeKnown: futureTime++; break;
default: bad++; break; default: bad++; break;
} }
} }
@ -800,7 +814,7 @@ int main(int argc, char** argv)
// 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); std::shared_ptr<eth::TrivialGasPricer> gasPricer = make_shared<eth::TrivialGasPricer>(askPrice, bidPrice);
eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr; eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr;
StructuredLogger::starting(clientImplString, dev::Version); StructuredLogger::starting(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version);
if (c) if (c)
{ {
c->setGasPricer(gasPricer); c->setGasPricer(gasPricer);
@ -822,17 +836,18 @@ int main(int argc, char** argv)
cout << "Networking disabled. To start, use netstart or pass -b or a remote host." << endl; cout << "Networking disabled. To start, use netstart or pass -b or a remote host." << endl;
#if ETH_JSONRPC || !ETH_TRUE #if ETH_JSONRPC || !ETH_TRUE
shared_ptr<WebThreeStubServer> jsonrpcServer; shared_ptr<dev::WebThreeStubServer> jsonrpcServer;
unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector; unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector;
if (jsonrpc > -1) if (jsonrpc > -1)
{ {
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>(), keyManager)); jsonrpcServer = shared_ptr<dev::WebThreeStubServer>(new dev::WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager, *gasPricer));
jsonrpcServer->setMiningBenefactorChanger([&](Address const& a) { beneficiary = a; });
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
if (jsonAdmin.empty()) if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true}); jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}});
else else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true}); jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
} }
#endif #endif
@ -982,12 +997,13 @@ int main(int argc, char** argv)
if (jsonrpc < 0) if (jsonrpc < 0)
jsonrpc = SensibleHttpPort; jsonrpc = SensibleHttpPort;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager)); jsonrpcServer = shared_ptr<dev::WebThreeStubServer>(new dev::WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager, *gasPricer));
jsonrpcServer->setMiningBenefactorChanger([&](Address const& a) { beneficiary = a; });
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
if (jsonAdmin.empty()) if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true}); jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}});
else else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true}); jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
} }
else if (cmd == "jsonstop") else if (cmd == "jsonstop")
@ -1083,7 +1099,7 @@ int main(int argc, char** argv)
} }
else if (c && cmd == "retryunknown") else if (c && cmd == "retryunknown")
{ {
c->retryUnkonwn(); c->retryUnknown();
} }
else if (cmd == "peers") else if (cmd == "peers")
{ {
@ -1123,10 +1139,10 @@ int main(int argc, char** argv)
{ {
cout << "Accounts:" << endl; cout << "Accounts:" << endl;
u256 total = 0; u256 total = 0;
for (auto const& i: keyManager.accountDetails()) for (auto const& address: keyManager.accounts())
{ {
auto b = c->balanceAt(i.first); auto b = c->balanceAt(address);
cout << ((i.first == signingKey) ? "SIGNING " : " ") << ((i.first == beneficiary) ? "COINBASE " : " ") << i.second.first << " (" << i.first << "): " << formatBalance(b) << " = " << b << " wei" << endl; cout << ((address == signingKey) ? "SIGNING " : " ") << ((address == beneficiary) ? "COINBASE " : " ") << keyManager.accountName(address) << " (" << address << "): " << formatBalance(b) << " = " << b << " wei" << endl;
total += b; total += b;
} }
cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl; cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl;
@ -1359,19 +1375,48 @@ int main(int argc, char** argv)
cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl; cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl;
} }
} }
// TODO implement << operator for std::unorderd_map else if (c && cmd == "balanceatblock")
// else if (c && cmd == "storageat") {
// { if (iss.peek() != -1)
// if (iss.peek() != -1) {
// { string stringHash;
// string stringHash; unsigned blocknumber;
// iss >> stringHash; iss >> stringHash >> blocknumber;
// Address address = h160(fromHex(stringHash)); Address address = h160(fromHex(stringHash));
cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address, blocknumber)) << endl;
}
}
else if (c && cmd == "storageat")
{
if (iss.peek() != -1)
{
string stringHash;
iss >> stringHash;
// cout << "storage at " << stringHash << " is: " << c->storageAt(address) << endl; Address address = h160(fromHex(stringHash));
// }
// } cout << "storage at " << stringHash << " is: " << endl;
for (auto s: c->storageAt(address))
cout << toHex(s.first) << " : " << toHex(s.second) << endl;
}
}
else if (c && cmd == "storageatblock")
{
if (iss.peek() != -1)
{
string stringHash;
unsigned blocknumber;
iss >> stringHash >> blocknumber;
Address address = h160(fromHex(stringHash));
cout << "storage at " << stringHash << " is: " << endl;
for (auto s: c->storageAt(address, blocknumber))
cout << "\"0x" << toHex(s.first) << "\" : \"0x" << toHex(s.second) << "\"," << endl;
}
}
else if (c && cmd == "codeat") else if (c && cmd == "codeat")
{ {
if (iss.peek() != -1) if (iss.peek() != -1)
@ -1385,6 +1430,7 @@ int main(int argc, char** argv)
} }
} }
#endif #endif
else if (c && cmd == "send") else if (c && cmd == "send")
{ {
if (iss.peek() != -1) if (iss.peek() != -1)
@ -1520,7 +1566,7 @@ int main(int argc, char** argv)
ofstream f; ofstream f;
f.open(filename); f.open(filename);
dev::eth::State state =c->state(index + 1,c->blockChain().numberHash(block)); dev::eth::State state = c->state(index + 1,c->blockChain().numberHash(block));
if (index < state.pending().size()) if (index < state.pending().size())
{ {
Executive e(state, c->blockChain(), 0); Executive e(state, c->blockChain(), 0);
@ -1699,7 +1745,7 @@ int main(int argc, char** argv)
JSConsole console(web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager)); JSConsole console(web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager));
while (!g_exit) while (!g_exit)
{ {
console.repl(); console.readExpression();
stopMiningAfterXBlocks(c, n, mining); stopMiningAfterXBlocks(c, n, mining);
} }
#endif #endif
@ -1712,7 +1758,7 @@ int main(int argc, char** argv)
while (!g_exit) while (!g_exit)
this_thread::sleep_for(chrono::milliseconds(1000)); this_thread::sleep_for(chrono::milliseconds(1000));
StructuredLogger::stopping(clientImplString, dev::Version); StructuredLogger::stopping(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version);
auto netData = web3.saveNetwork(); auto netData = web3.saveNetwork();
if (!netData.empty()) if (!netData.empty())
writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData); writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData);

20
ethkey/KeyAux.h

@ -44,7 +44,7 @@ class BadArgument: public Exception {};
string getAccountPassword(KeyManager& keyManager, Address const& a) string getAccountPassword(KeyManager& keyManager, Address const& a)
{ {
return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): ");
} }
string createPassword(std::string const& _prompt) string createPassword(std::string const& _prompt)
@ -221,26 +221,26 @@ public:
break; break;
} }
case OperationMode::ImportBare: case OperationMode::ImportBare:
for (string const& i: m_inputs) for (string const& input: m_inputs)
{ {
h128 u; h128 u;
bytes b; bytes b;
b = fromHex(i); b = fromHex(input);
if (b.size() != 32) if (b.size() != 32)
{ {
std::string s = contentsString(i); std::string s = contentsString(input);
b = fromHex(s); b = fromHex(s);
if (b.size() != 32) if (b.size() != 32)
u = store.importKey(i); u = store.importKey(input);
} }
if (!u && b.size() == 32) if (!u && b.size() == 32)
u = store.importSecret(b, lockPassword(toAddress(Secret(b)).abridged())); u = store.importSecret(b, lockPassword(toAddress(Secret(b)).abridged()));
if (!u) if (!u)
{ {
cerr << "Cannot import " << i << " not a file or secret." << endl; cerr << "Cannot import " << input << " not a file or secret." << endl;
continue; continue;
} }
cout << "Successfully imported " << i << " as " << toUUID(u); cout << "Successfully imported " << input << " as " << toUUID(u);
} }
break; break;
case OperationMode::InspectBare: case OperationMode::InspectBare:
@ -359,20 +359,18 @@ public:
nonIcap.push_back(u); nonIcap.push_back(u);
else else
{ {
std::pair<std::string, std::string> info = wallet.accountDetails()[a];
cout << toUUID(u) << " " << a.abridged(); cout << toUUID(u) << " " << a.abridged();
cout << " " << ICAP(a).encoded(); cout << " " << ICAP(a).encoded();
cout << " " << info.first << endl; cout << " " << wallet.accountName(a) << endl;
} }
else else
bare.push_back(u); bare.push_back(u);
for (auto const& u: nonIcap) for (auto const& u: nonIcap)
if (Address a = wallet.address(u)) if (Address a = wallet.address(u))
{ {
std::pair<std::string, std::string> info = wallet.accountDetails()[a];
cout << toUUID(u) << " " << a.abridged(); cout << toUUID(u) << " " << a.abridged();
cout << " (Not ICAP) "; cout << " (Not ICAP) ";
cout << " " << info.first << endl; cout << " " << wallet.accountName(a) << endl;
} }
for (auto const& u: bare) for (auto const& u: bare)
cout << toUUID(u) << " (Bare)" << endl; cout << toUUID(u) << " (Bare)" << endl;

32
ethminer/MinerAux.h

@ -107,7 +107,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
else if (arg == "--opencl-platform" && i + 1 < argc) else if (arg == "--opencl-platform" && i + 1 < argc)
try { try {
@ -116,7 +116,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
else if (arg == "--opencl-device" && i + 1 < argc) else if (arg == "--opencl-device" && i + 1 < argc)
try { try {
@ -126,7 +126,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
else if (arg == "--list-devices") else if (arg == "--list-devices")
m_shouldListDevices = true; m_shouldListDevices = true;
@ -134,8 +134,6 @@ public:
m_clAllowCPU = true; m_clAllowCPU = true;
else if (arg == "--cl-extragpu-mem" && i + 1 < argc) else if (arg == "--cl-extragpu-mem" && i + 1 < argc)
m_extraGPUMemory = 1000000 * stol(argv[++i]); m_extraGPUMemory = 1000000 * stol(argv[++i]);
else if (arg == "--force-single-chunk")
m_forceSingleChunk = true;
else if (arg == "--phone-home" && i + 1 < argc) else if (arg == "--phone-home" && i + 1 < argc)
{ {
string m = argv[++i]; string m = argv[++i];
@ -146,7 +144,7 @@ public:
else else
{ {
cerr << "Bad " << arg << " option: " << m << endl; cerr << "Bad " << arg << " option: " << m << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
} }
else if (arg == "--benchmark-warmup" && i + 1 < argc) else if (arg == "--benchmark-warmup" && i + 1 < argc)
@ -156,7 +154,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
else if (arg == "--benchmark-trial" && i + 1 < argc) else if (arg == "--benchmark-trial" && i + 1 < argc)
try { try {
@ -165,7 +163,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
else if (arg == "--benchmark-trials" && i + 1 < argc) else if (arg == "--benchmark-trials" && i + 1 < argc)
try { try {
@ -174,7 +172,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
else if (arg == "-C" || arg == "--cpu") else if (arg == "-C" || arg == "--cpu")
m_minerType = MinerType::CPU; m_minerType = MinerType::CPU;
@ -197,7 +195,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << m << endl; cerr << "Bad " << arg << " option: " << m << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
} }
else if ((arg == "-w" || arg == "--check-pow") && i + 4 < argc) else if ((arg == "-w" || arg == "--check-pow") && i + 4 < argc)
@ -234,7 +232,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << m << endl; cerr << "Bad " << arg << " option: " << m << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
} }
else if (arg == "-M" || arg == "--benchmark") else if (arg == "-M" || arg == "--benchmark")
@ -247,7 +245,7 @@ public:
catch (...) catch (...)
{ {
cerr << "Bad " << arg << " option: " << argv[i] << endl; cerr << "Bad " << arg << " option: " << argv[i] << endl;
throw BadArgument(); BOOST_THROW_EXCEPTION(BadArgument());
} }
} }
else else
@ -273,7 +271,6 @@ public:
m_openclDevice, m_openclDevice,
m_clAllowCPU, m_clAllowCPU,
m_extraGPUMemory, m_extraGPUMemory,
m_forceSingleChunk,
m_currentBlock m_currentBlock
)) ))
{ {
@ -318,10 +315,9 @@ public:
<< " --opencl-device <n> When mining using -G/--opencl use OpenCL device n (default: 0)." << endl << " --opencl-device <n> When mining using -G/--opencl use OpenCL device n (default: 0)." << endl
<< " -t, --mining-threads <n> Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl << " -t, --mining-threads <n> Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl
<< " --allow-opencl-cpu Allows CPU to be considered as an OpenCL device if the OpenCL platform supports it." << endl << " --allow-opencl-cpu Allows CPU to be considered as an OpenCL device if the OpenCL platform supports it." << endl
<< " --list-devices List the detected OpenCL devices and exit." <<endl << " --list-devices List the detected OpenCL devices and exit." << endl
<< " --current-block Let the miner know the current block number at configuration time. Will help determine DAG size and required GPU memory." <<endl << " --current-block Let the miner know the current block number at configuration time. Will help determine DAG size and required GPU memory." << endl
<< " --cl-extragpu-mem Set the memory (in MB) you believe your GPU requires for stuff other than mining. Windows rendering e.t.c.." <<endl << " --cl-extragpu-mem Set the memory (in MB) you believe your GPU requires for stuff other than mining. Windows rendering e.t.c.." << endl
<< " --force-single-chunk Force DAG uploading in a single chunk against OpenCL's judgement. Use at your own risk." <<endl
; ;
} }
@ -410,7 +406,6 @@ private:
} }
catch (...) catch (...)
{ {
cout << "Error phoning home. ET is sad." << endl;
} }
} }
#endif #endif
@ -511,7 +506,6 @@ private:
unsigned m_miningThreads = UINT_MAX; unsigned m_miningThreads = UINT_MAX;
bool m_shouldListDevices = false; bool m_shouldListDevices = false;
bool m_clAllowCPU = false; bool m_clAllowCPU = false;
bool m_forceSingleChunk = false;
boost::optional<uint64_t> m_currentBlock; boost::optional<uint64_t> m_currentBlock;
// default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.) // default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.)
unsigned m_extraGPUMemory = 350000000; unsigned m_extraGPUMemory = 350000000;

2
ethvm/CMakeLists.txt

@ -4,7 +4,7 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE ethvm) set(EXECUTABLE ethvm)

23
evmjit/CMakeLists.txt

@ -10,28 +10,17 @@ else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}")
endif() endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "DebugSan")
# Do not allow unresovled symbols in shared library (default on linux) # Do not allow unresovled symbols in shared library (default on linux)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
endif() endif()
# LLVM # LLVM
if(LLVM_DIR OR APPLE) # local LLVM build find_package(LLVM 3.7 REQUIRED CONFIG)
find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") add_definitions(${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS}) llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo)
# TODO: bitwriter is needed only for evmcc
llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter ipo)
else()
# Workaround for Ubuntu broken LLVM package
message(STATUS "Using llvm-3.5-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake")
execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS)
message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}")
set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm")
add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS)
link_directories(/usr/lib/llvm-3.5/lib)
endif()
get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE) get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE)

15
evmjit/libevmjit/Arith256.cpp

@ -167,21 +167,10 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
m_builder.SetInsertPoint(mainBB); m_builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type); auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero // both y and r are non-zero
auto yLz = m_builder.CreateCall2(ctlzIntr, yArg, m_builder.getInt1(true), "y.lz"); auto yLz = m_builder.CreateCall(ctlzIntr, {yArg, m_builder.getInt1(true)}, "y.lz");
auto rLz = m_builder.CreateCall2(ctlzIntr, r0, m_builder.getInt1(true), "r.lz"); auto rLz = m_builder.CreateCall(ctlzIntr, {r0, m_builder.getInt1(true)}, "r.lz");
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0"); auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto shlBy0 = m_builder.CreateICmpEQ(i0, zero);
auto y0 = m_builder.CreateShl(yArg, i0); auto y0 = m_builder.CreateShl(yArg, i0);
if (_type == m_builder.getIntNTy(512)) // Workaround for shl bug for long shifts
{
const auto treshold = m_builder.getIntN(512, 128);
auto highShift = m_builder.CreateICmpUGT(i0, treshold);
auto s = m_builder.CreateNUWSub(i0, treshold);
auto yhs = m_builder.CreateShl(yArg, treshold);
yhs = m_builder.CreateShl(yhs, s);
y0 = m_builder.CreateSelect(highShift, yhs, y0);
}
y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result
m_builder.CreateBr(loopBB); m_builder.CreateBr(loopBB);
m_builder.SetInsertPoint(loopBB); m_builder.SetInsertPoint(loopBB);

20
evmjit/libevmjit/Array.cpp

@ -45,9 +45,9 @@ llvm::Function* Array::createArrayPushFunc()
auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func);
m_builder.SetInsertPoint(entryBB); m_builder.SetInsertPoint(entryBB);
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr"); auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr");
auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr"); auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto size = m_builder.CreateLoad(sizePtr, "size"); auto size = m_builder.CreateLoad(sizePtr, "size");
auto cap = m_builder.CreateLoad(capPtr, "cap"); auto cap = m_builder.CreateLoad(capPtr, "cap");
@ -94,7 +94,7 @@ llvm::Function* Array::createArraySetFunc()
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr");
m_builder.CreateStore(value, valuePtr); m_builder.CreateStore(value, valuePtr);
@ -116,7 +116,7 @@ llvm::Function* Array::createArrayGetFunc()
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr");
auto value = m_builder.CreateLoad(valuePtr, "value"); auto value = m_builder.CreateLoad(valuePtr, "value");
@ -161,7 +161,7 @@ llvm::Function* Array::createFreeFunc()
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem"); auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem");
m_builder.CreateCall(freeFunc, mem); m_builder.CreateCall(freeFunc, mem);
@ -197,8 +197,8 @@ llvm::Function* Array::createExtendFunc()
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array
auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr"); auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr");
auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr"); auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto size = m_builder.CreateLoad(sizePtr, "size"); auto size = m_builder.CreateLoad(sizePtr, "size");
auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize"); auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize");
@ -244,7 +244,7 @@ Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) :
void Array::pop(llvm::Value* _count) void Array::pop(llvm::Value* _count)
{ {
auto sizePtr = m_builder.CreateStructGEP(m_array, 1, "sizePtr"); auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr");
auto size = m_builder.CreateLoad(sizePtr, "size"); auto size = m_builder.CreateLoad(sizePtr, "size");
auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); auto newSize = m_builder.CreateNUWSub(size, _count, "newSize");
m_builder.CreateStore(newSize, sizePtr); m_builder.CreateStore(newSize, sizePtr);
@ -252,7 +252,7 @@ void Array::pop(llvm::Value* _count)
llvm::Value* Array::size(llvm::Value* _array) llvm::Value* Array::size(llvm::Value* _array)
{ {
auto sizePtr = m_builder.CreateStructGEP(_array ? _array : m_array, 1, "sizePtr"); auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr");
return m_builder.CreateLoad(sizePtr, "array.size"); return m_builder.CreateLoad(sizePtr, "array.size");
} }

17
evmjit/libevmjit/Cache.cpp

@ -28,7 +28,7 @@ namespace
using Guard = std::lock_guard<std::mutex>; using Guard = std::lock_guard<std::mutex>;
std::mutex x_cacheMutex; std::mutex x_cacheMutex;
CacheMode g_mode; CacheMode g_mode;
llvm::MemoryBuffer* g_lastObject; std::unique_ptr<llvm::MemoryBuffer> g_lastObject;
ExecutionEngineListener* g_listener; ExecutionEngineListener* g_listener;
static const size_t c_versionStampLength = 32; static const size_t c_versionStampLength = 32;
@ -90,8 +90,7 @@ void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string,
if (auto module = getObject(name)) if (auto module = getObject(name))
{ {
DLOG(cache) << "Preload: " << name << "\n"; DLOG(cache) << "Preload: " << name << "\n";
_ee.addModule(module.get()); _ee.addModule(std::move(module));
module.release();
auto addr = _ee.getFunctionAddress(name); auto addr = _ee.getFunctionAddress(name);
assert(addr); assert(addr);
_funcCache[std::move(name)] = addr; _funcCache[std::move(name)] = addr;
@ -148,7 +147,7 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
} }
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object)
{ {
Guard g{x_cacheMutex}; Guard g{x_cacheMutex};
@ -171,19 +170,17 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
llvm::sys::path::append(cachePath, id); llvm::sys::path::append(cachePath, id);
DLOG(cache) << id << ": write\n"; DLOG(cache) << id << ": write\n";
std::string error; std::error_code error;
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None);
cacheFile << _object->getBuffer() << getLibVersionStamp(); cacheFile << _object.getBuffer() << getLibVersionStamp();
} }
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _module)
{ {
Guard g{x_cacheMutex}; Guard g{x_cacheMutex};
DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; DLOG(cache) << _module->getModuleIdentifier() << ": use\n";
auto o = g_lastObject; return std::move(g_lastObject);
g_lastObject = nullptr;
return o;
} }
} }

6
evmjit/libevmjit/Cache.h

@ -3,7 +3,9 @@
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ExecutionEngine/ObjectCache.h> #include <llvm/ExecutionEngine/ObjectCache.h>
#include "preprocessor/llvm_includes_end.h"
namespace llvm namespace llvm
{ {
@ -32,13 +34,13 @@ class ObjectCache : public llvm::ObjectCache
{ {
public: public:
/// notifyObjectCompiled - Provides a pointer to compiled code for Module M. /// notifyObjectCompiled - Provides a pointer to compiled code for Module M.
virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) final override; virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) final override;
/// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that
/// contains the object which corresponds with Module M, or 0 if an object is /// contains the object which corresponds with Module M, or 0 if an object is
/// not available. The caller owns both the MemoryBuffer returned by this /// not available. The caller owns both the MemoryBuffer returned by this
/// and the memory it references. /// and the memory it references.
virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override; virtual std::unique_ptr<llvm::MemoryBuffer> getObject(llvm::Module const* _module) final override;
}; };

10
evmjit/libevmjit/Compiler.cpp

@ -148,7 +148,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp");
m_builder.CreateStore(fp, jmpBufWords); m_builder.CreateStore(fp, jmpBufWords);
auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave);
auto sp = m_builder.CreateCall(stacksave, "sp"); auto sp = m_builder.CreateCall(stacksave, {}, "sp");
auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp");
m_builder.CreateStore(sp, jmpBufSp); m_builder.CreateStore(sp, jmpBufSp);
auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp);
@ -464,12 +464,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
// test for word >> (k * 8 + 7) // test for word >> (k * 8 + 7)
auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos");
auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word);
auto bittester = m_builder.CreateShl(Constant::get(1), bitposEx); auto bitval = m_builder.CreateLShr(word, bitposEx, "bitval");
auto bitresult = m_builder.CreateAnd(word, bittester); auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest");
auto bittest = m_builder.CreateICmpUGT(bitresult, Constant::get(0));
// FIXME: The following does not work - LLVM bug, report!
//auto bitval = m_builder.CreateLShr(word, bitpos, "bitval");
//auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest");
auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx);
auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask");

5
evmjit/libevmjit/Endianness.cpp

@ -18,9 +18,8 @@ llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _wo
{ {
if (llvm::sys::IsLittleEndianHost) if (llvm::sys::IsLittleEndianHost)
{ {
// FIXME: Disabled because of problems with BYTE if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word))
//if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word)) return _builder.getInt(constant->getValue().byteSwap());
// return _builder.getInt(constant->getValue().byteSwap());
// OPT: Cache func declaration? // OPT: Cache func declaration?
auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word); auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word);

28
evmjit/libevmjit/ExecutionEngine.cpp

@ -87,20 +87,7 @@ void parseOptions()
{ {
static llvm::llvm_shutdown_obj shutdownObj{}; static llvm::llvm_shutdown_obj shutdownObj{};
cl::AddExtraVersionPrinter(printVersion); cl::AddExtraVersionPrinter(printVersion);
//cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
// FIXME: LLVM workaround:
// Manually select instruction scheduler. Confirmed bad schedulers: source, list-burr, list-hybrid.
// "source" scheduler has a bug: http://llvm.org/bugs/show_bug.cgi?id=22304
auto envLine = std::getenv("EVMJIT");
auto commandLine = std::string{"evmjit "} + (envLine ? envLine : "") + " -pre-RA-sched=list-ilp\0";
static const auto c_maxArgs = 20;
char const* argv[c_maxArgs] = {nullptr, };
auto arg = std::strtok(&*commandLine.begin(), " ");
auto i = 0;
for (; i < c_maxArgs && arg; ++i, arg = std::strtok(nullptr, " "))
argv[i] = arg;
cl::ParseCommandLineOptions(i, argv, "Ethereum EVM JIT Compiler");
} }
} }
@ -131,20 +118,20 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext())); auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
llvm::EngineBuilder builder(module.get());
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setUseMCJIT(true);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
// FIXME: LLVM 3.7: test on Windows
auto triple = llvm::Triple(llvm::sys::getProcessTriple()); auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32) if (triple.getOS() == llvm::Triple::OSType::Win32)
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
module->setTargetTriple(triple.str()); module->setTargetTriple(triple.str());
llvm::EngineBuilder builder(std::move(module));
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
ee.reset(builder.create()); ee.reset(builder.create());
if (!CHECK(ee)) if (!CHECK(ee))
return ReturnCode::LLVMConfigError; return ReturnCode::LLVMConfigError;
module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
ee->setObjectCache(objectCache); ee->setObjectCache(objectCache);
// FIXME: Disabled during API changes // FIXME: Disabled during API changes
@ -177,8 +164,7 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
if (g_dump) if (g_dump)
module->dump(); module->dump();
ee->addModule(module.get()); ee->addModule(std::move(module));
module.release();
listener->stateChanged(ExecState::CodeGen); listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!CHECK(entryFuncPtr)) if (!CHECK(entryFuncPtr))

22
evmjit/libevmjit/GasMeter.cpp

@ -216,22 +216,12 @@ void GasMeter::countExp(llvm::Value* _exponent)
// cost = ((256 - lz) + 7) / 8 // cost = ((256 - lz) + 7) / 8
// OPT: Can gas update be done in exp algorithm? // OPT: Can gas update be done in exp algorithm?
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
auto t = llvm::APInt{256, 1}; auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)});
auto c = m_builder.CreateSelect(m_builder.CreateICmpUGE(_exponent, Constant::get(t)), m_builder.getInt64(1), m_builder.getInt64(0)); auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
for (auto i = 2; i <= 32; ++i) auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits");
{ auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8));
t <<= 8; count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(c_expByteGas)));
c = m_builder.CreateSelect(m_builder.CreateICmpUGE(_exponent, Constant::get(t)), m_builder.getInt64(i), c);
}
// FIXME: Does not work because of LLVM bug: https://llvm.org/bugs/show_bug.cgi?id=22304
// auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
// auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
// auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
// auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits");
// auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8));
count(m_builder.CreateNUWMul(c, m_builder.getInt64(c_expByteGas)));
} }
void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue)

8
evmjit/libevmjit/Optimizer.cpp

@ -1,7 +1,7 @@
#include "Optimizer.h" #include "Optimizer.h"
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/PassManager.h> #include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Scalar.h> #include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h> #include <llvm/Transforms/IPO.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
@ -15,10 +15,10 @@ namespace jit
bool optimize(llvm::Module& _module) bool optimize(llvm::Module& _module)
{ {
auto pm = llvm::PassManager{}; auto pm = llvm::legacy::PassManager{};
//pm.add(llvm::createFunctionInliningPass(2, 2)); // Produces invalid IR //pm.add(llvm::createFunctionInliningPass(2, 2)); // Problem with APInt value bigger than 64bit
pm.add(llvm::createCFGSimplificationPass()); pm.add(llvm::createCFGSimplificationPass());
//pm.add(llvm::createInstructionCombiningPass()); // Produces invalid runtime results pm.add(llvm::createInstructionCombiningPass());
pm.add(llvm::createAggressiveDCEPass()); pm.add(llvm::createAggressiveDCEPass());
pm.add(llvm::createLowerSwitchPass()); pm.add(llvm::createLowerSwitchPass());
return pm.run(_module); return pm.run(_module);

12
evmjit/libevmjit/RuntimeManager.cpp

@ -93,13 +93,13 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
// Unpack data // Unpack data
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data");
assert(m_dataPtr->getType() == Type::RuntimeDataPtr); assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
m_gasPtr = m_builder.CreateStructGEP(m_dataPtr, 0, "gas"); m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gas");
assert(m_gasPtr->getType() == Type::Gas->getPointerTo()); assert(m_gasPtr->getType() == Type::Gas->getPointerTo());
m_memPtr = m_builder.CreateStructGEP(rtPtr, 2, "mem"); m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem");
assert(m_memPtr->getType() == Array::getType()->getPointerTo()); assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env");
assert(m_envPtr->getType() == Type::EnvPtr); assert(m_envPtr->getType() == Type::EnvPtr);
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
@ -160,7 +160,7 @@ llvm::Value* RuntimeManager::getDataPtr()
return m_dataPtr; return m_dataPtr;
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data");
assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo());
return dataPtr; return dataPtr;
} }
@ -173,7 +173,7 @@ llvm::Value* RuntimeManager::getEnvPtr()
llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
{ {
auto ptr = getBuilder().CreateStructGEP(getDataPtr(), _index); auto ptr = getBuilder().CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index);
assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType());
return ptr; return ptr;
} }

3
evmjit/libevmjit/Type.h

@ -3,7 +3,8 @@
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Type.h> #include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h> #include <llvm/IR/Constants.h>
#include "preprocessor/llvm_includes_end.h" #include <llvm/IR/Metadata.h>
#include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed
#include "Common.h" #include "Common.h"

2
exp/CMakeLists.txt

@ -5,7 +5,7 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE exp) set(EXECUTABLE exp)

63
getcoverage.sh

@ -0,0 +1,63 @@
#!/bin/bash
CPP_ETHEREUM_PATH=$(pwd)
BUILD_DIR=$CPP_ETHEREUM_PATH/build
TEST_MODE=""
for i in "$@"
do
case $i in
-builddir)
shift
((i++))
BUILD_DIR=${!i}
shift
;;
--all)
TEST_MODE="--all"
shift
;;
esac
done
which $BUILD_DIR/test/testeth >/dev/null 2>&1
if [ $? != 0 ]
then
echo "You need to compile and build ethereum with cmake -DPROFILING option to the build dir!"
exit;
fi
OUTPUT_DIR=$BUILD_DIR/test/coverage
if which lcov >/dev/null; then
if which genhtml >/dev/null; then
echo Cleaning previous report...
if [ -d "$OUTPUT_DIR" ]; then
rm -r $OUTPUT_DIR
fi
mkdir $OUTPUT_DIR
lcov --directory $BUILD_DIR --zerocounters
lcov --capture --initial --directory $BUILD_DIR --output-file $OUTPUT_DIR/coverage_base.info
echo Running testeth...
$CPP_ETHEREUM_PATH/build/test/testeth $TEST_MODE
$CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit $TEST_MODE
$CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit $TEST_MODE
echo Prepearing coverage info...
lcov --capture --directory $BUILD_DIR --output-file $OUTPUT_DIR/coverage_test.info
lcov --add-tracefile $OUTPUT_DIR/coverage_base.info --add-tracefile $OUTPUT_DIR/coverage_test.info --output-file $OUTPUT_DIR/coverage_all.info
lcov --extract $OUTPUT_DIR/coverage_all.info *cpp-ethereum/* --output-file $OUTPUT_DIR/coverage_export.info
genhtml $OUTPUT_DIR/coverage_export.info --output-directory $OUTPUT_DIR/testeth
else
echo genhtml not found
exit;
fi
else
echo lcov not found
exit;
fi
echo "Coverage info should be located at: $OUTPUT_DIR/testeth"
echo "Opening index..."
xdg-open $OUTPUT_DIR/testeth/index.html &

BIN
install-folder-bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
install-folder-bg@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

2
libdevcore/CMakeLists.txt

@ -15,6 +15,7 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE devcore) set(EXECUTABLE devcore)
@ -26,6 +27,7 @@ target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${DB_LIBRARIES})
# transitive dependencies for windows executables # transitive dependencies for windows executables
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")

2
libdevcore/Common.cpp

@ -28,7 +28,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.9.26"; char const* Version = "0.9.27";
const u256 UndefinedU256 = ~(u256)0; const u256 UndefinedU256 = ~(u256)0;

19
libdevcore/Common.h

@ -113,25 +113,27 @@ static const u256 Invalid256 = ~(u256)0;
static const bytes NullBytes; static const bytes NullBytes;
static const std::map<u256, u256> EmptyMapU256U256; static const std::map<u256, u256> EmptyMapU256U256;
/// Interprets @a _u as a two's complement signed number and returns the resulting s256.
inline s256 u2s(u256 _u) inline s256 u2s(u256 _u)
{ {
static const bigint c_end = (bigint)1 << 256; static const bigint c_end = bigint(1) << 256;
static const u256 c_send = (u256)1 << 255; if (boost::multiprecision::bit_test(_u, 255))
if (_u < c_send) return s256(-(c_end - _u));
return (s256)_u;
else else
return (s256)-(c_end - _u); return s256(_u);
} }
/// @returns the two's complement signed representation of the signed number _u.
inline u256 s2u(s256 _u) inline u256 s2u(s256 _u)
{ {
static const bigint c_end = (bigint)1 << 256; static const bigint c_end = bigint(1) << 256;
if (_u >= 0) if (_u >= 0)
return (u256)_u; return u256(_u);
else else
return (u256)(c_end + _u); return u256(c_end + _u);
} }
/// @returns the smallest n >= 0 such that (1 << n) >= _x
inline unsigned int toLog2(u256 _x) inline unsigned int toLog2(u256 _x)
{ {
unsigned ret; unsigned ret;
@ -139,6 +141,7 @@ inline unsigned int toLog2(u256 _x)
return ret; return ret;
} }
/// @returns the absolute distance between _a and _b.
template <class N> template <class N>
inline N diff(N const& _a, N const& _b) inline N diff(N const& _a, N const& _b)
{ {

4
libdevcore/CommonData.cpp

@ -93,7 +93,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw)
if (h != -1) if (h != -1)
ret.push_back(h); ret.push_back(h);
else if (_throw == WhenError::Throw) else if (_throw == WhenError::Throw)
throw BadHexCharacter(); BOOST_THROW_EXCEPTION(BadHexCharacter());
else else
return bytes(); return bytes();
} }
@ -104,7 +104,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw)
if (h != -1 && l != -1) if (h != -1 && l != -1)
ret.push_back((byte)(h * 16 + l)); ret.push_back((byte)(h * 16 + l));
else if (_throw == WhenError::Throw) else if (_throw == WhenError::Throw)
throw BadHexCharacter(); BOOST_THROW_EXCEPTION(BadHexCharacter());
else else
return bytes(); return bytes();
} }

14
libdevcore/CommonData.h

@ -25,6 +25,7 @@
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <unordered_set>
#include <type_traits> #include <type_traits>
#include <cstring> #include <cstring>
#include <string> #include <string>
@ -68,11 +69,6 @@ int fromHex(char _i, WhenError _throw);
/// If _throw = ThrowType::DontThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception. /// If _throw = ThrowType::DontThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception.
bytes fromHex(std::string const& _s, WhenError _throw = WhenError::DontThrow); bytes fromHex(std::string const& _s, WhenError _throw = WhenError::DontThrow);
#if 0
std::string toBase58(bytesConstRef _data);
bytes fromBase58(std::string const& _s);
#endif
/// Converts byte array to a string containing the same (binary) data. Unless /// Converts byte array to a string containing the same (binary) data. Unless
/// the byte array happens to contain ASCII data, this won't be printable. /// the byte array happens to contain ASCII data, this won't be printable.
inline std::string asString(bytes const& _b) inline std::string asString(bytes const& _b)
@ -258,7 +254,7 @@ template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b
return _a; return _a;
} }
/// Insert the contents of a container into an unordered_st /// Insert the contents of a container into an unordered_set
template <class T, class U> std::unordered_set<T>& operator+=(std::unordered_set<T>& _a, U const& _b) template <class T, class U> std::unordered_set<T>& operator+=(std::unordered_set<T>& _a, U const& _b)
{ {
for (auto const& i: _b) for (auto const& i: _b)
@ -280,6 +276,12 @@ template <class T, class U> std::set<T> operator+(std::set<T> _a, U const& _b)
return _a += _b; return _a += _b;
} }
/// Insert the contents of a container into an unordered_set
template <class T, class U> std::unordered_set<T> operator+(std::unordered_set<T> _a, U const& _b)
{
return _a += _b;
}
/// Concatenate the contents of a container onto a vector /// Concatenate the contents of a container onto a vector
template <class T, class U> std::vector<T> operator+(std::vector<T> _a, U const& _b) template <class T, class U> std::vector<T> operator+(std::vector<T> _a, U const& _b)
{ {

83
libdevcore/CommonIO.cpp

@ -23,13 +23,14 @@
#include <iostream> #include <iostream>
#include <cstdlib> #include <cstdlib>
#include <fstream> #include <fstream>
#include "Exceptions.h"
#include <stdio.h> #include <stdio.h>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#else #else
#include <termios.h> #include <termios.h>
#endif #endif
#include <boost/filesystem.hpp>
#include "Exceptions.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -64,64 +65,58 @@ string dev::memDump(bytes const& _bytes, unsigned _width, bool _html)
return ret.str(); return ret.str();
} }
// Don't forget to delete[] later. template <typename _T>
bytesRef dev::contentsNew(std::string const& _file, bytesRef _dest) inline _T contentsGeneric(std::string const& _file)
{ {
_T ret;
size_t const c_elementSize = sizeof(typename _T::value_type);
std::ifstream is(_file, std::ifstream::binary); std::ifstream is(_file, std::ifstream::binary);
if (!is) if (!is)
return bytesRef(); return ret;
// get length of file: // get length of file:
is.seekg (0, is.end); is.seekg(0, is.end);
streamoff length = is.tellg(); streamoff length = is.tellg();
if (length == 0) // return early, MSVC does not like reading 0 bytes if (length == 0)
return bytesRef(); return ret; // do not read empty file (MSVC does not like it)
if (!_dest.empty() && _dest.size() != (unsigned)length) is.seekg(0, is.beg);
return bytesRef();
is.seekg (0, is.beg); ret.resize((length + c_elementSize - 1) / c_elementSize);
bytesRef ret = _dest.empty() ? bytesRef(new byte[length], length) : _dest; is.read(const_cast<char*>(reinterpret_cast<char const*>(ret.data())), length);
is.read((char*)ret.data(), length);
is.close();
return ret; return ret;
} }
bytes dev::contents(std::string const& _file) bytes dev::contents(string const& _file)
{ {
std::ifstream is(_file, std::ifstream::binary); return contentsGeneric<bytes>(_file);
if (!is)
return bytes();
// get length of file:
is.seekg (0, is.end);
streamoff length = is.tellg();
if (length == 0) // return early, MSVC does not like reading 0 bytes
return bytes();
is.seekg (0, is.beg);
bytes ret(length);
is.read((char*)ret.data(), length);
is.close();
return ret;
} }
string dev::contentsString(std::string const& _file) string dev::contentsString(string const& _file)
{ {
std::ifstream is(_file, std::ifstream::binary); return contentsGeneric<string>(_file);
if (!is)
return string();
// get length of file:
is.seekg (0, is.end);
streamoff length = is.tellg();
if (length == 0) // return early, MSVC does not like reading 0 bytes
return string();
is.seekg (0, is.beg);
string ret;
ret.resize(length);
is.read((char*)ret.data(), length);
is.close();
return ret;
} }
void dev::writeFile(std::string const& _file, bytesConstRef _data) void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename)
{ {
ofstream(_file, ios::trunc|ios::binary).write((char const*)_data.data(), _data.size()); namespace fs = boost::filesystem;
if (_writeDeleteRename)
{
fs::path tempPath = fs::unique_path(_file + "-%%%%%%");
writeFile(tempPath.string(), _data, false);
// will delete _file if it exists
fs::rename(tempPath, _file);
}
else
{
// create directory if not existent
fs::path p(_file);
fs::create_directories(p.parent_path());
ofstream s(_file, ios::trunc | ios::binary);
s.write(reinterpret_cast<char const*>(_data.data()), _data.size());
if (!s)
BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + _file));
}
} }
std::string dev::getPassword(std::string const& _prompt) std::string dev::getPassword(std::string const& _prompt)

15
libdevcore/CommonIO.h

@ -42,20 +42,27 @@
namespace dev namespace dev
{ {
/// Requests the user to enter a password on the console.
std::string getPassword(std::string const& _prompt); std::string getPassword(std::string const& _prompt);
/// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes. /// Retrieve and returns the contents of the given file.
/// If the file doesn't exist or isn't readable, returns an empty container / bytes.
bytes contents(std::string const& _file); bytes contents(std::string const& _file);
/// Retrieve and returns the contents of the given file as a std::string.
/// If the file doesn't exist or isn't readable, returns an empty container / bytes.
std::string contentsString(std::string const& _file); std::string contentsString(std::string const& _file);
/// Retrieve and returns the allocated contents of the given file; if @_dest is given, don't allocate, use it directly. /// Retrieve and returns the allocated contents of the given file; if @_dest is given, don't allocate, use it directly.
/// If the file doesn't exist or isn't readable, returns bytesRef(). Don't forget to delete [] the returned value's data when finished. /// If the file doesn't exist or isn't readable, returns bytesRef(). Don't forget to delete [] the returned value's data when finished.
bytesRef contentsNew(std::string const& _file, bytesRef _dest = bytesRef()); bytesRef contentsNew(std::string const& _file, bytesRef _dest = bytesRef());
/// Write the given binary data into the given file, replacing the file if it pre-exists. /// Write the given binary data into the given file, replacing the file if it pre-exists.
void writeFile(std::string const& _file, bytesConstRef _data); /// Throws exception on error.
/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in
/// the same directory and then moves that file.
void writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename = false);
/// Write the given binary data into the given file, replacing the file if it pre-exists. /// Write the given binary data into the given file, replacing the file if it pre-exists.
inline void writeFile(std::string const& _file, bytes const& _data) { writeFile(_file, bytesConstRef(&_data)); } inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); }
inline void writeFile(std::string const& _file, std::string const& _data) { writeFile(_file, bytesConstRef(_data)); } inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); }
/// Nicely renders the given bytes to a string, optionally as HTML. /// Nicely renders the given bytes to a string, optionally as HTML.
/// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line. /// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line.

7
libdevcore/FixedHash.h

@ -158,16 +158,17 @@ public:
template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h) template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h)
{ {
return (*this |= _h.template bloom<P, N>()); return (*this |= _h.template bloomPart<P, N>());
} }
template <unsigned P, unsigned M> inline bool containsBloom(FixedHash<M> const& _h) template <unsigned P, unsigned M> inline bool containsBloom(FixedHash<M> const& _h)
{ {
return contains(_h.template bloom<P, N>()); return contains(_h.template bloomPart<P, N>());
} }
template <unsigned P, unsigned M> inline FixedHash<M> bloom() const template <unsigned P, unsigned M> inline FixedHash<M> bloomPart() const
{ {
static_assert((M & (M - 1)) == 0, "M must be power-of-two");
static const unsigned c_bloomBits = M * 8; static const unsigned c_bloomBits = M * 8;
unsigned mask = c_bloomBits - 1; unsigned mask = c_bloomBits - 1;
unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8; unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8;

4
libdevcore/RLP.cpp

@ -202,9 +202,7 @@ unsigned RLP::items() const
RLPStream& RLPStream::appendRaw(bytesConstRef _s, unsigned _itemCount) RLPStream& RLPStream::appendRaw(bytesConstRef _s, unsigned _itemCount)
{ {
unsigned os = m_out.size(); m_out.insert(m_out.end(), _s.begin(), _s.end());
m_out.resize(os + _s.size());
memcpy(m_out.data() + os, _s.data(), _s.size());
noteAppended(_itemCount); noteAppended(_itemCount);
return *this; return *this;
} }

2
libdevcore/RLP.h

@ -292,7 +292,7 @@ public:
RLPs toList() const; RLPs toList() const;
/// @returns the data payload. Valid for all types. /// @returns the data payload. Valid for all types.
bytesConstRef payload() const { auto l = length(); if (l > m_data.size()) throw BadRLP(); return m_data.cropped(payloadOffset(), l); } bytesConstRef payload() const { auto l = length(); if (l > m_data.size()) BOOST_THROW_EXCEPTION(BadRLP()); return m_data.cropped(payloadOffset(), l); }
/// @returns the theoretical size of this item. /// @returns the theoretical size of this item.
/// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work. /// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work.

15
libdevcore/TrieDB.h

@ -21,19 +21,14 @@
#pragma once #pragma once
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <memory> #include <memory>
#include <libdevcore/Common.h> #include "db.h"
#include <libdevcore/Log.h> #include "Common.h"
#include <libdevcore/Exceptions.h> #include "Log.h"
#include <libdevcore/SHA3.h> #include "Exceptions.h"
#include "SHA3.h"
#include "MemoryDB.h" #include "MemoryDB.h"
#include "TrieCommon.h" #include "TrieCommon.h"
namespace ldb = leveldb;
namespace dev namespace dev
{ {

36
libdevcore/db.h

@ -0,0 +1,36 @@
/*
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 DB.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#if ETH_ROCKSDB || !ETH_TRUE
#include <rocksdb/db.h>
#include <rocksdb/write_batch.h>
namespace ldb = rocksdb;
#else
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
namespace ldb = leveldb;
#endif
#pragma warning(pop)
#define DEV_LDB 1

33
libdevcore/vector_ref.h

@ -9,6 +9,9 @@
namespace dev namespace dev
{ {
/**
* A modifiable reference to an existing object or vector in memory.
*/
template <class _T> template <class _T>
class vector_ref class vector_ref
{ {
@ -17,34 +20,50 @@ public:
using element_type = _T; using element_type = _T;
using mutable_value_type = typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type; using mutable_value_type = typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type;
static_assert(std::is_pod<value_type>::value, "vector_ref can only be used with PODs due to its low-level treatment of data.");
vector_ref(): m_data(nullptr), m_count(0) {} vector_ref(): m_data(nullptr), m_count(0) {}
/// Creates a new vector_ref to point to @a _count elements starting at @a _data.
vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {}
/// Creates a new vector_ref pointing to the data part of a string (given as pointer).
vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const*, std::string*>::type _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {} vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const*, std::string*>::type _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {}
/// Creates a new vector_ref pointing to the data part of a vector (given as pointer).
vector_ref(typename std::conditional<std::is_const<_T>::value, std::vector<typename std::remove_const<_T>::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} vector_ref(typename std::conditional<std::is_const<_T>::value, std::vector<typename std::remove_const<_T>::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {}
vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {} /// Creates a new vector_ref pointing to the data part of a string (given as reference).
#ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const&, std::string&>::type _data): m_data(reinterpret_cast<_T*>(_data.data())), m_count(_data.size() / sizeof(_T)) {}
vector_ref(leveldb::Slice const& _s): m_data(reinterpret_cast<_T*>(_s.data())), m_count(_s.size() / sizeof(_T)) {} #if DEV_LDB
vector_ref(ldb::Slice const& _s): m_data(reinterpret_cast<_T*>(_s.data())), m_count(_s.size() / sizeof(_T)) {}
#endif #endif
explicit operator bool() const { return m_data && m_count; } explicit operator bool() const { return m_data && m_count; }
bool contentsEqual(std::vector<mutable_value_type> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); } bool contentsEqual(std::vector<mutable_value_type> const& _c) const { if (!m_data || m_count == 0) return _c.empty(); else return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count * sizeof(_T)); }
std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); } std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); }
std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>(reinterpret_cast<unsigned char const*>(m_data), reinterpret_cast<unsigned char const*>(m_data) + m_count * sizeof(_T)); } std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>(reinterpret_cast<unsigned char const*>(m_data), reinterpret_cast<unsigned char const*>(m_data) + m_count * sizeof(_T)); }
std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); }
template <class _T2> explicit operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>(reinterpret_cast<_T2*>(m_data), m_count * sizeof(_T) / sizeof(_T2)); } template <class _T2> explicit operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>(reinterpret_cast<_T2*>(m_data), m_count * sizeof(_T) / sizeof(_T2)); }
operator vector_ref<_T const>() const { return vector_ref<_T const>(m_data, m_count); } operator vector_ref<_T const>() const { return vector_ref<_T const>(m_data, m_count); }
_T* data() const { return m_data; } _T* data() const { return m_data; }
/// @returns the number of elements referenced (not necessarily number of bytes).
size_t count() const { return m_count; } size_t count() const { return m_count; }
/// @returns the number of elements referenced (not necessarily number of bytes).
size_t size() const { return m_count; } size_t size() const { return m_count; }
bool empty() const { return !m_count; } bool empty() const { return !m_count; }
vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); } /// @returns a new vector_ref pointing at the next chunk of @a size() elements.
vector_ref<_T> next() const { if (!m_data) return *this; else return vector_ref<_T>(m_data + m_count, m_count); }
/// @returns a new vector_ref which is a shifted and shortened view of the original data.
/// If this goes out of bounds in any way, returns an empty vector_ref.
/// If @a _count is ~size_t(0), extends the view to the end of the data.
vector_ref<_T> cropped(size_t _begin, size_t _count) const { if (m_data && _begin + _count <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } vector_ref<_T> cropped(size_t _begin, size_t _count) const { if (m_data && _begin + _count <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); }
/// @returns a new vector_ref which is a shifted view of the original data (not going beyond it).
vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); } vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); }
void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; } void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; }
void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); }
template <class T> bool overlapsWith(vector_ref<T> _t) const { void const* f1 = data(); void const* t1 = data() + size(); void const* f2 = _t.data(); void const* t2 = _t.data() + _t.size(); return f1 < t2 && t1 > f2; } template <class T> bool overlapsWith(vector_ref<T> _t) const { void const* f1 = data(); void const* t1 = data() + size(); void const* f2 = _t.data(); void const* t2 = _t.data() + _t.size(); return f1 < t2 && t1 > f2; }
/// Copies the contents of this vector_ref to the contents of @a _t, up to the max size of @a _t.
void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { if (overlapsWith(_t)) memmove(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); else memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { if (overlapsWith(_t)) memmove(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); else memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); }
/// Copies the contents of this vector_ref to the contents of @a _t, and zeros further trailing elements in @a _t.
void populate(vector_ref<typename std::remove_const<_T>::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); } void populate(vector_ref<typename std::remove_const<_T>::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); }
_T* begin() { return m_data; } _T* begin() { return m_data; }
@ -58,8 +77,8 @@ public:
bool operator==(vector_ref<_T> const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; } bool operator==(vector_ref<_T> const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; }
bool operator!=(vector_ref<_T> const& _cmp) const { return !operator==(_cmp); } bool operator!=(vector_ref<_T> const& _cmp) const { return !operator==(_cmp); }
#ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ #if DEV_LDB
operator leveldb::Slice() const { return leveldb::Slice((char const*)m_data, m_count * sizeof(_T)); } operator ldb::Slice() const { return ldb::Slice((char const*)m_data, m_count * sizeof(_T)); }
#endif #endif
void reset() { m_data = nullptr; m_count = 0; } void reset() { m_data = nullptr; m_count = 0; }

2
libdevcrypto/AES.h

@ -76,6 +76,8 @@ public:
/// Adjust mac interval. Next mac will be xored with value. /// Adjust mac interval. Next mac will be xored with value.
void adjustInterval(unsigned _interval) { m_macInterval = _interval; } void adjustInterval(unsigned _interval) { m_macInterval = _interval; }
unsigned getMacInterval() { return m_macInterval;}
private: private:
AuthenticatedStream(AuthenticatedStream const&) = delete; AuthenticatedStream(AuthenticatedStream const&) = delete;
AuthenticatedStream& operator=(AuthenticatedStream const&) = delete; AuthenticatedStream& operator=(AuthenticatedStream const&) = delete;

4
libdevcrypto/CMakeLists.txt

@ -12,7 +12,7 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE devcrypto) set(EXECUTABLE devcrypto)
@ -20,7 +20,7 @@ file(GLOB HEADERS "*.h")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${DB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} scrypt) target_link_libraries(${EXECUTABLE} scrypt)
target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} devcore)

111
libdevcrypto/Common.cpp

@ -31,6 +31,7 @@
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include "AES.h" #include "AES.h"
#include "CryptoPP.h" #include "CryptoPP.h"
#include "Exceptions.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::crypto; using namespace dev::crypto;
@ -178,15 +179,35 @@ bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash)
bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen) bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen)
{ {
bytes ret(_dkLen); bytes ret(_dkLen);
PKCS5_PBKDF2_HMAC<SHA256> pbkdf; if (PKCS5_PBKDF2_HMAC<SHA256>().DeriveKey(
pbkdf.DeriveKey(ret.data(), ret.size(), 0, (byte*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _iterations); ret.data(),
ret.size(),
0,
reinterpret_cast<byte const*>(_pass.data()),
_pass.size(),
_salt.data(),
_salt.size(),
_iterations
) != _iterations)
BOOST_THROW_EXCEPTION(CryptoException() << errinfo_comment("Key derivation failed."));
return ret; return ret;
} }
bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen) bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen)
{ {
bytes ret(_dkLen); bytes ret(_dkLen);
libscrypt_scrypt((uint8_t const*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _n, _r, _p, ret.data(), ret.size()); if (libscrypt_scrypt(
reinterpret_cast<uint8_t const*>(_pass.data()),
_pass.size(),
_salt.data(),
_salt.size(),
_n,
_r,
_p,
ret.data(),
ret.size()
) != 0)
BOOST_THROW_EXCEPTION(CryptoException() << errinfo_comment("Key derivation failed."));
return ret; return ret;
} }
@ -233,42 +254,84 @@ h256 crypto::kdf(Secret const& _priv, h256 const& _hash)
return s; return s;
} }
h256 Nonce::get(bool _commit) mutex Nonce::s_x;
static string s_seedFile;
h256 Nonce::get()
{ {
// todo: atomic efface bit, periodic save, kdf, rr, rng // todo: atomic efface bit, periodic save, kdf, rr, rng
// todo: encrypt // todo: encrypt
static h256 s_seed; Guard l(Nonce::s_x);
static string s_seedFile(getDataDir() + "/seed"); return Nonce::singleton().next();
static mutex s_x; }
Guard l(s_x);
if (!s_seed) void Nonce::reset()
{ {
static Nonce s_nonce; Guard l(Nonce::s_x);
bytes b = contents(s_seedFile); Nonce::singleton().resetInternal();
}
void Nonce::setSeedFilePath(string const& _filePath)
{
s_seedFile = _filePath;
}
Nonce::~Nonce()
{
Guard l(Nonce::s_x);
if (m_value)
// this might throw
resetInternal();
}
Nonce& Nonce::singleton()
{
static Nonce s;
return s;
}
void Nonce::initialiseIfNeeded()
{
if (m_value)
return;
bytes b = contents(seedFile());
if (b.size() == 32) if (b.size() == 32)
memcpy(s_seed.data(), b.data(), 32); memcpy(m_value.data(), b.data(), 32);
else else
{ {
// todo: replace w/entropy from user and system // todo: replace w/entropy from user and system
std::mt19937_64 s_eng(time(0) + chrono::high_resolution_clock::now().time_since_epoch().count()); std::mt19937_64 s_eng(time(0) + chrono::high_resolution_clock::now().time_since_epoch().count());
std::uniform_int_distribution<uint16_t> d(0, 255); std::uniform_int_distribution<uint16_t> d(0, 255);
for (unsigned i = 0; i < 32; ++i) for (unsigned i = 0; i < 32; ++i)
s_seed[i] = (byte)d(s_eng); m_value[i] = byte(d(s_eng));
} }
if (!s_seed) if (!m_value)
BOOST_THROW_EXCEPTION(InvalidState()); BOOST_THROW_EXCEPTION(InvalidState());
// prevent seed reuse if process terminates abnormally // prevent seed reuse if process terminates abnormally
writeFile(s_seedFile, bytes()); // this might throw
} writeFile(seedFile(), bytes());
h256 prev(s_seed);
sha3(prev.ref(), s_seed.ref());
if (_commit)
writeFile(s_seedFile, s_seed.asBytes());
return std::move(s_seed);
} }
Nonce::~Nonce() h256 Nonce::next()
{
initialiseIfNeeded();
m_value = sha3(m_value);
return m_value;
}
void Nonce::resetInternal()
{
// this might throw
next();
writeFile(seedFile(), m_value.asBytes());
m_value = h256();
}
string const& Nonce::seedFile()
{ {
Nonce::get(true); if (s_seedFile.empty())
s_seedFile = getDataDir() + "/seed";
return s_seedFile;
} }

27
libdevcrypto/Common.h

@ -24,6 +24,7 @@
#pragma once #pragma once
#include <mutex>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
@ -180,14 +181,36 @@ struct InvalidState: public dev::Exception {};
h256 kdf(Secret const& _priv, h256 const& _hash); h256 kdf(Secret const& _priv, h256 const& _hash);
/** /**
* @brief Generator for nonce material * @brief Generator for nonce material.
*/ */
struct Nonce struct Nonce
{ {
static h256 get(bool _commit = false); /// Returns the next nonce (might be read from a file).
static h256 get();
/// Stores the current nonce in a file and resets Nonce to the uninitialised state.
static void reset();
/// Sets the location of the seed file to a non-default place. Used for testing.
static void setSeedFilePath(std::string const& _filePath);
private: private:
Nonce() {} Nonce() {}
~Nonce(); ~Nonce();
/// @returns the singleton instance.
static Nonce& singleton();
/// Reads the last seed from the seed file.
void initialiseIfNeeded();
/// @returns the next nonce.
h256 next();
/// Stores the current seed in the seed file.
void resetInternal();
/// @returns the path of the seed file.
static std::string const& seedFile();
/// Mutex for the singleton object.
/// @note Every access to any private function has to be guarded by this mutex.
static std::mutex s_x;
h256 m_value;
}; };
} }

35
libdevcrypto/Exceptions.h

@ -0,0 +1,35 @@
/*
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 Exceptions.h
* @author Christian <c@ethdev.com>
* @date 2016
*/
#pragma once
#include <libdevcore/Exceptions.h>
namespace dev
{
namespace crypto
{
/// Rare malfunction of cryptographic functions.
DEV_SIMPLE_EXCEPTION(CryptoException);
}
}

3
libdevcrypto/OverlayDB.cpp

@ -20,8 +20,7 @@
*/ */
#include <thread> #include <thread>
#include <leveldb/db.h> #include <libdevcore/db.h>
#include <leveldb/write_batch.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include "OverlayDB.h" #include "OverlayDB.h"
using namespace std; using namespace std;

7
libdevcrypto/OverlayDB.h

@ -21,16 +21,11 @@
#pragma once #pragma once
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <memory> #include <memory>
#include <libdevcore/db.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/MemoryDB.h> #include <libdevcore/MemoryDB.h>
namespace ldb = leveldb;
namespace dev namespace dev
{ {

120
libdevcrypto/SecretStore.cpp

@ -29,6 +29,7 @@
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <test/JsonSpiritHeaders.h> #include <test/JsonSpiritHeaders.h>
#include <libdevcrypto/Exceptions.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
namespace js = json_spirit; namespace js = json_spirit;
@ -36,7 +37,8 @@ namespace fs = boost::filesystem;
static const int c_keyFileVersion = 3; static const int c_keyFileVersion = 3;
static js::mValue upgraded(std::string const& _s) /// Upgrade the json-format to the current version.
static js::mValue upgraded(string const& _s)
{ {
js::mValue v; js::mValue v;
js::read_string(_s, v); js::read_string(_s, v);
@ -84,35 +86,34 @@ static js::mValue upgraded(std::string const& _s)
return js::mValue(); return js::mValue();
} }
SecretStore::SecretStore(std::string const& _path): m_path(_path) SecretStore::SecretStore(string const& _path): m_path(_path)
{ {
load(); load();
} }
SecretStore::~SecretStore() bytes SecretStore::secret(h128 const& _uuid, function<string()> const& _pass, bool _useCache) const
{ {
}
bytes SecretStore::secret(h128 const& _uuid, function<std::string()> const& _pass, bool _useCache) const
{
(void)_pass;
auto rit = m_cached.find(_uuid); auto rit = m_cached.find(_uuid);
if (_useCache && rit != m_cached.end()) if (_useCache && rit != m_cached.end())
return rit->second; return rit->second;
auto it = m_keys.find(_uuid); auto it = m_keys.find(_uuid);
if (it == m_keys.end()) bytes key;
return bytes(); if (it != m_keys.end())
bytes key = decrypt(it->second.first, _pass()); {
key = decrypt(it->second.encryptedKey, _pass());
if (!key.empty()) if (!key.empty())
m_cached[_uuid] = key; m_cached[_uuid] = key;
}
return key; return key;
} }
h128 SecretStore::importSecret(bytes const& _s, std::string const& _pass) h128 SecretStore::importSecret(bytes const& _s, string const& _pass)
{ {
h128 r = h128::random(); h128 r;
EncryptedKey key{encrypt(_s, _pass), string()};
r = h128::random();
m_cached[r] = _s; m_cached[r] = _s;
m_keys[r] = make_pair(encrypt(_s, _pass), std::string()); m_keys[r] = move(key);
save(); save();
return r; return r;
} }
@ -122,7 +123,7 @@ void SecretStore::kill(h128 const& _uuid)
m_cached.erase(_uuid); m_cached.erase(_uuid);
if (m_keys.count(_uuid)) if (m_keys.count(_uuid))
{ {
boost::filesystem::remove(m_keys[_uuid].second); fs::remove(m_keys[_uuid].filename);
m_keys.erase(_uuid); m_keys.erase(_uuid);
} }
} }
@ -132,50 +133,50 @@ void SecretStore::clearCache() const
m_cached.clear(); m_cached.clear();
} }
void SecretStore::save(std::string const& _keysPath) void SecretStore::save(string const& _keysPath)
{ {
fs::path p(_keysPath); fs::path p(_keysPath);
boost::filesystem::create_directories(p); fs::create_directories(p);
for (auto& k: m_keys) for (auto& k: m_keys)
{ {
std::string uuid = toUUID(k.first); string uuid = toUUID(k.first);
std::string filename = (p / uuid).string() + ".json"; string filename = (p / uuid).string() + ".json";
js::mObject v; js::mObject v;
js::mValue crypto; js::mValue crypto;
js::read_string(k.second.first, crypto); js::read_string(k.second.encryptedKey, crypto);
v["crypto"] = crypto; v["crypto"] = crypto;
v["id"] = uuid; v["id"] = uuid;
v["version"] = c_keyFileVersion; v["version"] = c_keyFileVersion;
writeFile(filename, js::write_string(js::mValue(v), true)); writeFile(filename, js::write_string(js::mValue(v), true));
if (!k.second.second.empty() && k.second.second != filename) swap(k.second.filename, filename);
boost::filesystem::remove(k.second.second); if (!filename.empty() && !fs::equivalent(filename, k.second.filename))
k.second.second = filename; fs::remove(filename);
} }
} }
void SecretStore::load(std::string const& _keysPath) void SecretStore::load(string const& _keysPath)
{ {
fs::path p(_keysPath); fs::path p(_keysPath);
boost::filesystem::create_directories(p); fs::create_directories(p);
for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it)
if (is_regular_file(it->path())) if (fs::is_regular_file(it->path()))
readKey(it->path().string(), true); readKey(it->path().string(), true);
} }
h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) h128 SecretStore::readKey(string const& _file, bool _takeFileOwnership)
{ {
cnote << "Reading" << _file; cnote << "Reading" << _file;
return readKeyContent(contentsString(_file), _deleteFile ? _file : string()); return readKeyContent(contentsString(_file), _takeFileOwnership ? _file : string());
} }
h128 SecretStore::readKeyContent(std::string const& _content, std::string const& _file) h128 SecretStore::readKeyContent(string const& _content, string const& _file)
{ {
js::mValue u = upgraded(_content); js::mValue u = upgraded(_content);
if (u.type() == js::obj_type) if (u.type() == js::obj_type)
{ {
js::mObject& o = u.get_obj(); js::mObject& o = u.get_obj();
auto uuid = fromUUID(o["id"].get_str()); auto uuid = fromUUID(o["id"].get_str());
m_keys[uuid] = make_pair(js::write_string(o["crypto"], false), _file); m_keys[uuid] = EncryptedKey{js::write_string(o["crypto"], false), _file};
return uuid; return uuid;
} }
else else
@ -183,62 +184,63 @@ h128 SecretStore::readKeyContent(std::string const& _content, std::string const&
return h128(); return h128();
} }
bool SecretStore::recode(h128 const& _uuid, string const& _newPass, std::function<std::string()> const& _pass, KDF _kdf) bool SecretStore::recode(h128 const& _uuid, string const& _newPass, function<string()> const& _pass, KDF _kdf)
{ {
// cdebug << "recode:" << toUUID(_uuid);
bytes s = secret(_uuid, _pass, true); bytes s = secret(_uuid, _pass, true);
if (s.empty()) if (s.empty())
return false; return false;
m_keys[_uuid].first = encrypt(s, _newPass, _kdf); m_cached.erase(_uuid);
m_keys[_uuid].encryptedKey = encrypt(s, _newPass, _kdf);
save(); save();
return true; return true;
} }
std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF _kdf) static bytes deriveNewKey(string const& _pass, KDF _kdf, js::mObject& o_ret)
{ {
js::mObject ret;
// KDF info
unsigned dklen = 32; unsigned dklen = 32;
unsigned iterations = 1 << 18;
bytes salt = h256::random().asBytes(); bytes salt = h256::random().asBytes();
bytes derivedKey;
if (_kdf == KDF::Scrypt) if (_kdf == KDF::Scrypt)
{ {
unsigned iterations = 262144;
unsigned p = 1; unsigned p = 1;
unsigned r = 8; unsigned r = 8;
ret["kdf"] = "scrypt"; o_ret["kdf"] = "scrypt";
{ {
js::mObject params; js::mObject params;
params["n"] = (int64_t)iterations; params["n"] = int64_t(iterations);
params["r"] = (int)r; params["r"] = int(r);
params["p"] = (int)p; params["p"] = int(p);
params["dklen"] = (int)dklen; params["dklen"] = int(dklen);
params["salt"] = toHex(salt); params["salt"] = toHex(salt);
ret["kdfparams"] = params; o_ret["kdfparams"] = params;
} }
derivedKey = scrypt(_pass, salt, iterations, r, p, dklen); return scrypt(_pass, salt, iterations, r, p, dklen);
} }
else else
{ {
unsigned iterations = 262144; o_ret["kdf"] = "pbkdf2";
ret["kdf"] = "pbkdf2";
{ {
js::mObject params; js::mObject params;
params["prf"] = "hmac-sha256"; params["prf"] = "hmac-sha256";
params["c"] = (int)iterations; params["c"] = int(iterations);
params["salt"] = toHex(salt); params["salt"] = toHex(salt);
params["dklen"] = (int)dklen; params["dklen"] = int(dklen);
ret["kdfparams"] = params; o_ret["kdfparams"] = params;
} }
derivedKey = pbkdf2(_pass, salt, iterations, dklen); return pbkdf2(_pass, salt, iterations, dklen);
} }
// cdebug << "derivedKey" << toHex(derivedKey); }
string SecretStore::encrypt(bytes const& _v, string const& _pass, KDF _kdf)
{
js::mObject ret;
bytes derivedKey = deriveNewKey(_pass, _kdf, ret);
if (derivedKey.empty())
BOOST_THROW_EXCEPTION(crypto::CryptoException() << errinfo_comment("Key derivation failed."));
// cipher info
ret["cipher"] = "aes-128-ctr"; ret["cipher"] = "aes-128-ctr";
h128 key(derivedKey, h128::AlignLeft); h128 key(derivedKey, h128::AlignLeft);
// cdebug << "cipherKey" << key.hex();
h128 iv = h128::random(); h128 iv = h128::random();
{ {
js::mObject params; js::mObject params;
@ -248,18 +250,18 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF
// cipher text // cipher text
bytes cipherText = encryptSymNoAuth(key, iv, &_v); bytes cipherText = encryptSymNoAuth(key, iv, &_v);
if (cipherText.empty())
BOOST_THROW_EXCEPTION(crypto::CryptoException() << errinfo_comment("Key encryption failed."));
ret["ciphertext"] = toHex(cipherText); ret["ciphertext"] = toHex(cipherText);
// and mac. // and mac.
h256 mac = sha3(ref(derivedKey).cropped(16, 16).toBytes() + cipherText); h256 mac = sha3(ref(derivedKey).cropped(16, 16).toBytes() + cipherText);
// cdebug << "macBody" << toHex(ref(derivedKey).cropped(16, 16).toBytes() + cipherText);
// cdebug << "mac" << mac.hex();
ret["mac"] = toHex(mac.ref()); ret["mac"] = toHex(mac.ref());
return js::write_string((js::mValue)ret, true); return js::write_string(js::mValue(ret), true);
} }
bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) bytes SecretStore::decrypt(string const& _v, string const& _pass)
{ {
js::mObject o; js::mObject o;
{ {

50
libdevcrypto/SecretStore.h

@ -35,41 +35,81 @@ enum class KDF {
Scrypt, Scrypt,
}; };
/**
* Manages encrypted keys stored in a certain directory on disk. The keys are read into memory
* and changes to the keys are automatically synced to the directory.
* Each file stores exactly one key in a specific JSON format whose file name is derived from the
* UUID of the key.
* @note that most of the functions here affect the filesystem and throw exceptions on failure,
* and they also throw exceptions upon rare malfunction in the cryptographic functions.
*/
class SecretStore class SecretStore
{ {
public: public:
/// Construct a new SecretStore and read all keys in the given directory.
SecretStore(std::string const& _path = defaultPath()); SecretStore(std::string const& _path = defaultPath());
~SecretStore();
/// @returns the secret key stored by the given @a _uuid.
/// @param _pass function that returns the password for the key.
/// @param _useCache if true, allow previously decrypted keys to be returned directly.
bytes secret(h128 const& _uuid, std::function<std::string()> const& _pass, bool _useCache = true) const; bytes secret(h128 const& _uuid, std::function<std::string()> const& _pass, bool _useCache = true) const;
/// Imports the (encrypted) key stored in the file @a _file and copies it to the managed directory.
h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; } h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; }
/// Imports the (encrypted) key contained in the json formatted @a _content and stores it in
/// the managed directory.
h128 importKeyContent(std::string const& _content) { auto ret = readKeyContent(_content, std::string()); if (ret) save(); return ret; } h128 importKeyContent(std::string const& _content) { auto ret = readKeyContent(_content, std::string()); if (ret) save(); return ret; }
/// Imports the decrypted key given by @a _s and stores it, encrypted with
/// (a key derived from) the password @a _pass.
h128 importSecret(bytes const& _s, std::string const& _pass); h128 importSecret(bytes const& _s, std::string const& _pass);
/// Decrypts and re-encrypts the key identified by @a _uuid.
bool recode(h128 const& _uuid, std::string const& _newPass, std::function<std::string()> const& _pass, KDF _kdf = KDF::Scrypt); bool recode(h128 const& _uuid, std::string const& _newPass, std::function<std::string()> const& _pass, KDF _kdf = KDF::Scrypt);
/// Removes the key specified by @a _uuid from both memory and disk.
void kill(h128 const& _uuid); void kill(h128 const& _uuid);
/// Returns the uuids of all stored keys.
std::vector<h128> keys() const { return keysOf(m_keys); } std::vector<h128> keys() const { return keysOf(m_keys); }
// Clear any cached keys. /// Clears all cached decrypted keys. The passwords have to be supplied in order to retrieve
/// secrets again after calling this function.
void clearCache() const; void clearCache() const;
// Doesn't save(). /// Import the key from the file @a _file, but do not copy it to the managed directory yet.
h128 readKey(std::string const& _file, bool _deleteFile); /// @param _takeFileOwnership if true, deletes the file if it is not the canonical file for the
/// key (derived from its uuid).
h128 readKey(std::string const& _file, bool _takeFileOwnership);
/// Import the key contained in the json-encoded @a _content, but do not store it in the
/// managed directory.
/// @param _file if given, assume this file contains @a _content and delete it later, if it is
/// not the canonical file for the key (derived from the uuid).
h128 readKeyContent(std::string const& _content, std::string const& _file = std::string()); h128 readKeyContent(std::string const& _content, std::string const& _file = std::string());
/// Store all keys in the directory @a _keysPath.
void save(std::string const& _keysPath); void save(std::string const& _keysPath);
/// Store all keys in the managed directory.
void save() { save(m_path); } void save() { save(m_path); }
/// @returns the default path for the managed directory.
static std::string defaultPath() { return getDataDir("web3") + "/keys"; } static std::string defaultPath() { return getDataDir("web3") + "/keys"; }
private: private:
struct EncryptedKey
{
std::string encryptedKey;
std::string filename;
};
/// Loads all keys in the given directory.
void load(std::string const& _keysPath); void load(std::string const& _keysPath);
void load() { load(m_path); } void load() { load(m_path); }
/// Encrypts @a _v with a key derived from @a _pass or the empty string on error.
static std::string encrypt(bytes const& _v, std::string const& _pass, KDF _kdf = KDF::Scrypt); static std::string encrypt(bytes const& _v, std::string const& _pass, KDF _kdf = KDF::Scrypt);
/// Decrypts @a _v with a key derived from @a _pass or the empty byte array on error.
static bytes decrypt(std::string const& _v, std::string const& _pass); static bytes decrypt(std::string const& _v, std::string const& _pass);
/// Stores decrypted keys by uuid.
mutable std::unordered_map<h128, bytes> m_cached; mutable std::unordered_map<h128, bytes> m_cached;
std::unordered_map<h128, std::pair<std::string, std::string>> m_keys; /// Stores encrypted keys together with the file they were loaded from by uuid.
std::unordered_map<h128, EncryptedKey> m_keys;
std::string m_path; std::string m_path;
}; };

21
libethash-cl/CMakeLists.txt

@ -1,12 +1,23 @@
set(EXECUTABLE ethash-cl) set(EXECUTABLE ethash-cl)
include(bin2h.cmake) # A custom command and target to turn the OpenCL kernel into a byte array header
bin2h(SOURCE_FILE ethash_cl_miner_kernel.cl # The normal build depends on it properly and if the kernel file is changed, then
VARIABLE_NAME ethash_cl_miner_kernel # a rebuild of libethash-cl should be triggered
HEADER_FILE ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h) add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h
COMMAND ${CMAKE_COMMAND} ARGS
-DBIN2H_SOURCE_FILE="${CMAKE_CURRENT_SOURCE_DIR}/ethash_cl_miner_kernel.cl"
-DBIN2H_VARIABLE_NAME=ethash_cl_miner_kernel
-DBIN2H_HEADER_FILE="${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h"
-P "${CMAKE_CURRENT_SOURCE_DIR}/bin2h.cmake"
COMMENT "Generating OpenCL Kernel Byte Array"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ethash_cl_miner_kernel.cl
)
add_custom_target(clbin2h DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h ${CMAKE_CURRENT_SOURCE_DIR}/ethash_cl_miner_kernel.cl)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
file(GLOB HEADERS "*.h") file(GLOB OUR_HEADERS "*.h")
set(HEADERS ${OUR_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${OpenCL_INCLUDE_DIRS}) include_directories(${OpenCL_INCLUDE_DIRS})

60
libethash-cl/bin2h.cmake

@ -30,7 +30,7 @@ function(WRAP_STRING)
set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE) set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE)
endfunction() endfunction()
# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file # Script to embed contents of a file as byte array in C/C++ header file(.h). The header file
# will contain a byte array and integer variable holding the size of the array. # will contain a byte array and integer variable holding the size of the array.
# Parameters # Parameters
# SOURCE_FILE - The path of source file whose contents will be embedded in the header file. # SOURCE_FILE - The path of source file whose contents will be embedded in the header file.
@ -42,45 +42,41 @@ endfunction()
# useful if the source file is a text file and we want to use the file contents # useful if the source file is a text file and we want to use the file contents
# as string. But the size variable holds size of the byte array without this # as string. But the size variable holds size of the byte array without this
# null byte. # null byte.
# Usage: set(options APPEND NULL_TERMINATE)
# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG") set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE)
function(BIN2H) # cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN})
set(options APPEND NULL_TERMINATE)
set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE)
cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN})
# reads source file contents as hex string # reads source file contents as hex string
file(READ ${BIN2H_SOURCE_FILE} hexString HEX) file(READ ${BIN2H_SOURCE_FILE} hexString HEX)
string(LENGTH ${hexString} hexStringLength) string(LENGTH ${hexString} hexStringLength)
# appends null byte if asked # appends null byte if asked
if(BIN2H_NULL_TERMINATE) if(BIN2H_NULL_TERMINATE)
set(hexString "${hexString}00") set(hexString "${hexString}00")
endif() endif()
# wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line) # wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line)
wrap_string(VARIABLE hexString AT_COLUMN 32) wrap_string(VARIABLE hexString AT_COLUMN 32)
math(EXPR arraySize "${hexStringLength} / 2") math(EXPR arraySize "${hexStringLength} / 2")
# adds '0x' prefix and comma suffix before and after every byte respectively # adds '0x' prefix and comma suffix before and after every byte respectively
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString}) string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString})
# removes trailing comma # removes trailing comma
string(REGEX REPLACE ", $" "" arrayValues ${arrayValues}) string(REGEX REPLACE ", $" "" arrayValues ${arrayValues})
# converts the variable name into proper C identifier # converts the variable name into proper C identifier
IF (${CMAKE_VERSION} GREATER 2.8.10) # fix for legacy cmake IF (${CMAKE_VERSION} GREATER 2.8.10) # fix for legacy cmake
string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
ENDIF() ENDIF()
string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
# declares byte array and the length variables # declares byte array and the length variables
set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };") set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };")
set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};") set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};")
set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n") set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n")
if(BIN2H_APPEND) if(BIN2H_APPEND)
file(APPEND ${BIN2H_HEADER_FILE} "${declarations}") file(APPEND ${BIN2H_HEADER_FILE} "${declarations}")
else() else()
file(WRITE ${BIN2H_HEADER_FILE} "${declarations}") file(WRITE ${BIN2H_HEADER_FILE} "${declarations}")
endif() endif()
endfunction()

74
libethash-cl/ethash_cl_miner.cpp

@ -140,12 +140,10 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
bool ethash_cl_miner::configureGPU( bool ethash_cl_miner::configureGPU(
bool _allowCPU, bool _allowCPU,
unsigned _extraGPUMemory, unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock boost::optional<uint64_t> _currentBlock
) )
{ {
s_allowCPU = _allowCPU; s_allowCPU = _allowCPU;
s_forceSingleChunk = _forceSingleChunk;
s_extraRequiredGPUMem = _extraGPUMemory; s_extraRequiredGPUMem = _extraGPUMemory;
// by default let's only consider the DAG of the first epoch // by default let's only consider the DAG of the first epoch
uint64_t dagSize = _currentBlock ? ethash_get_datasize(*_currentBlock) : 1073739904U; uint64_t dagSize = _currentBlock ? ethash_get_datasize(*_currentBlock) : 1073739904U;
@ -174,7 +172,6 @@ bool ethash_cl_miner::configureGPU(
} }
bool ethash_cl_miner::s_allowCPU = false; bool ethash_cl_miner::s_allowCPU = false;
bool ethash_cl_miner::s_forceSingleChunk = false;
unsigned ethash_cl_miner::s_extraRequiredGPUMem; unsigned ethash_cl_miner::s_extraRequiredGPUMem;
bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback) bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback)
@ -288,23 +285,6 @@ bool ethash_cl_miner::init(
string device_version = device.getInfo<CL_DEVICE_VERSION>(); string device_version = device.getInfo<CL_DEVICE_VERSION>();
ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")"); ETHCL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")");
// configure chunk number depending on max allocateable memory
cl_ulong result;
device.getInfo(CL_DEVICE_MAX_MEM_ALLOC_SIZE, &result);
if (s_forceSingleChunk || result >= _dagSize)
{
m_dagChunksNum = 1;
ETHCL_LOG(
((result <= _dagSize && s_forceSingleChunk) ? "Forcing single chunk. Good luck!\n" : "") <<
"Using 1 big chunk. Max OpenCL allocateable memory is " << result
);
}
else
{
m_dagChunksNum = 4;
ETHCL_LOG("Using 4 chunks. Max OpenCL allocateable memory is " << result);
}
if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
{ {
ETHCL_LOG("OpenCL 1.0 is not supported."); ETHCL_LOG("OpenCL 1.0 is not supported.");
@ -341,31 +321,32 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Printing program log"); ETHCL_LOG("Printing program log");
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str()); ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
} }
catch (cl::Error err) catch (cl::Error const& err)
{ {
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str()); ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false; return false;
} }
if (m_dagChunksNum == 1)
{
ETHCL_LOG("Loading single big chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash");
m_search_kernel = cl::Kernel(program, "ethash_search");
}
else
{
ETHCL_LOG("Loading chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash_chunks");
m_search_kernel = cl::Kernel(program, "ethash_search_chunks");
}
// create buffer for dag // create buffer for dag
if (m_dagChunksNum == 1) try
{ {
ETHCL_LOG("Creating one big buffer"); m_dagChunksNum = 1;
m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize)); m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize));
ETHCL_LOG("Created one big buffer for the DAG");
} }
else catch (cl::Error const& err)
{
int errCode = err.err();
if (errCode != CL_INVALID_BUFFER_SIZE || errCode != CL_MEM_OBJECT_ALLOCATION_FAILURE)
ETHCL_LOG("Allocating single buffer failed with: " << err.what() << "(" << errCode << ")");
cl_ulong result;
device.getInfo(CL_DEVICE_MAX_MEM_ALLOC_SIZE, &result);
ETHCL_LOG(
"Failed to allocate 1 big chunk. Max allocateable memory is "
<< result << ". Trying to allocate 4 chunks."
);
// The OpenCL kernel has a hard coded number of 4 chunks at the moment
m_dagChunksNum = 4;
for (unsigned i = 0; i < m_dagChunksNum; i++) for (unsigned i = 0; i < m_dagChunksNum; i++)
{ {
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation // TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
@ -376,6 +357,20 @@ bool ethash_cl_miner::init(
(i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7 (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7
)); ));
} }
}
if (m_dagChunksNum == 1)
{
ETHCL_LOG("Loading single big chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash");
m_search_kernel = cl::Kernel(program, "ethash_search");
}
else
{
ETHCL_LOG("Loading chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash_chunks");
m_search_kernel = cl::Kernel(program, "ethash_search_chunks");
}
// create buffer for header // create buffer for header
ETHCL_LOG("Creating buffer for header."); ETHCL_LOG("Creating buffer for header.");
@ -410,7 +405,7 @@ bool ethash_cl_miner::init(
m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t)); m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t));
} }
} }
catch (cl::Error err) catch (cl::Error const& err)
{ {
ETHCL_LOG(err.what() << "(" << err.err() << ")"); ETHCL_LOG(err.what() << "(" << err.err() << ")");
return false; return false;
@ -429,7 +424,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
}; };
queue<pending_batch> pending; queue<pending_batch> pending;
static uint32_t const c_zero = 0; // this can't be a static because in MacOSX OpenCL implementation a segfault occurs when a static is passed to OpenCL functions
uint32_t const c_zero = 0;
// update header constant buffer // update header constant buffer
m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header);
@ -503,7 +499,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
pre_return_event.wait(); pre_return_event.wait();
#endif #endif
} }
catch (cl::Error err) catch (cl::Error const& err)
{ {
ETHCL_LOG(err.what() << "(" << err.err() << ")"); ETHCL_LOG(err.what() << "(" << err.err() << ")");
} }

3
libethash-cl/ethash_cl_miner.h

@ -44,7 +44,6 @@ public:
static bool configureGPU( static bool configureGPU(
bool _allowCPU, bool _allowCPU,
unsigned _extraGPUMemory, unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock boost::optional<uint64_t> _currentBlock
); );
@ -79,8 +78,6 @@ private:
unsigned m_workgroup_size; unsigned m_workgroup_size;
bool m_opencl_1_1; bool m_opencl_1_1;
/// Force dag upload to GPU in a single chunk even if OpenCL thinks you can't do it. Use at your own risk.
static bool s_forceSingleChunk;
/// Allow CPU to appear as an OpenCL device or not. Default is false /// Allow CPU to appear as an OpenCL device or not. Default is false
static bool s_allowCPU; static bool s_allowCPU;
/// GPU memory required for other things, like window rendering e.t.c. /// GPU memory required for other things, like window rendering e.t.c.

24
libethash-cl/ethash_cl_miner_kernel.cl

@ -36,7 +36,7 @@ __constant uint2 const Keccak_f1600_RC[24] = {
(uint2)(0x80008008, 0x80000000), (uint2)(0x80008008, 0x80000000),
}; };
void keccak_f1600_round(uint2* a, uint r, uint out_size) static void keccak_f1600_round(uint2* a, uint r, uint out_size)
{ {
#if !__ENDIAN_LITTLE__ #if !__ENDIAN_LITTLE__
for (uint i = 0; i != 25; ++i) for (uint i = 0; i != 25; ++i)
@ -152,7 +152,7 @@ void keccak_f1600_round(uint2* a, uint r, uint out_size)
#endif #endif
} }
void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate) static void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate)
{ {
for (uint i = in_size; i != 25; ++i) for (uint i = in_size; i != 25; ++i)
{ {
@ -194,17 +194,17 @@ void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate)
#define countof(x) (sizeof(x) / sizeof(x[0])) #define countof(x) (sizeof(x) / sizeof(x[0]))
uint fnv(uint x, uint y) static uint fnv(uint x, uint y)
{ {
return x * FNV_PRIME ^ y; return x * FNV_PRIME ^ y;
} }
uint4 fnv4(uint4 x, uint4 y) static uint4 fnv4(uint4 x, uint4 y)
{ {
return x * FNV_PRIME ^ y; return x * FNV_PRIME ^ y;
} }
uint fnv_reduce(uint4 v) static uint fnv_reduce(uint4 v)
{ {
return fnv(fnv(fnv(v.x, v.y), v.z), v.w); return fnv(fnv(fnv(v.x, v.y), v.z), v.w);
} }
@ -227,7 +227,7 @@ typedef union
uint4 uint4s[128 / sizeof(uint4)]; uint4 uint4s[128 / sizeof(uint4)];
} hash128_t; } hash128_t;
hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate) static hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate)
{ {
hash64_t init; hash64_t init;
uint const init_size = countof(init.ulongs); uint const init_size = countof(init.ulongs);
@ -243,7 +243,7 @@ hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate)
return init; return init;
} }
uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, __global hash128_t const* g_dag1, __global hash128_t const* g_dag2, __global hash128_t const* g_dag3, uint isolate) static uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, __global hash128_t const* g_dag1, __global hash128_t const* g_dag2, __global hash128_t const* g_dag3, uint isolate)
{ {
uint4 mix = init; uint4 mix = init;
@ -277,7 +277,7 @@ uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global
uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) static uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate)
{ {
uint4 mix = init; uint4 mix = init;
@ -311,7 +311,7 @@ uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash12
} }
hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate) static hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate)
{ {
ulong state[25]; ulong state[25];
@ -330,7 +330,7 @@ hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate)
return hash; return hash;
} }
hash32_t compute_hash_simple( static hash32_t compute_hash_simple(
__constant hash32_t const* g_header, __constant hash32_t const* g_header,
__global hash128_t const* g_dag, __global hash128_t const* g_dag,
ulong nonce, ulong nonce,
@ -383,7 +383,7 @@ typedef union
} compute_hash_share; } compute_hash_share;
hash32_t compute_hash( static hash32_t compute_hash(
__local compute_hash_share* share, __local compute_hash_share* share,
__constant hash32_t const* g_header, __constant hash32_t const* g_header,
__global hash128_t const* g_dag, __global hash128_t const* g_dag,
@ -427,7 +427,7 @@ hash32_t compute_hash(
} }
hash32_t compute_hash_chunks( static hash32_t compute_hash_chunks(
__local compute_hash_share* share, __local compute_hash_share* share,
__constant hash32_t const* g_header, __constant hash32_t const* g_header,
__global hash128_t const* g_dag, __global hash128_t const* g_dag,

3
libethash/endian.h

@ -32,6 +32,9 @@
#include <libkern/OSByteOrder.h> #include <libkern/OSByteOrder.h>
#define ethash_swap_u32(input_) OSSwapInt32(input_) #define ethash_swap_u32(input_) OSSwapInt32(input_)
#define ethash_swap_u64(input_) OSSwapInt64(input_) #define ethash_swap_u64(input_) OSSwapInt64(input_)
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
#define ethash_swap_u32(input_) bswap32(input_)
#define ethash_swap_u64(input_) bswap64(input_)
#else // posix #else // posix
#include <byteswap.h> #include <byteswap.h>
#define ethash_swap_u32(input_) __bswap_32(input_) #define ethash_swap_u32(input_) __bswap_32(input_)

4
libethash/internal.c

@ -284,13 +284,13 @@ bool ethash_quick_check_difficulty(
ethash_h256_t const* header_hash, ethash_h256_t const* header_hash,
uint64_t const nonce, uint64_t const nonce,
ethash_h256_t const* mix_hash, ethash_h256_t const* mix_hash,
ethash_h256_t const* difficulty ethash_h256_t const* boundary
) )
{ {
ethash_h256_t return_hash; ethash_h256_t return_hash;
ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash); ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash);
return ethash_check_difficulty(&return_hash, difficulty); return ethash_check_difficulty(&return_hash, boundary);
} }
ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed) ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed)

21
libethash/internal.h

@ -46,27 +46,36 @@ static inline void ethash_h256_reset(ethash_h256_t* hash)
memset(hash, 0, 32); memset(hash, 0, 32);
} }
// Returns if hash is less than or equal to difficulty // Returns if hash is less than or equal to boundary (2^256/difficulty)
static inline bool ethash_check_difficulty( static inline bool ethash_check_difficulty(
ethash_h256_t const* hash, ethash_h256_t const* hash,
ethash_h256_t const* difficulty ethash_h256_t const* boundary
) )
{ {
// Difficulty is big endian // Boundary is big endian
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
if (ethash_h256_get(hash, i) == ethash_h256_get(difficulty, i)) { if (ethash_h256_get(hash, i) == ethash_h256_get(boundary, i)) {
continue; continue;
} }
return ethash_h256_get(hash, i) < ethash_h256_get(difficulty, i); return ethash_h256_get(hash, i) < ethash_h256_get(boundary, i);
} }
return true; return true;
} }
/**
* Difficulty quick check for POW preverification
*
* @param header_hash The hash of the header
* @param nonce The block's nonce
* @param mix_hash The mix digest hash
* @param boundary The boundary is defined as (2^256 / difficulty)
* @return true for succesful pre-verification and false otherwise
*/
bool ethash_quick_check_difficulty( bool ethash_quick_check_difficulty(
ethash_h256_t const* header_hash, ethash_h256_t const* header_hash,
uint64_t const nonce, uint64_t const nonce,
ethash_h256_t const* mix_hash, ethash_h256_t const* mix_hash,
ethash_h256_t const* difficulty ethash_h256_t const* boundary
); );
struct ethash_light { struct ethash_light {

4
libethcore/BlockInfo.cpp

@ -122,7 +122,7 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const
try try
{ {
if (_header.itemCount() != 15) if (_header.itemCount() != 15)
throw InvalidBlockHeaderItemCount(); BOOST_THROW_EXCEPTION(InvalidBlockHeaderItemCount());
parentHash = _header[field = 0].toHash<h256>(RLP::VeryStrict); parentHash = _header[field = 0].toHash<h256>(RLP::VeryStrict);
sha3Uncles = _header[field = 1].toHash<h256>(RLP::VeryStrict); sha3Uncles = _header[field = 1].toHash<h256>(RLP::VeryStrict);
coinbaseAddress = _header[field = 2].toHash<Address>(RLP::VeryStrict); coinbaseAddress = _header[field = 2].toHash<Address>(RLP::VeryStrict);
@ -146,7 +146,7 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const
} }
if (number > ~(unsigned)0) if (number > ~(unsigned)0)
throw InvalidNumber(); BOOST_THROW_EXCEPTION(InvalidNumber());
// check it hashes according to proof of work or that it's the genesis block. // check it hashes according to proof of work or that it's the genesis block.
if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this)) if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this))

6
libethcore/Common.h

@ -47,7 +47,8 @@ extern const unsigned c_databaseVersion;
enum class Network enum class Network
{ {
Olympic = 0, Olympic = 0,
Frontier = 1 Frontier = 1,
Turbo = 2
}; };
extern const Network c_network; extern const Network c_network;
@ -100,7 +101,8 @@ enum class ImportResult
{ {
Success = 0, Success = 0,
UnknownParent, UnknownParent,
FutureTime, FutureTimeKnown,
FutureTimeUnknown,
AlreadyInChain, AlreadyInChain,
AlreadyKnown, AlreadyKnown,
Malformed, Malformed,

6
libethcore/Ethash.cpp

@ -108,7 +108,10 @@ bool Ethash::verify(BlockInfo const& _header)
bool pre = preVerify(_header); bool pre = preVerify(_header);
#if !ETH_DEBUG #if !ETH_DEBUG
if (!pre) if (!pre)
{
cwarn << "Fail on preVerify";
return false; return false;
}
#endif #endif
auto result = EthashAux::eval(_header); auto result = EthashAux::eval(_header);
@ -386,13 +389,12 @@ bool Ethash::GPUMiner::configureGPU(
unsigned _deviceId, unsigned _deviceId,
bool _allowCPU, bool _allowCPU,
unsigned _extraGPUMemory, unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock boost::optional<uint64_t> _currentBlock
) )
{ {
s_platformId = _platformId; s_platformId = _platformId;
s_deviceId = _deviceId; s_deviceId = _deviceId;
return ethash_cl_miner::configureGPU(_allowCPU, _extraGPUMemory, _forceSingleChunk, _currentBlock); return ethash_cl_miner::configureGPU(_allowCPU, _extraGPUMemory, _currentBlock);
} }
#endif #endif

3
libethcore/Ethash.h

@ -88,7 +88,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo(); static std::string platformInfo();
static void listDevices() {} static void listDevices() {}
static bool configureGPU(unsigned, unsigned, bool, unsigned, bool, boost::optional<uint64_t>) { return false; } static bool configureGPU(unsigned, unsigned, bool, unsigned, boost::optional<uint64_t>) { return false; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected: protected:
void kickOff() override void kickOff() override
@ -122,7 +122,6 @@ public:
unsigned _deviceId, unsigned _deviceId,
bool _allowCPU, bool _allowCPU,
unsigned _extraGPUMemory, unsigned _extraGPUMemory,
bool _forceSingleChunk,
boost::optional<uint64_t> _currentBlock boost::optional<uint64_t> _currentBlock
); );
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); } static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, getNumDevices()); }

3
libethcore/Farm.h

@ -68,6 +68,7 @@ public:
void setWork(WorkPackage const& _wp) void setWork(WorkPackage const& _wp)
{ {
WriteGuard l(x_minerWork); WriteGuard l(x_minerWork);
cdebug << "Farm::setWork()";
if (_wp.headerHash == m_work.headerHash) if (_wp.headerHash == m_work.headerHash)
return; return;
m_work = _wp; m_work = _wp;
@ -94,6 +95,7 @@ public:
void stop() void stop()
{ {
WriteGuard l(x_minerWork); WriteGuard l(x_minerWork);
cdebug << "Farm::stop()";
m_miners.clear(); m_miners.clear();
m_work.reset(); m_work.reset();
m_isMining = false; m_isMining = false;
@ -175,6 +177,7 @@ private:
bool start() bool start()
{ {
WriteGuard l(x_minerWork); WriteGuard l(x_minerWork);
cdebug << "start()";
if (!m_miners.empty() && !!std::dynamic_pointer_cast<MinerType>(m_miners[0])) if (!m_miners.empty() && !!std::dynamic_pointer_cast<MinerType>(m_miners[0]))
return true; return true;
m_miners.clear(); m_miners.clear();

16
libethcore/ICAP.cpp

@ -71,7 +71,7 @@ ICAP ICAP::decoded(std::string const& _encoded)
std::string data; std::string data;
std::tie(country, data) = fromIBAN(_encoded); std::tie(country, data) = fromIBAN(_encoded);
if (country != "XE") if (country != "XE")
throw InvalidICAP(); BOOST_THROW_EXCEPTION(InvalidICAP());
if (data.size() == 30) if (data.size() == 30)
{ {
ret.m_type = Direct; ret.m_type = Direct;
@ -88,10 +88,10 @@ ICAP ICAP::decoded(std::string const& _encoded)
ret.m_client = data.substr(7); ret.m_client = data.substr(7);
} }
else else
throw InvalidICAP(); BOOST_THROW_EXCEPTION(InvalidICAP());
} }
else else
throw InvalidICAP(); BOOST_THROW_EXCEPTION(InvalidICAP());
return ret; return ret;
} }
@ -101,7 +101,7 @@ std::string ICAP::encoded() const
if (m_type == Direct) if (m_type == Direct)
{ {
if (!!m_direct[0]) if (!!m_direct[0])
throw InvalidICAP(); BOOST_THROW_EXCEPTION(InvalidICAP());
std::string d = toBase36<Address::size>(m_direct); std::string d = toBase36<Address::size>(m_direct);
while (d.size() < 30) while (d.size() < 30)
d = "0" + d; d = "0" + d;
@ -118,11 +118,11 @@ std::string ICAP::encoded() const
m_institution.size() != 4 || m_institution.size() != 4 ||
m_client.size() != 9 m_client.size() != 9
) )
throw InvalidICAP(); BOOST_THROW_EXCEPTION(InvalidICAP());
return iban("XE", m_asset + m_institution + m_client); return iban("XE", m_asset + m_institution + m_client);
} }
else else
throw InvalidICAP(); BOOST_THROW_EXCEPTION(InvalidICAP());
} }
pair<Address, bytes> ICAP::lookup(std::function<bytes(Address, bytes)> const& _call, Address const& _reg) const pair<Address, bytes> ICAP::lookup(std::function<bytes(Address, bytes)> const& _call, Address const& _reg) const
@ -149,9 +149,9 @@ pair<Address, bytes> ICAP::lookup(std::function<bytes(Address, bytes)> const& _c
else if (m_institution[0] != 'X') else if (m_institution[0] != 'X')
return make_pair(resolve(m_institution + "/" + m_client), bytes()); return make_pair(resolve(m_institution + "/" + m_client), bytes());
else else
throw InterfaceNotSupported("ICAP::lookup(), bad institution"); BOOST_THROW_EXCEPTION(InterfaceNotSupported("ICAP::lookup(), bad institution"));
} }
throw InterfaceNotSupported("ICAP::lookup(), bad asset"); BOOST_THROW_EXCEPTION(InterfaceNotSupported("ICAP::lookup(), bad asset"));
} }
} }

165
libethcore/KeyManager.cpp

@ -31,7 +31,7 @@ using namespace dev;
using namespace eth; using namespace eth;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
KeyManager::KeyManager(std::string const& _keysFile, std::string const& _secretsPath): KeyManager::KeyManager(string const& _keysFile, string const& _secretsPath):
m_keysFile(_keysFile), m_store(_secretsPath) m_keysFile(_keysFile), m_store(_secretsPath)
{} {}
@ -43,13 +43,13 @@ bool KeyManager::exists() const
return !contents(m_keysFile + ".salt").empty() && !contents(m_keysFile).empty(); return !contents(m_keysFile + ".salt").empty() && !contents(m_keysFile).empty();
} }
void KeyManager::create(std::string const& _pass) void KeyManager::create(string const& _pass)
{ {
m_password = asString(h256::random().asBytes()); m_defaultPasswordDeprecated = asString(h256::random().asBytes());
write(_pass, m_keysFile); write(_pass, m_keysFile);
} }
bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function<string()> const& _pass, KDF _kdf) bool KeyManager::recode(Address const& _address, string const& _newPass, string const& _hint, function<string()> const& _pass, KDF _kdf)
{ {
noteHint(_newPass, _hint); noteHint(_newPass, _hint);
h128 u = uuid(_address); h128 u = uuid(_address);
@ -61,10 +61,10 @@ bool KeyManager::recode(Address const& _address, std::string const& _newPass, st
return true; return true;
} }
bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std::function<string()> const& _pass, KDF _kdf) bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, function<string()> const& _pass, KDF _kdf)
{ {
h128 u = uuid(_address); h128 u = uuid(_address);
std::string p; string p;
if (_newPass == SemanticPassword::Existing) if (_newPass == SemanticPassword::Existing)
p = getPassword(u, _pass); p = getPassword(u, _pass);
else if (_newPass == SemanticPassword::Master) else if (_newPass == SemanticPassword::Master)
@ -75,41 +75,47 @@ bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std:
return recode(_address, p, string(), _pass, _kdf); return recode(_address, p, string(), _pass, _kdf);
} }
bool KeyManager::load(std::string const& _pass) bool KeyManager::load(string const& _pass)
{ {
try { try
{
bytes salt = contents(m_keysFile + ".salt"); bytes salt = contents(m_keysFile + ".salt");
bytes encKeys = contents(m_keysFile); bytes encKeys = contents(m_keysFile);
m_key = h128(pbkdf2(_pass, salt, 262144, 16)); m_keysFileKey = h128(pbkdf2(_pass, salt, 262144, 16));
bytes bs = decryptSymNoAuth(m_key, h128(), &encKeys); bytes bs = decryptSymNoAuth(m_keysFileKey, h128(), &encKeys);
RLP s(bs); RLP s(bs);
unsigned version = (unsigned)s[0]; unsigned version = unsigned(s[0]);
if (version == 1) if (version == 1)
{ {
for (auto const& i: s[1]) for (auto const& i: s[1])
{ {
m_keyInfo[m_addrLookup[(Address)i[0]] = (h128)i[1]] = KeyInfo((h256)i[2], (std::string)i[3]); h128 uuid(i[1]);
// cdebug << toString((Address)i[0]) << toString((h128)i[1]) << toString((h256)i[2]) << (std::string)i[3]; Address addr(i[0]);
m_addrLookup[addr] = uuid;
m_keyInfo[uuid] = KeyInfo(h256(i[2]), string(i[3]));
// cdebug << toString(addr) << toString(uuid) << toString((h256)i[2]) << (string)i[3];
} }
for (auto const& i: s[2]) for (auto const& i: s[2])
m_passwordInfo[(h256)i[0]] = (std::string)i[1]; m_passwordHint[h256(i[0])] = string(i[1]);
m_password = (string)s[3]; m_defaultPasswordDeprecated = string(s[3]);
} }
// cdebug << hashPassword(m_password) << toHex(m_password); // cdebug << hashPassword(m_password) << toHex(m_password);
m_cachedPasswords[hashPassword(m_password)] = m_password; cachePassword(m_defaultPasswordDeprecated);
// cdebug << hashPassword(asString(m_key.ref())) << m_key.hex(); // cdebug << hashPassword(asString(m_key.ref())) << m_key.hex();
m_cachedPasswords[hashPassword(asString(m_key.ref()))] = asString(m_key.ref()); cachePassword(asString(m_keysFileKey.ref()));
// cdebug << hashPassword(_pass) << _pass; // cdebug << hashPassword(_pass) << _pass;
m_cachedPasswords[m_master = hashPassword(_pass)] = _pass; m_master = hashPassword(_pass);
cachePassword(_pass);
return true; return true;
} }
catch (...) { catch (...)
{
return false; return false;
} }
} }
Secret KeyManager::secret(Address const& _address, function<std::string()> const& _pass) const Secret KeyManager::secret(Address const& _address, function<string()> const& _pass) const
{ {
auto it = m_addrLookup.find(_address); auto it = m_addrLookup.find(_address);
if (it == m_addrLookup.end()) if (it == m_addrLookup.end())
@ -117,12 +123,12 @@ Secret KeyManager::secret(Address const& _address, function<std::string()> const
return secret(it->second, _pass); return secret(it->second, _pass);
} }
Secret KeyManager::secret(h128 const& _uuid, function<std::string()> const& _pass) const Secret KeyManager::secret(h128 const& _uuid, function<string()> const& _pass) const
{ {
return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); })); return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); }));
} }
std::string KeyManager::getPassword(h128 const& _uuid, function<std::string()> const& _pass) const string KeyManager::getPassword(h128 const& _uuid, function<string()> const& _pass) const
{ {
auto kit = m_keyInfo.find(_uuid); auto kit = m_keyInfo.find(_uuid);
h256 ph; h256 ph;
@ -131,19 +137,19 @@ std::string KeyManager::getPassword(h128 const& _uuid, function<std::string()> c
return getPassword(ph, _pass); return getPassword(ph, _pass);
} }
std::string KeyManager::getPassword(h256 const& _passHash, function<std::string()> const& _pass) const string KeyManager::getPassword(h256 const& _passHash, function<string()> const& _pass) const
{ {
auto it = m_cachedPasswords.find(_passHash); auto it = m_cachedPasswords.find(_passHash);
if (it != m_cachedPasswords.end()) if (it != m_cachedPasswords.end())
return it->second; return it->second;
for (unsigned i = 0; i< 10; ++i) for (unsigned i = 0; i < 10; ++i)
{ {
std::string p = _pass(); string p = _pass();
if (p.empty()) if (p.empty())
break; break;
if (hashPassword(p) == _passHash || _passHash == UnknownPassword) if (_passHash == UnknownPassword || hashPassword(p) == _passHash)
{ {
m_cachedPasswords[hashPassword(p)] = p; cachePassword(p);
return p; return p;
} }
} }
@ -166,20 +172,20 @@ Address KeyManager::address(h128 const& _uuid) const
return Address(); return Address();
} }
h128 KeyManager::import(Secret const& _s, string const& _info, std::string const& _pass, string const& _passInfo) h128 KeyManager::import(Secret const& _s, string const& _accountName, string const& _pass, string const& _passwordHint)
{ {
Address addr = KeyPair(_s).address(); Address addr = KeyPair(_s).address();
auto passHash = hashPassword(_pass); auto passHash = hashPassword(_pass);
m_cachedPasswords[passHash] = _pass; cachePassword(_pass);
m_passwordInfo[passHash] = _passInfo; m_passwordHint[passHash] = _passwordHint;
auto uuid = m_store.importSecret(_s.asBytes(), _pass); auto uuid = m_store.importSecret(_s.asBytes(), _pass);
m_keyInfo[uuid] = KeyInfo{passHash, _info}; m_keyInfo[uuid] = KeyInfo{passHash, _accountName};
m_addrLookup[addr] = uuid; m_addrLookup[addr] = uuid;
write(m_keysFile); write(m_keysFile);
return uuid; return uuid;
} }
void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo) void KeyManager::importExisting(h128 const& _uuid, string const& _info, string const& _pass, string const& _passwordHint)
{ {
bytes key = m_store.secret(_uuid, [&](){ return _pass; }); bytes key = m_store.secret(_uuid, [&](){ return _pass; });
if (key.empty()) if (key.empty())
@ -187,17 +193,17 @@ void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std
Address a = KeyPair(Secret(key)).address(); Address a = KeyPair(Secret(key)).address();
auto passHash = hashPassword(_pass); auto passHash = hashPassword(_pass);
if (!m_cachedPasswords.count(passHash)) if (!m_cachedPasswords.count(passHash))
m_cachedPasswords[passHash] = _pass; cachePassword(_pass);
importExisting(_uuid, _info, a, passHash, _passInfo); importExisting(_uuid, _info, a, passHash, _passwordHint);
} }
void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, Address const& _address, h256 const& _passHash, std::string const& _passInfo) void KeyManager::importExisting(h128 const& _uuid, string const& _accountName, Address const& _address, h256 const& _passHash, string const& _passwordHint)
{ {
if (!m_passwordInfo.count(_passHash)) if (!m_passwordHint.count(_passHash))
m_passwordInfo[_passHash] = _passInfo; m_passwordHint[_passHash] = _passwordHint;
m_addrLookup[_address] = _uuid; m_addrLookup[_address] = _uuid;
m_keyInfo[_uuid].passHash = _passHash; m_keyInfo[_uuid].passHash = _passHash;
m_keyInfo[_uuid].info = _info; m_keyInfo[_uuid].accountName = _accountName;
write(m_keysFile); write(m_keysFile);
} }
@ -209,67 +215,92 @@ void KeyManager::kill(Address const& _a)
m_store.kill(id); m_store.kill(id);
} }
AddressHash KeyManager::accounts() const Addresses KeyManager::accounts() const
{ {
AddressHash ret; Addresses ret;
ret.reserve(m_addrLookup.size());
for (auto const& i: m_addrLookup) for (auto const& i: m_addrLookup)
if (m_keyInfo.count(i.second) > 0) if (m_keyInfo.count(i.second) > 0)
ret.insert(i.first); ret.push_back(i.first);
return ret; return ret;
} }
std::unordered_map<Address, std::pair<std::string, std::string>> KeyManager::accountDetails() const bool KeyManager::hasAccount(const Address& _address) const
{ {
std::unordered_map<Address, std::pair<std::string, std::string>> ret; return m_addrLookup.count(_address) && m_keyInfo.count(m_addrLookup.at(_address));
for (auto const& i: m_addrLookup) }
if (m_keyInfo.count(i.second) > 0)
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) : ""); string const& KeyManager::accountName(Address const& _address) const
return ret; {
try
{
return m_keyInfo.at(m_addrLookup.at(_address)).accountName;
}
catch (...)
{
return EmptyString;
}
} }
h256 KeyManager::hashPassword(std::string const& _pass) const string const& KeyManager::passwordHint(Address const& _address) const
{
try
{
return m_passwordHint.at(m_keyInfo.at(m_addrLookup.at(_address)).passHash);
}
catch (...)
{
return EmptyString;
}
}
h256 KeyManager::hashPassword(string const& _pass) const
{ {
// TODO SECURITY: store this a bit more securely; Scrypt perhaps? // TODO SECURITY: store this a bit more securely; Scrypt perhaps?
return h256(pbkdf2(_pass, asBytes(m_password), 262144, 32)); return h256(pbkdf2(_pass, asBytes(m_defaultPasswordDeprecated), 262144, 32));
}
void KeyManager::cachePassword(string const& _password) const
{
m_cachedPasswords[hashPassword(_password)] = _password;
} }
bool KeyManager::write(std::string const& _keysFile) const bool KeyManager::write(string const& _keysFile) const
{ {
if (!m_key) if (!m_keysFileKey)
return false; return false;
write(m_key, _keysFile); write(m_keysFileKey, _keysFile);
return true; return true;
} }
void KeyManager::write(std::string const& _pass, std::string const& _keysFile) const void KeyManager::write(string const& _pass, string const& _keysFile) const
{ {
bytes salt = h256::random().asBytes(); bytes salt = h256::random().asBytes();
writeFile(_keysFile + ".salt", salt); writeFile(_keysFile + ".salt", salt);
auto key = h128(pbkdf2(_pass, salt, 262144, 16)); auto key = h128(pbkdf2(_pass, salt, 262144, 16));
m_cachedPasswords[hashPassword(_pass)] = _pass; cachePassword(_pass);
m_master = hashPassword(_pass); m_master = hashPassword(_pass);
write(key, _keysFile); write(key, _keysFile);
} }
void KeyManager::write(h128 const& _key, std::string const& _keysFile) const void KeyManager::write(h128 const& _key, string const& _keysFile) const
{ {
RLPStream s(4); RLPStream s(4);
s << 1; s << 1; // version
s.appendList(m_addrLookup.size()); s.appendList(accounts().size());
for (auto const& i: m_addrLookup) for (auto const& address: accounts())
if (m_keyInfo.count(i.second))
{ {
auto ki = m_keyInfo.at(i.second); h128 id = uuid(address);
s.appendList(4) << i.first << i.second << ki.passHash << ki.info; auto const& ki = m_keyInfo.at(id);
s.appendList(4) << address << id << ki.passHash << ki.accountName;
} }
s.appendList(m_passwordInfo.size()); s.appendList(m_passwordHint.size());
for (auto const& i: m_passwordInfo) for (auto const& i: m_passwordHint)
s.appendList(2) << i.first << i.second; s.appendList(2) << i.first << i.second;
s.append(m_password); s.append(m_defaultPasswordDeprecated);
writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out())); writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out()));
m_key = _key; m_keysFileKey = _key;
m_cachedPasswords[hashPassword(defaultPassword())] = defaultPassword(); cachePassword(defaultPassword());
} }

68
libethcore/KeyManager.h

@ -23,8 +23,9 @@
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <libdevcrypto/SecretStore.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <libdevcore/CommonData.h>
#include <libdevcrypto/SecretStore.h>
namespace dev namespace dev
{ {
@ -35,14 +36,17 @@ class PasswordUnknown: public Exception {};
struct KeyInfo struct KeyInfo
{ {
KeyInfo() = default; KeyInfo() = default;
KeyInfo(h256 const& _passHash, std::string const& _info): passHash(_passHash), info(_info) {} KeyInfo(h256 const& _passHash, std::string const& _accountName): passHash(_passHash), accountName(_accountName) {}
h256 passHash; ///< Hash of the password or h256() if unknown. /// Hash of the password or h256() / UnknownPassword if unknown.
std::string info; ///< Name of the key, or JSON key info if begins with '{'. h256 passHash;
/// Name of the key, or JSON key info if begins with '{'.
std::string accountName;
}; };
static const h256 UnknownPassword; static h256 const UnknownPassword;
static const auto DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); }; /// Password query function that never returns a password.
static auto const DontKnowThrow = [](){ BOOST_THROW_EXCEPTION(PasswordUnknown()); return std::string(); };
enum class SemanticPassword enum class SemanticPassword
{ {
@ -53,12 +57,15 @@ enum class SemanticPassword
// TODO: This one is specifically for Ethereum, but we can make it generic in due course. // TODO: This one is specifically for Ethereum, but we can make it generic in due course.
// TODO: hidden-partition style key-store. // TODO: hidden-partition style key-store.
/** /**
* @brief High-level manager of keys for Ethereum. * @brief High-level manager of password-encrypted keys for Ethereum.
* Usage: * Usage:
* *
* Call exists() to check whether there is already a database. If so, get the master password from * Call exists() to check whether there is already a database. If so, get the master password from
* the user and call load() with it. If not, get a new master password from the user (get them to type * the user and call load() with it. If not, get a new master password from the user (get them to type
* it twice and keep some hint around!) and call create() with it. * it twice and keep some hint around!) and call create() with it.
*
* Uses a "key file" (and a corresponding .salt file) that contains encrypted information about the keys and
* a directory called "secrets path" that contains a file for each key.
*/ */
class KeyManager class KeyManager
{ {
@ -75,25 +82,37 @@ public:
void save(std::string const& _pass) const { write(_pass, m_keysFile); } void save(std::string const& _pass) const { write(_pass, m_keysFile); }
void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; } void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; }
void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordInfo[hashPassword(_pass)] = _hint; } void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordHint[hashPassword(_pass)] = _hint; }
bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); } bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); }
AddressHash accounts() const; /// @returns the list of account addresses.
std::unordered_map<Address, std::pair<std::string, std::string>> accountDetails() const; Addresses accounts() 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; } } /// @returns a hashset of all account addresses.
AddressHash accountsHash() const { return AddressHash() + accounts(); }
bool hasAccount(Address const& _address) const;
/// @returns the human-readable name or json-encoded info of the account for the given address.
std::string const& accountName(Address const& _address) const;
/// @returns the password hint for the account for the given address;
std::string const& passwordHint(Address const& _address) const;
/// @returns the uuid of the key for the address @a _a or the empty hash on error.
h128 uuid(Address const& _a) const; h128 uuid(Address const& _a) const;
/// @returns the address corresponding to the key with uuid @a _uuid or the zero address on error.
Address address(h128 const& _uuid) const; Address address(h128 const& _uuid) const;
h128 import(Secret const& _s, std::string const& _info, std::string const& _pass, std::string const& _passInfo); h128 import(Secret const& _s, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint);
h128 import(Secret const& _s, std::string const& _info) { return import(_s, _info, defaultPassword(), std::string()); } h128 import(Secret const& _s, std::string const& _accountName) { return import(_s, _accountName, defaultPassword(), std::string()); }
SecretStore& store() { return m_store; } SecretStore& store() { return m_store; }
void importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo); void importExisting(h128 const& _uuid, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint);
void importExisting(h128 const& _uuid, std::string const& _info) { importExisting(_uuid, _info, defaultPassword(), std::string()); } void importExisting(h128 const& _uuid, std::string const& _accountName) { importExisting(_uuid, _accountName, defaultPassword(), std::string()); }
void importExisting(h128 const& _uuid, std::string const& _info, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passInfo = std::string()); void importExisting(h128 const& _uuid, std::string const& _accountName, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passwordHint = std::string());
/// @returns the secret key associated with an address provided the password query
/// function @a _pass or the zero-secret key on error.
Secret secret(Address const& _address, std::function<std::string()> const& _pass = DontKnowThrow) const; Secret secret(Address const& _address, std::function<std::string()> const& _pass = DontKnowThrow) const;
/// @returns the secret key associated with the uuid of a key provided the password query
/// function @a _pass or the zero-secret key on error.
Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const; Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const;
bool recode(Address const& _address, SemanticPassword _newPass, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); bool recode(Address const& _address, SemanticPassword _newPass, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
@ -110,6 +129,9 @@ private:
std::string defaultPassword(std::function<std::string()> const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); } std::string defaultPassword(std::function<std::string()> const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); }
h256 hashPassword(std::string const& _pass) const; h256 hashPassword(std::string const& _pass) const;
/// Stores the password by its hash in the password cache.
void cachePassword(std::string const& _password) const;
// Only use if previously loaded ok. // Only use if previously loaded ok.
// @returns false if wasn't previously loaded ok. // @returns false if wasn't previously loaded ok.
bool write() const { return write(m_keysFile); } bool write() const { return write(m_keysFile); }
@ -118,11 +140,15 @@ private:
void write(h128 const& _key, std::string const& _keysFile) const; void write(h128 const& _key, std::string const& _keysFile) const;
// Ethereum keys. // Ethereum keys.
/// Mapping address -> key uuid.
std::unordered_map<Address, h128> m_addrLookup; std::unordered_map<Address, h128> m_addrLookup;
/// Mapping key uuid -> key info.
std::unordered_map<h128, KeyInfo> m_keyInfo; std::unordered_map<h128, KeyInfo> m_keyInfo;
std::unordered_map<h256, std::string> m_passwordInfo; /// Mapping password hash -> password hint.
std::unordered_map<h256, std::string> m_passwordHint;
// Passwords that we're storing. // Passwords that we're storing. Mapping password hash -> password.
mutable std::unordered_map<h256, std::string> m_cachedPasswords; mutable std::unordered_map<h256, std::string> m_cachedPasswords;
// DEPRECATED. // DEPRECATED.
@ -130,10 +156,10 @@ private:
// Now the default password is based off the key of the keys file directly, so this is redundant // Now the default password is based off the key of the keys file directly, so this is redundant
// except for the fact that people have existing keys stored with it. Leave for now until/unless // except for the fact that people have existing keys stored with it. Leave for now until/unless
// we have an upgrade strategy. // we have an upgrade strategy.
std::string m_password; std::string m_defaultPasswordDeprecated;
mutable std::string m_keysFile; mutable std::string m_keysFile;
mutable h128 m_key; mutable h128 m_keysFileKey;
mutable h256 m_master; mutable h256 m_master;
SecretStore m_store; SecretStore m_store;
}; };

6
libethcore/Params.cpp

@ -31,12 +31,12 @@ namespace eth
//--- BEGIN: AUTOGENERATED FROM github.com/ethereum/common/params.json //--- BEGIN: AUTOGENERATED FROM github.com/ethereum/common/params.json
u256 const c_genesisDifficulty = 131072; u256 const c_genesisDifficulty = 131072;
u256 const c_maximumExtraDataSize = 1024; u256 const c_maximumExtraDataSize = 1024;
u256 const c_genesisGasLimit = 3141592; u256 const c_genesisGasLimit = c_network == Network::Turbo ? 100000000 : 3141592;
u256 const c_minGasLimit = 125000; u256 const c_minGasLimit = c_network == Network::Turbo ? 100000000 : 125000;
u256 const c_gasLimitBoundDivisor = 1024; u256 const c_gasLimitBoundDivisor = 1024;
u256 const c_minimumDifficulty = 131072; u256 const c_minimumDifficulty = 131072;
u256 const c_difficultyBoundDivisor = 2048; u256 const c_difficultyBoundDivisor = 2048;
u256 const c_durationLimit = c_network == Network::Olympic ? 8 : 12; u256 const c_durationLimit = c_network == Network::Turbo ? 2 : c_network == Network::Olympic ? 8 : 12;
//--- END: AUTOGENERATED FROM /feeStructure.json //--- END: AUTOGENERATED FROM /feeStructure.json
} }

16
libethereum/BlockChain.cpp

@ -24,8 +24,6 @@
#if ETH_PROFILING_GPERF #if ETH_PROFILING_GPERF
#include <gperftools/profiler.h> #include <gperftools/profiler.h>
#endif #endif
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
#include <boost/timer.hpp> #include <boost/timer.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <test/JsonSpiritHeaders.h> #include <test/JsonSpiritHeaders.h>
@ -305,7 +303,7 @@ LastHashes BlockChain::lastHashes(unsigned _n) const
return m_lastLastHashes; return m_lastLastHashes;
} }
tuple<ImportRoute, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max)
{ {
// _bq.tick(*this); // _bq.tick(*this);
@ -315,6 +313,7 @@ tuple<ImportRoute, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _sta
h256s fresh; h256s fresh;
h256s dead; h256s dead;
h256s badBlocks; h256s badBlocks;
unsigned count = 0;
for (VerifiedBlock const& block: blocks) for (VerifiedBlock const& block: blocks)
if (!badBlocks.empty()) if (!badBlocks.empty())
badBlocks.push_back(block.verified.info.hash()); badBlocks.push_back(block.verified.info.hash());
@ -328,6 +327,7 @@ tuple<ImportRoute, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _sta
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles); r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.liveBlocks; fresh += r.liveBlocks;
dead += r.deadBlocks; dead += r.deadBlocks;
++count;
} }
catch (dev::eth::UnknownParent) catch (dev::eth::UnknownParent)
{ {
@ -353,7 +353,7 @@ tuple<ImportRoute, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _sta
badBlocks.push_back(block.verified.info.hash()); badBlocks.push_back(block.verified.info.hash());
} }
} }
return make_tuple(ImportRoute{dead, fresh}, _bq.doneDrain(badBlocks)); return make_tuple(ImportRoute{dead, fresh}, _bq.doneDrain(badBlocks), count);
} }
pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir) noexcept pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir) noexcept
@ -372,7 +372,7 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
} }
catch (FutureTime&) catch (FutureTime&)
{ {
return make_pair(ImportResult::FutureTime, ImportRoute()); return make_pair(ImportResult::FutureTimeKnown, ImportRoute());
} }
catch (Exception& ex) catch (Exception& ex)
{ {
@ -391,7 +391,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
try try
#endif #endif
{ {
block = verifyBlock(_block, m_onBad); block = verifyBlock(_block, m_onBad, _ir);
} }
#if ETH_CATCH #if ETH_CATCH
catch (Exception& ex) catch (Exception& ex)
@ -1072,7 +1072,7 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exce
try try
{ {
Strictness strictness = Strictness::CheckEverything; Strictness strictness = Strictness::CheckEverything;
if (_ir & ~ImportRequirements::ValidNonce) if (~_ir & ImportRequirements::ValidNonce)
strictness = Strictness::IgnoreNonce; strictness = Strictness::IgnoreNonce;
res.info.populate(_block, strictness); res.info.populate(_block, strictness);
@ -1122,6 +1122,6 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exce
++i; ++i;
} }
res.block = bytesConstRef(&_block); res.block = bytesConstRef(&_block);
return move(res); return res;
} }

9
libethereum/BlockChain.h

@ -21,15 +21,11 @@
#pragma once #pragma once
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <deque> #include <deque>
#include <chrono> #include <chrono>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <libdevcore/db.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
@ -41,7 +37,6 @@
#include "Transaction.h" #include "Transaction.h"
#include "BlockQueue.h" #include "BlockQueue.h"
#include "VerifiedBlock.h" #include "VerifiedBlock.h"
namespace ldb = leveldb;
namespace std namespace std
{ {
@ -117,7 +112,7 @@ public:
/// Sync the chain with any incoming blocks. All blocks should, if processed in order. /// Sync the chain with any incoming blocks. All blocks should, if processed in order.
/// @returns fresh blocks, dead blocks and true iff there are additional blocks to be processed waiting. /// @returns fresh blocks, dead blocks and true iff there are additional blocks to be processed waiting.
std::tuple<ImportRoute, bool> sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); std::tuple<ImportRoute, bool, unsigned> sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain.

800
libethereum/BlockChainSync.cpp

@ -0,0 +1,800 @@
/*
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 BlockChainSync.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "BlockChainSync.h"
#include <chrono>
#include <libdevcore/Common.h>
#include <libp2p/Host.h>
#include <libp2p/Session.h>
#include <libethcore/Exceptions.h>
#include <libethcore/Params.h>
#include "BlockChain.h"
#include "BlockQueue.h"
#include "EthereumPeer.h"
#include "EthereumHost.h"
#include "DownloadMan.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;
unsigned const c_chainReorgSize = 30000;
BlockChainSync::BlockChainSync(EthereumHost& _host):
m_host(_host)
{
m_bqRoomAvailable = host().bq().onRoomAvailable([this]()
{
RecursiveGuard l(x_sync);
continueSync();
});
}
BlockChainSync::~BlockChainSync()
{
RecursiveGuard l(x_sync);
abortSync();
}
DownloadMan const& BlockChainSync::downloadMan() const
{
return host().downloadMan();
}
DownloadMan& BlockChainSync::downloadMan()
{
return host().downloadMan();
}
void BlockChainSync::abortSync()
{
DEV_INVARIANT_CHECK;
host().foreachPeer([this](EthereumPeer* _p) { onPeerAborting(_p); return true; });
downloadMan().resetToChain(h256s());
DEV_INVARIANT_CHECK;
}
void BlockChainSync::onPeerStatus(EthereumPeer* _peer)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
if (_peer->m_genesisHash != host().chain().genesisHash())
_peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != host().protocolVersion() && _peer->m_protocolVersion != EthereumHost::c_oldProtocolVersion)
_peer->disable("Invalid protocol version.");
else if (_peer->m_networkId != host().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 (host().isBanned(_peer->session()->id()))
_peer->disable("Peer banned for previous bad behaviour.");
else
{
unsigned hashes = estimatedHashes();
_peer->m_expectedHashes = hashes;
onNewPeer(_peer);
}
DEV_INVARIANT_CHECK;
}
unsigned BlockChainSync::estimatedHashes() const
{
BlockInfo block = host().chain().info();
time_t lastBlockTime = (block.hash() == host().chain().genesisHash()) ? 1428192000 : (time_t)block.timestamp;
time_t now = time(0);
unsigned blockCount = c_chainReorgSize;
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;
return blockCount;
}
void BlockChainSync::requestBlocks(EthereumPeer* _peer)
{
if (host().bq().knownFull())
{
clog(NetAllDetail) << "Waiting for block queue before downloading blocks";
pauseSync();
_peer->setIdle();
return;
}
_peer->requestBlocks();
if (_peer->m_asking != Asking::Blocks) //nothing to download
{
peerDoneBlocks(_peer);
if (downloadMan().isComplete())
completeSync();
return;
}
}
void BlockChainSync::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
_peer->setIdle();
if (m_state != SyncState::Blocks && m_state != SyncState::NewBlocks)
clog(NetWarn) << "Unexpected Blocks received!";
if (m_state == SyncState::Waiting)
{
clog(NetAllDetail) << "Ignored blocks while waiting";
return;
}
if (itemCount == 0)
{
// Got to this peer's latest block - just give up.
peerDoneBlocks(_peer);
if (downloadMan().isComplete())
completeSync();
return;
}
unsigned success = 0;
unsigned future = 0;
unsigned unknown = 0;
unsigned got = 0;
unsigned repeated = 0;
u256 maxUnknownNumber = 0;
h256 maxUnknown;
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = BlockInfo::headerHash(_r[i].data());
if (_peer->m_sub.noteBlock(h))
{
_peer->addRating(10);
switch (host().bq().import(_r[i].data(), host().chain()))
{
case ImportResult::Success:
success++;
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
_peer->disable("Malformed block received.");
return;
case ImportResult::FutureTimeKnown:
future++;
break;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
got++;
break;
case ImportResult::FutureTimeUnknown:
future++; //Fall through
case ImportResult::UnknownParent:
{
unknown++;
if (m_state == SyncState::NewBlocks)
{
BlockInfo bi;
bi.populateFromHeader(_r[i][0]);
if (bi.number > maxUnknownNumber)
{
maxUnknownNumber = bi.number;
maxUnknown = h;
}
}
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 (host().bq().unknownFull())
{
clog(NetWarn) << "Too many unknown blocks, restarting sync";
restartSync();
return;
}
if (m_state == SyncState::NewBlocks && unknown > 0)
{
completeSync();
resetSyncFor(_peer, maxUnknown, std::numeric_limits<u256>::max()); //TODO: proper total difficuty
}
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)
{
if (downloadMan().isComplete())
completeSync();
else
requestBlocks(_peer); // Some of the blocks might have been downloaded by helping peers, proceed anyway
}
DEV_INVARIANT_CHECK;
}
void BlockChainSync::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{
DEV_INVARIANT_CHECK;
RecursiveGuard l(x_sync);
auto h = BlockInfo::headerHash(_r[0].data());
clog(NetMessageSummary) << "NewBlock: " << h;
if (_r.itemCount() != 2)
_peer->disable("NewBlock without 2 data fields.");
else
{
switch (host().bq().import(_r[0].data(), host().chain()))
{
case ImportResult::Success:
_peer->addRating(100);
break;
case ImportResult::FutureTimeKnown:
//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::FutureTimeUnknown:
case ImportResult::UnknownParent:
clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
resetSyncFor(_peer, h, _r[1].toInt<u256>());
break;
default:;
}
DEV_GUARDED(_peer->x_knownBlocks)
_peer->m_knownBlocks.insert(h);
}
DEV_INVARIANT_CHECK;
}
PV60Sync::PV60Sync(EthereumHost& _host):
BlockChainSync(_host)
{
resetSync();
}
SyncStatus PV60Sync::status() const
{
RecursiveGuard l(x_sync);
SyncStatus res;
res.state = m_state;
if (m_state == SyncState::Hashes)
{
res.hashesTotal = m_estimatedHashes;
res.hashesReceived = static_cast<unsigned>(m_syncingNeededBlocks.size());
res.hashesEstimated = true;
}
else if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks || m_state == SyncState::Waiting)
{
res.blocksTotal = downloadMan().chainSize();
res.blocksReceived = downloadMan().blocksGot().size();
}
return res;
}
void PV60Sync::setState(EthereumPeer* _peer, SyncState _s, bool _isSyncing, bool _needHelp)
{
bool changedState = (m_state != _s);
m_state = _s;
if (_isSyncing != (m_syncer == _peer) || (_isSyncing && changedState))
changeSyncer(_isSyncing ? _peer : nullptr, _needHelp);
else if (_s == SyncState::Idle)
changeSyncer(nullptr, _needHelp);
assert(!!m_syncer || _s == SyncState::Idle);
}
void PV60Sync::resetSync()
{
m_syncingLatestHash = h256();
m_syncingLastReceivedHash = h256();
m_syncingTotalDifficulty = 0;
m_syncingNeededBlocks.clear();
}
void PV60Sync::restartSync()
{
resetSync();
host().bq().clear();
if (isSyncing())
transition(m_syncer, SyncState::Idle);
}
void PV60Sync::completeSync()
{
if (isSyncing())
transition(m_syncer, SyncState::Idle);
}
void PV60Sync::pauseSync()
{
if (isSyncing())
setState(m_syncer, SyncState::Waiting, true);
}
void PV60Sync::continueSync()
{
transition(m_syncer, SyncState::Blocks);
}
void PV60Sync::onNewPeer(EthereumPeer* _peer)
{
setNeedsSyncing(_peer, _peer->m_latestHash, _peer->m_totalDifficulty);
}
void PV60Sync::transition(EthereumPeer* _peer, SyncState _s, bool _force, bool _needHelp)
{
clog(NetMessageSummary) << "Transition!" << EthereumHost::stateName(_s) << "from" << EthereumHost::stateName(m_state) << ", " << (isSyncing(_peer) ? "syncing" : "holding") << (needsSyncing(_peer) ? "& needed" : "");
if (m_state == SyncState::Idle && _s != SyncState::Idle)
_peer->m_requireTransactions = true;
RLPStream s;
if (_s == SyncState::Hashes)
{
if (m_state == SyncState::Idle)
{
if (isSyncing(_peer))
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
m_syncingLatestHash = _peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
setState(_peer, _s, true);
_peer->requestHashes(m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash);
DEV_INVARIANT_CHECK;
return;
}
else if (m_state == SyncState::Hashes)
{
if (!isSyncing(_peer))
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
setState(_peer, _s, true);
_peer->requestHashes(m_syncingLastReceivedHash);
DEV_INVARIANT_CHECK;
return;
}
}
else if (_s == SyncState::Blocks)
{
if (m_state == SyncState::Hashes)
{
if (!isSyncing(_peer))
{
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
return;
}
if (shouldGrabBlocks(_peer))
{
clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash << ", was" << host().latestBlockSent() << "]";
downloadMan().resetToChain(m_syncingNeededBlocks);
resetSync();
}
else
{
clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring.";
resetSync();
setState(_peer, SyncState::Idle, false);
return;
}
assert (isSyncing(_peer));
}
// run through into...
if (m_state == SyncState::Idle || m_state == SyncState::Hashes || m_state == SyncState::Blocks || m_state == SyncState::Waiting)
{
// Looks like it's the best yet for total difficulty. Set to download.
setState(_peer, SyncState::Blocks, isSyncing(_peer), _needHelp); // will kick off other peers to help if available.
requestBlocks(_peer);
DEV_INVARIANT_CHECK;
return;
}
}
else if (_s == SyncState::NewBlocks)
{
if (m_state != SyncState::Idle && m_state != SyncState::NewBlocks && m_state != SyncState::Waiting)
clog(NetWarn) << "Bad state: Asking new blocks while syncing!";
else
{
setState(_peer, SyncState::NewBlocks, true, _needHelp);
requestBlocks(_peer);
DEV_INVARIANT_CHECK;
return;
}
}
else if (_s == SyncState::Waiting)
{
if (m_state != SyncState::Blocks && m_state != SyncState::NewBlocks && m_state != SyncState::Hashes && m_state != SyncState::Waiting)
clog(NetWarn) << "Bad state: Entering waiting state while not downloading blocks!";
else
{
setState(_peer, SyncState::Waiting, isSyncing(_peer), _needHelp);
return;
}
}
else if (_s == SyncState::Idle)
{
host().foreachPeer([this](EthereumPeer* _p) { _p->setIdle(); return true; });
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)
{
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(_peer))
noteDoneBlocks(_peer, _force);
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
_peer->m_sub.doneFetch();
_peer->setIdle();
setState(_peer, SyncState::Idle, false);
}
else if (m_state == SyncState::Hashes)
{
clog(NetNote) << "Finishing hashes fetch...";
setState(_peer, SyncState::Idle, false);
}
// Otherwise it's fine. We don't care if it's Nothing->Nothing.
DEV_INVARIANT_CHECK;
return;
}
clog(NetWarn) << "Invalid state transition:" << EthereumHost::stateName(_s) << "from" << EthereumHost::stateName(m_state) << ", " << (isSyncing(_peer) ? "syncing" : "holding") << (needsSyncing(_peer) ? "& needed" : "");
}
void PV60Sync::resetSyncFor(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td)
{
setNeedsSyncing(_peer, _latestHash, _td);
}
void PV60Sync::setNeedsSyncing(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td)
{
_peer->m_latestHash = _latestHash;
_peer->m_totalDifficulty = _td;
if (_peer->m_latestHash)
noteNeedsSyncing(_peer);
_peer->session()->addNote("sync", string(isSyncing(_peer) ? "ongoing" : "holding") + (needsSyncing(_peer) ? " & needed" : ""));
}
bool PV60Sync::needsSyncing(EthereumPeer* _peer) const
{
return !!_peer->m_latestHash;
}
bool PV60Sync::isSyncing(EthereumPeer* _peer) const
{
return m_syncer == _peer;
}
bool PV60Sync::shouldGrabBlocks(EthereumPeer* _peer) const
{
auto td = _peer->m_totalDifficulty;
auto lh = _peer->m_latestHash;
auto ctd = host().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().chain().currentHash() == lh))
return false;
return true;
}
void PV60Sync::attemptSync(EthereumPeer* _peer)
{
if (m_state != SyncState::Idle)
{
clog(NetAllDetail) << "Can't sync with this peer - outstanding asks.";
return;
}
// if already done this, then ignore.
if (!needsSyncing(_peer))
{
clog(NetAllDetail) << "Already synced with this peer.";
return;
}
unsigned n = host().chain().number();
u256 td = host().chain().details().totalDifficulty;
if (host().bq().isActive())
td += host().bq().difficulty();
clog(NetAllDetail) << "Attempt chain-grab? Latest:" << (m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash) << ", number:" << n << ", TD:" << td << " versus " << _peer->m_totalDifficulty;
if (td >= _peer->m_totalDifficulty)
{
clog(NetAllDetail) << "No. Our chain is better.";
resetNeedsSyncing(_peer);
transition(_peer, SyncState::Idle);
}
else
{
clog(NetAllDetail) << "Yes. Their chain is better.";
m_estimatedHashes = _peer->m_expectedHashes - c_chainReorgSize;
transition(_peer, SyncState::Hashes);
}
}
void PV60Sync::noteNeedsSyncing(EthereumPeer* _peer)
{
// if already downloading hash-chain, ignore.
if (isSyncing())
{
clog(NetAllDetail) << "Sync in progress: Just set to help out.";
if (m_state == SyncState::Blocks)
requestBlocks(_peer);
}
else
// otherwise check to see if we should be downloading...
attemptSync(_peer);
}
void PV60Sync::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 && (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks))
host().foreachPeer([&](EthereumPeer* _p)
{
clog(NetNote) << "Getting help with downloading blocks";
if (_p != _syncer && _p->m_asking == Asking::Nothing)
transition(_p, m_state);
return true;
});
}
else
{
// start grabbing next hash chain if there is one.
host().foreachPeer([this](EthereumPeer* _p)
{
attemptSync(_p);
return !isSyncing();
});
if (!isSyncing())
{
if (m_state != SyncState::Idle)
setState(_syncer, SyncState::Idle);
clog(NetNote) << "No more peers to sync with.";
}
}
assert(!!m_syncer || m_state == SyncState::Idle);
}
void PV60Sync::peerDoneBlocks(EthereumPeer* _peer)
{
noteDoneBlocks(_peer, false);
}
void PV60Sync::noteDoneBlocks(EthereumPeer* _peer, bool _clemency)
{
resetNeedsSyncing(_peer);
if (downloadMan().isComplete())
{
// Done our chain-get.
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_peer->addRating(downloadMan().chainSize() / 100);
downloadMan().reset();
}
else if (isSyncing(_peer))
{
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) << downloadMan().remaining();
clog(NetWarn) << "WOULD BAN.";
// m_banned.insert(_peer->session()->id()); // We know who you are!
// _peer->disable("Peer sent hashes but was unable to provide the blocks.");
}
resetSync();
downloadMan().reset();
transition(_peer, SyncState::Idle);
}
_peer->m_sub.doneFetch();
}
void PV60Sync::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
_peer->setIdle();
if (!isSyncing(_peer))
{
clog(NetMessageSummary) << "Ignoring hashes since not syncing";
return;
}
if (_hashes.size() == 0)
{
transition(_peer, SyncState::Blocks);
return;
}
unsigned knowns = 0;
unsigned unknowns = 0;
for (unsigned i = 0; i < _hashes.size(); ++i)
{
auto h = _hashes[i];
auto status = host().bq().blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host().chain().isKnown(h))
{
clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download...";
assert (isSyncing(_peer));
transition(_peer, SyncState::Blocks);
return;
}
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
transition(_peer, SyncState::Idle);
return;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
m_syncingNeededBlocks.push_back(h);
}
else
knowns++;
m_syncingLastReceivedHash = h;
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLastReceivedHash;
if (m_syncingNeededBlocks.size() > _peer->m_expectedHashes)
{
_peer->disable("Too many hashes");
restartSync();
return;
}
// run through - ask for more.
transition(_peer, SyncState::Hashes);
DEV_INVARIANT_CHECK;
}
void PV60Sync::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
if (isSyncing())
{
clog(NetMessageSummary) << "Ignoring since we're already downloading.";
return;
}
clog(NetMessageDetail) << "Not syncing and new block hash discovered: syncing without help.";
unsigned knowns = 0;
unsigned unknowns = 0;
for (auto const& h: _hashes)
{
_peer->addRating(1);
DEV_GUARDED(_peer->x_knownBlocks)
_peer->m_knownBlocks.insert(h);
auto status = host().bq().blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host().chain().isKnown(h))
knowns++;
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
return;
}
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.";
downloadMan().resetToChain(m_syncingNeededBlocks);
resetSync();
transition(_peer, SyncState::NewBlocks, false, false);
}
DEV_INVARIANT_CHECK;
}
void PV60Sync::abortSync(EthereumPeer* _peer)
{
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
if (isSyncing(_peer))
{
host().foreachPeer([this](EthereumPeer* _p) { _p->setIdle(); return true; });
transition(_peer, SyncState::Idle, true);
}
DEV_INVARIANT_CHECK;
}
void PV60Sync::onPeerAborting(EthereumPeer* _peer)
{
RecursiveGuard l(x_sync);
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
abortSync(_peer);
DEV_INVARIANT_CHECK;
}
bool PV60Sync::invariants() const
{
if (m_state == SyncState::Idle && !!m_syncer)
return false;
if (m_state != SyncState::Idle && !m_syncer)
return false;
if (m_state == SyncState::Hashes)
{
bool hashes = false;
host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking == Asking::Hashes) hashes = true; return !hashes; });
if (!hashes)
return false;
if (!m_syncingLatestHash)
return false;
if (m_syncingNeededBlocks.empty() != (!m_syncingLastReceivedHash))
return false;
}
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)
{
bool blocks = false;
host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking == Asking::Blocks) blocks = true; return !blocks; });
if (!blocks)
return false;
if (downloadMan().isComplete())
return false;
}
if (m_state == SyncState::Idle)
{
bool busy = false;
host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking != Asking::Nothing && _p->m_asking != Asking::State) busy = true; return !busy; });
if (busy)
return false;
}
if (m_state == SyncState::Waiting && !host().bq().isActive())
return false;
return true;
}

278
libethereum/BlockChainSync.h

@ -0,0 +1,278 @@
/*
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 BlockChainSync.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <mutex>
#include <libdevcore/Guards.h>
#include <libdevcore/RangeMask.h>
#include <libethcore/Common.h>
#include <libp2p/Common.h>
#include "CommonNet.h"
#include "DownloadMan.h"
namespace dev
{
class RLPStream;
namespace eth
{
class EthereumHost;
class BlockQueue;
class EthereumPeer;
/**
* @brief Base BlockChain synchronization strategy class.
* Syncs to peers and keeps up to date. Base class handles blocks downloading but does not contain any details on state transfer logic.
*/
class BlockChainSync: public HasInvariants
{
public:
BlockChainSync(EthereumHost& _host);
virtual ~BlockChainSync();
void abortSync(); ///< Abort all sync activity
DownloadMan const& downloadMan() const;
DownloadMan& downloadMan();
/// @returns true is Sync is in progress
virtual bool isSyncing() const = 0;
/// Called by peer to report status
virtual void onPeerStatus(EthereumPeer* _peer);
/// Called by peer once it has new blocks during syn
virtual void onPeerBlocks(EthereumPeer* _peer, RLP const& _r);
/// Called by peer once it has new blocks
virtual void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r);
/// Called by peer once it has new hashes
virtual void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) = 0;
/// Called by peer once it has another sequential block of hashes during sync
virtual void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) = 0;
/// Called by peer when it is disconnecting
virtual void onPeerAborting(EthereumPeer* _peer) = 0;
/// @returns Synchonization status
virtual SyncStatus status() const = 0;
static char const* stateName(SyncState _s) { return s_stateNames[static_cast<int>(_s)]; }
protected:
//To be implemented in derived classes:
/// New valid peer appears
virtual void onNewPeer(EthereumPeer* _peer) = 0;
/// Peer done downloading blocks
virtual void peerDoneBlocks(EthereumPeer* _peer) = 0;
/// Resume downloading after witing state
virtual void continueSync() = 0;
/// Restart sync
virtual void restartSync() = 0;
/// Called after all blocks have been donloaded
virtual void completeSync() = 0;
/// Enter waiting state
virtual void pauseSync() = 0;
/// Restart sync for given peer
virtual void resetSyncFor(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td) = 0;
EthereumHost& host() { return m_host; }
EthereumHost const& host() const { return m_host; }
/// Estimates max number of hashes peers can give us.
unsigned estimatedHashes() const;
/// Request blocks from peer if needed
void requestBlocks(EthereumPeer* _peer);
protected:
Handler m_bqRoomAvailable; ///< Triggered once block queue
mutable RecursiveMutex x_sync;
SyncState m_state = SyncState::Idle; ///< Current sync state
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
private:
static char const* const s_stateNames[static_cast<int>(SyncState::Size)];
bool invariants() const override = 0;
EthereumHost& m_host;
HashDownloadMan m_hashMan;
};
/**
* @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash downaload is complete
* Syncs to peers and keeps up to date
*/
/**
* Transitions:
*
* Idle->Hashes
* Triggered when:
* * A new peer appears that we can sync to
* * Transtition to Idle, there are peers we can sync to
* Effects:
* * Set chain sync (m_syncingTotalDifficulty, m_syncingLatestHash, m_syncer)
* * Requests hashes from m_syncer
*
* Hashes->Idle
* Triggered when:
* * Received too many hashes
* * Received 0 total hashes from m_syncer
* * m_syncer aborts
* Effects:
* In case of too many hashes sync is reset
*
* Hashes->Blocks
* Triggered when:
* * Received known hash from m_syncer
* * Received 0 hashes from m_syncer and m_syncingTotalBlocks not empty
* Effects:
* * Set up download manager, clear m_syncingTotalBlocks. Set all peers to help with downloading if they can
*
* Blocks->Idle
* Triggered when:
* * m_syncer aborts
* * m_syncer does not have required block
* * All blocks downloaded
* * Block qeueue is full with unknown blocks
* Effects:
* * Download manager is reset
*
* Blocks->Waiting
* Triggered when:
* * Block queue is full with known blocks
* Effects:
* * Stop requesting blocks from peers
*
* Waiting->Blocks
* Triggered when:
* * Block queue has space for new blocks
* Effects:
* * Continue requesting blocks from peers
*
* Idle->NewBlocks
* Triggered when:
* * New block hashes arrive
* Effects:
* * Set up download manager, clear m_syncingTotalBlocks. Download blocks from a single peer. If downloaded blocks have unknown parents, set the peer to sync
*
* NewBlocks->Idle
* Triggered when:
* * m_syncer aborts
* * m_syncer does not have required block
* * All new blocks downloaded
* * Block qeueue is full with unknown blocks
* Effects:
* * Download manager is reset
*
*/
class PV60Sync: public BlockChainSync
{
public:
PV60Sync(EthereumHost& _host);
/// @returns true is Sync is in progress
bool isSyncing() const override { return !!m_syncer; }
/// Called by peer once it has new hashes
void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) override;
/// Called by peer once it has another sequential block of hashes during sync
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) override;
/// Called by peer when it is disconnecting
void onPeerAborting(EthereumPeer* _peer) override;
/// @returns Sync status
SyncStatus status() const override;
protected:
void onNewPeer(EthereumPeer* _peer) override;
void continueSync() override;
void peerDoneBlocks(EthereumPeer* _peer) override;
void restartSync() override;
void completeSync() override;
void pauseSync() override;
void resetSyncFor(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td) override;
private:
/// Transition sync state in a particular direction. @param _peer Peer that is responsible for state tranfer
void transition(EthereumPeer* _peer, SyncState _s, bool _force = false, bool _needHelp = true);
/// Reset peer syncing requirements state.
void resetNeedsSyncing(EthereumPeer* _peer) { setNeedsSyncing(_peer, h256(), 0); }
/// Update peer syncing requirements state.
void setNeedsSyncing(EthereumPeer* _peer, h256 const& _latestHash, u256 const& _td);
/// Do we presently need syncing with this peer?
bool needsSyncing(EthereumPeer* _peer) const;
/// Check whether the session should bother grabbing blocks from a peer.
bool shouldGrabBlocks(EthereumPeer* _peer) const;
/// Attempt to begin syncing with the peer; first check the peer has a more difficlult chain to download, then start asking for hashes, then move to blocks
void attemptSync(EthereumPeer* _peer);
/// Update our syncing state
void setState(EthereumPeer* _peer, SyncState _s, bool _isSyncing = false, bool _needHelp = false);
/// Check if peer is main syncer
bool isSyncing(EthereumPeer* _peer) const;
/// Check if we need (re-)syncing with the peer.
void noteNeedsSyncing(EthereumPeer* _who);
/// Set main syncing peer
void changeSyncer(EthereumPeer* _syncer, bool _needHelp);
/// Called when peer done downloading blocks
void noteDoneBlocks(EthereumPeer* _who, bool _clemency);
/// Abort syncing for peer
void abortSync(EthereumPeer* _peer);
/// Reset hash chain syncing
void resetSync();
bool invariants() const override;
h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer.
h256 m_syncingLastReceivedHash; ///< Hash most recently received from peer.
h256 m_syncingLatestHash; ///< Latest block's hash of the peer we are syncing to, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty of the peer we aresyncing to, as of the current sync.
// TODO: switch to weak_ptr
EthereumPeer* m_syncer = nullptr; ///< Peer we are currently syncing with
};
}
}

7
libethereum/BlockDetails.h

@ -22,15 +22,10 @@
#pragma once #pragma once
#include <unordered_map> #include <unordered_map>
#pragma warning(push) #include <libdevcore/db.h>
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include "TransactionReceipt.h" #include "TransactionReceipt.h"
namespace ldb = leveldb;
namespace dev namespace dev
{ {

53
libethereum/BlockQueue.cpp

@ -36,6 +36,7 @@ const char* BlockQueueChannel::name() { return EthOrange "[]>"; }
#else #else
const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; } const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; }
#endif #endif
const char* BlockQueueTraceChannel::name() { return EthOrange "▣ ▶"; }
size_t const c_maxKnownCount = 100000; size_t const c_maxKnownCount = 100000;
size_t const c_maxKnownSize = 128 * 1024 * 1024; size_t const c_maxKnownSize = 128 * 1024 * 1024;
@ -81,6 +82,8 @@ void BlockQueue::clear()
m_unknownCount = 0; m_unknownCount = 0;
m_knownSize = 0; m_knownSize = 0;
m_knownCount = 0; m_knownCount = 0;
m_difficulty = 0;
m_drainingDifficulty = 0;
} }
void BlockQueue::verifierBody() void BlockQueue::verifierBody()
@ -181,14 +184,14 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
// Check if we already know this block. // Check if we already know this block.
h256 h = BlockInfo::headerHash(_block); h256 h = BlockInfo::headerHash(_block);
cblockq << "Queuing block" << h << "for import..."; clog(BlockQueueTraceChannel) << "Queuing block" << h << "for import...";
UpgradableGuard l(m_lock); UpgradableGuard l(m_lock);
if (m_readySet.count(h) || m_drainingSet.count(h) || m_unknownSet.count(h) || m_knownBad.count(h)) if (m_readySet.count(h) || m_drainingSet.count(h) || m_unknownSet.count(h) || m_knownBad.count(h))
{ {
// Already know about this one. // Already know about this one.
cblockq << "Already known."; clog(BlockQueueTraceChannel) << "Already known.";
return ImportResult::AlreadyKnown; return ImportResult::AlreadyKnown;
} }
@ -226,10 +229,12 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
time_t bit = (unsigned)bi.timestamp; time_t bit = (unsigned)bi.timestamp;
if (strftime(buf, 24, "%X", localtime(&bit)) == 0) if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
buf[0] = '\0'; // empty if case strftime fails buf[0] = '\0'; // empty if case strftime fails
cblockq << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf; clog(BlockQueueTraceChannel) << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf;
m_unknownSize += _block.size(); m_unknownSize += _block.size();
m_unknownCount++; m_unknownCount++;
return ImportResult::FutureTime; m_difficulty += bi.difficulty;
bool unknown = !m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash);
return unknown ? ImportResult::FutureTimeUnknown : ImportResult::FutureTimeKnown;
} }
else else
{ {
@ -244,10 +249,11 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
else if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash)) else if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash))
{ {
// We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on. // We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on.
cblockq << "OK - queued as unknown parent:" << bi.parentHash; clog(BlockQueueTraceChannel) << "OK - queued as unknown parent:" << bi.parentHash;
m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes())));
m_unknownSet.insert(h); m_unknownSet.insert(h);
m_unknownSize += _block.size(); m_unknownSize += _block.size();
m_difficulty += bi.difficulty;
m_unknownCount++; m_unknownCount++;
return ImportResult::UnknownParent; return ImportResult::UnknownParent;
@ -255,12 +261,13 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
else else
{ {
// If valid, append to blocks. // If valid, append to blocks.
cblockq << "OK - ready for chain insertion."; clog(BlockQueueTraceChannel) << "OK - ready for chain insertion.";
DEV_GUARDED(m_verification) DEV_GUARDED(m_verification)
m_unverified.push_back(UnverifiedBlock { h, bi.parentHash, _block.toBytes() }); m_unverified.push_back(UnverifiedBlock { h, bi.parentHash, _block.toBytes() });
m_moreToVerify.notify_one(); m_moreToVerify.notify_one();
m_readySet.insert(h); m_readySet.insert(h);
m_knownSize += _block.size(); m_knownSize += _block.size();
m_difficulty += bi.difficulty;
m_knownCount++; m_knownCount++;
noteReady_WITH_LOCK(h); noteReady_WITH_LOCK(h);
@ -350,13 +357,16 @@ bool BlockQueue::doneDrain(h256s const& _bad)
WriteGuard l(m_lock); WriteGuard l(m_lock);
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
m_drainingSet.clear(); m_drainingSet.clear();
m_difficulty -= m_drainingDifficulty;
m_drainingDifficulty = 0;
if (_bad.size()) if (_bad.size())
{ {
// at least one of them was bad. // at least one of them was bad.
m_knownBad += _bad; m_knownBad += _bad;
for (h256 const& b : _bad) for (h256 const& b : _bad)
updateBad(b); updateBad(b);
} return !m_readySet.empty(); }
return !m_readySet.empty();
} }
void BlockQueue::tick(BlockChain const& _bc) void BlockQueue::tick(BlockChain const& _bc)
@ -427,12 +437,14 @@ bool BlockQueue::unknownFull() const
void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max) void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
{ {
WriteGuard l(m_lock); bool wasFull = false;
DEV_WRITE_GUARDED(m_lock)
{
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
wasFull = knownFull();
if (m_drainingSet.empty()) if (m_drainingSet.empty())
{ {
bool wasFull = knownFull(); m_drainingDifficulty = 0;
DEV_GUARDED(m_verification) DEV_GUARDED(m_verification)
{ {
o_out.resize(min<unsigned>(_max, m_verified.size())); o_out.resize(min<unsigned>(_max, m_verified.size()));
@ -445,14 +457,15 @@ void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
// TODO: @optimise use map<h256, bytes> rather than vector<bytes> & set<h256>. // TODO: @optimise use map<h256, bytes> rather than vector<bytes> & set<h256>.
auto h = bs.verified.info.hash(); auto h = bs.verified.info.hash();
m_drainingSet.insert(h); m_drainingSet.insert(h);
m_drainingDifficulty += bs.verified.info.difficulty;
m_readySet.erase(h); m_readySet.erase(h);
m_knownSize -= bs.verified.block.size(); m_knownSize -= bs.verified.block.size();
m_knownCount--; m_knownCount--;
} }
}
}
if (wasFull && !knownFull()) if (wasFull && !knownFull())
m_onRoomAvailable(); m_onRoomAvailable();
}
} }
bool BlockQueue::invariants() const bool BlockQueue::invariants() const
@ -524,3 +537,19 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _
return _out; return _out;
} }
u256 BlockQueue::difficulty() const
{
UpgradableGuard l(m_lock);
return m_difficulty;
}
bool BlockQueue::isActive() const
{
UpgradableGuard l(m_lock);
if (m_readySet.empty() && m_drainingSet.empty())
DEV_GUARDED(m_verification)
if (m_verified.empty() && m_verifying.empty() && m_unverified.empty())
return false;
return true;
}

5
libethereum/BlockQueue.h

@ -42,6 +42,7 @@ namespace eth
class BlockChain; class BlockChain;
struct BlockQueueChannel: public LogChannel { static const char* name(); static const int verbosity = 4; }; struct BlockQueueChannel: public LogChannel { static const char* name(); static const int verbosity = 4; };
struct BlockQueueTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 7; };
#define cblockq dev::LogOutputStream<dev::eth::BlockQueueChannel, true>() #define cblockq dev::LogOutputStream<dev::eth::BlockQueueChannel, true>()
struct BlockQueueStatus struct BlockQueueStatus
@ -117,6 +118,8 @@ public:
bool knownFull() const; bool knownFull() const;
bool unknownFull() const; bool unknownFull() const;
u256 difficulty() const; // Total difficulty of queueud blocks
bool isActive() const;
private: private:
struct UnverifiedBlock struct UnverifiedBlock
@ -158,6 +161,8 @@ private:
std::atomic<size_t> m_knownSize; ///< Tracks total size in bytes of all known blocks; std::atomic<size_t> m_knownSize; ///< Tracks total size in bytes of all known blocks;
std::atomic<size_t> m_unknownCount; ///< Tracks total count of unknown blocks. Used to avoid additional syncing std::atomic<size_t> m_unknownCount; ///< Tracks total count of unknown blocks. Used to avoid additional syncing
std::atomic<size_t> m_knownCount; ///< Tracks total count of known blocks. Used to avoid additional syncing std::atomic<size_t> m_knownCount; ///< Tracks total count of known blocks. Used to avoid additional syncing
u256 m_difficulty; ///< Total difficulty of blocks in the queue
u256 m_drainingDifficulty; ///< Total difficulty of blocks in draining
}; };
std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s); std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);

3
libethereum/CMakeLists.txt

@ -12,7 +12,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${DB_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
if (JSONRPC) if (JSONRPC)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
@ -31,7 +31,6 @@ target_link_libraries(${EXECUTABLE} whisper)
target_link_libraries(${EXECUTABLE} p2p) target_link_libraries(${EXECUTABLE} p2p)
target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} secp256k1)
if (JSONRPC) if (JSONRPC)

6
libethereum/CanonBlockChain.h

@ -21,11 +21,6 @@
#pragma once #pragma once
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <mutex> #include <mutex>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
@ -35,7 +30,6 @@
#include "BlockDetails.h" #include "BlockDetails.h"
#include "Account.h" #include "Account.h"
#include "BlockChain.h" #include "BlockChain.h"
namespace ldb = leveldb;
namespace dev namespace dev
{ {

71
libethereum/Client.cpp

@ -355,6 +355,14 @@ bool Client::isSyncing() const
return false; return false;
} }
bool Client::isMajorSyncing() const
{
// TODO: only return true if it is actually doing a proper chain sync.
if (auto h = m_host.lock())
return h->isSyncing();
return false;
}
void Client::startedWorking() void Client::startedWorking()
{ {
// Synchronise the state according to the head of the block chain. // Synchronise the state according to the head of the block chain.
@ -612,22 +620,23 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution)
} }
unsigned static const c_syncMin = 1; unsigned static const c_syncMin = 1;
unsigned static const c_syncMax = 100; unsigned static const c_syncMax = 1000;
double static const c_targetDuration = 1; double static const c_targetDuration = 1;
void Client::syncBlockQueue() void Client::syncBlockQueue()
{ {
ImportRoute ir;
cwork << "BQ ==> CHAIN ==> STATE"; cwork << "BQ ==> CHAIN ==> STATE";
ImportRoute ir;
unsigned count;
boost::timer t; boost::timer t;
tie(ir, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, m_syncAmount); tie(ir, m_syncBlockQueue, count) = m_bc.sync(m_bq, m_stateDB, m_syncAmount);
double elapsed = t.elapsed(); double elapsed = t.elapsed();
cnote << m_syncAmount << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (m_syncAmount / elapsed) << "blocks/s)"; cnote << count << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (count / elapsed) << "blocks/s)";
if (elapsed > c_targetDuration * 1.1 && m_syncAmount > c_syncMin) if (elapsed > c_targetDuration * 1.1 && count > c_syncMin)
m_syncAmount = max(c_syncMin, m_syncAmount * 9 / 10); m_syncAmount = max(c_syncMin, count * 9 / 10);
else if (elapsed < c_targetDuration * 0.9 && m_syncAmount < c_syncMax) else if (count == m_syncAmount && elapsed < c_targetDuration * 0.9 && m_syncAmount < c_syncMax)
m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1); m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1);
if (ir.liveBlocks.empty()) if (ir.liveBlocks.empty())
return; return;
@ -673,10 +682,10 @@ void Client::onDeadBlocks(h256s const& _blocks, h256Hash& io_changed)
// insert transactions that we are declaring the dead part of the chain // insert transactions that we are declaring the dead part of the chain
for (auto const& h: _blocks) for (auto const& h: _blocks)
{ {
clog(ClientNote) << "Dead block:" << h; clog(ClientTrace) << "Dead block:" << h;
for (auto const& t: m_bc.transactions(h)) for (auto const& t: m_bc.transactions(h))
{ {
clog(ClientNote) << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None); clog(ClientTrace) << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry); m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
} }
} }
@ -690,10 +699,10 @@ void Client::onNewBlocks(h256s const& _blocks, h256Hash& io_changed)
// remove transactions from m_tq nicely rather than relying on out of date nonce later on. // remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: _blocks) for (auto const& h: _blocks)
{ {
clog(ClientChat) << "Live block:" << h; clog(ClientTrace) << "Live block:" << h;
for (auto const& th: m_bc.transactionHashes(h)) for (auto const& th: m_bc.transactionHashes(h))
{ {
clog(ClientNote) << "Safely dropping transaction " << th; clog(ClientTrace) << "Safely dropping transaction " << th;
m_tq.drop(th); m_tq.drop(th);
} }
} }
@ -709,7 +718,7 @@ void Client::restartMining()
{ {
// RESTART MINING // RESTART MINING
if (!m_bq.items().first) if (!isMajorSyncing())
{ {
bool preChanged = false; bool preChanged = false;
State newPreMine; State newPreMine;
@ -731,7 +740,7 @@ void Client::restartMining()
DEV_READ_GUARDED(x_postMine) DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending()) for (auto const& t: m_postMine.pending())
{ {
clog(ClientNote) << "Resubmitting post-mine transaction " << t; clog(ClientTrace) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry); auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
if (ir != ImportResult::Success) if (ir != ImportResult::Success)
onTransactionQueueReady(); onTransactionQueueReady();
@ -777,7 +786,7 @@ void Client::startMining()
void Client::rejigMining() void Client::rejigMining()
{ {
if ((wouldMine() || remoteActive()) && !m_bq.items().first && (!isChainBad() || mineOnBadChain()) /*&& (forceMining() || transactionsWaiting())*/) if ((wouldMine() || remoteActive()) && !isMajorSyncing() && (!isChainBad() || mineOnBadChain()) /*&& (forceMining() || transactionsWaiting())*/)
{ {
cnote << "Rejigging mining..."; cnote << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working) DEV_WRITE_GUARDED(x_working)
@ -892,7 +901,9 @@ State Client::asOf(h256 const& _block) const
{ {
try try
{ {
return State(m_stateDB, bc(), _block); State ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
} }
catch (Exception& ex) catch (Exception& ex)
{ {
@ -909,12 +920,36 @@ void Client::prepareForTransaction()
State Client::state(unsigned _txi, h256 _block) const State Client::state(unsigned _txi, h256 _block) const
{ {
return State(m_stateDB, m_bc, _block).fromPending(_txi); try
{
State ret(m_stateDB);
ret.populateFromChain(m_bc, _block);
return ret.fromPending(_txi);
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return State();
}
} }
eth::State Client::state(h256 _block) const State Client::state(h256 const& _block, PopulationStatistics* o_stats) const
{ {
return State(m_stateDB, m_bc, _block); try
{
State ret(m_stateDB);
PopulationStatistics s = ret.populateFromChain(m_bc, _block);
if (o_stats)
swap(s, *o_stats);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return State();
}
} }
eth::State Client::state(unsigned _txi) const eth::State Client::state(unsigned _txi) const

5
libethereum/Client.h

@ -147,7 +147,7 @@ public:
// [PRIVATE API - only relevant for base clients, not available in general] // [PRIVATE API - only relevant for base clients, not available in general]
dev::eth::State state(unsigned _txi, h256 _block) const; dev::eth::State state(unsigned _txi, h256 _block) const;
dev::eth::State state(h256 _block) const; dev::eth::State state(h256 const& _block, PopulationStatistics* o_stats = nullptr) const;
dev::eth::State state(unsigned _txi) const; dev::eth::State state(unsigned _txi) const;
/// Get the object representing the current state of Ethereum. /// Get the object representing the current state of Ethereum.
@ -219,6 +219,7 @@ public:
DownloadMan const* downloadMan() const; DownloadMan const* downloadMan() const;
bool isSyncing() const; bool isSyncing() const;
bool isMajorSyncing() const;
/// Sets the network id. /// Sets the network id.
void setNetworkId(u256 _n); void setNetworkId(u256 _n);
/// Clears pending transactions. Just for debug use. /// Clears pending transactions. Just for debug use.
@ -226,7 +227,7 @@ public:
/// Kills the blockchain. Just for debug use. /// Kills the blockchain. Just for debug use.
void killChain(); void killChain();
/// Retries all blocks with unknown parents. /// Retries all blocks with unknown parents.
void retryUnkonwn() { m_bq.retryAllUnknown(); } void retryUnknown() { m_bq.retryAllUnknown(); }
/// Get a report of activity. /// Get a report of activity.
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; } ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks. /// Set a JSONRPC server to which we can report bad blocks.

6
libethereum/CommonNet.h

@ -80,10 +80,8 @@ enum class Asking
enum class SyncState enum class SyncState
{ {
Idle, ///< Initial chain sync complete. Waiting for new packets Idle, ///< Initial chain sync complete. Waiting for new packets
WaitingQueue, ///< Block downloading paused. Waiting for block queue to process blocks and free space Waiting, ///< Block downloading paused. Waiting for block queue to process blocks and free space
HashesNegotiate, ///< Waiting for first hashes to arrive Hashes, ///< Downloading hashes from multiple peers over
HashesSingle, ///< Locked on and downloading hashes from a single peer
HashesParallel, ///< Downloading hashes from multiple peers over
Blocks, ///< Downloading blocks Blocks, ///< Downloading blocks
NewBlocks, ///< Downloading blocks learned from NewHashes packet NewBlocks, ///< Downloading blocks learned from NewHashes packet

593
libethereum/EthereumHost.cpp

@ -33,6 +33,8 @@
#include "BlockQueue.h" #include "BlockQueue.h"
#include "EthereumPeer.h" #include "EthereumPeer.h"
#include "DownloadMan.h" #include "DownloadMan.h"
#include "BlockChainSync.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -41,7 +43,7 @@ using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
unsigned const c_chainReorgSize = 30000; unsigned const c_chainReorgSize = 30000;
char const* const EthereumHost::s_stateNames[static_cast<int>(SyncState::Size)] = {"Idle", "WaitingQueue", "HashesNegotiate", "HashesSingle", "HashesParallel", "Blocks", "NewBlocks" }; char const* const EthereumHost::s_stateNames[static_cast<int>(SyncState::Size)] = {"Idle", "Waiting", "Hashes", "Blocks", "NewBlocks" };
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId): EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(), HostCapability<EthereumPeer>(),
@ -51,15 +53,11 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
m_bq (_bq), m_bq (_bq),
m_networkId (_networkId) m_networkId (_networkId)
{ {
setState(SyncState::HashesNegotiate);
m_latestBlockSent = _ch.currentHash(); m_latestBlockSent = _ch.currentHash();
m_hashMan.reset(m_chain.number() + 1);
m_bqRoomAvailable = m_bq.onRoomAvailable([this](){ m_continueSync = true; });
} }
EthereumHost::~EthereumHost() EthereumHost::~EthereumHost()
{ {
foreachPeer([](EthereumPeer* _p) { _p->abortSync(); });
} }
bool EthereumHost::ensureInitialised() bool EthereumHost::ensureInitialised()
@ -79,31 +77,13 @@ bool EthereumHost::ensureInitialised()
void EthereumHost::reset() void EthereumHost::reset()
{ {
foreachPeer([](EthereumPeer* _p) { _p->abortSync(); }); Guard l(x_sync);
m_man.resetToChain(h256s()); if (m_sync)
m_hashMan.reset(m_chain.number() + 1); m_sync->abortSync();
setState(SyncState::HashesNegotiate); m_sync.reset();
m_syncingLatestHash = h256();
m_syncingTotalDifficulty = 0;
m_latestBlockSent = h256(); m_latestBlockSent = h256();
m_transactionsSent.clear(); m_transactionsSent.clear();
m_hashes.clear();
}
void EthereumHost::resetSyncTo(h256 const& _h)
{
setState(SyncState::HashesNegotiate);
m_syncingLatestHash = _h;
}
void EthereumHost::setState(SyncState _s)
{
if (m_state != _s)
{
clog(NetAllDetail) << "SyncState changed from " << stateName(m_state) << " to " << stateName(_s);
m_state = _s;
}
} }
void EthereumHost::doWork() void EthereumHost::doWork()
@ -125,14 +105,7 @@ void EthereumHost::doWork()
} }
} }
if (m_continueSync) foreachPeer([](EthereumPeer* _p) { _p->tick(); return true; });
{
m_continueSync = false;
RecursiveGuard l(x_sync);
continueSync();
}
foreachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange; // return netChange;
// TODO: Figure out what to do with netChange. // TODO: Figure out what to do with netChange.
@ -174,24 +147,28 @@ void EthereumHost::maintainTransactions()
cnote << "Sent" << n << "transactions to " << _p->session()->info().clientVersion; cnote << "Sent" << n << "transactions to " << _p->session()->info().clientVersion;
} }
_p->m_requireTransactions = false; _p->m_requireTransactions = false;
return true;
}); });
} }
void EthereumHost::foreachPeer(std::function<void(EthereumPeer*)> const& _f) const void EthereumHost::foreachPeer(std::function<bool(EthereumPeer*)> const& _f) const
{ {
foreachPeerPtr([&](std::shared_ptr<EthereumPeer> _p) foreachPeerPtr([&](std::shared_ptr<EthereumPeer> _p)
{ {
if (_p) if (_p)
_f(_p.get()); return _f(_p.get());
return true;
}); });
} }
void EthereumHost::foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const void EthereumHost::foreachPeerPtr(std::function<bool(std::shared_ptr<EthereumPeer>)> const& _f) const
{ {
for (auto s: peerSessions()) for (auto s: peerSessions())
_f(s.first->cap<EthereumPeer>()); if (!_f(s.first->cap<EthereumPeer>()))
return;
for (auto s: peerSessions(c_oldProtocolVersion)) //TODO: remove once v61+ is common for (auto s: peerSessions(c_oldProtocolVersion)) //TODO: remove once v61+ is common
_f(s.first->cap<EthereumPeer>(c_oldProtocolVersion)); if (!_f(s.first->cap<EthereumPeer>(c_oldProtocolVersion)))
return;
} }
tuple<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<Session>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow) tuple<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<Session>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
@ -263,334 +240,63 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
} }
} }
void EthereumHost::onPeerStatus(EthereumPeer* _peer) BlockChainSync& EthereumHost::sync()
{ {
RecursiveGuard l(x_sync); if (m_sync)
DEV_INVARIANT_CHECK; return *m_sync; // We only chose sync strategy once
if (_peer->m_genesisHash != m_chain.genesisHash())
_peer->disable("Invalid genesis hash"); bool pv61 = false;
else if (_peer->m_protocolVersion != protocolVersion() && _peer->m_protocolVersion != c_oldProtocolVersion) foreachPeer([&](EthereumPeer* _p)
_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
{
unsigned estimatedHashes = estimateHashes();
if (_peer->m_protocolVersion == protocolVersion())
{ {
if (_peer->m_latestBlockNumber > m_chain.number()) if (_p->m_protocolVersion == protocolVersion())
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number(); pv61 = true;
if (_peer->m_expectedHashes > estimatedHashes) return !pv61;
_peer->disable("Too many hashes"); });
else if (needHashes() && m_hashMan.chainSize() < _peer->m_expectedHashes) m_sync.reset(pv61 ? new PV60Sync(*this) : new PV60Sync(*this));
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes); return *m_sync;
}
else
_peer->m_expectedHashes = estimatedHashes;
continueSync(_peer);
}
DEV_INVARIANT_CHECK;
} }
unsigned EthereumHost::estimateHashes() void EthereumHost::onPeerStatus(EthereumPeer* _peer)
{ {
BlockInfo block = m_chain.info(); Guard l(x_sync);
time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp; sync().onPeerStatus(_peer);
time_t now = time(0);
unsigned blockCount = c_chainReorgSize;
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;
return blockCount;
} }
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
RecursiveGuard l(x_sync); Guard l(x_sync);
if (_peer->m_syncHashNumber > 0) sync().onPeerHashes(_peer, _hashes);
_peer->m_syncHashNumber += _hashes.size();
_peer->setAsking(Asking::Nothing);
onPeerHashes(_peer, _hashes, false);
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete)
{
DEV_INVARIANT_CHECK;
if (_hashes.empty())
{
_peer->m_hashSub.doneFetch();
continueSync();
return;
}
bool syncByNumber = _peer->m_syncHashNumber;
if (!syncByNumber && !_complete && _peer->m_syncHash != m_syncingLatestHash)
{
// Obsolete hashes, discard
continueSync(_peer);
return;
}
unsigned knowns = 0;
unsigned unknowns = 0;
h256s neededBlocks;
unsigned firstNumber = _peer->m_syncHashNumber - _hashes.size();
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;
else
_peer->m_hashSub.noteHash(firstNumber + i, 1);
}
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)
{
clog(NetMessageSummary) << "Start new blocks download...";
m_syncingLatestHash = h256();
setState(SyncState::NewBlocks);
m_man.resetToChain(m_hashes);
m_hashes.clear();
m_hashMan.reset(m_chain.number() + 1);
continueSync(_peer);
}
else if (syncByNumber && m_hashMan.isComplete())
{
// Done our chain-get.
clog(NetNote) << "Hashes download complete.";
onPeerDoneHashes(_peer, false);
}
else if (m_hashes.size() > _peer->m_expectedHashes)
{
_peer->disable("Too many hashes");
m_hashes.clear();
m_syncingLatestHash = h256();
setState(SyncState::HashesNegotiate);
continueSync(); ///Try with some other peer, keep the chain
}
else
continueSync(_peer); /// Grab next hashes
DEV_INVARIANT_CHECK;
}
void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
{
assert(_peer->m_asking == Asking::Nothing);
m_syncingLatestHash = h256();
setState(SyncState::Blocks);
if (_peer->m_protocolVersion != protocolVersion() || _localChain)
{
m_man.resetToChain(m_hashes);
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
}
m_hashMan.reset(m_chain.number() + 1);
m_hashes.clear();
continueSync();
} }
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{ {
RecursiveGuard l(x_sync); Guard l(x_sync);
DEV_INVARIANT_CHECK; sync().onPeerBlocks(_peer, _r);
_peer->setAsking(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->m_sub.doneFetch();
_peer->setIdle();
return;
}
unsigned success = 0;
unsigned future = 0;
unsigned unknown = 0;
unsigned got = 0;
unsigned repeated = 0;
h256 lastUnknown;
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:
lastUnknown = h;
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_state == SyncState::NewBlocks && unknown > 0)
{
_peer->m_latestHash = lastUnknown;
resetSyncTo(lastUnknown);
}
continueSync(_peer);
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{ {
RecursiveGuard l(x_sync); Guard l(x_sync);
DEV_INVARIANT_CHECK; sync().onPeerNewHashes(_peer, _hashes);
if (isSyncing() || _peer->isConversing())
{
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return;
}
clog(NetNote) << "New block hash discovered: syncing without help.";
_peer->m_syncHashNumber = 0;
onPeerHashes(_peer, _hashes, true);
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{ {
RecursiveGuard l(x_sync); Guard l(x_sync);
DEV_INVARIANT_CHECK; sync().onPeerNewBlock(_peer, _r);
if ((isSyncing() || _peer->isConversing()) && m_state != SyncState::NewBlocks)
{
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;
resetSyncTo(h);;
sync = true;
}
}
break;
default:;
}
DEV_GUARDED(_peer->x_knownBlocks)
_peer->m_knownBlocks.insert(h);
if (sync)
continueSync();
}
DEV_INVARIANT_CHECK;
} }
void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r) void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
{ {
if (_peer->isCriticalSyncing())
{
clog(NetAllDetail) << "Ignoring transaction from peer we are syncing with";
return;
}
unsigned itemCount = _r.itemCount(); unsigned itemCount = _r.itemCount();
clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)"; clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)";
Guard l(_peer->x_knownTransactions); Guard l(_peer->x_knownTransactions);
for (unsigned i = 0; i < itemCount; ++i) for (unsigned i = 0; i < min<unsigned>(itemCount, 256); ++i) // process 256 transactions at most. TODO: much better solution.
{ {
auto h = sha3(_r[i].data()); auto h = sha3(_r[i].data());
_peer->m_knownTransactions.insert(h); _peer->m_knownTransactions.insert(h);
@ -615,206 +321,23 @@ void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
void EthereumHost::onPeerAborting(EthereumPeer* _peer) void EthereumHost::onPeerAborting(EthereumPeer* _peer)
{ {
RecursiveGuard l(x_sync); Guard l(x_sync);
if (_peer->isConversing()) if (m_sync)
{ m_sync->onPeerAborting(_peer);
_peer->setIdle();
if (_peer->isCriticalSyncing())
_peer->setRude();
continueSync();
}
}
void EthereumHost::continueSync()
{
if (m_state == SyncState::WaitingQueue)
setState(m_lastActiveState);
clog(NetAllDetail) << "Continuing sync for all peers";
foreachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking == Asking::Nothing)
continueSync(_p);
});
}
void EthereumHost::continueSync(EthereumPeer* _peer)
{
DEV_INVARIANT_CHECK;
assert(_peer->m_asking == Asking::Nothing);
bool otherPeerV60Sync = false;
bool otherPeerV61Sync = false;
if (needHashes())
{
if (!peerShouldGrabChain(_peer))
{
_peer->setIdle();
return;
}
foreachPeer([&](EthereumPeer* _p)
{
if (_p != _peer && _p->m_asking == Asking::Hashes)
{
if (_p->m_protocolVersion != protocolVersion())
otherPeerV60Sync = true; // Already have a peer downloading hash chain with old protocol, do nothing
else
otherPeerV61Sync = true; // Already have a peer downloading hash chain with V61+ protocol, join if supported
}
});
if (otherPeerV60Sync && !m_hashes.empty())
{
/// Downloading from other peer with v60 protocol, nothing else we can do
_peer->setIdle();
return;
}
if (otherPeerV61Sync && _peer->m_protocolVersion != protocolVersion())
{
/// Downloading from other peer with v61+ protocol which this peer does not support,
_peer->setIdle();
return;
}
if (_peer->m_protocolVersion == protocolVersion() && !m_hashMan.isComplete())
{
setState(SyncState::HashesParallel);
_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;
}
if (_peer->m_totalDifficulty >= m_syncingTotalDifficulty)
{
_peer->requestHashes(m_syncingLatestHash);
setState(SyncState::HashesSingle);
m_estimatedHashes = _peer->m_expectedHashes - (_peer->m_protocolVersion == protocolVersion() ? 0 : c_chainReorgSize);
}
else
_peer->setIdle();
}
}
else if (needBlocks())
{
if (m_man.isComplete())
{
// Done our chain-get.
setState(SyncState::Idle);
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();
_peer->setIdle();
return;
}
else if (peerCanHelp(_peer))
{
// Check block queue status
if (m_bq.unknownFull())
{
clog(NetWarn) << "Too many unknown blocks, restarting sync";
m_bq.clear();
reset();
continueSync();
}
else if (m_bq.knownFull())
{
clog(NetAllDetail) << "Waiting for block queue before downloading blocks";
m_lastActiveState = m_state;
setState(SyncState::WaitingQueue);
_peer->setIdle();
}
else
_peer->requestBlocks();
}
}
else
_peer->setIdle();
DEV_INVARIANT_CHECK;
}
bool EthereumHost::peerCanHelp(EthereumPeer* _peer) const
{
(void)_peer;
return true;
}
bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const
{
// this is only good for deciding whether to go ahead and grab a particular peer's hash chain,
// yet it's being used in determining whether to allow a peer help with downloading an existing
// chain of blocks.
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
{
// Early exit if this peer has proved unreliable.
if (_peer->isRude())
return false;
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() const bool EthereumHost::isSyncing() const
{ {
return m_state != SyncState::Idle; Guard l(x_sync);
if (!m_sync)
return false;
return m_sync->isSyncing();
} }
SyncStatus EthereumHost::status() const SyncStatus EthereumHost::status() const
{ {
RecursiveGuard l(x_sync); Guard l(x_sync);
SyncStatus res; if (!m_sync)
res.state = m_state; return SyncStatus();
if (m_state == SyncState::HashesParallel) return m_sync->status();
{
res.hashesReceived = m_hashMan.hashesGot().size();
res.hashesTotal = m_hashMan.chainSize();
}
else if (m_state == SyncState::HashesSingle)
{
res.hashesTotal = m_estimatedHashes;
res.hashesReceived = static_cast<unsigned>(m_hashes.size());
res.hashesEstimated = true;
}
else if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks || m_state == SyncState::WaitingQueue)
{
res.blocksTotal = m_man.chainSize();
res.blocksReceived = m_man.blocksGot().size();
}
return res;
}
bool EthereumHost::invariants() const
{
if (m_state == SyncState::HashesNegotiate && !m_hashes.empty())
return false;
if (needBlocks() && (m_syncingLatestHash || !m_hashes.empty()))
return false;
return true;
} }

75
libethereum/EthereumHost.h

@ -48,16 +48,16 @@ namespace eth
class TransactionQueue; class TransactionQueue;
class BlockQueue; class BlockQueue;
class BlockChainSync;
/** /**
* @brief The EthereumHost class * @brief The EthereumHost class
* @warning None of this is thread-safe. You have been warned. * @warning None of this is thread-safe. You have been warned.
* @doWork Syncs to peers and sends new blocks and transactions. * @doWork Syncs to peers and sends new blocks and transactions.
*/ */
class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker, HasInvariants class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker
{ {
public: public:
/// Start server, but don't listen. /// Start server, but don't listen.
EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId); EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId);
@ -71,82 +71,60 @@ public:
void reset(); void reset();
DownloadMan const& downloadMan() const { return m_man; } DownloadMan const& downloadMan() const { return m_man; }
DownloadMan& downloadMan() { return m_man; }
bool isSyncing() const; bool isSyncing() const;
bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); } bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; } void noteNewTransactions() { m_newTransactions = true; }
void noteNewBlocks() { m_newBlocks = true; } void noteNewBlocks() { m_newBlocks = true; }
void onPeerStatus(EthereumPeer* _peer); ///< Called by peer to report status BlockChain const& chain() const { return m_chain; }
void onPeerBlocks(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks during syn BlockQueue& bq() { return m_bq; }
void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks BlockQueue const& bq() const { return m_bq; }
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
void onPeerAborting(EthereumPeer* _peer); ///< Called by peer when it is disconnecting
DownloadMan& downloadMan() { return m_man; }
HashDownloadMan& hashDownloadMan() { return m_hashMan; }
BlockChain const& chain() { return m_chain; }
SyncStatus status() const; SyncStatus status() const;
h256 latestBlockSent() { return m_latestBlockSent; }
static char const* stateName(SyncState _s) { return s_stateNames[static_cast<int>(_s)]; } static char const* stateName(SyncState _s) { return s_stateNames[static_cast<int>(_s)]; }
static unsigned const c_oldProtocolVersion; static unsigned const c_oldProtocolVersion;
void foreachPeerPtr(std::function<bool(std::shared_ptr<EthereumPeer>)> const& _f) const;
void foreachPeer(std::function<bool(EthereumPeer*)> const& _f) const;
void onPeerStatus(EthereumPeer* _peer);
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes);
void onPeerBlocks(EthereumPeer* _peer, RLP const& _r);
void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes);
void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r);
void onPeerTransactions(EthereumPeer* _peer, RLP const& _r);
void onPeerAborting(EthereumPeer* _peer);
private: private:
static char const* const s_stateNames[static_cast<int>(SyncState::Size)]; static char const* const s_stateNames[static_cast<int>(SyncState::Size)];
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; }); 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; });
void foreachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void foreachPeer(std::function<void(EthereumPeer*)> const& _f) const;
void resetSyncTo(h256 const& _h);
bool needHashes() const { return m_state == SyncState::HashesNegotiate || m_state == SyncState::HashesSingle || m_state == SyncState::HashesParallel; }
bool needBlocks() const { return m_state == SyncState::Blocks || m_state == SyncState::NewBlocks; }
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork(); virtual void doWork() override;
void maintainTransactions(); void maintainTransactions();
void maintainBlocks(h256 const& _currentBlock); void maintainBlocks(h256 const& _currentBlock);
/// Get a bunch of needed blocks.
/// Removes them from our list of needed blocks.
/// @returns empty if there's no more blocks left to fetch, otherwise the blocks to fetch.
h256Hash neededBlocks(h256Hash const& _exclude);
/// Check to see if the network peer-state initialisation has happened. /// Check to see if the network peer-state initialisation has happened.
bool isInitialised() const { return (bool)m_latestBlockSent; } bool isInitialised() const { return (bool)m_latestBlockSent; }
/// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first.
bool ensureInitialised(); bool ensureInitialised();
virtual void onStarting() { startWorking(); } virtual void onStarting() override { startWorking(); }
virtual void onStopping() { stopWorking(); } virtual void onStopping() override { stopWorking(); }
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;
bool peerCanHelp(EthereumPeer* _peer) const;
unsigned estimateHashes();
void estimatePeerHashes(EthereumPeer* _peer);
void setState(SyncState _s);
bool invariants() const override; BlockChainSync& sync();
BlockChain const& m_chain; BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
Handler m_bqRoomAvailable;
u256 m_networkId; u256 m_networkId;
DownloadMan m_man;
HashDownloadMan m_hashMan;
h256 m_latestBlockSent; h256 m_latestBlockSent;
h256Hash m_transactionsSent; h256Hash m_transactionsSent;
@ -155,14 +133,9 @@ private:
bool m_newTransactions = false; bool m_newTransactions = false;
bool m_newBlocks = false; bool m_newBlocks = false;
mutable RecursiveMutex x_sync; mutable Mutex x_sync;
SyncState m_state = SyncState::Idle; ///< Current sync state DownloadMan m_man;
SyncState m_lastActiveState = SyncState::Idle; ///< Saved state before entering waiting queue mode std::unique_ptr<BlockChainSync> m_sync;
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 PV60 chain downloading and catching up to a particular unknown
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
bool m_continueSync = false; ///< True when the block queue has processed a block; we should restart grabbing blocks.
}; };
} }

56
libethereum/EthereumPeer.cpp

@ -30,15 +30,28 @@
#include "EthereumHost.h" #include "EthereumHost.h"
#include "TransactionQueue.h" #include "TransactionQueue.h"
#include "BlockQueue.h" #include "BlockQueue.h"
#include "BlockChainSync.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace p2p; using namespace p2p;
string toString(Asking _a)
{
switch (_a)
{
case Asking::Blocks: return "Blocks";
case Asking::Hashes: return "Hashes";
case Asking::Nothing: return "Nothing";
case Asking::State: return "State";
}
return "?";
}
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, CapDesc const& _cap): EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, CapDesc const& _cap):
Capability(_s, _h, _i), Capability(_s, _h, _i),
m_sub(host()->downloadMan()), m_sub(host()->downloadMan()),
m_hashSub(host()->hashDownloadMan()),
m_peerCapabilityVersion(_cap.second) m_peerCapabilityVersion(_cap.second)
{ {
session()->addNote("manners", isRude() ? "RUDE" : "nice"); session()->addNote("manners", isRude() ? "RUDE" : "nice");
@ -48,7 +61,11 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap
EthereumPeer::~EthereumPeer() EthereumPeer::~EthereumPeer()
{ {
clog(NetMessageSummary) << "Aborting Sync :-("; if (m_asking != Asking::Nothing)
{
cnote << "Peer aborting while being asked for " << ::toString(m_asking);
setRude();
}
abortSync(); abortSync();
} }
@ -57,8 +74,20 @@ bool EthereumPeer::isRude() const
return repMan().isRude(*session(), name()); return repMan().isRude(*session(), name());
} }
unsigned EthereumPeer::askOverride() const
{
std::string static const badGeth = "Geth/v0.9.27";
if (session()->info().clientVersion.substr(0, badGeth.size()) == badGeth)
return 1;
bytes const& d = repMan().data(*session(), name());
return d.empty() ? c_maxBlocksAsk : RLP(d).toInt<unsigned>(RLP::LaisezFaire);
}
void EthereumPeer::setRude() void EthereumPeer::setRude()
{ {
auto old = askOverride();
repMan().setData(*session(), name(), rlp(askOverride() / 2 + 1));
cnote << "Rude behaviour; askOverride now" << askOverride() << ", was" << old;
repMan().noteRude(*session(), name()); repMan().noteRude(*session(), name());
session()->addNote("manners", "RUDE"); session()->addNote("manners", "RUDE");
} }
@ -77,22 +106,8 @@ EthereumHost* EthereumPeer::host() const
* Possible asking/syncing states for two peers: * Possible asking/syncing states for two peers:
*/ */
string toString(Asking _a)
{
switch (_a)
{
case Asking::Blocks: return "Blocks";
case Asking::Hashes: return "Hashes";
case Asking::Nothing: return "Nothing";
case Asking::State: return "State";
}
return "?";
}
void EthereumPeer::setIdle() void EthereumPeer::setIdle()
{ {
m_sub.doneFetch();
m_hashSub.doneFetch();
setAsking(Asking::Nothing); setAsking(Asking::Nothing);
} }
@ -100,6 +115,7 @@ void EthereumPeer::requestStatus()
{ {
assert(m_asking == Asking::Nothing); assert(m_asking == Asking::Nothing);
setAsking(Asking::State); setAsking(Asking::State);
m_requireTransactions = true;
RLPStream s; RLPStream s;
bool latest = m_peerCapabilityVersion == host()->protocolVersion(); bool latest = m_peerCapabilityVersion == host()->protocolVersion();
prep(s, StatusPacket, latest ? 6 : 5) prep(s, StatusPacket, latest ? 6 : 5)
@ -113,14 +129,14 @@ void EthereumPeer::requestStatus()
sealAndSend(s); sealAndSend(s);
} }
void EthereumPeer::requestHashes() void EthereumPeer::requestHashes(u256 _number, unsigned _count)
{ {
assert(m_asking == Asking::Nothing); assert(m_asking == Asking::Nothing);
m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk); m_syncHashNumber = _number;
m_syncHash = h256(); m_syncHash = h256();
setAsking(Asking::Hashes); setAsking(Asking::Hashes);
RLPStream s; RLPStream s;
prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk; prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << _count;
clog(NetMessageDetail) << "Requesting block hashes for numbers " << m_syncHashNumber << "-" << m_syncHashNumber + c_maxHashesAsk - 1; clog(NetMessageDetail) << "Requesting block hashes for numbers " << m_syncHashNumber << "-" << m_syncHashNumber + c_maxHashesAsk - 1;
sealAndSend(s); sealAndSend(s);
} }
@ -140,7 +156,7 @@ void EthereumPeer::requestHashes(h256 const& _lastHash)
void EthereumPeer::requestBlocks() void EthereumPeer::requestBlocks()
{ {
setAsking(Asking::Blocks); setAsking(Asking::Blocks);
auto blocks = m_sub.nextFetch(isRude() ? 1 : c_maxBlocksAsk); auto blocks = m_sub.nextFetch(askOverride());
if (blocks.size()) if (blocks.size())
{ {
RLPStream s; RLPStream s;

16
libethereum/EthereumPeer.h

@ -50,6 +50,9 @@ namespace eth
class EthereumPeer: public p2p::Capability class EthereumPeer: public p2p::Capability
{ {
friend class EthereumHost; //TODO: remove this friend class EthereumHost; //TODO: remove this
friend class BlockChainSync; //TODO: remove this
friend class PV60Sync; //TODO: remove this
friend class PV61Sync; //TODO: remove this
public: public:
/// Basic constructor. /// Basic constructor.
@ -73,8 +76,8 @@ public:
/// Abort sync and reset fetch /// Abort sync and reset fetch
void setIdle(); void setIdle();
/// Request hashes. Uses hash download manager to get hash number. v61+ protocol version only /// Request hashes by number. v61+ protocol version only
void requestHashes(); void requestHashes(u256 _number, unsigned _count);
/// Request hashes for given parent hash. /// Request hashes for given parent hash.
void requestHashes(h256 const& _lastHash); void requestHashes(h256 const& _lastHash);
@ -91,6 +94,9 @@ public:
private: private:
using p2p::Capability::sealAndSend; using p2p::Capability::sealAndSend;
/// Figure out the amount of blocks we should be asking for.
unsigned askOverride() const;
/// Interpret an incoming message. /// Interpret an incoming message.
virtual bool interpret(unsigned _id, RLP const& _r); virtual bool interpret(unsigned _id, RLP const& _r);
@ -135,18 +141,16 @@ private:
h256 m_genesisHash; ///< Peer's genesis hash h256 m_genesisHash; ///< Peer's genesis hash
u256 m_latestBlockNumber; ///< Number of the latest block this peer has 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 /// 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. /// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks.
unsigned m_expectedHashes = 0; ///< Estimated upper bound of hashes to expect from this peer. 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 (PV61+) u256 m_syncHashNumber = 0; ///< Number of latest hash we sync to (PV61+)
h256 m_syncHash; ///< Latest hash we sync to (PV60) h256 m_syncHash; ///< Latest hash we sync to (PV60)
/// Once we're asking for blocks, this becomes in use. /// Once we're asking for blocks, this becomes in use.
DownloadSub m_sub; 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 u256 m_peerCapabilityVersion; ///< Protocol version this peer supports received as capability
/// Have we received a GetTransactions packet that we haven't yet answered? /// Have we received a GetTransactions packet that we haven't yet answered?
bool m_requireTransactions = false; bool m_requireTransactions = false;

2
libethereum/Executive.h

@ -53,6 +53,8 @@ public:
std::string json(bool _styled = false) const; std::string json(bool _styled = false) const;
OnOpFunc onOp() { return [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { (*this)(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }; }
private: private:
bool m_showMnemonics = false; bool m_showMnemonics = false;
std::vector<Instruction> m_lastInst; std::vector<Instruction> m_lastInst;

24
libethereum/State.cpp

@ -115,21 +115,19 @@ State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress):
paranoia("end of normal construction.", true); paranoia("end of normal construction.", true);
} }
State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequirements::value _ir): PopulationStatistics State::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir)
m_db(_db),
m_state(&m_db),
m_blockReward(c_blockReward)
{ {
auto b = _bc.block(_h); PopulationStatistics ret;
BlockInfo bi(b);
if (!bi) if (!_bc.isKnown(_h))
{ {
// Might be worth throwing here. // Might be worth throwing here.
cwarn << "Invalid block given for state population: " << _h; cwarn << "Invalid block given for state population: " << _h;
return; return ret;
} }
auto b = _bc.block(_h);
BlockInfo bi(b);
if (bi.number) if (bi.number)
{ {
// Non-genesis: // Non-genesis:
@ -143,10 +141,10 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire
m_ourAddress = bi.coinbaseAddress; m_ourAddress = bi.coinbaseAddress;
boost::timer t; boost::timer t;
auto vb = BlockChain::verifyBlock(b); auto vb = BlockChain::verifyBlock(b);
cnote << "verifyBlock:" << t.elapsed(); ret.verify = t.elapsed();
t.restart(); t.restart();
enact(vb, _bc, _ir); enact(vb, _bc, _ir);
cnote << "enact:" << t.elapsed(); ret.enact = t.elapsed();
} }
else else
{ {
@ -155,6 +153,8 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire
m_state.init(); m_state.init();
sync(_bc, _h, bi, _ir); sync(_bc, _h, bi, _ir);
} }
return ret;
} }
State::State(State const& _s): State::State(State const& _s):
@ -456,7 +456,7 @@ unordered_map<Address, u256> State::addresses() const
ret[i.first] = RLP(i.second)[1].toInt<u256>(); ret[i.first] = RLP(i.second)[1].toInt<u256>();
return ret; return ret;
#else #else
throw InterfaceNotSupported("State::addresses()"); BOOST_THROW_EXCEPTION(InterfaceNotSupported("State::addresses()"));
#endif #endif
} }
@ -600,7 +600,7 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
{ {
StandardTrace st; StandardTrace st;
st.setShowMnemonics(); st.setShowMnemonics();
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { st(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }); execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp());
ret += (ret.empty() ? "[" : ",") + st.json(); ret += (ret.empty() ? "[" : ",") + st.json();
RLPStream receiptRLP; RLPStream receiptRLP;

14
libethereum/State.h

@ -123,6 +123,12 @@ enum class Permanence
Committed Committed
}; };
struct PopulationStatistics
{
double verify;
double enact;
};
/** /**
* @brief Model of the current state of the ledger. * @brief Model of the current state of the ledger.
* Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block).
@ -146,9 +152,6 @@ public:
/// You can also set the coinbase address. /// You can also set the coinbase address.
explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address()); explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address());
/// Construct state object from arbitrary point in blockchain.
State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash, ImportRequirements::value _ir = ImportRequirements::Default);
/// Copy state object. /// Copy state object.
State(State const& _s); State(State const& _s);
@ -157,6 +160,9 @@ public:
~State(); ~State();
/// Construct state object from arbitrary point in blockchain.
PopulationStatistics populateFromChain(BlockChain const& _bc, h256 const& _hash, ImportRequirements::value _ir = ImportRequirements::Default);
/// Set the coinbase address for any transactions we do. /// Set the coinbase address for any transactions we do.
/// This causes a complete reset of current block. /// This causes a complete reset of current block.
void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); }
@ -200,6 +206,8 @@ public:
return false; return false;
PoW::assignResult(_result, m_currentBlock); PoW::assignResult(_result, m_currentBlock);
if (!PoW::verify(m_currentBlock))
return false;
cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce) << m_currentBlock.nonce << m_currentBlock.difficulty << PoW::verify(m_currentBlock); cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce) << m_currentBlock.nonce << m_currentBlock.difficulty << PoW::verify(m_currentBlock);

3
libethereum/TransactionQueue.cpp

@ -29,6 +29,7 @@ using namespace dev;
using namespace dev::eth; using namespace dev::eth;
const char* TransactionQueueChannel::name() { return EthCyan "┉┅▶"; } const char* TransactionQueueChannel::name() { return EthCyan "┉┅▶"; }
const char* TransactionQueueTraceChannel::name() { return EthCyan " ┅▶"; }
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb, IfDropped _ik) ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb, IfDropped _ik)
{ {
@ -115,7 +116,7 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
m_known.insert(_h); m_known.insert(_h);
if (_cb) if (_cb)
m_callbacks[_h] = _cb; m_callbacks[_h] = _cb;
ctxq << "Queued vaguely legit-looking transaction" << _h; clog(TransactionQueueTraceChannel) << "Queued vaguely legit-looking transaction" << _h;
m_onReady(); m_onReady();
} }
catch (Exception const& _e) catch (Exception const& _e)

3
libethereum/TransactionQueue.h

@ -36,7 +36,8 @@ namespace eth
class BlockChain; class BlockChain;
struct TransactionQueueChannel: public LogChannel { static const char* name(); static const int verbosity = 4; }; struct TransactionQueueChannel: public LogChannel { static const char* name(); static const int verbosity = 4; };
#define ctxq dev::LogOutputStream<dev::eth::TransactionQueueChannel, true>() struct TransactionQueueTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 7; };
#define ctxq dev::LogOutputStream<dev::eth::TransactionQueueTraceChannel, true>()
enum class IfDropped { Ignore, Retry }; enum class IfDropped { Ignore, Retry };

28
libevm/ExtVMFace.h

@ -70,12 +70,16 @@ using LogEntries = std::vector<LogEntry>;
struct LocalisedLogEntry: public LogEntry struct LocalisedLogEntry: public LogEntry
{ {
LocalisedLogEntry() {} LocalisedLogEntry() {}
explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {}; explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {}
explicit LocalisedLogEntry( explicit LocalisedLogEntry(
LogEntry const& _le, LogEntry const& _le,
h256 _special h256 _special
): LogEntry(_le), special(_special) {}; ):
LogEntry(_le),
isSpecial(true),
special(_special)
{}
explicit LocalisedLogEntry( explicit LocalisedLogEntry(
LogEntry const& _le, LogEntry const& _le,
@ -84,16 +88,26 @@ struct LocalisedLogEntry: public LogEntry
unsigned _ti, unsigned _ti,
unsigned _li, unsigned _li,
BlockPolarity _polarity = BlockPolarity::Unknown BlockPolarity _polarity = BlockPolarity::Unknown
): LogEntry(_le), blockHash(_bi.hash()), blockNumber((BlockNumber)_bi.number), transactionHash(_th), transactionIndex(_ti), logIndex(_li), mined(true), polarity(_polarity) {}; ):
LogEntry(_le),
h256 blockHash = h256(); blockHash(_bi.hash()),
blockNumber((BlockNumber)_bi.number),
transactionHash(_th),
transactionIndex(_ti),
logIndex(_li),
mined(true),
polarity(_polarity)
{}
h256 blockHash;
BlockNumber blockNumber = 0; BlockNumber blockNumber = 0;
h256 transactionHash = h256(); h256 transactionHash;
unsigned transactionIndex = 0; unsigned transactionIndex = 0;
unsigned logIndex = 0; unsigned logIndex = 0;
bool mined = false; bool mined = false;
BlockPolarity polarity = BlockPolarity::Unknown; BlockPolarity polarity = BlockPolarity::Unknown;
h256 special = h256(); bool isSpecial = false;
h256 special;
}; };
using LocalisedLogEntries = std::vector<LocalisedLogEntry>; using LocalisedLogEntries = std::vector<LocalisedLogEntry>;

58
libevm/VM.cpp

@ -202,6 +202,24 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
return nextPC; return nextPC;
}; };
auto copyDataToMemory = [](bytesConstRef _data, decltype(m_stack)& _stack, decltype(m_temp)& _memory)
{
auto offset = static_cast<size_t>(_stack.back());
_stack.pop_back();
bigint bigIndex = _stack.back();
auto index = static_cast<size_t>(bigIndex);
_stack.pop_back();
auto size = static_cast<size_t>(_stack.back());
_stack.pop_back();
size_t sizeToBeCopied = bigIndex + size > _data.size() ? _data.size() < bigIndex ? 0 : _data.size() - index : size;
if (sizeToBeCopied > 0)
std::memcpy(_memory.data() + offset, _data.data() + index, sizeToBeCopied);
if (size > sizeToBeCopied)
std::memset(_memory.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied);
};
m_steps = 0; m_steps = 0;
for (auto nextPC = m_curPC + 1; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++m_steps) for (auto nextPC = m_curPC + 1; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++m_steps)
{ {
@ -364,44 +382,16 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size(); m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size();
break; break;
case Instruction::CALLDATACOPY: case Instruction::CALLDATACOPY:
case Instruction::CODECOPY: copyDataToMemory(_ext.data, m_stack, m_temp);
case Instruction::EXTCODECOPY:
{
Address a;
if (inst == Instruction::EXTCODECOPY)
{
a = asAddress(m_stack.back());
m_stack.pop_back();
}
unsigned offset = (unsigned)m_stack.back();
m_stack.pop_back();
u256 index = m_stack.back();
m_stack.pop_back();
unsigned size = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned sizeToBeCopied;
switch(inst)
{
case Instruction::CALLDATACOPY:
sizeToBeCopied = index + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < index ? 0 : _ext.data.size() - (unsigned)index : size;
memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)index, sizeToBeCopied);
break; break;
case Instruction::CODECOPY: case Instruction::CODECOPY:
sizeToBeCopied = index + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < index ? 0 : _ext.code.size() - (unsigned)index : size; copyDataToMemory(&_ext.code, m_stack, m_temp);
memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)index, sizeToBeCopied);
break; break;
case Instruction::EXTCODECOPY: case Instruction::EXTCODECOPY:
sizeToBeCopied = index + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < index ? 0 : _ext.codeAt(a).size() - (unsigned)index : size; {
memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)index, sizeToBeCopied); auto a = asAddress(m_stack.back());
break; m_stack.pop_back();
default: copyDataToMemory(&_ext.codeAt(a), m_stack, m_temp);
// this is unreachable, but if someone introduces a bug in the future, he may get here.
assert(false);
BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("CALLDATACOPY, CODECOPY or EXTCODECOPY instruction requested."));
break;
}
memset(m_temp.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied);
break;
} }
case Instruction::GASPRICE: case Instruction::GASPRICE:
m_stack.push_back(_ext.gasPrice); m_stack.push_back(_ext.gasPrice);

10
libevmasm/Assembly.cpp

@ -292,16 +292,6 @@ void Assembly::injectStart(AssemblyItem const& _i)
m_items.insert(m_items.begin(), _i); m_items.insert(m_items.begin(), _i);
} }
inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b)
{
if (_a.size() != _b.size())
return false;
for (unsigned i = 0; i < _a.size(); ++i)
if (!_a[i].match(_b[i]))
return false;
return true;
}
struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; }; struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>() #define copt dev::LogOutputStream<OptimiserChannel, true>()

7
libevmasm/AssemblyItem.cpp

@ -126,10 +126,3 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
} }
return _out; return _out;
} }
ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
{
for (AssemblyItem const& i: _i)
_out << i;
return _out;
}

9
libevmasm/AssemblyItem.h

@ -98,11 +98,14 @@ private:
}; };
using AssemblyItems = std::vector<AssemblyItem>; using AssemblyItems = std::vector<AssemblyItem>;
using AssemblyItemsConstRef = vector_ref<AssemblyItem const>;
std::ostream& operator<<(std::ostream& _out, AssemblyItem const& _item); std::ostream& operator<<(std::ostream& _out, AssemblyItem const& _item);
std::ostream& operator<<(std::ostream& _out, AssemblyItemsConstRef _i); inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _items)
inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { return operator<<(_out, AssemblyItemsConstRef(&_i)); } {
for (AssemblyItem const& item: _items)
_out << item;
return _out;
}
} }
} }

7
libjsconsole/JSConsole.cpp

@ -39,12 +39,11 @@ JSConsole::JSConsole(WebThreeDirect& _web3, shared_ptr<AccountHolder> const& _ac
m_printer(m_engine) m_printer(m_engine)
{ {
m_jsonrpcConnector.reset(new JSV8Connector(m_engine)); m_jsonrpcConnector.reset(new JSV8Connector(m_engine));
m_jsonrpcServer.reset(new WebThreeStubServer(*m_jsonrpcConnector.get(), _web3, _accounts, vector<KeyPair>())); (void)_web3; (void)_accounts;
// m_jsonrpcServer.reset(new WebThreeStubServer(*m_jsonrpcConnector.get(), _web3, _accounts, vector<KeyPair>()));
} }
JSConsole::~JSConsole() {} void JSConsole::readExpression() const
void JSConsole::repl() const
{ {
string cmd = ""; string cmd = "";
g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << flush; rl_forced_update_display(); }; g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << flush; rl_forced_update_display(); };

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

Loading…
Cancel
Save