(ethereum()->gasLimitRemaining(), ((b - value()) / gasPrice()));
// Dry-run execution to determine gas requirement and any execution errors
Address to;
@@ -326,10 +326,10 @@ void Transact::rejigData()
else
{
to = m_context->fromString(ui->destination->currentText());
- er = ethereum()->call(s, value(), to, m_data, ethereum()->gasLimitRemaining(), gasPrice());
+ er = ethereum()->call(s, value(), to, m_data, gasNeeded, gasPrice());
}
- gasNeeded = (qint64)er.gasUsed;
- htmlInfo = QString("INFO Gas required: %1 total = %2 base, %3 exec
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas) + htmlInfo;
+ gasNeeded = (qint64)(er.gasUsed + er.gasRefunded);
+ htmlInfo = QString("INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo;
if (er.excepted != TransactionException::None)
{
@@ -338,7 +338,7 @@ void Transact::rejigData()
}
if (er.codeDeposit == CodeDeposit::Failed)
{
- bail("ERROR Code deposit failed due to insufficient gas
");
+ bail("ERROR Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(er.gasForDeposit)) + " GAS < " + QString::fromStdString(toString(er.depositSize)) + " bytes * " + QString::fromStdString(toString(c_createDataGas)) + "GAS/byte
");
return;
}
diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake
index a0d5a50c1..dd14b0650 100755
--- a/cmake/EthCompilerSettings.cmake
+++ b/cmake/EthCompilerSettings.cmake
@@ -38,12 +38,14 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# disable decorated name length exceeded, name was truncated (4503)
# disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests)
# declare Windows XP requirement
- add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501)
+ # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions
+ add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501 /DNOMINMAX)
# disable empty object file warning
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
# warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification
# warning LNK4099: pdb was not found with lib
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075")
+ # stack size 16MB
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:16777216")
# windows likes static
set(ETH_STATIC 1)
diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake
index ea272da56..5c0eaa65b 100644
--- a/cmake/EthDependencies.cmake
+++ b/cmake/EthDependencies.cmake
@@ -30,6 +30,9 @@ if (APPLE)
set (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/usr/local/opt/qt5")
endif()
+find_program(CTEST_COMMAND ctest)
+message(STATUS "ctest path: ${CTEST_COMMAND}")
+
# Dependencies must have a version number, to ensure reproducible build. The version provided here is the one that is in the extdep repository. If you use system libraries, version numbers may be different.
find_package (CryptoPP 5.6.2 EXACT REQUIRED)
@@ -45,16 +48,10 @@ find_package (Jsoncpp 0.60 REQUIRED)
message(" - Jsoncpp header: ${JSONCPP_INCLUDE_DIRS}")
message(" - Jsoncpp lib : ${JSONCPP_LIBRARIES}")
-# TODO the JsonRpcCpp package does not yet check for correct version number
-# json-rpc-cpp support is currently not mandatory
-# TODO make headless client optional
# TODO get rid of -DETH_JSONRPC
+# TODO add EXACT once we commit ourselves to cmake 3.x
if (JSONRPC)
-
- find_package (JsonRpcCpp 0.3.2)
- if (NOT JSON_RPC_CPP_FOUND)
- message (FATAL_ERROR "JSONRPC 0.3.2. not found")
- endif()
+ find_package (json_rpc_cpp 0.4 REQUIRED)
message (" - json-rpc-cpp header: ${JSON_RPC_CPP_INCLUDE_DIRS}")
message (" - json-rpc-cpp lib : ${JSON_RPC_CPP_LIBRARIES}")
add_definitions(-DETH_JSONRPC)
@@ -114,14 +111,17 @@ if (NOT HEADLESS)
# find all of the Qt packages
# remember to use 'Qt' instead of 'QT', cause unix is case sensitive
# TODO make headless client optional
- find_package (Qt5Core REQUIRED)
- find_package (Qt5Gui REQUIRED)
- find_package (Qt5Quick REQUIRED)
- find_package (Qt5Qml REQUIRED)
- find_package (Qt5Network REQUIRED)
- find_package (Qt5Widgets REQUIRED)
- find_package (Qt5WebEngine REQUIRED)
- find_package (Qt5WebEngineWidgets REQUIRED)
+
+ set (ETH_QT_VERSION 5.4)
+
+ find_package (Qt5Core ${ETH_QT_VERSION} REQUIRED)
+ find_package (Qt5Gui ${ETH_QT_VERSION} REQUIRED)
+ find_package (Qt5Quick ${ETH_QT_VERSION} REQUIRED)
+ find_package (Qt5Qml ${ETH_QT_VERSION} REQUIRED)
+ find_package (Qt5Network ${ETH_QT_VERSION} REQUIRED)
+ find_package (Qt5Widgets ${ETH_QT_VERSION} REQUIRED)
+ find_package (Qt5WebEngine ${ETH_QT_VERSION} REQUIRED)
+ find_package (Qt5WebEngineWidgets ${ETH_QT_VERSION} REQUIRED)
# we need to find path to macdeployqt on mac
if (APPLE)
diff --git a/cmake/EthUtils.cmake b/cmake/EthUtils.cmake
index c6fd43ed4..69690156a 100644
--- a/cmake/EthUtils.cmake
+++ b/cmake/EthUtils.cmake
@@ -22,3 +22,43 @@ macro(replace_if_different SOURCE DST)
endif()
endmacro()
+macro(eth_add_test NAME)
+
+ # parse arguments here
+ set(commands)
+ set(current_command "")
+ foreach (arg ${ARGN})
+ if (arg STREQUAL "ARGS")
+ if (current_command)
+ list(APPEND commands ${current_command})
+ endif()
+ set(current_command "")
+ else ()
+ set(current_command "${current_command} ${arg}")
+ endif()
+ endforeach(arg)
+ list(APPEND commands ${current_command})
+
+ message(STATUS "test: ${NAME} | ${commands}")
+
+ # create tests
+ set(index 0)
+ list(LENGTH commands count)
+ while (index LESS count)
+ list(GET commands ${index} test_arguments)
+
+ set(run_test "--run_test=${NAME}")
+ add_test(NAME "${NAME}.${index}" COMMAND testeth ${run_test} ${test_arguments})
+
+ math(EXPR index "${index} + 1")
+ endwhile(index LESS count)
+
+ # add target to run them
+ add_custom_target("test.${NAME}"
+ DEPENDS testeth
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND ${CMAKE_COMMAND} -DETH_TEST_NAME="${NAME}" -DCTEST_COMMAND="${CTEST_COMMAND}" -P "${ETH_SCRIPTS_DIR}/runtest.cmake"
+ )
+
+endmacro()
+
diff --git a/cmake/FindJsonRpcCpp.cmake b/cmake/Findjson_rpc_cpp.cmake
similarity index 65%
rename from cmake/FindJsonRpcCpp.cmake
rename to cmake/Findjson_rpc_cpp.cmake
index 39fd3b39c..9b64cda2b 100644
--- a/cmake/FindJsonRpcCpp.cmake
+++ b/cmake/Findjson_rpc_cpp.cmake
@@ -10,6 +10,11 @@
# JSON_RPC_CPP_SERVER_LIBRARIES, the libraries needed to use json-rpc-cpp-server
# JSON_RPC_CPP_CLIENT_LIBRARIES, the libraries needed to use json-rpc-cpp-client
# JSON_RCP_CPP_FOUND, If false, do not try to use json-rpc-cpp.
+# JSON_RPC_CPP_VERSION, version of library
+# JSON_RPC_CPP_VERSION_MAJOR
+# JSON_RPC_CPP_VERSION_MINOR
+# JSON_RPC_CPP_VERSION_PATCH
+
# only look in default directories
find_path(
@@ -90,10 +95,28 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
endif()
+if (JSON_RPC_CPP_INCLUDE_DIR)
+ set (JSON_RPC_CPP_VERSION_HEADER "${JSON_RPC_CPP_INCLUDE_DIR}/jsonrpccpp/version.h")
+ if (EXISTS ${JSON_RPC_CPP_VERSION_HEADER})
+ file (STRINGS ${JSON_RPC_CPP_VERSION_HEADER} JSON_RPC_CPP_VERSION_MAJOR REGEX "^#define JSONRPC_CPP_MAJOR_VERSION[ \t]+[0-9]+$")
+ file (STRINGS ${JSON_RPC_CPP_VERSION_HEADER} JSON_RPC_CPP_VERSION_MINOR REGEX "^#define JSONRPC_CPP_MINOR_VERSION[ \t]+[0-9]+$")
+ file (STRINGS ${JSON_RPC_CPP_VERSION_HEADER} JSON_RPC_CPP_VERSION_PATCH REGEX "^#define JSONRPC_CPP_PATCH_VERSION[ \t]+[0-9]+$")
+ string (REGEX REPLACE "^#define JSONRPC_CPP_MAJOR_VERSION[ \t]+([0-9]+)" "\\1" JSON_RPC_CPP_VERSION_MAJOR ${JSON_RPC_CPP_VERSION_MAJOR})
+ string (REGEX REPLACE "^#define JSONRPC_CPP_MINOR_VERSION[ \t]+([0-9]+)" "\\1" JSON_RPC_CPP_VERSION_MINOR ${JSON_RPC_CPP_VERSION_MINOR})
+ string (REGEX REPLACE "^#define JSONRPC_CPP_PATCH_VERSION[ \t]+([0-9]+)" "\\1" JSON_RPC_CPP_VERSION_PATCH ${JSON_RPC_CPP_VERSION_PATCH})
+ set (JSON_RPC_CPP_VERSION ${JSON_RPC_CPP_VERSION_MAJOR}.${JSON_RPC_CPP_VERSION_MINOR}.${JSON_RPC_CPP_VERSION_PATCH})
+ endif()
+endif()
+
# handle the QUIETLY and REQUIRED arguments and set JSON_RPC_CPP_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(json_rpc_cpp DEFAULT_MSG
- JSON_RPC_CPP_COMMON_LIBRARY JSON_RPC_CPP_SERVER_LIBRARY JSON_RPC_CPP_CLIENT_LIBRARY JSON_RPC_CPP_INCLUDE_DIR)
-mark_as_advanced (JSON_RPC_CPP_COMMON_LIBRARY JSON_RPC_CPP_SERVER_LIBRARY JSON_RPC_CPP_CLIENT_LIBRARY JSON_RPC_CPP_INCLUDE_DIR)
+
+find_package_handle_standard_args(
+ json_rpc_cpp
+ REQUIRED_VARS JSON_RPC_CPP_INCLUDE_DIR JSON_RPC_CPP_COMMON_LIBRARY JSON_RPC_CPP_SERVER_LIBRARY JSON_RPC_CPP_CLIENT_LIBRARY
+ VERSION_VAR JSON_RPC_CPP_VERSION
+)
+
+mark_as_advanced (JSON_RPC_CPP_INCLUDE_DIR JSON_RPC_CPP_COMMON_LIBRARY JSON_RPC_CPP_SERVER_LIBRARY JSON_RPC_CPP_CLIENT_LIBRARY)
diff --git a/cmake/scripts/runtest.cmake b/cmake/scripts/runtest.cmake
new file mode 100644
index 000000000..15f7409ef
--- /dev/null
+++ b/cmake/scripts/runtest.cmake
@@ -0,0 +1,15 @@
+# Should be used to run ctest
+#
+# example usage:
+# cmake -DETH_TEST_NAME=TestInterfaceStub -DCTEST_COMMAND=/path/to/ctest -P scripts/runtest.cmake
+
+if (NOT CTEST_COMMAND)
+ message(FATAL_ERROR "ctest could not be found!")
+endif()
+
+# verbosity is off, cause BOOST_MESSAGE is not thread safe and output is a trash
+# see https://codecrafter.wordpress.com/2012/11/01/c-unit-test-framework-adapter-part-3/
+#
+# output might not be usefull cause of thread safety issue
+execute_process(COMMAND ${CTEST_COMMAND} --force-new-ctest-process -C Debug --output-on-failure -j 4 -R "${ETH_TEST_NAME}[.].*")
+
diff --git a/eth/main.cpp b/eth/main.cpp
index eda6e12ae..2a065cc8c 100644
--- a/eth/main.cpp
+++ b/eth/main.cpp
@@ -77,7 +77,6 @@ void interactiveHelp()
<< " setetherprice Resets the ether price." << endl
<< " setpriority
Resets the transaction priority." << endl
<< " minestart Starts mining." << endl
- << " minestart Starts mining." << endl
<< " minestop Stops mining." << endl
<< " mineforce Forces mining, even when there are no transactions." << endl
<< " address Gives the current address." << endl
@@ -88,12 +87,14 @@ void interactiveHelp()
<< " send Execute a given transaction with current secret." << endl
<< " contract Create a new contract with current secret." << endl
<< " peers List the peers that are connected" << endl
+#if ETH_FATDB
<< " listaccounts List the accounts on the network." << endl
<< " listcontracts List the contracts on the network." << endl
- << " setsecret Set the secret to the hex secret key." < Set the coinbase (mining payout) address." < Export the config (.RLP) to the path provided." < Import the config (.RLP) from the path provided." < Set the secret to the hex secret key." << endl
+ << " setaddress Set the coinbase (mining payout) address." << endl
+ << " exportconfig Export the config (.RLP) to the path provided." << endl
+ << " importconfig Import the config (.RLP) from the path provided." << endl
<< " inspect Dumps a contract to /.evm." << endl
<< " dumptrace Dumps a transaction trace" << endl << "to . should be one of pretty, standard, standard+." << endl
<< " dumpreceipt Dumps a transation receipt." << endl
@@ -117,11 +118,12 @@ void help()
<< " -i,--interactive Enter interactive mode (default: non-interactive)." << endl
#if ETH_JSONRPC
<< " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl
- << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl
+ << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
#endif
<< " -K,--kill-blockchain First kill the blockchain." << endl
- << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl
- << " -L,--local-networking Use peers whose addresses are local." << endl
+ << " --listen-ip Listen on the given port for incoming connections (default: 30303)." << endl
+ << " -l,--listen Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl
+ << " -u,--public-ip Force public ip to given (default: auto)." << endl
<< " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl
<< " -n,--upnp Use upnp for NAT (default: on)." << endl
<< " -o,--mode Start a full node or a peer node (Default: full)." << endl
@@ -130,7 +132,6 @@ void help()
<< " -r,--remote Connect to remote host (default: none)." << endl
<< " -s,--secret Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
- << " -u,--public-ip Force public ip to given (default; auto)." << endl
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl
<< " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl
<< " -V,--version Show the version and exit." << endl
@@ -198,7 +199,9 @@ enum class NodeMode
int main(int argc, char** argv)
{
+ string listenIP;
unsigned short listenPort = 30303;
+ string publicIP;
string remoteHost;
unsigned short remotePort = 30303;
string dbPath;
@@ -210,10 +213,8 @@ int main(int argc, char** argv)
#if ETH_JSONRPC
int jsonrpc = -1;
#endif
- string publicIP;
bool bootstrap = false;
bool upnp = true;
- bool useLocal = false;
bool forceMining = false;
bool killChain = false;
bool jit = false;
@@ -249,7 +250,9 @@ int main(int argc, char** argv)
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
- if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc)
+ if (arg == "--listen-ip" && i + 1 < argc)
+ listenIP = argv[++i];
+ else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc)
listenPort = (short)atoi(argv[++i]);
else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc)
publicIP = argv[++i];
@@ -270,8 +273,6 @@ int main(int argc, char** argv)
return -1;
}
}
- else if (arg == "-L" || arg == "--local-networking")
- useLocal = true;
else if (arg == "-K" || arg == "--kill-blockchain")
killChain = true;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
@@ -370,7 +371,7 @@ int main(int argc, char** argv)
interactive = true;
#if ETH_JSONRPC
else if ((arg == "-j" || arg == "--json-rpc"))
- jsonrpc = jsonrpc == -1 ? 8080 : jsonrpc;
+ jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc;
else if (arg == "--json-rpc-port" && i + 1 < argc)
jsonrpc = atoi(argv[++i]);
#endif
@@ -420,7 +421,7 @@ int main(int argc, char** argv)
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
- NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal);
+ auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
std::string clientImplString = "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : "");
dev::WebThreeDirect web3(
@@ -448,16 +449,16 @@ int main(int argc, char** argv)
web3.startNetwork();
if (bootstrap)
- web3.connect(Host::pocHost());
+ web3.addNode(p2p::NodeId(), Host::pocHost());
if (remoteHost.size())
- web3.connect(remoteHost, remotePort);
+ web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
#if ETH_JSONRPC
shared_ptr jsonrpcServer;
unique_ptr jsonrpcConnector;
if (jsonrpc > -1)
{
- jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc));
+ jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
@@ -510,7 +511,7 @@ int main(int argc, char** argv)
string addr;
unsigned port;
iss >> addr >> port;
- web3.connect(addr, (short)port);
+ web3.addNode(p2p::NodeId(), addr + ":" + toString(port ? port : p2p::c_defaultIPPort));
}
else if (cmd == "netstop")
{
@@ -582,8 +583,8 @@ int main(int argc, char** argv)
else if (cmd == "jsonstart")
{
if (jsonrpc < 0)
- jsonrpc = 8080;
- jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc));
+ jsonrpc = SensibleHttpPort;
+ jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
@@ -685,6 +686,7 @@ int main(int argc, char** argv)
else
cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA";
}
+#if ETH_FATDB
else if (c && cmd == "listcontracts")
{
auto acs =c->addresses();
@@ -707,6 +709,7 @@ int main(int argc, char** argv)
cout << ss << endl;
}
}
+#endif
else if (c && cmd == "send")
{
if (iss.peek() != -1)
@@ -832,11 +835,8 @@ int main(int argc, char** argv)
Executive e(state, c->blockChain(), 0);
Transaction t = state.pending()[index];
state = state.fromPending(index);
- bytes r = t.rlp();
try
{
- e.setup(&r);
-
OnOpFunc oof;
if (format == "pretty")
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
@@ -869,7 +869,9 @@ int main(int argc, char** argv)
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
};
- e.go(oof);
+ e.initialize(t);
+ if (!e.execute())
+ e.go(oof);
e.finalize();
}
catch(Exception const& _e)
diff --git a/ethrpctest/CMakeLists.txt b/ethrpctest/CMakeLists.txt
new file mode 100644
index 000000000..5d3fef542
--- /dev/null
+++ b/ethrpctest/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_policy(SET CMP0015 NEW)
+set(CMAKE_AUTOMOC OFF)
+
+aux_source_directory(. SRC_LIST)
+
+include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
+include_directories(BEFORE ..)
+include_directories(${Boost_INCLUDE_DIRS})
+include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
+
+set(EXECUTABLE ethrpctest)
+
+file(GLOB HEADERS "*.h")
+
+add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
+
+add_dependencies(${EXECUTABLE} BuildInfo.h)
+
+target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
+
+if (READLINE_FOUND)
+ target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
+endif()
+
+target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES})
+target_link_libraries(${EXECUTABLE} ${Boost_PROGRAM_OPTIONS_LIBRARIES})
+target_link_libraries(${EXECUTABLE} testutils)
+target_link_libraries(${EXECUTABLE} web3jsonrpc)
+
+if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
+ add_custom_command(TARGET ${EXECUTABLE} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -E copy ${MHD_DLL_RELEASE} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
+endif()
+
+install( TARGETS ${EXECUTABLE} DESTINATION bin )
+
diff --git a/ethrpctest/CommandLineInterface.cpp b/ethrpctest/CommandLineInterface.cpp
new file mode 100644
index 000000000..c4b3d8f27
--- /dev/null
+++ b/ethrpctest/CommandLineInterface.cpp
@@ -0,0 +1,129 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file CommandLineInterface.cpp
+ * @author Marek Kotewicz
+ * @date 2015
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "CommandLineInterface.h"
+#include "BuildInfo.h"
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+using namespace dev::test;
+namespace po = boost::program_options;
+
+bool CommandLineInterface::parseArguments(int argc, char** argv)
+{
+ // Declare the supported options.
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "Show help message and exit")
+ ("json", po::value>()->required(), "input file")
+ ("test", po::value>()->required(), "test case name");
+
+ // All positional options should be interpreted as input files
+ po::positional_options_description p;
+
+ // parse the compiler arguments
+ try
+ {
+ po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args);
+
+ if (m_args.count("help"))
+ {
+ cout << desc;
+ return false;
+ }
+
+ po::notify(m_args);
+ }
+ catch (po::error const& _exception)
+ {
+ cout << _exception.what() << endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool CommandLineInterface::processInput()
+{
+ string infile = m_args["json"].as>()[0];
+
+ auto path = boost::filesystem::path(infile);
+ if (!boost::filesystem::exists(path))
+ {
+ cout << "Non existant input file \"" << infile << "\"" << endl;
+ return false;
+ }
+
+ string test = m_args["test"].as>()[0];
+ Json::Value j = dev::test::loadJsonFromFile(path.string());
+
+ if (j[test].empty())
+ {
+ cout << "Non existant test case \"" << infile << "\"" << endl;
+ return false;
+ }
+
+ if (!j[test].isObject())
+ {
+ cout << "Incorrect JSON file \"" << infile << "\"" << endl;
+ return false;
+ }
+
+ m_json = j[test];
+
+ return true;
+}
+
+bool g_exit = false;
+
+void sighandler(int)
+{
+ g_exit = true;
+}
+
+void CommandLineInterface::actOnInput()
+{
+ BlockChainLoader bcl(m_json);
+ FixedClient client(bcl.bc(), bcl.state());
+ unique_ptr jsonrpcServer;
+ auto server = new jsonrpc::HttpServer(8080, "", "", 2);
+ jsonrpcServer.reset(new FixedWebThreeServer(*server, {}, &client));
+ jsonrpcServer->StartListening();
+
+ signal(SIGABRT, &sighandler);
+ signal(SIGTERM, &sighandler);
+ signal(SIGINT, &sighandler);
+
+ while (!g_exit)
+ this_thread::sleep_for(chrono::milliseconds(1000));
+}
diff --git a/ethrpctest/CommandLineInterface.h b/ethrpctest/CommandLineInterface.h
new file mode 100644
index 000000000..b57e1df5e
--- /dev/null
+++ b/ethrpctest/CommandLineInterface.h
@@ -0,0 +1,46 @@
+/*
+ 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 .
+*/
+/** CommandLineInterface.h
+ * @author Marek Kotewicz
+ * @date 2015
+ */
+#pragma once
+
+#include
+#include
+
+class CommandLineInterface
+{
+public:
+ CommandLineInterface() {}
+
+ /// Parse command line arguments and return false if we should not continue
+ bool parseArguments(int argc, char** argv);
+ /// Parse input file and check if test exists
+ bool processInput();
+ /// Start FixedJsonRpcServer
+ void actOnInput();
+
+private:
+
+ /// Compiler arguments variable map
+ boost::program_options::variables_map m_args;
+
+ /// loaded json test case
+ Json::Value m_json;
+};
+
diff --git a/ethrpctest/main.cpp b/ethrpctest/main.cpp
new file mode 100644
index 000000000..3d710dd5b
--- /dev/null
+++ b/ethrpctest/main.cpp
@@ -0,0 +1,34 @@
+/*
+ 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 .
+*/
+/** main.cpp
+ * @author Marek Kotewicz
+ * @date 2015
+ */
+
+#include "CommandLineInterface.h"
+
+int main(int argc, char** argv)
+{
+ CommandLineInterface cli;
+ if (!cli.parseArguments(argc, argv))
+ return 1;
+ if (!cli.processInput())
+ return 1;
+ cli.actOnInput();
+
+ return 0;
+}
diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp
index fae320c28..b89aca726 100644
--- a/evmjit/libevmjit-cpp/Env.cpp
+++ b/evmjit/libevmjit-cpp/Env.cpp
@@ -52,7 +52,6 @@ extern "C"
auto endowment = llvm2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
- _env->subBalance(endowment);
u256 gas = *io_gas;
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
*io_gas = static_cast(gas);
@@ -89,10 +88,7 @@ extern "C"
auto ret = false;
auto callGas = u256{_callGas};
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
- {
- _env->subBalance(value);
ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress);
- }
*io_gas += static_cast(callGas); // it is never more than initial _callGas
return ret;
diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp
index f9588fbea..ce1f530d7 100644
--- a/evmjit/libevmjit/ExecutionEngine.cpp
+++ b/evmjit/libevmjit/ExecutionEngine.cpp
@@ -4,6 +4,8 @@
#include
#include
#include
+#include
+#include
#include "preprocessor/llvm_includes_start.h"
#include
@@ -82,7 +84,20 @@ void parseOptions()
{
static llvm::llvm_shutdown_obj shutdownObj{};
cl::AddExtraVersionPrinter(printVersion);
- cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
+ //cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
+
+ // FIXME: LLVM workaround:
+ // Manually select instruction scheduler other than "source".
+ // "source" scheduler has a bug: http://llvm.org/bugs/show_bug.cgi?id=22304
+ auto envLine = std::getenv("EVMJIT");
+ auto commandLine = std::string{"evmjit "} + (envLine ? envLine : "") + " -pre-RA-sched=list-burr\0";
+ static const auto c_maxArgs = 20;
+ char const* argv[c_maxArgs] = {nullptr, };
+ auto arg = std::strtok(&*commandLine.begin(), " ");
+ auto i = 0;
+ for (; i < c_maxArgs && arg; ++i, arg = std::strtok(nullptr, " "))
+ argv[i] = arg;
+ cl::ParseCommandLineOptions(i, argv, "Ethereum EVM JIT Compiler");
}
}
diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h
index 1c7a4cd61..6468e250f 100644
--- a/libdevcore/CommonData.h
+++ b/libdevcore/CommonData.h
@@ -42,14 +42,15 @@ enum class WhenError
};
/// Convert a series of bytes to the corresponding string of hex duplets.
-/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte.
+/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169"
template
std::string toHex(_T const& _data, int _w = 2)
{
std::ostringstream ret;
+ unsigned ii = 0;
for (auto i: _data)
- ret << std::hex << std::setfill('0') << std::setw(_w) << (int)(typename std::make_unsigned::type)i;
+ ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned::type)i;
return ret.str();
}
@@ -74,6 +75,13 @@ inline std::string asString(bytes const& _b)
return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size()));
}
+/// Converts byte array ref to a string containing the same (binary) data. Unless
+/// the byte array happens to contain ASCII data, this won't be printable.
+inline std::string asString(bytesConstRef _b)
+{
+ return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size()));
+}
+
/// Converts a string to a byte array containing the string's (byte) data.
inline bytes asBytes(std::string const& _b)
{
diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp
index 4fa132073..46d6e3a6b 100644
--- a/libdevcore/CommonIO.cpp
+++ b/libdevcore/CommonIO.cpp
@@ -112,6 +112,6 @@ string dev::contentsString(std::string const& _file)
void dev::writeFile(std::string const& _file, bytesConstRef _data)
{
- ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size());
+ ofstream(_file, ios::trunc|ios::binary).write((char const*)_data.data(), _data.size());
}
diff --git a/libdevcore/CommonJS.h b/libdevcore/CommonJS.h
index cc487d54c..ade089e16 100644
--- a/libdevcore/CommonJS.h
+++ b/libdevcore/CommonJS.h
@@ -38,7 +38,10 @@ template std::string toJS(FixedHash const& _h)
template std::string toJS(boost::multiprecision::number> const& _n)
{
- return "0x" + toHex(toCompactBigEndian(_n, 1));
+ std::string h = toHex(toCompactBigEndian(_n, 1));
+ // remove first 0, if it is necessary;
+ std::string res = h[0] != '0' ? h : h.substr(1);
+ return "0x" + res;
}
inline std::string toJS(bytes const& _n, std::size_t _padding = 0)
diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h
index f5469ada8..456365299 100644
--- a/libdevcore/FixedHash.h
+++ b/libdevcore/FixedHash.h
@@ -141,6 +141,7 @@ public:
return ret;
}
+ /// @returns a random valued object.
static FixedHash random() { return random(s_fixedHashEngine); }
/// A generic std::hash compatible function object.
diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h
index 4ecc1cbd1..5e9bba3e8 100644
--- a/libdevcore/vector_ref.h
+++ b/libdevcore/vector_ref.h
@@ -39,7 +39,8 @@ public:
size_t size() const { return m_count; }
bool empty() const { return !m_count; }
vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); }
- vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); }
+ vector_ref<_T> cropped(size_t _begin, size_t _count) const { if (m_data && _begin + _count <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); }
+ vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); }
void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; }
void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); }
void copyTo(vector_ref::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); }
diff --git a/libdevcrypto/MemoryDB.h b/libdevcrypto/MemoryDB.h
index 7d39ba73b..7858126f8 100644
--- a/libdevcrypto/MemoryDB.h
+++ b/libdevcrypto/MemoryDB.h
@@ -32,8 +32,10 @@ namespace dev
{
struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 18; };
+struct DBWarn: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 1; };
#define dbdebug clog(DBChannel)
+#define dbwarn clog(DBWarn)
class MemoryDB
{
diff --git a/libethash/CMakeLists.txt b/libethash/CMakeLists.txt
index 38fc821c0..c92240086 100644
--- a/libethash/CMakeLists.txt
+++ b/libethash/CMakeLists.txt
@@ -12,6 +12,7 @@ endif()
set(FILES util.c
util.h
+ io.c
internal.c
ethash.h
endian.h
@@ -19,6 +20,12 @@ set(FILES util.c
fnv.h
data_sizes.h)
+if (MSVC)
+ list(APPEND FILES io_win32.c)
+else()
+ list(APPEND FILES io_posix.c)
+endif()
+
if (NOT CRYPTOPP_FOUND)
find_package(CryptoPP 5.6.2)
endif()
diff --git a/libethash/data_sizes.h b/libethash/data_sizes.h
index 3b747b3ea..cf52ae4f8 100644
--- a/libethash/data_sizes.h
+++ b/libethash/data_sizes.h
@@ -48,7 +48,7 @@ extern "C" {
// Sow[i*HashBytes]; j++]]]][[2]][[1]]
-static const size_t dag_sizes[2048] = {
+static const uint64_t dag_sizes[2048] = {
1073739904U, 1082130304U, 1090514816U, 1098906752U, 1107293056U,
1115684224U, 1124070016U, 1132461952U, 1140849536U, 1149232768U,
1157627776U, 1166013824U, 1174404736U, 1182786944U, 1191180416U,
@@ -477,7 +477,7 @@ static const size_t dag_sizes[2048] = {
// While[! PrimeQ[i], i--];
// Sow[i*HashBytes]; j++]]]][[2]][[1]]
-const size_t cache_sizes[2048] = {
+const uint64_t cache_sizes[2048] = {
16776896U, 16907456U, 17039296U, 17170112U, 17301056U, 17432512U, 17563072U,
17693888U, 17824192U, 17955904U, 18087488U, 18218176U, 18349504U, 18481088U,
18611392U, 18742336U, 18874304U, 19004224U, 19135936U, 19267264U, 19398208U,
diff --git a/libethash/ethash.h b/libethash/ethash.h
index a7159de65..7594fc835 100644
--- a/libethash/ethash.h
+++ b/libethash/ethash.h
@@ -24,92 +24,115 @@
#include
#include
#include
+#include
#include "compiler.h"
-#define REVISION 23
-#define DATASET_BYTES_INIT 1073741824U // 2**30
-#define DATASET_BYTES_GROWTH 8388608U // 2**23
-#define CACHE_BYTES_INIT 1073741824U // 2**24
-#define CACHE_BYTES_GROWTH 131072U // 2**17
-#define EPOCH_LENGTH 30000U
-#define MIX_BYTES 128
-#define HASH_BYTES 64
-#define DATASET_PARENTS 256
-#define CACHE_ROUNDS 3
-#define ACCESSES 64
+#define ETHASH_REVISION 23
+#define ETHASH_DATASET_BYTES_INIT 1073741824U // 2**30
+#define ETHASH_DATASET_BYTES_GROWTH 8388608U // 2**23
+#define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24
+#define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17
+#define ETHASH_EPOCH_LENGTH 30000U
+#define ETHASH_MIX_BYTES 128
+#define ETHASH_HASH_BYTES 64
+#define ETHASH_DATASET_PARENTS 256
+#define ETHASH_CACHE_ROUNDS 3
+#define ETHASH_ACCESSES 64
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ethash_params {
- size_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
- size_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
+ 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;
typedef struct ethash_return_value {
- uint8_t result[32];
- uint8_t mix_hash[32];
+ uint8_t result[32];
+ uint8_t mix_hash[32];
} ethash_return_value;
-size_t ethash_get_datasize(const uint32_t block_number);
-size_t ethash_get_cachesize(const uint32_t block_number);
+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);
+ params->full_size = ethash_get_datasize(block_number);
+ params->cache_size = ethash_get_cachesize(block_number);
}
-typedef struct ethash_cache {
- void *mem;
-} ethash_cache;
+/***********************************
+ * OLD API *************************
+ ***********************************
+ ******************** (deprecated) *
+ ***********************************/
-void ethash_mkcache(ethash_cache *cache, ethash_params const *params, const uint8_t seed[32]);
-void ethash_compute_full_data(void *mem, ethash_params const *params, ethash_cache 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);
-void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
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);
-static inline void ethash_prep_light(void *cache, ethash_params const *params, const uint8_t seed[32]) {
- ethash_cache c;
- c.mem = cache;
- ethash_mkcache(&c, params, seed);
-}
+/***********************************
+ * 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];
-static inline void ethash_compute_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
- ethash_cache c;
- c.mem = (void *) cache;
- ethash_light(ret, &c, params, header_hash, nonce);
+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(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(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_cache c;
- c.mem = (void *) cache;
- ethash_compute_full_data(full, params, &c);
+ 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);
+ ethash_full(ret, full, params, header_hash, nonce);
}
-// Returns if hash is less than or equal to difficulty
-static inline int ethash_check_difficulty(
- const uint8_t hash[32],
- const uint8_t difficulty[32]) {
- // Difficulty is big endian
- for (int i = 0; i < 32; i++) {
- if (hash[i] == difficulty[i]) continue;
- return hash[i] < difficulty[i];
- }
- return 1;
+/// @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;
}
-int ethash_quick_check_difficulty(
- const uint8_t header_hash[32],
- const uint64_t nonce,
- const uint8_t mix_hash[32],
- const uint8_t difficulty[32]);
+/// 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
#ifdef __cplusplus
}
diff --git a/libethash/internal.c b/libethash/internal.c
index 0a7e767e7..130ca13c3 100644
--- a/libethash/internal.c
+++ b/libethash/internal.c
@@ -37,14 +37,14 @@
#include "sha3.h"
#endif // WITH_CRYPTOPP
-size_t ethash_get_datasize(const uint32_t block_number) {
- assert(block_number / EPOCH_LENGTH < 2048);
- return dag_sizes[block_number / EPOCH_LENGTH];
+uint64_t ethash_get_datasize(const uint32_t block_number) {
+ assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
+ return dag_sizes[block_number / ETHASH_EPOCH_LENGTH];
}
-size_t ethash_get_cachesize(const uint32_t block_number) {
- assert(block_number / EPOCH_LENGTH < 2048);
- return cache_sizes[block_number / EPOCH_LENGTH];
+uint64_t ethash_get_cachesize(const uint32_t block_number) {
+ assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
+ return cache_sizes[block_number / ETHASH_EPOCH_LENGTH];
}
// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014)
@@ -63,7 +63,7 @@ void static ethash_compute_cache_nodes(
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
}
- for (unsigned j = 0; j != CACHE_ROUNDS; j++) {
+ for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes;
node data;
@@ -85,10 +85,10 @@ void static ethash_compute_cache_nodes(
}
void ethash_mkcache(
- ethash_cache *cache,
+ void *cache,
ethash_params const *params,
const uint8_t seed[32]) {
- node *nodes = (node *) cache->mem;
+ node *nodes = (node *) cache;
ethash_compute_cache_nodes(nodes, params, seed);
}
@@ -96,10 +96,10 @@ void ethash_calculate_dag_item(
node *const ret,
const unsigned node_index,
const struct ethash_params *params,
- const struct ethash_cache *cache) {
+ const void *cache) {
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
- node const *cache_nodes = (node const *) cache->mem;
+ node const *cache_nodes = (node const *) cache;
node const *init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
@@ -114,7 +114,7 @@ void ethash_calculate_dag_item(
__m128i xmm3 = ret->xmm[3];
#endif
- for (unsigned i = 0; i != DATASET_PARENTS; ++i) {
+ 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;
node const *parent = &cache_nodes[parent_index];
@@ -150,7 +150,7 @@ void ethash_calculate_dag_item(
void ethash_compute_full_data(
void *mem,
ethash_params const *params,
- ethash_cache const *cache) {
+ void const *cache) {
assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0);
assert((params->full_size % sizeof(node)) == 0);
node *full_nodes = mem;
@@ -164,7 +164,7 @@ void ethash_compute_full_data(
static void ethash_hash(
ethash_return_value *ret,
node const *full_nodes,
- ethash_cache const *cache,
+ void const *cache,
ethash_params const *params,
const uint8_t header_hash[32],
const uint64_t nonce) {
@@ -201,7 +201,7 @@ static void ethash_hash(
num_full_pages = (unsigned) (params->full_size / page_size);
- for (unsigned i = 0; i != ACCESSES; ++i) {
+ 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;
for (unsigned n = 0; n != MIX_NODES; ++n) {
@@ -275,26 +275,26 @@ void ethash_quick_hash(
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) {
memset(seedhash, 0, 32);
- const uint32_t epochs = block_number / EPOCH_LENGTH;
+ const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, seedhash, 32);
}
-int ethash_quick_check_difficulty(
+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]) {
+ const uint8_t difficulty[32]) {
- uint8_t return_hash[32];
+ uint8_t return_hash[32];
ethash_quick_hash(return_hash, header_hash, nonce, mix_hash);
- return ethash_check_difficulty(return_hash, difficulty);
+ return ethash_leq_be256(return_hash, difficulty);
}
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 ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
+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);
-}
\ No newline at end of file
+}
diff --git a/libethash/internal.h b/libethash/internal.h
index bcbacdaa4..dec7e6b13 100644
--- a/libethash/internal.h
+++ b/libethash/internal.h
@@ -3,7 +3,7 @@
#include "endian.h"
#include "ethash.h"
-#define ENABLE_SSE 1
+#define ENABLE_SSE 0
#if defined(_M_X64) && ENABLE_SSE
#include
@@ -15,7 +15,7 @@ extern "C" {
// compile time settings
#define NODE_WORDS (64/4)
-#define MIX_WORDS (MIX_BYTES/4)
+#define MIX_WORDS (ETHASH_MIX_BYTES/4)
#define MIX_NODES (MIX_WORDS / NODE_WORDS)
#include
@@ -34,7 +34,7 @@ void ethash_calculate_dag_item(
node *const ret,
const unsigned node_index,
ethash_params const *params,
- ethash_cache const *cache
+ void const *cache
);
void ethash_quick_hash(
@@ -45,4 +45,4 @@ void ethash_quick_hash(
#ifdef __cplusplus
}
-#endif
\ No newline at end of file
+#endif
diff --git a/libethash/io.c b/libethash/io.c
new file mode 100644
index 000000000..0e935fa59
--- /dev/null
+++ b/libethash/io.c
@@ -0,0 +1,89 @@
+/*
+ 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 .
+*/
+/** @file io.c
+ * @author Lefteris Karapetsas
+ * @date 2015
+ */
+#include "io.h"
+#include
+#include
+
+// 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)
+{
+ 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;
+ }
+
+ 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(params->full_size);
+ if (!temp_data_ptr) {
+ 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, params->full_size)) {
+ goto fail_free;
+ }
+
+ 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;
+ }
+
+ *data = temp_data_ptr;
+ *data_size = params->full_size;
+ return true;
+
+fail_free:
+ free(temp_data_ptr);
+end:
+ return false;
+}
+
+#undef PASS_ARR
diff --git a/libethash/io.h b/libethash/io.h
new file mode 100644
index 000000000..0fa292362
--- /dev/null
+++ b/libethash/io.h
@@ -0,0 +1,116 @@
+/*
+ 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 .
+*/
+/** @file io.h
+ * @author Lefteris Karapetsas
+ * @date 2015
+ */
+#pragma once
+#include
+#include
+#include
+#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
+
+/// 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
+};
+
+/**
+ * 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()
+ *
+ * @param 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
+ * @return For possible return values @see enum ethash_io_rc
+ */
+enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash);
+/**
+ * Fully computes data and writes it to the file on disk.
+ *
+ * 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.
+ *
+ * @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.
+ */
+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);
+
+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);
+}
+
+static inline 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;
+ }
+
+ name[0] = '\0';
+ strcat(name, dirname);
+ strcat(name, filename);
+ return name;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libethash/io_posix.c b/libethash/io_posix.c
new file mode 100644
index 000000000..9d9ccac69
--- /dev/null
+++ b/libethash/io_posix.c
@@ -0,0 +1,76 @@
+/*
+ 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 .
+*/
+/** @file io_posix.c
+ * @author Lefteris Karapetsas
+ * @date 2015
+ */
+
+#include "io.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
+{
+ char read_buffer[DAG_MEMO_BYTESIZE];
+ char expect_buffer[DAG_MEMO_BYTESIZE];
+ enum ethash_io_rc ret = ETHASH_IO_FAIL;
+
+ // assert directory exists, full owner permissions and read/search for others
+ int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ if (rc == -1 && errno != EEXIST) {
+ goto end;
+ }
+
+ char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
+ if (!memofile) {
+ goto end;
+ }
+
+ // 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;
+ }
+
+ if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
+ goto close;
+ }
+
+ 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;
+ }
+ ret = ETHASH_IO_MEMO_MISMATCH;
+ }
+
+ ret = ETHASH_IO_MEMO_MATCH;
+
+close:
+ fclose(f);
+free_memo:
+ free(memofile);
+end:
+ return ret;
+}
diff --git a/libethash/io_win32.c b/libethash/io_win32.c
new file mode 100644
index 000000000..77367fdd9
--- /dev/null
+++ b/libethash/io_win32.c
@@ -0,0 +1,73 @@
+/*
+ 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 .
+*/
+/** @file io_win32.c
+ * @author Lefteris Karapetsas
+ * @date 2015
+ */
+
+#include "io.h"
+#include
+#include
+#include
+
+enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
+{
+ char read_buffer[DAG_MEMO_BYTESIZE];
+ char expect_buffer[DAG_MEMO_BYTESIZE];
+ enum ethash_io_rc ret = ETHASH_IO_FAIL;
+
+ // assert directory exists
+ int rc = _mkdir(dirname);
+ if (rc == -1 && errno != EEXIST) {
+ goto end;
+ }
+
+ char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
+ if (!memofile) {
+ goto end;
+ }
+
+ // 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;
+ }
+
+ if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
+ goto close;
+ }
+
+ 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;
+ }
+ ret = ETHASH_IO_MEMO_MISMATCH;
+ }
+
+ ret = ETHASH_IO_MEMO_MATCH;
+
+close:
+ fclose(f);
+free_memo:
+ free(memofile);
+end:
+ return ret;
+}
diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp
index d26ac0260..d3ecbcc5f 100644
--- a/libethcore/Common.cpp
+++ b/libethcore/Common.cpp
@@ -34,7 +34,7 @@ namespace eth
{
const unsigned c_ethashVersion = c_ethashRevision;
-const unsigned c_protocolVersion = 58;
+const unsigned c_protocolVersion = 59;
const unsigned c_databaseBaseVersion = 8;
#if ETH_FATDB
const unsigned c_databaseVersionModifier = 1;
diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp
index 44a555fde..64db69187 100644
--- a/libethcore/Ethasher.cpp
+++ b/libethcore/Ethasher.cpp
@@ -41,7 +41,23 @@ using namespace eth;
Ethasher* dev::eth::Ethasher::s_this = nullptr;
-bytes const& Ethasher::cache(BlockInfo const& _header)
+Ethasher::~Ethasher()
+{
+ while (!m_caches.empty())
+ killCache(m_caches.begin()->first);
+}
+
+void Ethasher::killCache(h256 const& _s)
+{
+ RecursiveGuard l(x_this);
+ if (m_caches.count(_s))
+ {
+ ethash_delete_light(m_caches.at(_s));
+ m_caches.erase(_s);
+ }
+}
+
+void const* Ethasher::cache(BlockInfo const& _header)
{
RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048)
@@ -54,8 +70,7 @@ bytes const& Ethasher::cache(BlockInfo const& _header)
if (!m_caches.count(_header.seedHash()))
{
ethash_params p = params((unsigned)_header.number);
- m_caches[_header.seedHash()].resize(p.cache_size);
- ethash_prep_light(m_caches[_header.seedHash()].data(), &p, _header.seedHash().data());
+ m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data());
}
return m_caches[_header.seedHash()];
}
@@ -84,7 +99,7 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
ethash_params p = params((unsigned)_header.number);
m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size);
auto c = cache(_header);
- ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c.data());
+ ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c);
writeFile(memoFile, m_fulls[_header.seedHash()]);
writeFile(memoFile + ".info", info);
}
@@ -112,22 +127,42 @@ bool Ethasher::verify(BlockInfo const& _header)
h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
- // should be equivalent to:
- auto r = eval(_header);
- return r.mixHash == _header.mixHash && r.value <= boundary;
-
- return ethash_quick_check_difficulty(
+ bool quick = ethash_quick_check_difficulty(
_header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce,
_header.mixHash.data(),
boundary.data());
+
+#if !ETH_DEBUG
+ if (!quick)
+ return false;
+#endif
+
+ auto result = eval(_header);
+ bool slow = result.value <= boundary && result.mixHash == _header.mixHash;
+
+#if ETH_DEBUG
+ if (!quick && slow)
+ {
+ cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false.";
+ cwarn << "headerHash:" << _header.headerHash(WithoutNonce);
+ cwarn << "nonce:" << _header.nonce;
+ cwarn << "mixHash:" << _header.mixHash;
+ cwarn << "difficulty:" << _header.difficulty;
+ cwarn << "boundary:" << boundary;
+ cwarn << "result.value:" << result.value;
+ cwarn << "result.mixHash:" << result.mixHash;
+ }
+#endif
+
+ return slow;
}
Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{
auto p = Ethasher::params(_header);
ethash_return_value r;
- ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
+ ethash_compute_light(&r, Ethasher::get()->cache(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer);
return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
}
diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h
index c160d38da..a10c206d1 100644
--- a/libethcore/Ethasher.h
+++ b/libethcore/Ethasher.h
@@ -30,21 +30,8 @@
#include
#include
#include // TODO: REMOVE once everything merged into this class and an opaque API can be provided.
-static const unsigned c_ethashRevision = REVISION;
-static const unsigned c_ethashEpochLength = EPOCH_LENGTH;
-#undef REVISION
-#undef DATASET_BYTES_INIT
-#undef DATASET_BYTES_GROWTH
-#undef CACHE_BYTES_INIT
-#undef CACHE_BYTES_GROWTH
-#undef DAGSIZE_BYTES_INIT
-#undef DAG_GROWTH
-#undef EPOCH_LENGTH
-#undef MIX_BYTES
-#undef HASH_BYTES
-#undef DATASET_PARENTS
-#undef CACHE_ROUNDS
-#undef ACCESSES
+static const unsigned c_ethashRevision = ETHASH_REVISION;
+static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH;
#include "Common.h"
#include "BlockInfo.h"
@@ -57,10 +44,14 @@ class Ethasher
{
public:
Ethasher() {}
+ ~Ethasher();
static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; }
- bytes const& cache(BlockInfo const& _header);
+ using LightType = void const*;
+ using FullType = void const*;
+
+ LightType cache(BlockInfo const& _header);
bytesConstRef full(BlockInfo const& _header);
static ethash_params params(BlockInfo const& _header);
static ethash_params params(unsigned _n);
@@ -104,9 +95,11 @@ public:
};
private:
+ void killCache(h256 const& _s);
+
static Ethasher* s_this;
RecursiveMutex x_this;
- std::map m_caches;
+ std::map m_caches;
std::map m_fulls;
};
diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h
index df6a08817..db2718fc4 100644
--- a/libethcore/Exceptions.h
+++ b/libethcore/Exceptions.h
@@ -38,6 +38,7 @@ using errinfo_difficulty = boost::error_info;
using BadFieldError = boost::tuple;
struct DatabaseAlreadyOpen: virtual dev::Exception {};
+struct NotEnoughAvailableSpace: virtual dev::Exception {};
struct NotEnoughCash: virtual dev::Exception {};
struct GasPriceTooLow: virtual dev::Exception {};
struct BlockGasLimitReached: virtual dev::Exception {};
diff --git a/libethcore/Params.h b/libethcore/Params.h
index 7520e49f1..62cf6b2d8 100644
--- a/libethcore/Params.h
+++ b/libethcore/Params.h
@@ -29,15 +29,15 @@ namespace eth
{
//--- BEGIN: AUTOGENERATED FROM /feeStructure.json
-extern u256 const c_genesisDifficulty;
-extern u256 const c_maximumExtraDataSize;
-extern u256 const c_epochDuration;
extern u256 const c_genesisGasLimit;
extern u256 const c_minGasLimit;
extern u256 const c_gasLimitBoundDivisor;
+extern u256 const c_genesisDifficulty;
extern u256 const c_minimumDifficulty;
extern u256 const c_difficultyBoundDivisor;
extern u256 const c_durationLimit;
+extern u256 const c_maximumExtraDataSize;
+extern u256 const c_epochDuration;
extern u256 const c_stackLimit;
extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them.
diff --git a/libethereum/ABI.cpp b/libethereum/ABI.cpp
new file mode 100644
index 000000000..eada1c419
--- /dev/null
+++ b/libethereum/ABI.cpp
@@ -0,0 +1,27 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file ABI.cpp
+ * @author Gav Wood
+ * @date 2014
+ */
+
+#include "ABI.h"
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+
diff --git a/libethereum/ABI.h b/libethereum/ABI.h
new file mode 100644
index 000000000..04c623ac5
--- /dev/null
+++ b/libethereum/ABI.h
@@ -0,0 +1,64 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file ABI.h
+ * @author Gav Wood
+ * @date 2014
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace dev
+{
+namespace eth
+{
+
+template struct ABISerialiser {};
+template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } };
+template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } };
+template <> struct ABISerialiser { static bytes serialise(u160 const& _t) { return bytes(12, 0) + h160(_t).asBytes(); } };
+template <> struct ABISerialiser { static bytes serialise(string32 const& _t) { return bytesConstRef((byte const*)_t.data(), 32).toBytes(); } };
+
+inline bytes abiInAux() { return {}; }
+template bytes abiInAux(T const& _t, U const& ... _u)
+{
+ return ABISerialiser::serialise(_t) + abiInAux(_u ...);
+}
+
+template bytes abiIn(std::string _id, T const& ... _t)
+{
+ return sha3(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...);
+}
+
+template struct ABIDeserialiser {};
+template struct ABIDeserialiser> { static FixedHash deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } };
+template <> struct ABIDeserialiser { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } };
+template <> struct ABIDeserialiser { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } };
+template <> struct ABIDeserialiser { static string32 deserialise(bytesConstRef& io_t) { string32 ret; io_t.cropped(0, 32).populate(bytesRef((byte*)ret.data(), 32)); io_t = io_t.cropped(32); return ret; } };
+
+template T abiOut(bytes const& _data)
+{
+ bytesConstRef o(&_data);
+ return ABIDeserialiser::deserialise(o);
+}
+
+}
+}
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index f80680f38..8c0bd2b8b 100644
--- a/libethereum/BlockChain.cpp
+++ b/libethereum/BlockChain.cpp
@@ -63,7 +63,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc)
return _out;
}
-ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub)
+ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub)
{
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT
static thread_local h256 h = _h ^ sha3(h256(u256(_sub)));
@@ -131,10 +131,19 @@ void BlockChain::open(std::string _path, bool _killExisting)
o.create_if_missing = true;
ldb::DB::Open(o, _path + "/blocks", &m_blocksDB);
ldb::DB::Open(o, _path + "/details", &m_extrasDB);
- if (!m_blocksDB)
- BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen());
- if (!m_extrasDB)
- BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen());
+ if (!m_blocksDB || !m_extrasDB)
+ {
+ if (boost::filesystem::space(_path + "/blocks").available < 1024)
+ {
+ cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing.";
+ BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace());
+ }
+ else
+ {
+ cwarn << "Database already open. You appear to have another instance of ethereum running. Bailing.";
+ BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen());
+ }
+ }
if (!details(m_genesisHash))
{
@@ -184,6 +193,19 @@ inline string toString(h256s const& _bs)
return out.str();
}
+LastHashes BlockChain::lastHashes(unsigned _n) const
+{
+ Guard l(x_lastLastHashes);
+ if (m_lastLastHashesNumber != _n || m_lastLastHashes.empty())
+ {
+ m_lastLastHashes.resize(256);
+ for (unsigned i = 0; i < 256; ++i)
+ m_lastLastHashes[i] = _n >= i ? numberHash(_n - i) : h256();
+ m_lastLastHashesNumber = _n;
+ }
+ return m_lastLastHashes;
+}
+
h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max)
{
_bq.tick(*this);
@@ -317,6 +339,13 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
#endif
// All ok - insert into DB
{
+ // ensure parent is cached for later addition.
+ // TODO: this is a bit horrible would be better refactored into an enveloping UpgradableGuard
+ // together with an "ensureCachedWithUpdatableLock(l)" method.
+ // This is safe in practice since the caches don't get flushed nearly often enough to be
+ // done here.
+ details(bi.parentHash);
+
WriteGuard l(x_details);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash);
@@ -412,6 +441,9 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
WriteGuard l(x_lastBlockHash);
m_lastBlockHash = newHash;
}
+
+ noteCanonChanged();
+
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32));
clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret);
StructuredLogger::chainNewHead(
@@ -428,48 +460,52 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
return ret;
}
-h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, bool _post) const
+h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const
{
- // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
+ cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
if (!_from || !_to)
return h256s();
h256s ret;
h256s back;
unsigned fn = details(_from).number;
unsigned tn = details(_to).number;
- // cdebug << "treeRoute" << fn << "..." << tn;
+ cdebug << "treeRoute" << fn << "..." << tn;
+ h256 from = _from;
while (fn > tn)
{
if (_pre)
- ret.push_back(_from);
- _from = details(_from).parent;
+ ret.push_back(from);
+ from = details(from).parent;
fn--;
- // cdebug << "from:" << fn << _from.abridged();
+ cdebug << "from:" << fn << _from.abridged();
}
+ h256 to = _to;
while (fn < tn)
{
if (_post)
- back.push_back(_to);
- _to = details(_to).parent;
+ back.push_back(to);
+ to = details(to).parent;
tn--;
- // cdebug << "to:" << tn << _to.abridged();
+ cdebug << "to:" << tn << _to.abridged();
}
- while (_from != _to)
+ while (from != to)
{
- assert(_from);
- assert(_to);
- _from = details(_from).parent;
- _to = details(_to).parent;
+ if (!from)
+ assert(from);
+ if (!to)
+ assert(to);
+ from = details(from).parent;
+ to = details(to).parent;
if (_pre)
- ret.push_back(_from);
+ ret.push_back(from);
if (_post)
- back.push_back(_to);
+ back.push_back(to);
fn--;
tn--;
// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged();
}
if (o_common)
- *o_common = _from;
+ *o_common = from;
ret.reserve(ret.size() + back.size());
for (auto it = back.cbegin(); it != back.cend(); ++it)
ret.push_back(*it);
@@ -677,7 +713,7 @@ vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earlie
return ret;
}
-h256Set BlockChain::allUnclesFrom(h256 _parent) const
+h256Set BlockChain::allUnclesFrom(h256 const& _parent) const
{
// Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5).
h256Set ret;
@@ -692,7 +728,7 @@ h256Set BlockChain::allUnclesFrom(h256 _parent) const
return ret;
}
-bool BlockChain::isKnown(h256 _hash) const
+bool BlockChain::isKnown(h256 const& _hash) const
{
if (_hash == m_genesisHash)
return true;
@@ -706,7 +742,7 @@ bool BlockChain::isKnown(h256 _hash) const
return !!d.size();
}
-bytes BlockChain::block(h256 _hash) const
+bytes BlockChain::block(h256 const& _hash) const
{
if (_hash == m_genesisHash)
return m_genesisBlock;
diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h
index 9676b4a78..03c0fdcfd 100644
--- a/libethereum/BlockChain.h
+++ b/libethereum/BlockChain.h
@@ -30,9 +30,10 @@
#include
#include
#include
+#include
#include
#include
-#include
+#include
#include "BlockDetails.h"
#include "Account.h"
#include "Transaction.h"
@@ -61,10 +62,11 @@ struct BlockChainNote: public LogChannel { static const char* name() { return "=
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::map const& genesisState();
-ldb::Slice toSlice(h256 _h, unsigned _sub = 0);
+ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0);
using BlocksHash = std::map;
using TransactionHashes = h256s;
+using UncleHashes = h256s;
enum {
ExtraDetails = 0,
@@ -104,34 +106,42 @@ public:
h256s import(bytes const& _block, OverlayDB const& _stateDB);
/// Returns true if the given block is known (though not necessarily a part of the canon chain).
- bool isKnown(h256 _hash) const;
+ bool isKnown(h256 const& _hash) const;
/// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
- BlockInfo info(h256 _hash) const { return BlockInfo(block(_hash)); }
+ BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash)); }
BlockInfo info() const { return BlockInfo(block()); }
/// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe.
- bytes block(h256 _hash) const;
+ bytes block(h256 const& _hash) const;
bytes block() const { return block(currentHash()); }
/// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
- BlockDetails details(h256 _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); }
+ BlockDetails details(h256 const& _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); }
BlockDetails details() const { return details(currentHash()); }
/// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe.
- BlockLogBlooms logBlooms(h256 _hash) const { return queryExtras(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); }
+ BlockLogBlooms logBlooms(h256 const& _hash) const { return queryExtras(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); }
BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
/// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe.
- BlockReceipts receipts(h256 _hash) const { return queryExtras(_hash, m_receipts, x_receipts, NullBlockReceipts); }
+ BlockReceipts receipts(h256 const& _hash) const { return queryExtras(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); }
/// Get a list of transaction hashes for a given block. Thread-safe.
- TransactionHashes transactionHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; }
+ TransactionHashes transactionHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; }
TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); }
- /// Get a list of transaction hashes for a given block. Thread-safe.
- h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; }
+ /// Get a list of uncle hashes for a given block. Thread-safe.
+ UncleHashes uncleHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; }
+ UncleHashes uncleHashes() const { return uncleHashes(currentHash()); }
+
+ /// Get the hash for a given block's number.
+ h256 numberHash(unsigned _i) const { if (!_i) return genesisHash(); return queryExtras(h256(u256(_i)), m_blockHashes, x_blockHashes, NullBlockHash).value; }
+
+ /// Get the last N hashes for a given block. (N is determined by the LastHashes type.)
+ LastHashes lastHashes() const { return lastHashes(number()); }
+ LastHashes lastHashes(unsigned _i) const;
/** Get the block blooms for a number of blocks. Thread-safe.
* @returns the object pertaining to the blocks:
@@ -154,15 +164,15 @@ public:
std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const;
/// Get a transaction from its hash. Thread-safe.
- bytes transaction(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); }
- std::pair transactionLocation(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair(h256(), 0); return std::make_pair(ta.blockHash, ta.index); }
+ bytes transaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); }
+ std::pair transactionLocation(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair(h256(), 0); return std::make_pair(ta.blockHash, ta.index); }
/// Get a block's transaction (RLP format) for the given block hash (or the most recent mined if none given) & index. Thread-safe.
- bytes transaction(h256 _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); }
+ bytes transaction(h256 const& _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); }
bytes transaction(unsigned _i) const { return transaction(currentHash(), _i); }
/// Get a number for the given hash (or the most recent mined if none given). Thread-safe.
- unsigned number(h256 _hash) const { return details(_hash).number; }
+ unsigned number(h256 const& _hash) const { return details(_hash).number; }
unsigned number() const { return number(currentHash()); }
/// Get a given block (RLP format). Thread-safe.
@@ -174,7 +184,7 @@ public:
/// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5).
/// @returns set including the header-hash of every parent (including @a _parent) up to and including generation +5
/// togther with all their quoted uncles.
- h256Set allUnclesFrom(h256 _parent) const;
+ h256Set allUnclesFrom(h256 const& _parent) const;
/** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of
* blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent.
@@ -190,7 +200,7 @@ public:
* treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g
* @endcode
*/
- h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const;
+ h256s treeRoute(h256 const& _from, h256 const& _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const;
struct Statistics
{
@@ -215,7 +225,7 @@ private:
void open(std::string _path, bool _killExisting = false);
void close();
- template T queryExtras(h256 _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const
+ template T queryExtras(h256 const& _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const
{
{
ReadGuard l(_x);
@@ -264,6 +274,11 @@ private:
void noteUsed(h256 const& _h, unsigned _extra = (unsigned)-1) const;
std::chrono::system_clock::time_point m_lastCollection;
+ void noteCanonChanged() const { Guard l(x_lastLastHashes); m_lastLastHashes.clear(); }
+ mutable Mutex x_lastLastHashes;
+ mutable LastHashes m_lastLastHashes;
+ mutable unsigned m_lastLastHashesNumber = (unsigned)-1;
+
void updateStats() const;
mutable Statistics m_lastStats;
diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp
index 883b7f615..54f23dc84 100644
--- a/libethereum/Client.cpp
+++ b/libethereum/Client.cpp
@@ -186,11 +186,6 @@ void Client::doneWorking()
m_postMine = m_preMine;
}
-void Client::flushTransactions()
-{
- doWork();
-}
-
void Client::killChain()
{
bool wasMining = isMining();
@@ -249,117 +244,29 @@ void Client::clearPending()
noteChanged(changeds);
}
-unsigned Client::installWatch(h256 _h, Reaping _r)
-{
- unsigned ret;
- {
- Guard l(m_filterLock);
- ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
- m_watches[ret] = ClientWatch(_h, _r);
- cwatch << "+++" << ret << _h.abridged();
- }
- auto ch = logs(ret);
- if (ch.empty())
- ch.push_back(InitialChange);
- {
- Guard l(m_filterLock);
- swap(m_watches[ret].changes, ch);
- }
- return ret;
-}
-
-unsigned Client::installWatch(LogFilter const& _f, Reaping _r)
-{
- h256 h = _f.sha3();
- {
- Guard l(m_filterLock);
- if (!m_filters.count(h))
- {
- cwatch << "FFF" << _f << h.abridged();
- m_filters.insert(make_pair(h, _f));
- }
- }
- return installWatch(h, _r);
-}
-
-bool Client::uninstallWatch(unsigned _i)
-{
- cwatch << "XXX" << _i;
-
- Guard l(m_filterLock);
-
- auto it = m_watches.find(_i);
- if (it == m_watches.end())
- return false;
- auto id = it->second.id;
- m_watches.erase(it);
-
- auto fit = m_filters.find(id);
- if (fit != m_filters.end())
- if (!--fit->second.refCount)
- {
- cwatch << "*X*" << fit->first << ":" << fit->second.filter;
- m_filters.erase(fit);
- }
- return true;
-}
-
void Client::noteChanged(h256Set const& _filters)
{
- Guard l(m_filterLock);
+ Guard l(x_filtersWatches);
if (_filters.size())
cnote << "noteChanged(" << _filters << ")";
// accrue all changes left in each filter into the watches.
- for (auto& i: m_watches)
- if (_filters.count(i.second.id))
+ for (auto& w: m_watches)
+ if (_filters.count(w.second.id))
{
- cwatch << "!!!" << i.first << i.second.id;
- if (m_filters.count(i.second.id))
- i.second.changes += m_filters.at(i.second.id).changes;
- else
- i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
+ cwatch << "!!!" << w.first << w.second.id;
+ if (m_filters.count(w.second.id)) // Normal filtering watch
+ w.second.changes += m_filters.at(w.second.id).changes;
+ else // Special ('pending'/'latest') watch
+ w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
}
// clear the filters now.
for (auto& i: m_filters)
i.second.changes.clear();
}
-LocalisedLogEntries Client::peekWatch(unsigned _watchId) const
-{
- Guard l(m_filterLock);
-
-#if ETH_DEBUG
- cdebug << "peekWatch" << _watchId;
-#endif
- auto& w = m_watches.at(_watchId);
-#if ETH_DEBUG
- cdebug << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count();
-#endif
- w.lastPoll = chrono::system_clock::now();
- return w.changes;
-}
-
-LocalisedLogEntries Client::checkWatch(unsigned _watchId)
-{
- Guard l(m_filterLock);
- LocalisedLogEntries ret;
-
-#if ETH_DEBUG && 0
- cdebug << "checkWatch" << _watchId;
-#endif
- auto& w = m_watches.at(_watchId);
-#if ETH_DEBUG && 0
- cdebug << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count();
-#endif
- std::swap(ret, w.changes);
- w.lastPoll = chrono::system_clock::now();
-
- return ret;
-}
-
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash)
{
- Guard l(m_filterLock);
+ Guard l(x_filtersWatches);
for (pair& i: m_filters)
if (i.second.filter.envelops(RelativeBlock::Pending, m_bc.number() + 1))
{
@@ -381,7 +288,7 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed)
auto d = m_bc.info(_block);
auto br = m_bc.receipts(_block);
- Guard l(m_filterLock);
+ Guard l(x_filtersWatches);
for (pair& i: m_filters)
if (i.second.filter.envelops(RelativeBlock::Latest, d.number) && i.second.filter.matches(d.logBloom))
// acceptable number & looks like block may contain a matching log entry.
@@ -475,68 +382,6 @@ void Client::setupState(State& _s)
_s.commitToMine(m_bc);
}
-void Client::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
-{
- startWorking();
-
- u256 n;
- {
- ReadGuard l(x_stateDB);
- n = m_postMine.transactionsFrom(toAddress(_secret));
- }
- Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
-// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
- StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
- cnote << "New transaction " << t;
- m_tq.attemptImport(t.rlp());
-}
-
-ExecutionResult Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
-{
- ExecutionResult ret;
- try
- {
- u256 n;
- State temp;
- // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
- {
- ReadGuard l(x_stateDB);
- temp = asOf(_blockNumber);
- n = temp.transactionsFrom(toAddress(_secret));
- }
- Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
- ret = temp.execute(m_bc, t.rlp(), Permanence::Reverted);
- }
- catch (...)
- {
- // TODO: Some sort of notification of failure.
- }
- return ret;
-}
-
-ExecutionResult Client::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
-{
- ExecutionResult ret;
- try
- {
- u256 n;
- State temp;
- // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
- {
- ReadGuard l(x_stateDB);
- temp = asOf(_blockNumber);
- n = temp.transactionsFrom(toAddress(_secret));
- }
- Transaction t(_value, _gasPrice, _gas, _data, n, _secret);
- ret = temp.execute(m_bc, t.rlp(), Permanence::Reverted);
- }
- catch (...)
- {
- // TODO: Some sort of notification of failure.
- }
- return ret;
-}
-
ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice)
{
ExecutionResult ret;
@@ -547,6 +392,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
{
ReadGuard l(x_stateDB);
temp = m_postMine;
+ temp.addBalance(Address(), _value + _gasPrice * _gas);
}
Executive e(temp, LastHashes(), 0);
if (!e.call(_dest, _dest, Address(), _value, _gasPrice, &_data, _gas, Address()))
@@ -560,28 +406,6 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
return ret;
}
-Address Client::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
-{
- startWorking();
-
- u256 n;
- {
- ReadGuard l(x_stateDB);
- n = m_postMine.transactionsFrom(toAddress(_secret));
- }
- Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
- cnote << "New transaction " << t;
- m_tq.attemptImport(t.rlp());
- return right160(sha3(rlpList(t.sender(), t.nonce())));
-}
-
-void Client::inject(bytesConstRef _rlp)
-{
- startWorking();
-
- m_tq.attemptImport(_rlp);
-}
-
pair Client::getWork()
{
Guard l(x_remoteMiner);
@@ -711,7 +535,7 @@ void Client::doWork()
// watches garbage collection
vector toUninstall;
{
- Guard l(m_filterLock);
+ Guard l(x_filtersWatches);
for (auto key: keysOf(m_watches))
if (m_watches[key].lastPoll != chrono::system_clock::time_point::max() && chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20))
{
@@ -729,25 +553,15 @@ void Client::doWork()
}
}
-unsigned Client::numberOf(int _n) const
+State Client::asOf(h256 const& _block) const
{
- if (_n > 0)
- return _n;
- else if (_n == GenesisBlock)
- return 0;
- else
- return m_bc.details().number + max(-(int)m_bc.details().number, 1 + _n);
+ ReadGuard l(x_stateDB);
+ return State(m_stateDB, bc(), _block);
}
-State Client::asOf(unsigned _h) const
+void Client::prepareForTransaction()
{
- ReadGuard l(x_stateDB);
- if (_h == PendingBlock)
- return m_postMine;
- else if (_h == LatestBlock)
- return m_preMine;
- else
- return State(m_stateDB, m_bc, m_bc.numberHash(numberOf(_h)));
+ startWorking();
}
State Client::state(unsigned _txi, h256 _block) const
@@ -768,183 +582,14 @@ eth::State Client::state(unsigned _txi) const
return m_postMine.fromPending(_txi);
}
-StateDiff Client::diff(unsigned _txi, BlockNumber _block) const
-{
- State st = asOf(_block);
- return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
-}
-
-StateDiff Client::diff(unsigned _txi, h256 _block) const
-{
- State st = state(_block);
- return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
-}
-
-std::vector Client::addresses(BlockNumber _block) const
-{
- vector ret;
- for (auto const& i: asOf(_block).addresses())
- ret.push_back(i.first);
- return ret;
-}
-
-u256 Client::balanceAt(Address _a, BlockNumber _block) const
-{
- return asOf(_block).balance(_a);
-}
-
-std::map Client::storageAt(Address _a, BlockNumber _block) const
-{
- return asOf(_block).storage(_a);
-}
-
-u256 Client::countAt(Address _a, BlockNumber _block) const
-{
- return asOf(_block).transactionsFrom(_a);
-}
-
-u256 Client::stateAt(Address _a, u256 _l, BlockNumber _block) const
-{
- return asOf(_block).storage(_a, _l);
-}
-
-bytes Client::codeAt(Address _a, BlockNumber _block) const
-{
- return asOf(_block).code(_a);
-}
-
-Transaction Client::transaction(h256 _transactionHash) const
-{
- return Transaction(m_bc.transaction(_transactionHash), CheckSignature::Range);
-}
-
-Transaction Client::transaction(h256 _blockHash, unsigned _i) const
-{
- auto bl = m_bc.block(_blockHash);
- RLP b(bl);
- if (_i < b[1].itemCount())
- return Transaction(b[1][_i].data(), CheckSignature::Range);
- else
- return Transaction();
-}
-
-BlockInfo Client::uncle(h256 _blockHash, unsigned _i) const
-{
- auto bl = m_bc.block(_blockHash);
- RLP b(bl);
- if (_i < b[2].itemCount())
- return BlockInfo::fromHeader(b[2][_i].data());
- else
- return BlockInfo();
-}
-
-unsigned Client::transactionCount(h256 _blockHash) const
-{
- auto bl = m_bc.block(_blockHash);
- RLP b(bl);
- return b[1].itemCount();
-}
-
-unsigned Client::uncleCount(h256 _blockHash) const
-{
- auto bl = m_bc.block(_blockHash);
- RLP b(bl);
- return b[2].itemCount();
-}
-
-Transactions Client::transactions(h256 _blockHash) const
-{
- auto bl = m_bc.block(_blockHash);
- RLP b(bl);
- Transactions res;
- for (unsigned i = 0; i < b[1].itemCount(); i++)
- res.emplace_back(b[1][i].data(), CheckSignature::Range);
- return res;
-}
-
-TransactionHashes Client::transactionHashes(h256 _blockHash) const
-{
- return m_bc.transactionHashes(_blockHash);
-}
-
-LocalisedLogEntries Client::logs(unsigned _watchId) const
+void Client::inject(bytesConstRef _rlp)
{
- LogFilter f;
- try
- {
- Guard l(m_filterLock);
- f = m_filters.at(m_watches.at(_watchId).id).filter;
- }
- catch (...)
- {
- return LocalisedLogEntries();
- }
- return logs(f);
+ startWorking();
+
+ m_tq.attemptImport(_rlp);
}
-LocalisedLogEntries Client::logs(LogFilter const& _f) const
+void Client::flushTransactions()
{
- LocalisedLogEntries ret;
- unsigned begin = min(m_bc.number() + 1, (unsigned)_f.latest());
- unsigned end = min(m_bc.number(), min(begin, (unsigned)_f.earliest()));
-
- // Handle pending transactions differently as they're not on the block chain.
- if (begin > m_bc.number())
- {
- ReadGuard l(x_stateDB);
- for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
- {
- // Might have a transaction that contains a matching log.
- TransactionReceipt const& tr = m_postMine.receipt(i);
- auto sha3 = m_postMine.pending()[i].sha3();
- LogEntries le = _f.matches(tr);
- if (le.size())
- for (unsigned j = 0; j < le.size(); ++j)
- ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3));
- }
- begin = m_bc.number();
- }
-
- set matchingBlocks;
- for (auto const& i: _f.bloomPossibilities())
- for (auto u: m_bc.withBlockBloom(i, end, begin))
- matchingBlocks.insert(u);
-
-#if ETH_DEBUG
- unsigned falsePos = 0;
-#endif
- for (auto n: matchingBlocks)
- {
-#if ETH_DEBUG
- int total = 0;
-#endif
- auto h = m_bc.numberHash(n);
- auto receipts = m_bc.receipts(h).receipts;
- for (size_t i = 0; i < receipts.size(); i++)
- {
- TransactionReceipt receipt = receipts[i];
- if (_f.matches(receipt.bloom()))
- {
- auto info = m_bc.info(h);
- auto h = transaction(info.hash, i).sha3();
- LogEntries le = _f.matches(receipt);
- if (le.size())
- {
-#if ETH_DEBUG
- total += le.size();
-#endif
- for (unsigned j = 0; j < le.size(); ++j)
- ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h));
- }
- }
-#if ETH_DEBUG
- if (!total)
- falsePos++;
-#endif
- }
- }
-#if ETH_DEBUG
- cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves";
-#endif
- return ret;
+ doWork();
}
diff --git a/libethereum/Client.h b/libethereum/Client.h
index f34eea5bf..1091bba58 100644
--- a/libethereum/Client.h
+++ b/libethereum/Client.h
@@ -40,9 +40,9 @@
#include "TransactionQueue.h"
#include "State.h"
#include "CommonNet.h"
-#include "LogFilter.h"
#include "Miner.h"
-#include "Interface.h"
+#include "ABI.h"
+#include "ClientBase.h"
namespace dev
{
@@ -72,71 +72,6 @@ private:
std::string m_path;
};
-static const int GenesisBlock = INT_MIN;
-
-struct InstalledFilter
-{
- InstalledFilter(LogFilter const& _f): filter(_f) {}
-
- LogFilter filter;
- unsigned refCount = 1;
- LocalisedLogEntries changes;
-};
-
-static const h256 PendingChangedFilter = u256(0);
-static const h256 ChainChangedFilter = u256(1);
-
-static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes());
-static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0);
-
-struct ClientWatch
-{
- ClientWatch(): lastPoll(std::chrono::system_clock::now()) {}
- explicit ClientWatch(h256 _id, Reaping _r): id(_id), lastPoll(_r == Reaping::Automatic ? std::chrono::system_clock::now() : std::chrono::system_clock::time_point::max()) {}
-
- h256 id;
- LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange };
- mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now();
-};
-
-struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; };
-#define cwatch dev::LogOutputStream()
-struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; };
-struct WorkOutChannel: public LogChannel { static const char* name() { return "()
-#define cworkin dev::LogOutputStream