Browse Source

Merge branch 'develop' into discovery

cl-refactor
subtly 10 years ago
parent
commit
86381122ff
  1. 1
      CMakeLists.txt
  2. 6
      alethzero/Main.ui
  3. 23
      alethzero/MainWin.cpp
  4. 1
      alethzero/MainWin.h
  5. 5
      alethzero/Transact.cpp
  6. 24
      cmake/EthExecutableHelper.cmake
  7. 3
      cmake/scripts/copydlls.cmake
  8. 111
      eth/main.cpp
  9. 4
      ethminer/main.cpp
  10. 2
      evmjit/CMakeLists.txt
  11. 56
      evmjit/include/evmjit/DataTypes.h
  12. 36
      evmjit/include/evmjit/JIT.h
  13. 1
      evmjit/libevmjit-cpp/CMakeLists.txt
  14. 3
      evmjit/libevmjit-cpp/Env.cpp
  15. 4
      evmjit/libevmjit-cpp/JitVM.cpp
  16. 14
      evmjit/libevmjit-cpp/Utils.h
  17. 3
      evmjit/libevmjit/CMakeLists.txt
  18. 10
      evmjit/libevmjit/Common.h
  19. 22
      evmjit/libevmjit/ExecutionEngine.cpp
  20. 46
      evmjit/libevmjit/JIT.cpp
  21. 5
      evmjit/libevmjit/RuntimeData.h
  22. 2
      libdevcore/Common.cpp
  23. 8
      libdevcore/Common.h
  24. 10
      libdevcore/Worker.cpp
  25. 34
      libdevcrypto/MemoryDB.cpp
  26. 11
      libdevcrypto/MemoryDB.h
  27. 39
      libdevcrypto/OverlayDB.cpp
  28. 8
      libdevcrypto/OverlayDB.h
  29. 2
      libdevcrypto/TrieDB.cpp
  30. 34
      libdevcrypto/TrieDB.h
  31. 4
      libethash-cl/ethash_cl_miner.cpp
  32. 5
      libethash/CMakeLists.txt
  33. 72
      libethash/endian.h
  34. 184
      libethash/ethash.h
  35. 5
      libethash/fnv.h
  36. 428
      libethash/internal.c
  37. 136
      libethash/internal.h
  38. 119
      libethash/io.c
  39. 193
      libethash/io.h
  40. 94
      libethash/io_posix.c
  41. 93
      libethash/io_win32.c
  42. 47
      libethash/mmap.h
  43. 84
      libethash/mmap_win32.c
  44. 12
      libethash/sha3.h
  45. 11
      libethash/sha3_cryptopp.cpp
  46. 7
      libethash/sha3_cryptopp.h
  47. 4
      libethash/util.h
  48. 38
      libethash/util_win32.c
  49. 32
      libethcore/Ethash.cpp
  50. 5
      libethcore/Ethash.h
  51. 177
      libethcore/EthashAux.cpp
  52. 59
      libethcore/EthashAux.h
  53. 3
      libethcore/Exceptions.h
  54. 7
      libethereum/BlockChain.cpp
  55. 2
      libethereum/Client.cpp
  56. 4
      libethereum/ClientBase.h
  57. 16
      libethereum/State.cpp
  58. 3
      libevm/CMakeLists.txt
  59. 79
      libevm/SmartVM.cpp
  60. 43
      libevm/SmartVM.h
  61. 21
      libevm/VMFactory.cpp
  62. 11
      libevm/VMFactory.h
  63. 60
      libevmasm/Assembly.cpp
  64. 2
      libevmasm/AssemblyItem.h
  65. 91
      libevmasm/BlockDeduplicator.cpp
  66. 69
      libevmasm/BlockDeduplicator.h
  67. 7
      libevmasm/CMakeLists.txt
  68. 103
      libevmasm/CommonSubexpressionEliminator.cpp
  69. 22
      libevmasm/CommonSubexpressionEliminator.h
  70. 20
      libevmasm/ControlFlowGraph.cpp
  71. 52
      libevmasm/KnownState.cpp
  72. 12
      libsolidity/Compiler.cpp
  73. 1
      mix/ClientModel.cpp
  74. 13
      mix/CodeModel.cpp
  75. 2
      mix/CodeModel.h
  76. 6
      mix/FileIo.cpp
  77. 2
      mix/FileIo.h
  78. 13
      mix/qml/CodeEditorView.qml
  79. 31
      mix/qml/FilesSection.qml
  80. 10
      mix/qml/js/ProjectModel.js
  81. 1
      mix/test/qml/TestMain.qml
  82. 7
      mix/test/qml/js/TestDebugger.js
  83. 14
      mix/test/qml/js/TestProject.js
  84. 7
      test/CMakeLists.txt
  85. 43
      test/TestHelper.cpp
  86. 4
      test/TestHelper.h
  87. 10
      test/libdevcrypto/trie.cpp
  88. 6
      test/libethcore/dagger.cpp
  89. 2
      test/libethereum/blockchain.cpp
  90. 2
      test/libethereum/state.cpp
  91. 2
      test/libethereum/transaction.cpp
  92. 4
      test/libevm/vm.cpp
  93. 150
      test/libp2p/capability.cpp
  94. 33
      test/libsolidity/SolidityCompiler.cpp
  95. 95
      test/libsolidity/SolidityOptimizer.cpp

1
CMakeLists.txt

@ -295,6 +295,7 @@ message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}"
message("------------------------------------------------------------------------")
message("")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
set(CMAKE_THREAD_LIBS_INIT pthread)
include(EthCompilerSettings)

6
alethzero/Main.ui

@ -151,6 +151,7 @@
</property>
<addaction name="mine"/>
<addaction name="turboMining"/>
<addaction name="prepNextDAG"/>
<addaction name="separator"/>
<addaction name="newTransaction"/>
<addaction name="newAccount"/>
@ -1727,6 +1728,11 @@ font-size: 14pt</string>
<string>In&amp;ject Block</string>
</property>
</action>
<action name="prepNextDAG">
<property name="text">
<string>Prepare Next &amp;DAG</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

23
alethzero/MainWin.cpp

@ -204,7 +204,7 @@ Main::Main(QWidget *parent) :
QSettings s("ethereum", "alethzero");
m_networkConfig = s.value("peers").toByteArray();
bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size());
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth", "shh"}, p2p::NetworkPreferences(), network));
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network));
m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this));
@ -943,10 +943,21 @@ void Main::on_preview_triggered()
refreshAll();
}
void Main::on_prepNextDAG_triggered()
{
EthashAux::computeFull(ethereum()->blockChain().number() + ETHASH_EPOCH_LENGTH);
}
void Main::refreshMining()
{
pair<uint64_t, unsigned> gp = EthashAux::fullGeneratingProgress();
QString t;
if (gp.first != EthashAux::NotGenerating)
t = QString("DAG for #%1-#%2: %3% complete; ").arg(gp.first).arg(gp.first + ETHASH_EPOCH_LENGTH - 1).arg(gp.second);
MiningProgress p = ethereum()->miningProgress();
ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining");
ui->mineStatus->setText(t + (ethereum()->isMining() ? p.hashes > 0 ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Awaiting DAG" : "Not mining"));
if (ethereum()->isMining() && p.hashes > 0)
{
if (!ui->miningView->isVisible())
return;
list<MineInfo> l = ethereum()->miningHistory();
@ -955,10 +966,7 @@ void Main::refreshMining()
ui->miningView->resetStats();
lh = p.hashes;
ui->miningView->appendStats(l, p);
/* if (p.ms)
for (MineInfo const& i: l)
cnote << i.hashes * 10 << "h/sec, need:" << i.requirement << " best:" << i.best << " best-so-far:" << p.best << " avg-speed:" << (p.hashes * 1000 / p.ms) << "h/sec";
*/
}
}
void Main::setBeneficiary(Address const& _b)
@ -1878,6 +1886,7 @@ void Main::on_mine_triggered()
{
if (ui->mine->isChecked())
{
// EthashAux::computeFull(ethereum()->blockChain().number());
ethereum()->setAddress(m_beneficiary);
ethereum()->startMining();
}
@ -2027,6 +2036,7 @@ std::string Main::prettyU256(dev::u256 const& _n) const
void Main::on_post_clicked()
{
return;
shh::Message m;
m.setTo(stringToPublic(ui->shhTo->currentText()));
m.setPayload(parseData(ui->shhData->toPlainText().toStdString()));
@ -2051,6 +2061,7 @@ int Main::authenticate(QString _title, QString _text)
void Main::refreshWhispers()
{
return;
ui->whispers->clear();
for (auto const& w: whisper()->all())
{

1
alethzero/MainWin.h

@ -124,6 +124,7 @@ private slots:
// Mining
void on_mine_triggered();
void on_prepNextDAG_triggered();
// View
void on_refresh_triggered();

5
alethzero/Transact.cpp

@ -299,8 +299,9 @@ void Transact::rejigData()
return;
// Determine how much balance we have to play with...
auto s = findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice());
auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock);
//findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice());
auto s = fromAccount();
auto b = ethereum()->balanceAt(s, PendingBlock);
m_allGood = true;
QString htmlInfo;

24
cmake/EthExecutableHelper.cmake

@ -48,13 +48,14 @@ macro(eth_copy_dlls EXECUTABLE DLLS)
# optimized;path_to_dll.dll;debug;path_to_dlld.dll
list(GET ${DLLS} 1 DLL_RELEASE)
list(GET ${DLLS} 3 DLL_DEBUG)
get_target_property(TARGET_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE} RUNTIME_OUTPUT_DIRECTORY)
add_custom_command(TARGET ${EXECUTABLE}
POST_BUILD
COMMAND ${CMAKE_COMMAND} ARGS
-DDLL_RELEASE="${DLL_RELEASE}"
-DDLL_DEBUG="${DLL_DEBUG}"
-DCONF="$<CONFIGURATION>"
-DDESTINATION="${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
-DDESTINATION="${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}"
-P "${ETH_SCRIPTS_DIR}/copydlls.cmake"
)
endmacro()
@ -87,19 +88,15 @@ macro(eth_install_executable EXECUTABLE)
if (APPLE)
# First have qt5 install plugins and frameworks
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app -executable=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents/MacOS/${EXECUTABLE} ${eth_qml_dir}
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents
COMMAND ${MACDEPLOYQT_APP} ${EXECUTABLE}.app -executable=${EXECUTABLE}.app/Contents/MacOS/${EXECUTABLE} ${eth_qml_dir}
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}
COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${EXECUTABLE}.app/Contents
)
# This tool and next will inspect linked libraries in order to determine which dependencies are required
if (${CMAKE_CFG_INTDIR} STREQUAL ".")
# TODO: This should only happen for GUI application
set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXECUTABLE}.app")
else ()
set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app")
endif ()
set(APP_BUNDLE_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INDIR}/${EXECUTABLE}.app")
# This tool and next will inspect linked libraries in order to determine which dependencies are required
install(CODE "
include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS 1)
@ -111,14 +108,15 @@ macro(eth_install_executable EXECUTABLE)
get_target_property(TARGET_LIBS ${EXECUTABLE} INTERFACE_LINK_LIBRARIES)
string(REGEX MATCH "Qt5::Core" HAVE_QT ${TARGET_LIBS})
if ("${HAVE_QT}" STREQUAL "Qt5::Core")
get_target_property(TARGET_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE} RUNTIME_OUTPUT_DIRECTORY)
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}"
COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}"
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
#workaround for https://bugreports.qt.io/browse/QTBUG-42083
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM
WORKING_DIRECTORY ${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR} VERBATIM
)
endif()
@ -144,5 +142,3 @@ macro(eth_install_executable EXECUTABLE)
endif ()
endmacro()

3
cmake/scripts/copydlls.cmake

@ -14,5 +14,4 @@ else () # Debug
set(DLL ${DLL_DEBUG})
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${DLL}" "${DESTINATION}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${DLL}" "${DESTINATION}")

111
eth/main.cpp

@ -158,6 +158,11 @@ void help()
<< " --port <port> Connect to remote port (default: 30303)." << 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
<< endl
<< "Client structured logging:" << endl
<< " --structured-logging Enable structured logging (default output to stdout)." << endl
<< " --structured-logging-format <format> Set the structured logging time format." << endl
<< " --structured-logging-url <URL> Set the structured logging destination (currently only file:// supported)." << endl
#if ETH_JSONRPC || !ETH_TRUE
<< endl
<< "Work farming mode:" << endl
@ -806,8 +811,11 @@ int main(int argc, char** argv)
structuredLoggingFormat = string(argv[++i]);
else if (arg == "--structured-logging")
structuredLogging = true;
else if (arg == "--structured-logging-destination" && i + 1 < argc)
else if (arg == "--structured-logging-url" && i + 1 < argc)
{
structuredLogging = true;
structuredLoggingURL = argv[++i];
}
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc)
@ -848,7 +856,7 @@ int main(int argc, char** argv)
auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m);
auto r = EthashAux::eval(seedHash, powHash, bi.nonce);
auto r = EthashAux::eval((uint64_t)bi.number, powHash, bi.nonce);
bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
@ -857,7 +865,7 @@ int main(int argc, char** argv)
cout << " with seed as " << seedHash << endl;
if (valid)
cout << "(mixHash = " << r.mixHash << ")" << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light(seedHash)->data()) << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light((uint64_t)bi.number)->data()) << endl;
exit(0);
}
catch (...)
@ -1033,6 +1041,28 @@ int main(int argc, char** argv)
if (!clientName.empty())
clientName += "/";
string logbuf;
bool silence = false;
std::string additional;
g_logPost = [&](std::string const& a, char const*){
if (silence)
logbuf += a + "\n";
else
cout << "\r \r" << a << endl << additional << flush;
};
auto getPassword = [&](string const& prompt){
auto s = silence;
silence = true;
cout << endl;
string ret = dev::getPassword(prompt);
silence = s;
return ret;
};
auto getAccountPassword = [&](Address const& a){
return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): ");
};
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
@ -1042,13 +1072,38 @@ int main(int argc, char** argv)
clientImplString,
dbPath,
killChain,
nodeMode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(),
netPrefs,
&nodesState);
if (mode == OperationMode::DAGInit)
doInitDAG(web3.ethereum()->blockChain().number() + (initDAG == PendingBlock ? 30000 : 0));
if (keyManager.exists())
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter your MASTER password: ");
if (!keyManager.load(masterPassword))
{
cout << "Password invalid. Try again." << endl;
masterPassword.clear();
}
}
else
{
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): ");
string confirm = getPassword("Please confirm the password by entering it again: ");
if (masterPassword != confirm)
{
cout << "Passwords were different. Try again." << endl;
masterPassword.clear();
}
}
keyManager.create(masterPassword);
}
auto toNumber = [&](string const& s) -> unsigned {
if (s == "latest")
return web3.ethereum()->number();
@ -1137,53 +1192,13 @@ int main(int argc, char** argv)
if (remoteHost.size())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
if (keyManager.exists())
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter your MASTER password: ");
if (!keyManager.load(masterPassword))
{
cout << "Password invalid. Try again." << endl;
masterPassword.clear();
}
}
else
{
while (masterPassword.empty())
{
masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): ");
string confirm = getPassword("Please confirm the password by entering it again: ");
if (masterPassword != confirm)
{
cout << "Passwords were different. Try again." << endl;
masterPassword.clear();
}
}
keyManager.create(masterPassword);
}
string logbuf;
bool silence = false;
std::string additional;
g_logPost = [&](std::string const& a, char const*) { if (silence) logbuf += a + "\n"; else cout << "\r \r" << a << endl << additional << flush; };
// TODO: give hints &c.
auto getPassword = [&](Address const& a){
auto s = silence;
silence = true;
cout << endl;
string ret = dev::getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): ");
silence = s;
return ret;
};
#if ETH_JSONRPC || !ETH_TRUE
shared_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector;
if (jsonrpc > -1)
{
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();}, getPassword, keyManager), vector<KeyPair>()));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>()));
jsonrpcServer->StartListening();
}
#endif
@ -1327,7 +1342,7 @@ int main(int argc, char** argv)
if (jsonrpc < 0)
jsonrpc = SensibleHttpPort;
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();}, getPassword, keyManager), vector<KeyPair>()));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector<KeyPair>()));
jsonrpcServer->StartListening();
}
else if (cmd == "jsonstop")
@ -1479,7 +1494,7 @@ int main(int argc, char** argv)
try
{
Address dest = h160(fromHex(hexAddr, WhenError::Throw));
c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), amount, dest, bytes(), minGas);
c->submitTransaction(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
@ -1548,7 +1563,7 @@ int main(int argc, char** argv)
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else
c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), endowment, init, gas, gasPrice);
c->submitTransaction(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), endowment, init, gas, gasPrice);
}
else
cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX";

4
ethminer/main.cpp

@ -417,7 +417,7 @@ int main(int argc, char** argv)
auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m);
auto r = EthashAux::eval(seedHash, powHash, bi.nonce);
auto r = EthashAux::eval((uint64_t)bi.number, powHash, bi.nonce);
bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
@ -426,7 +426,7 @@ int main(int argc, char** argv)
cout << " with seed as " << seedHash << endl;
if (valid)
cout << "(mixHash = " << r.mixHash << ")" << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light(seedHash)->data()) << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light((uint64_t)bi.number)->data()) << endl;
exit(0);
}
catch (...)

2
evmjit/CMakeLists.txt

@ -33,6 +33,8 @@ else()
link_directories(/usr/lib/llvm-3.5/lib)
endif()
get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE)
add_subdirectory(libevmjit)
if(EVMJIT_CPP)

56
evmjit/include/evmjit/DataTypes.h

@ -0,0 +1,56 @@
#pragma once
#include <cstdint>
#include <functional>
namespace dev
{
namespace evmjit
{
struct h256
{
uint64_t words[4];
};
inline bool operator==(h256 _h1, h256 _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
i256() = default;
i256(h256 _h)
{
a = _h.words[0];
b = _h.words[1];
c = _h.words[2];
d = _h.words[3];
}
};
}
}
namespace std
{
template<> struct hash<dev::evmjit::h256>
{
size_t operator()(dev::evmjit::h256 const& _h) const
{
/// This implementation expects the argument to be a full 256-bit Keccak hash.
/// It does nothing more than returning a slice of the input hash.
return static_cast<size_t>(_h.words[0]);
};
};
}

36
evmjit/include/evmjit/JIT.h

@ -0,0 +1,36 @@
#pragma once
#include "evmjit/DataTypes.h"
namespace dev
{
namespace eth
{
namespace jit
{
class ExecutionEngine;
}
}
namespace evmjit
{
class JIT
{
public:
/// Ask JIT if the EVM code is ready for execution.
/// Returns `true` if the EVM code has been compiled and loaded into memory.
/// In this case the code can be executed without overhead.
/// \param _codeHash The Keccak hash of the EVM code.
static bool isCodeReady(h256 _codeHash);
private:
friend class dev::eth::jit::ExecutionEngine;
static void* getCode(h256 _codeHash);
static void mapCode(h256 _codeHash, void* _funcAddr);
};
}
}

1
evmjit/libevmjit-cpp/CMakeLists.txt

@ -19,6 +19,7 @@ add_library(${TARGET_NAME} STATIC ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
include_directories(../..)
include_directories(${EVMJIT_INCLUDE_DIR})
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})

3
evmjit/libevmjit-cpp/Env.cpp

@ -3,6 +3,7 @@
#include <libdevcrypto/SHA3.h>
#include <libevmcore/Params.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/DataTypes.h>
#include "Utils.h"
@ -16,7 +17,7 @@ extern "C"
using namespace dev;
using namespace dev::eth;
using jit::i256;
using evmjit::i256;
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
{

4
evmjit/libevmjit-cpp/JitVM.cpp

@ -32,9 +32,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
if (rejected)
{
cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter";
VMFactory::setKind(VMKind::Interpreter);
m_fallbackVM = VMFactory::create(m_gas);
VMFactory::setKind(VMKind::JIT);
m_fallbackVM = VMFactory::create(VMKind::Interpreter, m_gas);
auto&& output = m_fallbackVM->go(_ext, _onOp, _step);
m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it
return output;

14
evmjit/libevmjit-cpp/Utils.h

@ -1,13 +1,13 @@
#pragma once
#include <evmjit/libevmjit/Common.h>
#include <evmjit/DataTypes.h>
namespace dev
{
namespace eth
{
inline u256 llvm2eth(jit::i256 _i)
inline u256 llvm2eth(evmjit::i256 _i)
{
u256 u = 0;
u |= _i.d;
@ -20,9 +20,9 @@ inline u256 llvm2eth(jit::i256 _i)
return u;
}
inline jit::i256 eth2llvm(u256 _u)
inline evmjit::i256 eth2llvm(u256 _u)
{
jit::i256 i;
evmjit::i256 i;
u256 mask = 0xFFFFFFFFFFFFFFFF;
i.a = static_cast<uint64_t>(_u & mask);
_u >>= 64;
@ -34,5 +34,11 @@ inline jit::i256 eth2llvm(u256 _u)
return i;
}
inline evmjit::h256 eth2llvm(h256 _u)
{
/// Just directly copies memory
return *(evmjit::h256*)&_u;
}
}
}

3
evmjit/libevmjit/CMakeLists.txt

@ -8,6 +8,7 @@ set(SOURCES
Common.h
Compiler.cpp Compiler.h
CompilerHelper.cpp CompilerHelper.h
${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h
Endianness.cpp Endianness.h
ExecStats.cpp ExecStats.h
ExecutionEngine.cpp ExecutionEngine.h
@ -15,6 +16,7 @@ set(SOURCES
GasMeter.cpp GasMeter.h
Instruction.cpp Instruction.h
interface.cpp interface.h
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Memory.cpp Memory.h
Optimizer.cpp Optimizer.h
Runtime.cpp Runtime.h
@ -79,6 +81,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
FOLDER "libs")
include_directories(${EVMJIT_INCLUDE_DIR})
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen)

10
evmjit/libevmjit/Common.h

@ -46,16 +46,6 @@ enum class ReturnCode
LinkerWorkaround = -299,
};
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
};
static_assert(sizeof(i256) == 32, "Wrong i265 size");
#define UNTESTED assert(false)
}

22
evmjit/libevmjit/ExecutionEngine.cpp

@ -19,6 +19,7 @@
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h"
#include "evmjit/JIT.h"
#include "Runtime.h"
#include "Compiler.h"
#include "Optimizer.h"
@ -33,6 +34,7 @@ namespace eth
{
namespace jit
{
using evmjit::JIT;
namespace
{
@ -119,8 +121,6 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
// TODO: Do not pseudo-init the cache every time
auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr;
static std::unordered_map<std::string, uint64_t> funcCache;
static std::unique_ptr<llvm::ExecutionEngine> ee;
if (!ee)
{
@ -147,8 +147,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
ee->setObjectCache(objectCache);
if (preloadCache)
Cache::preload(*ee, funcCache);
// FIXME: Disabled during API changes
//if (preloadCache)
// Cache::preload(*ee, funcCache);
}
static StatsCollector statsCollector;
@ -156,11 +157,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
auto mainFuncName = codeHash(_data->codeHash);
m_runtime.init(_data, _env);
EntryFuncPtr entryFuncPtr = nullptr;
auto it = funcCache.find(mainFuncName);
if (it != funcCache.end())
entryFuncPtr = (EntryFuncPtr) it->second;
// TODO: Remove cast
auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash);
if (!entryFuncPtr)
{
auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
@ -183,12 +181,10 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
module.release();
listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
}
if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError;
if (it == funcCache.end())
funcCache[mainFuncName] = (uint64_t) entryFuncPtr;
JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast
}
listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&m_runtime);

46
evmjit/libevmjit/JIT.cpp

@ -0,0 +1,46 @@
#include "evmjit/JIT.h"
#include <unordered_map>
namespace dev
{
namespace evmjit
{
namespace
{
class JITImpl: JIT
{
public:
std::unordered_map<h256, void*> codeMap;
static JITImpl& instance()
{
static JITImpl s_instance;
return s_instance;
}
};
} // anonymous namespace
bool JIT::isCodeReady(h256 _codeHash)
{
return JITImpl::instance().codeMap.count(_codeHash) != 0;
}
void* JIT::getCode(h256 _codeHash)
{
auto& codeMap = JITImpl::instance().codeMap;
auto it = codeMap.find(_codeHash);
if (it != codeMap.end())
return it->second;
return nullptr;
}
void JIT::mapCode(h256 _codeHash, void* _funcAddr)
{
JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr));
}
}
}

5
evmjit/libevmjit/RuntimeData.h

@ -1,5 +1,6 @@
#pragma once
#include "evmjit/DataTypes.h"
#include "Common.h"
namespace dev
@ -8,6 +9,8 @@ namespace eth
{
namespace jit
{
using evmjit::i256;
using evmjit::h256;
struct RuntimeData
{
@ -49,7 +52,7 @@ struct RuntimeData
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
i256 codeHash;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type

2
libdevcore/Common.cpp

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

8
libdevcore/Common.h

@ -199,12 +199,12 @@ private:
#define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__PRETTY_FUNCTION__)
#endif
#define DEV_TIMED_IF(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(#S, MS), true); __eth_t.second; __eth_t.second = false)
#define DEV_TIMED_SCOPE_IF(S) ::dev::TimerHelper __eth_t(S, MS)
#define DEV_TIMED_ABOVE(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(#S, MS), true); __eth_t.second; __eth_t.second = false)
#define DEV_TIMED_SCOPE_ABOVE(S) ::dev::TimerHelper __eth_t(S, MS)
#if WIN32
#define DEV_TIMED_FUNCTION_IF(MS) DEV_TIMED_SCOPE_IF(__FUNCSIG__, MS)
#define DEV_TIMED_FUNCTION_ABOVE(MS) DEV_TIMED_SCOPE_ABOVE(__FUNCSIG__, MS)
#else
#define DEV_TIMED_FUNCTION_IF(MS) DEV_TIMED_SCOPE_IF(__PRETTY_FUNCTION__, MS)
#define DEV_TIMED_FUNCTION_ABOVE(MS) DEV_TIMED_SCOPE_ABOVE(__PRETTY_FUNCTION__, MS)
#endif
enum class WithExisting: int

10
libdevcore/Worker.cpp

@ -65,15 +65,15 @@ void Worker::startWorking()
m_state.exchange(ex);
// cnote << "Waiting until not Stopped...";
DEV_TIMED_IF(Worker stopping, 100)
DEV_TIMED_ABOVE(Worker stopping, 100)
while (m_state == WorkerState::Stopped)
this_thread::sleep_for(chrono::milliseconds(20));
}
}));
// cnote << "Spawning" << m_name;
}
DEV_TIMED_IF(Start worker, 100)
while (m_state != WorkerState::Started)
DEV_TIMED_ABOVE(Start worker, 100)
while (m_state == WorkerState::Starting)
this_thread::sleep_for(chrono::microseconds(20));
}
@ -85,7 +85,7 @@ void Worker::stopWorking()
WorkerState ex = WorkerState::Started;
m_state.compare_exchange_strong(ex, WorkerState::Stopping);
DEV_TIMED_IF(Stop worker, 100)
DEV_TIMED_ABOVE(Stop worker, 100)
while (m_state != WorkerState::Stopped)
this_thread::sleep_for(chrono::microseconds(20));
}
@ -99,7 +99,7 @@ void Worker::terminate()
{
m_state.exchange(WorkerState::Killing);
DEV_TIMED_IF(Terminate worker, 100)
DEV_TIMED_ABOVE(Terminate worker, 100)
m_work->join();
m_work.reset();

34
libdevcrypto/MemoryDB.cpp

@ -32,6 +32,7 @@ const char* DBWarn::name() { return "TDB"; }
std::unordered_map<h256, std::string> MemoryDB::get() const
{
ReadGuard l(x_this);
std::unordered_map<h256, std::string> ret;
for (auto const& i: m_main)
if (!m_enforceRefs || i.second.second > 0)
@ -39,21 +40,34 @@ std::unordered_map<h256, std::string> MemoryDB::get() const
return ret;
}
MemoryDB& MemoryDB::operator=(MemoryDB const& _c)
{
if (this == &_c)
return *this;
ReadGuard l(_c.x_this);
WriteGuard l2(x_this);
m_main = _c.m_main;
m_aux = _c.m_aux;
return *this;
}
std::string MemoryDB::lookup(h256 const& _h) const
{
ReadGuard l(x_this);
auto it = m_main.find(_h);
if (it != m_main.end())
{
if (!m_enforceRefs || it->second.second > 0)
return it->second.first;
// else if (m_enforceRefs && m_refCount.count(it->first) && !m_refCount.at(it->first))
// cnote << "Lookup required for value with no refs. Let's hope it's in the DB." << _h;
else
cwarn << "Lookup required for value with refcount == 0. This is probably a critical trie issue" << _h;
}
return std::string();
}
bool MemoryDB::exists(h256 const& _h) const
{
ReadGuard l(x_this);
auto it = m_main.find(_h);
if (it != m_main.end() && (!m_enforceRefs || it->second.second > 0))
return true;
@ -62,6 +76,7 @@ bool MemoryDB::exists(h256 const& _h) const
void MemoryDB::insert(h256 const& _h, bytesConstRef _v)
{
WriteGuard l(x_this);
auto it = m_main.find(_h);
if (it != m_main.end())
{
@ -77,34 +92,34 @@ void MemoryDB::insert(h256 const& _h, bytesConstRef _v)
bool MemoryDB::kill(h256 const& _h)
{
ReadGuard l(x_this);
if (m_main.count(_h))
{
if (m_main[_h].second > 0)
{
m_main[_h].second--;
return true;
}
#if ETH_PARANOIA
else
{
// If we get to this point, then there was probably a node in the level DB which we need to remove and which we have previously
// used as part of the memory-based MemoryDB. Nothing to be worried about *as long as the node exists in the DB*.
dbdebug << "NOKILL-WAS" << _h;
return false;
}
dbdebug << "KILL" << _h << "=>" << m_main[_h].second;
return true;
}
else
{
dbdebug << "NOKILL" << _h;
return false;
}
#else
}
return true;
#endif
}
return false;
}
void MemoryDB::purge()
{
WriteGuard l(x_this);
for (auto it = m_main.begin(); it != m_main.end(); )
if (it->second.second)
++it;
@ -114,6 +129,7 @@ void MemoryDB::purge()
h256Hash MemoryDB::keys() const
{
ReadGuard l(x_this);
h256Hash ret;
for (auto const& i: m_main)
if (i.second.second)

11
libdevcrypto/MemoryDB.h

@ -23,6 +23,7 @@
#include <unordered_map>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/FixedHash.h>
#include <libdevcore/Log.h>
#include <libdevcore/RLP.h>
@ -43,6 +44,9 @@ class MemoryDB
public:
MemoryDB() {}
MemoryDB(MemoryDB const& _c) { operator=(_c); }
MemoryDB& operator=(MemoryDB const& _c);
void clear() { m_main.clear(); } // WARNING !!!! didn't originally clear m_refCount!!!
std::unordered_map<h256, std::string> get() const;
@ -53,13 +57,14 @@ public:
bool kill(h256 const& _h);
void purge();
bytes lookupAux(h256 const& _h) const { try { return m_aux.at(_h).first; } catch (...) { return bytes(); } }
void removeAux(h256 const& _h) { m_aux[_h].second = false; }
void insertAux(h256 const& _h, bytesConstRef _v) { m_aux[_h] = make_pair(_v.toBytes(), true); }
bytes lookupAux(h256 const& _h) const { ReadGuard l(x_this); auto it = m_aux.find(_h); if (it != m_aux.end() && (!m_enforceRefs || it->second.second)) return it->second.first; return bytes(); }
void removeAux(h256 const& _h) { WriteGuard l(x_this); m_aux[_h].second = false; }
void insertAux(h256 const& _h, bytesConstRef _v) { WriteGuard l(x_this); m_aux[_h] = make_pair(_v.toBytes(), true); }
h256Hash keys() const;
protected:
mutable SharedMutex x_this;
std::unordered_map<h256, std::pair<std::string, unsigned>> m_main;
std::unordered_map<h256, std::pair<bytes, bool>> m_aux;

39
libdevcrypto/OverlayDB.cpp

@ -19,6 +19,7 @@
* @date 2014
*/
#include <thread>
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
#include <libdevcore/Common.h>
@ -29,6 +30,8 @@ using namespace dev;
namespace dev
{
h256 const EmptyTrie = sha3(rlp(""));
OverlayDB::~OverlayDB()
{
if (m_db.use_count() == 1 && m_db.get())
@ -41,11 +44,13 @@ void OverlayDB::commit()
{
ldb::WriteBatch batch;
// cnote << "Committing nodes to disk DB:";
DEV_READ_GUARDED(x_this)
{
for (auto const& i: m_main)
{
// cnote << i.first << "#" << m_main[i.first].second;
if (i.second.second)
batch.Put(ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.first.data(), i.second.first.size()));
// cnote << i.first << "#" << m_main[i.first].second;
}
for (auto const& i: m_aux)
if (i.second.second)
@ -54,17 +59,26 @@ void OverlayDB::commit()
b.push_back(255); // for aux
batch.Put(bytesConstRef(&b), bytesConstRef(&i.second.first));
}
m_db->Write(m_writeOptions, &batch);
}
while (true)
{
ldb::Status o = m_db->Write(m_writeOptions, &batch);
if (o.ok())
break;
cwarn << "Error writing to database. Sleeping a while then retrying. If it keeps saying this, free up some space!";
this_thread::sleep_for(chrono::milliseconds(500));
}
m_aux.clear();
m_main.clear();
}
}
bytes OverlayDB::lookupAux(h256 _h) const
bytes OverlayDB::lookupAux(h256 const& _h) const
{
bytes ret = MemoryDB::lookupAux(_h);
if (!ret.empty())
return ret;
return move(ret);
std::string v;
bytes b = _h.asBytes();
b.push_back(255); // for aux
@ -76,18 +90,19 @@ bytes OverlayDB::lookupAux(h256 _h) const
void OverlayDB::rollback()
{
WriteGuard l(x_this);
m_main.clear();
}
std::string OverlayDB::lookup(h256 _h) const
std::string OverlayDB::lookup(h256 const& _h) const
{
std::string ret = MemoryDB::lookup(_h);
if (ret.empty() && m_db)
m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret);
return ret;
return move(ret);
}
bool OverlayDB::exists(h256 _h) const
bool OverlayDB::exists(h256 const& _h) const
{
if (MemoryDB::exists(_h))
return true;
@ -97,16 +112,20 @@ bool OverlayDB::exists(h256 _h) const
return !ret.empty();
}
void OverlayDB::kill(h256 _h)
void OverlayDB::kill(h256 const& _h)
{
#if ETH_PARANOIA
#if ETH_PARANOIA || 1
if (!MemoryDB::kill(_h))
{
std::string ret;
if (m_db)
m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret);
if (ret.empty())
// No point node ref decreasing for EmptyTrie since we never bother incrementing it in the first place for
// empty storage tries.
if (ret.empty() && _h != EmptyTrie)
cnote << "Decreasing DB node ref count below zero with no DB node. Probably have a corrupt Trie." << _h;
// TODO: for 1.1: ref-counted triedb.
}
#else
MemoryDB::kill(_h);

8
libdevcrypto/OverlayDB.h

@ -46,11 +46,11 @@ public:
void commit();
void rollback();
std::string lookup(h256 _h) const;
bool exists(h256 _h) const;
void kill(h256 _h);
std::string lookup(h256 const& _h) const;
bool exists(h256 const& _h) const;
void kill(h256 const& _h);
bytes lookupAux(h256 _h) const;
bytes lookupAux(h256 const& _h) const;
private:
using MemoryDB::clear;

2
libdevcrypto/TrieDB.cpp

@ -25,6 +25,6 @@ using namespace std;
using namespace dev;
h256 const dev::c_shaNull = sha3(rlp(""));
h256 const dev::EmptyTrie = c_shaNull;
h256 const dev::EmptyTrie = sha3(rlp(""));
const char* TrieDBChannel::name() { return "-T-"; }

34
libdevcrypto/TrieDB.h

@ -79,7 +79,7 @@ public:
void open(DB* _db) { m_db = _db; }
void open(DB* _db, h256 const& _root, Verification _v = Verification::Normal) { m_db = _db; setRoot(_root, _v); }
void init() { setRoot(insertNode(&RLPNull)); assert(node(m_root).size()); }
void init() { setRoot(forceInsertNode(&RLPNull)); assert(node(m_root).size()); }
void setRoot(h256 const& _root, Verification _v = Verification::Normal)
{
@ -88,12 +88,14 @@ public:
{
if (m_root == c_shaNull && !m_db->exists(m_root))
init();
}
/*std::cout << "Setting root to " << _root << " (patched to " << m_root << ")" << std::endl;*/
#if ETH_DEBUG
if (_v == Verification::Normal)
#endif
if (!node(m_root).size())
BOOST_THROW_EXCEPTION(RootNotFound());
}
}
/// True if the trie is uninitialised (i.e. that the DB doesn't contain the root node).
bool isNull() const { return !node(m_root).size(); }
@ -282,11 +284,17 @@ private:
std::string deref(RLP const& _n) const;
std::string node(h256 _h) const { return m_db->lookup(_h); }
void insertNode(h256 _h, bytesConstRef _v) { m_db->insert(_h, _v); }
void killNode(h256 _h) { m_db->kill(_h); }
h256 insertNode(bytesConstRef _v) { auto h = sha3(_v); insertNode(h, _v); return h; }
void killNode(RLP const& _d) { if (_d.data().size() >= 32) killNode(sha3(_d.data())); }
// These are low-level node insertion functions that just go straight through into the DB.
h256 forceInsertNode(bytesConstRef _v) { auto h = sha3(_v); forceInsertNode(h, _v); return h; }
void forceInsertNode(h256 _h, bytesConstRef _v) { m_db->insert(_h, _v); }
void forceKillNode(h256 _h) { m_db->kill(_h); }
// This are semantically-aware node insertion functions that only kills when the node's
// data is < 32 bytes. It can safely be used when pruning the trie but won't work correctly
// for the special case of the root (which is always looked up via a hash). In that case,
// use forceKillNode().
void killNode(RLP const& _d) { if (_d.data().size() >= 32) forceKillNode(sha3(_d.data())); }
h256 m_root;
DB* m_db = nullptr;
@ -743,8 +751,8 @@ template <class DB> void GenericTrieDB<DB>::insert(bytesConstRef _key, bytesCons
// However, we know it's the root node and thus always hashed.
// So, if it's less than 32 (and thus should have been deleted but wasn't) then we delete it here.
if (rv.size() < 32)
killNode(m_root);
m_root = insertNode(&b);
forceKillNode(m_root);
m_root = forceInsertNode(&b);
}
template <class DB> std::string GenericTrieDB<DB>::at(bytesConstRef _key) const
@ -890,8 +898,8 @@ template <class DB> void GenericTrieDB<DB>::remove(bytesConstRef _key)
if (b.size())
{
if (rv.size() < 32)
killNode(m_root);
m_root = insertNode(&b);
forceKillNode(m_root);
m_root = forceInsertNode(&b);
}
}
@ -1081,7 +1089,7 @@ template <class DB> RLPStream& GenericTrieDB<DB>::streamNode(RLPStream& _s, byte
if (_b.size() < 32)
_s.appendRaw(_b);
else
_s.append(insertNode(&_b));
_s.append(forceInsertNode(&_b));
return _s;
}
@ -1122,7 +1130,7 @@ template <class DB> bytes GenericTrieDB<DB>::graft(RLP const& _orig)
// remove second item from the trie after derefrencing it into s & n.
auto lh = _orig[1].toHash<h256>();
s = node(lh);
killNode(lh);
forceKillNode(lh);
n = RLP(s);
}
assert(n.itemCount() == 2);

4
libethash-cl/ethash_cl_miner.cpp

@ -120,9 +120,7 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
void ethash_cl_miner::finish()
{
if (m_queue())
{
m_queue.finish();
}
}
bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned workgroup_size, unsigned _platformId, unsigned _deviceId)
@ -162,9 +160,7 @@ bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned work
return false;
}
if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
{
m_opencl_1_1 = true;
}
// create context
m_context = cl::Context(std::vector<cl::Device>(&device, &device + 1));

5
libethash/CMakeLists.txt

@ -10,8 +10,7 @@ if (NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
endif()
set(FILES util.c
util.h
set(FILES util.h
io.c
internal.c
ethash.h
@ -21,7 +20,7 @@ set(FILES util.c
data_sizes.h)
if (MSVC)
list(APPEND FILES io_win32.c)
list(APPEND FILES util_win32.c io_win32.c mmap_win32.c)
else()
list(APPEND FILES io_posix.c)
endif()

72
libethash/endian.h

@ -3,38 +3,6 @@
#include <stdint.h>
#include "compiler.h"
static const uint8_t BitReverseTable256[] =
{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
static inline uint32_t bitfn_swap32(uint32_t a) {
return (BitReverseTable256[a & 0xff] << 24) |
(BitReverseTable256[(a >> 8) & 0xff] << 16) |
(BitReverseTable256[(a >> 16) & 0xff] << 8) |
(BitReverseTable256[(a >> 24) & 0xff]);
}
static inline uint64_t bitfn_swap64(uint64_t a) {
return ((uint64_t) bitfn_swap32((uint32_t) (a >> 32))) |
(((uint64_t) bitfn_swap32((uint32_t) a)) << 32);
}
#if defined(__MINGW32__) || defined(_WIN32)
# define LITTLE_ENDIAN 1234
# define BYTE_ORDER LITTLE_ENDIAN
@ -53,21 +21,51 @@ static inline uint64_t bitfn_swap64(uint64_t a) {
# define BIG_ENDIAN 1234
# define BYTE_ORDER BIG_ENDIAN
#else
# include <endian.h>
#endif
#if defined(_WIN32)
#include <stdlib.h>
#define ethash_swap_u32(input_) _byteswap_ulong(input_)
#define ethash_swap_u64(input_) _byteswap_uint64(input_)
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define ethash_swap_u32(input_) OSSwapInt32(input_)
#define ethash_swap_u64(input_) OSSwapInt64(input_)
#else // posix
#include <byteswap.h>
#define ethash_swap_u32(input_) __bswap_32(input_)
#define ethash_swap_u64(input_) __bswap_64(input_)
#endif
#if LITTLE_ENDIAN == BYTE_ORDER
#define fix_endian32(x) (x)
#define fix_endian64(x) (x)
#define fix_endian32(dst_ ,src_) dst_ = src_
#define fix_endian32_same(val_)
#define fix_endian64(dst_, src_) dst_ = src_
#define fix_endian64_same(val_)
#define fix_endian_arr32(arr_, size_)
#define fix_endian_arr64(arr_, size_)
#elif BIG_ENDIAN == BYTE_ORDER
#define fix_endian32(x) bitfn_swap32(x)
#define fix_endian64(x) bitfn_swap64(x)
#define fix_endian32(dst_, src_) dst_ = ethash_swap_u32(src_)
#define fix_endian32_same(val_) val_ = ethash_swap_u32(val_)
#define fix_endian64(dst_, src_) dst_ = ethash_swap_u64(src_
#define fix_endian64_same(val_) val_ = ethash_swap_u64(val_)
#define fix_endian_arr32(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
arr_[i_] = ethash_swap_u32(arr_[i_]); \
} \
while (0)
#define fix_endian_arr64(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
arr_[i_] = ethash_swap_u64(arr_[i_]); \
} \
while (0) \
#else
# error "endian not supported"

184
libethash/ethash.h

@ -24,7 +24,6 @@
#include <stdbool.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include "compiler.h"
#define ETHASH_REVISION 23
@ -38,101 +37,110 @@
#define ETHASH_DATASET_PARENTS 256
#define ETHASH_CACHE_ROUNDS 3
#define ETHASH_ACCESSES 64
#define ETHASH_DAG_MAGIC_NUM_SIZE 8
#define ETHASH_DAG_MAGIC_NUM 0xFEE1DEADBADDCAFE
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ethash_params {
uint64_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
uint64_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
} ethash_params;
/// Type of a seedhash/blockhash e.t.c.
typedef struct ethash_h256 { uint8_t b[32]; } ethash_h256_t;
typedef struct ethash_return_value {
uint8_t result[32];
uint8_t mix_hash[32];
} ethash_return_value;
uint64_t ethash_get_datasize(const uint32_t block_number);
uint64_t ethash_get_cachesize(const uint32_t block_number);
// initialize the parameters
static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) {
params->full_size = ethash_get_datasize(block_number);
params->cache_size = ethash_get_cachesize(block_number);
}
/***********************************
* OLD API *************************
***********************************
******************** (deprecated) *
***********************************/
// convenience macro to statically initialize an h256_t
// usage:
// ethash_h256_t a = ethash_h256_static_init(1, 2, 3, ... )
// have to provide all 32 values. If you don't provide all the rest
// will simply be unitialized (not guranteed to be 0)
#define ethash_h256_static_init(...) \
{ {__VA_ARGS__} }
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number);
void ethash_mkcache(void *cache, ethash_params const *params, const uint8_t seed[32]);
void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
void ethash_compute_full_data(void *mem, ethash_params const *params, void const *cache);
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
struct ethash_light;
typedef struct ethash_light* ethash_light_t;
struct ethash_full;
typedef struct ethash_full* ethash_full_t;
typedef int(*ethash_callback_t)(unsigned);
/***********************************
* NEW API *************************
***********************************/
// TODO: compute params and seed in ethash_new_light; it should take only block_number
// TODO: store params in ethash_light_t/ethash_full_t to avoid having to repass into compute/new_full
typedef uint8_t const ethash_seedhash_t[32];
typedef void const* ethash_light_t;
static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) {
void* ret = malloc((size_t)params->cache_size);
ethash_mkcache(ret, params, seed);
return ret;
}
static inline void ethash_compute_light(ethash_return_value *ret, ethash_light_t light, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
ethash_light(ret, light, params, header_hash, nonce);
}
static inline void ethash_delete_light(ethash_light_t light) {
free((void*)light);
}
typedef void const* ethash_full_t;
static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) {
void* ret = malloc((size_t)params->full_size);
ethash_compute_full_data(ret, params, light);
return ret;
}
static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) {
ethash_compute_full_data(full, params, cache);
}
static inline void ethash_compute_full(ethash_return_value *ret, void const *full, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
ethash_full(ret, full, params, header_hash, nonce);
}
/// @brief Compare two s256-bit big-endian values.
/// @returns 1 if @a a is less than or equal to @a b, 0 otherwise.
/// Both parameters are 256-bit big-endian values.
static inline int ethash_leq_be256(const uint8_t a[32], const uint8_t b[32]) {
// Boundary is big endian
for (int i = 0; i < 32; i++) {
if (a[i] == b[i])
continue;
return a[i] < b[i];
}
return 1;
}
/// Perofrms a cursory check on the validity of the nonce.
/// @returns 1 if the nonce may possibly be valid for the given header_hash & boundary.
/// @p boundary equivalent to 2 ^ 256 / block_difficulty, represented as a 256-bit big-endian.
int ethash_preliminary_check_boundary(
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32],
const uint8_t boundary[32]);
#define ethash_quick_check_difficulty ethash_preliminary_check_boundary
#define ethash_check_difficulty ethash_leq_be256
typedef struct ethash_return_value {
ethash_h256_t result;
ethash_h256_t mix_hash;
bool success;
} ethash_return_value_t;
/**
* Allocate and initialize a new ethash_light handler
*
* @param block_number The block number for which to create the handler
* @return Newly allocated ethash_light handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
*/
ethash_light_t ethash_light_new(uint64_t block_number);
/**
* Frees a previously allocated ethash_light handler
* @param light The light handler to free
*/
void ethash_light_delete(ethash_light_t light);
/**
* Calculate the light client data
*
* @param light The light client handler
* @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix
* @return an object of ethash_return_value_t holding the return values
*/
ethash_return_value_t ethash_light_compute(
ethash_light_t light,
ethash_h256_t const header_hash,
uint64_t nonce
);
/**
* Allocate and initialize a new ethash_full handler
*
* @param light The light handler containing the cache.
* @param callback A callback function with signature of @ref ethash_callback_t
* It accepts an unsigned with which a progress of DAG calculation
* can be displayed. If all goes well the callback should return 0.
* If a non-zero value is returned then DAG generation will stop.
* Be advised. A progress value of 100 means that DAG creation is
* almost complete and that this function will soon return succesfully.
* It does not mean that the function has already had a succesfull return.
* @return Newly allocated ethash_full handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data()
*/
ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback);
/**
* Frees a previously allocated ethash_full handler
* @param full The light handler to free
*/
void ethash_full_delete(ethash_full_t full);
/**
* Calculate the full client data
*
* @param full The full client handler
* @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix
* @return An object of ethash_return_value to hold the return value
*/
ethash_return_value_t ethash_full_compute(
ethash_full_t full,
ethash_h256_t const header_hash,
uint64_t nonce
);
/**
* Get a pointer to the full DAG data
*/
void const* ethash_full_dag(ethash_full_t full);
/**
* Get the size of the DAG data
*/
uint64_t ethash_full_dag_size(ethash_full_t full);
/**
* Calculate the seedhash for a given block number
*/
ethash_h256_t ethash_get_seedhash(uint64_t block_number);
#ifdef __cplusplus
}

5
libethash/fnv.h

@ -29,8 +29,9 @@ extern "C" {
#define FNV_PRIME 0x01000193
static inline uint32_t fnv_hash(const uint32_t x, const uint32_t y) {
return x*FNV_PRIME ^ y;
static inline uint32_t fnv_hash(uint32_t const x, uint32_t const y)
{
return x * FNV_PRIME ^ y;
}
#ifdef __cplusplus

428
libethash/internal.c

@ -23,11 +23,15 @@
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <errno.h>
#include <math.h>
#include "mmap.h"
#include "ethash.h"
#include "fnv.h"
#include "endian.h"
#include "internal.h"
#include "data_sizes.h"
#include "io.h"
#ifdef WITH_CRYPTOPP
@ -37,12 +41,14 @@
#include "sha3.h"
#endif // WITH_CRYPTOPP
uint64_t ethash_get_datasize(const uint32_t block_number) {
uint64_t ethash_get_datasize(uint64_t const block_number)
{
assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return dag_sizes[block_number / ETHASH_EPOCH_LENGTH];
}
uint64_t ethash_get_cachesize(const uint32_t block_number) {
uint64_t ethash_get_cachesize(uint64_t const block_number)
{
assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return cache_sizes[block_number / ETHASH_EPOCH_LENGTH];
}
@ -50,25 +56,29 @@ uint64_t ethash_get_cachesize(const uint32_t block_number) {
// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014)
// https://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf
// SeqMemoHash(s, R, N)
void static ethash_compute_cache_nodes(
node *const nodes,
ethash_params const *params,
const uint8_t seed[32]) {
assert((params->cache_size % sizeof(node)) == 0);
uint32_t const num_nodes = (uint32_t) (params->cache_size / sizeof(node));
bool static ethash_compute_cache_nodes(
node* const nodes,
uint64_t cache_size,
ethash_h256_t const* seed
)
{
if (cache_size % sizeof(node) != 0) {
return false;
}
uint32_t const num_nodes = (uint32_t) (cache_size / sizeof(node));
SHA3_512(nodes[0].bytes, seed, 32);
SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32);
for (unsigned i = 1; i != num_nodes; ++i) {
for (uint32_t i = 1; i != num_nodes; ++i) {
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
}
for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) {
for (uint32_t j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (uint32_t i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes;
node data;
data = nodes[(num_nodes - 1 + i) % num_nodes];
for (unsigned w = 0; w != NODE_WORDS; ++w) {
for (uint32_t w = 0; w != NODE_WORDS; ++w) {
data.words[w] ^= nodes[idx].words[w];
}
SHA3_512(nodes[i].bytes, data.bytes, sizeof(data));
@ -76,36 +86,22 @@ void static ethash_compute_cache_nodes(
}
// now perform endian conversion
#if BYTE_ORDER != LITTLE_ENDIAN
for (unsigned w = 0; w != (num_nodes*NODE_WORDS); ++w)
{
nodes->words[w] = fix_endian32(nodes->words[w]);
}
#endif
}
void ethash_mkcache(
void *cache,
ethash_params const *params,
const uint8_t seed[32]) {
node *nodes = (node *) cache;
ethash_compute_cache_nodes(nodes, params, seed);
fix_endian_arr32(nodes->words, num_nodes * NODE_WORDS);
return true;
}
void ethash_calculate_dag_item(
node *const ret,
const unsigned node_index,
const struct ethash_params *params,
const void *cache) {
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
node const *cache_nodes = (node const *) cache;
node const *init = &cache_nodes[node_index % num_parent_nodes];
node* const ret,
uint32_t node_index,
ethash_light_t const light
)
{
uint32_t num_parent_nodes = (uint32_t) (light->cache_size / sizeof(node));
node const* cache_nodes = (node const *) light->cache;
node const* init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
ret->words[0] ^= node_index;
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
#if defined(_M_X64) && ENABLE_SSE
__m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME);
__m128i xmm0 = ret->xmm[0];
@ -114,8 +110,8 @@ void ethash_calculate_dag_item(
__m128i xmm3 = ret->xmm[3];
#endif
for (unsigned i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes;
for (uint32_t i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
uint32_t parent_index = fnv_hash(node_index ^ i, ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index];
#if defined(_M_X64) && ENABLE_SSE
@ -143,73 +139,79 @@ void ethash_calculate_dag_item(
}
#endif
}
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
}
void ethash_compute_full_data(
void *mem,
ethash_params const *params,
void const *cache) {
assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0);
assert((params->full_size % sizeof(node)) == 0);
node *full_nodes = mem;
bool ethash_compute_full_data(
void* mem,
uint64_t full_size,
ethash_light_t const light,
ethash_callback_t callback
)
{
if (full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 ||
(full_size % sizeof(node)) != 0) {
return false;
}
uint32_t const max_n = (uint32_t)(full_size / sizeof(node));
node* full_nodes = mem;
double const progress_change = 1.0f / max_n;
double progress = 0.0f;
// now compute full nodes
for (unsigned n = 0; n != (params->full_size / sizeof(node)); ++n) {
ethash_calculate_dag_item(&(full_nodes[n]), n, params, cache);
for (uint32_t n = 0; n != max_n; ++n) {
if (callback &&
n % (max_n / 100) == 0 &&
callback((unsigned int)(ceil(progress * 100.0f))) != 0) {
return false;
}
progress += progress_change;
ethash_calculate_dag_item(&(full_nodes[n]), n, light);
}
return true;
}
static void ethash_hash(
ethash_return_value *ret,
node const *full_nodes,
void const *cache,
ethash_params const *params,
const uint8_t header_hash[32],
const uint64_t nonce) {
assert((params->full_size % MIX_WORDS) == 0);
static bool ethash_hash(
ethash_return_value_t* ret,
node const* full_nodes,
ethash_light_t const light,
uint64_t full_size,
ethash_h256_t const header_hash,
uint64_t const nonce
)
{
if (full_size % MIX_WORDS != 0) {
return false;
}
// pack hash and nonce together into first 40 bytes of s_mix
assert(sizeof(node) * 8 == 512);
node s_mix[MIX_NODES + 1];
memcpy(s_mix[0].bytes, header_hash, 32);
#if BYTE_ORDER != LITTLE_ENDIAN
s_mix[0].double_words[4] = fix_endian64(nonce);
#else
s_mix[0].double_words[4] = nonce;
#endif
memcpy(s_mix[0].bytes, &header_hash, 32);
fix_endian64(s_mix[0].double_words[4], nonce);
// compute sha3-512 hash and replicate across mix
SHA3_512(s_mix->bytes, s_mix->bytes, 40);
fix_endian_arr32(s_mix[0].words, 16);
#if BYTE_ORDER != LITTLE_ENDIAN
for (unsigned w = 0; w != 16; ++w) {
s_mix[0].words[w] = fix_endian32(s_mix[0].words[w]);
}
#endif
node *const mix = s_mix + 1;
for (unsigned w = 0; w != MIX_WORDS; ++w) {
node* const mix = s_mix + 1;
for (uint32_t w = 0; w != MIX_WORDS; ++w) {
mix->words[w] = s_mix[0].words[w % NODE_WORDS];
}
unsigned const
page_size = sizeof(uint32_t) * MIX_WORDS,
num_full_pages = (unsigned) (params->full_size / page_size);
unsigned const page_size = sizeof(uint32_t) * MIX_WORDS;
unsigned const num_full_pages = (unsigned) (full_size / page_size);
for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) {
uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages;
uint32_t const index = fnv_hash(s_mix->words[0] ^ i, mix->words[i % MIX_WORDS]) % num_full_pages;
for (unsigned n = 0; n != MIX_NODES; ++n) {
const node *dag_node = &full_nodes[MIX_NODES * index + n];
if (!full_nodes) {
node const* dag_node;
if (full_nodes) {
dag_node = &full_nodes[MIX_NODES * index + n];
} else {
node tmp_node;
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, params, cache);
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light);
dag_node = &tmp_node;
}
@ -237,7 +239,7 @@ static void ethash_hash(
}
// compress mix
for (unsigned w = 0; w != MIX_WORDS; w += 4) {
for (uint32_t w = 0; w != MIX_WORDS; w += 4) {
uint32_t reduction = mix->words[w + 0];
reduction = reduction * FNV_PRIME ^ mix->words[w + 1];
reduction = reduction * FNV_PRIME ^ mix->words[w + 2];
@ -245,56 +247,250 @@ static void ethash_hash(
mix->words[w / 4] = reduction;
}
#if BYTE_ORDER != LITTLE_ENDIAN
for (unsigned w = 0; w != MIX_WORDS/4; ++w) {
mix->words[w] = fix_endian32(mix->words[w]);
}
#endif
memcpy(ret->mix_hash, mix->bytes, 32);
fix_endian_arr32(mix->words, MIX_WORDS / 4);
memcpy(&ret->mix_hash, mix->bytes, 32);
// final Keccak hash
SHA3_256(ret->result, s_mix->bytes, 64 + 32); // Keccak-256(s + compressed_mix)
SHA3_256(&ret->result, s_mix->bytes, 64 + 32); // Keccak-256(s + compressed_mix)
return true;
}
void ethash_quick_hash(
uint8_t return_hash[32],
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32]) {
ethash_h256_t* return_hash,
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_h256_t const* mix_hash
)
{
uint8_t buf[64 + 32];
memcpy(buf, header_hash, 32);
#if BYTE_ORDER != LITTLE_ENDIAN
nonce = fix_endian64(nonce);
#endif
fix_endian64_same(nonce);
memcpy(&(buf[32]), &nonce, 8);
SHA3_512(buf, buf, 40);
memcpy(&(buf[64]), mix_hash, 32);
SHA3_256(return_hash, buf, 64 + 32);
}
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) {
memset(seedhash, 0, 32);
const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH;
ethash_h256_t ethash_get_seedhash(uint64_t block_number)
{
ethash_h256_t ret;
ethash_h256_reset(&ret);
uint64_t const epochs = block_number / ETHASH_EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, seedhash, 32);
SHA3_256(&ret, (uint8_t*)&ret, 32);
return ret;
}
int ethash_preliminary_check_boundary(
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32],
const uint8_t difficulty[32]) {
bool ethash_quick_check_difficulty(
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_h256_t const* mix_hash,
ethash_h256_t const* difficulty
)
{
ethash_h256_t return_hash;
ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash);
return ethash_check_difficulty(&return_hash, difficulty);
}
uint8_t return_hash[32];
ethash_quick_hash(return_hash, header_hash, nonce, mix_hash);
return ethash_leq_be256(return_hash, difficulty);
ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed)
{
struct ethash_light *ret;
ret = calloc(sizeof(*ret), 1);
if (!ret) {
return NULL;
}
ret->cache = malloc((size_t)cache_size);
if (!ret->cache) {
goto fail_free_light;
}
node* nodes = (node*)ret->cache;
if (!ethash_compute_cache_nodes(nodes, cache_size, seed)) {
goto fail_free_cache_mem;
}
ret->cache_size = cache_size;
return ret;
fail_free_cache_mem:
free(ret->cache);
fail_free_light:
free(ret);
return NULL;
}
ethash_light_t ethash_light_new(uint64_t block_number)
{
ethash_h256_t seedhash = ethash_get_seedhash(block_number);
ethash_light_t ret;
ret = ethash_light_new_internal(ethash_get_cachesize(block_number), &seedhash);
ret->block_number = block_number;
return ret;
}
void ethash_light_delete(ethash_light_t light)
{
if (light->cache) {
free(light->cache);
}
free(light);
}
ethash_return_value_t ethash_light_compute_internal(
ethash_light_t light,
uint64_t full_size,
ethash_h256_t const header_hash,
uint64_t nonce
)
{
ethash_return_value_t ret;
ret.success = true;
if (!ethash_hash(&ret, NULL, light, full_size, header_hash, nonce)) {
ret.success = false;
}
return ret;
}
ethash_return_value_t ethash_light_compute(
ethash_light_t light,
ethash_h256_t const header_hash,
uint64_t nonce
)
{
uint64_t full_size = ethash_get_datasize(light->block_number);
return ethash_light_compute_internal(light, full_size, header_hash, nonce);
}
static bool ethash_mmap(struct ethash_full* ret, FILE* f)
{
int fd;
char* mmapped_data;
ret->file = f;
if ((fd = ethash_fileno(ret->file)) == -1) {
return false;
}
mmapped_data= mmap(
NULL,
(size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (mmapped_data == MAP_FAILED) {
return false;
}
ret->data = (node*)(mmapped_data + ETHASH_DAG_MAGIC_NUM_SIZE);
return true;
}
ethash_full_t ethash_full_new_internal(
char const* dirname,
ethash_h256_t const seed_hash,
uint64_t full_size,
ethash_light_t const light,
ethash_callback_t callback
)
{
struct ethash_full* ret;
FILE *f = NULL;
ret = calloc(sizeof(*ret), 1);
if (!ret) {
return NULL;
}
ret->file_size = (size_t)full_size;
switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) {
case ETHASH_IO_FAIL:
goto fail_free_full;
case ETHASH_IO_MEMO_MATCH:
if (!ethash_mmap(ret, f)) {
goto fail_close_file;
}
return ret;
case ETHASH_IO_MEMO_SIZE_MISMATCH:
// if a DAG of same filename but unexpected size is found, silently force new file creation
if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) {
goto fail_free_full;
}
// fallthrough to the mismatch case here, DO NOT go through match
case ETHASH_IO_MEMO_MISMATCH:
if (!ethash_mmap(ret, f)) {
goto fail_close_file;
}
break;
}
if (!ethash_compute_full_data(ret->data, full_size, light, callback)) {
goto fail_free_full_data;
}
// after the DAG has been filled then we finalize it by writting the magic number at the beginning
if (fseek(f, 0, SEEK_SET) != 0) {
goto fail_free_full_data;
}
uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM;
if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
goto fail_free_full_data;
}
fflush(f); // make sure the magic number IS there
return ret;
fail_free_full_data:
// could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(ret->data, (size_t)full_size);
fail_close_file:
fclose(ret->file);
fail_free_full:
free(ret);
return NULL;
}
ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback)
{
char strbuf[256];
if (!ethash_get_default_dirname(strbuf, 256)) {
return NULL;
}
uint64_t full_size = ethash_get_datasize(light->block_number);
ethash_h256_t seedhash = ethash_get_seedhash(light->block_number);
return ethash_full_new_internal(strbuf, seedhash, full_size, light, callback);
}
void ethash_full_delete(ethash_full_t full)
{
// could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(full->data, (size_t)full->file_size);
if (full->file) {
fclose(full->file);
}
free(full);
}
ethash_return_value_t ethash_full_compute(
ethash_full_t full,
ethash_h256_t const header_hash,
uint64_t nonce
)
{
ethash_return_value_t ret;
ret.success = true;
if (!ethash_hash(
&ret,
(node const*)full->data,
NULL,
full->file_size,
header_hash,
nonce)) {
ret.success = false;
}
return ret;
}
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
ethash_hash(ret, (node const *) full_mem, NULL, params, previous_hash, nonce);
void const* ethash_full_dag(ethash_full_t full)
{
return full->data;
}
void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
ethash_hash(ret, NULL, cache, params, previous_hash, nonce);
uint64_t ethash_full_dag_size(ethash_full_t full)
{
return full->file_size;
}

136
libethash/internal.h

@ -2,6 +2,7 @@
#include "compiler.h"
#include "endian.h"
#include "ethash.h"
#include <stdio.h>
#define ENABLE_SSE 0
@ -30,18 +31,139 @@ typedef union node {
} node;
static inline uint8_t ethash_h256_get(ethash_h256_t const* hash, unsigned int i)
{
return hash->b[i];
}
static inline void ethash_h256_set(ethash_h256_t* hash, unsigned int i, uint8_t v)
{
hash->b[i] = v;
}
static inline void ethash_h256_reset(ethash_h256_t* hash)
{
memset(hash, 0, 32);
}
// Returns if hash is less than or equal to difficulty
static inline bool ethash_check_difficulty(
ethash_h256_t const* hash,
ethash_h256_t const* difficulty
)
{
// Difficulty is big endian
for (int i = 0; i < 32; i++) {
if (ethash_h256_get(hash, i) == ethash_h256_get(difficulty, i)) {
continue;
}
return ethash_h256_get(hash, i) < ethash_h256_get(difficulty, i);
}
return true;
}
bool ethash_quick_check_difficulty(
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_h256_t const* mix_hash,
ethash_h256_t const* difficulty
);
struct ethash_light {
void* cache;
uint64_t cache_size;
uint64_t block_number;
};
/**
* Allocate and initialize a new ethash_light handler. Internal version
*
* @param cache_size The size of the cache in bytes
* @param seed Block seedhash to be used during the computation of the
* cache nodes
* @return Newly allocated ethash_light handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
*/
ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed);
/**
* Calculate the light client data. Internal version.
*
* @param light The light client handler
* @param full_size The size of the full data in bytes.
* @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix
* @return The resulting hash.
*/
ethash_return_value_t ethash_light_compute_internal(
ethash_light_t light,
uint64_t full_size,
ethash_h256_t const header_hash,
uint64_t nonce
);
struct ethash_full {
FILE* file;
uint64_t file_size;
node* data;
};
/**
* Allocate and initialize a new ethash_full handler. Internal version.
*
* @param dirname The directory in which to put the DAG file.
* @param seedhash The seed hash of the block. Used in the DAG file naming.
* @param full_size The size of the full data in bytes.
* @param cache A cache object to use that was allocated with @ref ethash_cache_new().
* Iff this function succeeds the ethash_full_t will take memory
* memory ownership of the cache and free it at deletion. If
* not then the user still has to handle freeing of the cache himself.
* @param callback A callback function with signature of @ref ethash_callback_t
* It accepts an unsigned with which a progress of DAG calculation
* can be displayed. If all goes well the callback should return 0.
* If a non-zero value is returned then DAG generation will stop.
* @return Newly allocated ethash_full handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data()
*/
ethash_full_t ethash_full_new_internal(
char const* dirname,
ethash_h256_t const seed_hash,
uint64_t full_size,
ethash_light_t const light,
ethash_callback_t callback
);
void ethash_calculate_dag_item(
node *const ret,
const unsigned node_index,
ethash_params const *params,
void const *cache
node* const ret,
uint32_t node_index,
ethash_light_t const cache
);
void ethash_quick_hash(
uint8_t return_hash[32],
const uint8_t header_hash[32],
ethash_h256_t* return_hash,
ethash_h256_t const* header_hash,
const uint64_t nonce,
const uint8_t mix_hash[32]);
ethash_h256_t const* mix_hash
);
uint64_t ethash_get_datasize(uint64_t const block_number);
uint64_t ethash_get_cachesize(uint64_t const block_number);
/**
* Compute the memory data for a full node's memory
*
* @param mem A pointer to an ethash full's memory
* @param full_size The size of the full data in bytes
* @param cache A cache object to use in the calculation
* @param callback The callback function. Check @ref ethash_full_new() for details.
* @return true if all went fine and false for invalid parameters
*/
bool ethash_compute_full_data(
void* mem,
uint64_t full_size,
ethash_light_t const light,
ethash_callback_t callback
);
#ifdef __cplusplus
}

119
libethash/io.c

@ -22,68 +22,81 @@
#include <string.h>
#include <stdio.h>
// silly macro to save some typing
#define PASS_ARR(c_) (c_), sizeof(c_)
static bool ethash_io_write_file(char const *dirname,
char const* filename,
size_t filename_length,
void const* data,
size_t data_size)
enum ethash_io_rc ethash_io_prepare(
char const* dirname,
ethash_h256_t const seedhash,
FILE** output_file,
uint64_t file_size,
bool force_create
)
{
bool ret = false;
char *fullname = ethash_io_create_filename(dirname, filename, filename_length);
if (!fullname) {
return false;
}
FILE *f = fopen(fullname, "wb");
if (!f) {
goto free_name;
}
if (data_size != fwrite(data, 1, data_size, f)) {
goto close;
}
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
ret = true;
close:
fclose(f);
free_name:
free(fullname);
return ret;
}
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
size_t *data_size)
{
char info_buffer[DAG_MEMO_BYTESIZE];
// allocate the bytes
uint8_t *temp_data_ptr = malloc((size_t)params->full_size);
if (!temp_data_ptr) {
// assert directory exists
if (!ethash_mkdir(dirname)) {
goto end;
}
ethash_compute_full_data(temp_data_ptr, params, cache);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, (size_t)params->full_size)) {
goto fail_free;
ethash_io_mutable_name(ETHASH_REVISION, &seedhash, mutable_name);
char* tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name));
if (!tmpfile) {
goto end;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, info_buffer);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) {
goto fail_free;
FILE *f;
if (!force_create) {
// try to open the file
f = ethash_fopen(tmpfile, "rb+");
if (f) {
size_t found_size;
if (!ethash_file_size(f, &found_size)) {
fclose(f);
goto free_memo;
}
if (file_size != found_size - ETHASH_DAG_MAGIC_NUM_SIZE) {
fclose(f);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
// compare the magic number, no need to care about endianess since it's local
uint64_t magic_num;
if (fread(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
// I/O error
fclose(f);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
if (magic_num != ETHASH_DAG_MAGIC_NUM) {
fclose(f);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
ret = ETHASH_IO_MEMO_MATCH;
goto set_file;
}
}
*data = temp_data_ptr;
*data_size = (size_t)params->full_size;
return true;
// file does not exist, will need to be created
f = ethash_fopen(tmpfile, "wb+");
if (!f) {
goto free_memo;
}
// make sure it's of the proper size
if (fseek(f, (long int)(file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1), SEEK_SET) != 0) {
fclose(f);
goto free_memo;
}
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
goto set_file;
fail_free:
free(temp_data_ptr);
ret = ETHASH_IO_MEMO_MATCH;
set_file:
*output_file = f;
free_memo:
free(tmpfile);
end:
return false;
return ret;
}
#undef PASS_ARR

193
libethash/io.h

@ -22,94 +22,163 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#ifdef __cplusplus
#define __STDC_FORMAT_MACROS 1
#endif
#include <inttypes.h>
#include "endian.h"
#include "ethash.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ethash_blockhash { uint8_t b[32]; } ethash_blockhash_t;
static const char DAG_FILE_NAME[] = "full";
static const char DAG_MEMO_NAME[] = "full.info";
// MSVC thinks that "static const unsigned int" is not a compile time variable. Sorry for the #define :(
#define DAG_MEMO_BYTESIZE 36
// Maximum size for mutable part of DAG file name
// 6 is for "full-R", the suffix of the filename
// 10 is for maximum number of digits of a uint32_t (for REVISION)
// 1 is for - and 16 is for the first 16 hex digits for first 8 bytes of
// the seedhash and last 1 is for the null terminating character
// Reference: https://github.com/ethereum/wiki/wiki/Ethash-DAG
#define DAG_MUTABLE_NAME_MAX_SIZE (6 + 10 + 1 + 16 + 1)
/// Possible return values of @see ethash_io_prepare
enum ethash_io_rc {
ETHASH_IO_FAIL = 0, ///< There has been an IO failure
ETHASH_IO_MEMO_MISMATCH, ///< Memo file either did not exist or there was content mismatch
ETHASH_IO_MEMO_MATCH, ///< Memo file existed and contents matched. No need to do anything
ETHASH_IO_MEMO_SIZE_MISMATCH, ///< DAG with revision/hash match, but file size was wrong.
ETHASH_IO_MEMO_MISMATCH, ///< The DAG file did not exist or there was revision/hash mismatch
ETHASH_IO_MEMO_MATCH, ///< DAG file existed and revision/hash matched. No need to do anything
};
// small hack for windows. I don't feel I should use va_args and forward just
// to have this one function properly cross-platform abstracted
#if defined(_WIN32) && !defined(__GNUC__)
#define snprintf(...) sprintf_s(__VA_ARGS__)
#endif
/**
* Prepares io for ethash
*
* Create the DAG directory if it does not exist, and check if the memo file matches.
* If it does not match then it's deleted to pave the way for @ref ethash_io_write()
* Create the DAG directory and the DAG file if they don't exist.
*
* @param dirname A null terminated c-string of the path of the ethash
* @param[in] dirname A null terminated c-string of the path of the ethash
* data directory. If it does not exist it's created.
* @param seedhash The seedhash of the current block number
* @param[in] seedhash The seedhash of the current block number, used in the
* naming of the file as can be seen from the spec at:
* https://github.com/ethereum/wiki/wiki/Ethash-DAG
* @param[out] output_file If there was no failure then this will point to an open
* file descriptor. User is responsible for closing it.
* In the case of memo match then the file is open on read
* mode, while on the case of mismatch a new file is created
* on write mode
* @param[in] file_size The size that the DAG file should have on disk
* @param[out] force_create If true then there is no check to see if the file
* already exists
* @return For possible return values @see enum ethash_io_rc
*/
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash);
enum ethash_io_rc ethash_io_prepare(
char const* dirname,
ethash_h256_t const seedhash,
FILE** output_file,
uint64_t file_size,
bool force_create
);
/**
* Fully computes data and writes it to the file on disk.
* An fopen wrapper for no-warnings crossplatform fopen.
*
* This function should be called after @see ethash_io_prepare() and only if
* its return value is @c ETHASH_IO_MEMO_MISMATCH. Will write both the full data
* and the memo file.
* Msvc compiler considers fopen to be insecure and suggests to use their
* alternative. This is a wrapper for this alternative. Another way is to
* #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does
* not sound like a good idea.
*
* @param[in] dirname A null terminated c-string of the path of the ethash
* data directory. Has to exist.
* @param[in] params An ethash_params object containing the full size
* and the cache size
* @param[in] seedhash The seedhash of the current block number
* @param[in] cache The cache data. Would have usually been calulated by
* @see ethash_prep_light().
* @param[out] data Pass a pointer to uint8_t by reference here. If the
* function is succesfull then this point to the allocated
* data calculated by @see ethash_prep_full(). Memory
* ownership is transfered to the callee. Remember that
* you eventually need to free this with a call to free().
* @param[out] data_size Pass a size_t by value. If the function is succesfull
* then this will contain the number of bytes allocated
* for @a data.
* @return True for success and false in case of failure.
* @param file_name The path to the file to open
* @param mode Opening mode. Check fopen()
* @return The FILE* or NULL in failure
*/
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
size_t *data_size);
FILE* ethash_fopen(char const* file_name, char const* mode);
static inline void ethash_io_serialize_info(uint32_t revision,
ethash_blockhash_t seed_hash,
char *output)
{
// if .info is only consumed locally we don't really care about endianess
memcpy(output, &revision, 4);
memcpy(output + 4, &seed_hash, 32);
}
/**
* An strncat wrapper for no-warnings crossplatform strncat.
*
* Msvc compiler considers strncat to be insecure and suggests to use their
* alternative. This is a wrapper for this alternative. Another way is to
* #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does
* not sound like a good idea.
*
* @param des Destination buffer
* @param dest_size Maximum size of the destination buffer. This is the
* extra argument for the MSVC secure strncat
* @param src Souce buffer
* @param count Number of bytes to copy from source
* @return If all is well returns the dest buffer. If there is an
* error returns NULL
*/
char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count);
/**
* A cross-platform mkdir wrapper to create a directory or assert it's there
*
* @param dirname The full path of the directory to create
* @return true if the directory was created or if it already
* existed
*/
bool ethash_mkdir(char const* dirname);
static inline char *ethash_io_create_filename(char const *dirname,
/**
* Get a file's size
*
* @param[in] f The open file stream whose size to get
* @param[out] size Pass a size_t by reference to contain the file size
* @return true in success and false if there was a failure
*/
bool ethash_file_size(FILE* f, size_t* ret_size);
/**
* Get a file descriptor number from a FILE stream
*
* @param f The file stream whose fd to get
* @return Platform specific fd handler
*/
int ethash_fileno(FILE* f);
/**
* Create the filename for the DAG.
*
* @param dirname The directory name in which the DAG file should reside
* If it does not end with a directory separator it is appended.
* @param filename The actual name of the file
* @param filename_length The length of the filename in bytes
* @return A char* containing the full name. User must deallocate.
*/
char* ethash_io_create_filename(
char const* dirname,
char const* filename,
size_t filename_length)
{
// in C the cast is not needed, but a C++ compiler will complain for invalid conversion
char *name = (char*)malloc(strlen(dirname) + filename_length);
if (!name) {
return NULL;
}
size_t filename_length
);
name[0] = '\0';
strcat(name, dirname);
strcat(name, filename);
return name;
}
/**
* Gets the default directory name for the DAG depending on the system
*
* The spec defining this directory is here: https://github.com/ethereum/wiki/wiki/Ethash-DAG
*
* @param[out] strbuf A string buffer of sufficient size to keep the
* null termninated string of the directory name
* @param[in] buffsize Size of @a strbuf in bytes
* @return true for success and false otherwise
*/
bool ethash_get_default_dirname(char* strbuf, size_t buffsize);
static inline bool ethash_io_mutable_name(
uint32_t revision,
ethash_h256_t const* seed_hash,
char* output
)
{
uint64_t hash = *((uint64_t*)seed_hash);
#if LITTLE_ENDIAN == BYTE_ORDER
hash = ethash_swap_u64(hash);
#endif
return snprintf(output, DAG_MUTABLE_NAME_MAX_SIZE, "full-R%u-%016" PRIx64, revision, hash) >= 0;
}
#ifdef __cplusplus
}

94
libethash/io_posix.c

@ -27,50 +27,76 @@
#include <stdio.h>
#include <unistd.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
FILE* ethash_fopen(char const* file_name, char const* mode)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
return fopen(file_name, mode);
}
// assert directory exists, full owner permissions and read/search for others
char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count)
{
return strlen(dest) + count + 1 <= dest_size ? strncat(dest, src, count) : NULL;
}
bool ethash_mkdir(char const* dirname)
{
int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (rc == -1 && errno != EEXIST) {
goto end;
}
return rc != -1 || errno == EEXIST;
}
int ethash_fileno(FILE *f)
{
return fileno(f);
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
char* ethash_io_create_filename(
char const* dirname,
char const* filename,
size_t filename_length
)
{
size_t dirlen = strlen(dirname);
size_t dest_size = dirlen + filename_length + 1;
if (dirname[dirlen] != '/') {
dest_size += 1;
}
char* name = malloc(dest_size);
if (!name) {
return NULL;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
name[0] = '\0';
ethash_strncat(name, dest_size, dirname, dirlen);
if (dirname[dirlen] != '/') {
ethash_strncat(name, dest_size, "/", 1);
}
ethash_strncat(name, dest_size, filename, filename_length);
return name;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
bool ethash_file_size(FILE* f, size_t* ret_size)
{
struct stat st;
int fd;
if ((fd = fileno(f)) == -1 || fstat(fd, &st) != 0) {
return false;
}
*ret_size = st.st_size;
return true;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (unlink(memofile) != 0) {
goto close;
bool ethash_get_default_dirname(char* strbuf, size_t buffsize)
{
static const char dir_suffix[] = ".ethash/";
strbuf[0] = '\0';
char* home_dir = getenv("HOME");
size_t len = strlen(home_dir);
if (!ethash_strncat(strbuf, buffsize, home_dir, len)) {
return false;
}
ret = ETHASH_IO_MEMO_MISMATCH;
if (home_dir[len] != '/') {
if (!ethash_strncat(strbuf, buffsize, "/", 1)) {
return false;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
}
return ethash_strncat(strbuf, buffsize, dir_suffix, sizeof(dir_suffix));
}

93
libethash/io_win32.c

@ -23,51 +23,78 @@
#include <direct.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <shlobj.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
FILE* ethash_fopen(char const* file_name, char const* mode)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
FILE* f;
return fopen_s(&f, file_name, mode) == 0 ? f : NULL;
}
// assert directory exists
char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count)
{
return strncat_s(dest, dest_size, src, count) == 0 ? dest : NULL;
}
bool ethash_mkdir(char const* dirname)
{
int rc = _mkdir(dirname);
if (rc == -1 && errno != EEXIST) {
goto end;
}
return rc != -1 || errno == EEXIST;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
int ethash_fileno(FILE* f)
{
return _fileno(f);
}
char* ethash_io_create_filename(
char const* dirname,
char const* filename,
size_t filename_length
)
{
size_t dirlen = strlen(dirname);
size_t dest_size = dirlen + filename_length + 1;
if (dirname[dirlen] != '\\' || dirname[dirlen] != '/') {
dest_size += 1;
}
char* name = malloc(dest_size);
if (!name) {
return NULL;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
name[0] = '\0';
ethash_strncat(name, dest_size, dirname, dirlen);
if (dirname[dirlen] != '\\' || dirname[dirlen] != '/') {
ethash_strncat(name, dest_size, "\\", 1);
}
ethash_strncat(name, dest_size, filename, filename_length);
return name;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
bool ethash_file_size(FILE* f, size_t* ret_size)
{
struct _stat st;
int fd;
if ((fd = _fileno(f)) == -1 || _fstat(fd, &st) != 0) {
return false;
}
*ret_size = st.st_size;
return true;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (_unlink(memofile) != 0) {
goto close;
bool ethash_get_default_dirname(char* strbuf, size_t buffsize)
{
static const char dir_suffix[] = "Appdata\\Ethash\\";
strbuf[0] = '\0';
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, (WCHAR*)strbuf))) {
return false;
}
ret = ETHASH_IO_MEMO_MISMATCH;
if (!ethash_strncat(strbuf, buffsize, "\\", 1)) {
return false;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
return ethash_strncat(strbuf, buffsize, dir_suffix, sizeof(dir_suffix));
}

47
libethash/mmap.h

@ -0,0 +1,47 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file mmap.h
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#if defined(__MINGW32__) || defined(_WIN32)
#include <sys/types.h>
#define PROT_READ 0x1
#define PROT_WRITE 0x2
/* This flag is only available in WinXP+ */
#ifdef FILE_MAP_EXECUTE
#define PROT_EXEC 0x4
#else
#define PROT_EXEC 0x0
#define FILE_MAP_EXECUTE 0
#endif
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *) -1)
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
void munmap(void* addr, size_t length);
#else // posix, yay! ^_^
#include <sys/mman.h>
#endif

84
libethash/mmap_win32.c

@ -0,0 +1,84 @@
/* mmap() replacement for Windows
*
* Author: Mike Frysinger <vapier@gentoo.org>
* Placed into the public domain
*/
/* References:
* CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
* CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
* MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
* UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
*/
#include <io.h>
#include <windows.h>
#include "mmap.h"
#ifdef __USE_FILE_OFFSET64
# define DWORD_HI(x) (x >> 32)
# define DWORD_LO(x) ((x) & 0xffffffff)
#else
# define DWORD_HI(x) (0)
# define DWORD_LO(x) (x)
#endif
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset)
{
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
return MAP_FAILED;
if (fd == -1) {
if (!(flags & MAP_ANON) || offset)
return MAP_FAILED;
} else if (flags & MAP_ANON)
return MAP_FAILED;
DWORD flProtect;
if (prot & PROT_WRITE) {
if (prot & PROT_EXEC)
flProtect = PAGE_EXECUTE_READWRITE;
else
flProtect = PAGE_READWRITE;
} else if (prot & PROT_EXEC) {
if (prot & PROT_READ)
flProtect = PAGE_EXECUTE_READ;
else if (prot & PROT_EXEC)
flProtect = PAGE_EXECUTE;
} else
flProtect = PAGE_READONLY;
off_t end = length + offset;
HANDLE mmap_fd, h;
if (fd == -1)
mmap_fd = INVALID_HANDLE_VALUE;
else
mmap_fd = (HANDLE)_get_osfhandle(fd);
h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL);
if (h == NULL)
return MAP_FAILED;
DWORD dwDesiredAccess;
if (prot & PROT_WRITE)
dwDesiredAccess = FILE_MAP_WRITE;
else
dwDesiredAccess = FILE_MAP_READ;
if (prot & PROT_EXEC)
dwDesiredAccess |= FILE_MAP_EXECUTE;
if (flags & MAP_PRIVATE)
dwDesiredAccess |= FILE_MAP_COPY;
void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length);
if (ret == NULL) {
ret = MAP_FAILED;
}
// since we are handling the file ourselves with fd, close the Windows Handle here
CloseHandle(h);
return ret;
}
void munmap(void* addr, size_t length)
{
UnmapViewOfFile(addr);
}
#undef DWORD_HI
#undef DWORD_LO

12
libethash/sha3.h

@ -8,17 +8,21 @@ extern "C" {
#include <stdint.h>
#include <stdlib.h>
struct ethash_h256;
#define decsha3(bits) \
int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t);
int sha3_##bits(uint8_t*, size_t, uint8_t const*, size_t);
decsha3(256)
decsha3(512)
static inline void SHA3_256(uint8_t * const ret, uint8_t const *data, const size_t size) {
sha3_256(ret, 32, data, size);
static inline void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t const size)
{
sha3_256((uint8_t*)ret, 32, data, size);
}
static inline void SHA3_512(uint8_t * const ret, uint8_t const *data, const size_t size) {
static inline void SHA3_512(uint8_t* ret, uint8_t const* data, size_t const size)
{
sha3_512(ret, 64, data, size);
}

11
libethash/sha3_cryptopp.cpp

@ -19,16 +19,19 @@
* @author Tim Hughes <tim@twistedfury.com>
* @date 2015
*/
#include <stdint.h>
#include <cryptopp/sha3.h>
extern "C" {
void SHA3_256(uint8_t *const ret, const uint8_t *data, size_t size) {
CryptoPP::SHA3_256().CalculateDigest(ret, data, size);
struct ethash_h256;
typedef struct ethash_h256 ethash_h256_t;
void SHA3_256(ethash_h256_t const* ret, uint8_t const* data, size_t size)
{
CryptoPP::SHA3_256().CalculateDigest((uint8_t*)ret, data, size);
}
void SHA3_512(uint8_t *const ret, const uint8_t *data, size_t size) {
void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size)
{
CryptoPP::SHA3_512().CalculateDigest(ret, data, size);
}
}

7
libethash/sha3_cryptopp.h

@ -2,13 +2,16 @@
#include "compiler.h"
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
void SHA3_256(uint8_t *const ret, const uint8_t *data, size_t size);
void SHA3_512(uint8_t *const ret, const uint8_t *data, size_t size);
struct ethash_h256;
void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t size);
void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size);
#ifdef __cplusplus
}

4
libethash/util.h

@ -27,9 +27,9 @@ extern "C" {
#endif
#ifdef _MSC_VER
void debugf(const char *str, ...);
void debugf(char const* str, ...);
#else
#define debugf(...) fprintf(stderr, __VA_ARGS__)
#define debugf printf
#endif
static inline uint32_t min_u32(uint32_t a, uint32_t b)

38
libethash/util_win32.c

@ -0,0 +1,38 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file util.c
* @author Tim Hughes <tim@twistedfury.com>
* @date 2015
*/
#include <stdarg.h>
#include <stdio.h>
#include "util.h"
// foward declare without all of Windows.h
__declspec(dllimport) void __stdcall OutputDebugStringA(char const* lpOutputString);
void debugf(char const* str, ...)
{
va_list args;
va_start(args, str);
char buf[1<<16];
_vsnprintf_s(buf, sizeof(buf), sizeof(buf), str, args);
buf[sizeof(buf)-1] = '\0';
OutputDebugStringA(buf);
}

32
libethcore/Ethash.cpp

@ -36,6 +36,7 @@
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/FileSystem.h>
#include <libethash/ethash.h>
#include <libethash/internal.h>
#if ETH_ETHASHCL || !ETH_TRUE
#include <libethash-cl/ethash_cl_miner.h>
#endif
@ -71,12 +72,13 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi)
ret.boundary = _bi.boundary();
ret.headerHash = _bi.headerHash(WithoutNonce);
ret.seedHash = _bi.seedHash();
ret.blockNumber = (uint64_t) _bi.number;
return ret;
}
void Ethash::prep(BlockInfo const& _header)
void Ethash::prep(BlockInfo const& _header, std::function<int(unsigned)> const& _f)
{
EthashAux::full(_header);
EthashAux::full((unsigned)_header.number, _f);
}
bool Ethash::preVerify(BlockInfo const& _header)
@ -87,10 +89,10 @@ bool Ethash::preVerify(BlockInfo const& _header)
h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
return !!ethash_quick_check_difficulty(
_header.headerHash(WithoutNonce).data(),
(ethash_h256_t const*)_header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce,
_header.mixHash.data(),
boundary.data());
(ethash_h256_t const*)_header.mixHash.data(),
(ethash_h256_t const*)boundary.data());
}
bool Ethash::verify(BlockInfo const& _header)
@ -133,17 +135,14 @@ void Ethash::CPUMiner::workLoop()
WorkPackage w = work();
auto p = EthashAux::params(w.seedHash);
auto dag = EthashAux::full(w.seedHash);
auto dagPointer = dag->data.data();
uint8_t const* headerHashPointer = w.headerHash.data();
auto dag = EthashAux::full(w.blockNumber);
h256 boundary = w.boundary;
unsigned hashCount = 1;
for (; !shouldStop(); tryNonce++, hashCount++)
{
ethash_compute_full(&ethashReturn, dagPointer, &p, headerHashPointer, tryNonce);
h256 value = h256(ethashReturn.result, h256::ConstructFromPointer);
if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)}))
ethashReturn = ethash_full_compute(dag->full, *(ethash_h256_t*)w.headerHash.data(), tryNonce);
h256 value = h256((uint8_t*)&ethashReturn.result, h256::ConstructFromPointer);
if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256((uint8_t*)&ethashReturn.mix_hash, h256::ConstructFromPointer)}))
break;
if (!(hashCount % 1000))
accumulateHashes(1000);
@ -285,7 +284,7 @@ Ethash::GPUMiner::~GPUMiner()
bool Ethash::GPUMiner::report(uint64_t _nonce)
{
Nonce n = (Nonce)(u64)_nonce;
Result r = EthashAux::eval(work().seedHash, work().headerHash, n);
Result r = EthashAux::eval(work().blockNumber, work().headerHash, n);
if (r.value < work().boundary)
return submitProof(Solution{n, r.mixHash});
return false;
@ -311,8 +310,11 @@ void Ethash::GPUMiner::workLoop()
unsigned device = instances() > 1 ? index() : s_deviceId;
EthashAux::FullType dag = EthashAux::full(m_minerSeed);
m_miner->init(dag->data.data(), dag->data.size(), 32, s_platformId, device);
if (!EthashAux::computeFull(w.blockNumber))
return;
EthashAux::FullType dag = EthashAux::full(w.blockNumber);
bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device);
}
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);

5
libethcore/Ethash.h

@ -66,14 +66,15 @@ public:
h256 boundary;
h256 headerHash; ///< When h256() means "pause until notified a new work package is available".
h256 seedHash;
h256 seedHash; /// LTODO: IS this needed now that we use the block number instead?
uint64_t blockNumber;
};
static const WorkPackage NullWorkPackage;
static std::string name();
static unsigned revision();
static void prep(BlockInfo const& _header);
static void prep(BlockInfo const& _header, std::function<int(unsigned)> const& _f = std::function<int(unsigned)>());
static bool verify(BlockInfo const& _header);
static bool preVerify(BlockInfo const& _header);
static WorkPackage package(BlockInfo const& _header);

177
libethcore/EthashAux.cpp

@ -33,29 +33,25 @@
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/SHA3.h>
#include <libdevcrypto/FileSystem.h>
#include <libethash/internal.h>
#include "BlockInfo.h"
#include "Exceptions.h"
using namespace std;
using namespace chrono;
using namespace dev;
using namespace eth;
const char* DAGChannel::name() { return EthGreen "DAG"; }
EthashAux* dev::eth::EthashAux::s_this = nullptr;
EthashAux::~EthashAux()
{
}
ethash_params EthashAux::params(BlockInfo const& _header)
{
return params((unsigned)_header.number);
}
ethash_params EthashAux::params(unsigned _n)
uint64_t EthashAux::cacheSize(BlockInfo const& _header)
{
ethash_params p;
p.cache_size = ethash_get_cachesize(_n);
p.full_size = ethash_get_datasize(_n);
return p;
return ethash_get_cachesize((uint64_t)_header.number);
}
h256 EthashAux::seedHash(unsigned _number)
@ -82,7 +78,7 @@ h256 EthashAux::seedHash(unsigned _number)
return get()->m_seedHashes[epoch];
}
ethash_params EthashAux::params(h256 const& _seedHash)
uint64_t EthashAux::number(h256 const& _seedHash)
{
Guard l(get()->x_epochs);
unsigned epoch = 0;
@ -100,125 +96,136 @@ ethash_params EthashAux::params(h256 const& _seedHash)
}
else
epoch = epochIter->second;
return params(epoch * ETHASH_EPOCH_LENGTH);
return epoch * ETHASH_EPOCH_LENGTH;
}
void EthashAux::killCache(h256 const& _s)
{
RecursiveGuard l(x_this);
RecursiveGuard l(x_lights);
m_lights.erase(_s);
}
EthashAux::LightType EthashAux::light(BlockInfo const& _header)
{
return light(_header.seedHash());
return light((uint64_t)_header.number);
}
EthashAux::LightType EthashAux::light(h256 const& _seedHash)
EthashAux::LightType EthashAux::light(uint64_t _blockNumber)
{
RecursiveGuard l(get()->x_this);
LightType ret = get()->m_lights[_seedHash];
return ret ? ret : (get()->m_lights[_seedHash] = make_shared<LightAllocation>(_seedHash));
RecursiveGuard l(get()->x_lights);
h256 seedHash = EthashAux::seedHash(_blockNumber);
LightType ret = get()->m_lights[seedHash];
return ret ? ret : (get()->m_lights[seedHash] = make_shared<LightAllocation>(_blockNumber));
}
EthashAux::LightAllocation::LightAllocation(h256 const& _seed)
EthashAux::LightAllocation::LightAllocation(uint64_t _blockNumber)
{
auto p = params(_seed);
size = p.cache_size;
light = ethash_new_light(&p, _seed.data());
light = ethash_light_new(_blockNumber);
size = ethash_get_cachesize(_blockNumber);
}
EthashAux::LightAllocation::~LightAllocation()
{
ethash_delete_light(light);
ethash_light_delete(light);
}
EthashAux::FullType EthashAux::full(BlockInfo const& _header, bytesRef _dest, bool _createIfMissing)
bytesConstRef EthashAux::LightAllocation::data() const
{
return full(_header.seedHash(), _dest, _createIfMissing);
return bytesConstRef((byte const*)light->cache, size);
}
EthashAux::FullType EthashAux::full(h256 const& _seedHash, bytesRef _dest, bool _createIfMissing)
EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb)
{
RecursiveGuard l(get()->x_this);
FullType ret = get()->m_fulls[_seedHash].lock();
if (ret && _dest)
{
assert(ret->data.size() <= _dest.size());
ret->data.copyTo(_dest);
return FullType();
}
if (!ret)
{
// drop our last used cache sine we're allocating another 1GB.
get()->m_lastUsedFull.reset();
full = ethash_full_new(_light, _cb);
}
EthashAux::FullAllocation::~FullAllocation()
{
ethash_full_delete(full);
}
bytesConstRef EthashAux::FullAllocation::data() const
{
return bytesConstRef((byte const*)ethash_full_dag(full), size());
}
static std::function<int(unsigned)> s_dagCallback;
static int dagCallbackShim(unsigned _p)
{
clog(DAGChannel) << "Generating DAG file. Progress: " << toString(_p) << "%";
return s_dagCallback ? s_dagCallback(_p) : 0;
}
try {
boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {}
EthashAux::FullType EthashAux::full(uint64_t _blockNumber, function<int(unsigned)> const& _f)
{
auto l = light(_blockNumber);
h256 seedHash = EthashAux::seedHash(_blockNumber);
FullType ret;
auto info = rlpList(Ethash::revision(), _seedHash);
std::string oldMemoFile = getDataDir("ethash") + "/full";
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(ETHASH_REVISION) + "-" + toHex(_seedHash.ref().cropped(0, 8));
if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info)
DEV_GUARDED(get()->x_fulls)
if ((ret = get()->m_fulls[seedHash].lock()))
{
// memofile valid - rename.
boost::filesystem::rename(oldMemoFile, memoFile);
get()->m_lastUsedFull = ret;
return ret;
}
DEV_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile));
DEV_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info"));
s_dagCallback = _f;
ret = make_shared<FullAllocation>(l->light, dagCallbackShim);
ethash_params p = params(_seedHash);
assert(!_dest || _dest.size() >= p.full_size); // must be big enough.
DEV_GUARDED(get()->x_fulls)
get()->m_fulls[seedHash] = get()->m_lastUsedFull = ret;
return ret;
}
bytesRef r = contentsNew(memoFile, _dest);
if (!r)
unsigned EthashAux::computeFull(uint64_t _blockNumber)
{
Guard l(get()->x_fulls);
h256 seedHash = EthashAux::seedHash(_blockNumber);
if (FullType ret = get()->m_fulls[seedHash].lock())
{
if (!_createIfMissing)
return FullType();
// file didn't exist.
if (_dest)
// buffer was passed in - no insertion into cache nor need to allocate
r = _dest;
else
r = bytesRef(new byte[p.full_size], p.full_size);
ethash_prep_full(r.data(), &p, light(_seedHash)->light);
writeFile(memoFile, r);
get()->m_lastUsedFull = ret;
return 100;
}
if (_dest)
return FullType();
ret = make_shared<FullAllocation>(r);
get()->m_fulls[_seedHash] = ret;
if (!get()->m_fullGenerator || !get()->m_fullGenerator->joinable())
{
get()->m_fullProgress = 0;
get()->m_generatingFullNumber = _blockNumber / ETHASH_EPOCH_LENGTH * ETHASH_EPOCH_LENGTH;
get()->m_fullGenerator = unique_ptr<thread>(new thread([=](){
get()->full(_blockNumber, [](unsigned p){ get()->m_fullProgress = p; return 0; });
get()->m_fullProgress = 0;
get()->m_generatingFullNumber = NotGenerating;
}));
}
get()->m_lastUsedFull = ret;
return ret;
return (get()->m_generatingFullNumber == _blockNumber) ? get()->m_fullProgress : 0;
}
Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
Ethash::Result EthashAux::FullAllocation::compute(h256 const& _headerHash, Nonce const& _nonce) const
{
return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce);
ethash_return_value_t r = ethash_full_compute(full, *(ethash_h256_t*)_headerHash.data(), (uint64_t)(u64)_nonce);
if (!r.success)
BOOST_THROW_EXCEPTION(DAGCreationFailure());
return Ethash::Result{h256((uint8_t*)&r.result, h256::ConstructFromPointer), h256((uint8_t*)&r.mix_hash, h256::ConstructFromPointer)};
}
Ethash::Result EthashAux::FullAllocation::compute(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) const
Ethash::Result EthashAux::LightAllocation::compute(h256 const& _headerHash, Nonce const& _nonce) const
{
ethash_return_value r;
auto p = EthashAux::params(_seedHash);
ethash_compute_full(&r, data.data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce);
return Ethash::Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
ethash_return_value r = ethash_light_compute(light, *(ethash_h256_t*)_headerHash.data(), (uint64_t)(u64)_nonce);
if (!r.success)
BOOST_THROW_EXCEPTION(DAGCreationFailure());
return Ethash::Result{h256((uint8_t*)&r.result, h256::ConstructFromPointer), h256((uint8_t*)&r.mix_hash, h256::ConstructFromPointer)};
}
Ethash::Result EthashAux::LightAllocation::compute(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) const
Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
{
ethash_return_value r;
auto p = EthashAux::params(_seedHash);
ethash_compute_light(&r, light, &p, _headerHash.data(), (uint64_t)(u64)_nonce);
return Ethash::Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
return eval((uint64_t)_header.number, _header.headerHash(WithoutNonce), _nonce);
}
Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
Ethash::Result EthashAux::eval(uint64_t _blockNumber, h256 const& _headerHash, Nonce const& _nonce)
{
if (auto dag = EthashAux::get()->full(_seedHash, bytesRef(), false))
return dag->compute(_seedHash, _headerHash, _nonce);
return EthashAux::get()->light(_seedHash)->compute(_seedHash, _headerHash, _nonce);
h256 seedHash = EthashAux::seedHash(_blockNumber);
if (FullType dag = get()->m_fulls[seedHash].lock())
return dag->compute(_headerHash, _nonce);
return EthashAux::get()->light(_blockNumber)->compute(_headerHash, _nonce);
}

59
libethcore/EthashAux.h

@ -19,12 +19,17 @@
* @date 2014
*/
#include <condition_variable>
#include <libethash/ethash.h>
#include <libdevcore/Worker.h>
#include "Ethash.h"
namespace dev
{
namespace eth{
namespace eth
{
struct DAGChannel: public LogChannel { static const char* name(); static const int verbosity = 1; };
class EthashAux
{
@ -33,39 +38,47 @@ public:
static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; }
struct FullAllocation
{
FullAllocation(bytesConstRef _d): data(_d) {}
~FullAllocation() { delete [] data.data(); }
Ethash::Result compute(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) const;
bytesConstRef const data;
};
struct LightAllocation
{
LightAllocation(h256 const& _seed);
LightAllocation(uint64_t _blockNumber);
~LightAllocation();
bytesConstRef data() const { return bytesConstRef((byte const*)light, size); }
Ethash::Result compute(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) const;
bytesConstRef data() const;
Ethash::Result compute(h256 const& _headerHash, Nonce const& _nonce) const;
ethash_light_t light;
uint64_t size;
};
struct FullAllocation
{
FullAllocation(ethash_light_t _light, ethash_callback_t _cb);
~FullAllocation();
Ethash::Result compute(h256 const& _headerHash, Nonce const& _nonce) const;
bytesConstRef data() const;
uint64_t size() const { return ethash_full_dag_size(full); }
ethash_full_t full;
};
using LightType = std::shared_ptr<LightAllocation>;
using FullType = std::shared_ptr<FullAllocation>;
static h256 seedHash(unsigned _number);
static ethash_params params(BlockInfo const& _header);
static ethash_params params(h256 const& _seedHash);
static ethash_params params(unsigned _n);
static uint64_t number(h256 const& _seedHash);
static uint64_t cacheSize(BlockInfo const& _header);
static LightType light(BlockInfo const& _header);
static LightType light(h256 const& _seedHash);
static FullType full(BlockInfo const& _header, bytesRef _dest = bytesRef(), bool _createIfMissing = true);
static FullType full(h256 const& _header, bytesRef _dest = bytesRef(), bool _createIfMissing = true);
static LightType light(uint64_t _blockNumber);
static const uint64_t NotGenerating = (uint64_t)-1;
/// Kicks off generation of DAG for @a _blocknumber and @returns false or @returns true if ready.
static unsigned computeFull(uint64_t _blockNumber);
/// Information on the generation progress.
static std::pair<uint64_t, unsigned> fullGeneratingProgress() { return std::make_pair(get()->m_generatingFullNumber, get()->m_fullProgress); }
/// Kicks off generation of DAG for @a _blocknumber and blocks until ready; @returns result.
static FullType full(uint64_t _blockNumber, std::function<int(unsigned)> const& _f = std::function<int(unsigned)>());
static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); }
static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce);
static Ethash::Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce);
static Ethash::Result eval(uint64_t _blockNumber, h256 const& _headerHash, Nonce const& _nonce);
private:
EthashAux() {}
@ -73,11 +86,17 @@ private:
void killCache(h256 const& _s);
static EthashAux* s_this;
RecursiveMutex x_this;
RecursiveMutex x_lights;
std::unordered_map<h256, std::shared_ptr<LightAllocation>> m_lights;
Mutex x_fulls;
std::condition_variable m_fullsChanged;
std::unordered_map<h256, std::weak_ptr<FullAllocation>> m_fulls;
FullType m_lastUsedFull;
std::unique_ptr<std::thread> m_fullGenerator;
uint64_t m_generatingFullNumber = NotGenerating;
unsigned m_fullProgress;
Mutex x_epochs;
std::unordered_map<h256, unsigned> m_epochs;

3
libethcore/Exceptions.h

@ -73,6 +73,7 @@ class InvalidBlockNonce: virtual public dev::Exception {};
struct InvalidParentHash: virtual dev::Exception {};
struct InvalidNumber: virtual dev::Exception {};
struct InvalidContractAddress: virtual public dev::Exception {};
struct DAGCreationFailure: virtual public dev::Exception {};
struct DAGComputeFailure: virtual public dev::Exception {};
}
}

7
libethereum/BlockChain.cpp

@ -466,7 +466,14 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i));
}
try {
s.cleanup(true);
}
catch (BadRoot)
{
cwarn << "BadRoot error. Retrying import later.";
BOOST_THROW_EXCEPTION(FutureTime());
}
td = pd.totalDifficulty + tdIncrease;

2
libethereum/Client.cpp

@ -164,7 +164,7 @@ const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; }
#endif
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth"),
Worker("eth", 0),
m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(new TrivialGasPricer),

4
libethereum/ClientBase.h

@ -84,11 +84,11 @@ public:
using Interface::submitTransaction;
/// Makes the given call. Nothing is recorded into the state.
virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override;
virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) override;
using Interface::call;
/// Makes the given create. Nothing is recorded into the state.
virtual ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override;
virtual ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) override;
using Interface::create;
using Interface::balanceAt;

16
libethereum/State.cpp

@ -515,10 +515,10 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
cnote << i.first << "Dropping old transaction (nonce too low)";
_tq.drop(i.first);
}
else if (got > req + 5)
else if (got > req + 25)
{
// too new
cnote << i.first << "Dropping new transaction (> 5 nonces ahead)";
cnote << i.first << "Dropping new transaction (> 25 nonces ahead)";
_tq.drop(i.first);
}
else
@ -707,11 +707,21 @@ void State::cleanup(bool _fullCommit)
{
if (_fullCommit)
{
paranoia("immediately before database commit", true);
// Commit the new trie to disk.
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
try {
EnforceRefs er(m_db, true);
rootHash();
}
catch (BadRoot const&)
{
clog(StateChat) << "Trie corrupt! :-(";
throw;
}
m_db.commit();
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));

3
libevm/CMakeLists.txt

@ -15,6 +15,9 @@ aux_source_directory(. SRC_LIST)
# and windows is failing to build without that
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
if (EVMJIT)
include_directories(../evmjit/include)
endif()
set(EXECUTABLE evm)

79
libevm/SmartVM.cpp

@ -0,0 +1,79 @@
/*
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/>.
*/
// SmartVM is only available if EVM JIT is enabled
#if ETH_EVMJIT
#include "SmartVM.h"
#include <unordered_map>
#include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h>
#include <evmjit/JIT.h>
#include <evmjit/libevmjit-cpp/Utils.h>
#include "VMFactory.h"
namespace dev
{
namespace eth
{
namespace
{
using HitMap = std::unordered_map<h256, uint64_t>;
HitMap& getHitMap()
{
static HitMap s_hitMap;
return s_hitMap;
}
}
bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
auto codeHash = sha3(_ext.code);
auto vmKind = VMKind::Interpreter; // default VM
// Jitted EVM code already in memory?
if (evmjit::JIT::isCodeReady(eth2llvm(codeHash)))
{
cnote << "Jitted";
vmKind = VMKind::JIT;
}
else
{
// Check EVM code hit count
static const uint64_t c_hitTreshold = 1;
auto& hits = getHitMap()[codeHash];
++hits;
if (hits > c_hitTreshold)
{
cnote << "JIT selected";
vmKind = VMKind::JIT;
}
}
// TODO: Selected VM must be kept only because it returns reference to its internal memory.
// VM implementations should be stateless, without gas counter and escaping memory reference.
m_selectedVM = VMFactory::create(vmKind, gas());
auto out = m_selectedVM->go(_ext, _onOp, _steps);
m_gas = m_selectedVM->gas();
return out;
}
}
}
#endif

43
libevm/SmartVM.h

@ -0,0 +1,43 @@
/*
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/>.
*/
#pragma once
#include "VMFace.h"
namespace dev
{
namespace eth
{
/// Smart VM proxy.
///
/// This class is a strategy pattern implementation for VM. For every EVM code
/// execution request it tries to select the best VM implementation (Interpreter or JIT)
/// by analyzing available information like: code size, hit count, JIT status, etc.
class SmartVM: public VMFace
{
public:
SmartVM(u256 _gas): VMFace(_gas) {}
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
private:
std::unique_ptr<VMFace> m_selectedVM;
};
}
}

21
libevm/VMFactory.cpp

@ -18,6 +18,7 @@
#include "VMFactory.h"
#include <libdevcore/Assertions.h>
#include "VM.h"
#include "SmartVM.h"
#if ETH_EVMJIT
#include <evmjit/libevmjit-cpp/JitVM.h>
@ -29,7 +30,7 @@ namespace eth
{
namespace
{
VMKind g_kind = VMKind::Interpreter;
auto g_kind = VMKind::Interpreter;
}
void VMFactory::setKind(VMKind _kind)
@ -38,11 +39,25 @@ void VMFactory::setKind(VMKind _kind)
}
std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
{
return create(g_kind, _gas);
}
std::unique_ptr<VMFace> VMFactory::create(VMKind _kind, u256 _gas)
{
#if ETH_EVMJIT
return std::unique_ptr<VMFace>(g_kind == VMKind::JIT ? static_cast<VMFace*>(new JitVM(_gas)) : static_cast<VMFace*>(new VM(_gas)));
switch (_kind)
{
default:
case VMKind::Interpreter:
return std::unique_ptr<VMFace>(new VM(_gas));
case VMKind::JIT:
return std::unique_ptr<VMFace>(new JitVM(_gas));
case VMKind::Smart:
return std::unique_ptr<VMFace>(new SmartVM(_gas));
}
#else
asserts(g_kind == VMKind::Interpreter && "JIT disabled in build configuration");
asserts(_kind == VMKind::Interpreter && "JIT disabled in build configuration");
return std::unique_ptr<VMFace>(new VM(_gas));
#endif
}

11
libevm/VMFactory.h

@ -23,10 +23,11 @@ namespace dev
namespace eth
{
enum class VMKind: bool
enum class VMKind
{
Interpreter,
JIT
JIT,
Smart
};
class VMFactory
@ -34,7 +35,13 @@ class VMFactory
public:
VMFactory() = delete;
/// Creates a VM instance of global kind (controlled by setKind() function).
static std::unique_ptr<VMFace> create(u256 _gas);
/// Creates a VM instance of kind provided.
static std::unique_ptr<VMFace> create(VMKind _kind, u256 _gas);
/// Set global VM kind
static void setKind(VMKind _kind);
};

60
libevmasm/Assembly.cpp

@ -24,6 +24,7 @@
#include <libdevcore/Log.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/BlockDeduplicator.h>
#include <json/json.h>
using namespace std;
using namespace dev;
@ -311,39 +312,26 @@ Assembly& Assembly::optimise(bool _enable)
copt << toString(*this);
count = 0;
//@todo CFG interface should be a generator, that returns an item and a pointer to a
// knownstate, which has to replace the current state if it is not null.
// Feed these items to the CSE, but also store them and replace the stored version
// if the items generated by the CSE are shorter. (or even use less gas?)
copt << "Performing control flow analysis...";
copt << "Performing optimisation...";
{
ControlFlowGraph cfg(m_items);
AssemblyItems optItems;
AssemblyItems optimisedItems;
for (BasicBlock const& block: cfg.optimisedBlocks())
copy(m_items.begin() + block.begin, m_items.begin() + block.end,
back_inserter(optItems));
if (optItems.size() < m_items.size())
{
copt << "Old size: " << m_items.size() << ", new size: " << optItems.size();
m_items = move(optItems);
count++;
}
}
copt << "Performing common subexpression elimination...";
for (auto iter = m_items.begin(); iter != m_items.end();)
assertThrow(!!block.startState, OptimizerException, "");
CommonSubexpressionEliminator eliminator(*block.startState);
auto iter = m_items.begin() + block.begin;
auto const end = m_items.begin() + block.end;
while (iter < end)
{
//@todo use only a single state / expression classes instance.
KnownState state(make_shared<ExpressionClasses>());
CommonSubexpressionEliminator eliminator(state);
auto orig = iter;
iter = eliminator.feedItems(iter, m_items.end());
AssemblyItems optItems;
iter = eliminator.feedItems(iter, end);
bool shouldReplace = false;
AssemblyItems optimisedChunk;
try
{
optItems = eliminator.getOptimizedItems();
shouldReplace = (optItems.size() < size_t(iter - orig));
optimisedChunk = eliminator.getOptimizedItems();
shouldReplace = (optimisedChunk.size() < size_t(iter - orig));
}
catch (StackTooDeepException const&)
{
@ -353,12 +341,25 @@ Assembly& Assembly::optimise(bool _enable)
if (shouldReplace)
{
copt << "Old size: " << (iter - orig) << ", new size: " << optItems.size();
copt << "Old size: " << (iter - orig) << ", new size: " << optimisedChunk.size();
count++;
for (auto moveIter = optItems.begin(); moveIter != optItems.end(); ++orig, ++moveIter)
*orig = move(*moveIter);
iter = m_items.erase(orig, iter);
optimisedItems += optimisedChunk;
}
else
copy(orig, iter, back_inserter(optimisedItems));
}
}
if (optimisedItems.size() < m_items.size())
{
m_items = move(optimisedItems);
count++;
}
// This only modifies PushTags, we have to run again to actually remove code.
BlockDeduplicator dedup(m_items);
if (dedup.deduplicate())
count++;
}
}
@ -461,7 +462,8 @@ bytes Assembly::assemble() const
for (auto const& i: tagRef)
{
bytesRef r(ret.data() + i.first, bytesPerTag);
toBigEndian(tagPos[i.second], r);
//@todo in the failure case, we could use the position of the invalid jumpdest
toBigEndian(i.second < tagPos.size() ? tagPos[i.second] : (1 << (8 * bytesPerTag)) - 1, r);
}
if (!m_data.empty())

2
libevmasm/AssemblyItem.h

@ -68,6 +68,8 @@ public:
/// @returns true iff the type and data of the items are equal.
bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; }
bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); }
/// Less-than operator compatible with operator==.
bool operator<(AssemblyItem const& _other) const { return std::tie(m_type, m_data) < std::tie(_other.m_type, _other.m_data); }
/// @returns an upper bound for the number of bytes required by this item, assuming that
/// the value of a jump tag takes @a _addressLength bytes.

91
libevmasm/BlockDeduplicator.cpp

@ -0,0 +1,91 @@
/*
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 BlockDeduplicator.cpp
* @author Christian <c@ethdev.com>
* @date 2015
* Unifies basic blocks that share content.
*/
#include <libevmasm/BlockDeduplicator.h>
#include <functional>
#include <libevmasm/AssemblyItem.h>
#include <libevmasm/SemanticInformation.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
bool BlockDeduplicator::deduplicate()
{
// Compares indices based on the suffix that starts there, ignoring tags and stopping at
// opcodes that stop the control flow.
function<bool(size_t, size_t)> comparator = [&](size_t _i, size_t _j)
{
if (_i == _j)
return false;
BlockIterator first(m_items.begin() + _i, m_items.end());
BlockIterator second(m_items.begin() + _j, m_items.end());
BlockIterator end(m_items.end(), m_items.end());
if (first != end && (*first).type() == Tag)
++first;
if (second != end && (*second).type() == Tag)
++second;
return std::lexicographical_compare(first, end, second, end);
};
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
map<u256, u256> tagReplacement;
for (size_t i = 0; i < m_items.size(); ++i)
{
if (m_items.at(i).type() != Tag)
continue;
auto it = blocksSeen.find(i);
if (it == blocksSeen.end())
blocksSeen.insert(i);
else
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
}
bool ret = false;
for (AssemblyItem& item: m_items)
if (item.type() == PushTag && tagReplacement.count(item.data()))
{
ret = true;
item.setData(tagReplacement.at(item.data()));
}
return ret;
}
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
{
if (it == end)
return *this;
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(eth::Instruction::JUMPI))
it = end;
else
{
++it;
while (it != end && it->type() == Tag)
++it;
}
return *this;
}

69
libevmasm/BlockDeduplicator.h

@ -0,0 +1,69 @@
/*
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 BlockDeduplicator.h
* @author Christian <c@ethdev.com>
* @date 2015
* Unifies basic blocks that share content.
*/
#pragma once
#include <cstddef>
#include <vector>
#include <functional>
namespace dev
{
namespace eth
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
/**
* Optimizer class to be used to unify blocks that share content.
* Modifies the passed vector in place.
*/
class BlockDeduplicator
{
public:
BlockDeduplicator(AssemblyItems& _items): m_items(_items) {}
/// @returns true if something was changed
bool deduplicate();
private:
/// Iterator that skips tags skips to the end if (all branches of) the control
/// flow does not continue to the next instruction.
struct BlockIterator: std::iterator<std::forward_iterator_tag, AssemblyItem const>
{
public:
BlockIterator(AssemblyItems::const_iterator _it, AssemblyItems::const_iterator _end):
it(_it), end(_end) { }
BlockIterator& operator++();
bool operator==(BlockIterator const& _other) const { return it == _other.it; }
bool operator!=(BlockIterator const& _other) const { return it != _other.it; }
AssemblyItem const& operator*() const { return *it; }
AssemblyItems::const_iterator it;
AssemblyItems::const_iterator end;
};
AssemblyItems& m_items;
};
}
}

7
libevmasm/CMakeLists.txt

@ -19,15 +19,10 @@ set(EXECUTABLE evmasm)
file(GLOB HEADERS "*.h")
if (ETH_STATIC)
add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS})
else()
add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS})
endif()
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcrypto)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

103
libevmasm/CommonSubexpressionEliminator.cpp

@ -45,16 +45,22 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
for (int height = minHeight; height <= m_state.stackHeight(); ++height)
targetStackContents[height] = m_state.stackElement(height, SourceLocation());
// Debug info:
//stream(cout, initialStackContents, targetStackContents);
AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations).generateCode(
m_initialState.stackHeight(),
initialStackContents,
targetStackContents
);
if (m_breakingItem)
{
items.push_back(*m_breakingItem);
m_state.feedItem(*m_breakingItem);
}
// cleanup
m_initialState = m_state;
m_breakingItem = nullptr;
m_storeOperations.clear();
return items;
}
@ -113,16 +119,14 @@ AssemblyItems CSECodeGenerator::generateCode(
{
m_stackHeight = _initialStackHeight;
m_stack = _initialStack;
m_targetStack = _targetStackContents;
for (auto const& item: m_stack)
if (!m_classPositions.count(item.second))
m_classPositions[item.second] = item.first;
// @todo: provide information about the positions of copies of class elements
m_classPositions[item.second].insert(item.first);
// generate the dependency graph starting from final storage and memory writes and target stack contents
for (auto const& p: m_storeOperations)
addDependencies(p.second.back().expression);
for (auto const& targetItem: _targetStackContents)
for (auto const& targetItem: m_targetStack)
{
m_finalClasses.insert(targetItem.second);
addDependencies(targetItem.second);
@ -141,13 +145,16 @@ AssemblyItems CSECodeGenerator::generateCode(
generateClassElement(seqAndId.second, true);
// generate the target stack elements
for (auto const& targetItem: _targetStackContents)
for (auto const& targetItem: m_targetStack)
{
int position = generateClassElement(targetItem.second);
assertThrow(position != c_invalidPosition, OptimizerException, "");
if (position == targetItem.first)
if (m_stack.count(targetItem.first) && m_stack.at(targetItem.first) == targetItem.second)
continue; // already there
generateClassElement(targetItem.second);
assertThrow(!m_classPositions[targetItem.second].empty(), OptimizerException, "");
if (m_classPositions[targetItem.second].count(targetItem.first))
continue;
SourceLocation const& location = m_expressionClasses.representative(targetItem.second).item->getLocation();
int position = classElementPosition(targetItem.second);
if (position < targetItem.first)
// it is already at its target, we need another copy
appendDup(position, location);
@ -164,21 +171,24 @@ AssemblyItems CSECodeGenerator::generateCode(
// check validity
int finalHeight = 0;
if (!_targetStackContents.empty())
if (!m_targetStack.empty())
// have target stack, so its height should be the final height
finalHeight = (--_targetStackContents.end())->first;
finalHeight = (--m_targetStack.end())->first;
else if (!_initialStack.empty())
// no target stack, only erase the initial stack
finalHeight = _initialStack.begin()->first - 1;
else
// neither initial no target stack, no change in height
finalHeight = 0;
finalHeight = _initialStackHeight;
assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height.");
return m_generatedItems;
}
void CSECodeGenerator::addDependencies(Id _c)
{
if (m_classPositions.count(_c))
return; // it is already on the stack
if (m_neededBy.count(_c))
return; // we already computed the dependencies for _c
ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
@ -254,19 +264,23 @@ void CSECodeGenerator::addDependencies(Id _c)
}
}
int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
{
for (auto it: m_classPositions)
for (auto p: it.second)
if (p > m_stackHeight)
assertThrow(false, OptimizerException, "");
// do some cleanup
removeStackTopIfPossible();
if (m_classPositions.count(_c))
{
assertThrow(
m_classPositions[_c] != c_invalidPosition,
!m_classPositions[_c].empty(),
OptimizerException,
"Element already removed but still needed."
);
return m_classPositions[_c];
return;
}
ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c);
assertThrow(
@ -339,16 +353,16 @@ int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1))
// this will not append a swap but remove the one that is already there
appendOrRemoveSwap(m_stackHeight - 1, location);
for (auto arg: arguments)
if (canBeRemoved(arg, _c))
m_classPositions[arg] = c_invalidPosition;
for (size_t i = 0; i < arguments.size(); ++i)
{
m_classPositions[m_stack[m_stackHeight - i]].erase(m_stackHeight - i);
m_stack.erase(m_stackHeight - i);
}
appendItem(*expr.item);
if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1)
{
m_stack[m_stackHeight] = _c;
return m_classPositions[_c] = m_stackHeight;
m_classPositions[_c].insert(m_stackHeight);
}
else
{
@ -357,31 +371,39 @@ int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
OptimizerException,
"Invalid number of return values."
);
return m_classPositions[_c] = c_invalidPosition;
m_classPositions[_c]; // ensure it is created to mark the expression as generated
}
}
int CSECodeGenerator::classElementPosition(Id _id) const
{
assertThrow(
m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition,
m_classPositions.count(_id) && !m_classPositions.at(_id).empty(),
OptimizerException,
"Element requested but is not present."
);
return m_classPositions.at(_id);
return *max_element(m_classPositions.at(_id).begin(), m_classPositions.at(_id).end());
}
bool CSECodeGenerator::canBeRemoved(Id _element, Id _result)
bool CSECodeGenerator::canBeRemoved(Id _element, Id _result, int _fromPosition)
{
// Returns false if _element is finally needed or is needed by a class that has not been
// computed yet. Note that m_classPositions also includes classes that were deleted in the meantime.
if (m_finalClasses.count(_element))
return false;
// Default for _fromPosition is the canonical position of the element.
if (_fromPosition == c_invalidPosition)
_fromPosition = classElementPosition(_element);
bool haveCopy = m_classPositions.at(_element).size() > 1;
if (m_finalClasses.count(_element))
// It is part of the target stack. It can be removed if it is a copy that is not in the target position.
return haveCopy && (!m_targetStack.count(_fromPosition) || m_targetStack[_fromPosition] != _element);
else if (!haveCopy)
{
// Can be removed unless it is needed by a class that has not been computed yet.
// Note that m_classPositions also includes classes that were deleted in the meantime.
auto range = m_neededBy.equal_range(_element);
for (auto it = range.first; it != range.second; ++it)
if (it->second != _result && !m_classPositions.count(it->second))
return false;
}
return true;
}
@ -391,11 +413,11 @@ bool CSECodeGenerator::removeStackTopIfPossible()
return false;
assertThrow(m_stack.count(m_stackHeight) > 0, OptimizerException, "");
Id top = m_stack[m_stackHeight];
if (!canBeRemoved(top))
if (!canBeRemoved(top, Id(-1), m_stackHeight))
return false;
m_generatedItems.push_back(AssemblyItem(Instruction::POP));
m_classPositions[m_stack[m_stackHeight]].erase(m_stackHeight);
m_stack.erase(m_stackHeight);
m_stackHeight--;
appendItem(AssemblyItem(Instruction::POP));
return true;
}
@ -407,6 +429,7 @@ void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _locat
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(dupInstruction(instructionNum), _location));
m_stack[m_stackHeight] = m_stack[_fromPosition];
m_classPositions[m_stack[m_stackHeight]].insert(m_stackHeight);
}
void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation const& _location)
@ -418,13 +441,15 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation cons
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(swapInstruction(instructionNum), _location));
// The value of a class can be present in multiple locations on the stack. We only update the
// "canonical" one that is tracked by m_classPositions
if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight)
m_classPositions[m_stack[m_stackHeight]] = _fromPosition;
if (m_classPositions[m_stack[_fromPosition]] == _fromPosition)
m_classPositions[m_stack[_fromPosition]] = m_stackHeight;
if (m_stack[m_stackHeight] != m_stack[_fromPosition])
{
m_classPositions[m_stack[m_stackHeight]].erase(m_stackHeight);
m_classPositions[m_stack[m_stackHeight]].insert(_fromPosition);
m_classPositions[m_stack[_fromPosition]].erase(_fromPosition);
m_classPositions[m_stack[_fromPosition]].insert(m_stackHeight);
swap(m_stack[m_stackHeight], m_stack[_fromPosition]);
}
if (m_generatedItems.size() >= 2 &&
SemanticInformation::isSwapInstruction(m_generatedItems.back()) &&
*(m_generatedItems.end() - 2) == m_generatedItems.back())

22
libevmasm/CommonSubexpressionEliminator.h

@ -71,13 +71,6 @@ public:
/// @returns the resulting items after optimization.
AssemblyItems getOptimizedItems();
/// Streams debugging information to @a _out.
std::ostream& stream(
std::ostream& _out,
std::map<int, Id> _initialStack = std::map<int, Id>(),
std::map<int, Id> _targetStack = std::map<int, Id>()
) const;
private:
/// Feeds the item into the system for analysis.
void feedItem(AssemblyItem const& _item, bool _copyItem = false);
@ -126,16 +119,15 @@ private:
void addDependencies(Id _c);
/// Produce code that generates the given element if it is not yet present.
/// @returns the stack position of the element or c_invalidPosition if it does not actually
/// generate a value on the stack.
/// @param _allowSequenced indicates that sequence-constrained operations are allowed
int generateClassElement(Id _c, bool _allowSequenced = false);
void generateClassElement(Id _c, bool _allowSequenced = false);
/// @returns the position of the representative of the given id on the stack.
/// @note throws an exception if it is not on the stack.
int classElementPosition(Id _id) const;
/// @returns true if @a _element can be removed - in general or, if given, while computing @a _result.
bool canBeRemoved(Id _element, Id _result = Id(-1));
/// @returns true if the copy of @a _element can be removed from stack position _fromPosition
/// - in general or, if given, while computing @a _result.
bool canBeRemoved(Id _element, Id _result = Id(-1), int _fromPosition = c_invalidPosition);
/// Appends code to remove the topmost stack element if it can be removed.
bool removeStackTopIfPossible();
@ -157,8 +149,8 @@ private:
std::multimap<Id, Id> m_neededBy;
/// Current content of the stack.
std::map<int, Id> m_stack;
/// Current positions of equivalence classes, equal to c_invalidPosition if already deleted.
std::map<Id, int> m_classPositions;
/// Current positions of equivalence classes, equal to the empty set if already deleted.
std::map<Id, std::set<int>> m_classPositions;
/// The actual eqivalence class items and how to compute them.
ExpressionClasses& m_expressionClasses;
@ -167,6 +159,7 @@ private:
std::map<std::pair<StoreOperation::Target, Id>, StoreOperations> m_storeOperations;
/// The set of equivalence classes that should be present on the stack at the end.
std::set<Id> m_finalClasses;
std::map<int, Id> m_targetStack;
};
template <class _AssemblyItemIterator>
@ -175,6 +168,7 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
_AssemblyItemIterator _end
)
{
assertThrow(!m_breakingItem, OptimizerException, "Invalid use of CommonSubexpressionEliminator.");
for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator)
feedItem(*_iterator);
if (_iterator != _end)

20
libevmasm/ControlFlowGraph.cpp

@ -142,7 +142,7 @@ void ControlFlowGraph::removeUnusedBlocks()
BasicBlock const& block = m_blocks.at(blocksToProcess.back());
blocksToProcess.pop_back();
for (BlockId tag: block.pushedTags)
if (!neededBlocks.count(tag))
if (!neededBlocks.count(tag) && m_blocks.count(tag))
{
neededBlocks.insert(tag);
blocksToProcess.push_back(tag);
@ -191,12 +191,12 @@ void ControlFlowGraph::setPrevLinks()
if (push.type() != PushTag)
continue;
BlockId nextId(push.data());
if (m_blocks.at(nextId).prev)
if (m_blocks.count(nextId) && m_blocks.at(nextId).prev)
continue;
bool hasLoop = false;
for (BlockId id = nextId; id && !hasLoop; id = m_blocks.at(id).next)
for (BlockId id = nextId; id && m_blocks.count(id) && !hasLoop; id = m_blocks.at(id).next)
hasLoop = (id == blockId);
if (hasLoop)
if (hasLoop || !m_blocks.count(nextId))
continue;
m_blocks[nextId].prev = blockId;
@ -225,6 +225,8 @@ void ControlFlowGraph::gatherKnowledge()
{
//@todo we might have to do something like incrementing the sequence number for each JUMPDEST
assertThrow(!!workQueue.back().first, OptimizerException, "");
if (!m_blocks.count(workQueue.back().first))
continue; // too bad, we do not know the tag, probably an invalid jump
BasicBlock& block = m_blocks.at(workQueue.back().first);
KnownStatePointer state = workQueue.back().second;
workQueue.pop_back();
@ -281,6 +283,15 @@ void ControlFlowGraph::gatherKnowledge()
)
workQueue.push_back(make_pair(block.next, state->copy()));
}
// Remove all blocks we never visited here. This might happen because a tag is pushed but
// never used for a JUMP.
// Note that this invalidates some contents of pushedTags
for (auto it = m_blocks.begin(); it != m_blocks.end();)
if (!it->second.startState)
it = m_blocks.erase(it);
else
it++;
}
BasicBlocks ControlFlowGraph::rebuildCode()
@ -288,6 +299,7 @@ BasicBlocks ControlFlowGraph::rebuildCode()
map<BlockId, unsigned> pushes;
for (auto& idAndBlock: m_blocks)
for (BlockId ref: idAndBlock.second.pushedTags)
if (m_blocks.count(ref))
pushes[ref]++;
set<BlockId> blocksToAdd;

52
libevmasm/KnownState.cpp

@ -160,23 +160,51 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
return op;
}
void KnownState::reduceToCommonKnowledge(KnownState const& /*_other*/)
/// Helper function for KnownState::reduceToCommonKnowledge, removes everything from
/// _this which is not in or not equal to the value in _other.
template <class _Mapping, class _KeyType> void intersect(
_Mapping& _this,
_Mapping const& _other,
function<_KeyType(_KeyType)> const& _keyTrans = [](_KeyType _k) { return _k; }
)
{
for (auto it = _this.begin(); it != _this.end();)
if (_other.count(_keyTrans(it->first)) && _other.at(_keyTrans(it->first)) == it->second)
++it;
else
it = _this.erase(it);
}
template <class _Mapping> void intersect(_Mapping& _this, _Mapping const& _other)
{
intersect<_Mapping, ExpressionClasses::Id>(_this, _other, [](ExpressionClasses::Id _k) { return _k; });
}
void KnownState::reduceToCommonKnowledge(KnownState const& _other)
{
//@todo
*this = KnownState(m_expressionClasses);
int stackDiff = m_stackHeight - _other.m_stackHeight;
function<int(int)> stackKeyTransform = [=](int _key) -> int { return _key - stackDiff; };
intersect(m_stackElements, _other.m_stackElements, stackKeyTransform);
// Use the smaller stack height. Essential to terminate in case of loops.
if (m_stackHeight > _other.m_stackHeight)
{
map<int, Id> shiftedStack;
for (auto const& stackElement: m_stackElements)
shiftedStack[stackElement.first - stackDiff] = stackElement.second;
m_stackElements = move(shiftedStack);
m_stackHeight = _other.m_stackHeight;
}
intersect(m_storageContent, _other.m_storageContent);
intersect(m_memoryContent, _other.m_memoryContent);
}
bool KnownState::operator==(const KnownState& _other) const
{
//@todo
return (
m_stackElements.empty() &&
_other.m_stackElements.empty() &&
m_storageContent.empty() &&
_other.m_storageContent.empty() &&
m_memoryContent.empty() &&
_other.m_memoryContent.empty()
);
return m_storageContent == _other.m_storageContent &&
m_memoryContent == _other.m_memoryContent &&
m_stackHeight == _other.m_stackHeight &&
m_stackElements == _other.m_stackElements;
}
ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation const& _location)

12
libsolidity/Compiler.cpp

@ -377,12 +377,16 @@ bool Compiler::visit(IfStatement const& _ifStatement)
StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, _ifStatement);
compileExpression(_ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
m_context << eth::Instruction::ISZERO;
eth::AssemblyItem falseTag = m_context.appendConditionalJump();
eth::AssemblyItem endTag = falseTag;
_ifStatement.getTrueStatement().accept(*this);
if (_ifStatement.getFalseStatement())
{
endTag = m_context.appendJumpToNew();
m_context << falseTag;
_ifStatement.getFalseStatement()->accept(*this);
eth::AssemblyItem endTag = m_context.appendJumpToNew();
m_context << trueTag;
_ifStatement.getTrueStatement().accept(*this);
}
m_context << endTag;
checker.check();

1
mix/ClientModel.cpp

@ -316,7 +316,6 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
TransactionSettings stdTransaction = transaction;
stdTransaction.gasAuto = true;
Address address = deployContract(stdContractCode, stdTransaction);
deployedContracts.push_back(address);
m_stdContractAddresses[stdTransaction.contractId] = address;
m_stdContractNames[address] = stdTransaction.contractId;
}

13
mix/CodeModel.cpp

@ -219,6 +219,19 @@ void CodeModel::reset(QVariantMap const& _documents)
emit scheduleCompilationJob(++m_backgroundJobId);
}
void CodeModel::unregisterContractSrc(QString const& _documentId)
{
{
Guard pl(x_pendingContracts);
m_pendingContracts.erase(_documentId);
}
// launch the background thread
m_compiling = true;
emit stateChanged();
emit scheduleCompilationJob(++m_backgroundJobId);
}
void CodeModel::registerCodeChange(QString const& _documentId, QString const& _code)
{
{

2
mix/CodeModel.h

@ -160,6 +160,8 @@ public:
Q_INVOKABLE CompiledContract* contractByDocumentId(QString const& _documentId) const;
/// Reset code model
Q_INVOKABLE void reset() { reset(QVariantMap()); }
/// Delete a contract source
Q_INVOKABLE void unregisterContractSrc(QString const& _documentId);
/// Convert solidity type info to mix type
static SolidityType nodeType(dev::solidity::Type const* _type);
/// Check if given location belongs to contract or function

6
mix/FileIo.cpp

@ -210,3 +210,9 @@ void FileIo::stopWatching(QString const& _path)
{
m_watcher->removePath(pathFromUrl(_path));
}
void FileIo::deleteFile(QString const& _path)
{
QFile file(pathFromUrl(_path));
file.remove();
}

2
mix/FileIo.h

@ -66,6 +66,8 @@ public:
Q_INVOKABLE void watchFileChanged(QString const& _path);
/// Stop Listenning for files change in @arg _path.
Q_INVOKABLE void stopWatching(QString const& _path);
/// Delete a file
Q_INVOKABLE void deleteFile(QString const& _path);
private:
QString getHomePath() const;

13
mix/qml/CodeEditorView.qml

@ -190,11 +190,14 @@ Item {
for (var i = 0; i < openDocCount; i++)
{
var doc = editorListModel.get(i);
if (editors.itemAt(i))
{
var editor = editors.itemAt(i).item;
if (editor)
fileIo.writeFile(doc.path, editor.getText());
}
}
}
onProjectSaved: {
if (projectModel.appIsClosing || projectModel.projectIsClosing)
@ -315,6 +318,16 @@ Item {
break;
}
}
onDocumentRemoved: {
for (var i = 0; i < editorListModel.count; i++)
if (editorListModel.get(i).documentId === documentId)
{
editorListModel.remove(i);
openDocCount--;
break;
}
}
}
function loadIfNotLoaded () {

31
mix/qml/FilesSection.qml

@ -3,6 +3,7 @@ import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.3
import QtQuick.Dialogs 1.2
import "."
@ -241,8 +242,13 @@ Rectangle
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked:{
if (mouse.button === Qt.RightButton && !isContract)
if (mouse.button === Qt.RightButton)
{
if (isContract)
contextMenuContract.popup();
else
contextMenu.popup();
}
else if (mouse.button === Qt.LeftButton)
{
rootItem.isSelected = true;
@ -263,6 +269,28 @@ Rectangle
MenuItem {
text: qsTr("Delete")
onTriggered: {
deleteConfirmation.open();
}
}
}
Menu {
id: contextMenuContract
MenuItem {
text: qsTr("Delete")
onTriggered: {
deleteConfirmation.open();
}
}
}
MessageDialog
{
id: deleteConfirmation
text: qsTr("Are you sure to delete this file ?")
standardButtons: StandardIcon.Ok | StandardIcon.Cancel
onAccepted:
{
projectModel.removeDocument(documentId);
wrapperItem.removeDocument(documentId);
}
@ -271,5 +299,4 @@ Rectangle
}
}
}
}
}

10
mix/qml/js/ProjectModel.js

@ -294,6 +294,9 @@ function renameDocument(documentId, newName) {
function getDocument(documentId) {
var i = getDocumentIndex(documentId);
if (i === -1)
return undefined;
else
return projectListModel.get(i);
}
@ -308,10 +311,13 @@ function getDocumentIdByName(fileName)
function removeDocument(documentId) {
var i = getDocumentIndex(documentId);
var document = projectListModel.get(i);
if (!document.isContract) {
fileIo.stopWatching(document.path);
fileIo.deleteFile(document.path);
if (document.isContract)
codeModel.unregisterContractSrc(documentId);
projectListModel.remove(i);
saveProjectFile();
documentRemoved(documentId);
}
}
function newHtmlFile() {

1
mix/test/qml/TestMain.qml

@ -113,5 +113,6 @@ TestCase
function test_project_contractRename() { TestProject.test_contractRename(); }
function test_project_multipleWebPages() { TestProject.test_multipleWebPages(); }
function test_project_multipleContractsSameFile() { TestProject.test_multipleContractsSameFile(); }
function test_project_deleteFile() { TestProject.test_deleteFile(); }
}

7
mix/test/qml/js/TestDebugger.js

@ -223,13 +223,16 @@ function test_ctrTypeAsParam()
"}");
mainApplication.projectModel.stateListModel.editState(0); //C1 ctor already added
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
mainApplication.projectModel.stateDialog.model.editTransaction(3);
ts.waitForRendering(transactionDialog, 3000);
clickElement(transactionDialog, 200, 300);
ts.typeString("<C1 - 0>", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectContract("C2");
transactionDialog.selectFunction("getFromC1");
clickElement(transactionDialog, 406, 340);
clickElement(transactionDialog, 406, 366);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();

14
mix/test/qml/js/TestProject.js

@ -44,3 +44,17 @@ function test_multipleContractsSameFile()
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "contract", "C2");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "contract", "C3");
}
function test_deleteFile()
{
newProject();
var path = mainApplication.projectModel.projectPath;
createHtml("page1.html", "<html><body><div id='queryres'>Fail</div></body><script>if (web3) document.getElementById('queryres').innerText='OK'</script></html>");
createHtml("page2.html", "<html><body><div id='queryres'>Fail</div></body><script>if (web3) document.getElementById('queryres').innerText='OK'</script></html>");
createHtml("page3.html", "<html><body><div id='queryres'>Fail</div></body><script>if (web3) document.getElementById('queryres').innerText='OK'</script></html>");
mainApplication.projectModel.removeDocument("page2.html");
mainApplication.projectModel.closeProject(function(){});
mainApplication.projectModel.loadProject(path);
var doc = mainApplication.projectModel.getDocument("page2.html");
verify(!doc, "page2.html has not been removed");
}

7
test/CMakeLists.txt

@ -95,6 +95,12 @@ if (JSONRPC)
target_link_libraries(testeth ${JSON_RPC_CPP_CLIENT_LIBRARIES})
endif()
if (UNIX) # Create symlink to old testeth location to make bildbot happy
add_custom_command(TARGET testeth POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_BINARY_DIR}/bin/testeth ${CMAKE_BINARY_DIR}/test/testeth
)
endif()
enable_testing()
set(CTEST_OUTPUT_ON_FAILURE TRUE)
@ -113,4 +119,3 @@ eth_add_test(JsonRpc
ARGS --eth_testfile=BlockTests/bcJS_API_Test
ARGS --eth_testfile=BlockTests/bcValidBlockTest
)

43
test/TestHelper.cpp

@ -288,7 +288,7 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta
{
addressOptions = _expectedStateOptions.at(a.first);
}
catch(std::out_of_range)
catch(std::out_of_range const&)
{
BOOST_ERROR("expectedStateOptions map does not match expectedState in checkExpectedState!");
break;
@ -549,22 +549,19 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e
}
}
void userDefinedTest(string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests)
void userDefinedTest(std::function<void(json_spirit::mValue&, bool)> doTests)
{
Options::get(); // parse command line options, e.g. to enable JIT
if (!Options::get().singleTest)
return;
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
{
string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == testTypeFlag)
if (Options::get().singleTestFile.empty() || Options::get().singleTestName.empty())
{
if (boost::unit_test::framework::master_test_suite().argc <= i + 2)
{
cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " <filename> <testname>\n";
cnote << "Missing user test specification\nUsage: testeth --singletest <filename> <testname>\n";
return;
}
string filename = boost::unit_test::framework::master_test_suite().argv[i + 1];
string testname = boost::unit_test::framework::master_test_suite().argv[i + 2];
auto& filename = Options::get().singleTestFile;
auto& testname = Options::get().singleTestName;
int currentVerbosity = g_logVerbosity;
g_logVerbosity = 12;
try
@ -599,8 +596,6 @@ void userDefinedTest(string testTypeFlag, std::function<void(json_spirit::mValue
g_logVerbosity = currentVerbosity;
}
g_logVerbosity = currentVerbosity;
}
}
}
void executeTests(const string& _name, const string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests)
@ -707,10 +702,9 @@ Options::Options()
{
auto arg = std::string{argv[i]};
if (arg == "--jit")
{
jit = true;
eth::VMFactory::setKind(eth::VMKind::JIT);
}
else if (arg == "--vm=smart")
eth::VMFactory::setKind(eth::VMKind::Smart);
else if (arg == "--vmtrace")
vmtrace = true;
else if (arg == "--filltests")
@ -743,7 +737,20 @@ Options::Options()
else if (arg == "--singletest" && i + 1 < argc)
{
singleTest = true;
singleTestName = argv[i + 1];
auto name1 = std::string{argv[i + 1]};
if (i + 1 < argc) // two params
{
auto name2 = std::string{argv[i + 2]};
if (name2[0] == '-') // not param, another option
singleTestName = std::move(name1);
else
{
singleTestFile = std::move(name1);
singleTestName = std::move(name2);
}
}
else
singleTestName = std::move(name1);
}
}
}

4
test/TestHelper.h

@ -157,7 +157,7 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);
void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates);
void executeTests(const std::string& _name, const std::string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests);
void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests);
void userDefinedTest(std::function<void(json_spirit::mValue&, bool)> doTests);
RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj);
eth::LastHashes lastHashes(u256 _currentBlockNumber);
json_spirit::mObject fillJsonWithState(eth::State _state);
@ -179,7 +179,6 @@ void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)
class Options
{
public:
bool jit = false; ///< Use JIT
bool vmtrace = false; ///< Create EVM execution tracer // TODO: Link with log verbosity?
bool fillTests = false; ///< Create JSON test files from execution results
bool stats = false; ///< Execution time stats
@ -189,6 +188,7 @@ public:
/// Test selection
/// @{
bool singleTest = false;
std::string singleTestFile;
std::string singleTestName;
bool performance = false;
bool quadratic = false;

10
test/libdevcrypto/trie.cpp

@ -102,10 +102,13 @@ BOOST_AUTO_TEST_CASE(hex_encoded_securetrie_test)
{
next_permutation(ss.begin(), ss.end());
MemoryDB m;
EnforceRefs r(m, true);
GenericTrieDB<MemoryDB> t(&m);
MemoryDB hm;
EnforceRefs hr(hm, true);
HashedGenericTrieDB<MemoryDB> ht(&hm);
MemoryDB fm;
EnforceRefs fr(fm, true);
FatGenericTrieDB<MemoryDB> ft(&fm);
t.init();
ht.init();
@ -164,10 +167,13 @@ BOOST_AUTO_TEST_CASE(trie_test_anyorder)
{
next_permutation(ss.begin(), ss.end());
MemoryDB m;
EnforceRefs r(m, true);
GenericTrieDB<MemoryDB> t(&m);
MemoryDB hm;
EnforceRefs hr(hm, true);
HashedGenericTrieDB<MemoryDB> ht(&hm);
MemoryDB fm;
EnforceRefs fr(fm, true);
FatGenericTrieDB<MemoryDB> ft(&fm);
t.init();
ht.init();
@ -244,10 +250,13 @@ BOOST_AUTO_TEST_CASE(trie_tests_ordered)
}
MemoryDB m;
EnforceRefs r(m, true);
GenericTrieDB<MemoryDB> t(&m);
MemoryDB hm;
EnforceRefs hr(hm, true);
HashedGenericTrieDB<MemoryDB> ht(&hm);
MemoryDB fm;
EnforceRefs fr(fm, true);
FatGenericTrieDB<MemoryDB> ft(&fm);
t.init();
ht.init();
@ -360,6 +369,7 @@ BOOST_AUTO_TEST_CASE(moreTrieTests)
#endif
{
MemoryDB m;
EnforceRefs r(m, true);
GenericTrieDB<MemoryDB> d(&m);
d.init(); // initialise as empty tree.
MemTrie t;

6
test/libethcore/dagger.cpp

@ -63,14 +63,14 @@ BOOST_AUTO_TEST_CASE(basic_test)
unsigned cacheSize(o["cache_size"].get_int());
h256 cacheHash(o["cache_hash"].get_str());
BOOST_REQUIRE_EQUAL(EthashAux::get()->params(header).cache_size, cacheSize);
BOOST_REQUIRE_EQUAL(EthashAux::get()->light(header)->size, cacheSize);
BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->light(header)->data()), cacheHash);
#if TEST_FULL
unsigned fullSize(o["full_size"].get_int());
h256 fullHash(o["full_hash"].get_str());
BOOST_REQUIRE_EQUAL(EthashAux::get()->full(header).size(), fullSize);
BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->full(header)), fullHash);
BOOST_REQUIRE_EQUAL(EthashAux::get()->full(header)->size(), fullSize);
BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->full(header)->data()), fullHash);
#endif
h256 result(o["result"].get_str());

2
test/libethereum/blockchain.cpp

@ -792,7 +792,7 @@ BOOST_AUTO_TEST_CASE(bcWalletTest)
BOOST_AUTO_TEST_CASE(userDefinedFile)
{
dev::test::userDefinedTest("--singletest", dev::test::doBlockchainTests);
dev::test::userDefinedTest(dev::test::doBlockchainTests);
}
BOOST_AUTO_TEST_SUITE_END()

2
test/libethereum/state.cpp

@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(stRandom)
BOOST_AUTO_TEST_CASE(userDefinedFileState)
{
dev::test::userDefinedTest("--singletest", dev::test::doStateTests);
dev::test::userDefinedTest(dev::test::doStateTests);
}
BOOST_AUTO_TEST_SUITE_END()

2
test/libethereum/transaction.cpp

@ -195,7 +195,7 @@ BOOST_AUTO_TEST_CASE(ttCreateTest)
BOOST_AUTO_TEST_CASE(userDefinedFile)
{
dev::test::userDefinedTest("--singletest", dev::test::doTransactionTests);
dev::test::userDefinedTest(dev::test::doTransactionTests);
}
BOOST_AUTO_TEST_SUITE_END()

4
test/libevm/vm.cpp

@ -432,7 +432,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
}
}
} } // Namespace Close
} } // namespace close
BOOST_AUTO_TEST_SUITE(VMTests)
@ -542,7 +542,7 @@ BOOST_AUTO_TEST_CASE(vmRandom)
BOOST_AUTO_TEST_CASE(userDefinedFile)
{
dev::test::userDefinedTest("--singletest", dev::test::doVMTests);
dev::test::userDefinedTest(dev::test::doVMTests);
}
BOOST_AUTO_TEST_SUITE_END()

150
test/libp2p/capability.cpp

@ -0,0 +1,150 @@
/*
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 capability.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date May 2015
*/
#include <boost/test/unit_test.hpp>
#include <chrono>
#include <thread>
#include <libp2p/Common.h>
#include <libp2p/Host.h>
#include <libp2p/Session.h>
#include <libp2p/Capability.h>
#include <libp2p/HostCapability.h>
using namespace std;
using namespace dev;
using namespace dev::p2p;
struct P2PFixture
{
P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = true; }
~P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = false; }
};
struct VerbosityHolder
{
VerbosityHolder(): oldLogVerbosity(g_logVerbosity) { g_logVerbosity = 10; }
~VerbosityHolder() { g_logVerbosity = oldLogVerbosity; }
int oldLogVerbosity;
};
class TestCapability: public Capability
{
public:
TestCapability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): Capability(_s, _h, _idOffset), m_cntReceivedMessages(0), m_testSum(0) {}
virtual ~TestCapability() {}
int countReceivedMessages() { return m_cntReceivedMessages; }
int testSum() { return m_testSum; }
static std::string name() { return "test"; }
static u256 version() { return 2; }
static unsigned messageCount() { return UserPacket + 1; }
void sendTestMessage(int _i) { RLPStream s; sealAndSend(prep(s, UserPacket, 1) << _i); }
protected:
virtual bool interpret(unsigned _id, RLP const& _r) override;
int m_cntReceivedMessages;
int m_testSum;
};
bool TestCapability::interpret(unsigned _id, RLP const& _r)
{
cnote << "Capability::interpret(): custom message received";
++m_cntReceivedMessages;
m_testSum += _r[0].toInt();
BOOST_ASSERT(_id == UserPacket);
return (_id == UserPacket);
}
class TestHostCapability: public HostCapability<TestCapability>, public Worker
{
public:
TestHostCapability(): Worker("test") {}
virtual ~TestHostCapability() {}
void sendTestMessage(NodeId const& _id, int _x)
{
for (auto i: peerSessions())
if (_id == i.second->id)
i.first->cap<TestCapability>().get()->sendTestMessage(_x);
}
std::pair<int, int> retrieveTestData(NodeId const& _id)
{
int cnt = 0;
int checksum = 0;
for (auto i: peerSessions())
if (_id == i.second->id)
{
cnt += i.first->cap<TestCapability>().get()->countReceivedMessages();
checksum += i.first->cap<TestCapability>().get()->testSum();
}
return std::pair<int, int>(cnt, checksum);
}
};
BOOST_FIXTURE_TEST_SUITE(p2pCapability, P2PFixture)
BOOST_AUTO_TEST_CASE(capability)
{
VerbosityHolder verbosityHolder;
cnote << "Testing Capability...";
const char* const localhost = "127.0.0.1";
NetworkPreferences prefs1(localhost, 30301, false);
NetworkPreferences prefs2(localhost, 30302, false);
Host host1("Test", prefs1);
auto thc1 = host1.registerCapability(new TestHostCapability());
host1.start();
Host host2("Test", prefs2);
auto thc2 = host2.registerCapability(new TestHostCapability());
host2.start();
int const step = 10;
for (int i = 0; i < 3000 && (!host1.isStarted() || !host2.isStarted()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host1.isStarted() && host2.isStarted());
host1.requirePeer(host2.id(), NodeIPEndpoint(bi::address::from_string(localhost), prefs2.listenPort, prefs2.listenPort));
for (int i = 0; i < 3000 && (!host1.peerCount() || !host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host1.peerCount() > 0 && host2.peerCount() > 0);
int const target = 7;
int checksum = 0;
for (int i = 0; i < target; checksum += i++)
thc2->sendTestMessage(host1.id(), i);
this_thread::sleep_for(chrono::seconds(1));
std::pair<int, int> testData = thc1->retrieveTestData(host2.id());
BOOST_REQUIRE_EQUAL(target, testData.first);
BOOST_REQUIRE_EQUAL(checksum, testData.second);
}
BOOST_AUTO_TEST_SUITE_END()

33
test/libsolidity/SolidityCompiler.cpp

@ -116,36 +116,35 @@ BOOST_AUTO_TEST_CASE(ifStatement)
bytes code = compileContract(sourceCode);
unsigned shift = 60;
unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST),
bytes expectation({
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1),
byte(Instruction::PUSH1), byte(0x1b + shift), // "true" target
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x0f + shift), // "false" target
byte(Instruction::JUMPI),
// "if" body
byte(Instruction::PUSH1), 0x4d,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::JUMP),
// new check "else if" condition
byte(Instruction::JUMPDEST),
byte(Instruction::DUP1),
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x13 + shift),
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x1c + shift),
byte(Instruction::JUMPI),
// "else" body
byte(Instruction::PUSH1), 0x4f,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x17 + shift), // exit path of second part
byte(Instruction::JUMP),
// "else if" body
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x4e,
byte(Instruction::POP),
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), byte(0x1f + shift),
byte(Instruction::PUSH1), byte(0x20 + shift),
byte(Instruction::JUMP),
// "if" body
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x4d,
byte(Instruction::POP),
byte(Instruction::JUMPDEST),
// "else" body
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x4f,
byte(Instruction::POP),
byte(Instruction::JUMP)});
});
checkCodePresentAt(code, expectation, boilerplateSize);
}

95
test/libsolidity/SolidityOptimizer.cpp

@ -29,6 +29,7 @@
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/BlockDeduplicator.h>
using namespace std;
using namespace dev::eth;
@ -125,7 +126,7 @@ public:
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation)
AssemblyItems getCFG(AssemblyItems const& _input)
{
AssemblyItems output = _input;
// Running it four times should be enough for these tests.
@ -138,6 +139,12 @@ public:
back_inserter(optItems));
output = move(optItems);
}
return output;
}
void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation)
{
AssemblyItems output = getCFG(_input);
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
@ -251,6 +258,63 @@ BOOST_AUTO_TEST_CASE(function_calls)
compareVersions("f(uint256)", 36);
}
BOOST_AUTO_TEST_CASE(storage_write_in_loops)
{
char const* sourceCode = R"(
contract test {
uint d;
function f(uint a) returns (uint r) {
var x = d;
for (uint i = 1; i < a * a; i++) {
r = d;
d = i;
}
}
}
)";
compileBothVersions(sourceCode);
compareVersions("f(uint256)", 0);
compareVersions("f(uint256)", 10);
compareVersions("f(uint256)", 36);
}
BOOST_AUTO_TEST_CASE(retain_information_in_branches)
{
// This tests that the optimizer knows that we already have "z == sha3(y)" inside both branches.
char const* sourceCode = R"(
contract c {
bytes32 d;
uint a;
function f(uint x, bytes32 y) returns (uint r_a, bytes32 r_d) {
bytes32 z = sha3(y);
if (x > 8) {
z = sha3(y);
a = x;
} else {
z = sha3(y);
a = x;
}
r_a = a;
r_d = d;
}
}
)";
compileBothVersions(sourceCode);
compareVersions("f(uint256,bytes32)", 0, "abc");
compareVersions("f(uint256,bytes32)", 8, "def");
compareVersions("f(uint256,bytes32)", 10, "ghi");
m_optimize = true;
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
size_t numSHA3s = 0;
eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
if (_instr == eth::Instruction::SHA3)
numSHA3s++;
});
BOOST_CHECK_EQUAL(1, numSHA3s);
}
BOOST_AUTO_TEST_CASE(cse_intermediate_swap)
{
eth::KnownState state;
@ -868,6 +932,35 @@ BOOST_AUTO_TEST_CASE(control_flow_graph_do_not_remove_returned_to)
checkCFG(input, {u256(2)});
}
BOOST_AUTO_TEST_CASE(block_deduplicator)
{
AssemblyItems input{
AssemblyItem(PushTag, 2),
AssemblyItem(PushTag, 1),
AssemblyItem(PushTag, 3),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 1),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 2),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 3)
};
BlockDeduplicator dedup(input);
dedup.deduplicate();
set<u256> pushTags;
for (AssemblyItem const& item: input)
if (item.type() == PushTag)
pushTags.insert(item.data());
BOOST_CHECK_EQUAL(pushTags.size(), 2);
}
BOOST_AUTO_TEST_SUITE_END()
}

Loading…
Cancel
Save