Browse Source

Merge branch 'remove-libraries'

cl-refactor
Genoil 9 years ago
parent
commit
8e55be3ca1
  1. 92
      CMakeLists.txt
  2. 13
      cmake/EthDependencies.cmake
  3. 49
      cmake/FindRocksDB.cmake
  4. 52
      eth/CMakeLists.txt
  5. 39
      eth/Farm.h
  6. 28
      eth/PhoneHome.h
  7. 4
      eth/farm.json
  8. 1136
      eth/main.cpp
  9. 3
      eth/phonehome.json
  10. 2
      ethminer/CMakeLists.txt
  11. 3
      ethminer/MinerAux.h
  12. 35
      ethrpctest/CMakeLists.txt
  13. 130
      ethrpctest/CommandLineInterface.cpp
  14. 46
      ethrpctest/CommandLineInterface.h
  15. 34
      ethrpctest/main.cpp
  16. 1
      json_spirit/JsonSpiritHeaders.h
  17. 5
      libdevcore/TrieDB.h
  18. 6
      libdevcore/db.h
  19. 57
      libdevcrypto/AES.cpp
  20. 34
      libdevcrypto/AES.h
  21. 28
      libdevcrypto/CMakeLists.txt
  22. 322
      libdevcrypto/Common.cpp
  23. 218
      libdevcrypto/Common.h
  24. 348
      libdevcrypto/CryptoPP.cpp
  25. 141
      libdevcrypto/CryptoPP.h
  26. 46
      libdevcrypto/ECDHE.cpp
  27. 81
      libdevcrypto/ECDHE.h
  28. 35
      libdevcrypto/Exceptions.h
  29. 157
      libdevcrypto/OverlayDB.cpp
  30. 59
      libdevcrypto/OverlayDB.h
  31. 375
      libdevcrypto/SecretStore.cpp
  32. 125
      libdevcrypto/SecretStore.h
  33. 5036
      libdevcrypto/WordList.cpp
  34. 31
      libdevcrypto/WordList.h
  35. 27
      libethcore/ABI.cpp
  36. 100
      libethcore/ABI.h
  37. 117
      libethcore/BasicAuthority.cpp
  38. 95
      libethcore/BasicAuthority.h
  39. 2
      libethcore/CMakeLists.txt
  40. 13
      libethcore/Common.cpp
  41. 6
      libethcore/Common.h
  42. 78
      libethcore/CommonJS.cpp
  43. 61
      libethcore/CommonJS.h
  44. 1
      libethcore/Ethash.cpp
  45. 1
      libethcore/EthashAux.cpp
  46. 167
      libethcore/ICAP.cpp
  47. 106
      libethcore/ICAP.h
  48. 409
      libethcore/KeyManager.cpp
  49. 186
      libethcore/KeyManager.h
  50. 132
      libethcore/Transaction.cpp
  51. 179
      libethcore/Transaction.h
  52. 95
      libethereum/Account.cpp
  53. 248
      libethereum/Account.h
  54. 92
      libethereum/AccountDiff.cpp
  55. 85
      libethereum/AccountDiff.h
  56. 15
      libethereum/All.h
  57. 100
      libethereum/BasicGasPricer.cpp
  58. 53
      libethereum/BasicGasPricer.h
  59. 815
      libethereum/Block.cpp
  60. 310
      libethereum/Block.h
  61. 1284
      libethereum/BlockChain.cpp
  62. 516
      libethereum/BlockChain.h
  63. 1224
      libethereum/BlockChainSync.cpp
  64. 330
      libethereum/BlockChainSync.h
  65. 43
      libethereum/BlockDetails.cpp
  66. 128
      libethereum/BlockDetails.h
  67. 564
      libethereum/BlockQueue.cpp
  68. 176
      libethereum/BlockQueue.h
  69. 43
      libethereum/CMakeLists.txt
  70. 102
      libethereum/CachedAddressState.cpp
  71. 63
      libethereum/CachedAddressState.h
  72. 156
      libethereum/CanonBlockChain.cpp
  73. 124
      libethereum/CanonBlockChain.h
  74. 954
      libethereum/Client.cpp
  75. 425
      libethereum/Client.h
  76. 541
      libethereum/ClientBase.cpp
  77. 200
      libethereum/ClientBase.h
  78. 28
      libethereum/CommonNet.cpp
  79. 103
      libethereum/CommonNet.h
  80. 34
      libethereum/Defaults.cpp
  81. 50
      libethereum/Defaults.h
  82. 91
      libethereum/DownloadMan.cpp
  83. 179
      libethereum/DownloadMan.h
  84. 400
      libethereum/EthereumHost.cpp
  85. 145
      libethereum/EthereumHost.h
  86. 390
      libethereum/EthereumPeer.cpp
  87. 165
      libethereum/EthereumPeer.h
  88. 447
      libethereum/Executive.cpp
  89. 196
      libethereum/Executive.h
  90. 125
      libethereum/ExtVM.cpp
  91. 100
      libethereum/ExtVM.h
  92. 26
      libethereum/GasPricer.cpp
  93. 76
      libethereum/GasPricer.h
  94. 27012
      libethereum/GenesisInfo.cpp
  95. 33
      libethereum/GenesisInfo.h
  96. 50
      libethereum/Interface.cpp
  97. 272
      libethereum/Interface.h
  98. 166
      libethereum/LogFilter.cpp
  99. 88
      libethereum/LogFilter.h
  100. 99
      libethereum/Precompiled.cpp

92
CMakeLists.txt

@ -37,80 +37,46 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
#defaults:
set(D_CMAKE_BUILD_TYPE "Release")
set(D_GUI ON)
set(D_TOOLS ON)
set(D_TESTS ON)
set(D_FATDB ON)
set(D_ETHASHCL ON)
set(D_ETHASHCUDA OFF)
set(D_JSONRPC ON)
set(D_VMTRACE OFF)
set(D_PARANOID OFF)
set(D_PROFILING OFF)
set(D_ROCKSDB OFF)
set(D_OLYMPIC OFF)
set(D_MINER ON)
set(D_ETHSTRATUM OFF)
if (BUNDLE STREQUAL "minimal")
set(D_GUI OFF)
set(D_TOOLS ON)
set(D_TESTS OFF)
elseif (BUNDLE STREQUAL "full")
set(D_GUI ON)
set(D_TOOLS ON)
set(D_TESTS ON)
set(D_FATDB ON)
elseif (BUNDLE STREQUAL "cli")
set(D_GUI OFF)
set(D_TOOLS ON)
set(D_TESTS ON)
set(D_FATDB ON)
elseif (BUNDLE STREQUAL "core")
set(D_GUI ON)
set(D_TOOLS ON)
set(D_TESTS OFF)
set(D_FATDB ON)
elseif (BUNDLE STREQUAL "tests")
set(D_GUI OFF)
set(D_TOOLS OFF)
set(D_TESTS ON)
set(D_FATDB ON)
elseif (BUNDLE STREQUAL "user")
set(D_GUI ON)
set(D_TOOLS ON)
set(D_TESTS OFF)
elseif (BUNDLE STREQUAL "wallet")
set(D_GUI OFF)
set(D_TOOLS OFF)
set(D_TESTS OFF)
set(D_MINER OFF)
set(D_ETHASHCL OFF)
set(D_FATDB OFF)
set(D_JSONRPC OFF)
elseif (BUNDLE STREQUAL "miner")
set(D_GUI OFF)
set(D_TOOLS OFF)
set(D_TESTS OFF)
set(D_MINER ON)
set(D_ETHASHCL ON)
set(D_FATDB OFF)
set(D_JSONRPC ON)
set(D_ETHSTRATUM ON)
elseif (BUNDLE STREQUAL "cudaminer")
set(D_GUI OFF)
set(D_TOOLS OFF)
set(D_TESTS OFF)
set(D_MINER ON)
set(D_ETHASHCL ON)
set(D_ETHASHCUDA ON)
set(D_FATDB OFF)
set(D_JSONRPC ON)
set(D_ETHSTRATUM ON)
elseif (BUNDLE STREQUAL "release") # release builds
set(D_GUI ON)
set(D_TOOLS ON)
set(D_TESTS OFF)
set(D_FATDB OFF)
set(D_ETHASHCL ON)
set(D_JSONRPC ON)
set(D_CMAKE_BUILD_TYPE "Release")
@ -254,15 +220,10 @@ endmacro()
# Normalise build options
eth_format_option(PARANOID)
eth_format_option(VMTRACE)
eth_format_option(FATDB)
eth_format_option(JSONRPC)
eth_format_option(MINER)
eth_format_option(PROFILING)
eth_format_option(ROCKSDB)
eth_format_option(GUI)
eth_format_option(TESTS)
eth_format_option(ROCKSDB)
eth_format_option(TOOLS)
eth_format_option(ETHASHCL)
eth_format_option(ETHASHCUDA)
eth_format_option(OLYMPIC)
@ -299,13 +260,10 @@ message("-- VMTRACE VM execution tracing ${VMTRACE}
message("-- PROFILING Profiling support ${PROFILING}")
message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- ROCKSDB Prefer rocksdb to leveldb ${ROCKSDB}")
message("-- OLYMPIC Default to the Olympic network ${OLYMPIC}")
message("------------------------------------------------------------- components")
message("-- MINER Build miner ${MINER}")
message("-- TOOLS Build basic tools ${TOOLS}")
message("-- GUI Build GUI components ${GUI}")
message("-- TESTS Build tests ${TESTS}")
message("-- ETHASHCL Build OpenCL components ${ETHASHCL}")
message("-- ETHASHCUDA Build CUDA components ${ETHASHCUDA}")
message("-- ETHSTRATUM Build Stratum components (experimental) ${ETHSTRATUM}")
@ -323,46 +281,12 @@ include(EthExecutableHelper)
message("creating build info...")
createBuildInfo()
if (ROCKSDB AND ROCKSDB_FOUND)
set(DB_INCLUDE_DIRS ${ROCKSDB_INCLUDE_DIRS})
set(DB_LIBRARIES ${ROCKSDB_LIBRARIES})
add_definitions(-DETH_ROCKSDB)
else()
set(DB_INCLUDE_DIRS ${LEVELDB_INCLUDE_DIRS})
set(DB_LIBRARIES ${LEVELDB_LIBRARIES})
endif()
if (TOOLS OR GUI OR TESTS)
set(GENERAL 1)
else ()
set(GENERAL 0)
endif ()
set(DB_INCLUDE_DIRS ${LEVELDB_INCLUDE_DIRS})
set(DB_LIBRARIES ${LEVELDB_LIBRARIES})
add_subdirectory(libdevcore)
if (GENERAL)
add_subdirectory(libevmcore)
add_subdirectory(libevmasm)
add_subdirectory(liblll)
endif ()
if (TOOLS)
add_subdirectory(lllc)
endif ()
if (NOT WIN32)
add_definitions(-DETH_HAVE_SECP256K1)
add_subdirectory(secp256k1)
endif ()
add_subdirectory(libscrypt)
add_subdirectory(libdevcrypto)
if (GENERAL)
add_subdirectory(libp2p)
add_subdirectory(libwhisper)
endif ()
if (GENERAL OR MINER)
if (MINER)
add_subdirectory(libethash)
if (ETHASHCL)
add_subdirectory(libethash-cl)
@ -376,12 +300,4 @@ if (GENERAL OR MINER)
endif ()
add_subdirectory(libethcore)
if (GENERAL)
add_subdirectory(libevm)
add_subdirectory(libethereum)
endif ()
if (MINER OR TOOLS)
add_subdirectory(ethminer)
endif ()
add_subdirectory(ethminer)

13
cmake/EthDependencies.cmake

@ -49,19 +49,6 @@ find_package (LevelDB REQUIRED)
message(" - LevelDB header: ${LEVELDB_INCLUDE_DIRS}")
message(" - LevelDB lib: ${LEVELDB_LIBRARIES}")
find_package (RocksDB)
if (ROCKSDB_FOUND)
message(" - RocksDB header: ${ROCKSDB_INCLUDE_DIRS}")
message(" - RocksDB lib: ${ROCKSDB_LIBRARIES}")
endif()
if (JSCONSOLE)
find_package (v8 REQUIRED)
message(" - v8 header: ${V8_INCLUDE_DIRS}")
message(" - v8 lib : ${V8_LIBRARIES}")
add_definitions(-DETH_JSCONSOLE)
endif()
# TODO the Jsoncpp package does not yet check for correct version number
find_package (Jsoncpp 0.60 REQUIRED)
message(" - Jsoncpp header: ${JSONCPP_INCLUDE_DIRS}")

49
cmake/FindRocksDB.cmake

@ -1,49 +0,0 @@
# Find rocksdb
#
# Find the rocksdb includes and library
#
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
#
# This module defines
# ROCKSDB_INCLUDE_DIRS, where to find header, etc.
# ROCKSDB_LIBRARIES, the libraries needed to use rocksdb.
# ROCKSDB_FOUND, If false, do not try to use rocksdb.
# only look in default directories
find_path(
ROCKSDB_INCLUDE_DIR
NAMES rocksdb/db.h
DOC "rocksdb include dir"
)
find_library(
ROCKSDB_LIBRARY
NAMES rocksdb
DOC "rocksdb library"
)
set(ROCKSDB_INCLUDE_DIRS ${ROCKSDB_INCLUDE_DIR})
set(ROCKSDB_LIBRARIES ${ROCKSDB_LIBRARY})
# debug library on windows
# same naming convention as in qt (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library(
ROCKSDB_LIBRARY_DEBUG
NAMES rocksdbd
DOC "rocksdb debug library"
)
set(ROCKSDB_LIBRARIES optimized ${ROCKSDB_LIBRARIES} debug ${ROCKSDB_LIBRARY_DEBUG})
endif()
# handle the QUIETLY and REQUIRED arguments and set ROCKSDB_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(rocksdb DEFAULT_MSG
ROCKSDB_INCLUDE_DIR ROCKSDB_LIBRARY)
mark_as_advanced (ROCKSDB_INCLUDE_DIR ROCKSDB_LIBRARY)

52
eth/CMakeLists.txt

@ -1,52 +0,0 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
if (JSCONSOLE)
include_directories(${V8_INCLUDE_DIRS})
endif()
set(EXECUTABLE eth)
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()
if (JSONRPC)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls("${EXECUTABLE}" CURL_DLLS)
endif()
endif()
target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} ethash)
if (JSCONSOLE)
target_link_libraries(${EXECUTABLE} jsconsole)
endif()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS EVMJIT_DLLS OpenCL_DLLS)
endif()
if (APPLE)
install(TARGETS ${EXECUTABLE} DESTINATION bin)
else()
eth_install_executable(${EXECUTABLE})
endif()

39
eth/Farm.h

@ -1,39 +0,0 @@
/**
* This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!
*/
#ifndef JSONRPC_CPP_STUB_FARM_H_
#define JSONRPC_CPP_STUB_FARM_H_
#include <jsonrpccpp/client.h>
class Farm : public jsonrpc::Client
{
public:
Farm(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type = jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {}
Json::Value eth_getWork() throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p = Json::nullValue;
Json::Value result = this->CallMethod("eth_getWork",p);
if (result.isArray())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
p.append(param3);
Json::Value result = this->CallMethod("eth_submitWork",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
};
#endif //JSONRPC_CPP_STUB_FARM_H_

28
eth/PhoneHome.h

@ -1,28 +0,0 @@
/**
* This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!
*/
#ifndef JSONRPC_CPP_STUB_PHONEHOME_H_
#define JSONRPC_CPP_STUB_PHONEHOME_H_
#include <jsonrpccpp/client.h>
class PhoneHome : public jsonrpc::Client
{
public:
PhoneHome(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type = jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {}
int report_benchmark(const std::string& param1, int param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("report_benchmark",p);
if (result.isInt())
return result.asInt();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
};
#endif //JSONRPC_CPP_STUB_PHONEHOME_H_

4
eth/farm.json

@ -1,4 +0,0 @@
[
{ "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true}
]

1136
eth/main.cpp

File diff suppressed because it is too large

3
eth/phonehome.json

@ -1,3 +0,0 @@
[
{ "name": "report_benchmark", "params": [ "", 0 ], "order": [], "returns": 0 }
]

2
ethminer/CMakeLists.txt

@ -35,7 +35,7 @@ endif()
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ethash)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} devcore)
if (ETHSTRATUM)
target_link_libraries(${EXECUTABLE} ethstratum)

3
ethminer/MinerAux.h

@ -34,7 +34,6 @@
#include <boost/optional.hpp>
#include <libdevcore/FileSystem.h>
#include <libevmcore/Instruction.h>
#include <libdevcore/StructuredLogger.h>
#include <libethcore/Exceptions.h>
#include <libdevcore/SHA3.h>
@ -51,7 +50,6 @@
#include <libethash-cuda/ethash_cuda_miner.h>
#endif
#if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
#endif
@ -67,7 +65,6 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace boost::algorithm;
using dev::eth::Instruction;
#undef RETURN

35
ethrpctest/CMakeLists.txt

@ -1,35 +0,0 @@
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)
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS)
endif()
install( TARGETS ${EXECUTABLE} DESTINATION bin )

130
ethrpctest/CommandLineInterface.cpp

@ -1,130 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CommandLineInterface.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
#include <string>
#include <iostream>
#include <fstream>
#include <csignal>
#include <thread>
#include <boost/filesystem.hpp>
#include <jsonrpccpp/server/connectors/httpserver.h>
#include <libtestutils/Common.h>
#include <libtestutils/BlockChainLoader.h>
#include <libtestutils/FixedClient.h>
#include <libtestutils/FixedWebThreeServer.h>
#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<vector<string>>()->required(), "input file")
("test", po::value<vector<string>>()->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<vector<string>>()[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<vector<string>>()[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);
cerr << "void CommandLineInterface::actOnInput() FixedClient now accepts eth::Block!!!" << endl;
FixedClient client(bcl.bc(), eth::Block{}/*bcl.state()*/);
unique_ptr<FixedWebThreeServer> 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));
}

46
ethrpctest/CommandLineInterface.h

@ -1,46 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** CommandLineInterface.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
#pragma once
#include <json/json.h>
#include <boost/program_options.hpp>
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;
};

34
ethrpctest/main.cpp

@ -1,34 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** main.cpp
* @author Marek Kotewicz <c@ethdev.com>
* @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;
}

1
test/JsonSpiritHeaders.h → json_spirit/JsonSpiritHeaders.h

@ -29,4 +29,3 @@
#include "../json_spirit/json_spirit_writer_template.h"
#pragma GCC diagnostic pop
#pragma warning(pop)

5
libdevcore/TrieDB.h

@ -477,12 +477,7 @@ public:
};
template <class KeyType, class DB> using TrieDB = SpecificTrieDB<GenericTrieDB<DB>, KeyType>;
#if ETH_FATDB
template <class KeyType, class DB> using SecureTrieDB = SpecificTrieDB<FatGenericTrieDB<DB>, KeyType>;
#else
template <class KeyType, class DB> using SecureTrieDB = SpecificTrieDB<HashedGenericTrieDB<DB>, KeyType>;
#endif
}

6
libdevcore/db.h

@ -23,14 +23,8 @@
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#if ETH_ROCKSDB || !ETH_TRUE
#include <rocksdb/db.h>
#include <rocksdb/write_batch.h>
namespace ldb = rocksdb;
#else
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
namespace ldb = leveldb;
#endif
#pragma warning(pop)
#define DEV_LDB 1

57
libdevcrypto/AES.cpp

@ -1,57 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AES.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
#include "AES.h"
#include <libdevcore/Common.h>
#include "CryptoPP.h"
using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
bytes dev::aesDecrypt(bytesConstRef _ivCipher, std::string const& _password, unsigned _rounds, bytesConstRef _salt)
{
bytes pw = asBytes(_password);
if (!_salt.size())
_salt = &pw;
bytes target(64);
CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA256>().DeriveKey(target.data(), target.size(), 0, pw.data(), pw.size(), _salt.data(), _salt.size(), _rounds);
try
{
CryptoPP::AES::Decryption aesDecryption(target.data(), 16);
auto cipher = _ivCipher.cropped(16);
auto iv = _ivCipher.cropped(0, 16);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data());
std::string decrypted;
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(decrypted));
stfDecryptor.Put(cipher.data(), cipher.size());
stfDecryptor.MessageEnd();
return asBytes(decrypted);
}
catch (exception const& e)
{
cerr << e.what() << endl;
return bytes();
}
}

34
libdevcrypto/AES.h

@ -1,34 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AES.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* AES
* todo: use openssl
*/
#pragma once
#include "Common.h"
namespace dev
{
bytes aesDecrypt(bytesConstRef _cipher, std::string const& _password, unsigned _rounds = 2000, bytesConstRef _salt = bytesConstRef());
}

28
libdevcrypto/CMakeLists.txt

@ -1,28 +0,0 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE devcrypto)
file(GLOB HEADERS "*.h")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${DB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} scrypt)
target_link_libraries(${EXECUTABLE} devcore)
if (NOT WIN32)
add_definitions(-DETH_HAVE_SECP256K1)
target_link_libraries(${EXECUTABLE} secp256k1)
endif ()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

322
libdevcrypto/Common.cpp

@ -1,322 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Common.cpp
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Common.h"
#include <cstdint>
#include <chrono>
#include <thread>
#include <mutex>
#include <libscrypt/libscrypt.h>
#include <libdevcore/Guards.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/RLP.h>
#if ETH_HAVE_SECP256K1
#include <secp256k1/include/secp256k1.h>
#endif
#include "AES.h"
#include "CryptoPP.h"
#include "Exceptions.h"
using namespace std;
using namespace dev;
using namespace dev::crypto;
#ifdef ETH_HAVE_SECP256K1
struct Secp256k1Context
{
Secp256k1Context() { ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); }
~Secp256k1Context() { secp256k1_context_destroy(ctx); }
secp256k1_context_t* ctx;
operator secp256k1_context_t const*() const { return ctx; }
};
static Secp256k1Context s_secp256k1;
#endif
static Secp256k1PP s_secp256k1pp;
bool dev::SignatureStruct::isValid() const noexcept
{
if (v > 1 ||
r >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") ||
s >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") ||
s < h256(1) ||
r < h256(1))
return false;
return true;
}
Public SignatureStruct::recover(h256 const& _hash) const
{
return dev::recover((Signature)*this, _hash);
}
Address dev::ZeroAddress = Address();
Public dev::toPublic(Secret const& _secret)
{
#ifdef ETH_HAVE_SECP256K1
bytes o(65);
int pubkeylen;
if (!secp256k1_ec_pubkey_create(s_secp256k1, o.data(), &pubkeylen, _secret.data(), false))
return Public();
return FixedHash<64>(o.data()+1, Public::ConstructFromPointer);
#else
Public p;
s_secp256k1pp.toPublic(_secret, p);
return p;
#endif
}
Address dev::toAddress(Public const& _public)
{
return right160(sha3(_public.ref()));
}
Address dev::toAddress(Secret const& _secret)
{
Public p;
s_secp256k1pp.toPublic(_secret, p);
return toAddress(p);
}
Address dev::toAddress(Address const& _from, u256 const& _nonce)
{
return right160(sha3(rlpList(_from, _nonce)));
}
void dev::encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher)
{
bytes io = _plain.toBytes();
s_secp256k1pp.encrypt(_k, io);
o_cipher = std::move(io);
}
bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
{
bytes io = _cipher.toBytes();
s_secp256k1pp.decrypt(_k, io);
if (io.empty())
return false;
o_plaintext = std::move(io);
return true;
}
void dev::encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher)
{
bytes io = _plain.toBytes();
s_secp256k1pp.encryptECIES(_k, io);
o_cipher = std::move(io);
}
bool dev::decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
{
bytes io = _cipher.toBytes();
if (!s_secp256k1pp.decryptECIES(_k, io))
return false;
o_plaintext = std::move(io);
return true;
}
void dev::encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher)
{
// TOOD: @alex @subtly do this properly.
encrypt(KeyPair(_k).pub(), _plain, o_cipher);
}
bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain)
{
// TODO: @alex @subtly do this properly.
return decrypt(_k, _cipher, o_plain);
}
std::pair<bytes, h128> dev::encryptSymNoAuth(SecureFixedHash<16> const& _k, bytesConstRef _plain)
{
h128 iv(Nonce::get().makeInsecure());
return make_pair(encryptSymNoAuth(_k, iv, _plain), iv);
}
bytes dev::encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain)
{
if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
return bytes();
SecByteBlock key(_k.data(), _k.size());
try
{
CTR_Mode<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), _iv.data());
bytes ret(_plain.size());
e.ProcessData(ret.data(), _plain.data(), _plain.size());
return ret;
}
catch (CryptoPP::Exception& _e)
{
cerr << _e.what() << endl;
return bytes();
}
}
bytesSec dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher)
{
if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
return bytesSec();
SecByteBlock key(_k.data(), _k.size());
try
{
CTR_Mode<AES>::Decryption d;
d.SetKeyWithIV(key, key.size(), _iv.data());
bytesSec ret(_cipher.size());
d.ProcessData(ret.writable().data(), _cipher.data(), _cipher.size());
return ret;
}
catch (CryptoPP::Exception& _e)
{
cerr << _e.what() << endl;
return bytesSec();
}
}
static const Public c_zeroKey("3f17f1962b36e491b30a40b2405849e597ba5fb5");
Public dev::recover(Signature const& _sig, h256 const& _message)
{
Public ret;
#ifdef ETH_HAVE_SECP256K1
bytes o(65);
int pubkeylen;
if (!secp256k1_ecdsa_recover_compact(s_secp256k1, _message.data(), _sig.data(), o.data(), &pubkeylen, false, _sig[64]))
return Public();
ret = FixedHash<64>(o.data() + 1, Public::ConstructFromPointer);
#else
ret = s_secp256k1pp.recover(_sig, _message.ref());
#endif
if (ret == c_zeroKey)
return Public();
return ret;
}
Signature dev::sign(Secret const& _k, h256 const& _hash)
{
#ifdef ETH_HAVE_SECP256K1
Signature s;
int v;
if (!secp256k1_ecdsa_sign_compact(s_secp256k1, _hash.data(), s.data(), _k.data(), NULL, NULL, &v))
return Signature();
s[64] = v;
return s;
#else
return s_secp256k1pp.sign(_k, _hash);
#endif
}
bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash)
{
if (!_p)
return false;
#ifdef ETH_HAVE_SECP256K1
return _p == recover(_s, _hash);
#else
return s_secp256k1pp.verify(_p, _s, _hash.ref(), true);
#endif
}
bytesSec dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen)
{
bytesSec ret(_dkLen);
if (PKCS5_PBKDF2_HMAC<SHA256>().DeriveKey(
ret.writable().data(),
_dkLen,
0,
reinterpret_cast<byte const*>(_pass.data()),
_pass.size(),
_salt.data(),
_salt.size(),
_iterations
) != _iterations)
BOOST_THROW_EXCEPTION(CryptoException() << errinfo_comment("Key derivation failed."));
return ret;
}
bytesSec dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen)
{
bytesSec ret(_dkLen);
if (libscrypt_scrypt(
reinterpret_cast<uint8_t const*>(_pass.data()),
_pass.size(),
_salt.data(),
_salt.size(),
_n,
_r,
_p,
ret.writable().data(),
_dkLen
) != 0)
BOOST_THROW_EXCEPTION(CryptoException() << errinfo_comment("Key derivation failed."));
return ret;
}
void KeyPair::populateFromSecret(Secret const& _sec)
{
m_secret = _sec;
if (s_secp256k1pp.verifySecret(m_secret, m_public))
m_address = toAddress(m_public);
}
KeyPair KeyPair::create()
{
for (int i = 0; i < 100; ++i)
{
KeyPair ret(Secret::random());
if (ret.address())
return ret;
}
return KeyPair();
}
KeyPair KeyPair::fromEncryptedSeed(bytesConstRef _seed, std::string const& _password)
{
return KeyPair(Secret(sha3(aesDecrypt(_seed, _password))));
}
h256 crypto::kdf(Secret const& _priv, h256 const& _hash)
{
// H(H(r||k)^h)
h256 s;
sha3mac(Secret::random().ref(), _priv.ref(), s.ref());
s ^= _hash;
sha3(s.ref(), s.ref());
if (!s || !_hash || !_priv)
BOOST_THROW_EXCEPTION(InvalidState());
return s;
}
Secret Nonce::next()
{
Guard l(x_value);
if (!m_value)
{
m_value = Secret::random();
if (!m_value)
BOOST_THROW_EXCEPTION(InvalidState());
}
m_value = sha3Secure(m_value.ref());
return sha3(~m_value);
}

218
libdevcrypto/Common.h

@ -1,218 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Common.h
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* Ethereum-specific data structures & algorithms.
*/
#pragma once
#include <mutex>
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/FileSystem.h>
namespace dev
{
using Secret = SecureFixedHash<32>;
/// A public key: 64 bytes.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Public = h512;
/// A signature: 65 bytes: r: [0, 32), s: [32, 64), v: 64.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Signature = h520;
struct SignatureStruct
{
SignatureStruct() = default;
SignatureStruct(Signature const& _s) { *(h520*)this = _s; }
SignatureStruct(h256 const& _r, h256 const& _s, byte _v): r(_r), s(_s), v(_v) {}
operator Signature() const { return *(h520 const*)this; }
/// @returns true if r,s,v values are valid, otherwise false
bool isValid() const noexcept;
/// @returns the public part of the key that signed @a _hash to give this sig.
Public recover(h256 const& _hash) const;
h256 r;
h256 s;
byte v = 0;
};
/// An Ethereum address: 20 bytes.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Address = h160;
/// The zero address.
extern Address ZeroAddress;
/// A vector of Ethereum addresses.
using Addresses = h160s;
/// A hash set of Ethereum addresses.
using AddressHash = std::unordered_set<h160>;
/// A vector of secrets.
using Secrets = std::vector<Secret>;
/// Convert a secret key into the public key equivalent.
Public toPublic(Secret const& _secret);
/// Convert a public key to address.
Address toAddress(Public const& _public);
/// Convert a secret key into address of public key equivalent.
/// @returns 0 if it's not a valid secret key.
Address toAddress(Secret const& _secret);
// Convert transaction from and nonce to address.
Address toAddress(Address const& _from, u256 const& _nonce);
/// Encrypts plain text using Public key.
void encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypts cipher using Secret key.
bool decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Symmetric encryption.
void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Symmetric decryption.
bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Encrypt payload using ECIES standard with AES128-CTR.
void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypt payload using ECIES standard with AES128-CTR.
bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Encrypts payload with random IV/ctr using AES128-CTR.
std::pair<bytes, h128> encryptSymNoAuth(SecureFixedHash<16> const& _k, bytesConstRef _plain);
/// Encrypts payload with specified IV/ctr using AES128-CTR.
bytes encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain);
/// Decrypts payload with specified IV/ctr using AES128-CTR.
bytesSec decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher);
/// Encrypts payload with specified IV/ctr using AES128-CTR.
inline bytes encryptSymNoAuth(SecureFixedHash<16> const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }
inline bytes encryptSymNoAuth(SecureFixedHash<32> const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }
/// Decrypts payload with specified IV/ctr using AES128-CTR.
inline bytesSec decryptSymNoAuth(SecureFixedHash<16> const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }
inline bytesSec decryptSymNoAuth(SecureFixedHash<32> const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }
/// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash);
/// Returns siganture of message hash.
Signature sign(Secret const& _k, h256 const& _hash);
/// Verify signature.
bool verify(Public const& _k, Signature const& _s, h256 const& _hash);
/// Derive key via PBKDF2.
bytesSec pbkdf2(std::string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen = 32);
/// Derive key via Scrypt.
bytesSec scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen);
/// Simple class that represents a "key pair".
/// All of the data of the class can be regenerated from the secret key (m_secret) alone.
/// Actually stores a tuplet of secret, public and address (the right 160-bits of the public).
class KeyPair
{
public:
/// Null constructor.
KeyPair() {}
/// Normal constructor - populates object from the given secret key.
KeyPair(Secret const& _k) { populateFromSecret(_k); }
/// Create a new, randomly generated object.
static KeyPair create();
/// Create from an encrypted seed.
static KeyPair fromEncryptedSeed(bytesConstRef _seed, std::string const& _password);
/// Retrieve the secret key.
Secret const& secret() const { return m_secret; }
/// Retrieve the secret key.
Secret const& sec() const { return m_secret; }
/// Retrieve the public key.
Public const& pub() const { return m_public; }
/// Retrieve the associated address of the public key.
Address const& address() const { return m_address; }
bool operator==(KeyPair const& _c) const { return m_public == _c.m_public; }
bool operator!=(KeyPair const& _c) const { return m_public != _c.m_public; }
private:
void populateFromSecret(Secret const& _k);
Secret m_secret;
Public m_public;
Address m_address;
};
namespace crypto
{
DEV_SIMPLE_EXCEPTION(InvalidState);
/// Key derivation
h256 kdf(Secret const& _priv, h256 const& _hash);
/**
* @brief Generator for non-repeating nonce material.
* The Nonce class should only be used when a non-repeating nonce
* is required and, in its current form, not recommended for signatures.
* This is primarily because the key-material for signatures is
* encrypted on disk whereas the seed for Nonce is not.
* Thus, Nonce's primary intended use at this time is for networking
* where the key is also stored in plaintext.
*/
class Nonce
{
public:
/// Returns the next nonce (might be read from a file).
static Secret get() { static Nonce s; return s.next(); }
private:
Nonce() = default;
/// @returns the next nonce.
Secret next();
std::mutex x_value;
Secret m_value;
};
}
}

348
libdevcrypto/CryptoPP.cpp

@ -1,348 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CryptoPP.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
#include <libdevcore/Guards.h>
#include <libdevcore/Assertions.h>
#include "ECDHE.h"
#include "CryptoPP.h"
using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes.");
static_assert(dev::Public::size == 64, "Public key must be 64 bytes.");
static_assert(dev::Signature::size == 65, "Signature must be 65 bytes.");
bytes Secp256k1PP::eciesKDF(Secret const& _z, bytes _s1, unsigned kdByteLen)
{
auto reps = ((kdByteLen + 7) * 8) / (CryptoPP::SHA256::BLOCKSIZE * 8);
// SEC/ISO/Shoup specify counter size SHOULD be equivalent
// to size of hash output, however, it also notes that
// the 4 bytes is okay. NIST specifies 4 bytes.
bytes ctr({0, 0, 0, 1});
bytes k;
CryptoPP::SHA256 ctx;
for (unsigned i = 0; i <= reps; i++)
{
ctx.Update(ctr.data(), ctr.size());
ctx.Update(_z.data(), Secret::size);
ctx.Update(_s1.data(), _s1.size());
// append hash to k
bytes digest(32);
ctx.Final(digest.data());
ctx.Restart();
k.reserve(k.size() + h256::size);
move(digest.begin(), digest.end(), back_inserter(k));
if (++ctr[3] || ++ctr[2] || ++ctr[1] || ++ctr[0])
continue;
}
k.resize(kdByteLen);
return k;
}
void Secp256k1PP::encryptECIES(Public const& _k, bytes& io_cipher)
{
// interop w/go ecies implementation
auto r = KeyPair::create();
Secret z;
ecdh::agree(r.sec(), _k, z);
auto key = eciesKDF(z, bytes(), 32);
bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16);
bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16);
CryptoPP::SHA256 ctx;
ctx.Update(mKeyMaterial.data(), mKeyMaterial.size());
bytes mKey(32);
ctx.Final(mKey.data());
bytes cipherText = encryptSymNoAuth(SecureFixedHash<16>(eKey), h128(), bytesConstRef(&io_cipher));
if (cipherText.empty())
return;
bytes msg(1 + Public::size + h128::size + cipherText.size() + 32);
msg[0] = 0x04;
r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size));
bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size + h128::size, cipherText.size());
bytesConstRef(&cipherText).copyTo(msgCipherRef);
// tag message
CryptoPP::HMAC<SHA256> hmacctx(mKey.data(), mKey.size());
bytesConstRef cipherWithIV = bytesRef(&msg).cropped(1 + Public::size, h128::size + cipherText.size());
hmacctx.Update(cipherWithIV.data(), cipherWithIV.size());
hmacctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size());
io_cipher.resize(msg.size());
io_cipher.swap(msg);
}
bool Secp256k1PP::decryptECIES(Secret const& _k, bytes& io_text)
{
// interop w/go ecies implementation
// io_cipher[0] must be 2, 3, or 4, else invalidpublickey
if (io_text.empty() || io_text[0] < 2 || io_text[0] > 4)
// invalid message: publickey
return false;
if (io_text.size() < (1 + Public::size + h128::size + 1 + h256::size))
// invalid message: length
return false;
Secret z;
ecdh::agree(_k, *(Public*)(io_text.data() + 1), z);
auto key = eciesKDF(z, bytes(), 64);
bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16);
bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16);
bytes mKey(32);
CryptoPP::SHA256 ctx;
ctx.Update(mKeyMaterial.data(), mKeyMaterial.size());
ctx.Final(mKey.data());
bytes plain;
size_t cipherLen = io_text.size() - 1 - Public::size - h128::size - h256::size;
bytesConstRef cipherWithIV(io_text.data() + 1 + Public::size, h128::size + cipherLen);
bytesConstRef cipherIV = cipherWithIV.cropped(0, h128::size);
bytesConstRef cipherNoIV = cipherWithIV.cropped(h128::size, cipherLen);
bytesConstRef msgMac(cipherNoIV.data() + cipherLen, h256::size);
h128 iv(cipherIV.toBytes());
// verify tag
CryptoPP::HMAC<SHA256> hmacctx(mKey.data(), mKey.size());
hmacctx.Update(cipherWithIV.data(), cipherWithIV.size());
h256 mac;
hmacctx.Final(mac.data());
for (unsigned i = 0; i < h256::size; i++)
if (mac[i] != msgMac[i])
return false;
plain = decryptSymNoAuth(SecureFixedHash<16>(eKey), iv, cipherNoIV).makeInsecure();
io_text.resize(plain.size());
io_text.swap(plain);
return true;
}
void Secp256k1PP::encrypt(Public const& _k, bytes& io_cipher)
{
ECIES<ECP>::Encryptor e;
initializeDLScheme(_k, e);
size_t plen = io_cipher.size();
bytes ciphertext;
ciphertext.resize(e.CiphertextLength(plen));
{
Guard l(x_rng);
e.Encrypt(m_rng, io_cipher.data(), plen, ciphertext.data());
}
memset(io_cipher.data(), 0, io_cipher.size());
io_cipher = std::move(ciphertext);
}
void Secp256k1PP::decrypt(Secret const& _k, bytes& io_text)
{
CryptoPP::ECIES<CryptoPP::ECP>::Decryptor d;
initializeDLScheme(_k, d);
if (!io_text.size())
{
io_text.resize(1);
io_text[0] = 0;
}
size_t clen = io_text.size();
bytes plain;
plain.resize(d.MaxPlaintextLength(io_text.size()));
DecodingResult r;
{
Guard l(x_rng);
r = d.Decrypt(m_rng, io_text.data(), clen, plain.data());
}
if (!r.isValidCoding)
{
io_text.clear();
return;
}
io_text.resize(r.messageLength);
io_text = std::move(plain);
}
Signature Secp256k1PP::sign(Secret const& _k, bytesConstRef _message)
{
return sign(_k, sha3(_message));
}
Signature Secp256k1PP::sign(Secret const& _key, h256 const& _hash)
{
// assumption made by signing alogrithm
asserts(m_q == m_qs);
Signature sig;
Integer k(kdf(_key, _hash).data(), 32);
if (k == 0)
BOOST_THROW_EXCEPTION(InvalidState());
k = 1 + (k % (m_qs - 1));
ECP::Point rp;
Integer r;
{
Guard l(x_params);
rp = m_params.ExponentiateBase(k);
r = m_params.ConvertElementToInteger(rp);
}
sig[64] = 0;
// sig[64] = (r >= m_q) ? 2 : 0;
Integer kInv = k.InverseMod(m_q);
Integer z(_hash.asBytes().data(), 32);
Integer s = (kInv * (Integer(_key.data(), 32) * r + z)) % m_q;
if (r == 0 || s == 0)
BOOST_THROW_EXCEPTION(InvalidState());
// if (s > m_qs)
// {
// s = m_q - s;
// if (sig[64])
// sig[64] ^= 1;
// }
sig[64] |= rp.y.IsOdd() ? 1 : 0;
r.Encode(sig.data(), 32);
s.Encode(sig.data() + 32, 32);
return sig;
}
bool Secp256k1PP::verify(Signature const& _signature, bytesConstRef _message)
{
return !!recover(_signature, _message);
}
bool Secp256k1PP::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed)
{
// todo: verify w/o recovery (if faster)
return _p == (_hashed ? recover(_sig, _message) : recover(_sig, sha3(_message).ref()));
}
Public Secp256k1PP::recover(Signature _signature, bytesConstRef _message)
{
Public recovered;
Integer r(_signature.data(), 32);
Integer s(_signature.data()+32, 32);
// cryptopp encodes sign of y as 0x02/0x03 instead of 0/1 or 27/28
byte encodedpoint[33];
encodedpoint[0] = _signature[64] | 2;
memcpy(&encodedpoint[1], _signature.data(), 32);
ECP::Element x;
{
m_curve.DecodePoint(x, encodedpoint, 33);
if (!m_curve.VerifyPoint(x))
return recovered;
}
// if (_signature[64] & 2)
// {
// r += m_q;
// Guard l(x_params);
// if (r >= m_params.GetMaxExponent())
// return recovered;
// }
Integer z(_message.data(), 32);
Integer rn = r.InverseMod(m_q);
Integer u1 = m_q - (rn.Times(z)).Modulo(m_q);
Integer u2 = (rn.Times(s)).Modulo(m_q);
ECP::Point p;
byte recoveredbytes[65];
{
// todo: make generator member
p = m_curve.CascadeMultiply(u2, x, u1, m_params.GetSubgroupGenerator());
if (p.identity)
return Public();
m_curve.EncodePoint(recoveredbytes, p, false);
}
memcpy(recovered.data(), &recoveredbytes[1], 64);
return recovered;
}
bool Secp256k1PP::verifySecret(Secret const& _s, Public& _p)
{
DL_PrivateKey_EC<ECP> k;
k.Initialize(m_params, secretToExponent(_s));
if (!k.Validate(m_rng, 3))
return false;
DL_PublicKey_EC<CryptoPP::ECP> pub;
k.MakePublicKey(pub);
if (!k.Validate(m_rng, 3))
return false;
exportPublicKey(pub, _p);
return true;
}
void Secp256k1PP::agree(Secret const& _s, Public const& _r, Secret& o_s)
{
// TODO: mutex ASN1::secp256k1() singleton
// Creating Domain is non-const for m_oid and m_oid is not thread-safe
ECDH<ECP>::Domain d(ASN1::secp256k1());
assert(d.AgreedValueLength() == sizeof(o_s));
byte remote[65] = {0x04};
memcpy(&remote[1], _r.data(), 64);
d.Agree(o_s.writable().data(), _s.data(), remote);
}
void Secp256k1PP::exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& o_p)
{
bytes prefixedKey(_k.GetGroupParameters().GetEncodedElementSize(true));
{
Guard l(x_params);
m_params.GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false);
assert(Public::size + 1 == _k.GetGroupParameters().GetEncodedElementSize(true));
}
memcpy(o_p.data(), &prefixedKey[1], Public::size);
}
void Secp256k1PP::exponentToPublic(Integer const& _e, Public& o_p)
{
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pk;
{
Guard l(x_params);
pk.Initialize(m_params, m_params.ExponentiateBase(_e));
}
exportPublicKey(pk, o_p);
}

141
libdevcrypto/CryptoPP.h

@ -1,141 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CryptoPP.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* CryptoPP headers and primitive helper methods
*/
#pragma once
#include <mutex>
// need to leave this one disabled for link-time. blame cryptopp.
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma warning(push)
#pragma warning(disable:4100 4244)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
#pragma GCC diagnostic ignored "-Wextra"
#include <cryptopp/sha.h>
#include <cryptopp/sha3.h>
#include <cryptopp/ripemd.h>
#include <cryptopp/aes.h>
#include <cryptopp/pwdbased.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h>
#include <cryptopp/eccrypto.h>
#include <cryptopp/ecp.h>
#include <cryptopp/files.h>
#include <cryptopp/osrng.h>
#include <cryptopp/oids.h>
#include <cryptopp/dsa.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include <libdevcore/SHA3.h>
#include "Common.h"
namespace dev
{
namespace crypto
{
using namespace CryptoPP;
inline ECP::Point publicToPoint(Public const& _p) { Integer x(_p.data(), 32); Integer y(_p.data() + 32, 32); return ECP::Point(x,y); }
inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.data(), Secret::size)); }
/**
* CryptoPP secp256k1 algorithms.
* @todo Collect ECIES methods into class.
*/
class Secp256k1PP
{
public:
Secp256k1PP(): m_oid(ASN1::secp256k1()), m_params(m_oid), m_curve(m_params.GetCurve()), m_q(m_params.GetGroupOrder()), m_qs(m_params.GetSubgroupOrder()) {}
void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); }
/// Encrypts text (replace input). (ECIES w/XOR-SHA1)
void encrypt(Public const& _k, bytes& io_cipher);
/// Decrypts text (replace input). (ECIES w/XOR-SHA1)
void decrypt(Secret const& _k, bytes& io_text);
/// Encrypts text (replace input). (ECIES w/AES128-CTR-SHA256)
void encryptECIES(Public const& _k, bytes& io_cipher);
/// Decrypts text (replace input). (ECIES w/AES128-CTR-SHA256)
bool decryptECIES(Secret const& _k, bytes& io_text);
/// Key derivation function used by encryptECIES and decryptECIES.
bytes eciesKDF(Secret const& _z, bytes _s1, unsigned kdBitLen = 256);
/// @returns siganture of message.
Signature sign(Secret const& _k, bytesConstRef _message);
/// @returns compact siganture of provided hash.
Signature sign(Secret const& _k, h256 const& _hash);
/// Verify compact signature (public key is extracted from signature).
bool verify(Signature const& _signature, bytesConstRef _message);
/// Verify signature.
bool verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed = false);
/// Recovers public key from compact signature. Uses libsecp256k1.
Public recover(Signature _signature, bytesConstRef _message);
/// Verifies _s is a valid secret key and returns corresponding public key in o_p.
bool verifySecret(Secret const& _s, Public& o_p);
void agree(Secret const& _s, Public const& _r, Secret& o_s);
protected:
void exportPrivateKey(DL_PrivateKey_EC<ECP> const& _k, Secret& o_s) { _k.GetPrivateExponent().Encode(o_s.writable().data(), Secret::size); }
void exportPublicKey(DL_PublicKey_EC<ECP> const& _k, Public& o_p);
void exponentToPublic(Integer const& _e, Public& o_p);
template <class T> void initializeDLScheme(Secret const& _s, T& io_operator) { std::lock_guard<std::mutex> l(x_params); io_operator.AccessKey().Initialize(m_params, secretToExponent(_s)); }
template <class T> void initializeDLScheme(Public const& _p, T& io_operator) { std::lock_guard<std::mutex> l(x_params); io_operator.AccessKey().Initialize(m_params, publicToPoint(_p)); }
private:
OID m_oid;
std::mutex x_rng;
AutoSeededRandomPool m_rng;
std::mutex x_params;
DL_GroupParameters_EC<ECP> m_params;
std::mutex x_curve;
DL_GroupParameters_EC<ECP>::EllipticCurve m_curve;
Integer m_q;
Integer m_qs;
};
}
}

46
libdevcrypto/ECDHE.cpp

@ -1,46 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ECDHE.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
#include "ECDHE.h"
#include <libdevcore/SHA3.h>
#include "CryptoPP.h"
using namespace std;
using namespace dev;
using namespace dev::crypto;
static Secp256k1PP s_secp256k1;
void dev::crypto::ecdh::agree(Secret const& _s, Public const& _r, Secret& o_s)
{
s_secp256k1.agree(_s, _r, o_s);
}
void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) const
{
if (m_remoteEphemeral)
// agreement can only occur once
BOOST_THROW_EXCEPTION(InvalidState());
m_remoteEphemeral = _remote;
s_secp256k1.agree(m_ephemeral.sec(), m_remoteEphemeral, o_sharedSecret);
}

81
libdevcrypto/ECDHE.h

@ -1,81 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ECDHE.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Elliptic curve Diffie-Hellman ephemeral key exchange
*/
#pragma once
#include "AES.h"
namespace dev
{
namespace crypto
{
/// Public key of remote and corresponding shared secret.
using AliasSession = std::pair<Public,h256>;
/**
* @brief An addressable EC key pair.
*/
class Alias
{
public:
Alias(Secret const& _s): m_secret(_s) {};
AliasSession session(Address _a) { return m_sessions.count(_a) ? m_sessions.find(_a)->second : AliasSession(); }
private:
std::map<Address,AliasSession> m_sessions;
Secret m_secret;
};
namespace ecdh
{
void agree(Secret const& _s, Public const& _r, Secret& o_s);
}
/**
* @brief Derive DH shared secret from EC keypairs.
* As ephemeral keys are single-use, agreement is limited to a single occurence.
*/
class ECDHE
{
public:
/// Constructor (pass public key for ingress exchange).
ECDHE(): m_ephemeral(KeyPair::create()) {};
/// Public key sent to remote.
Public pubkey() { return m_ephemeral.pub(); }
Secret seckey() { return m_ephemeral.sec(); }
/// Input public key for dh agreement, output generated shared secret.
void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret) const;
protected:
KeyPair m_ephemeral; ///< Ephemeral keypair; generated.
mutable Public m_remoteEphemeral; ///< Public key of remote; parameter. Set once when agree is called, otherwise immutable.
};
}
}

35
libdevcrypto/Exceptions.h

@ -1,35 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Exceptions.h
* @author Christian <c@ethdev.com>
* @date 2016
*/
#pragma once
#include <libdevcore/Exceptions.h>
namespace dev
{
namespace crypto
{
/// Rare malfunction of cryptographic functions.
DEV_SIMPLE_EXCEPTION(CryptoException);
}
}

157
libdevcrypto/OverlayDB.cpp

@ -1,157 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file OverlayDB.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <thread>
#include <libdevcore/db.h>
#include <libdevcore/Common.h>
#include "OverlayDB.h"
using namespace std;
using namespace dev;
namespace dev
{
h256 const EmptyTrie = sha3(rlp(""));
OverlayDB::~OverlayDB()
{
if (m_db.use_count() == 1 && m_db.get())
cnote << "Closing state DB";
}
class WriteBatchNoter: public ldb::WriteBatch::Handler
{
virtual void Put(ldb::Slice const& _key, ldb::Slice const& _value) { cnote << "Put" << toHex(bytesConstRef(_key)) << "=>" << toHex(bytesConstRef(_value)); }
virtual void Delete(ldb::Slice const& _key) { cnote << "Delete" << toHex(bytesConstRef(_key)); }
};
void OverlayDB::commit()
{
if (m_db)
{
ldb::WriteBatch batch;
// cnote << "Committing nodes to disk DB:";
#if DEV_GUARDED_DB
DEV_READ_GUARDED(x_this)
#endif
{
for (auto const& i: m_main)
{
if (i.second.second)
batch.Put(ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.first.data(), i.second.first.size()));
// cnote << i.first << "#" << m_main[i.first].second;
}
for (auto const& i: m_aux)
if (i.second.second)
{
bytes b = i.first.asBytes();
b.push_back(255); // for aux
batch.Put(bytesConstRef(&b), bytesConstRef(&i.second.first));
}
}
for (unsigned i = 0; i < 10; ++i)
{
ldb::Status o = m_db->Write(m_writeOptions, &batch);
if (o.ok())
break;
if (i == 9)
{
cwarn << "Fail writing to state database. Bombing out.";
exit(-1);
}
cwarn << "Error writing to state database: " << o.ToString();
WriteBatchNoter n;
batch.Iterate(&n);
cwarn << "Sleeping for" << (i + 1) << "seconds, then retrying.";
this_thread::sleep_for(chrono::seconds(i + 1));
}
#if DEV_GUARDED_DB
DEV_WRITE_GUARDED(x_this)
#endif
{
m_aux.clear();
m_main.clear();
}
}
}
bytes OverlayDB::lookupAux(h256 const& _h) const
{
bytes ret = MemoryDB::lookupAux(_h);
if (!ret.empty() || !m_db)
return ret;
std::string v;
bytes b = _h.asBytes();
b.push_back(255); // for aux
m_db->Get(m_readOptions, bytesConstRef(&b), &v);
if (v.empty())
cwarn << "Aux not found: " << _h;
return asBytes(v);
}
void OverlayDB::rollback()
{
#if DEV_GUARDED_DB
WriteGuard l(x_this);
#endif
m_main.clear();
}
std::string OverlayDB::lookup(h256 const& _h) const
{
std::string ret = MemoryDB::lookup(_h);
if (ret.empty() && m_db)
m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret);
return ret;
}
bool OverlayDB::exists(h256 const& _h) const
{
if (MemoryDB::exists(_h))
return true;
std::string ret;
if (m_db)
m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret);
return !ret.empty();
}
void OverlayDB::kill(h256 const& _h)
{
#if ETH_PARANOIA || 1
if (!MemoryDB::kill(_h))
{
std::string ret;
if (m_db)
m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret);
// No point node ref decreasing for EmptyTrie since we never bother incrementing it in the first place for
// empty storage tries.
if (ret.empty() && _h != EmptyTrie)
cnote << "Decreasing DB node ref count below zero with no DB node. Probably have a corrupt Trie." << _h;
// TODO: for 1.1: ref-counted triedb.
}
#else
MemoryDB::kill(_h);
#endif
}
}

59
libdevcrypto/OverlayDB.h

@ -1,59 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file OverlayDB.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <memory>
#include <libdevcore/db.h>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libdevcore/MemoryDB.h>
namespace dev
{
class OverlayDB: public MemoryDB
{
public:
OverlayDB(ldb::DB* _db = nullptr): m_db(_db) {}
~OverlayDB();
ldb::DB* db() const { return m_db.get(); }
void commit();
void rollback();
std::string lookup(h256 const& _h) const;
bool exists(h256 const& _h) const;
void kill(h256 const& _h);
bytes lookupAux(h256 const& _h) const;
private:
using MemoryDB::clear;
std::shared_ptr<ldb::DB> m_db;
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
};
}

375
libdevcrypto/SecretStore.cpp

@ -1,375 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file SecretStore.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "SecretStore.h"
#include <thread>
#include <mutex>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <libdevcore/Log.h>
#include <libdevcore/Guards.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h>
#include <test/JsonSpiritHeaders.h>
#include <libdevcrypto/Exceptions.h>
using namespace std;
using namespace dev;
namespace js = json_spirit;
namespace fs = boost::filesystem;
static const int c_keyFileVersion = 3;
/// Upgrade the json-format to the current version.
static js::mValue upgraded(string const& _s)
{
js::mValue v;
js::read_string(_s, v);
if (v.type() != js::obj_type)
return js::mValue();
js::mObject ret = v.get_obj();
unsigned version = ret.count("Version") ? stoi(ret["Version"].get_str()) : ret.count("version") ? ret["version"].get_int() : 0;
if (version == 1)
{
// upgrade to version 2
js::mObject old;
swap(old, ret);
ret["id"] = old["Id"];
js::mObject c;
c["ciphertext"] = old["Crypto"].get_obj()["CipherText"];
c["cipher"] = "aes-128-cbc";
{
js::mObject cp;
cp["iv"] = old["Crypto"].get_obj()["IV"];
c["cipherparams"] = cp;
}
c["kdf"] = old["Crypto"].get_obj()["KeyHeader"].get_obj()["Kdf"];
{
js::mObject kp;
kp["salt"] = old["Crypto"].get_obj()["Salt"];
for (auto const& i: old["Crypto"].get_obj()["KeyHeader"].get_obj()["KdfParams"].get_obj())
if (i.first != "SaltLen")
kp[boost::to_lower_copy(i.first)] = i.second;
c["kdfparams"] = kp;
}
c["sillymac"] = old["Crypto"].get_obj()["MAC"];
c["sillymacjson"] = _s;
ret["crypto"] = c;
version = 2;
}
if (version == 2)
{
ret["crypto"].get_obj()["cipher"] = "aes-128-ctr";
ret["crypto"].get_obj()["compat"] = "2";
version = 3;
}
if (version == c_keyFileVersion)
return ret;
return js::mValue();
}
SecretStore::SecretStore(string const& _path): m_path(_path)
{
load();
}
bytesSec SecretStore::secret(h128 const& _uuid, function<string()> const& _pass, bool _useCache) const
{
auto rit = m_cached.find(_uuid);
if (_useCache && rit != m_cached.end())
return rit->second;
auto it = m_keys.find(_uuid);
bytesSec key;
if (it != m_keys.end())
{
key = bytesSec(decrypt(it->second.encryptedKey, _pass()));
if (!key.empty())
m_cached[_uuid] = key;
}
return key;
}
bytesSec SecretStore::secret(string const& _content, string const& _pass)
{
js::mValue u = upgraded(_content);
if (u.type() != js::obj_type)
return bytesSec();
return decrypt(js::write_string(u.get_obj()["crypto"], false), _pass);
}
h128 SecretStore::importSecret(bytesSec const& _s, string const& _pass)
{
h128 r;
EncryptedKey key{encrypt(_s.ref(), _pass), string()};
r = h128::random();
m_cached[r] = _s;
m_keys[r] = move(key);
save();
return r;
}
h128 SecretStore::importSecret(bytesConstRef _s, string const& _pass)
{
h128 r;
EncryptedKey key{encrypt(_s, _pass), string()};
r = h128::random();
m_cached[r] = bytesSec(_s);
m_keys[r] = move(key);
save();
return r;
}
void SecretStore::kill(h128 const& _uuid)
{
m_cached.erase(_uuid);
if (m_keys.count(_uuid))
{
fs::remove(m_keys[_uuid].filename);
m_keys.erase(_uuid);
}
}
void SecretStore::clearCache() const
{
m_cached.clear();
}
void SecretStore::save(string const& _keysPath)
{
fs::path p(_keysPath);
fs::create_directories(p);
DEV_IGNORE_EXCEPTIONS(fs::permissions(p, fs::owner_all));
for (auto& k: m_keys)
{
string uuid = toUUID(k.first);
string filename = (p / uuid).string() + ".json";
js::mObject v;
js::mValue crypto;
js::read_string(k.second.encryptedKey, crypto);
v["crypto"] = crypto;
v["id"] = uuid;
v["version"] = c_keyFileVersion;
writeFile(filename, js::write_string(js::mValue(v), true));
swap(k.second.filename, filename);
if (!filename.empty() && !fs::equivalent(filename, k.second.filename))
fs::remove(filename);
}
}
void SecretStore::load(string const& _keysPath)
{
fs::path p(_keysPath);
try
{
for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it)
if (fs::is_regular_file(it->path()))
readKey(it->path().string(), true);
}
catch (...) {}
}
h128 SecretStore::readKey(string const& _file, bool _takeFileOwnership)
{
cnote << "Reading" << _file;
return readKeyContent(contentsString(_file), _takeFileOwnership ? _file : string());
}
h128 SecretStore::readKeyContent(string const& _content, string const& _file)
{
js::mValue u = upgraded(_content);
if (u.type() == js::obj_type)
{
js::mObject& o = u.get_obj();
auto uuid = fromUUID(o["id"].get_str());
m_keys[uuid] = EncryptedKey{js::write_string(o["crypto"], false), _file};
return uuid;
}
else
cwarn << "Invalid JSON in key file" << _file;
return h128();
}
bool SecretStore::recode(h128 const& _uuid, string const& _newPass, function<string()> const& _pass, KDF _kdf)
{
bytesSec s = secret(_uuid, _pass, true);
if (s.empty())
return false;
m_cached.erase(_uuid);
m_keys[_uuid].encryptedKey = encrypt(s.ref(), _newPass, _kdf);
save();
return true;
}
static bytesSec deriveNewKey(string const& _pass, KDF _kdf, js::mObject& o_ret)
{
unsigned dklen = 32;
unsigned iterations = 1 << 18;
bytes salt = h256::random().asBytes();
if (_kdf == KDF::Scrypt)
{
unsigned p = 1;
unsigned r = 8;
o_ret["kdf"] = "scrypt";
{
js::mObject params;
params["n"] = int64_t(iterations);
params["r"] = int(r);
params["p"] = int(p);
params["dklen"] = int(dklen);
params["salt"] = toHex(salt);
o_ret["kdfparams"] = params;
}
return scrypt(_pass, salt, iterations, r, p, dklen);
}
else
{
o_ret["kdf"] = "pbkdf2";
{
js::mObject params;
params["prf"] = "hmac-sha256";
params["c"] = int(iterations);
params["salt"] = toHex(salt);
params["dklen"] = int(dklen);
o_ret["kdfparams"] = params;
}
return pbkdf2(_pass, salt, iterations, dklen);
}
}
string SecretStore::encrypt(bytesConstRef _v, string const& _pass, KDF _kdf)
{
js::mObject ret;
bytesSec derivedKey = deriveNewKey(_pass, _kdf, ret);
if (derivedKey.empty())
BOOST_THROW_EXCEPTION(crypto::CryptoException() << errinfo_comment("Key derivation failed."));
ret["cipher"] = "aes-128-ctr";
SecureFixedHash<16> key(derivedKey, h128::AlignLeft);
h128 iv = h128::random();
{
js::mObject params;
params["iv"] = toHex(iv.ref());
ret["cipherparams"] = params;
}
// cipher text
bytes cipherText = encryptSymNoAuth(key, iv, _v);
if (cipherText.empty())
BOOST_THROW_EXCEPTION(crypto::CryptoException() << errinfo_comment("Key encryption failed."));
ret["ciphertext"] = toHex(cipherText);
// and mac.
h256 mac = sha3(derivedKey.ref().cropped(16, 16).toBytes() + cipherText);
ret["mac"] = toHex(mac.ref());
return js::write_string(js::mValue(ret), true);
}
bytesSec SecretStore::decrypt(string const& _v, string const& _pass)
{
js::mObject o;
{
js::mValue ov;
js::read_string(_v, ov);
o = ov.get_obj();
}
// derive key
bytesSec derivedKey;
if (o["kdf"].get_str() == "pbkdf2")
{
auto params = o["kdfparams"].get_obj();
if (params["prf"].get_str() != "hmac-sha256")
{
cwarn << "Unknown PRF for PBKDF2" << params["prf"].get_str() << "not supported.";
return bytesSec();
}
unsigned iterations = params["c"].get_int();
bytes salt = fromHex(params["salt"].get_str());
derivedKey = pbkdf2(_pass, salt, iterations, params["dklen"].get_int());
}
else if (o["kdf"].get_str() == "scrypt")
{
auto p = o["kdfparams"].get_obj();
derivedKey = scrypt(_pass, fromHex(p["salt"].get_str()), p["n"].get_int(), p["r"].get_int(), p["p"].get_int(), p["dklen"].get_int());
}
else
{
cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported.";
return bytesSec();
}
if (derivedKey.size() < 32 && !(o.count("compat") && o["compat"].get_str() == "2"))
{
cwarn << "Derived key's length too short (<32 bytes)";
return bytesSec();
}
bytes cipherText = fromHex(o["ciphertext"].get_str());
// check MAC
if (o.count("mac"))
{
h256 mac(o["mac"].get_str());
h256 macExp;
if (o.count("compat") && o["compat"].get_str() == "2")
macExp = sha3(derivedKey.ref().cropped(derivedKey.size() - 16).toBytes() + cipherText);
else
macExp = sha3(derivedKey.ref().cropped(16, 16).toBytes() + cipherText);
if (mac != macExp)
{
cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac);
return bytesSec();
}
}
else if (o.count("sillymac"))
{
h256 mac(o["sillymac"].get_str());
h256 macExp = sha3(asBytes(o["sillymacjson"].get_str()) + derivedKey.ref().cropped(derivedKey.size() - 16).toBytes() + cipherText);
if (mac != macExp)
{
cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac);
return bytesSec();
}
}
else
cwarn << "No MAC. Proceeding anyway.";
// decrypt
if (o["cipher"].get_str() == "aes-128-ctr")
{
auto params = o["cipherparams"].get_obj();
h128 iv(params["iv"].get_str());
if (o.count("compat") && o["compat"].get_str() == "2")
{
SecureFixedHash<16> key(sha3Secure(derivedKey.ref().cropped(derivedKey.size() - 16)), h128::AlignRight);
return decryptSymNoAuth(key, iv, &cipherText);
}
else
return decryptSymNoAuth(SecureFixedHash<16>(derivedKey, h128::AlignLeft), iv, &cipherText);
}
else
{
cwarn << "Unknown cipher" << o["cipher"].get_str() << "not supported.";
return bytesSec();
}
}

125
libdevcrypto/SecretStore.h

@ -1,125 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file SecretStore.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <functional>
#include <mutex>
#include <libdevcore/FixedHash.h>
#include <libdevcore/FileSystem.h>
#include "Common.h"
namespace dev
{
enum class KDF {
PBKDF2_SHA256,
Scrypt,
};
/**
* Manages encrypted keys stored in a certain directory on disk. The keys are read into memory
* and changes to the keys are automatically synced to the directory.
* Each file stores exactly one key in a specific JSON format whose file name is derived from the
* UUID of the key.
* @note that most of the functions here affect the filesystem and throw exceptions on failure,
* and they also throw exceptions upon rare malfunction in the cryptographic functions.
*/
class SecretStore
{
public:
/// Construct a new SecretStore and read all keys in the given directory.
SecretStore(std::string const& _path = defaultPath());
/// @returns the secret key stored by the given @a _uuid.
/// @param _pass function that returns the password for the key.
/// @param _useCache if true, allow previously decrypted keys to be returned directly.
bytesSec secret(h128 const& _uuid, std::function<std::string()> const& _pass, bool _useCache = true) const;
/// @returns the secret key stored by the given @a _uuid.
/// @param _pass function that returns the password for the key.
static bytesSec secret(std::string const& _content, std::string const& _pass);
/// Imports the (encrypted) key stored in the file @a _file and copies it to the managed directory.
h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; }
/// Imports the (encrypted) key contained in the json formatted @a _content and stores it in
/// the managed directory.
h128 importKeyContent(std::string const& _content) { auto ret = readKeyContent(_content, std::string()); if (ret) save(); return ret; }
/// Imports the decrypted key given by @a _s and stores it, encrypted with
/// (a key derived from) the password @a _pass.
h128 importSecret(bytesSec const& _s, std::string const& _pass);
h128 importSecret(bytesConstRef _s, std::string const& _pass);
/// Decrypts and re-encrypts the key identified by @a _uuid.
bool recode(h128 const& _uuid, std::string const& _newPass, std::function<std::string()> const& _pass, KDF _kdf = KDF::Scrypt);
/// Removes the key specified by @a _uuid from both memory and disk.
void kill(h128 const& _uuid);
/// Returns the uuids of all stored keys.
std::vector<h128> keys() const { return keysOf(m_keys); }
/// @returns true iff we have the given key stored.
bool contains(h128 const& _k) const { return m_keys.count(_k); }
/// Clears all cached decrypted keys. The passwords have to be supplied in order to retrieve
/// secrets again after calling this function.
void clearCache() const;
/// Import the key from the file @a _file, but do not copy it to the managed directory yet.
/// @param _takeFileOwnership if true, deletes the file if it is not the canonical file for the
/// key (derived from its uuid).
h128 readKey(std::string const& _file, bool _takeFileOwnership);
/// Import the key contained in the json-encoded @a _content, but do not store it in the
/// managed directory.
/// @param _file if given, assume this file contains @a _content and delete it later, if it is
/// not the canonical file for the key (derived from the uuid).
h128 readKeyContent(std::string const& _content, std::string const& _file = std::string());
/// Store all keys in the directory @a _keysPath.
void save(std::string const& _keysPath);
/// Store all keys in the managed directory.
void save() { save(m_path); }
/// @returns the default path for the managed directory.
static std::string defaultPath() { return getDataDir("web3") + "/keys"; }
private:
struct EncryptedKey
{
std::string encryptedKey;
std::string filename;
};
/// Loads all keys in the given directory.
void load(std::string const& _keysPath);
void load() { load(m_path); }
/// Encrypts @a _v with a key derived from @a _pass or the empty string on error.
static std::string encrypt(bytesConstRef _v, std::string const& _pass, KDF _kdf = KDF::Scrypt);
/// Decrypts @a _v with a key derived from @a _pass or the empty byte array on error.
static bytesSec decrypt(std::string const& _v, std::string const& _pass);
/// Stores decrypted keys by uuid.
mutable std::unordered_map<h128, bytesSec> m_cached;
/// Stores encrypted keys together with the file they were loaded from by uuid.
std::unordered_map<h128, EncryptedKey> m_keys;
std::string m_path;
};
}

5036
libdevcrypto/WordList.cpp

File diff suppressed because it is too large

31
libdevcrypto/WordList.h

@ -1,31 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file WordList.h
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#pragma once
#include "Common.h"
namespace dev
{
extern strings const WordList;
}

27
libethcore/ABI.cpp

@ -1,27 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ABI.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "ABI.h"
using namespace std;
using namespace dev;
using namespace dev::eth;

100
libethcore/ABI.h

@ -1,100 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ABI.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/SHA3.h>
namespace dev
{
namespace eth
{
inline string32 toString32(std::string const& _s)
{
string32 ret;
for (unsigned i = 0; i < 32; ++i)
ret[i] = i < _s.size() ? _s[i] : 0;
return ret;
}
template <class T> struct ABISerialiser {};
template <unsigned N> struct ABISerialiser<FixedHash<N>> { static bytes serialise(FixedHash<N> 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<u256> { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } };
template <> struct ABISerialiser<u160> { static bytes serialise(u160 const& _t) { return bytes(12, 0) + h160(_t).asBytes(); } };
template <> struct ABISerialiser<string32> { static bytes serialise(string32 const& _t) { bytes ret; bytesConstRef((byte const*)_t.data(), 32).populate(bytesRef(&ret)); return ret; } };
template <> struct ABISerialiser<std::string>
{
static bytes serialise(std::string const& _t)
{
bytes ret = h256(u256(32)).asBytes() + h256(u256(_t.size())).asBytes();
ret.resize(ret.size() + (_t.size() + 31) / 32 * 32);
bytesConstRef(&_t).populate(bytesRef(&ret).cropped(64));
return ret;
}
};
inline bytes abiInAux() { return {}; }
template <class T, class ... U> bytes abiInAux(T const& _t, U const& ... _u)
{
return ABISerialiser<T>::serialise(_t) + abiInAux(_u ...);
}
template <class ... T> bytes abiIn(std::string _id, T const& ... _t)
{
return sha3(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...);
}
template <class T> struct ABIDeserialiser {};
template <unsigned N> struct ABIDeserialiser<FixedHash<N>> { static FixedHash<N> deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash<N> ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } };
template <> struct ABIDeserialiser<u256> { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian<u256>(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } };
template <> struct ABIDeserialiser<u160> { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian<u160>(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } };
template <> struct ABIDeserialiser<string32> { 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 <> struct ABIDeserialiser<std::string>
{
static std::string deserialise(bytesConstRef& io_t)
{
unsigned o = (uint16_t)u256(h256(io_t.cropped(0, 32)));
unsigned s = (uint16_t)u256(h256(io_t.cropped(o, 32)));
std::string ret;
ret.resize(s);
io_t.cropped(o + 32, s).populate(bytesRef((byte*)ret.data(), s));
io_t = io_t.cropped(32);
return ret;
}
};
template <class T> T abiOut(bytes const& _data)
{
bytesConstRef o(&_data);
return ABIDeserialiser<T>::deserialise(o);
}
template <class T> T abiOut(bytesConstRef& _data)
{
return ABIDeserialiser<T>::deserialise(_data);
}
}
}

117
libethcore/BasicAuthority.cpp

@ -1,117 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BasicAuthority.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <libdevcore/CommonJS.h>
#include "Exceptions.h"
#include "BasicAuthority.h"
#include "BlockInfo.h"
using namespace std;
using namespace dev;
using namespace eth;
AddressHash BasicAuthority::s_authorities;
bool BasicAuthority::BlockHeaderRaw::verify() const
{
return s_authorities.count(toAddress(recover(m_sig, hashWithout())));
}
bool BasicAuthority::BlockHeaderRaw::preVerify() const
{
return SignatureStruct(m_sig).isValid();
}
void BasicAuthority::BlockHeaderRaw::populateFromHeader(RLP const& _header, Strictness _s)
{
m_sig = _header[BlockInfo::BasicFields].toHash<Signature>();
// check it hashes according to proof of work or that it's the genesis block.
if (_s == CheckEverything && m_parentHash && !verify())
{
InvalidBlockNonce ex;
ex << errinfo_hash256(hashWithout());
ex << errinfo_difficulty(m_difficulty);
ex << errinfo_target(boundary());
BOOST_THROW_EXCEPTION(ex);
}
else if (_s == QuickNonce && m_parentHash && !preVerify())
{
InvalidBlockNonce ex;
ex << errinfo_hash256(hashWithout());
ex << errinfo_difficulty(m_difficulty);
BOOST_THROW_EXCEPTION(ex);
}
}
void BasicAuthority::BlockHeaderRaw::verifyParent(BlockHeaderRaw const& _parent)
{
(void)_parent;
}
void BasicAuthority::BlockHeaderRaw::populateFromParent(BlockHeaderRaw const& _parent)
{
(void)_parent;
}
StringHashMap BasicAuthority::BlockHeaderRaw::jsInfo() const
{
return { { "sig", toJS(m_sig) } };
}
class BasicAuthoritySealEngine: public SealEngineBase<BasicAuthority>
{
public:
void setSecret(Secret const& _s) { m_secret = _s; }
void generateSeal(BlockInfo const& _bi)
{
BasicAuthority::BlockHeader h(_bi);
h.m_sig = sign(m_secret, _bi.hashWithout());
RLPStream ret;
h.streamRLP(ret);
m_onSealGenerated(ret.out());
}
void onSealGenerated(std::function<void(bytes const&)> const& _f) { m_onSealGenerated = _f; }
bool isWorking() const { return false; }
WorkingProgress workingProgress() const { return WorkingProgress(); }
private:
virtual bool onOptionChanging(std::string const& _name, bytes const& _value)
{
RLP rlp(_value);
if (_name == "authorities")
BasicAuthority::s_authorities = rlp.toUnorderedSet<Address>();
else if (_name == "authority")
m_secret = Secret(rlp.toHash<h256>());
else
return false;
return true;
}
Secret m_secret;
std::function<void(bytes const& s)> m_onSealGenerated;
};
SealEngineFace* BasicAuthority::createSealEngine()
{
return new BasicAuthoritySealEngine;
}

95
libethcore/BasicAuthority.h

@ -1,95 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BasicAuthority.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* Determines the PoW algorithm.
*/
#pragma once
#include <libdevcore/RLP.h>
#include <libdevcrypto/Common.h>
#include "BlockInfo.h"
#include "Common.h"
#include "Sealer.h"
class BasicAuthoritySeal;
class BasicAuthoritySealEngine;
namespace dev
{
namespace eth
{
/**
* The proof of work algorithm base type.
*
* Must implement a basic templated interface, including:
* typename Result
* typename Solution
* typename CPUMiner
* typename GPUMiner
* typename CUDAMiner
* and a few others. TODO
*/
class BasicAuthority
{
friend class ::BasicAuthoritySealEngine;
public:
static std::string name() { return "BasicAuthority"; }
static unsigned revision() { return 0; }
static SealEngineFace* createSealEngine();
class BlockHeaderRaw: public BlockInfo
{
friend class ::BasicAuthoritySealEngine;
public:
static const unsigned SealFields = 1;
bool verify() const;
bool preVerify() const;
Signature sig() const { return m_sig; }
StringHashMap jsInfo() const;
protected:
BlockHeaderRaw() = default;
BlockHeaderRaw(BlockInfo const& _bi): BlockInfo(_bi) {}
void populateFromHeader(RLP const& _header, Strictness _s);
void populateFromParent(BlockHeaderRaw const& _parent);
void verifyParent(BlockHeaderRaw const& _parent);
void streamRLPFields(RLPStream& _s) const { _s << m_sig; }
void clear() { m_sig = Signature(); }
void noteDirty() const {}
private:
Signature m_sig;
};
using BlockHeader = BlockHeaderPolished<BlockHeaderRaw>;
private:
static AddressHash s_authorities;
};
}
}

2
libethcore/CMakeLists.txt

@ -28,11 +28,9 @@ endif ()
if (ETHASHCUDA)
target_link_libraries(${EXECUTABLE} ethash-cuda)
endif ()
target_link_libraries(${EXECUTABLE} devcrypto)
if (CPUID_FOUND)
target_link_libraries(${EXECUTABLE} ${CPUID_LIBRARIES})
endif ()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

13
libethcore/Common.cpp

@ -27,7 +27,6 @@
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h>
#include <libdevcore/SHA3.h>
#include "ICAP.h"
#include "Exceptions.h"
#include "Params.h"
#include "BlockInfo.h"
@ -41,26 +40,14 @@ namespace eth
{
const unsigned c_protocolVersion = 61;
#if ETH_FATDB
const unsigned c_minorProtocolVersion = 3;
const unsigned c_databaseBaseVersion = 9;
const unsigned c_databaseVersionModifier = 1;
#else
const unsigned c_minorProtocolVersion = 2;
const unsigned c_databaseBaseVersion = 9;
const unsigned c_databaseVersionModifier = 0;
#endif
const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (23 << 9);
Address toAddress(std::string const& _s)
{
try
{
eth::ICAP i = eth::ICAP::decoded(_s);
return i.direct();
}
catch (eth::InvalidICAP&) {}
try
{
auto b = fromHex(_s.substr(0, 2) == "0x" ? _s.substr(2) : _s, WhenError::Throw);

6
libethcore/Common.h

@ -26,8 +26,8 @@
#include <string>
#include <functional>
#include <libdevcore/Common.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/FixedHash.h>
#include <libdevcrypto/Common.h>
namespace dev
{
@ -57,6 +57,10 @@ Network resetNetwork(Network _n);
/// User-friendly string representation of the amount _b in wei.
std::string formatBalance(bigint const& _b);
/// An Ethereum address: 20 bytes.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Address = h160;
DEV_SIMPLE_EXCEPTION(InvalidAddress);
/// Convert the given string into an address.

78
libethcore/CommonJS.cpp

@ -1,78 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CommonJS.cpp
* @authors:
* Gav Wood <i@gavwood.com>
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
#include "CommonJS.h"
namespace dev
{
std::string prettyU256(u256 _n, bool _abridged)
{
unsigned inc = 0;
std::string raw;
std::ostringstream s;
if (!(_n >> 64))
s << " " << (uint64_t)_n << " (0x" << std::hex << (uint64_t)_n << ")";
else if (!~(_n >> 64))
s << " " << (int64_t)_n << " (0x" << std::hex << (int64_t)_n << ")";
else if ((_n >> 160) == 0)
{
Address a = right160(_n);
std::string n;
if (_abridged)
n = a.abridged();
else
n = toHex(a.ref());
if (n.empty())
s << "0";
else
s << _n << "(0x" << n << ")";
}
else if ((raw = fromRaw((h256)_n, &inc)).size())
return "\"" + raw + "\"" + (inc ? " + " + std::to_string(inc) : "");
else
s << "" << (h256)_n;
return s.str();
}
namespace eth
{
BlockNumber jsToBlockNumber(std::string const& _js)
{
if (_js == "latest")
return LatestBlock;
else if (_js == "earliest")
return 0;
else if (_js == "pending")
return PendingBlock;
else
return (unsigned)jsToInt(_js);
}
}
}

61
libethcore/CommonJS.h

@ -1,61 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CommonJS.h
* @authors:
* Gav Wood <i@gavwood.com>
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
#pragma once
#include <string>
#include <libdevcore/CommonJS.h>
#include <libdevcrypto/Common.h>
#include "Common.h"
// devcrypto
namespace dev
{
/// Leniently convert string to Public (h512). Accepts integers, "0x" prefixing, non-exact length.
inline Public jsToPublic(std::string const& _s) { return jsToFixed<sizeof(dev::Public)>(_s); }
/// Leniently convert string to Secret (h256). Accepts integers, "0x" prefixing, non-exact length.
inline Secret jsToSecret(std::string const& _s) { h256 d = jsToFixed<sizeof(dev::Secret)>(_s); Secret ret(d); d.ref().cleanse(); return ret; }
/// Leniently convert string to Address (h160). Accepts integers, "0x" prefixing, non-exact length.
inline Address jsToAddress(std::string const& _s) { return eth::toAddress(_s); }
/// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256.
std::string prettyU256(u256 _n, bool _abridged = true);
}
// ethcore
namespace dev
{
namespace eth
{
/// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest".
BlockNumber jsToBlockNumber(std::string const& _js);
}
}

1
libethcore/Ethash.cpp

@ -32,7 +32,6 @@
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonJS.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcore/FileSystem.h>
#include <libethash/ethash.h>
#include <libethash/internal.h>

1
libethcore/EthashAux.cpp

@ -29,7 +29,6 @@
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h>
#include <libethash/internal.h>

167
libethcore/ICAP.cpp

@ -1,167 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ICAP.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "ICAP.h"
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string.hpp>
#include <libdevcore/Base64.h>
#include <libdevcore/SHA3.h>
#include "Exceptions.h"
#include "ABI.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace dev
{
namespace eth
{
string ICAP::iban(std::string _c, std::string _d)
{
boost::to_upper(_c);
boost::to_upper(_d);
auto totStr = _d + _c + "00";
bigint tot = 0;
for (char x: totStr)
if (x >= 'A')
tot = tot * 100 + x - 'A' + 10;
else
tot = tot * 10 + x - '0';
unsigned check = (unsigned)(u256)(98 - tot % 97);
ostringstream out;
out << _c << setfill('0') << setw(2) << check << _d;
return out.str();
}
std::pair<string, string> ICAP::fromIBAN(std::string _iban)
{
if (_iban.size() < 4)
return std::make_pair(string(), string());
boost::to_upper(_iban);
std::string c = _iban.substr(0, 2);
std::string d = _iban.substr(4);
if (iban(c, d) != _iban)
return std::make_pair(string(), string());
return make_pair(c, d);
}
Secret ICAP::createDirect()
{
Secret ret;
while (true)
{
ret = Secret::random();
if (!toAddress(ret)[0])
return ret;
}
}
ICAP ICAP::decoded(std::string const& _encoded)
{
ICAP ret;
std::string country;
std::string data;
std::tie(country, data) = fromIBAN(_encoded);
if (country != "XE")
BOOST_THROW_EXCEPTION(InvalidICAP());
if (data.size() == 30 || data.size() == 31)
{
ret.m_type = Direct;
// Direct ICAP
ret.m_direct = fromBase36<Address::size>(data);
}
else if (data.size() == 16)
{
ret.m_type = Indirect;
ret.m_asset = data.substr(0, 3);
if (ret.m_asset == "XET" || ret.m_asset == "ETH")
{
ret.m_institution = data.substr(3, 4);
ret.m_client = data.substr(7);
}
else
BOOST_THROW_EXCEPTION(InvalidICAP());
}
else
BOOST_THROW_EXCEPTION(InvalidICAP());
return ret;
}
std::string ICAP::encoded() const
{
if (m_type == Direct)
{
std::string d = toBase36<Address::size>(m_direct);
while (d.size() < 30) // always 34, sometimes 35.
d = "0" + d;
return iban("XE", d);
}
else if (m_type == Indirect)
{
if (
m_asset.find_first_not_of("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890") != string::npos ||
m_institution.find_first_not_of("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890") != string::npos ||
m_client.find_first_not_of("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890") != string::npos ||
m_asset.size() != 3 ||
(boost::to_upper_copy(m_asset) != "XET" && boost::to_upper_copy(m_asset) != "ETH") ||
m_institution.size() != 4 ||
m_client.size() != 9
)
BOOST_THROW_EXCEPTION(InvalidICAP());
return iban("XE", m_asset + m_institution + m_client);
}
else
BOOST_THROW_EXCEPTION(InvalidICAP());
}
pair<Address, bytes> ICAP::lookup(std::function<bytes(Address, bytes)> const& _call, Address const& _reg) const
{
auto resolve = [&](string const& s)
{
vector<string> ss;
boost::algorithm::split(ss, s, boost::is_any_of("/"));
Address r = _reg;
for (unsigned i = 0; i < ss.size() - 1; ++i)
r = abiOut<Address>(_call(r, abiIn("subRegistrar(bytes32)", toString32(ss[i]))));
return abiOut<Address>(_call(r, abiIn("addr(bytes32)", toString32(ss.back()))));
};
if (m_asset == "XET")
{
Address a = resolve(m_institution);
bytes d = abiIn("deposit(uint64)", fromBase36<8>(m_client));
return make_pair(a, d);
}
else if (m_asset == "ETH")
{
if (m_institution == "XREG")
return make_pair(resolve(m_client), bytes());
else if (m_institution[0] != 'X')
return make_pair(resolve(m_institution + "/" + m_client), bytes());
else
BOOST_THROW_EXCEPTION(InterfaceNotSupported("ICAP::lookup(), bad institution"));
}
BOOST_THROW_EXCEPTION(InterfaceNotSupported("ICAP::lookup(), bad asset"));
}
}
}

106
libethcore/ICAP.h

@ -1,106 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ICAP.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* Ethereum-specific data structures & algorithms.
*/
#pragma once
#include <string>
#include <functional>
#include <boost/algorithm/string/case_conv.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/FixedHash.h>
#include "Common.h"
namespace dev
{
namespace eth
{
DEV_SIMPLE_EXCEPTION(InvalidICAP);
/**
* @brief Encapsulation of an ICAP address.
* Can be encoded, decoded, looked-up and inspected.
*/
class ICAP
{
public:
/// Construct null ICAP object.
ICAP() = default;
/// Construct a direct ICAP object for given target address. Must have a zero first byte.
ICAP(Address const& _target): m_type(Direct), m_direct(_target) {}
/// Construct an indirect ICAP object for given client and institution names.
ICAP(std::string const& _client, std::string const& _inst): m_type(Indirect), m_client(boost::algorithm::to_upper_copy(_client)), m_institution(boost::algorithm::to_upper_copy(_inst)), m_asset("XET") {}
/// Construct an indirect ICAP object for given client, institution and asset names. You generally don't want to use this.
ICAP(std::string const& _c, std::string const& _i, std::string const& _a): m_type(Indirect), m_client(boost::algorithm::to_upper_copy(_c)), m_institution(boost::algorithm::to_upper_copy(_i)), m_asset(boost::algorithm::to_upper_copy(_a)) {}
/// Type of ICAP address.
enum Type
{
Invalid,
Direct,
Indirect
};
/// Create a direct address for ICAP.
static Secret createDirect();
/// @returns IBAN encoding of client and data.
static std::string iban(std::string _c, std::string _d);
/// @returns Client and data from given IBAN address.
static std::pair<std::string, std::string> fromIBAN(std::string _iban);
/// @returns the ICAP object for the ICAP address given.
static ICAP decoded(std::string const& _encoded);
/// @returns the encoded ICAP address.
std::string encoded() const;
/// @returns type of ICAP.
Type type() const { return m_type; }
/// @returns target address. Only valid when type() == Direct.
Address const& direct() const { return m_type == Direct ? m_direct : ZeroAddress; }
/// @returns asset. Only valid when type() == Indirect.
std::string const& asset() const { return m_type == Indirect ? m_asset : EmptyString; }
/// @returns target name. Only valid when type() == Indirect and asset() == "ETH".
std::string const& target() const { return m_type == Indirect && m_asset == "ETH" ? m_client : EmptyString; }
/// @returns institution name. Only valid when type() == Indirect and asset() == "XET".
std::string const& institution() const { return m_type == Indirect && m_asset == "XET" ? m_institution : EmptyString; }
/// @returns client name. Only valid when type() == Indirect and asset() == "XET".
std::string const& client() const { return m_type == Indirect && m_asset == "XET" ? m_client : EmptyString; }
/// @returns target address. Always valid, but requires the Registry address and a function to make calls.
std::pair<Address, bytes> address(std::function<bytes(Address, bytes)> const& _call, Address const& _reg) const { return m_type == Direct ? make_pair(direct(), bytes()) : m_type == Indirect ? lookup(_call, _reg) : make_pair(Address(), bytes()); }
/// @returns target address. Looks up through the given Registry and call function. Only valid when type() == Indirect.
std::pair<Address, bytes> lookup(std::function<bytes(Address, bytes)> const& _call, Address const& _reg) const;
private:
Type m_type = Invalid;
Address m_direct;
std::string m_client;
std::string m_institution;
std::string m_asset;
};
}
}

409
libethcore/KeyManager.cpp

@ -1,409 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file KeyManager.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "KeyManager.h"
#include <thread>
#include <mutex>
#include <boost/filesystem.hpp>
#include <test/JsonSpiritHeaders.h>
#include <libdevcore/Log.h>
#include <libdevcore/Guards.h>
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
using namespace std;
using namespace dev;
using namespace eth;
namespace js = json_spirit;
namespace fs = boost::filesystem;
KeyManager::KeyManager(string const& _keysFile, string const& _secretsPath):
m_keysFile(_keysFile), m_store(_secretsPath)
{}
KeyManager::~KeyManager()
{}
bool KeyManager::exists() const
{
return !contents(m_keysFile + ".salt").empty() && !contents(m_keysFile).empty();
}
void KeyManager::create(string const& _pass)
{
m_defaultPasswordDeprecated = asString(h256::random().asBytes());
write(_pass, m_keysFile);
}
bool KeyManager::recode(Address const& _address, string const& _newPass, string const& _hint, function<string()> const& _pass, KDF _kdf)
{
noteHint(_newPass, _hint);
h128 u = uuid(_address);
if (!store().recode(u, _newPass, [&](){ return getPassword(u, _pass); }, _kdf))
return false;
m_keyInfo[_address].passHash = hashPassword(_newPass);
write();
return true;
}
bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, function<string()> const& _pass, KDF _kdf)
{
h128 u = uuid(_address);
string p;
if (_newPass == SemanticPassword::Existing)
p = getPassword(u, _pass);
else if (_newPass == SemanticPassword::Master)
p = defaultPassword();
else
return false;
return recode(_address, p, string(), _pass, _kdf);
}
bool KeyManager::load(string const& _pass)
{
try
{
bytes salt = contents(m_keysFile + ".salt");
bytes encKeys = contents(m_keysFile);
if (encKeys.empty())
return false;
m_keysFileKey = SecureFixedHash<16>(pbkdf2(_pass, salt, 262144, 16));
bytesSec bs = decryptSymNoAuth(m_keysFileKey, h128(), &encKeys);
RLP s(bs.ref());
unsigned version = unsigned(s[0]);
if (version == 1)
{
for (auto const& i: s[1])
{
h128 uuid(i[1]);
Address addr(i[0]);
if (uuid)
{
if (m_store.contains(uuid))
{
m_addrLookup[addr] = uuid;
m_uuidLookup[uuid] = addr;
m_keyInfo[addr] = KeyInfo(h256(i[2]), string(i[3]), i.itemCount() > 4 ? string(i[4]) : "");
}
else
cwarn << "Missing key:" << uuid << addr;
}
else
{
// TODO: brain wallet.
m_keyInfo[addr] = KeyInfo(h256(i[2]), string(i[3]), i.itemCount() > 4 ? string(i[4]) : "");
}
// cdebug << toString(addr) << toString(uuid) << toString((h256)i[2]) << (string)i[3];
}
for (auto const& i: s[2])
m_passwordHint[h256(i[0])] = string(i[1]);
m_defaultPasswordDeprecated = string(s[3]);
}
// cdebug << hashPassword(m_password) << toHex(m_password);
cachePassword(m_defaultPasswordDeprecated);
// cdebug << hashPassword(asString(m_key.ref())) << m_key.hex();
cachePassword(asString(m_keysFileKey.ref()));
// cdebug << hashPassword(_pass) << _pass;
m_master = hashPassword(_pass);
cachePassword(_pass);
return true;
}
catch (...)
{
return false;
}
}
Secret KeyManager::secret(Address const& _address, function<string()> const& _pass) const
{
auto it = m_keyInfo.find(_address);
if (it == m_keyInfo.end())
return Secret();
if (m_addrLookup.count(_address))
return secret(m_addrLookup.at(_address), _pass);
else
return brain(_pass());
}
Secret KeyManager::secret(h128 const& _uuid, function<string()> const& _pass) const
{
return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); }));
}
string KeyManager::getPassword(h128 const& _uuid, function<string()> const& _pass) const
{
h256 ph;
auto ait = m_uuidLookup.find(_uuid);
if (ait != m_uuidLookup.end())
{
auto kit = m_keyInfo.find(ait->second);
if (kit != m_keyInfo.end())
ph = kit->second.passHash;
}
return getPassword(ph, _pass);
}
string KeyManager::getPassword(h256 const& _passHash, function<string()> const& _pass) const
{
auto it = m_cachedPasswords.find(_passHash);
if (it != m_cachedPasswords.end())
return it->second;
for (unsigned i = 0; i < 10; ++i)
{
string p = _pass();
if (p.empty())
break;
if (_passHash == UnknownPassword || hashPassword(p) == _passHash)
{
cachePassword(p);
return p;
}
}
return string();
}
h128 KeyManager::uuid(Address const& _a) const
{
auto it = m_addrLookup.find(_a);
if (it == m_addrLookup.end())
return h128();
return it->second;
}
Address KeyManager::address(h128 const& _uuid) const
{
auto it = m_uuidLookup.find(_uuid);
if (it == m_uuidLookup.end())
return Address();
return it->second;
}
h128 KeyManager::import(Secret const& _s, string const& _accountName, string const& _pass, string const& _passwordHint)
{
Address addr = KeyPair(_s).address();
auto passHash = hashPassword(_pass);
cachePassword(_pass);
m_passwordHint[passHash] = _passwordHint;
auto uuid = m_store.importSecret(_s.asBytesSec(), _pass);
m_keyInfo[addr] = KeyInfo{passHash, _accountName, ""};
m_addrLookup[addr] = uuid;
m_uuidLookup[uuid] = addr;
write(m_keysFile);
return uuid;
}
Secret KeyManager::brain(string const& _seed)
{
h256 r = sha3(_seed);
for (auto i = 0; i < 16384; ++i)
r = sha3(r);
Secret ret(r);
r.ref().cleanse();
while (toAddress(ret)[0])
ret = sha3(ret);
return ret;
}
Secret KeyManager::subkey(Secret const& _s, unsigned _index)
{
RLPStream out(2);
out << _s.ref();
out << _index;
bytesSec r;
out.swapOut(r.writable());
return sha3(r);
}
Address KeyManager::importBrain(string const& _seed, string const& _accountName, string const& _passwordHint)
{
Address addr = toAddress(brain(_seed));
m_keyInfo[addr].accountName = _accountName;
m_keyInfo[addr].passwordHint = _passwordHint;
write();
return addr;
}
void KeyManager::importExistingBrain(Address const& _a, string const& _accountName, string const& _passwordHint)
{
m_keyInfo[_a].accountName = _accountName;
m_keyInfo[_a].passwordHint = _passwordHint;
write();
}
void KeyManager::importExisting(h128 const& _uuid, string const& _info, string const& _pass, string const& _passwordHint)
{
bytesSec key = m_store.secret(_uuid, [&](){ return _pass; });
if (key.empty())
return;
Address a = KeyPair(Secret(key)).address();
auto passHash = hashPassword(_pass);
if (!m_cachedPasswords.count(passHash))
cachePassword(_pass);
importExisting(_uuid, _info, a, passHash, _passwordHint);
}
void KeyManager::importExisting(h128 const& _uuid, string const& _accountName, Address const& _address, h256 const& _passHash, string const& _passwordHint)
{
if (!m_passwordHint.count(_passHash))
m_passwordHint[_passHash] = _passwordHint;
m_uuidLookup[_uuid] = _address;
m_addrLookup[_address] = _uuid;
m_keyInfo[_address].passHash = _passHash;
m_keyInfo[_address].accountName = _accountName;
write(m_keysFile);
}
void KeyManager::kill(Address const& _a)
{
auto id = m_addrLookup[_a];
m_uuidLookup.erase(id);
m_addrLookup.erase(_a);
m_keyInfo.erase(_a);
m_store.kill(id);
write(m_keysFile);
}
KeyPair KeyManager::presaleSecret(std::string const& _json, function<string(bool)> const& _password)
{
js::mValue val;
json_spirit::read_string(_json, val);
auto obj = val.get_obj();
string p = _password(true);
if (obj["encseed"].type() == js::str_type)
{
auto encseed = fromHex(obj["encseed"].get_str());
KeyPair k;
for (bool gotit = false; !gotit;)
{
gotit = true;
k = KeyPair::fromEncryptedSeed(&encseed, p);
if (obj["ethaddr"].type() == js::str_type)
{
Address a(obj["ethaddr"].get_str());
Address b = k.address();
if (a != b)
{
if ((p = _password(false)).empty())
BOOST_THROW_EXCEPTION(PasswordUnknown());
else
gotit = false;
}
}
}
return k;
}
else
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("encseed type is not js::str_type"));
}
Addresses KeyManager::accounts() const
{
Addresses ret;
ret.reserve(m_keyInfo.size());
for (auto const& i: m_keyInfo)
ret.push_back(i.first);
return ret;
}
bool KeyManager::hasAccount(Address const& _address) const
{
return m_keyInfo.count(_address);
}
string const& KeyManager::accountName(Address const& _address) const
{
try
{
return m_keyInfo.at(_address).accountName;
}
catch (...)
{
return EmptyString;
}
}
string const& KeyManager::passwordHint(Address const& _address) const
{
try
{
auto& info = m_keyInfo.at(_address);
if (info.passwordHint.size())
return info.passwordHint;
return m_passwordHint.at(info.passHash);
}
catch (...)
{
return EmptyString;
}
}
h256 KeyManager::hashPassword(string const& _pass) const
{
// TODO SECURITY: store this a bit more securely; Scrypt perhaps?
return h256(pbkdf2(_pass, asBytes(m_defaultPasswordDeprecated), 262144, 32).makeInsecure());
}
void KeyManager::cachePassword(string const& _password) const
{
m_cachedPasswords[hashPassword(_password)] = _password;
}
bool KeyManager::write(string const& _keysFile) const
{
if (!m_keysFileKey)
return false;
write(m_keysFileKey, _keysFile);
return true;
}
void KeyManager::write(string const& _pass, string const& _keysFile) const
{
bytes salt = h256::random().asBytes();
writeFile(_keysFile + ".salt", salt, true);
auto key = SecureFixedHash<16>(pbkdf2(_pass, salt, 262144, 16));
cachePassword(_pass);
m_master = hashPassword(_pass);
write(key, _keysFile);
}
void KeyManager::write(SecureFixedHash<16> const& _key, string const& _keysFile) const
{
RLPStream s(4);
s << 1; // version
s.appendList(accounts().size());
for (auto const& address: accounts())
{
h128 id = uuid(address);
auto const& ki = m_keyInfo.at(address);
s.appendList(5) << address << id << ki.passHash << ki.accountName << ki.passwordHint;
}
s.appendList(m_passwordHint.size());
for (auto const& i: m_passwordHint)
s.appendList(2) << i.first << i.second;
s.append(m_defaultPasswordDeprecated);
writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out()), true);
m_keysFileKey = _key;
cachePassword(defaultPassword());
}

186
libethcore/KeyManager.h

@ -1,186 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file KeyManager.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <functional>
#include <mutex>
#include <libdevcore/FileSystem.h>
#include <libdevcore/CommonData.h>
#include <libdevcrypto/SecretStore.h>
namespace dev
{
namespace eth
{
class PasswordUnknown: public Exception {};
struct KeyInfo
{
KeyInfo() = default;
KeyInfo(h256 const& _passHash, std::string const& _accountName, std::string const& _passwordHint = std::string()): passHash(_passHash), accountName(_accountName), passwordHint(_passwordHint) {}
/// Hash of the password or h256() / UnknownPassword if unknown.
h256 passHash;
/// Name of the key, or JSON key info if begins with '{'.
std::string accountName;
/// Hint of the password. Alternative place for storage than the hash-based lookup.
std::string passwordHint;
};
static h256 const UnknownPassword;
/// Password query function that never returns a password.
static auto const DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); };
enum class SemanticPassword
{
Existing,
Master
};
// TODO: This one is specifically for Ethereum, but we can make it generic in due course.
// TODO: hidden-partition style key-store.
/**
* @brief High-level manager of password-encrypted keys for Ethereum.
* Usage:
*
* Call exists() to check whether there is already a database. If so, get the master password from
* the user and call load() with it. If not, get a new master password from the user (get them to type
* it twice and keep some hint around!) and call create() with it.
*
* Uses a "key file" (and a corresponding .salt file) that contains encrypted information about the keys and
* a directory called "secrets path" that contains a file for each key.
*/
class KeyManager
{
public:
KeyManager(std::string const& _keysFile = defaultPath(), std::string const& _secretsPath = SecretStore::defaultPath());
~KeyManager();
void setKeysFile(std::string const& _keysFile) { m_keysFile = _keysFile; }
std::string const& keysFile() const { return m_keysFile; }
bool exists() const;
void create(std::string const& _pass);
bool load(std::string const& _pass);
void save(std::string const& _pass) const { write(_pass, m_keysFile); }
void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; }
void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordHint[hashPassword(_pass)] = _hint; }
bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); }
/// @returns the list of account addresses.
Addresses accounts() const;
/// @returns a hashset of all account addresses.
AddressHash accountsHash() const { return AddressHash() + accounts(); }
bool hasAccount(Address const& _address) const;
/// @returns the human-readable name or json-encoded info of the account for the given address.
std::string const& accountName(Address const& _address) const;
/// @returns the password hint for the account for the given address;
std::string const& passwordHint(Address const& _address) const;
/// @returns true if the given address has a key (UUID) associated with it. Equivalent to !!uuid(_a)
/// If the address has no key, it could be a brain wallet.
bool haveKey(Address const& _a) const { return m_addrLookup.count(_a); }
/// @returns the uuid of the key for the address @a _a or the empty hash on error.
h128 uuid(Address const& _a) const;
/// @returns the address corresponding to the key with uuid @a _uuid or the zero address on error.
Address address(h128 const& _uuid) const;
h128 import(Secret const& _s, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint);
h128 import(Secret const& _s, std::string const& _accountName) { return import(_s, _accountName, defaultPassword(), std::string()); }
Address importBrain(std::string const& _seed, std::string const& _accountName, std::string const& _seedHint);
void importExistingBrain(Address const& _a, std::string const& _accountName, std::string const& _seedHint);
SecretStore& store() { return m_store; }
void importExisting(h128 const& _uuid, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint);
void importExisting(h128 const& _uuid, std::string const& _accountName) { importExisting(_uuid, _accountName, defaultPassword(), std::string()); }
void importExisting(h128 const& _uuid, std::string const& _accountName, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passwordHint = std::string());
/// @returns the secret key associated with an address provided the password query
/// function @a _pass or the zero-secret key on error.
Secret secret(Address const& _address, std::function<std::string()> const& _pass = DontKnowThrow) const;
/// @returns the secret key associated with the uuid of a key provided the password query
/// function @a _pass or the zero-secret key on error.
Secret secret(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const;
bool recode(Address const& _address, SemanticPassword _newPass, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
bool recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function<std::string()> const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt);
void kill(h128 const& _id) { kill(address(_id)); }
void kill(Address const& _a);
static std::string defaultPath() { return getDataDir("ethereum") + "/keys.info"; }
/// Extracts the secret key from the presale wallet.
static KeyPair presaleSecret(std::string const& _json, std::function<std::string(bool)> const& _password);
/// @returns the brainwallet secret for the given seed.
static Secret brain(std::string const& _seed);
/// @returns the HD subkey for a given key.
static Secret subkey(Secret const& _s, unsigned _index);
private:
std::string getPassword(h128 const& _uuid, std::function<std::string()> const& _pass = DontKnowThrow) const;
std::string getPassword(h256 const& _passHash, std::function<std::string()> const& _pass = DontKnowThrow) const;
std::string defaultPassword(std::function<std::string()> const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); }
h256 hashPassword(std::string const& _pass) const;
/// Stores the password by its hash in the password cache.
void cachePassword(std::string const& _password) const;
// Only use if previously loaded ok.
// @returns false if wasn't previously loaded ok.
bool write() const { return write(m_keysFile); }
bool write(std::string const& _keysFile) const;
void write(std::string const& _pass, std::string const& _keysFile) const; // TODO: all passwords should be a secure string.
void write(SecureFixedHash<16> const& _key, std::string const& _keysFile) const;
// Ethereum keys.
/// Mapping key uuid -> address.
std::unordered_map<h128, Address> m_uuidLookup;
/// Mapping address -> key uuid.
std::unordered_map<Address, h128> m_addrLookup;
/// Mapping address -> key info.
std::unordered_map<Address, KeyInfo> m_keyInfo;
/// Mapping password hash -> password hint.
std::unordered_map<h256, std::string> m_passwordHint;
// Passwords that we're storing. Mapping password hash -> password.
mutable std::unordered_map<h256, std::string> m_cachedPasswords;
// DEPRECATED.
// Used to be the default password for keys in the keystore, stored in the keys file.
// Now the default password is based off the key of the keys file directly, so this is redundant
// except for the fact that people have existing keys stored with it. Leave for now until/unless
// we have an upgrade strategy.
std::string m_defaultPasswordDeprecated;
mutable std::string m_keysFile;
mutable SecureFixedHash<16> m_keysFileKey;
mutable h256 m_master;
SecretStore m_store;
};
}
}

132
libethcore/Transaction.cpp

@ -1,132 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionBase.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <libdevcore/vector_ref.h>
#include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Exceptions.h>
#include "Transaction.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
TransactionBase::TransactionBase(TransactionSkeleton const& _ts, Secret const& _s):
m_type(_ts.creation ? ContractCreation : MessageCall),
m_nonce(_ts.nonce),
m_value(_ts.value),
m_receiveAddress(_ts.to),
m_gasPrice(_ts.gasPrice),
m_gas(_ts.gas),
m_data(_ts.data),
m_sender(_ts.from)
{
if (_s)
sign(_s);
}
TransactionBase::TransactionBase(bytesConstRef _rlpData, CheckTransaction _checkSig)
{
int field = 0;
RLP rlp(_rlpData);
try
{
if (!rlp.isList())
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list"));
m_nonce = rlp[field = 0].toInt<u256>();
m_gasPrice = rlp[field = 1].toInt<u256>();
m_gas = rlp[field = 2].toInt<u256>();
m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash<Address>(RLP::VeryStrict);
m_value = rlp[field = 4].toInt<u256>();
if (!rlp[field = 5].isData())
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array"));
m_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt<byte>() - 27;
h256 r = rlp[field = 7].toInt<u256>();
h256 s = rlp[field = 8].toInt<u256>();
if (rlp.itemCount() > 9)
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP"));
m_vrs = SignatureStruct{ r, s, v };
if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid())
BOOST_THROW_EXCEPTION(InvalidSignature());
if (_checkSig == CheckTransaction::Everything)
m_sender = sender();
}
catch (Exception& _e)
{
_e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes()));
throw;
}
}
Address const& TransactionBase::safeSender() const noexcept
{
try
{
return sender();
}
catch (...)
{
cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information();
return ZeroAddress;
}
}
Address const& TransactionBase::sender() const
{
if (!m_sender)
{
auto p = recover(m_vrs, sha3(WithoutSignature));
if (!p)
BOOST_THROW_EXCEPTION(InvalidSignature());
m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
}
return m_sender;
}
void TransactionBase::sign(Secret const& _priv)
{
auto sig = dev::sign(_priv, sha3(WithoutSignature));
SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
if (sigStruct.isValid())
m_vrs = sigStruct;
}
void TransactionBase::streamRLP(RLPStream& _s, IncludeSignature _sig) const
{
if (m_type == NullTransaction)
return;
_s.appendList((_sig ? 3 : 0) + 6);
_s << m_nonce << m_gasPrice << m_gas;
if (m_type == MessageCall)
_s << m_receiveAddress;
else
_s << "";
_s << m_value << m_data;
if (_sig)
_s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
}

179
libethcore/Transaction.h

@ -1,179 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionBase.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include <libethcore/Common.h>
namespace dev
{
namespace eth
{
/// Named-boolean type to encode whether a signature be included in the serialisation process.
enum IncludeSignature
{
WithoutSignature = 0, ///< Do not include a signature.
WithSignature = 1, ///< Do include a signature.
};
enum class CheckTransaction
{
None,
Cheap,
Everything
};
/// Encodes a transaction, ready to be exported to or freshly imported from RLP.
class TransactionBase
{
public:
/// Constructs a null transaction.
TransactionBase() {}
/// Constructs a transaction from a transaction skeleton & optional secret.
TransactionBase(TransactionSkeleton const& _ts, Secret const& _s = Secret());
/// Constructs a signed message-call transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
/// Constructs a signed contract-creation transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
/// Constructs an unsigned message-call transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs an unsigned contract-creation transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs a transaction from the given RLP.
explicit TransactionBase(bytesConstRef _rlp, CheckTransaction _checkSig);
/// Constructs a transaction from the given RLP.
explicit TransactionBase(bytes const& _rlp, CheckTransaction _checkSig): TransactionBase(&_rlp, _checkSig) {}
/// Checks equality of transactions.
bool operator==(TransactionBase const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
/// Checks inequality of transactions.
bool operator!=(TransactionBase const& _c) const { return !operator==(_c); }
/// @returns sender of the transaction from the signature (and hash).
Address const& sender() const;
/// Like sender() but will never throw. @returns a null Address if the signature is invalid.
Address const& safeSender() const noexcept;
/// Force the sender to a particular value. This will result in an invalid transaction RLP.
void forceSender(Address const& _a) { m_sender = _a; }
/// @returns true if transaction is non-null.
explicit operator bool() const { return m_type != NullTransaction; }
/// @returns true if transaction is contract-creation.
bool isCreation() const { return m_type == ContractCreation; }
/// @returns true if transaction is message-call.
bool isMessageCall() const { return m_type == MessageCall; }
/// Serialises this transaction to an RLPStream.
void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const;
/// @returns the RLP serialisation of this transaction.
bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
/// @returns the SHA3 hash of the RLP serialisation of this transaction.
h256 sha3(IncludeSignature _sig = WithSignature) const { if (_sig == WithSignature && m_hashWith) return m_hashWith; RLPStream s; streamRLP(s, _sig); auto ret = dev::sha3(s.out()); if (_sig == WithSignature) m_hashWith = ret; return ret; }
/// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment().
u256 value() const { return m_value; }
/// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value().
u256 endowment() const { return m_value; }
/// @returns the base fee and thus the implied exchange rate of ETH to GAS.
u256 gasPrice() const { return m_gasPrice; }
/// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
u256 gas() const { return m_gas; }
/// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions).
Address receiveAddress() const { return m_receiveAddress; }
/// Synonym for receiveAddress().
Address to() const { return m_receiveAddress; }
/// Synonym for safeSender().
Address from() const { return safeSender(); }
/// @returns the data associated with this (message-call) transaction. Synonym for initCode().
bytes const& data() const { return m_data; }
/// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data().
bytes const& initCode() const { return m_data; }
/// @returns the transaction-count of the sender.
u256 nonce() const { return m_nonce; }
/// @returns the signature of the transaction. Encodes the sender.
SignatureStruct const& signature() const { return m_vrs; }
void sign(Secret const& _priv); ///< Sign the transaction.
protected:
/// Type of transaction.
enum Type
{
NullTransaction, ///< Null transaction.
ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored.
MessageCall ///< Transaction to invoke a message call - receiveAddress() is used.
};
Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction?
u256 m_nonce; ///< The transaction-count of the sender.
u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address m_receiveAddress; ///< The receiving address of the transaction.
u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender.
mutable h256 m_hashWith; ///< Cached hash of transaction with signature.
mutable Address m_sender; ///< Cached sender, determined from signature.
mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run.
};
/// Nice name for vector of Transaction.
using TransactionBases = std::vector<TransactionBase>;
/// Simple human-readable stream-shift operator.
inline std::ostream& operator<<(std::ostream& _out, TransactionBase const& _t)
{
_out << _t.sha3().abridged() << "{";
if (_t.receiveAddress())
_out << _t.receiveAddress().abridged();
else
_out << "[CREATE]";
_out << "/" << _t.data().size() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice();
_out << "<-" << _t.safeSender().abridged() << " #" << _t.nonce() << "}";
return _out;
}
}
}

95
libethereum/Account.cpp

@ -1,95 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Account.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Account.h"
#include <liblll/Compiler.h>
#include <test/JsonSpiritHeaders.h>
#include <libethcore/Common.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace js = json_spirit;
#pragma GCC diagnostic ignored "-Wunused-variable"
const h256 Account::c_contractConceptionCodeHash;
AccountMap dev::eth::jsonToAccountMap(std::string const& _json, AccountMaskMap* o_mask)
{
auto u256Safe = [](std::string const& s) -> u256 {
bigint ret(s);
if (ret >= bigint(1) << 256)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State value is equal or greater than 2**256") );
return (u256)ret;
};
std::unordered_map<Address, Account> ret;
js::mValue val;
json_spirit::read_string(_json, val);
for (auto account: val.get_obj().count("alloc") ? val.get_obj()["alloc"].get_obj() : val.get_obj())
{
Address a(fromHex(account.first));
auto o = account.second.get_obj();
u256 balance = 0;
bool haveBalance = (o.count("wei") || o.count("finney") || o.count("balance"));
if (o.count("wei"))
balance = u256Safe(o["wei"].get_str());
else if (o.count("finney"))
balance = u256Safe(o["finney"].get_str()) * finney;
else if (o.count("balance"))
balance = u256Safe(o["balance"].get_str());
bool haveCode = o.count("code");
if (haveCode)
{
ret[a] = Account(balance, Account::ContractConception);
if (o["code"].type() == json_spirit::str_type)
{
if (o["code"].get_str().find("0x") != 0)
ret[a].setCode(compileLLL(o["code"].get_str(), false));
else
ret[a].setCode(fromHex(o["code"].get_str().substr(2)));
}
else
cerr << "Error importing code of account " << a << "! Code field needs to be a string";
}
else
ret[a] = Account(balance, Account::NormalCreation);
bool haveStorage = o.count("storage");
if (haveStorage)
for (pair<string, js::mValue> const& j: o["storage"].get_obj())
ret[a].setStorage(u256(j.first), u256(j.second.get_str()));
bool haveNonce = o.count("nonce");
if (haveNonce)
for (auto i = 0; i < u256Safe(o["nonce"].get_str()); ++i)
ret[a].incNonce();
if (o_mask)
(*o_mask)[a] = AccountMask(haveBalance, haveNonce, haveCode, haveStorage);
}
return ret;
}

248
libethereum/Account.h

@ -1,248 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Account.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/TrieDB.h>
#include <libdevcore/SHA3.h>
#include <libethcore/Common.h>
namespace dev
{
namespace eth
{
/**
* Models the state of a single Ethereum account.
* Used to cache a portion of the full Ethereum state. State keeps a mapping of Address's to Accounts.
*
* Aside from storing the nonce and balance, the account may also be "dead" (where isAlive() returns false).
* This allows State to explicitly store the notion of a deleted account in it's cache. kill() can be used
* for this.
*
* For the account's storage, the class operates a cache. baseRoot() specifies the base state of the storage
* given as the Trie root to be looked up in the state database. Alterations beyond this base are specified
* in the overlay, stored in this class and retrieved with storageOverlay(). setStorage allows the overlay
* to be altered.
*
* The code handling explicitly supports a two-stage commit model needed for contract-creation. When creating
* a contract (running the initialisation code), the code of the account is considered empty. The attribute
* of emptiness can be retrieved with codeBearing(). After initialisation one must set the code accordingly;
* the code of the Account can be set with setCode(). To validate a setCode() call, this class records the
* state of being in contract-creation (and thus in a state where setCode may validly be called). It can be
* determined through isFreshCode().
*
* The code can be retrieved through code(), and its hash through codeHash(). codeHash() is only valid when
* the account is not in the contract-creation phase (i.e. when isFreshCode() returns false). This class
* supports populating code on-demand from the state database. To determine if the code has been prepopulated
* call codeCacheValid(). To populate the code, look it up with codeHash() and populate with noteCode().
*
* @todo: need to make a noteCodeCommitted().
*
* The constructor allows you to create an one of a number of "types" of accounts. The default constructor
* makes a dead account (this is ignored by State when writing out the Trie). Another three allow a basic
* or contract account to be specified along with an initial balance. The fina two allow either a basic or
* a contract account to be created with arbitrary values.
*/
class Account
{
public:
/// Type of account to create.
enum NewAccountType
{
/// Normal account.
NormalCreation,
/// Contract account - we place this object into the contract-creation state (and as such we
/// expect setCode(), but codeHash() won't work).
ContractConception
};
/// Changedness of account to create.
enum Changedness
{
/// Account starts as though it has been changed.
Changed,
/// Account starts as though it has not been changed.
Unchanged
};
/// Construct a dead Account.
Account() {}
/// Construct an alive Account, with given endowment, for either a normal (non-contract) account or for a
/// contract account in the
/// conception phase, where the code is not yet known.
Account(u256 _balance, NewAccountType _t, Changedness _c = Changed): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_balance(_balance), m_codeHash(_t == NormalCreation ? EmptySHA3 : c_contractConceptionCodeHash) {}
/// Explicit constructor for wierd cases of construction of a normal account.
Account(u256 _nonce, u256 _balance, Changedness _c = Changed): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance) {}
/// Explicit constructor for wierd cases of construction or a contract account.
Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash, Changedness _c): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance), m_storageRoot(_contractRoot), m_codeHash(_codeHash) { assert(_contractRoot); }
/// Kill this account. Useful for the suicide opcode. Following this call, isAlive() returns false.
void kill() { m_isAlive = false; m_storageOverlay.clear(); m_codeHash = EmptySHA3; m_storageRoot = EmptyTrie; m_balance = 0; m_nonce = 0; changed(); }
/// @returns true iff this object represents an account in the state. Returns false if this object
/// represents an account that should no longer exist in the trie (an account that never existed or was
/// suicided).
bool isAlive() const { return m_isAlive; }
/// @returns true if the account is unchanged from creation.
bool isDirty() const { return !m_isUnchanged; }
/// @returns the balance of this account. Can be altered in place.
u256& balance() { return m_balance; }
/// @returns the balance of this account.
u256 const& balance() const { return m_balance; }
/// Increments the balance of this account by the given amount. It's a bigint, so can be negative.
void addBalance(bigint _i) { if (!_i) return; m_balance = (u256)((bigint)m_balance + _i); changed(); }
/// @returns the nonce of the account. Can be altered in place.
u256& nonce() { return m_nonce; }
/// @returns the nonce of the account.
u256 const& nonce() const { return m_nonce; }
/// Increment the nonce of the account by one.
void incNonce() { m_nonce++; changed(); }
/// @returns the root of the trie (whose nodes are stored in the state db externally to this class)
/// which encodes the base-state of the account's storage (upon which the storage is overlaid).
h256 baseRoot() const { assert(m_storageRoot); return m_storageRoot; }
/// @returns the storage overlay as a simple hash map.
std::unordered_map<u256, u256> const& storageOverlay() const { return m_storageOverlay; }
/// Set a key/value pair in the account's storage. This actually goes into the overlay, for committing
/// to the trie later.
void setStorage(u256 _p, u256 _v) { m_storageOverlay[_p] = _v; changed(); }
/// @returns true if we are in the contract-conception state and setCode is valid to call.
bool isFreshCode() const { return m_codeHash == c_contractConceptionCodeHash; }
/// @returns true if we are either in the contract-conception state or if the account's code is not
/// empty.
bool codeBearing() const { return m_codeHash != EmptySHA3; }
/// @returns the hash of the account's code. Must only be called when isFreshCode() returns false.
h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; }
/// Sets the code of the account. Must only be called when isFreshCode() returns true.
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = std::move(_code); changed(); }
/// @returns true if the account's code is available through code().
bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); }
/// Specify to the object what the actual code is for the account. @a _code must have a SHA3 equal to
/// codeHash() and must only be called when isFreshCode() returns false.
void noteCode(bytesConstRef _code) { assert(sha3(_code) == m_codeHash); m_codeCache = _code.toBytes(); }
/// @returns the account's code. Must only be called when codeCacheValid returns true.
bytes const& code() const { assert(codeCacheValid()); return m_codeCache; }
private:
/// Note that we've altered the account.
void changed() { m_isUnchanged = false; }
/// Is this account existant? If not, it represents a deleted account.
bool m_isAlive = false;
/// True if we've not made any alteration to the account having been given it's properties directly.
bool m_isUnchanged = false;
/// Account's nonce.
u256 m_nonce = 0;
/// Account's balance.
u256 m_balance = 0;
/// The base storage root. Used with the state DB to give a base to the storage. m_storageOverlay is
/// overlaid on this and takes precedence for all values set.
h256 m_storageRoot = EmptyTrie;
/** If c_contractConceptionCodeHash then we're in the limbo where we're running the initialisation code.
* We expect a setCode() at some point later.
* If EmptySHA3, then m_code, which should be empty, is valid.
* If anything else, then m_code is valid iff it's not empty, otherwise, State::ensureCached() needs to
* be called with the correct args.
*/
h256 m_codeHash = EmptySHA3;
/// The map with is overlaid onto whatever storage is implied by the m_storageRoot in the trie.
std::unordered_map<u256, u256> m_storageOverlay;
/// The associated code for this account. The SHA3 of this should be equal to m_codeHash unless m_codeHash
/// equals c_contractConceptionCodeHash.
bytes m_codeCache;
/// Value for m_codeHash when this account is having its code determined.
static const h256 c_contractConceptionCodeHash;
};
class AccountMask
{
public:
AccountMask(bool _all = false):
m_hasBalance(_all),
m_hasNonce(_all),
m_hasCode(_all),
m_hasStorage(_all)
{}
AccountMask(
bool _hasBalance,
bool _hasNonce,
bool _hasCode,
bool _hasStorage
):
m_hasBalance(_hasBalance),
m_hasNonce(_hasNonce),
m_hasCode(_hasCode),
m_hasStorage(_hasStorage)
{}
bool allSet() const { return m_hasBalance && m_hasNonce && m_hasCode && m_hasStorage; }
bool hasBalance() const { return m_hasBalance; }
bool hasNonce() const { return m_hasNonce; }
bool hasCode() const { return m_hasCode; }
bool hasStorage() const { return m_hasStorage; }
private:
bool m_hasBalance;
bool m_hasNonce;
bool m_hasCode;
bool m_hasStorage;
};
using AccountMap = std::unordered_map<Address, Account>;
using AccountMaskMap = std::unordered_map<Address, AccountMask>;
AccountMap jsonToAccountMap(std::string const& _json, AccountMaskMap* o_mask = nullptr);
}
}

92
libethereum/AccountDiff.cpp

@ -1,92 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AccountDiff.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "AccountDiff.h"
#include <libdevcore/CommonIO.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
AccountChange AccountDiff::changeType() const
{
bool bn = (balance || nonce);
bool sc = (!storage.empty() || code);
return exist ? exist.from() ? AccountChange::Deletion : AccountChange::Creation : (bn && sc) ? AccountChange::All : bn ? AccountChange::Intrinsic: sc ? AccountChange::CodeStorage : AccountChange::None;
}
char const* dev::eth::lead(AccountChange _c)
{
switch (_c)
{
case AccountChange::None: return " ";
case AccountChange::Creation: return "+++";
case AccountChange::Deletion: return "XXX";
case AccountChange::Intrinsic: return " * ";
case AccountChange::CodeStorage: return "* *";
case AccountChange::All: return "***";
}
assert(false);
return "";
}
namespace dev {
std::ostream& operator<<(std::ostream& _out, dev::eth::AccountDiff const& _s)
{
if (!_s.exist.to())
return _out;
if (_s.nonce)
{
_out << std::dec << "#" << _s.nonce.to() << " ";
if (_s.nonce.from())
_out << "(" << std::showpos << (((bigint)_s.nonce.to()) - ((bigint)_s.nonce.from())) << std::noshowpos << ") ";
}
if (_s.balance)
{
_out << std::dec << _s.balance.to() << " ";
if (_s.balance.from())
_out << "(" << std::showpos << (((bigint)_s.balance.to()) - ((bigint)_s.balance.from())) << std::noshowpos << ") ";
}
if (_s.code)
_out << "$" << std::hex << nouppercase << _s.code.to() << " (" << _s.code.from() << ") ";
for (pair<u256, Diff<u256>> const& i: _s.storage)
if (!i.second.from())
_out << endl << " + " << (h256)i.first << ": " << std::hex << nouppercase << i.second.to();
else if (!i.second.to())
_out << endl << "XXX " << (h256)i.first << " (" << std::hex << nouppercase << i.second.from() << ")";
else
_out << endl << " * " << (h256)i.first << ": " << std::hex << nouppercase << i.second.to() << " (" << i.second.from() << ")";
return _out;
}
std::ostream& operator<<(std::ostream& _out, dev::eth::StateDiff const& _s)
{
_out << _s.accounts.size() << " accounts changed:" << endl;
dev::eth::AccountDiff d;
_out << d;
for (auto const& i: _s.accounts)
_out << lead(i.second.changeType()) << " " << i.first << ": " << i.second << endl;
return _out;
}
}

85
libethereum/AccountDiff.h

@ -1,85 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AccountDiff.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/Diff.h>
#include <libethcore/Common.h>
namespace dev
{
namespace eth
{
/// Type of change that an account can have from state to state.
enum class AccountChange
{
None, ///< Nothing changed at all.
Creation, ///< Account came into existance.
Deletion, ///< Account was deleted.
Intrinsic, ///< Account was already in existance and some internal aspect of the account altered such as balance, nonce or storage.
CodeStorage, ///< Account was already in existance and the code of the account changed.
All ///< Account was already in existance and all aspects of the account changed.
};
/// @returns a three-character code that expresses the type of change.
char const* lead(AccountChange _c);
/**
* @brief Stores the difference between two accounts (typically the same account at two times).
*
* In order to determine what about an account has altered, this struct can be used to specify
* alterations. Use changed() and changeType() to determine what, if anything, is different.
*
* Five members are accessible: to determine the nature of the changes.
*/
struct AccountDiff
{
/// @returns true if the account has changed at all.
inline bool changed() const { return storage.size() || code || nonce || balance || exist; }
/// @returns a three-character code that expresses the change.
AccountChange changeType() const;
Diff<bool> exist; ///< The account's existance; was it created/deleted or not?
Diff<u256> balance; ///< The account's balance; did it alter?
Diff<u256> nonce; ///< The account's nonce; did it alter?
std::map<u256, Diff<u256>> storage; ///< The account's storage addresses; each has its own Diff.
Diff<bytes> code; ///< The account's code; in general this should only have changed if exist also changed.
};
/**
* @brief Stores the difference between two states; this is just their encumbent accounts.
*/
struct StateDiff
{
std::map<Address, AccountDiff> accounts; ///< The state's account changes; each has its own AccountDiff.
};
}
/// Simple stream output for the StateDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::StateDiff const& _s);
/// Simple stream output for the AccountDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::AccountDiff const& _s);
}

15
libethereum/All.h

@ -1,15 +0,0 @@
#pragma once
#include "Account.h"
#include "CanonBlockChain.h"
#include "Client.h"
#include "Defaults.h"
#include "Executive.h"
#include "ExtVM.h"
#include "CommonNet.h"
#include "EthereumHost.h"
#include "EthereumPeer.h"
#include "State.h"
#include "Transaction.h"
#include "TransactionQueue.h"
#include "Utility.h"

100
libethereum/BasicGasPricer.cpp

@ -1,100 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BasicGasPricer.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <boost/math/distributions/normal.hpp>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include "BasicGasPricer.h"
#include "BlockChain.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
void BasicGasPricer::update(BlockChain const& _bc)
{
unsigned c = 0;
h256 p = _bc.currentHash();
m_gasPerBlock = _bc.info(p).gasLimit();
map<u256, u256> dist;
u256 total = 0;
// make gasPrice versus gasUsed distribution for the last 1000 blocks
while (c < 1000 && p)
{
BlockInfo bi = _bc.info(p);
if (bi.transactionsRoot() != EmptyTrie)
{
auto bb = _bc.block(p);
RLP r(bb);
BlockReceipts brs(_bc.receipts(bi.hash()));
size_t i = 0;
for (auto const& tr: r[1])
{
Transaction tx(tr.data(), CheckTransaction::None);
u256 gu = brs.receipts[i].gasUsed();
dist[tx.gasPrice()] += gu;
total += gu;
i++;
}
}
p = bi.parentHash();
++c;
}
// fill m_octiles with weighted gasPrices
if (total > 0)
{
m_octiles[0] = dist.begin()->first;
// calc mean
u256 mean = 0;
for (auto const& i: dist)
mean += i.first * i.second;
mean /= total;
// calc standard deviation
u256 sdSquared = 0;
for (auto const& i: dist)
sdSquared += i.second * (i.first - mean) * (i.first - mean);
sdSquared /= total;
if (sdSquared)
{
long double sd = sqrt(sdSquared.convert_to<long double>());
long double normalizedSd = sd / mean.convert_to<long double>();
// calc octiles normalized to gaussian distribution
boost::math::normal gauss(1.0, (normalizedSd > 0.01) ? normalizedSd : 0.01);
for (size_t i = 1; i < 8; i++)
m_octiles[i] = u256(mean.convert_to<long double>() * boost::math::quantile(gauss, i / 8.0));
m_octiles[8] = dist.rbegin()->first;
}
else
{
for (size_t i = 0; i < 9; i++)
m_octiles[i] = (i + 1) * mean / 5;
}
}
}

53
libethereum/BasicGasPricer.h

@ -1,53 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BasicGasPricer.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <array>
#include "GasPricer.h"
namespace dev
{
namespace eth
{
class BasicGasPricer: public GasPricer
{
public:
explicit BasicGasPricer(u256 _weiPerRef, u256 _refsPerBlock): m_weiPerRef(_weiPerRef), m_refsPerBlock(_refsPerBlock) {}
void setRefPrice(u256 _weiPerRef) { if ((bigint)m_refsPerBlock * _weiPerRef > std::numeric_limits<u256>::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_weiPerRef = _weiPerRef; }
void setRefBlockFees(u256 _refsPerBlock) { if ((bigint)m_weiPerRef * _refsPerBlock > std::numeric_limits<u256>::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_refsPerBlock = _refsPerBlock; }
u256 ask(Block const&) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; }
u256 bid(TransactionPriority _p = TransactionPriority::Medium) const override { return m_octiles[(int)_p] > 0 ? m_octiles[(int)_p] : (m_weiPerRef * m_refsPerBlock / m_gasPerBlock); }
void update(BlockChain const& _bc) override;
private:
u256 m_weiPerRef;
u256 m_refsPerBlock;
u256 m_gasPerBlock = 3141592;
std::array<u256, 9> m_octiles;
};
}
}

815
libethereum/Block.cpp

@ -1,815 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Block.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Block.h"
#include <ctime>
#include <boost/filesystem.hpp>
#include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/StructuredLogger.h>
#include <libdevcore/TrieHash.h>
#include <libevmcore/Instruction.h>
#include <libethcore/Exceptions.h>
#include <libethcore/Params.h>
#include <libevm/VMFactory.h>
#include "BlockChain.h"
#include "Defaults.h"
#include "ExtVM.h"
#include "Executive.h"
#include "CachedAddressState.h"
#include "CanonBlockChain.h"
#include "TransactionQueue.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace fs = boost::filesystem;
#define ctrace clog(BlockTrace)
#define ETH_TIMED_ENACTMENTS 0
static const unsigned c_maxSyncTransactions = 256;
const char* BlockSafeExceptions::name() { return EthViolet "" EthBlue ""; }
const char* BlockDetail::name() { return EthViolet "" EthWhite ""; }
const char* BlockTrace::name() { return EthViolet "" EthGray ""; }
const char* BlockChat::name() { return EthViolet "" EthWhite ""; }
Block::Block(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress):
m_state(_db, _bs),
m_beneficiary(_coinbaseAddress),
m_blockReward(c_blockReward)
{
m_previousBlock.clear();
m_currentBlock.clear();
// assert(m_state.root() == m_previousBlock.stateRoot());
}
Block::Block(Block const& _s):
m_state(_s.m_state),
m_transactions(_s.m_transactions),
m_receipts(_s.m_receipts),
m_transactionSet(_s.m_transactionSet),
m_previousBlock(_s.m_previousBlock),
m_currentBlock(_s.m_currentBlock),
m_beneficiary(_s.m_beneficiary),
m_blockReward(_s.m_blockReward)
{
m_precommit = m_state;
m_committedToMine = false;
}
Block& Block::operator=(Block const& _s)
{
if (&_s == this)
return *this;
m_state = _s.m_state;
m_transactions = _s.m_transactions;
m_receipts = _s.m_receipts;
m_transactionSet = _s.m_transactionSet;
m_previousBlock = _s.m_previousBlock;
m_currentBlock = _s.m_currentBlock;
m_beneficiary = _s.m_beneficiary;
m_blockReward = _s.m_blockReward;
m_precommit = m_state;
m_committedToMine = false;
return *this;
}
void Block::resetCurrent()
{
m_transactions.clear();
m_receipts.clear();
m_transactionSet.clear();
m_currentBlock = BlockInfo();
m_currentBlock.setCoinbaseAddress(m_beneficiary);
m_currentBlock.setTimestamp(max(m_previousBlock.timestamp() + 1, (u256)time(0)));
m_currentBlock.populateFromParent(m_previousBlock);
// TODO: check.
m_state.setRoot(m_previousBlock.stateRoot());
m_precommit = m_state;
m_committedToMine = false;
}
PopulationStatistics Block::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir)
{
PopulationStatistics ret { 0.0, 0.0 };
if (!_bc.isKnown(_h))
{
// Might be worth throwing here.
cwarn << "Invalid block given for state population: " << _h;
BOOST_THROW_EXCEPTION(BlockNotFound() << errinfo_target(_h));
}
auto b = _bc.block(_h);
BlockInfo bi(b);
if (bi.number())
{
// Non-genesis:
// 1. Start at parent's end state (state root).
BlockInfo bip(_bc.block(bi.parentHash()));
sync(_bc, bi.parentHash(), bip);
// 2. Enact the block's transactions onto this state.
m_beneficiary = bi.beneficiary();
Timer t;
auto vb = _bc.verifyBlock(&b, function<void(Exception&)>(), _ir | ImportRequirements::TransactionBasic);
ret.verify = t.elapsed();
t.restart();
enact(vb, _bc);
ret.enact = t.elapsed();
}
else
{
// Genesis required:
// We know there are no transactions, so just populate directly.
m_state = State(m_state.db(), BaseState::Empty); // TODO: try with PreExisting.
sync(_bc, _h, bi);
}
return ret;
}
bool Block::sync(BlockChain const& _bc)
{
return sync(_bc, _bc.currentHash());
}
bool Block::sync(BlockChain const& _bc, h256 const& _block, BlockInfo const& _bi)
{
bool ret = false;
// BLOCK
BlockInfo bi = _bi ? _bi : _bc.info(_block);
#if ETH_PARANOIA
if (!bi)
while (1)
{
try
{
auto b = _bc.block(_block);
bi.populate(b);
break;
}
catch (Exception const& _e)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << diagnostic_information(_e) << endl;
}
catch (std::exception const& _e)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << _e.what() << endl;
}
}
#endif
if (bi == m_currentBlock)
{
// We mined the last block.
// Our state is good - we just need to move on to next.
m_previousBlock = m_currentBlock;
resetCurrent();
ret = true;
}
else if (bi == m_previousBlock)
{
// No change since last sync.
// Carry on as we were.
}
else
{
// New blocks available, or we've switched to a different branch. All change.
// Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.)
if (m_state.db().lookup(bi.stateRoot()).empty()) // TODO: API in State for this?
{
cwarn << "Unable to sync to" << bi.hash() << "; state root" << bi.stateRoot() << "not found in database.";
cwarn << "Database corrupt: contains block without stateRoot:" << bi;
cwarn << "Try rescuing the database by running: eth --rescue";
BOOST_THROW_EXCEPTION(InvalidStateRoot() << errinfo_target(bi.stateRoot()));
}
m_previousBlock = bi;
resetCurrent();
ret = true;
}
#if ALLOW_REBUILD
else
{
// New blocks available, or we've switched to a different branch. All change.
// Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.)
std::vector<h256> chain;
while (bi.number() != 0 && m_db.lookup(bi.stateRoot()).empty()) // while we don't have the state root of the latest block...
{
chain.push_back(bi.hash()); // push back for later replay.
bi.populate(_bc.block(bi.parentHash())); // move to parent.
}
m_previousBlock = bi;
resetCurrent();
// Iterate through in reverse, playing back each of the blocks.
try
{
for (auto it = chain.rbegin(); it != chain.rend(); ++it)
{
auto b = _bc.block(*it);
enact(&b, _bc, _ir);
cleanup(true);
}
}
catch (...)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << boost::current_exception_diagnostic_information() << endl;
exit(1);
}
resetCurrent();
ret = true;
}
#endif
return ret;
}
pair<TransactionReceipts, bool> Block::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned msTimeout)
{
// TRANSACTIONS
pair<TransactionReceipts, bool> ret;
ret.second = false;
auto ts = _tq.topTransactions(c_maxSyncTransactions);
LastHashes lh;
auto deadline = chrono::steady_clock::now() + chrono::milliseconds(msTimeout);
for (int goodTxs = 1; goodTxs; )
{
goodTxs = 0;
for (auto const& t: ts)
if (!m_transactionSet.count(t.sha3()))
{
try
{
if (t.gasPrice() >= _gp.ask(*this))
{
// Timer t;
if (lh.empty())
lh = _bc.lastHashes();
execute(lh, t);
ret.first.push_back(m_receipts.back());
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
}
else if (t.gasPrice() < _gp.ask(*this) * 9 / 10)
{
clog(StateTrace) << t.sha3() << "Dropping El Cheapo transaction (<90% of ask price)";
_tq.drop(t.sha3());
}
}
catch (InvalidNonce const& in)
{
bigint const& req = *boost::get_error_info<errinfo_required>(in);
bigint const& got = *boost::get_error_info<errinfo_got>(in);
if (req > got)
{
// too old
clog(StateTrace) << t.sha3() << "Dropping old transaction (nonce too low)";
_tq.drop(t.sha3());
}
else if (got > req + _tq.waiting(t.sender()))
{
// too new
clog(StateTrace) << t.sha3() << "Dropping new transaction (too many nonces ahead)";
_tq.drop(t.sha3());
}
else
_tq.setFuture(t.sha3());
}
catch (BlockGasLimitReached const& e)
{
bigint const& got = *boost::get_error_info<errinfo_got>(e);
if (got > m_currentBlock.gasLimit())
{
clog(StateTrace) << t.sha3() << "Dropping over-gassy transaction (gas > block's gas limit)";
_tq.drop(t.sha3());
}
else
{
// Temporarily no gas left in current block.
// OPTIMISE: could note this and then we don't evaluate until a block that does have the gas left.
// for now, just leave alone.
}
}
catch (Exception const& _e)
{
// Something else went wrong - drop it.
clog(StateTrace) << t.sha3() << "Dropping invalid transaction:" << diagnostic_information(_e);
_tq.drop(t.sha3());
}
catch (std::exception const&)
{
// Something else went wrong - drop it.
_tq.drop(t.sha3());
cwarn << t.sha3() << "Transaction caused low-level exception :(";
}
}
if (chrono::steady_clock::now() > deadline)
{
ret.second = true;
break;
}
}
return ret;
}
u256 Block::enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc)
{
#if ETH_TIMED_ENACTMENTS
Timer t;
double populateVerify;
double populateGrand;
double syncReset;
double enactment;
#endif
// Check family:
BlockInfo biParent = _bc.info(_block.info.parentHash());
_block.info.verifyParent(biParent);
#if ETH_TIMED_ENACTMENTS
populateVerify = t.elapsed();
t.restart();
#endif
BlockInfo biGrandParent;
if (biParent.number())
biGrandParent = _bc.info(biParent.parentHash());
#if ETH_TIMED_ENACTMENTS
populateGrand = t.elapsed();
t.restart();
#endif
sync(_bc, _block.info.parentHash(), BlockInfo());
resetCurrent();
#if ETH_TIMED_ENACTMENTS
syncReset = t.elapsed();
t.restart();
#endif
m_previousBlock = biParent;
auto ret = enact(_block, _bc);
#if ETH_TIMED_ENACTMENTS
enactment = t.elapsed();
if (populateVerify + populateGrand + syncReset + enactment > 0.5)
clog(StateChat) << "popVer/popGrand/syncReset/enactment = " << populateVerify << "/" << populateGrand << "/" << syncReset << "/" << enactment;
#endif
return ret;
}
u256 Block::enact(VerifiedBlockRef const& _block, BlockChain const& _bc)
{
DEV_TIMED_FUNCTION_ABOVE(500);
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
assert(m_previousBlock.hash() == _block.info.parentHash());
assert(m_currentBlock.parentHash() == _block.info.parentHash());
assert(rootHash() == m_previousBlock.stateRoot());
#endif
if (m_currentBlock.parentHash() != m_previousBlock.hash())
// Internal client error.
BOOST_THROW_EXCEPTION(InvalidParentHash());
// Populate m_currentBlock with the correct values.
m_currentBlock.noteDirty();
m_currentBlock = _block.info;
// cnote << "playback begins:" << m_state.root();
// cnote << m_state;
LastHashes lh;
DEV_TIMED_ABOVE("lastHashes", 500)
lh = _bc.lastHashes((unsigned)m_previousBlock.number());
RLP rlp(_block.block);
vector<bytes> receipts;
// All ok with the block generally. Play back the transactions now...
unsigned i = 0;
DEV_TIMED_ABOVE("txExec", 500)
for (auto const& tr: _block.transactions)
{
try
{
LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, tr);
}
catch (Exception& ex)
{
ex << errinfo_transactionIndex(i);
throw;
}
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
++i;
}
h256 receiptsRoot;
DEV_TIMED_ABOVE(".receiptsRoot()", 500)
receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot())
{
InvalidReceiptsStateRoot ex;
ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot());
ex << errinfo_receipts(receipts);
// ex << errinfo_vmtrace(vmTrace(_block.block, _bc, ImportRequirements::None));
BOOST_THROW_EXCEPTION(ex);
}
if (m_currentBlock.logBloom() != logBloom())
{
InvalidLogBloom ex;
ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom());
ex << errinfo_receipts(receipts);
BOOST_THROW_EXCEPTION(ex);
}
// Initialise total difficulty calculation.
u256 tdIncrease = m_currentBlock.difficulty();
// Check uncles & apply their rewards to state.
if (rlp[2].itemCount() > 2)
{
TooManyUncles ex;
ex << errinfo_max(2);
ex << errinfo_got(rlp[2].itemCount());
BOOST_THROW_EXCEPTION(ex);
}
vector<BlockInfo> rewarded;
h256Hash excluded;
DEV_TIMED_ABOVE("allKin", 500)
excluded = _bc.allKinFrom(m_currentBlock.parentHash(), 6);
excluded.insert(m_currentBlock.hash());
unsigned ii = 0;
DEV_TIMED_ABOVE("uncleCheck", 500)
for (auto const& i: rlp[2])
{
try
{
auto h = sha3(i.data());
if (excluded.count(h))
{
UncleInChain ex;
ex << errinfo_comment("Uncle in block already mentioned");
ex << errinfo_unclesExcluded(excluded);
ex << errinfo_hash256(sha3(i.data()));
BOOST_THROW_EXCEPTION(ex);
}
excluded.insert(h);
// IgnoreSeal since it's a VerifiedBlock.
BlockInfo uncle(i.data(), IgnoreSeal, h, HeaderData);
BlockInfo uncleParent;
if (!_bc.isKnown(uncle.parentHash()))
BOOST_THROW_EXCEPTION(UnknownParent());
uncleParent = BlockInfo(_bc.block(uncle.parentHash()));
if ((bigint)uncleParent.number() < (bigint)m_currentBlock.number() - 7)
{
UncleTooOld ex;
ex << errinfo_uncleNumber(uncle.number());
ex << errinfo_currentNumber(m_currentBlock.number());
BOOST_THROW_EXCEPTION(ex);
}
else if (uncle.number() == m_currentBlock.number())
{
UncleIsBrother ex;
ex << errinfo_uncleNumber(uncle.number());
ex << errinfo_currentNumber(m_currentBlock.number());
BOOST_THROW_EXCEPTION(ex);
}
uncle.verifyParent(uncleParent);
rewarded.push_back(uncle);
++ii;
}
catch (Exception& ex)
{
ex << errinfo_uncleIndex(ii);
throw;
}
}
DEV_TIMED_ABOVE("applyRewards", 500)
applyRewards(rewarded);
// Commit all cached state changes to the state trie.
DEV_TIMED_ABOVE("commit", 500)
m_state.commit();
// Hash the state trie and check against the state_root hash in m_currentBlock.
if (m_currentBlock.stateRoot() != m_previousBlock.stateRoot() && m_currentBlock.stateRoot() != rootHash())
{
auto r = rootHash();
m_state.db().rollback(); // TODO: API in State for this?
BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(r, m_currentBlock.stateRoot()));
}
if (m_currentBlock.gasUsed() != gasUsed())
{
// Rollback the trie.
m_state.db().rollback(); // TODO: API in State for this?
BOOST_THROW_EXCEPTION(InvalidGasUsed() << RequirementError(bigint(gasUsed()), bigint(m_currentBlock.gasUsed())));
}
return tdIncrease;
}
ExecutionResult Block::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp)
{
// Uncommitting is a non-trivial operation - only do it once we've verified as much of the
// transaction as possible.
uncommitToMine();
std::pair<ExecutionResult, TransactionReceipt> resultReceipt = m_state.execute(EnvInfo(info(), _lh, gasUsed()), _t, _p, _onOp);
if (_p == Permanence::Committed)
{
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(_t);
m_receipts.push_back(resultReceipt.second);
m_transactionSet.insert(_t.sha3());
}
return resultReceipt.first;
}
void Block::applyRewards(vector<BlockInfo> const& _uncleBlockHeaders)
{
u256 r = m_blockReward;
for (auto const& i: _uncleBlockHeaders)
{
m_state.addBalance(i.beneficiary(), m_blockReward * (8 + i.number() - m_currentBlock.number()) / 8);
r += m_blockReward / 32;
}
m_state.addBalance(m_currentBlock.beneficiary(), r);
}
void Block::commitToSeal(BlockChain const& _bc, bytes const& _extraData)
{
if (m_committedToMine)
uncommitToMine();
else
m_precommit = m_state;
vector<BlockInfo> uncleBlockHeaders;
RLPStream unclesData;
unsigned unclesCount = 0;
if (m_previousBlock.number() != 0)
{
// Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations.
clog(StateDetail) << "Checking " << m_previousBlock.hash() << ", parent=" << m_previousBlock.parentHash();
h256Hash excluded = _bc.allKinFrom(m_currentBlock.parentHash(), 6);
auto p = m_previousBlock.parentHash();
for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash() && unclesCount < 2; ++gen, p = _bc.details(p).parent)
{
auto us = _bc.details(p).children;
assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent!
for (auto const& u: us)
if (!excluded.count(u)) // ignore any uncles/mainline blocks that we know about.
{
uncleBlockHeaders.push_back(_bc.info(u));
unclesData.appendRaw(_bc.headerData(u));
++unclesCount;
if (unclesCount == 2)
break;
}
}
}
BytesMap transactionsMap;
BytesMap receiptsMap;
RLPStream txs;
txs.appendList(m_transactions.size());
for (unsigned i = 0; i < m_transactions.size(); ++i)
{
RLPStream k;
k << i;
RLPStream receiptrlp;
m_receipts[i].streamRLP(receiptrlp);
receiptsMap.insert(std::make_pair(k.out(), receiptrlp.out()));
RLPStream txrlp;
m_transactions[i].streamRLP(txrlp);
transactionsMap.insert(std::make_pair(k.out(), txrlp.out()));
txs.appendRaw(txrlp.out());
}
txs.swapOut(m_currentTxs);
RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles);
// Apply rewards last of all.
applyRewards(uncleBlockHeaders);
// Commit any and all changes to the trie that are in the cache, then update the state root accordingly.
m_state.commit();
clog(StateDetail) << "Post-reward stateRoot:" << m_state.rootHash();
clog(StateDetail) << m_state;
clog(StateDetail) << *this;
m_currentBlock.setLogBloom(logBloom());
m_currentBlock.setGasUsed(gasUsed());
m_currentBlock.setRoots(hash256(transactionsMap), hash256(receiptsMap), sha3(m_currentUncles), m_state.rootHash());
m_currentBlock.setParentHash(m_previousBlock.hash());
m_currentBlock.setExtraData(_extraData);
if (m_currentBlock.extraData().size() > 32)
{
auto ed = m_currentBlock.extraData();
ed.resize(32);
m_currentBlock.setExtraData(ed);
}
m_committedToMine = true;
}
void Block::uncommitToMine()
{
if (m_committedToMine)
{
m_state = m_precommit;
m_committedToMine = false;
}
}
bool Block::sealBlock(bytesConstRef _header)
{
if (!m_committedToMine)
return false;
if (BlockInfo(_header, CheckNothing, h256{}, HeaderData).hashWithout() != m_currentBlock.hashWithout())
return false;
clog(StateDetail) << "Sealing block!";
// Compile block:
RLPStream ret;
ret.appendList(3);
ret.appendRaw(_header);
ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes);
m_currentBlock = BlockInfo(_header, CheckNothing, h256(), HeaderData);
cnote << "Mined " << m_currentBlock.hash() << "(parent: " << m_currentBlock.parentHash() << ")";
// TODO: move into Sealer
StructuredLogger::minedNewBlock(
m_currentBlock.hash().abridged(),
"", // Can't give the nonce here.
"", //TODO: chain head hash here ??
m_currentBlock.parentHash().abridged()
);
// Quickly reset the transactions.
// TODO: Leave this in a better state than this limbo, or at least record that it's in limbo.
m_transactions.clear();
m_receipts.clear();
m_transactionSet.clear();
m_precommit = m_state;
return true;
}
State Block::fromPending(unsigned _i) const
{
State ret = m_state;
_i = min<unsigned>(_i, m_transactions.size());
if (!_i)
ret.setRoot(m_previousBlock.stateRoot());
else
ret.setRoot(m_receipts[_i - 1].stateRoot());
return ret;
}
LogBloom Block::logBloom() const
{
LogBloom ret;
for (TransactionReceipt const& i: m_receipts)
ret |= i.bloom();
return ret;
}
void Block::cleanup(bool _fullCommit)
{
if (_fullCommit)
{
// Commit the new trie to disk.
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot() << "=" << rootHash() << "=" << toHex(asBytes(db().lookup(rootHash())));
try
{
EnforceRefs er(db(), true);
rootHash();
}
catch (BadRoot const&)
{
clog(StateChat) << "Trie corrupt! :-(";
throw;
}
m_state.db().commit(); // TODO: State API for this?
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot() << "=" << rootHash() << "=" << toHex(asBytes(db().lookup(rootHash())));
m_previousBlock = m_currentBlock;
m_currentBlock.populateFromParent(m_previousBlock);
clog(StateTrace) << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash();
}
else
m_state.db().rollback(); // TODO: State API for this?
resetCurrent();
}
string Block::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
RLP rlp(_block);
cleanup(false);
BlockInfo bi(_block, (_ir & ImportRequirements::ValidSeal) ? CheckEverything : IgnoreSeal);
m_currentBlock = bi;
m_currentBlock.verifyInternals(_block);
m_currentBlock.noteDirty();
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number());
string ret;
unsigned i = 0;
for (auto const& tr: rlp[1])
{
StandardTrace st;
st.setShowMnemonics();
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp());
ret += (ret.empty() ? "[" : ",") + st.json();
++i;
}
return ret.empty() ? "[]" : (ret + "]");
}
std::ostream& dev::eth::operator<<(std::ostream& _out, Block const& _s)
{
(void)_s;
return _out;
}

310
libethereum/Block.h

@ -1,310 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Block.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <array>
#include <unordered_map>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/TrieDB.h>
#include <libdevcrypto/OverlayDB.h>
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/Miner.h>
#include <libevm/ExtVMFace.h>
#include "Account.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
#include "AccountDiff.h"
#include "GasPricer.h"
#include "State.h"
namespace dev
{
namespace test { class ImportTest; class StateLoader; }
namespace eth
{
class BlockChain;
class State;
class TransactionQueue;
struct VerifiedBlockRef;
struct BlockChat: public LogChannel { static const char* name(); static const int verbosity = 4; };
struct BlockTrace: public LogChannel { static const char* name(); static const int verbosity = 5; };
struct BlockDetail: public LogChannel { static const char* name(); static const int verbosity = 14; };
struct BlockSafeExceptions: public LogChannel { static const char* name(); static const int verbosity = 21; };
struct PopulationStatistics
{
double verify;
double enact;
};
/**
* @brief Active model of a block within the block chain.
* Keeps track of all transactions, receipts and state for a particular block. Can apply all
* needed transforms of the state for rewards and contains logic for sealing the block.
*/
class Block
{
friend class ExtVM;
friend class dev::test::ImportTest;
friend class dev::test::StateLoader;
friend class Executive;
friend class BlockChain;
public:
/// Default constructor; creates with a blank database prepopulated with the genesis block.
Block(): m_state(OverlayDB(), BaseState::Empty) {}
/// Basic state object from database.
/// Use the default when you already have a database and you just want to make a Block object
/// which uses it. If you have no preexisting database then set BaseState to something other
/// than BaseState::PreExisting in order to prepopulate the Trie.
/// You can also set the beneficiary address.
explicit Block(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address());
/// Copy state object.
Block(Block const& _s);
/// Copy state object.
Block& operator=(Block const& _s);
/// Get the beneficiary address for any transactions we do and rewards we get.
Address beneficiary() const { return m_beneficiary; }
/// Set the beneficiary address for any transactions we do and rewards we get.
/// This causes a complete reset of current block.
void setBeneficiary(Address const& _id) { m_beneficiary = _id; resetCurrent(); }
// Account-getters. All operate on the final state.
/// Get an account's balance.
/// @returns 0 if the address has never been used.
u256 balance(Address const& _address) const { return m_state.balance(_address); }
/// Get the number of transactions a particular address has sent (used for the transaction nonce).
/// @returns 0 if the address has never been used.
u256 transactionsFrom(Address const& _address) const { return m_state.transactionsFrom(_address); }
/// Check if the address is in use.
bool addressInUse(Address const& _address) const { return m_state.addressInUse(_address); }
/// Check if the address contains executable code.
bool addressHasCode(Address const& _address) const { return m_state.addressHasCode(_address); }
/// Get the root of the storage of an account.
h256 storageRoot(Address const& _contract) const { return m_state.storageRoot(_contract); }
/// Get the value of a storage position of an account.
/// @returns 0 if no account exists at that address.
u256 storage(Address const& _contract, u256 const& _memory) const { return m_state.storage(_contract, _memory); }
/// Get the storage of an account.
/// @note This is expensive. Don't use it unless you need to.
/// @returns std::unordered_map<u256, u256> if no account exists at that address.
std::unordered_map<u256, u256> storage(Address const& _contract) const { return m_state.storage(_contract); }
/// Get the code of an account.
/// @returns bytes() if no account exists at that address.
bytes const& code(Address const& _contract) const { return m_state.code(_contract); }
/// Get the code hash of an account.
/// @returns EmptySHA3 if no account exists at that address or if there is no code associated with the address.
h256 codeHash(Address const& _contract) const { return m_state.codeHash(_contract); }
// General information from state
/// Get the backing state object.
State const& state() const { return m_state; }
/// Open a DB - useful for passing into the constructor & keeping for other states that are necessary.
OverlayDB const& db() const { return m_state.db(); }
/// The hash of the root of our state tree.
h256 rootHash() const { return m_state.rootHash(); }
/// @returns the set containing all addresses currently in use in Ethereum.
/// @throws InterfaceNotSupported if compiled without ETH_FATDB.
std::unordered_map<Address, u256> addresses() const { return m_state.addresses(); }
// For altering accounts behind-the-scenes
/// Get a mutable State object which is backing this block.
/// @warning Only use this is you know what you're doing. If you use it while constructing a
/// normal sealable block, don't expect things to work right.
State& mutableState() { return m_state; }
// Information concerning ongoing transactions
/// Get the remaining gas limit in this block.
u256 gasLimitRemaining() const { return m_currentBlock.gasLimit() - gasUsed(); }
/// Get the list of pending transactions.
Transactions const& pending() const { return m_transactions; }
/// Get the list of hashes of pending transactions.
h256Hash const& pendingHashes() const { return m_transactionSet; }
/// Get the transaction receipt for the transaction of the given index.
TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; }
/// Get the list of pending transactions.
LogEntries const& log(unsigned _i) const { return m_receipts[_i].log(); }
/// Get the bloom filter of all logs that happened in the block.
LogBloom logBloom() const;
/// Get the bloom filter of a particular transaction that happened in the block.
LogBloom const& logBloom(unsigned _i) const { return m_receipts[_i].bloom(); }
/// Get the State immediately after the given number of pending transactions have been applied.
/// If (_i == 0) returns the initial state of the block.
/// If (_i == pending().size()) returns the final state of the block, prior to rewards.
State fromPending(unsigned _i) const;
/// @returns the StateDiff caused by the pending transaction of index @a _i.
StateDiff pendingDiff(unsigned _i) const { return fromPending(_i).diff(fromPending(_i + 1), true); }
// State-change operations
/// Construct state object from arbitrary point in blockchain.
PopulationStatistics populateFromChain(BlockChain const& _bc, h256 const& _hash, ImportRequirements::value _ir = ImportRequirements::None);
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
ExecutionResult execute(LastHashes const& _lh, Transaction const& _t, Permanence _p = Permanence::Committed, OnOpFunc const& _onOp = OnOpFunc());
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
/// @returns a list of receipts one for each transaction placed from the queue into the state and bool, true iff there are more transactions to be processed.
std::pair<TransactionReceipts, bool> sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned _msTimeout = 100);
/// Sync our state with the block chain.
/// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue.
bool sync(BlockChain const& _bc);
/// Sync with the block chain, but rather than synching to the latest block, instead sync to the given block.
bool sync(BlockChain const& _bc, h256 const& _blockHash, BlockInfo const& _bi = BlockInfo());
/// Execute all transactions within a given block.
/// @returns the additional total difficulty.
u256 enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc);
/// Returns back to a pristine state after having done a playback.
/// @arg _fullCommit if true flush everything out to disk. If false, this effectively only validates
/// the block since all state changes are ultimately reversed.
void cleanup(bool _fullCommit);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();
// Sealing
/// Prepares the current state for mining.
/// Commits all transactions into the trie, compiles uncles and transactions list, applies all
/// rewards and populates the current block header with the appropriate hashes.
/// The only thing left to do after this is to actually mine().
///
/// This may be called multiple times and without issue.
void commitToSeal(BlockChain const& _bc, bytes const& _extraData = {});
/// Pass in a solution to the proof-of-work.
/// @returns true iff we were previously committed to mining.
/// TODO: verify it prior to calling this.
/** Commit to DB and build the final block if the previous call to mine()'s result is completion.
* Typically looks like:
* @code
* while (notYetMined)
* {
* // lock
* commitToSeal(_blockChain); // will call uncommitToMine if a repeat.
* completeMine();
* // unlock
* @endcode
*/
bool sealBlock(bytes const& _header) { return sealBlock(&_header); }
bool sealBlock(bytesConstRef _header);
/// Get the complete current block, including valid nonce.
/// Only valid after mine() returns true.
bytes const& blockData() const { return m_currentBytes; }
/// Get the header information on the present block.
BlockInfo const& info() const { return m_currentBlock; }
private:
/// Undo the changes to the state for committing to mine.
void uncommitToMine();
/// Retrieve all information about a given address into the cache.
/// If _requireMemory is true, grab the full memory should it be a contract item.
/// If _forceCreate is true, then insert a default item into the cache, in the case it doesn't
/// exist in the DB.
void ensureCached(Address const& _a, bool _requireCode, bool _forceCreate) const;
/// Retrieve all information about a given address into a cache.
void ensureCached(std::unordered_map<Address, Account>& _cache, Address const& _a, bool _requireCode, bool _forceCreate) const;
/// Execute the given block, assuming it corresponds to m_currentBlock.
/// Throws on failure.
u256 enact(VerifiedBlockRef const& _block, BlockChain const& _bc);
/// Finalise the block, applying the earned rewards.
void applyRewards(std::vector<BlockInfo> const& _uncleBlockHeaders);
/// @returns gas used by transactions thus far executed.
u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; }
/// Provide a standard VM trace for debugging purposes.
std::string vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir);
State m_state; ///< Our state tree, as an OverlayDB DB.
Transactions m_transactions; ///< The current list of transactions that we've included in the state.
TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts.
h256Hash m_transactionSet; ///< The set of transaction hashes that we've included in the state.
State m_precommit; ///< State at the point immediately prior to rewards.
BlockInfo m_previousBlock; ///< The previous block's information.
BlockInfo m_currentBlock; ///< The current block's information.
bytes m_currentBytes; ///< The current block.
bool m_committedToMine = false; ///< Have we committed to mine on the present m_currentBlock?
bytes m_currentTxs; ///< The RLP-encoded block of transactions.
bytes m_currentUncles; ///< The RLP-encoded block of uncles.
Address m_beneficiary; ///< Our address (i.e. the address to which fees go).
u256 m_blockReward;
friend std::ostream& operator<<(std::ostream& _out, Block const& _s);
};
std::ostream& operator<<(std::ostream& _out, Block const& _s);
}
}

1284
libethereum/BlockChain.cpp

File diff suppressed because it is too large

516
libethereum/BlockChain.h

@ -1,516 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockChain.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <deque>
#include <chrono>
#include <unordered_map>
#include <unordered_set>
#include <libdevcore/db.h>
#include <libdevcore/Log.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Guards.h>
#include <libethcore/Common.h>
#include <libethcore/BlockInfo.h>
#include <libevm/ExtVMFace.h>
#include "BlockDetails.h"
#include "Account.h"
#include "Transaction.h"
#include "BlockQueue.h"
#include "VerifiedBlock.h"
#include "State.h"
namespace std
{
template <> struct hash<pair<dev::h256, unsigned>>
{
size_t operator()(pair<dev::h256, unsigned> const& _x) const { return hash<dev::h256>()(_x.first) ^ hash<unsigned>()(_x.second); }
};
}
namespace dev
{
class OverlayDB;
namespace eth
{
static const h256s NullH256s;
class State;
class Block;
struct AlreadyHaveBlock: virtual Exception {};
struct UnknownParent: virtual Exception {};
struct FutureTime: virtual Exception {};
struct TransientError: virtual Exception {};
struct BlockChainChat: public LogChannel { static const char* name(); static const int verbosity = 5; };
struct BlockChainNote: public LogChannel { static const char* name(); static const int verbosity = 3; };
struct BlockChainWarn: public LogChannel { static const char* name(); static const int verbosity = 1; };
struct BlockChainDebug: public LogChannel { static const char* name(); static const int verbosity = 0; };
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::unordered_map<Address, Account> const& genesisState();
ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0);
ldb::Slice toSlice(uint64_t _n, unsigned _sub = 0);
using BlocksHash = std::unordered_map<h256, bytes>;
using TransactionHashes = h256s;
using UncleHashes = h256s;
enum {
ExtraDetails = 0,
ExtraBlockHash,
ExtraTransactionAddress,
ExtraLogBlooms,
ExtraReceipts,
ExtraBlocksBlooms
};
using ProgressCallback = std::function<void(unsigned, unsigned)>;
class VersionChecker
{
public:
VersionChecker(std::string const& _dbPath, h256 const& _genesisHash);
};
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
* @threadsafe
*/
class BlockChain
{
public:
/// Doesn't open the database - if you want it open it's up to you to subclass this and open it
/// in the constructor there.
BlockChain(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path);
~BlockChain();
/// Reopen everything.
virtual void reopen(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()) { close(); open(m_genesisBlock, m_genesisState, m_dbPath); openDatabase(m_dbPath, _we, _pc); }
/// (Potentially) renders invalid existing bytesConstRef returned by lastBlock.
/// To be called from main loop every 100ms or so.
void process();
/// Sync the chain with any incoming blocks. All blocks should, if processed in order.
/// @returns fresh blocks, dead blocks and true iff there are additional blocks to be processed waiting.
std::tuple<ImportRoute, bool, unsigned> sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
std::pair<ImportResult, ImportRoute> attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _mutBeNew = true) noexcept;
/// Import block into disk-backed DB
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
ImportRoute import(bytes const& _block, OverlayDB const& _stateDB, bool _mustBeNew = true);
ImportRoute import(VerifiedBlockRef const& _block, OverlayDB const& _db, bool _mustBeNew = true);
/// Returns true if the given block is known (though not necessarily a part of the canon chain).
bool isKnown(h256 const& _hash) const;
/// Get the partial-header of a block (or the most recent mined if none given). Thread-safe.
BlockInfo info(h256 const& _hash) const { return BlockInfo(headerData(_hash), CheckNothing, _hash, HeaderData); }
BlockInfo info() const { return info(currentHash()); }
/// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe.
bytes block(h256 const& _hash) const;
bytes block() const { return block(currentHash()); }
/// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe.
bytes headerData(h256 const& _hash) const;
bytes headerData() const { return headerData(currentHash()); }
/// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
BlockDetails details(h256 const& _hash) const { return queryExtras<BlockDetails, ExtraDetails>(_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 const& _hash) const { return queryExtras<BlockLogBlooms, ExtraLogBlooms>(_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.
/// receipts are given in the same order are in the same order as the transactions
BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); }
/// Get the transaction by block hash and index;
TransactionReceipt transactionReceipt(h256 const& _blockHash, unsigned _i) const { return receipts(_blockHash).receipts[_i]; }
/// Get the transaction receipt by transaction hash. Thread-safe.
TransactionReceipt transactionReceipt(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytesConstRef(); return transactionReceipt(ta.blockHash, ta.index); }
/// Get a list of transaction hashes for a given block. Thread-safe.
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 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<BlockHash, uint64_t, ExtraBlockHash>(_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:
* level 0:
* 0x, 0x + 1, .. (1x - 1)
* 1x, 1x + 1, .. (2x - 1)
* ...
* (255x .. (256x - 1))
* level 1:
* 0x .. (1x - 1), 1x .. (2x - 1), ..., (255x .. (256x - 1))
* 256x .. (257x - 1), 257x .. (258x - 1), ..., (511x .. (512x - 1))
* ...
* level n, index i, offset o:
* i * (x ^ n) + o * x ^ (n - 1)
*/
BlocksBlooms blocksBlooms(unsigned _level, unsigned _index) const { return blocksBlooms(chunkId(_level, _index)); }
BlocksBlooms blocksBlooms(h256 const& _chunkId) const { return queryExtras<BlocksBlooms, ExtraBlocksBlooms>(_chunkId, m_blocksBlooms, x_blocksBlooms, NullBlocksBlooms); }
void clearBlockBlooms(unsigned _begin, unsigned _end);
LogBloom blockBloom(unsigned _number) const { return blocksBlooms(chunkId(0, _number / c_bloomIndexSize)).blooms[_number % c_bloomIndexSize]; }
std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const;
std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const;
/// Returns true if transaction is known. Thread-safe
bool isKnownTransaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); return !!ta; }
/// Get a transaction from its hash. Thread-safe.
bytes transaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); }
std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair<h256, unsigned>(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 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 all transactions from a block.
std::vector<bytes> transactions(h256 const& _blockHash) const { bytes b = block(_blockHash); std::vector<bytes> ret; for (auto const& i: RLP(b)[1]) ret.push_back(i.data().toBytes()); return ret; }
std::vector<bytes> transactions() const { return transactions(currentHash()); }
/// Get a number for the given hash (or the most recent mined if none given). Thread-safe.
unsigned number(h256 const& _hash) const { return details(_hash).number; }
unsigned number() const { return m_lastBlockNumber; }
/// Get a given block (RLP format). Thread-safe.
h256 currentHash() const { ReadGuard l(x_lastBlockHash); return m_lastBlockHash; }
/// Get the hash of the genesis block. Thread-safe.
h256 genesisHash() const { return m_genesisHash; }
/// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + @a _generations).
/// @returns set including the header-hash of every parent (including @a _parent) up to and including generation + @a _generations
/// togther with all their quoted uncles.
h256Hash allKinFrom(h256 const& _parent, unsigned _generations) const;
/// Run through database and verify all blocks by reevaluating.
/// Will call _progress with the progress in this operation first param done, second total.
void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function<void(unsigned, unsigned)>(), bool _prepPoW = false);
/// Alter the head of the chain to some prior block along it.
void rewind(unsigned _newHead);
/// Rescue the database.
void rescue(OverlayDB& _db);
/** @returns a tuple of:
* - an vector of hashes 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;
* - the block hash of the latest common ancestor of both blocks;
* - the index where the latest common ancestor of both blocks would either be found or inserted, depending
* on whether it is included.
*
* @param _common if true, include the common ancestor in the returned vector.
* @param _pre if true, include all block hashes running from @a _from until the common ancestor in the returned vector.
* @param _post if true, include all block hashes running from the common ancestor until @a _to in the returned vector.
*
* e.g. if the block tree is 3a -> 2a -> 1a -> g and 2b -> 1b -> g (g is genesis, *a, *b are competing chains),
* then:
* @code
* treeRoute(3a, 2b, false) == make_tuple({ 3a, 2a, 1a, 1b, 2b }, g, 3);
* treeRoute(2a, 1a, false) == make_tuple({ 2a, 1a }, 1a, 1)
* treeRoute(1a, 2a, false) == make_tuple({ 1a, 2a }, 1a, 0)
* treeRoute(1b, 2a, false) == make_tuple({ 1b, 1a, 2a }, g, 1)
* treeRoute(3a, 2b, true) == make_tuple({ 3a, 2a, 1a, g, 1b, 2b }, g, 3);
* treeRoute(2a, 1a, true) == make_tuple({ 2a, 1a }, 1a, 1)
* treeRoute(1a, 2a, true) == make_tuple({ 1a, 2a }, 1a, 0)
* treeRoute(1b, 2a, true) == make_tuple({ 1b, g, 1a, 2a }, g, 1)
* @endcode
*/
std::tuple<h256s, h256, unsigned> treeRoute(h256 const& _from, h256 const& _to, bool _common = true, bool _pre = true, bool _post = true) const;
struct Statistics
{
unsigned memBlocks;
unsigned memDetails;
unsigned memLogBlooms;
unsigned memReceipts;
unsigned memTransactionAddresses;
unsigned memBlockHashes;
unsigned memTotal() const { return memBlocks + memDetails + memLogBlooms + memReceipts + memTransactionAddresses + memBlockHashes; }
};
/// @returns statistics about memory usage.
Statistics usage(bool _freshen = false) const { if (_freshen) updateStats(); return m_lastStats; }
/// Deallocate unused data.
void garbageCollect(bool _force = false);
/// Change the function that is called with a bad block.
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
/// Get a pre-made genesis State object.
Block genesisBlock(OverlayDB const& _db) const;
/// Verify block and prepare it for enactment
virtual VerifiedBlockRef verifyBlock(bytesConstRef _block, std::function<void(Exception&)> const& _onBad, ImportRequirements::value _ir = ImportRequirements::OutOfOrderChecks) const = 0;
protected:
static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); }
/// Initialise everything and ready for openning the database.
// TODO: rename to init
void open(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path);
/// Open the database.
// TODO: rename to open.
unsigned openDatabase(std::string const& _path, WithExisting _we);
/// Finalise everything and close the database.
void close();
/// Open the database, rebuilding if necessary.
void openDatabase(std::string const& _path, WithExisting _we, ProgressCallback const& _pc)
{
if (openDatabase(_path, _we) != c_minorProtocolVersion || _we == WithExisting::Verify)
rebuild(_path, _pc);
}
template<class T, class K, unsigned N> T queryExtras(K const& _h, std::unordered_map<K, T>& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const
{
{
ReadGuard l(_x);
auto it = _m.find(_h);
if (it != _m.end())
return it->second;
}
std::string s;
(_extrasDB ? _extrasDB : m_extrasDB)->Get(m_readOptions, toSlice(_h, N), &s);
if (s.empty())
return _n;
noteUsed(_h, N);
WriteGuard l(_x);
auto ret = _m.insert(std::make_pair(_h, T(RLP(s))));
return ret.first->second;
}
template<class T, unsigned N> T queryExtras(h256 const& _h, std::unordered_map<h256, T>& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const
{
return queryExtras<T, h256, N>(_h, _m, _x, _n, _extrasDB);
}
void checkConsistency();
/// The caches of the disk DB and their locks.
mutable SharedMutex x_blocks;
mutable BlocksHash m_blocks;
mutable SharedMutex x_details;
mutable BlockDetailsHash m_details;
mutable SharedMutex x_logBlooms;
mutable BlockLogBloomsHash m_logBlooms;
mutable SharedMutex x_receipts;
mutable BlockReceiptsHash m_receipts;
mutable SharedMutex x_transactionAddresses;
mutable TransactionAddressHash m_transactionAddresses;
mutable SharedMutex x_blockHashes;
mutable BlockHashHash m_blockHashes;
mutable SharedMutex x_blocksBlooms;
mutable BlocksBloomsHash m_blocksBlooms;
using CacheID = std::pair<h256, unsigned>;
mutable Mutex x_cacheUsage;
mutable std::deque<std::unordered_set<CacheID>> m_cacheUsage;
mutable std::unordered_set<CacheID> m_inUse;
void noteUsed(h256 const& _h, unsigned _extra = (unsigned)-1) const;
void noteUsed(uint64_t const& _h, unsigned _extra = (unsigned)-1) const { (void)_h; (void)_extra; } // don't note non-hash types
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;
/// The disk DBs. Thread-safe, so no need for locks.
ldb::DB* m_blocksDB;
ldb::DB* m_extrasDB;
/// Hash of the last (valid) block on the longest chain.
mutable boost::shared_mutex x_lastBlockHash;
h256 m_lastBlockHash;
unsigned m_lastBlockNumber = 0;
/// Genesis block info.
h256 m_genesisHash;
bytes m_genesisBlock;
std::unordered_map<Address, Account> m_genesisState;
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
std::string m_dbPath;
friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
};
template <class Sealer>
class FullBlockChain: public BlockChain
{
public:
using BlockHeader = typename Sealer::BlockHeader;
FullBlockChain(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path, WithExisting _we, ProgressCallback const& _pc = ProgressCallback()):
BlockChain(_genesisBlock, _genesisState, _path)
{
openDatabase(_path, _we, _pc);
}
/// Get the header of a block (or the most recent mined if none given). Thread-safe.
typename Sealer::BlockHeader header(h256 const& _hash) const { return typename Sealer::BlockHeader(headerData(_hash), IgnoreSeal, _hash, HeaderData); }
typename Sealer::BlockHeader header() const { return header(currentHash()); }
virtual VerifiedBlockRef verifyBlock(bytesConstRef _block, std::function<void(Exception&)> const& _onBad, ImportRequirements::value _ir = ImportRequirements::OutOfOrderChecks) const override
{
VerifiedBlockRef res;
BlockHeader h;
try
{
h = BlockHeader(_block, (_ir & ImportRequirements::ValidSeal) ? Strictness::CheckEverything : Strictness::QuickNonce);
h.verifyInternals(_block);
if (!!(_ir & ImportRequirements::Parent))
{
bytes parentHeader(headerData(h.parentHash()));
if (parentHeader.empty())
BOOST_THROW_EXCEPTION(InvalidParentHash() << errinfo_required_h256(h.parentHash()) << errinfo_currentNumber(h.number()));
h.verifyParent(typename Sealer::BlockHeader(parentHeader, IgnoreSeal, h.parentHash(), HeaderData));
}
res.info = static_cast<BlockInfo&>(h);
}
catch (Exception& ex)
{
ex << errinfo_phase(1);
ex << errinfo_now(time(0));
ex << errinfo_block(_block.toBytes());
// only populate extraData if we actually managed to extract it. otherwise,
// we might be clobbering the existing one.
if (!h.extraData().empty())
ex << errinfo_extraData(h.extraData());
if (_onBad)
_onBad(ex);
throw;
}
RLP r(_block);
unsigned i = 0;
if (_ir && !!(ImportRequirements::UncleBasic | ImportRequirements::UncleParent | ImportRequirements::UncleSeals))
for (auto const& uncle: r[2])
{
BlockHeader uh;
try
{
uh.populateFromHeader(RLP(uncle.data()), (_ir & ImportRequirements::UncleSeals) ? Strictness::CheckEverything : Strictness::IgnoreSeal);
if (!!(_ir & ImportRequirements::UncleParent))
{
bytes parentHeader(headerData(uh.parentHash()));
if (parentHeader.empty())
BOOST_THROW_EXCEPTION(InvalidUncleParentHash() << errinfo_required_h256(uh.parentHash()) << errinfo_currentNumber(h.number()) << errinfo_uncleNumber(uh.number()));
uh.verifyParent(typename Sealer::BlockHeader(parentHeader, IgnoreSeal, uh.parentHash(), HeaderData));
}
}
catch (Exception& ex)
{
ex << errinfo_phase(1);
ex << errinfo_uncleIndex(i);
ex << errinfo_now(time(0));
ex << errinfo_block(_block.toBytes());
// only populate extraData if we actually managed to extract it. otherwise,
// we might be clobbering the existing one.
if (!uh.extraData().empty())
ex << errinfo_extraData(uh.extraData());
if (_onBad)
_onBad(ex);
throw;
}
++i;
}
i = 0;
if (_ir && !!(ImportRequirements::TransactionBasic | ImportRequirements::TransactionSignatures))
for (RLP const& tr: r[1])
{
bytesConstRef d = tr.data();
try
{
res.transactions.push_back(Transaction(d, (_ir & ImportRequirements::TransactionSignatures) ? CheckTransaction::Everything : CheckTransaction::None));
}
catch (Exception& ex)
{
ex << errinfo_phase(1);
ex << errinfo_transactionIndex(i);
ex << errinfo_transaction(d.toBytes());
ex << errinfo_block(_block.toBytes());
// only populate extraData if we actually managed to extract it. otherwise,
// we might be clobbering the existing one.
if (!h.extraData().empty())
ex << errinfo_extraData(h.extraData());
if (_onBad)
_onBad(ex);
throw;
}
++i;
}
res.block = bytesConstRef(_block);
return res;
}
protected:
/// Constructor for derived classes to use when they'll open the chain db afterwards.
FullBlockChain(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path):
BlockChain(_genesisBlock, _genesisState, _path)
{}
};
std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
}
}

1224
libethereum/BlockChainSync.cpp

File diff suppressed because it is too large

330
libethereum/BlockChainSync.h

@ -1,330 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockChainSync.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <mutex>
#include <libdevcore/Guards.h>
#include <libethcore/Common.h>
#include <libp2p/Common.h>
#include "CommonNet.h"
#include "DownloadMan.h"
namespace dev
{
class RLPStream;
namespace eth
{
class EthereumHost;
class BlockQueue;
class EthereumPeer;
/**
* @brief Base BlockChain synchronization strategy class.
* Syncs to peers and keeps up to date. Base class handles blocks downloading but does not contain any details on state transfer logic.
*/
class BlockChainSync: public HasInvariants
{
public:
BlockChainSync(EthereumHost& _host);
virtual ~BlockChainSync();
void abortSync(); ///< Abort all sync activity
DownloadMan const& downloadMan() const;
DownloadMan& downloadMan();
/// @returns true is Sync is in progress
virtual bool isSyncing() const = 0;
/// Restart sync
virtual void restartSync() = 0;
/// Called by peer to report status
virtual void onPeerStatus(std::shared_ptr<EthereumPeer> _peer);
/// Called by peer once it has new blocks during syn
virtual void onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const& _r);
/// Called by peer once it has new blocks
virtual void onPeerNewBlock(std::shared_ptr<EthereumPeer> _peer, RLP const& _r);
/// Called by peer once it has new hashes
virtual void onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) = 0;
/// Called by peer once it has another sequential block of hashes during sync
virtual void onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) = 0;
/// Called by peer when it is disconnecting
virtual void onPeerAborting() = 0;
/// @returns Synchonization status
virtual SyncStatus status() const = 0;
static char const* stateName(SyncState _s) { return s_stateNames[static_cast<int>(_s)]; }
protected:
//To be implemented in derived classes:
/// New valid peer appears
virtual void onNewPeer(std::shared_ptr<EthereumPeer> _peer) = 0;
/// Peer done downloading blocks
virtual void peerDoneBlocks(std::shared_ptr<EthereumPeer> _peer) = 0;
/// Resume downloading after witing state
virtual void continueSync() = 0;
/// Called after all blocks have been donloaded
virtual void completeSync() = 0;
/// Enter waiting state
virtual void pauseSync() = 0;
/// Restart sync for given peer
virtual void resetSyncFor(std::shared_ptr<EthereumPeer> _peer, h256 const& _latestHash, u256 const& _td) = 0;
EthereumHost& host() { return m_host; }
EthereumHost const& host() const { return m_host; }
/// Estimates max number of hashes peers can give us.
unsigned estimatedHashes() const;
/// Request blocks from peer if needed
void requestBlocks(std::shared_ptr<EthereumPeer> _peer);
private:
EthereumHost& m_host;
protected:
Handler<> m_bqRoomAvailable; ///< Triggered once block queue
mutable RecursiveMutex x_sync;
SyncState m_state = SyncState::Idle; ///< Current sync state
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
h256Hash m_knownNewHashes; ///< New hashes we know about use for logging only
private:
static char const* const s_stateNames[static_cast<int>(SyncState::Size)];
bool invariants() const override = 0;
void logNewBlock(h256 const& _h);
};
/**
* @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash download is complete
* syncs to peers and keeps up to date
*/
/**
* Transitions:
*
* Idle->Hashes
* Triggered when:
* * A new peer appears that we can sync to
* * Transtition to Idle, there are peers we can sync to
* Effects:
* * Set chain sync (m_syncingTotalDifficulty, m_syncingLatestHash, m_syncer)
* * Requests hashes from m_syncer
*
* Hashes->Idle
* Triggered when:
* * Received too many hashes
* * Received 0 total hashes from m_syncer
* * m_syncer aborts
* Effects:
* In case of too many hashes sync is reset
*
* Hashes->Blocks
* Triggered when:
* * Received known hash from m_syncer
* * Received 0 hashes from m_syncer and m_syncingTotalBlocks not empty
* Effects:
* * Set up download manager, clear m_syncingTotalBlocks. Set all peers to help with downloading if they can
*
* Blocks->Idle
* Triggered when:
* * m_syncer aborts
* * m_syncer does not have required block
* * All blocks downloaded
* * Block qeueue is full with unknown blocks
* Effects:
* * Download manager is reset
*
* Blocks->Waiting
* Triggered when:
* * Block queue is full with known blocks
* Effects:
* * Stop requesting blocks from peers
*
* Waiting->Blocks
* Triggered when:
* * Block queue has space for new blocks
* Effects:
* * Continue requesting blocks from peers
*
* Idle->NewBlocks
* Triggered when:
* * New block hashes arrive
* Effects:
* * Set up download manager, clear m_syncingTotalBlocks. Download blocks from a single peer. If downloaded blocks have unknown parents, set the peer to sync
*
* NewBlocks->Idle
* Triggered when:
* * m_syncer aborts
* * m_syncer does not have required block
* * All new blocks downloaded
* * Block qeueue is full with unknown blocks
* Effects:
* * Download manager is reset
*
*/
class PV60Sync: public BlockChainSync
{
public:
PV60Sync(EthereumHost& _host);
/// @returns true is Sync is in progress
bool isSyncing() const override { return !!m_syncer.lock(); }
/// Called by peer once it has new hashes
void onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) override;
/// Called by peer once it has another sequential block of hashes during sync
void onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) override;
/// Called by peer when it is disconnecting
void onPeerAborting() override;
/// @returns Sync status
SyncStatus status() const override;
protected:
void onNewPeer(std::shared_ptr<EthereumPeer> _peer) override;
void continueSync() override;
void peerDoneBlocks(std::shared_ptr<EthereumPeer> _peer) override;
void restartSync() override;
void completeSync() override;
void pauseSync() override;
void resetSyncFor(std::shared_ptr<EthereumPeer> _peer, h256 const& _latestHash, u256 const& _td) override;
protected:
/// Transition sync state in a particular direction. @param _peer Peer that is responsible for state tranfer
void transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, bool _force = false, bool _needHelp = true);
/// Reset peer syncing requirements state.
void resetNeedsSyncing(std::shared_ptr<EthereumPeer> _peer) { setNeedsSyncing(_peer, h256(), 0); }
/// Update peer syncing requirements state.
void setNeedsSyncing(std::shared_ptr<EthereumPeer> _peer, h256 const& _latestHash, u256 const& _td);
/// Do we presently need syncing with this peer?
bool needsSyncing(std::shared_ptr<EthereumPeer> _peer) const;
/// Check whether the session should bother grabbing blocks from a peer.
bool shouldGrabBlocks(std::shared_ptr<EthereumPeer> _peer) const;
/// Attempt to begin syncing with the peer; first check the peer has a more difficlult chain to download, then start asking for hashes, then move to blocks
void attemptSync(std::shared_ptr<EthereumPeer> _peer);
/// Update our syncing state
void setState(std::shared_ptr<EthereumPeer> _peer, SyncState _s, bool _isSyncing = false, bool _needHelp = false);
/// Check if peer is main syncer
bool isSyncing(std::shared_ptr<EthereumPeer> _peer) const;
/// Check if we need (re-)syncing with the peer.
void noteNeedsSyncing(std::shared_ptr<EthereumPeer> _who);
/// Set main syncing peer
void changeSyncer(std::shared_ptr<EthereumPeer> _syncer, bool _needHelp);
/// Called when peer done downloading blocks
void noteDoneBlocks(std::shared_ptr<EthereumPeer> _who, bool _clemency);
/// Start chainhash sync
virtual void syncHashes(std::shared_ptr<EthereumPeer> _peer);
/// Request subchain, no-op for pv60
virtual void requestSubchain(std::shared_ptr<EthereumPeer> /*_peer*/) {}
/// Abort syncing
void abortSync();
/// Reset hash chain syncing
void resetSync();
bool invariants() const override;
h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer.
h256 m_syncingLastReceivedHash; ///< Hash most recently received from peer.
h256 m_syncingLatestHash; ///< Latest block's hash of the peer we are syncing to, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty of the peer we aresyncing to, as of the current sync.
std::weak_ptr<EthereumPeer> m_syncer; ///< Peer we are currently syncing with
};
/**
* @brief Syncrhonization over PV61. Selects a single peer and requests every c_hashSubchainSize hash, splitting the hashchain into subchains and downloading each subchain in parallel.
* Syncs to peers and keeps up to date
*/
class PV61Sync: public PV60Sync
{
public:
PV61Sync(EthereumHost& _host);
protected:
void restartSync() override;
void completeSync() override;
void requestSubchain(std::shared_ptr<EthereumPeer> _peer) override;
void syncHashes(std::shared_ptr<EthereumPeer> _peer) override;
void onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) override;
void onPeerAborting() override;
SyncStatus status() const override;
bool invariants() const override;
private:
/// Called when subchain is complete. Check if if hashchain is fully downloaded and proceed to downloading blocks
void completeSubchain(std::shared_ptr<EthereumPeer> _peer, unsigned _n);
/// Find a subchain for peers to downloading
void requestSubchains();
/// Check if downloading hashes in parallel
bool isPV61Syncing() const;
struct SubChain
{
h256s hashes; ///< List of subchain hashes
h256 lastHash; ///< Last requested subchain hash
};
std::map<unsigned, SubChain> m_completeChainMap; ///< Fully downloaded subchains
std::map<unsigned, SubChain> m_readyChainMap; ///< Subchains ready for download
std::map<unsigned, SubChain> m_downloadingChainMap; ///< Subchains currently being downloading. In sync with m_chainSyncPeers
std::map<std::weak_ptr<EthereumPeer>, unsigned, std::owner_less<std::weak_ptr<EthereumPeer>>> m_chainSyncPeers; ///< Peers to m_downloadingSubchain number map
h256Hash m_knownHashes; ///< Subchain start markers. Used to track suchain completion
unsigned m_syncingBlockNumber = 0; ///< Current subchain marker
bool m_hashScanComplete = false; ///< True if leading peer completed hashchain scan and we have a list of subchains ready
};
std::ostream& operator<<(std::ostream& _out, SyncStatus const& _sync);
}
}

43
libethereum/BlockDetails.cpp

@ -1,43 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockDetails.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "BlockDetails.h"
#include <libdevcore/Common.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
BlockDetails::BlockDetails(RLP const& _r)
{
number = _r[0].toInt<unsigned>();
totalDifficulty = _r[1].toInt<u256>();
parent = _r[2].toHash<h256>();
children = _r[3].toVector<h256>();
size = _r.size();
}
bytes BlockDetails::rlp() const
{
auto ret = rlpList(number, totalDifficulty, parent, children);
size = ret.size();
return ret;
}

128
libethereum/BlockDetails.h

@ -1,128 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockDetails.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <unordered_map>
#include <libdevcore/db.h>
#include <libdevcore/Log.h>
#include <libdevcore/RLP.h>
#include "TransactionReceipt.h"
namespace dev
{
namespace eth
{
// TODO: OPTIMISE: constructors take bytes, RLP used only in necessary classes.
static const unsigned c_bloomIndexSize = 16;
static const unsigned c_bloomIndexLevels = 2;
struct BlockDetails
{
BlockDetails(): number(0), totalDifficulty(0) {}
BlockDetails(unsigned _n, u256 _tD, h256 _p, h256s _c): number(_n), totalDifficulty(_tD), parent(_p), children(_c) {}
BlockDetails(RLP const& _r);
bytes rlp() const;
bool isNull() const { return !totalDifficulty; }
explicit operator bool() const { return !isNull(); }
unsigned number;
u256 totalDifficulty;
h256 parent;
h256s children;
mutable unsigned size;
};
struct BlockLogBlooms
{
BlockLogBlooms() {}
BlockLogBlooms(RLP const& _r) { blooms = _r.toVector<LogBloom>(); size = _r.data().size(); }
bytes rlp() const { bytes r = dev::rlp(blooms); size = r.size(); return r; }
LogBlooms blooms;
mutable unsigned size;
};
struct BlocksBlooms
{
BlocksBlooms() {}
BlocksBlooms(RLP const& _r) { blooms = _r.toArray<LogBloom, c_bloomIndexSize>(); size = _r.data().size(); }
bytes rlp() const { bytes r = dev::rlp(blooms); size = r.size(); return r; }
std::array<LogBloom, c_bloomIndexSize> blooms;
mutable unsigned size;
};
struct BlockReceipts
{
BlockReceipts() {}
BlockReceipts(RLP const& _r) { for (auto const& i: _r) receipts.emplace_back(i.data()); size = _r.data().size(); }
bytes rlp() const { RLPStream s(receipts.size()); for (TransactionReceipt const& i: receipts) i.streamRLP(s); size = s.out().size(); return s.out(); }
TransactionReceipts receipts;
mutable unsigned size;
};
struct BlockHash
{
BlockHash() {}
BlockHash(h256 const& _h): value(_h) {}
BlockHash(RLP const& _r) { value = _r.toHash<h256>(); }
bytes rlp() const { return dev::rlp(value); }
h256 value;
static const unsigned size = 65;
};
struct TransactionAddress
{
TransactionAddress() {}
TransactionAddress(RLP const& _rlp) { blockHash = _rlp[0].toHash<h256>(); index = _rlp[1].toInt<unsigned>(); }
bytes rlp() const { RLPStream s(2); s << blockHash << index; return s.out(); }
explicit operator bool() const { return !!blockHash; }
h256 blockHash;
unsigned index = 0;
static const unsigned size = 67;
};
using BlockDetailsHash = std::unordered_map<h256, BlockDetails>;
using BlockLogBloomsHash = std::unordered_map<h256, BlockLogBlooms>;
using BlockReceiptsHash = std::unordered_map<h256, BlockReceipts>;
using TransactionAddressHash = std::unordered_map<h256, TransactionAddress>;
using BlockHashHash = std::unordered_map<uint64_t, BlockHash>;
using BlocksBloomsHash = std::unordered_map<h256, BlocksBlooms>;
static const BlockDetails NullBlockDetails;
static const BlockLogBlooms NullBlockLogBlooms;
static const BlockReceipts NullBlockReceipts;
static const TransactionAddress NullTransactionAddress;
static const BlockHash NullBlockHash;
static const BlocksBlooms NullBlocksBlooms;
}
}

564
libethereum/BlockQueue.cpp

@ -1,564 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockQueue.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "BlockQueue.h"
#include <thread>
#include <sstream>
#include <libdevcore/Log.h>
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
#include "BlockChain.h"
#include "VerifiedBlock.h"
#include "State.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
#ifdef _WIN32
const char* BlockQueueChannel::name() { return EthOrange "[]>"; }
#else
const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; }
#endif
const char* BlockQueueTraceChannel::name() { return EthOrange "▣ ▶"; }
size_t const c_maxKnownCount = 100000;
size_t const c_maxKnownSize = 128 * 1024 * 1024;
size_t const c_maxUnknownCount = 100000;
size_t const c_maxUnknownSize = 512 * 1024 * 1024; // Block size can be ~50kb
BlockQueue::BlockQueue():
m_unknownSize(0),
m_knownSize(0),
m_unknownCount(0),
m_knownCount(0)
{
// Allow some room for other activity
unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
for (unsigned i = 0; i < verifierThreads; ++i)
m_verifiers.emplace_back([=](){
setThreadName("verifier" + toString(i));
this->verifierBody();
});
}
BlockQueue::~BlockQueue()
{
m_deleting = true;
m_moreToVerify.notify_all();
for (auto& i: m_verifiers)
i.join();
}
void BlockQueue::clear()
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
Guard l2(m_verification);
m_readySet.clear();
m_drainingSet.clear();
m_verified.clear();
m_unverified.clear();
m_verifying.clear();
m_unknownSet.clear();
m_unknown.clear();
m_future.clear();
m_unknownSize = 0;
m_unknownCount = 0;
m_knownSize = 0;
m_knownCount = 0;
m_difficulty = 0;
m_drainingDifficulty = 0;
}
void BlockQueue::verifierBody()
{
while (!m_deleting)
{
UnverifiedBlock work;
{
unique_lock<Mutex> l(m_verification);
m_moreToVerify.wait(l, [&](){ return !m_unverified.empty() || m_deleting; });
if (m_deleting)
return;
swap(work, m_unverified.front());
m_unverified.pop_front();
BlockInfo bi;
bi.setSha3Uncles(work.hash);
bi.setParentHash(work.parentHash);
m_verifying.emplace_back(move(bi));
}
VerifiedBlock res;
swap(work.block, res.blockData);
try
{
res.verified = m_bc->verifyBlock(&res.blockData, m_onBad, ImportRequirements::OutOfOrderChecks);
}
catch (...)
{
// bad block.
// has to be this order as that's how invariants() assumes.
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.hash);
m_knownBad.insert(work.hash);
for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it)
if (it->verified.info.sha3Uncles() == work.hash)
{
m_verifying.erase(it);
goto OK1;
}
cwarn << "BlockQueue missing our job: was there a GM?";
OK1:;
drainVerified_WITH_BOTH_LOCKS();
continue;
}
bool ready = false;
{
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
if (!m_verifying.empty() && m_verifying.front().verified.info.sha3Uncles() == work.hash)
{
// we're next!
m_verifying.pop_front();
if (m_knownBad.count(res.verified.info.parentHash()))
{
m_readySet.erase(res.verified.info.hash());
m_knownBad.insert(res.verified.info.hash());
}
else
m_verified.emplace_back(move(res));
drainVerified_WITH_BOTH_LOCKS();
ready = true;
}
else
{
for (auto& i: m_verifying)
if (i.verified.info.sha3Uncles() == work.hash)
{
i = move(res);
goto OK;
}
cwarn << "BlockQueue missing our job: was there a GM?";
OK:;
}
}
if (ready)
m_onReady();
}
}
void BlockQueue::drainVerified_WITH_BOTH_LOCKS()
{
while (!m_verifying.empty() && !m_verifying.front().blockData.empty())
{
if (m_knownBad.count(m_verifying.front().verified.info.parentHash()))
{
m_readySet.erase(m_verifying.front().verified.info.hash());
m_knownBad.insert(m_verifying.front().verified.info.hash());
}
else
m_verified.emplace_back(move(m_verifying.front()));
m_verifying.pop_front();
}
}
ImportResult BlockQueue::import(bytesConstRef _block, bool _isOurs)
{
clog(BlockQueueTraceChannel) << std::this_thread::get_id();
// Check if we already know this block.
h256 h = BlockInfo::headerHashFromBlock(_block);
clog(BlockQueueTraceChannel) << "Queuing block" << h << "for import...";
UpgradableGuard l(m_lock);
if (m_readySet.count(h) || m_drainingSet.count(h) || m_unknownSet.count(h) || m_knownBad.count(h))
{
// Already know about this one.
clog(BlockQueueTraceChannel) << "Already known.";
return ImportResult::AlreadyKnown;
}
BlockInfo bi;
try
{
// TODO: quick verification of seal - will require BlockQueue to be templated on Sealer
// VERIFY: populates from the block and checks the block is internally coherent.
bi = m_bc->verifyBlock(_block, m_onBad, ImportRequirements::None).info;
}
catch (Exception const& _e)
{
cwarn << "Ignoring malformed block: " << diagnostic_information(_e);
return ImportResult::Malformed;
}
clog(BlockQueueTraceChannel) << "Block" << h << "is" << bi.number() << "parent is" << bi.parentHash();
// Check block doesn't already exist first!
if (m_bc->isKnown(h))
{
cblockq << "Already known in chain.";
return ImportResult::AlreadyInChain;
}
UpgradeGuard ul(l);
DEV_INVARIANT_CHECK;
// Check it's not in the future
(void)_isOurs;
if (bi.timestamp() > (u256)time(0)/* && !_isOurs*/)
{
m_future.insert(make_pair((unsigned)bi.timestamp(), make_pair(h, _block.toBytes())));
char buf[24];
time_t bit = (unsigned)bi.timestamp();
if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
buf[0] = '\0'; // empty if case strftime fails
clog(BlockQueueTraceChannel) << "OK - queued for future [" << bi.timestamp() << "vs" << time(0) << "] - will wait until" << buf;
m_unknownSize += _block.size();
m_unknownCount++;
m_difficulty += bi.difficulty();
bool unknown = !m_readySet.count(bi.parentHash()) && !m_drainingSet.count(bi.parentHash()) && !m_bc->isKnown(bi.parentHash());
return unknown ? ImportResult::FutureTimeUnknown : ImportResult::FutureTimeKnown;
}
else
{
// We now know it.
if (m_knownBad.count(bi.parentHash()))
{
m_knownBad.insert(bi.hash());
updateBad_WITH_LOCK(bi.hash());
// bad parent; this is bad too, note it as such
return ImportResult::BadChain;
}
else if (!m_readySet.count(bi.parentHash()) && !m_drainingSet.count(bi.parentHash()) && !m_bc->isKnown(bi.parentHash()))
{
// We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on.
clog(BlockQueueTraceChannel) << "OK - queued as unknown parent:" << bi.parentHash();
m_unknown.insert(make_pair(bi.parentHash(), make_pair(h, _block.toBytes())));
m_unknownSet.insert(h);
m_unknownSize += _block.size();
m_difficulty += bi.difficulty();
m_unknownCount++;
return ImportResult::UnknownParent;
}
else
{
// If valid, append to blocks.
clog(BlockQueueTraceChannel) << "OK - ready for chain insertion.";
DEV_GUARDED(m_verification)
m_unverified.push_back(UnverifiedBlock { h, bi.parentHash(), _block.toBytes() });
m_moreToVerify.notify_one();
m_readySet.insert(h);
m_knownSize += _block.size();
m_difficulty += bi.difficulty();
m_knownCount++;
noteReady_WITH_LOCK(h);
return ImportResult::Success;
}
}
}
void BlockQueue::updateBad_WITH_LOCK(h256 const& _bad)
{
DEV_INVARIANT_CHECK;
DEV_GUARDED(m_verification)
{
collectUnknownBad_WITH_BOTH_LOCKS(_bad);
bool moreBad = true;
while (moreBad)
{
moreBad = false;
std::deque<VerifiedBlock> oldVerified;
swap(m_verified, oldVerified);
for (auto& b: oldVerified)
if (m_knownBad.count(b.verified.info.parentHash()) || m_knownBad.count(b.verified.info.hash()))
{
m_knownBad.insert(b.verified.info.hash());
m_readySet.erase(b.verified.info.hash());
collectUnknownBad_WITH_BOTH_LOCKS(b.verified.info.hash());
moreBad = true;
}
else
m_verified.push_back(std::move(b));
std::deque<UnverifiedBlock> oldUnverified;
swap(m_unverified, oldUnverified);
for (auto& b: oldUnverified)
if (m_knownBad.count(b.parentHash) || m_knownBad.count(b.hash))
{
m_knownBad.insert(b.hash);
m_readySet.erase(b.hash);
collectUnknownBad_WITH_BOTH_LOCKS(b.hash);
moreBad = true;
}
else
m_unverified.push_back(std::move(b));
std::deque<VerifiedBlock> oldVerifying;
swap(m_verifying, oldVerifying);
for (auto& b: oldVerifying)
if (m_knownBad.count(b.verified.info.parentHash()) || m_knownBad.count(b.verified.info.sha3Uncles()))
{
h256 const& h = b.blockData.size() != 0 ? b.verified.info.hash() : b.verified.info.sha3Uncles();
m_knownBad.insert(h);
m_readySet.erase(h);
collectUnknownBad_WITH_BOTH_LOCKS(h);
moreBad = true;
}
else
m_verifying.push_back(std::move(b));
}
}
}
void BlockQueue::collectUnknownBad_WITH_BOTH_LOCKS(h256 const& _bad)
{
list<h256> badQueue(1, _bad);
while (!badQueue.empty())
{
auto r = m_unknown.equal_range(badQueue.front());
badQueue.pop_front();
for (auto it = r.first; it != r.second; ++it)
{
m_unknownSize -= it->second.second.size();
m_unknownCount--;
auto newBad = it->second.first;
m_unknownSet.erase(newBad);
m_knownBad.insert(newBad);
badQueue.push_back(newBad);
}
m_unknown.erase(r.first, r.second);
}
}
bool BlockQueue::doneDrain(h256s const& _bad)
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
m_drainingSet.clear();
m_difficulty -= m_drainingDifficulty;
m_drainingDifficulty = 0;
if (_bad.size())
{
// at least one of them was bad.
m_knownBad += _bad;
for (h256 const& b : _bad)
updateBad_WITH_LOCK(b);
}
return !m_readySet.empty();
}
void BlockQueue::tick()
{
vector<pair<h256, bytes>> todo;
{
UpgradableGuard l(m_lock);
if (m_future.empty())
return;
cblockq << "Checking past-future blocks...";
unsigned t = time(0);
if (t <= m_future.begin()->first)
return;
cblockq << "Past-future blocks ready.";
{
UpgradeGuard l2(l);
DEV_INVARIANT_CHECK;
auto end = m_future.lower_bound(t);
for (auto i = m_future.begin(); i != end; ++i)
{
m_unknownSize -= i->second.second.size();
m_unknownCount--;
todo.push_back(move(i->second));
}
m_future.erase(m_future.begin(), end);
}
}
cblockq << "Importing" << todo.size() << "past-future blocks.";
for (auto const& b: todo)
import(&b.second);
}
template <class T> T advanced(T _t, unsigned _n)
{
std::advance(_t, _n);
return _t;
}
QueueStatus BlockQueue::blockStatus(h256 const& _h) const
{
ReadGuard l(m_lock);
return
m_readySet.count(_h) ?
QueueStatus::Ready :
m_drainingSet.count(_h) ?
QueueStatus::Importing :
m_unknownSet.count(_h) ?
QueueStatus::UnknownParent :
m_knownBad.count(_h) ?
QueueStatus::Bad :
QueueStatus::Unknown;
}
bool BlockQueue::knownFull() const
{
return m_knownSize > c_maxKnownSize || m_knownCount > c_maxKnownCount;
}
bool BlockQueue::unknownFull() const
{
return m_unknownSize > c_maxUnknownSize || m_unknownCount > c_maxUnknownCount;
}
void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
{
bool wasFull = false;
DEV_WRITE_GUARDED(m_lock)
{
DEV_INVARIANT_CHECK;
wasFull = knownFull();
if (m_drainingSet.empty())
{
m_drainingDifficulty = 0;
DEV_GUARDED(m_verification)
{
o_out.resize(min<unsigned>(_max, m_verified.size()));
for (unsigned i = 0; i < o_out.size(); ++i)
swap(o_out[i], m_verified[i]);
m_verified.erase(m_verified.begin(), advanced(m_verified.begin(), o_out.size()));
}
for (auto const& bs: o_out)
{
// TODO: @optimise use map<h256, bytes> rather than vector<bytes> & set<h256>.
auto h = bs.verified.info.hash();
m_drainingSet.insert(h);
m_drainingDifficulty += bs.verified.info.difficulty();
m_readySet.erase(h);
m_knownSize -= bs.verified.block.size();
m_knownCount--;
}
}
}
if (wasFull && !knownFull())
m_onRoomAvailable();
}
bool BlockQueue::invariants() const
{
Guard l(m_verification);
if (!(m_readySet.size() == m_verified.size() + m_unverified.size() + m_verifying.size()))
{
std::stringstream s;
s << "Failed BlockQueue invariant: m_readySet: " << m_readySet.size() << " m_verified: " << m_verified.size() << " m_unverified: " << m_unverified.size() << " m_verifying" << m_verifying.size();
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment(s.str()));
}
return true;
}
void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
{
DEV_INVARIANT_CHECK;
list<h256> goodQueue(1, _good);
bool notify = false;
while (!goodQueue.empty())
{
auto r = m_unknown.equal_range(goodQueue.front());
goodQueue.pop_front();
for (auto it = r.first; it != r.second; ++it)
{
DEV_GUARDED(m_verification)
m_unverified.push_back(UnverifiedBlock { it->second.first, it->first, it->second.second });
m_knownSize += it->second.second.size();
m_knownCount++;
m_unknownSize -= it->second.second.size();
m_unknownCount--;
auto newReady = it->second.first;
m_unknownSet.erase(newReady);
m_readySet.insert(newReady);
goodQueue.push_back(newReady);
notify = true;
}
m_unknown.erase(r.first, r.second);
}
if (notify)
m_moreToVerify.notify_all();
}
void BlockQueue::retryAllUnknown()
{
WriteGuard l(m_lock);
DEV_INVARIANT_CHECK;
for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it)
{
DEV_GUARDED(m_verification)
m_unverified.push_back(UnverifiedBlock { it->second.first, it->first, it->second.second });
auto newReady = it->second.first;
m_unknownSet.erase(newReady);
m_readySet.insert(newReady);
m_knownCount++;
m_moreToVerify.notify_one();
}
m_unknown.clear();
m_knownSize += m_unknownSize;
m_unknownSize = 0;
m_unknownCount = 0;
m_moreToVerify.notify_all();
}
std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _bqs)
{
_out << "importing: " << _bqs.importing << endl;
_out << "verified: " << _bqs.verified << endl;
_out << "verifying: " << _bqs.verifying << endl;
_out << "unverified: " << _bqs.unverified << endl;
_out << "future: " << _bqs.future << endl;
_out << "unknown: " << _bqs.unknown << endl;
_out << "bad: " << _bqs.bad << endl;
return _out;
}
u256 BlockQueue::difficulty() const
{
UpgradableGuard l(m_lock);
return m_difficulty;
}
bool BlockQueue::isActive() const
{
UpgradableGuard l(m_lock);
if (m_readySet.empty() && m_drainingSet.empty())
DEV_GUARDED(m_verification)
if (m_verified.empty() && m_verifying.empty() && m_unverified.empty())
return false;
return true;
}

176
libethereum/BlockQueue.h

@ -1,176 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BlockQueue.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <condition_variable>
#include <thread>
#include <deque>
#include <boost/thread.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libethcore/Common.h>
#include <libdevcore/Guards.h>
#include <libethcore/Common.h>
#include <libethcore/BlockInfo.h>
#include "VerifiedBlock.h"
namespace dev
{
namespace eth
{
class BlockChain;
struct BlockQueueChannel: public LogChannel { static const char* name(); static const int verbosity = 4; };
struct BlockQueueTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 7; };
#define cblockq dev::LogOutputStream<dev::eth::BlockQueueChannel, true>()
struct BlockQueueStatus
{
size_t importing;
size_t verified;
size_t verifying;
size_t unverified;
size_t future;
size_t unknown;
size_t bad;
};
enum class QueueStatus
{
Ready,
Importing,
UnknownParent,
Bad,
Unknown
};
/**
* @brief A queue of blocks. Sits between network or other I/O and the BlockChain.
* Sorts them ready for blockchain insertion (with the BlockChain::sync() method).
* @threadsafe
*/
class BlockQueue: HasInvariants
{
public:
BlockQueue();
~BlockQueue();
void setChain(BlockChain const& _bc) { m_bc = &_bc; }
/// Import a block into the queue.
ImportResult import(bytesConstRef _block, bool _isOurs = false);
/// Notes that time has moved on and some blocks that used to be "in the future" may no be valid.
void tick();
/// Grabs at most @a _max of the blocks that are ready, giving them in the correct order for insertion into the chain.
/// Don't forget to call doneDrain() once you're done importing.
void drain(std::vector<VerifiedBlock>& o_out, unsigned _max);
/// Must be called after a drain() call. Notes that the drained blocks have been imported into the blockchain, so we can forget about them.
/// @returns true iff there are additional blocks ready to be processed.
bool doneDrain(h256s const& _knownBad = h256s());
/// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain).
void noteReady(h256 const& _b) { WriteGuard l(m_lock); noteReady_WITH_LOCK(_b); }
/// Force a retry of all the blocks with unknown parents.
void retryAllUnknown();
/// Get information on the items queued.
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_readySet.size(), m_unknownSet.size()); }
/// Clear everything.
void clear();
/// Return first block with an unknown parent.
h256 firstUnknown() const { ReadGuard l(m_lock); return m_unknownSet.size() ? *m_unknownSet.begin() : h256(); }
/// Get some infomration on the current status.
BlockQueueStatus status() const { ReadGuard l(m_lock); Guard l2(m_verification); return BlockQueueStatus{m_drainingSet.size(), m_verified.size(), m_verifying.size(), m_unverified.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; }
/// Get some infomration on the given block's status regarding us.
QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler<> onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler<> onRoomAvailable(T const& _t) { return m_onRoomAvailable.add(_t); }
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
bool knownFull() const;
bool unknownFull() const;
u256 difficulty() const; // Total difficulty of queueud blocks
bool isActive() const;
private:
struct UnverifiedBlock
{
h256 hash;
h256 parentHash;
bytes block;
};
void noteReady_WITH_LOCK(h256 const& _b);
bool invariants() const override;
void verifierBody();
void collectUnknownBad_WITH_BOTH_LOCKS(h256 const& _bad);
void updateBad_WITH_LOCK(h256 const& _bad);
void drainVerified_WITH_BOTH_LOCKS();
BlockChain const* m_bc; ///< The blockchain into which our imports go.
mutable boost::shared_mutex m_lock; ///< General lock for the sets, m_future and m_unknown.
h256Hash m_drainingSet; ///< All blocks being imported.
h256Hash m_readySet; ///< All blocks ready for chain import.
h256Hash m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain.
std::unordered_multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears.
h256Hash m_knownBad; ///< Set of blocks that we know will never be valid.
std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp
Signal<> m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal<> m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified.
std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry.
std::deque<VerifiedBlock> m_verified; ///< List of blocks, in correct order, verified and ready for chain-import.
std::deque<VerifiedBlock> m_verifying; ///< List of blocks being verified; as long as the block component (bytes) is empty, it's not finished.
std::deque<UnverifiedBlock> m_unverified; ///< List of <block hash, parent hash, block data> in correct order, ready for verification.
std::vector<std::thread> m_verifiers; ///< Threads who only verify.
bool m_deleting = false; ///< Exit condition for verifiers.
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
std::atomic<size_t> m_unknownSize; ///< Tracks total size in bytes of all unknown blocks
std::atomic<size_t> m_knownSize; ///< Tracks total size in bytes of all known blocks;
std::atomic<size_t> m_unknownCount; ///< Tracks total count of unknown blocks. Used to avoid additional syncing
std::atomic<size_t> m_knownCount; ///< Tracks total count of known blocks. Used to avoid additional syncing
u256 m_difficulty; ///< Total difficulty of blocks in the queue
u256 m_drainingDifficulty; ///< Total difficulty of blocks in draining
};
std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);
}
}

43
libethereum/CMakeLists.txt

@ -1,43 +0,0 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
if (JSONRPC)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
endif()
set(EXECUTABLE ethereum)
file(GLOB HEADERS "*.h")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} whisper)
target_link_libraries(${EXECUTABLE} p2p)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
endif()
endif()
if (CMAKE_COMPILER_IS_MINGW)
target_link_libraries(${EXECUTABLE} ssp shlwapi)
endif()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

102
libethereum/CachedAddressState.cpp

@ -1,102 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CachedAddressState.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "CachedAddressState.h"
#include <libdevcore/TrieDB.h>
#include <libdevcrypto/Common.h>
#include <libdevcrypto/OverlayDB.h>
#include "Account.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
bool CachedAddressState::exists() const
{
return (m_r && (!m_s || m_s->isAlive())) || (m_s && m_s->isAlive());
}
u256 CachedAddressState::balance() const
{
return m_r ? m_s ? m_s->balance() : m_r[1].toInt<u256>() : 0;
}
u256 CachedAddressState::nonce() const
{
return m_r ? m_s ? m_s->nonce() : m_r[0].toInt<u256>() : 0;
}
bytes CachedAddressState::code() const
{
if (m_s && m_s->codeCacheValid())
return m_s->code();
h256 h = m_r ? m_s ? m_s->codeHash() : m_r[3].toHash<h256>() : EmptySHA3;
return h == EmptySHA3 ? bytes() : asBytes(m_o->lookup(h));
}
std::map<u256, u256> CachedAddressState::storage() const
{
std::map<u256, u256> ret;
if (m_r)
{
SecureTrieDB<h256, OverlayDB> memdb(const_cast<OverlayDB*>(m_o), m_r[2].toHash<h256>()); // promise we won't alter the overlay! :)
for (auto const& j: memdb)
ret[j.first] = RLP(j.second).toInt<u256>();
}
if (m_s)
for (auto const& j: m_s->storageOverlay())
if ((!ret.count(j.first) && j.second) || (ret.count(j.first) && ret.at(j.first) != j.second))
ret[j.first] = j.second;
return ret;
}
AccountDiff CachedAddressState::diff(CachedAddressState const& _c)
{
AccountDiff ret;
ret.exist = Diff<bool>(exists(), _c.exists());
ret.balance = Diff<u256>(balance(), _c.balance());
ret.nonce = Diff<u256>(nonce(), _c.nonce());
ret.code = Diff<bytes>(code(), _c.code());
auto st = storage();
auto cst = _c.storage();
auto it = st.begin();
auto cit = cst.begin();
while (it != st.end() || cit != cst.end())
{
if (it != st.end() && cit != cst.end() && it->first == cit->first && (it->second || cit->second) && (it->second != cit->second))
ret.storage[it->first] = Diff<u256>(it->second, cit->second);
else if (it != st.end() && (cit == cst.end() || it->first < cit->first) && it->second)
ret.storage[it->first] = Diff<u256>(it->second, 0);
else if (cit != cst.end() && (it == st.end() || it->first > cit->first) && cit->second)
ret.storage[cit->first] = Diff<u256>(0, cit->second);
if (it == st.end())
++cit;
else if (cit == cst.end())
++it;
else if (it->first < cit->first)
++it;
else if (it->first > cit->first)
++cit;
else
++it, ++cit;
}
return ret;
}

63
libethereum/CachedAddressState.h

@ -1,63 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CachedAddressState.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <string>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include "AccountDiff.h"
namespace dev
{
class OverlayDB;
namespace eth
{
class Account;
class CachedAddressState
{
public:
CachedAddressState(std::string const& _rlp, Account const* _s, OverlayDB const* _o): m_rS(_rlp), m_r(m_rS), m_s(_s), m_o(_o) {}
bool exists() const;
u256 balance() const;
u256 nonce() const;
bytes code() const;
// TODO: DEPRECATE.
std::map<u256, u256> storage() const;
AccountDiff diff(CachedAddressState const& _c);
private:
std::string m_rS;
RLP m_r;
Account const* m_s;
OverlayDB const* m_o;
};
}
}

156
libethereum/CanonBlockChain.cpp

@ -1,156 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CanonBlockChain.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "CanonBlockChain.h"
#include <boost/filesystem.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/FileSystem.h>
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/Params.h>
#include <liblll/Compiler.h>
#include <test/JsonSpiritHeaders.h>
#include "GenesisInfo.h"
#include "State.h"
#include "Defaults.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace js = json_spirit;
unique_ptr<Ethash::BlockHeader> CanonBlockChain<Ethash>::s_genesis;
boost::shared_mutex CanonBlockChain<Ethash>::x_genesis;
Nonce CanonBlockChain<Ethash>::s_nonce(u64(42));
string CanonBlockChain<Ethash>::s_genesisStateJSON;
bytes CanonBlockChain<Ethash>::s_genesisExtraData;
u256 CanonBlockChain<Ethash>::s_genesisDifficulty;
u256 CanonBlockChain<Ethash>::s_genesisGasLimit;
CanonBlockChain<Ethash>::CanonBlockChain(std::string const& _path, WithExisting _we, ProgressCallback const& _pc):
FullBlockChain<Ethash>(createGenesisBlock(), createGenesisState(), _path)
{
BlockChain::openDatabase(_path, _we, _pc);
}
void CanonBlockChain<Ethash>::reopen(WithExisting _we, ProgressCallback const& _pc)
{
close();
open(createGenesisBlock(), createGenesisState(), m_dbPath);
openDatabase(m_dbPath, _we, _pc);
}
bytes CanonBlockChain<Ethash>::createGenesisBlock()
{
RLPStream block(3);
h256 stateRoot;
{
MemoryDB db;
SecureTrieDB<Address, MemoryDB> state(&db);
state.init();
dev::eth::commit(createGenesisState(), state);
stateRoot = state.root();
}
js::mValue val;
json_spirit::read_string(s_genesisStateJSON.empty() ? c_network == Network::Frontier ? c_genesisInfoFrontier : c_genesisInfoOlympic : s_genesisStateJSON, val);
js::mObject genesis = val.get_obj();
h256 mixHash(genesis["mixhash"].get_str());
h256 parentHash(genesis["parentHash"].get_str());
h160 beneficiary(genesis["coinbase"].get_str());
u256 difficulty = fromBigEndian<u256>(fromHex(genesis["difficulty"].get_str()));
u256 gasLimit = fromBigEndian<u256>(fromHex(genesis["gasLimit"].get_str()));
u256 timestamp = fromBigEndian<u256>(fromHex(genesis["timestamp"].get_str()));
bytes extraData = fromHex(genesis["extraData"].get_str());
h64 nonce(genesis["nonce"].get_str());
block.appendList(15)
<< parentHash
<< EmptyListSHA3 // sha3(uncles)
<< beneficiary
<< stateRoot
<< EmptyTrie // transactions
<< EmptyTrie // receipts
<< LogBloom()
<< (s_genesisDifficulty ? s_genesisDifficulty : difficulty)
<< 0 // number
<< (s_genesisGasLimit ? s_genesisGasLimit : gasLimit)
<< 0 // gasUsed
<< timestamp
<< (s_genesisExtraData.empty() ? extraData : s_genesisExtraData)
<< mixHash
<< nonce;
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
}
AccountMap const& CanonBlockChain<Ethash>::createGenesisState()
{
static AccountMap s_ret;
if (s_ret.empty())
s_ret = jsonToAccountMap(s_genesisStateJSON.empty() ? c_network == Network::Frontier ? c_genesisInfoFrontier : c_genesisInfoOlympic : s_genesisStateJSON);
return s_ret;
}
void CanonBlockChain<Ethash>::setGenesis(std::string const& _json)
{
WriteGuard l(x_genesis);
s_genesisStateJSON = _json;
s_genesis.reset();
}
void CanonBlockChain<Ethash>::forceGenesisExtraData(bytes const& _genesisExtraData)
{
WriteGuard l(x_genesis);
s_genesisExtraData = _genesisExtraData;
s_genesis.reset();
}
void CanonBlockChain<Ethash>::forceGenesisDifficulty(u256 const& _genesisDifficulty)
{
WriteGuard l(x_genesis);
s_genesisDifficulty = _genesisDifficulty;
s_genesis.reset();
}
void CanonBlockChain<Ethash>::forceGenesisGasLimit(u256 const& _genesisGasLimit)
{
WriteGuard l(x_genesis);
s_genesisGasLimit = _genesisGasLimit;
s_genesis.reset();
}
Ethash::BlockHeader const& CanonBlockChain<Ethash>::genesis()
{
UpgradableGuard l(x_genesis);
if (!s_genesis)
{
auto gb = createGenesisBlock();
UpgradeGuard ul(l);
s_genesis.reset(new Ethash::BlockHeader);
s_genesis->populate(&gb, CheckEverything);
}
return *s_genesis;
}

124
libethereum/CanonBlockChain.h

@ -1,124 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CanonBlockChain.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <mutex>
#include <libdevcore/Log.h>
#include <libdevcore/Exceptions.h>
#include <libethcore/Common.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/Ethash.h>
#include <libdevcore/Guards.h>
#include "BlockDetails.h"
#include "Account.h"
#include "BlockChain.h"
namespace dev
{
namespace eth
{
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::unordered_map<Address, Account> const& genesisState();
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
* @threadsafe
* @todo Make not memory hog (should actually act as a cache and deallocate old entries).
*/
template <class Sealer>
class CanonBlockChain: public FullBlockChain<Sealer>
{
public:
CanonBlockChain(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()): CanonBlockChain<Sealer>(std::string(), _we, _pc) {}
CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()):
FullBlockChain<Sealer>(createGenesisBlock(), AccountMap(), _path)
{
BlockChain::openDatabase(_path, _we, _pc);
}
~CanonBlockChain() {}
/// @returns the genesis block as its RLP-encoded byte array.
/// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static bytes createGenesisBlock()
{
RLPStream block(3);
block.appendList(Sealer::BlockHeader::Fields)
<< h256() << EmptyListSHA3 << h160() << EmptyTrie << EmptyTrie << EmptyTrie << LogBloom() << 1 << 0 << (u256(1) << 255) << 0 << (unsigned)0 << std::string();
bytes sealFields = typename Sealer::BlockHeader().sealFieldsRLP();
block.appendRaw(sealFields, Sealer::BlockHeader::SealFields);
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
}
};
template <>
class CanonBlockChain<Ethash>: public FullBlockChain<Ethash>
{
public:
CanonBlockChain(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()): CanonBlockChain(std::string(), _we, _pc) {}
CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback());
~CanonBlockChain() {}
/// Reopen everything.
virtual void reopen(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback());
/// @returns the genesis block header.
static Ethash::BlockHeader const& genesis();
/// @returns the genesis block as its RLP-encoded byte array.
/// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static bytes createGenesisBlock();
/// @returns the genesis block as its RLP-encoded byte array.
/// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static AccountMap const& createGenesisState();
/// Alter all the genesis block's state by giving a JSON string with account details.
/// @warning Unless you're very careful, make sure you call this right at the start of the
/// program, before anything has had the chance to use this class at all.
static void setGenesis(std::string const& _genesisInfoJSON);
/// Override the genesis block's extraData field.
static void forceGenesisExtraData(bytes const& _genesisExtraData);
/// Override the genesis block's difficulty field.
static void forceGenesisDifficulty(u256 const& _genesisDifficulty);
/// Override the genesis block's gasLimit field.
static void forceGenesisGasLimit(u256 const& _genesisGasLimit);
private:
/// Static genesis info and its lock.
static boost::shared_mutex x_genesis;
static std::unique_ptr<Ethash::BlockHeader> s_genesis;
static Nonce s_nonce;
static std::string s_genesisStateJSON;
static bytes s_genesisExtraData;
static u256 s_genesisDifficulty;
static u256 s_genesisGasLimit;
};
}
}

954
libethereum/Client.cpp

@ -1,954 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Client.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Client.h"
#include <chrono>
#include <memory>
#include <thread>
#include <boost/filesystem.hpp>
#if ETH_JSONRPC || !ETH_TRUE
#include <jsonrpccpp/client.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
#endif
#include <libdevcore/Log.h>
#include <libdevcore/StructuredLogger.h>
#include <libp2p/Host.h>
#include <libethcore/Ethash.h>
#if ETH_JSONRPC || !ETH_TRUE
#include "Sentinel.h"
#endif
#include "Defaults.h"
#include "Executive.h"
#include "EthereumHost.h"
#include "Utility.h"
#include "Block.h"
#include "TransactionQueue.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;
std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r)
{
_out << "Since " << toString(_r.since) << " (" << std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - _r.since).count();
_out << "): " << _r.ticks << "ticks";
return _out;
}
#ifdef _WIN32
const char* ClientNote::name() { return EthTeal "^" EthBlue " i"; }
const char* ClientChat::name() { return EthTeal "^" EthWhite " o"; }
const char* ClientTrace::name() { return EthTeal "^" EthGray " O"; }
const char* ClientDetail::name() { return EthTeal "^" EthCoal " 0"; }
#else
const char* ClientNote::name() { return EthTeal "" EthBlue ""; }
const char* ClientChat::name() { return EthTeal "" EthWhite ""; }
const char* ClientTrace::name() { return EthTeal "" EthGray ""; }
const char* ClientDetail::name() { return EthTeal "" EthCoal ""; }
#endif
bool Client::s_shouldExit = false;
void Client::exitHandler(int)
{
s_shouldExit = true;
}
static const Addresses c_canaries =
{
Address("4bb7e8ae99b645c2b7860b8f3a2328aae28bd80a"), // gav
Address("1baf27b88c48dd02b744999cf3522766929d2b2a"), // vitalik
Address("a8edb1ac2c86d3d9d78f96cd18001f60df29e52c"), // jeff
Address("ace7813896a84d3f5f80223916a5353ab16e46e6") // christoph
};
Client::Client(std::shared_ptr<GasPricer> _gp):
Worker("eth", 0),
m_gp(_gp ? _gp : make_shared<TrivialGasPricer>())
{
}
Client::~Client()
{
stopWorking();
}
void Client::init(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId)
{
// Cannot be opened until after blockchain is open, since BlockChain may upgrade the database.
// TODO: consider returning the upgrade mechanism here. will delaying the opening of the blockchain database
// until after the construction.
m_stateDB = State::openDB(_dbPath, bc().genesisHash(), _forceAction);
// LAZY. TODO: move genesis state construction/commiting to stateDB openning and have this just take the root from the genesis block.
m_preMine = bc().genesisBlock(m_stateDB);
m_postMine = m_preMine;
m_bq.setChain(bc());
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_tqReplaced = m_tq.onReplaced([=](h256 const&){ m_needStateReset = true; });
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_bq.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
bc().setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
if (_forceAction == WithExisting::Rescue)
bc().rescue(m_stateDB);
m_gp->update(bc());
auto host = _extNet->registerCapability(make_shared<EthereumHost>(bc(), m_tq, m_bq, _networkId));
m_host = host;
_extNet->addCapability(host, EthereumHost::staticName(), EthereumHost::c_oldProtocolVersion); //TODO: remove this once v61+ protocol is common
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
doWork();
startWorking();
}
Block Client::asOf(h256 const& _block) const
{
try
{
Block ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return Block();
}
}
ImportResult Client::queueBlock(bytes const& _block, bool _isSafe)
{
if (m_bq.status().verified + m_bq.status().verifying + m_bq.status().unverified > 10000)
this_thread::sleep_for(std::chrono::milliseconds(500));
return m_bq.import(&_block, _isSafe);
}
tuple<ImportRoute, bool, unsigned> Client::syncQueue(unsigned _max)
{
stopWorking();
return bc().sync(m_bq, m_stateDB, _max);
}
void Client::onBadBlock(Exception& _ex) const
{
// BAD BLOCK!!!
bytes const* block = boost::get_error_info<errinfo_block>(_ex);
if (!block)
{
cwarn << "ODD: onBadBlock called but exception (" << _ex.what() << ") has no block in it.";
cwarn << boost::diagnostic_information(_ex, true);
return;
}
badBlock(*block, _ex.what());
#if ETH_JSONRPC || !ETH_TRUE
Json::Value report;
report["client"] = "cpp";
report["version"] = Version;
report["protocolVersion"] = c_protocolVersion;
report["databaseVersion"] = c_databaseVersion;
report["errortype"] = _ex.what();
report["block"] = toHex(*block);
// add the various hints.
if (unsigned const* uncleIndex = boost::get_error_info<errinfo_uncleIndex>(_ex))
{
// uncle that failed.
report["hints"]["uncleIndex"] = *uncleIndex;
}
else if (unsigned const* txIndex = boost::get_error_info<errinfo_transactionIndex>(_ex))
{
// transaction that failed.
report["hints"]["transactionIndex"] = *txIndex;
}
else
{
// general block failure.
}
if (string const* vmtraceJson = boost::get_error_info<errinfo_vmtrace>(_ex))
Json::Reader().parse(*vmtraceJson, report["hints"]["vmtrace"]);
if (vector<bytes> const* receipts = boost::get_error_info<errinfo_receipts>(_ex))
{
report["hints"]["receipts"] = Json::arrayValue;
for (auto const& r: *receipts)
report["hints"]["receipts"].append(toHex(r));
}
if (h256Hash const* excluded = boost::get_error_info<errinfo_unclesExcluded>(_ex))
{
report["hints"]["unclesExcluded"] = Json::arrayValue;
for (auto const& r: h256Set() + *excluded)
report["hints"]["unclesExcluded"].append(Json::Value(r.hex()));
}
#define DEV_HINT_ERRINFO(X) \
if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \
report["hints"][#X] = toString(*n)
#define DEV_HINT_ERRINFO_HASH(X) \
if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \
report["hints"][#X] = n->hex()
DEV_HINT_ERRINFO_HASH(hash256);
DEV_HINT_ERRINFO(uncleNumber);
DEV_HINT_ERRINFO(currentNumber);
DEV_HINT_ERRINFO(now);
DEV_HINT_ERRINFO(invalidSymbol);
DEV_HINT_ERRINFO(wrongAddress);
DEV_HINT_ERRINFO(comment);
DEV_HINT_ERRINFO(min);
DEV_HINT_ERRINFO(max);
DEV_HINT_ERRINFO(name);
DEV_HINT_ERRINFO(field);
DEV_HINT_ERRINFO(transaction);
DEV_HINT_ERRINFO(data);
DEV_HINT_ERRINFO(phase);
DEV_HINT_ERRINFO_HASH(nonce);
DEV_HINT_ERRINFO(difficulty);
DEV_HINT_ERRINFO(target);
DEV_HINT_ERRINFO_HASH(seedHash);
DEV_HINT_ERRINFO_HASH(mixHash);
if (tuple<h256, h256> const* r = boost::get_error_info<errinfo_ethashResult>(_ex))
{
report["hints"]["ethashResult"]["value"] = get<0>(*r).hex();
report["hints"]["ethashResult"]["mixHash"] = get<1>(*r).hex();
}
if (bytes const* ed = boost::get_error_info<errinfo_extraData>(_ex))
{
report["hints"]["extraData"] = toHex(*ed);
try
{
RLP r(*ed);
if (r[0].toInt<int>() == 0)
report["hints"]["minerVersion"] = r[1].toString();
}
catch (...) {}
}
DEV_HINT_ERRINFO(required);
DEV_HINT_ERRINFO(got);
DEV_HINT_ERRINFO_HASH(required_LogBloom);
DEV_HINT_ERRINFO_HASH(got_LogBloom);
DEV_HINT_ERRINFO_HASH(required_h256);
DEV_HINT_ERRINFO_HASH(got_h256);
cwarn << ("Report: \n" + Json::StyledWriter().write(report));
if (!m_sentinel.empty())
{
jsonrpc::HttpClient client(m_sentinel);
Sentinel rpc(client);
try
{
rpc.eth_badBlock(report);
}
catch (...)
{
cwarn << "Error reporting to sentinel. Sure the address" << m_sentinel << "is correct?";
}
}
#endif
}
bool Client::isChainBad() const
{
unsigned numberBad = 0;
for (auto const& a: c_canaries)
if (!!stateAt(a, 0))
numberBad++;
return numberBad >= 2;
}
bool Client::isUpgradeNeeded() const
{
return stateAt(c_canaries[0], 0) == 2;
}
void Client::setNetworkId(u256 _n)
{
if (auto h = m_host.lock())
h->setNetworkId(_n);
}
DownloadMan const* Client::downloadMan() const
{
if (auto h = m_host.lock())
return &(h->downloadMan());
return nullptr;
}
bool Client::isSyncing() const
{
if (auto h = m_host.lock())
return h->isSyncing();
return false;
}
bool Client::isMajorSyncing() const
{
// TODO: only return true if it is actually doing a proper chain sync.
if (auto h = m_host.lock())
return h->isSyncing();
return false;
}
void Client::startedWorking()
{
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
clog(ClientTrace) << "startedWorking()";
DEV_WRITE_GUARDED(x_preMine)
m_preMine.sync(bc());
DEV_READ_GUARDED(x_preMine)
{
DEV_WRITE_GUARDED(x_working)
m_working = m_preMine;
DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_preMine;
}
}
void Client::doneWorking()
{
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
DEV_WRITE_GUARDED(x_preMine)
m_preMine.sync(bc());
DEV_READ_GUARDED(x_preMine)
{
DEV_WRITE_GUARDED(x_working)
m_working = m_preMine;
DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_preMine;
}
}
void Client::reopenChain(WithExisting _we)
{
bool wasMining = isMining();
if (wasMining)
stopMining();
stopWorking();
m_tq.clear();
m_bq.clear();
m_sealEngine->cancelGeneration();
{
WriteGuard l(x_postMine);
WriteGuard l2(x_preMine);
WriteGuard l3(x_working);
m_preMine = Block();
m_postMine = Block();
m_working = Block();
m_stateDB = OverlayDB();
bc().reopen(_we);
m_stateDB = State::openDB(Defaults::dbPath(), bc().genesisHash(), _we);
m_preMine = bc().genesisBlock(m_stateDB);
m_postMine = m_preMine;
}
if (auto h = m_host.lock())
h->reset();
startedWorking();
doWork();
startWorking();
if (wasMining)
startMining();
}
void Client::clearPending()
{
DEV_WRITE_GUARDED(x_postMine)
{
if (!m_postMine.pending().size())
return;
m_tq.clear();
DEV_READ_GUARDED(x_preMine)
m_postMine = m_preMine;
}
startMining();
h256Hash changeds;
noteChanged(changeds);
}
template <class S, class T>
static S& filtersStreamOut(S& _out, T const& _fs)
{
_out << "{";
unsigned i = 0;
for (h256 const& f: _fs)
{
_out << (i++ ? ", " : "");
if (f == PendingChangedFilter)
_out << LogTag::Special << "pending";
else if (f == ChainChangedFilter)
_out << LogTag::Special << "chain";
else
_out << f;
}
_out << "}";
return _out;
}
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _sha3)
{
Guard l(x_filtersWatches);
io_changed.insert(PendingChangedFilter);
m_specialFilters.at(PendingChangedFilter).push_back(_sha3);
for (pair<h256 const, InstalledFilter>& i: m_filters)
{
// acceptable number.
auto m = i.second.filter.matches(_receipt);
if (m.size())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l));
io_changed.insert(i.first);
}
}
}
void Client::appendFromBlock(h256 const& _block, BlockPolarity _polarity, h256Hash& io_changed)
{
// TODO: more precise check on whether the txs match.
auto receipts = bc().receipts(_block).receipts;
Guard l(x_filtersWatches);
io_changed.insert(ChainChangedFilter);
m_specialFilters.at(ChainChangedFilter).push_back(_block);
for (pair<h256 const, InstalledFilter>& i: m_filters)
{
// acceptable number & looks like block may contain a matching log entry.
for (size_t j = 0; j < receipts.size(); j++)
{
auto tr = receipts[j];
auto m = i.second.filter.matches(tr);
if (m.size())
{
auto transactionHash = transaction(_block, j).sha3();
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, _block, (BlockNumber)bc().number(_block), transactionHash, j, 0, _polarity));
io_changed.insert(i.first);
}
}
}
}
void Client::setForceMining(bool _enable)
{
m_forceMining = _enable;
if (isMining())
startMining();
}
void Client::setShouldPrecomputeDAG(bool _precompute)
{
bytes trueBytes {1};
bytes falseBytes {0};
sealEngine()->setOption("precomputeDAG", _precompute ? trueBytes: falseBytes);
}
void Client::setTurboMining(bool _enable)
{
m_turboMining = _enable;
#if ETH_ETHASHCL || !ETH_TRUE
sealEngine()->setSealer(_enable ? "opencl" : "cpu");
#endif
if (isMining())
startMining();
}
bool Client::isMining() const
{
return Ethash::isWorking(m_sealEngine.get());
}
WorkingProgress Client::miningProgress() const
{
if (Ethash::isWorking(m_sealEngine.get()))
return Ethash::workingProgress(m_sealEngine.get());
return WorkingProgress();
}
u256 Client::hashrate() const
{
u256 r = externalHashrate();
if (Ethash::isWorking(m_sealEngine.get()))
r += Ethash::workingProgress(m_sealEngine.get()).rate();
return r;
}
std::list<MineInfo> Client::miningHistory()
{
//TODO: reimplement for CPU/GPU miner
return std::list<MineInfo> {};
}
ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice, Address const& _from)
{
ExecutionResult ret;
try
{
Block temp;
clog(ClientDetail) << "Nonce at " << _dest << " pre:" << m_preMine.transactionsFrom(_dest) << " post:" << m_postMine.transactionsFrom(_dest);
DEV_READ_GUARDED(x_postMine)
temp = m_postMine;
temp.mutableState().addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp);
e.setResultRecipient(ret);
if (!e.call(_dest, _from, _value, _gasPrice, &_data, _gas))
e.go();
e.finalize();
}
catch (...)
{
cwarn << "Client::call failed: " << boost::current_exception_diagnostic_information();
}
return ret;
}
unsigned static const c_syncMin = 1;
unsigned static const c_syncMax = 1000;
double static const c_targetDuration = 1;
void Client::syncBlockQueue()
{
cwork << "BQ ==> CHAIN ==> STATE";
ImportRoute ir;
unsigned count;
Timer t;
tie(ir, m_syncBlockQueue, count) = bc().sync(m_bq, m_stateDB, m_syncAmount);
double elapsed = t.elapsed();
if (count)
clog(ClientNote) << count << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (count / elapsed) << "blocks/s)";
if (elapsed > c_targetDuration * 1.1 && count > c_syncMin)
m_syncAmount = max(c_syncMin, count * 9 / 10);
else if (count == m_syncAmount && elapsed < c_targetDuration * 0.9 && m_syncAmount < c_syncMax)
m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1);
if (ir.liveBlocks.empty())
return;
onChainChanged(ir);
}
void Client::syncTransactionQueue()
{
// returns TransactionReceipts, once for each transaction.
cwork << "postSTATE <== TQ";
h256Hash changeds;
TransactionReceipts newPendingReceipts;
DEV_WRITE_GUARDED(x_working)
tie(newPendingReceipts, m_syncTransactionQueue) = m_working.sync(bc(), m_tq, *m_gp);
if (newPendingReceipts.empty())
return;
DEV_READ_GUARDED(x_working)
DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
DEV_READ_GUARDED(x_postMine)
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
// Tell farm about new transaction (i.e. restart mining).
onPostStateChanged();
// Tell watches about the new transactions.
noteChanged(changeds);
// Tell network about the new transactions.
if (auto h = m_host.lock())
h->noteNewTransactions();
}
void Client::onDeadBlocks(h256s const& _blocks, h256Hash& io_changed)
{
// insert transactions that we are declaring the dead part of the chain
for (auto const& h: _blocks)
{
clog(ClientTrace) << "Dead block:" << h;
for (auto const& t: bc().transactions(h))
{
clog(ClientTrace) << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t, IfDropped::Retry);
}
}
for (auto const& h: _blocks)
appendFromBlock(h, BlockPolarity::Dead, io_changed);
}
void Client::onNewBlocks(h256s const& _blocks, h256Hash& io_changed)
{
// remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: _blocks)
clog(ClientTrace) << "Live block:" << h;
if (auto h = m_host.lock())
h->noteNewBlocks();
for (auto const& h: _blocks)
appendFromBlock(h, BlockPolarity::Live, io_changed);
}
void Client::resyncStateFromChain()
{
// RESTART MINING
if (!isMajorSyncing())
{
bool preChanged = false;
Block newPreMine;
DEV_READ_GUARDED(x_preMine)
newPreMine = m_preMine;
// TODO: use m_postMine to avoid re-evaluating our own blocks.
preChanged = newPreMine.sync(bc());
if (preChanged || m_postMine.beneficiary() != m_preMine.beneficiary())
{
if (isMining())
clog(ClientTrace) << "New block on chain.";
DEV_WRITE_GUARDED(x_preMine)
m_preMine = newPreMine;
DEV_WRITE_GUARDED(x_working)
m_working = newPreMine;
DEV_READ_GUARDED(x_postMine)
for (auto const& t: m_postMine.pending())
{
clog(ClientTrace) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, IfDropped::Retry);
if (ir != ImportResult::Success)
onTransactionQueueReady();
}
DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
onPostStateChanged();
}
// Quick hack for now - the TQ at this point already has the prior pending transactions in it;
// we should resync with it manually until we are stricter about what constitutes "knowing".
onTransactionQueueReady();
}
}
void Client::resetState()
{
Block newPreMine;
DEV_READ_GUARDED(x_preMine)
newPreMine = m_preMine;
DEV_WRITE_GUARDED(x_working)
m_working = newPreMine;
DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
onPostStateChanged();
onTransactionQueueReady();
}
void Client::onChainChanged(ImportRoute const& _ir)
{
h256Hash changeds;
onDeadBlocks(_ir.deadBlocks, changeds);
for (auto const& t: _ir.goodTranactions)
{
clog(ClientTrace) << "Safely dropping transaction " << t.sha3();
m_tq.dropGood(t);
}
onNewBlocks(_ir.liveBlocks, changeds);
resyncStateFromChain();
noteChanged(changeds);
}
bool Client::remoteActive() const
{
return chrono::system_clock::now() - m_lastGetWork < chrono::seconds(30);
}
void Client::onPostStateChanged()
{
clog(ClientTrace) << "Post state changed.";
rejigMining();
m_remoteWorking = false;
}
void Client::startMining()
{
clog(ClientNote) << "MiningBenefactor: " << address();
if (address() != Address())
{
m_wouldMine = true;
rejigMining();
}
else
clog(ClientNote) << "You need to set a MiningBenefactor in order to mine!";
}
void Client::rejigMining()
{
if ((wouldMine() || remoteActive()) && !isMajorSyncing() && (!isChainBad() || mineOnBadChain()) /*&& (forceMining() || transactionsWaiting())*/)
{
clog(ClientTrace) << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working)
m_working.commitToSeal(bc(), m_extraData);
DEV_READ_GUARDED(x_working)
{
DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
m_miningInfo = m_postMine.info();
}
if (m_wouldMine)
m_sealEngine->generateSeal(m_miningInfo);
}
if (!m_wouldMine)
m_sealEngine->cancelGeneration();
}
void Client::noteChanged(h256Hash const& _filters)
{
Guard l(x_filtersWatches);
if (_filters.size())
filtersStreamOut(cwatch << "noteChanged:", _filters);
// accrue all changes left in each filter into the watches.
for (auto& w: m_watches)
if (_filters.count(w.second.id))
{
if (m_filters.count(w.second.id))
{
cwatch << "!!!" << w.first << w.second.id.abridged();
w.second.changes += m_filters.at(w.second.id).changes;
}
else if (m_specialFilters.count(w.second.id))
for (h256 const& hash: m_specialFilters.at(w.second.id))
{
cwatch << "!!!" << w.first << LogTag::Special << (w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, hash));
}
}
// clear the filters now.
for (auto& i: m_filters)
i.second.changes.clear();
for (auto& i: m_specialFilters)
i.second.clear();
}
void Client::doWork()
{
bool t = true;
if (m_syncBlockQueue.compare_exchange_strong(t, false))
syncBlockQueue();
if (m_needStateReset)
{
resetState();
m_needStateReset = false;
}
t = true;
if (!isSyncing() && !m_remoteWorking && m_syncTransactionQueue.compare_exchange_strong(t, false))
syncTransactionQueue();
tick();
if (!m_syncBlockQueue && !m_syncTransactionQueue)
{
std::unique_lock<std::mutex> l(x_signalled);
m_signalled.wait_for(l, chrono::seconds(1));
}
}
void Client::tick()
{
if (chrono::system_clock::now() - m_lastTick > chrono::seconds(1))
{
m_report.ticks++;
checkWatchGarbage();
m_bq.tick();
m_lastTick = chrono::system_clock::now();
if (m_report.ticks == 15)
clog(ClientTrace) << activityReport();
}
}
void Client::checkWatchGarbage()
{
if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5))
{
// watches garbage collection
vector<unsigned> toUninstall;
DEV_GUARDED(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))
{
toUninstall.push_back(key);
clog(ClientTrace) << "GC: Uninstall" << key << "(" << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
}
for (auto i: toUninstall)
uninstallWatch(i);
// blockchain GC
bc().garbageCollect();
m_lastGarbageCollection = chrono::system_clock::now();
}
}
void Client::prepareForTransaction()
{
startWorking();
}
Block Client::block(h256 const& _blockHash, PopulationStatistics* o_stats) const
{
try
{
Block ret(m_stateDB);
PopulationStatistics s = ret.populateFromChain(bc(), _blockHash);
if (o_stats)
swap(s, *o_stats);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_blockHash));
onBadBlock(ex);
return Block();
}
}
State Client::state(unsigned _txi, h256 const& _blockHash) const
{
try
{
return block(_blockHash).fromPending(_txi);
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_blockHash));
onBadBlock(ex);
return State();
}
}
eth::State Client::state(unsigned _txi) const
{
DEV_READ_GUARDED(x_postMine)
return m_postMine.fromPending(_txi);
assert(false);
return State();
}
void Client::flushTransactions()
{
doWork();
}
SyncStatus Client::syncStatus() const
{
auto h = m_host.lock();
return h ? h->status() : SyncStatus();
}
bool Client::submitSealed(bytes const& _header)
{
DEV_WRITE_GUARDED(x_working)
if (!m_working.sealBlock(_header))
return false;
bytes newBlock;
DEV_READ_GUARDED(x_working)
{
DEV_WRITE_GUARDED(x_postMine)
m_postMine = m_working;
newBlock = m_working.blockData();
}
// OPTIMISE: very inefficient to not utilise the existing OverlayDB in m_postMine that contains all trie changes.
return m_bq.import(&newBlock, true) == ImportResult::Success;
}
std::tuple<h256, h256, h256> EthashClient::getEthashWork()
{
// lock the work so a later submission isn't invalidated by processing a transaction elsewhere.
// this will be reset as soon as a new block arrives, allowing more transactions to be processed.
bool oldShould = shouldServeWork();
m_lastGetWork = chrono::system_clock::now();
if (!m_mineOnBadChain && isChainBad())
return std::tuple<h256, h256, h256>();
// if this request has made us bother to serve work, prep it now.
if (!oldShould && shouldServeWork())
onPostStateChanged();
else
// otherwise, set this to true so that it gets prepped next time.
m_remoteWorking = true;
Ethash::BlockHeader bh = Ethash::BlockHeader(m_miningInfo);
Ethash::manuallySetWork(m_sealEngine.get(), bh);
return std::tuple<h256, h256, h256>(bh.hashWithout(), bh.seedHash(), bh.boundary());
}
bool EthashClient::submitEthashWork(h256 const& _mixHash, h64 const& _nonce)
{
Ethash::manuallySubmitWork(m_sealEngine.get(), _mixHash, _nonce);
return true;
}

425
libethereum/Client.h

@ -1,425 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Client.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <thread>
#include <condition_variable>
#include <mutex>
#include <list>
#include <atomic>
#include <string>
#include <array>
#include <boost/utility.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Worker.h>
#include <libethcore/Params.h>
#include <libethcore/Sealer.h>
#include <libethcore/ABI.h>
#include <libp2p/Common.h>
#include "CanonBlockChain.h"
#include "Block.h"
#include "CommonNet.h"
#include "ClientBase.h"
namespace dev
{
namespace eth
{
class Client;
class DownloadMan;
enum ClientWorkState
{
Active = 0,
Deleting,
Deleted
};
struct ClientNote: public LogChannel { static const char* name(); static const int verbosity = 2; };
struct ClientChat: public LogChannel { static const char* name(); static const int verbosity = 4; };
struct ClientTrace: public LogChannel { static const char* name(); static const int verbosity = 7; };
struct ClientDetail: public LogChannel { static const char* name(); static const int verbosity = 14; };
struct ActivityReport
{
unsigned ticks = 0;
std::chrono::system_clock::time_point since = std::chrono::system_clock::now();
};
std::ostream& operator<<(std::ostream& _out, ActivityReport const& _r);
/**
* @brief Main API hub for interfacing with Ethereum.
* Not to be used directly - subclass.
*/
class Client: public ClientBase, protected Worker
{
public:
/// Destructor.
virtual ~Client();
/// Resets the gas pricer to some other object.
void setGasPricer(std::shared_ptr<GasPricer> _gp) { m_gp = _gp; }
std::shared_ptr<GasPricer> gasPricer() const { return m_gp; }
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() override;
/// Queues a block for import.
ImportResult queueBlock(bytes const& _block, bool _isSafe = false);
using Interface::call; // to remove warning about hiding virtual function
/// Makes the given call. Nothing is recorded into the state. This cheats by creating a null address and endowing it with a lot of ETH.
ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether, Address const& _from = Address());
/// Get the remaining gas limit in this block.
virtual u256 gasLimitRemaining() const override { return m_postMine.gasLimitRemaining(); }
/// Get the gas bid price
virtual u256 gasBidPrice() const override { return m_gp->bid(); }
// [PRIVATE API - only relevant for base clients, not available in general]
/// Get the block.
dev::eth::Block block(h256 const& _blockHash, PopulationStatistics* o_stats = nullptr) const;
/// Get the state of the given block part way through execution, immediately before transaction
/// index @a _txi.
dev::eth::State state(unsigned _txi, h256 const& _block) const;
/// Get the state of the currently pending block part way through execution, immediately before
/// transaction index @a _txi.
dev::eth::State state(unsigned _txi) const;
/// Get the object representing the current state of Ethereum.
dev::eth::Block postState() const { ReadGuard l(x_postMine); return m_postMine; }
/// Get the object representing the current canonical blockchain.
BlockChain const& blockChain() const { return bc(); }
/// Get some information on the block queue.
BlockQueueStatus blockQueueStatus() const { return m_bq.status(); }
/// Get some information on the block queue.
SyncStatus syncStatus() const;
/// Get the block queue.
BlockQueue const& blockQueue() const { return m_bq; }
/// Get the block queue.
OverlayDB const& stateDB() const { return m_stateDB; }
/// Handles a request to exit the client with a specific signal
static void exitHandler(int signal);
/// Freeze worker thread and sync some of the block queue.
std::tuple<ImportRoute, bool, unsigned> syncQueue(unsigned _max = 1);
// Mining stuff:
virtual void setBeneficiary(Address _us) override { WriteGuard l(x_preMine); m_preMine.setBeneficiary(_us); }
/// Check block validity prior to mining.
bool miningParanoia() const { return m_paranoia; }
/// Change whether we check block validity prior to mining.
void setParanoia(bool _p) { m_paranoia = _p; }
/// Should we force mining to happen, even without transactions?
bool forceMining() const { return m_forceMining; }
/// Enable/disable forcing of mining to happen, even without transactions.
void setForceMining(bool _enable);
/// Are we allowed to GPU mine?
bool turboMining() const { return m_turboMining; }
/// Enable/disable GPU mining.
void setTurboMining(bool _enable = true);
/// Enable/disable precomputing of the DAG for next epoch
void setShouldPrecomputeDAG(bool _precompute);
/// Check to see if we should exit
static bool shouldExit() { return s_shouldExit; }
/// Check to see if we'd mine on an apparently bad chain.
bool mineOnBadChain() const { return m_mineOnBadChain; }
/// Set true if you want to mine even when the canary says you're on the wrong chain.
void setMineOnBadChain(bool _v) { m_mineOnBadChain = _v; }
/// @returns true if the canary says that the chain is bad.
bool isChainBad() const;
/// @returns true if the canary says that the client should be upgraded.
bool isUpgradeNeeded() const;
/// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread
void startMining() override;
/// Stop mining.
/// NOT thread-safe
void stopMining() override { m_wouldMine = false; rejigMining(); }
/// Are we mining now?
bool isMining() const override;
/// Are we mining now?
bool wouldMine() const override { return m_wouldMine; }
/// The hashrate...
u256 hashrate() const override;
/// Check the progress of the mining.
WorkingProgress miningProgress() const override;
/// Get and clear the mining history.
std::list<MineInfo> miningHistory();
// Debug stuff:
DownloadMan const* downloadMan() const;
bool isSyncing() const;
bool isMajorSyncing() const;
/// Sets the network id.
void setNetworkId(u256 _n);
/// Clears pending transactions. Just for debug use.
void clearPending();
/// Kills the blockchain. Just for debug use.
void killChain() { reopenChain(WithExisting::Kill); }
/// Reloads the blockchain. Just for debug use.
void reopenChain(WithExisting _we = WithExisting::Trust);
/// Retries all blocks with unknown parents.
void retryUnknown() { m_bq.retryAllUnknown(); }
/// Get a report of activity.
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks.
void setSentinel(std::string const& _server) { m_sentinel = _server; }
/// Get the JSONRPC server to which we report bad blocks.
std::string const& sentinel() const { return m_sentinel; }
/// Set the extra data that goes into mined blocks.
void setExtraData(bytes const& _extraData) { m_extraData = _extraData; }
/// Rewind to a prior head.
void rewind(unsigned _n) { bc().rewind(_n); }
/// Rescue the chain.
void rescue() { bc().rescue(m_stateDB); }
/// Get the seal engine.
SealEngineFace* sealEngine() const { return m_sealEngine.get(); }
protected:
/// New-style Constructor.
/// Any final derived class's constructor should make sure they call init().
explicit Client(std::shared_ptr<GasPricer> _gpForAdoption);
/// Perform critical setup functions.
/// Must be called in the constructor of the finally derived class.
void init(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId);
/// InterfaceStub methods
virtual BlockChain& bc() override = 0;
virtual BlockChain const& bc() const override = 0;
/// Returns the state object for the full block (i.e. the terminal state) for index _h.
/// Works properly with LatestBlock and PendingBlock.
using ClientBase::asOf;
virtual Block asOf(h256 const& _block) const override;
virtual Block preMine() const override { ReadGuard l(x_preMine); return m_preMine; }
virtual Block postMine() const override { ReadGuard l(x_postMine); return m_postMine; }
virtual void prepareForTransaction() override;
/// Collate the changed filters for the bloom filter of the given pending transaction.
/// Insert any filters that are activated into @a o_changed.
void appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _sha3);
/// Collate the changed filters for the hash of the given block.
/// Insert any filters that are activated into @a o_changed.
void appendFromBlock(h256 const& _blockHash, BlockPolarity _polarity, h256Hash& io_changed);
/// Record that the set of filters @a _filters have changed.
/// This doesn't actually make any callbacks, but incrememnts some counters in m_watches.
void noteChanged(h256Hash const& _filters);
/// Submit
bool submitSealed(bytes const& _s);
protected:
/// Called when Worker is starting.
void startedWorking() override;
/// Do some work. Handles blockchain maintenance and mining.
void doWork() override;
/// Called when Worker is exiting.
void doneWorking() override;
/// Called when wouldMine(), turboMining(), isChainBad(), forceMining(), pendingTransactions() have changed.
void rejigMining();
/// Called on chain changes
void onDeadBlocks(h256s const& _blocks, h256Hash& io_changed);
/// Called on chain changes
void onNewBlocks(h256s const& _blocks, h256Hash& io_changed);
/// Called after processing blocks by onChainChanged(_ir)
void resyncStateFromChain();
/// Clear working state of transactions
void resetState();
/// Magically called when the chain has changed. An import route is provided.
/// Called by either submitWork() or in our main thread through syncBlockQueue().
void onChainChanged(ImportRoute const& _ir);
/// Signal handler for when the block queue needs processing.
void syncBlockQueue();
/// Signal handler for when the block queue needs processing.
void syncTransactionQueue();
/// Magically called when m_tq needs syncing. Be nice and don't block.
void onTransactionQueueReady() { m_syncTransactionQueue = true; m_signalled.notify_all(); }
/// Magically called when m_tq needs syncing. Be nice and don't block.
void onBlockQueueReady() { m_syncBlockQueue = true; m_signalled.notify_all(); }
/// Called when the post state has changed (i.e. when more transactions are in it or we're mining on a new block).
/// This updates m_miningInfo.
void onPostStateChanged();
/// Does garbage collection on watches.
void checkWatchGarbage();
/// Ticks various system-level objects.
void tick();
/// @returns true only if it's worth bothering to prep the mining block.
bool shouldServeWork() const { return m_bq.items().first == 0 && (isMining() || remoteActive()); }
/// Called when we have attempted to import a bad block.
/// @warning May be called from any thread.
void onBadBlock(Exception& _ex) const;
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
std::shared_ptr<GasPricer> m_gp; ///< The gas pricer.
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
mutable SharedMutex x_preMine; ///< Lock on m_preMine.
Block m_preMine; ///< The present state of the client.
mutable SharedMutex x_postMine; ///< Lock on m_postMine.
Block m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
mutable SharedMutex x_working; ///< Lock on m_working.
Block m_working; ///< The state of the client which we're mining (i.e. it'll have all the rewards added), while we're actually working on it.
BlockInfo m_miningInfo; ///< The header we're attempting to mine on (derived from m_postMine).
bool remoteActive() const; ///< Is there an active and valid remote worker?
bool m_remoteWorking = false; ///< Has the remote worker recently been reset?
std::atomic<bool> m_needStateReset = { false }; ///< Need reset working state to premin on next sync
std::chrono::system_clock::time_point m_lastGetWork; ///< Is there an active and valid remote worker?
std::weak_ptr<EthereumHost> m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
std::shared_ptr<SealEngineFace> m_sealEngine; ///< Our block-sealing engine.
Handler<> m_tqReady;
Handler<h256 const&> m_tqReplaced;
Handler<> m_bqReady;
bool m_wouldMine = false; ///< True if we /should/ be mining.
bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping.
bool m_forceMining = false; ///< Mine even when there are no transactions pending?
bool m_mineOnBadChain = false; ///< Mine even when the canary says it's a bad chain.
bool m_paranoia = false; ///< Should we be paranoid about our state?
static bool s_shouldExit; ///< Exit requested?
mutable std::chrono::system_clock::time_point m_lastGarbageCollection;
///< When did we last both doing GC on the watches?
mutable std::chrono::system_clock::time_point m_lastTick = std::chrono::system_clock::now();
///< When did we last tick()?
unsigned m_syncAmount = 50; ///< Number of blocks to sync in each go.
ActivityReport m_report;
std::condition_variable m_signalled;
Mutex x_signalled;
std::atomic<bool> m_syncTransactionQueue = {false};
std::atomic<bool> m_syncBlockQueue = {false};
std::string m_sentinel;
bytes m_extraData;
};
template <class Sealer>
class SpecialisedClient: public Client
{
public:
explicit SpecialisedClient(
p2p::Host* _host,
std::shared_ptr<GasPricer> _gpForAdoption,
std::string const& _dbPath = std::string(),
WithExisting _forceAction = WithExisting::Trust,
u256 _networkId = 0
):
SpecialisedClient(_gpForAdoption, _dbPath, _forceAction)
{
init(_host, _dbPath, _forceAction, _networkId);
}
virtual ~SpecialisedClient() { stopWorking(); }
/// Get the object representing the current canonical blockchain.
CanonBlockChain<Sealer> const& blockChain() const { return m_bc; }
protected:
explicit SpecialisedClient(
std::shared_ptr<GasPricer> _gpForAdoption,
std::string const& _dbPath = std::string(),
WithExisting _forceAction = WithExisting::Trust
):
Client(_gpForAdoption),
m_bc(_dbPath, _forceAction, [](unsigned d, unsigned t){ std::cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; })
{
m_sealEngine = std::shared_ptr<SealEngineFace>(Ethash::createSealEngine());
m_sealEngine->onSealGenerated([=](bytes const& header){
this->submitSealed(header);
});
}
virtual BlockChain& bc() override { return m_bc; }
virtual BlockChain const& bc() const override { return m_bc; }
private:
CanonBlockChain<Sealer> m_bc; ///< Maintains block database.
};
class EthashClient: public SpecialisedClient<Ethash>
{
public:
/// Trivial forwarding constructor.
explicit EthashClient(
p2p::Host* _host,
std::shared_ptr<GasPricer> _gpForAdoption,
std::string const& _dbPath = std::string(),
WithExisting _forceAction = WithExisting::Trust,
u256 _networkId = 0
):
SpecialisedClient<Ethash>(_gpForAdoption, _dbPath, _forceAction)
{
init(_host, _dbPath, _forceAction, _networkId);
}
/// Update to the latest transactions and get hash of the current block to be mined minus the
/// nonce (the 'work hash') and the difficulty to be met.
/// @returns Tuple of hash without seal, seed hash, target boundary.
virtual std::tuple<h256, h256, h256> getEthashWork() override;
/** @brief Submit the proof for the proof-of-work.
* @param _s A valid solution.
* @return true if the solution was indeed valid and accepted.
*/
virtual bool submitEthashWork(h256 const& _mixHash, h64 const& _nonce) override;
};
}
}

541
libethereum/ClientBase.cpp

@ -1,541 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ClientBase.cpp
* @author Gav Wood <i@gavwood.com>
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
#include "ClientBase.h"
#include <algorithm>
#include <libdevcore/StructuredLogger.h>
#include "BlockChain.h"
#include "Executive.h"
#include "State.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
const char* WatchChannel::name() { return EthBlue "" EthWhite " "; }
const char* WorkInChannel::name() { return EthOrange "" EthGreen "▬▶"; }
const char* WorkOutChannel::name() { return EthOrange "" EthNavy "◀▬"; }
const char* WorkChannel::name() { return EthOrange "" EthWhite " "; }
Block ClientBase::asOf(BlockNumber _h) const
{
if (_h == PendingBlock)
return postMine();
else if (_h == LatestBlock)
return preMine();
return asOf(bc().numberHash(_h));
}
pair<h256, Address> ClientBase::submitTransaction(TransactionSkeleton const& _t, Secret const& _secret)
{
prepareForTransaction();
TransactionSkeleton ts(_t);
ts.from = toAddress(_secret);
if (_t.nonce == UndefinedU256)
ts.nonce = max<u256>(postMine().transactionsFrom(ts.from), m_tq.maxNonce(ts.from));
Transaction t(ts, _secret);
m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t;
return make_pair(t.sha3(), toAddress(ts.from, ts.nonce));
}
// TODO: remove try/catch, allow exceptions
ExecutionResult ClientBase::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff)
{
ExecutionResult ret;
try
{
Block temp = asOf(_blockNumber);
u256 n = temp.transactionsFrom(_from);
Transaction t(_value, _gasPrice, _gas, _dest, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
temp.mutableState().addBalance(_from, (u256)(t.gas() * t.gasPrice() + t.value()));
ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
}
catch (...)
{
// TODO: Some sort of notification of failure.
}
return ret;
}
ExecutionResult ClientBase::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff)
{
ExecutionResult ret;
try
{
Block temp = asOf(_blockNumber);
u256 n = temp.transactionsFrom(_from);
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
temp.mutableState().addBalance(_from, (u256)(t.gas() * t.gasPrice() + t.value()));
ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
}
catch (...)
{
// TODO: Some sort of notification of failure.
}
return ret;
}
ImportResult ClientBase::injectBlock(bytes const& _block)
{
return bc().attemptImport(_block, preMine().db()).first;
}
u256 ClientBase::balanceAt(Address _a, BlockNumber _block) const
{
return asOf(_block).balance(_a);
}
u256 ClientBase::countAt(Address _a, BlockNumber _block) const
{
return asOf(_block).transactionsFrom(_a);
}
u256 ClientBase::stateAt(Address _a, u256 _l, BlockNumber _block) const
{
return asOf(_block).storage(_a, _l);
}
bytes ClientBase::codeAt(Address _a, BlockNumber _block) const
{
return asOf(_block).code(_a);
}
h256 ClientBase::codeHashAt(Address _a, BlockNumber _block) const
{
return asOf(_block).codeHash(_a);
}
unordered_map<u256, u256> ClientBase::storageAt(Address _a, BlockNumber _block) const
{
return asOf(_block).storage(_a);
}
// TODO: remove try/catch, allow exceptions
LocalisedLogEntries ClientBase::logs(unsigned _watchId) const
{
LogFilter f;
try
{
Guard l(x_filtersWatches);
f = m_filters.at(m_watches.at(_watchId).id).filter;
}
catch (...)
{
return LocalisedLogEntries();
}
return logs(f);
}
LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
{
LocalisedLogEntries ret;
unsigned begin = min(bc().number() + 1, (unsigned)numberFromHash(_f.latest()));
unsigned end = min(bc().number(), min(begin, (unsigned)numberFromHash(_f.earliest())));
// Handle pending transactions differently as they're not on the block chain.
if (begin > bc().number())
{
Block temp = postMine();
for (unsigned i = 0; i < temp.pending().size(); ++i)
{
// Might have a transaction that contains a matching log.
TransactionReceipt const& tr = temp.receipt(i);
LogEntries le = _f.matches(tr);
for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j]));
}
begin = bc().number();
}
// Handle reverted blocks
// There are not so many, so let's iterate over them
h256s blocks;
h256 ancestor;
unsigned ancestorIndex;
tie(blocks, ancestor, ancestorIndex) = bc().treeRoute(_f.earliest(), _f.latest(), false);
for (size_t i = 0; i < ancestorIndex; i++)
prependLogsFromBlock(_f, blocks[i], BlockPolarity::Dead, ret);
// cause end is our earliest block, let's compare it with our ancestor
// if ancestor is smaller let's move our end to it
// example:
//
// 3b -> 2b -> 1b
// -> g
// 3a -> 2a -> 1a
//
// if earliest is at 2a and latest is a 3b, coverting them to numbers
// will give us pair (2, 3)
// and we want to get all logs from 1 (ancestor + 1) to 3
// so we have to move 2a to g + 1
end = min(end, (unsigned)numberFromHash(ancestor) + 1);
// Handle blocks from main chain
set<unsigned> matchingBlocks;
if (!_f.isRangeFilter())
for (auto const& i: _f.bloomPossibilities())
for (auto u: bc().withBlockBloom(i, end, begin))
matchingBlocks.insert(u);
else
// if it is a range filter, we want to get all logs from all blocks in given range
for (unsigned i = end; i <= begin; i++)
matchingBlocks.insert(i);
for (auto n: matchingBlocks)
prependLogsFromBlock(_f, bc().numberHash(n), BlockPolarity::Live, ret);
reverse(ret.begin(), ret.end());
return ret;
}
void ClientBase::prependLogsFromBlock(LogFilter const& _f, h256 const& _blockHash, BlockPolarity _polarity, LocalisedLogEntries& io_logs) const
{
auto receipts = bc().receipts(_blockHash).receipts;
for (size_t i = 0; i < receipts.size(); i++)
{
TransactionReceipt receipt = receipts[i];
auto th = transaction(_blockHash, i).sha3();
LogEntries le = _f.matches(receipt);
for (unsigned j = 0; j < le.size(); ++j)
io_logs.insert(io_logs.begin(), LocalisedLogEntry(le[j], _blockHash, (BlockNumber)bc().number(_blockHash), th, i, 0, _polarity));
}
}
unsigned ClientBase::installWatch(LogFilter const& _f, Reaping _r)
{
h256 h = _f.sha3();
{
Guard l(x_filtersWatches);
if (!m_filters.count(h))
{
cwatch << "FFF" << _f << h;
m_filters.insert(make_pair(h, _f));
}
}
return installWatch(h, _r);
}
unsigned ClientBase::installWatch(h256 _h, Reaping _r)
{
unsigned ret;
{
Guard l(x_filtersWatches);
ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
m_watches[ret] = ClientWatch(_h, _r);
cwatch << "+++" << ret << _h;
}
#if INITIAL_STATE_AS_CHANGES
auto ch = logs(ret);
if (ch.empty())
ch.push_back(InitialChange);
{
Guard l(x_filtersWatches);
swap(m_watches[ret].changes, ch);
}
#endif
return ret;
}
bool ClientBase::uninstallWatch(unsigned _i)
{
cwatch << "XXX" << _i;
Guard l(x_filtersWatches);
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;
}
LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const
{
Guard l(x_filtersWatches);
// cwatch << "peekWatch" << _watchId;
auto& w = m_watches.at(_watchId);
// cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count();
if (w.lastPoll != chrono::system_clock::time_point::max())
w.lastPoll = chrono::system_clock::now();
return w.changes;
}
LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId)
{
Guard l(x_filtersWatches);
LocalisedLogEntries ret;
// cwatch << "checkWatch" << _watchId;
auto& w = m_watches.at(_watchId);
// cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count();
std::swap(ret, w.changes);
if (w.lastPoll != chrono::system_clock::time_point::max())
w.lastPoll = chrono::system_clock::now();
return ret;
}
BlockInfo ClientBase::blockInfo(h256 _hash) const
{
if (_hash == PendingBlockHash)
return preMine().info();
return BlockInfo(bc().block(_hash));
}
BlockDetails ClientBase::blockDetails(h256 _hash) const
{
return bc().details(_hash);
}
Transaction ClientBase::transaction(h256 _transactionHash) const
{
return Transaction(bc().transaction(_transactionHash), CheckTransaction::Cheap);
}
LocalisedTransaction ClientBase::localisedTransaction(h256 const& _transactionHash) const
{
std::pair<h256, unsigned> tl = bc().transactionLocation(_transactionHash);
return localisedTransaction(tl.first, tl.second);
}
Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const
{
auto bl = bc().block(_blockHash);
RLP b(bl);
if (_i < b[1].itemCount())
return Transaction(b[1][_i].data(), CheckTransaction::Cheap);
else
return Transaction();
}
LocalisedTransaction ClientBase::localisedTransaction(h256 const& _blockHash, unsigned _i) const
{
Transaction t = Transaction(bc().transaction(_blockHash, _i), CheckTransaction::Cheap);
return LocalisedTransaction(t, _blockHash, _i, numberFromHash(_blockHash));
}
TransactionReceipt ClientBase::transactionReceipt(h256 const& _transactionHash) const
{
return bc().transactionReceipt(_transactionHash);
}
LocalisedTransactionReceipt ClientBase::localisedTransactionReceipt(h256 const& _transactionHash) const
{
std::pair<h256, unsigned> tl = bc().transactionLocation(_transactionHash);
Transaction t = Transaction(bc().transaction(tl.first, tl.second), CheckTransaction::Cheap);
TransactionReceipt tr = bc().transactionReceipt(tl.first, tl.second);
return LocalisedTransactionReceipt(
tr,
t.sha3(),
tl.first,
numberFromHash(tl.first),
tl.second,
toAddress(t.from(), t.nonce()));
}
pair<h256, unsigned> ClientBase::transactionLocation(h256 const& _transactionHash) const
{
return bc().transactionLocation(_transactionHash);
}
Transactions ClientBase::transactions(h256 _blockHash) const
{
auto bl = bc().block(_blockHash);
RLP b(bl);
Transactions res;
for (unsigned i = 0; i < b[1].itemCount(); i++)
res.emplace_back(b[1][i].data(), CheckTransaction::Cheap);
return res;
}
TransactionHashes ClientBase::transactionHashes(h256 _blockHash) const
{
return bc().transactionHashes(_blockHash);
}
BlockInfo ClientBase::uncle(h256 _blockHash, unsigned _i) const
{
auto bl = bc().block(_blockHash);
RLP b(bl);
if (_i < b[2].itemCount())
return BlockInfo(b[2][_i].data(), CheckNothing, h256(), HeaderData);
else
return BlockInfo();
}
UncleHashes ClientBase::uncleHashes(h256 _blockHash) const
{
return bc().uncleHashes(_blockHash);
}
unsigned ClientBase::transactionCount(h256 _blockHash) const
{
auto bl = bc().block(_blockHash);
RLP b(bl);
return b[1].itemCount();
}
unsigned ClientBase::uncleCount(h256 _blockHash) const
{
auto bl = bc().block(_blockHash);
RLP b(bl);
return b[2].itemCount();
}
unsigned ClientBase::number() const
{
return bc().number();
}
Transactions ClientBase::pending() const
{
return postMine().pending();
}
h256s ClientBase::pendingHashes() const
{
return h256s() + postMine().pendingHashes();
}
StateDiff ClientBase::diff(unsigned _txi, h256 _block) const
{
Block b = asOf(_block);
return b.fromPending(_txi).diff(b.fromPending(_txi + 1), true);
}
StateDiff ClientBase::diff(unsigned _txi, BlockNumber _block) const
{
Block b = asOf(_block);
return b.fromPending(_txi).diff(b.fromPending(_txi + 1), true);
}
Addresses ClientBase::addresses(BlockNumber _block) const
{
Addresses ret;
for (auto const& i: asOf(_block).addresses())
ret.push_back(i.first);
return ret;
}
u256 ClientBase::gasLimitRemaining() const
{
return postMine().gasLimitRemaining();
}
Address ClientBase::address() const
{
return preMine().beneficiary();
}
h256 ClientBase::hashFromNumber(BlockNumber _number) const
{
if (_number == PendingBlock)
return h256();
if (_number == LatestBlock)
return bc().currentHash();
return bc().numberHash(_number);
}
BlockNumber ClientBase::numberFromHash(h256 _blockHash) const
{
if (_blockHash == PendingBlockHash)
return bc().number() + 1;
else if (_blockHash == LatestBlockHash)
return bc().number();
else if (_blockHash == EarliestBlockHash)
return 0;
return bc().number(_blockHash);
}
int ClientBase::compareBlockHashes(h256 _h1, h256 _h2) const
{
BlockNumber n1 = numberFromHash(_h1);
BlockNumber n2 = numberFromHash(_h2);
if (n1 > n2)
return 1;
else if (n1 == n2)
return 0;
return -1;
}
bool ClientBase::isKnown(h256 const& _hash) const
{
return _hash == PendingBlockHash ||
_hash == LatestBlockHash ||
_hash == EarliestBlockHash ||
bc().isKnown(_hash);
}
bool ClientBase::isKnown(BlockNumber _block) const
{
return _block == PendingBlock ||
_block == LatestBlock ||
bc().numberHash(_block) != h256();
}
bool ClientBase::isKnownTransaction(h256 const& _transactionHash) const
{
return bc().isKnownTransaction(_transactionHash);
}
bool ClientBase::isKnownTransaction(h256 const& _blockHash, unsigned _i) const
{
return isKnown(_blockHash) && bc().transactions().size() > _i;
}
void ClientBase::submitExternalHashrate(u256 const& _rate, h256 const& _id)
{
m_externalRates[_id] = make_pair(_rate, chrono::steady_clock::now());
}
u256 ClientBase::externalHashrate() const
{
u256 ret = 0;
for (auto i = m_externalRates.begin(); i != m_externalRates.end();)
if (chrono::steady_clock::now() - i->second.second > chrono::seconds(5))
i = m_externalRates.erase(i);
else
ret += i++->second.first;
return ret;
}

200
libethereum/ClientBase.h

@ -1,200 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ClientBase.h
* @author Gav Wood <i@gavwood.com>
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
#pragma once
#include <chrono>
#include "Interface.h"
#include "LogFilter.h"
#include "TransactionQueue.h"
#include "Block.h"
namespace dev
{
namespace eth
{
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);
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;
#if INITIAL_STATE_AS_CHANGES
LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange };
#else
LocalisedLogEntries changes;
#endif
mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now();
};
struct WatchChannel: public LogChannel { static const char* name(); static const int verbosity = 7; };
#define cwatch LogOutputStream<WatchChannel, true>()
struct WorkInChannel: public LogChannel { static const char* name(); static const int verbosity = 16; };
struct WorkOutChannel: public LogChannel { static const char* name(); static const int verbosity = 16; };
struct WorkChannel: public LogChannel { static const char* name(); static const int verbosity = 21; };
#define cwork LogOutputStream<WorkChannel, true>()
#define cworkin LogOutputStream<WorkInChannel, true>()
#define cworkout LogOutputStream<WorkOutChannel, true>()
class ClientBase: public Interface
{
public:
ClientBase() {}
virtual ~ClientBase() {}
/// Submits the given transaction.
/// @returns the new transaction's hash.
virtual std::pair<h256, Address> submitTransaction(TransactionSkeleton const& _t, Secret const& _secret) override;
using Interface::submitTransaction;
/// Makes the given call. Nothing is recorded into the state.
virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) override;
using Interface::call;
/// Makes the given create. Nothing is recorded into the state.
virtual ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) override;
using Interface::create;
using Interface::balanceAt;
using Interface::countAt;
using Interface::stateAt;
using Interface::codeAt;
using Interface::codeHashAt;
using Interface::storageAt;
virtual u256 balanceAt(Address _a, BlockNumber _block) const override;
virtual u256 countAt(Address _a, BlockNumber _block) const override;
virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const override;
virtual bytes codeAt(Address _a, BlockNumber _block) const override;
virtual h256 codeHashAt(Address _a, BlockNumber _block) const override;
virtual std::unordered_map<u256, u256> storageAt(Address _a, BlockNumber _block) const override;
virtual LocalisedLogEntries logs(unsigned _watchId) const override;
virtual LocalisedLogEntries logs(LogFilter const& _filter) const override;
virtual void prependLogsFromBlock(LogFilter const& _filter, h256 const& _blockHash, BlockPolarity _polarity, LocalisedLogEntries& io_logs) const;
/// Install, uninstall and query watches.
virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) override;
virtual unsigned installWatch(h256 _filterId, Reaping _r = Reaping::Automatic) override;
virtual bool uninstallWatch(unsigned _watchId) override;
virtual LocalisedLogEntries peekWatch(unsigned _watchId) const override;
virtual LocalisedLogEntries checkWatch(unsigned _watchId) override;
virtual h256 hashFromNumber(BlockNumber _number) const override;
virtual BlockNumber numberFromHash(h256 _blockHash) const override;
virtual int compareBlockHashes(h256 _h1, h256 _h2) const override;
virtual BlockInfo blockInfo(h256 _hash) const override;
virtual BlockDetails blockDetails(h256 _hash) const override;
virtual Transaction transaction(h256 _transactionHash) const override;
virtual LocalisedTransaction localisedTransaction(h256 const& _transactionHash) const override;
virtual Transaction transaction(h256 _blockHash, unsigned _i) const override;
virtual LocalisedTransaction localisedTransaction(h256 const& _blockHash, unsigned _i) const override;
virtual TransactionReceipt transactionReceipt(h256 const& _transactionHash) const override;
virtual LocalisedTransactionReceipt localisedTransactionReceipt(h256 const& _transactionHash) const override;
virtual std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const override;
virtual Transactions transactions(h256 _blockHash) const override;
virtual TransactionHashes transactionHashes(h256 _blockHash) const override;
virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const override;
virtual UncleHashes uncleHashes(h256 _blockHash) const override;
virtual unsigned transactionCount(h256 _blockHash) const override;
virtual unsigned uncleCount(h256 _blockHash) const override;
virtual unsigned number() const override;
virtual Transactions pending() const override;
virtual h256s pendingHashes() const override;
virtual ImportResult injectTransaction(bytes const& _rlp, IfDropped _id = IfDropped::Ignore) override { prepareForTransaction(); return m_tq.import(_rlp, _id); }
virtual ImportResult injectBlock(bytes const& _block) override;
using Interface::diff;
virtual StateDiff diff(unsigned _txi, h256 _block) const override;
virtual StateDiff diff(unsigned _txi, BlockNumber _block) const override;
using Interface::addresses;
virtual Addresses addresses(BlockNumber _block) const override;
virtual u256 gasLimitRemaining() const override;
virtual u256 gasBidPrice() const override { return DefaultGasPrice; }
/// Get the coinbase address
virtual Address address() const override;
virtual bool isKnown(h256 const& _hash) const override;
virtual bool isKnown(BlockNumber _block) const override;
virtual bool isKnownTransaction(h256 const& _transactionHash) const override;
virtual bool isKnownTransaction(h256 const& _blockHash, unsigned _i) const override;
/// TODO: consider moving it to a separate interface
virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::startMining")); }
virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::stopMining")); }
virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::isMining")); }
virtual bool wouldMine() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::wouldMine")); }
virtual u256 hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::hashrate")); }
virtual WorkingProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::miningProgress")); }
virtual void submitExternalHashrate(u256 const& _rate, h256 const& _id) override;
Block asOf(BlockNumber _h) const;
protected:
/// The interface that must be implemented in any class deriving this.
/// {
virtual BlockChain& bc() = 0;
virtual BlockChain const& bc() const = 0;
virtual Block asOf(h256 const& _h) const = 0;
virtual Block preMine() const = 0;
virtual Block postMine() const = 0;
virtual void prepareForTransaction() = 0;
/// }
u256 externalHashrate() const;
TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
// filters
mutable Mutex x_filtersWatches; ///< Our lock.
std::unordered_map<h256, InstalledFilter> m_filters; ///< The dictionary of filters that are active.
std::unordered_map<h256, h256s> m_specialFilters = std::unordered_map<h256, std::vector<h256>>{{PendingChangedFilter, {}}, {ChainChangedFilter, {}}};
///< The dictionary of special filters and their additional data
std::map<unsigned, ClientWatch> m_watches; ///< Each and every watch - these reference a filter.
// external hashrate
mutable std::unordered_map<h256, std::pair<u256, std::chrono::steady_clock::time_point>> m_externalRates;
};
}}

28
libethereum/CommonNet.cpp

@ -1,28 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CommonNet.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "CommonNet.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
#pragma GCC diagnostic ignored "-Wunused-variable"
namespace { char dummy; }

103
libethereum/CommonNet.h

@ -1,103 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CommonNet.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* Miscellanea required for the PeerServer/Session classes.
*/
#pragma once
#include <string>
#include <chrono>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
namespace dev
{
class OverlayDB;
namespace eth
{
#if ETH_DEBUG
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxPayload = 262144; ///< Maximum size of packet for us to send.
#else
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
static const unsigned c_maxPayload = 262144; ///< Maximum size of packet for us to send.
#endif
class BlockChain;
class TransactionQueue;
class EthereumHost;
class EthereumPeer;
enum
{
StatusPacket = 0,
NewBlockHashesPacket,
TransactionsPacket,
GetBlockHashesPacket,
BlockHashesPacket,
GetBlocksPacket,
BlocksPacket,
NewBlockPacket,
GetBlockHashesByNumberPacket,
PacketCount
};
enum class Asking
{
State,
Hashes,
Blocks,
Nothing
};
enum class SyncState
{
Idle, ///< Initial chain sync complete. Waiting for new packets
Waiting, ///< Block downloading paused. Waiting for block queue to process blocks and free space
Hashes, ///< Downloading hashes from multiple peers over
Blocks, ///< Downloading blocks
NewBlocks, ///< Downloading blocks learned from NewHashes packet
Size /// Must be kept last
};
struct SyncStatus
{
SyncState state = SyncState::Idle;
unsigned protocolVersion = 0;
unsigned hashesTotal = 0;
unsigned hashesReceived = 0;
bool hashesEstimated = false;
unsigned blocksTotal = 0;
unsigned blocksReceived = 0;
};
}
}

34
libethereum/Defaults.cpp

@ -1,34 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Defaults.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Defaults.h"
#include <libdevcore/FileSystem.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
Defaults* Defaults::s_this = nullptr;
Defaults::Defaults()
{
m_dbPath = getDataDir();
}

50
libethereum/Defaults.h

@ -1,50 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Defaults.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
namespace dev
{
namespace eth
{
struct Defaults
{
friend class BlockChain;
friend class State;
public:
Defaults();
static Defaults* get() { if (!s_this) s_this = new Defaults; return s_this; }
static void setDBPath(std::string const& _dbPath) { get()->m_dbPath = _dbPath; }
static std::string const& dbPath() { return get()->m_dbPath; }
private:
std::string m_dbPath;
static Defaults* s_this;
};
}
}

91
libethereum/DownloadMan.cpp

@ -1,91 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file DownloadMan.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "DownloadMan.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
size_t const c_maxDownloadAhead = 50000; // Must not be higher than BlockQueue::c_maxUnknownCount
DownloadMan::Overview DownloadMan::overview() const
{
ReadGuard l(m_lock);
Overview ret;
ret.firstIncomplete = m_blocksGot.firstOut();
ret.lastComplete = ret.lastStarted = m_blocksGot.lastIn();// TODO: lastStarted properly
ret.total = m_blocksGot.size();
return ret;
}
DownloadSub::DownloadSub(DownloadMan& _man): m_man(&_man)
{
WriteGuard l(m_man->x_subs);
m_man->m_subs.insert(this);
}
DownloadSub::~DownloadSub()
{
Guard fl(m_fetch);
if (m_man)
{
WriteGuard l(m_man->x_subs);
m_man->m_subs.erase(this);
}
}
h256Hash DownloadSub::nextFetch(unsigned _n)
{
Guard l(m_fetch);
if (m_remaining.size())
return m_remaining;
m_asked.clear();
m_indices.clear();
m_remaining.clear();
if (!m_man || m_man->chainEmpty())
return h256Hash();
RangeMask<unsigned> downloaded = m_man->taken(true);
m_asked = (~(m_man->taken(false) + m_attempted)).lowest(_n);
if (m_asked.empty() || m_asked.lastIn() - downloaded.firstOut() >= c_maxDownloadAhead)
m_asked = (~(downloaded + m_attempted)).lowest(_n);
m_attempted += m_asked;
for (auto i: m_asked)
{
auto x = m_man->m_chain[i];
m_remaining.insert(x);
m_indices[x] = i;
}
return m_remaining;
}
bool DownloadSub::noteBlock(h256 _hash)
{
Guard l(m_fetch);
if (m_man && m_indices.count(_hash))
m_man->m_blocksGot += m_indices[_hash];
bool ret = !!m_remaining.count(_hash);
m_remaining.erase(_hash);
return ret;
}

179
libethereum/DownloadMan.h

@ -1,179 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file DownloadMan.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <libdevcore/Guards.h>
#include <libdevcore/Worker.h>
#include <libdevcore/RangeMask.h>
#include <libdevcore/FixedHash.h>
namespace dev
{
namespace eth
{
class DownloadMan;
class DownloadSub
{
friend class DownloadMan;
public:
DownloadSub(DownloadMan& _man);
~DownloadSub();
/// Finished last fetch - grab the next bunch of block hashes to download.
h256Hash nextFetch(unsigned _n);
/// Note that we've received a particular block. @returns true if we had asked for it but haven't received it yet.
bool noteBlock(h256 _hash);
/// Nothing doing here.
void doneFetch() { resetFetch(); }
bool askedContains(unsigned _i) const { Guard l(m_fetch); return m_asked.contains(_i); }
RangeMask<unsigned> const& asked() const { return m_asked; }
RangeMask<unsigned> const& attemped() const { return m_attempted; }
private:
void resetFetch() // Called by DownloadMan when we need to reset the download.
{
Guard l(m_fetch);
m_remaining.clear();
m_indices.clear();
m_asked.reset();
m_attempted.reset();
}
DownloadMan* m_man = nullptr;
mutable Mutex m_fetch;
h256Hash m_remaining;
std::unordered_map<h256, unsigned> m_indices;
RangeMask<unsigned> m_asked;
RangeMask<unsigned> m_attempted;
};
class DownloadMan
{
friend class DownloadSub;
public:
struct Overview
{
size_t total;
size_t firstIncomplete;
size_t lastComplete;
size_t lastStarted;
};
~DownloadMan()
{
for (auto i: m_subs)
{
Guard l(i->m_fetch);
i->m_man = nullptr;
}
}
void appendToChain(h256s const& _hashes)
{
WriteGuard l(m_lock);
m_chain.insert(m_chain.end(), _hashes.cbegin(), _hashes.cend());
m_blocksGot = RangeMask<unsigned>(0, m_chain.size());
}
void resetToChain(h256s const& _chain)
{
DEV_READ_GUARDED(x_subs)
for (auto i: m_subs)
i->resetFetch();
WriteGuard l(m_lock);
m_chain.clear();
m_chain.reserve(_chain.size());
for (auto i = _chain.rbegin(); i != _chain.rend(); ++i)
m_chain.push_back(*i);
m_blocksGot = RangeMask<unsigned>(0, m_chain.size());
}
void reset()
{
DEV_READ_GUARDED(x_subs)
for (auto i: m_subs)
i->resetFetch();
WriteGuard l(m_lock);
m_chain.clear();
m_blocksGot.reset();
}
RangeMask<unsigned> taken(bool _desperate = false) const
{
ReadGuard l(m_lock);
auto ret = m_blocksGot;
if (!_desperate)
DEV_READ_GUARDED(x_subs)
for (auto i: m_subs)
ret += i->m_asked;
return ret;
}
bool isComplete() const
{
ReadGuard l(m_lock);
return m_blocksGot.full();
}
h256s remaining() const
{
h256s ret;
DEV_READ_GUARDED(m_lock)
for (auto i: m_blocksGot.inverted())
ret.push_back(m_chain[i]);
return ret;
}
h256 firstBlock() const { return m_chain.empty() ? h256() : m_chain[0]; }
Overview overview() const;
size_t chainSize() const { ReadGuard l(m_lock); return m_chain.size(); }
size_t chainEmpty() const { ReadGuard l(m_lock); return m_chain.empty(); }
void foreachSub(std::function<void(DownloadSub const&)> const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); }
unsigned subCount() const { ReadGuard l(x_subs); return m_subs.size(); }
RangeMask<unsigned> blocksGot() const { ReadGuard l(m_lock); return m_blocksGot; }
private:
mutable SharedMutex m_lock;
h256s m_chain;
RangeMask<unsigned> m_blocksGot;
mutable SharedMutex x_subs;
std::unordered_set<DownloadSub*> m_subs;
};
}
}

400
libethereum/EthereumHost.cpp

@ -1,400 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthereumHost.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "EthereumHost.h"
#include <chrono>
#include <thread>
#include <libdevcore/Common.h>
#include <libp2p/Host.h>
#include <libp2p/Session.h>
#include <libethcore/Exceptions.h>
#include <libethcore/Params.h>
#include "BlockChain.h"
#include "TransactionQueue.h"
#include "BlockQueue.h"
#include "EthereumPeer.h"
#include "DownloadMan.h"
#include "BlockChainSync.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
static unsigned const c_maxSendTransactions = 256;
char const* const EthereumHost::s_stateNames[static_cast<int>(SyncState::Size)] = {"Idle", "Waiting", "Hashes", "Blocks", "NewBlocks" };
#ifdef _WIN32
const char* EthereumHostTrace::name() { return EthPurple "^" EthGray " "; }
#else
const char* EthereumHostTrace::name() { return EthPurple "" EthGray " "; }
#endif
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(),
Worker ("ethsync"),
m_chain (_ch),
m_tq (_tq),
m_bq (_bq),
m_networkId (_networkId)
{
m_latestBlockSent = _ch.currentHash();
m_tq.onImport([this](ImportResult _ir, h256 const& _h, h512 const& _nodeId) { onTransactionImported(_ir, _h, _nodeId); });
}
EthereumHost::~EthereumHost()
{
}
bool EthereumHost::ensureInitialised()
{
if (!m_latestBlockSent)
{
// First time - just initialise.
m_latestBlockSent = m_chain.currentHash();
clog(EthereumHostTrace) << "Initialising: latest=" << m_latestBlockSent;
Guard l(x_transactions);
m_transactionsSent = m_tq.knownTransactions();
return true;
}
return false;
}
void EthereumHost::reset()
{
RecursiveGuard l(x_sync);
if (m_sync)
m_sync->abortSync();
m_sync.reset();
m_syncStart = 0;
m_latestBlockSent = h256();
Guard tl(x_transactions);
m_transactionsSent.clear();
}
void EthereumHost::doWork()
{
bool netChange = ensureInitialised();
auto h = m_chain.currentHash();
// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
{
if (m_newTransactions)
{
m_newTransactions = false;
maintainTransactions();
}
if (m_newBlocks)
{
m_newBlocks = false;
maintainBlocks(h);
}
}
foreachPeer([](std::shared_ptr<EthereumPeer> _p) { _p->tick(); return true; });
if (m_syncStart)
{
DEV_RECURSIVE_GUARDED(x_sync)
if (!m_sync)
{
time_t now = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
if (now - m_syncStart > 10)
{
m_sync.reset(new PV60Sync(*this));
m_syncStart = 0;
m_sync->restartSync();
}
}
}
// return netChange;
// TODO: Figure out what to do with netChange.
(void)netChange;
}
void EthereumHost::maintainTransactions()
{
// Send any new transactions.
unordered_map<std::shared_ptr<EthereumPeer>, std::vector<size_t>> peerTransactions;
auto ts = m_tq.topTransactions(c_maxSendTransactions);
{
Guard l(x_transactions);
for (size_t i = 0; i < ts.size(); ++i)
{
auto const& t = ts[i];
bool unsent = !m_transactionsSent.count(t.sha3());
auto peers = get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(t.sha3())); }));
for (auto const& p: peers)
peerTransactions[p].push_back(i);
}
for (auto const& t: ts)
m_transactionsSent.insert(t.sha3());
}
foreachPeer([&](shared_ptr<EthereumPeer> _p)
{
bytes b;
unsigned n = 0;
for (auto const& i: peerTransactions[_p])
{
_p->m_knownTransactions.insert(ts[i].sha3());
b += ts[i].rlp();
++n;
}
_p->clearKnownTransactions();
if (n || _p->m_requireTransactions)
{
RLPStream ts;
_p->prep(ts, TransactionsPacket, n).appendRaw(b, n);
_p->sealAndSend(ts);
clog(EthereumHostTrace) << "Sent" << n << "transactions to " << _p->session()->info().clientVersion;
}
_p->m_requireTransactions = false;
return true;
});
}
void EthereumHost::foreachPeer(std::function<bool(std::shared_ptr<EthereumPeer>)> const& _f) const
{
//order peers by protocol, rating, connection age
auto sessions = peerSessions();
auto sessionLess = [](std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>> const& _left, std::pair<std::shared_ptr<Session>, std::shared_ptr<Peer>> const& _right)
{ return _left.first->rating() == _right.first->rating() ? _left.first->connectionTime() < _right.first->connectionTime() : _left.first->rating() > _right.first->rating(); };
std::sort(sessions.begin(), sessions.end(), sessionLess);
for (auto s: sessions)
if (!_f(s.first->cap<EthereumPeer>()))
return;
sessions = peerSessions(c_oldProtocolVersion); //TODO: remove once v61+ is common
std::sort(sessions.begin(), sessions.end(), sessionLess);
for (auto s: sessions)
if (!_f(s.first->cap<EthereumPeer>(c_oldProtocolVersion)))
return;
}
tuple<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<Session>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
{
vector<shared_ptr<EthereumPeer>> chosen;
vector<shared_ptr<EthereumPeer>> allowed;
vector<shared_ptr<Session>> sessions;
size_t peerCount = 0;
foreachPeer([&](std::shared_ptr<EthereumPeer> _p)
{
if (_allow(_p.get()))
{
allowed.push_back(_p);
sessions.push_back(_p->session());
}
++peerCount;
return true;
});
size_t chosenSize = (peerCount * _percent + 99) / 100;
chosen.reserve(chosenSize);
for (unsigned i = chosenSize; i && allowed.size(); i--)
{
unsigned n = rand() % allowed.size();
chosen.push_back(std::move(allowed[n]));
allowed.erase(allowed.begin() + n);
}
return make_tuple(move(chosen), move(allowed), move(sessions));
}
void EthereumHost::maintainBlocks(h256 const& _currentHash)
{
// Send any new blocks.
auto detailsFrom = m_chain.details(m_latestBlockSent);
auto detailsTo = m_chain.details(_currentHash);
if (detailsFrom.totalDifficulty < detailsTo.totalDifficulty)
{
if (diff(detailsFrom.number, detailsTo.number) < 20)
{
// don't be sending more than 20 "new" blocks. if there are any more we were probably waaaay behind.
clog(EthereumHostTrace) << "Sending a new block (current is" << _currentHash << ", was" << m_latestBlockSent << ")";
h256s blocks = get<0>(m_chain.treeRoute(m_latestBlockSent, _currentHash, false, false, true));
auto s = randomSelection(25, [&](EthereumPeer* p){
DEV_GUARDED(p->x_knownBlocks)
return !p->m_knownBlocks.count(_currentHash);
return false;
});
for (shared_ptr<EthereumPeer> const& p: get<0>(s))
for (auto const& b: blocks)
{
RLPStream ts;
p->prep(ts, NewBlockPacket, 2).appendRaw(m_chain.block(b), 1).append(m_chain.details(b).totalDifficulty);
Guard l(p->x_knownBlocks);
p->sealAndSend(ts);
p->m_knownBlocks.clear();
}
for (shared_ptr<EthereumPeer> const& p: get<1>(s))
{
RLPStream ts;
p->prep(ts, NewBlockHashesPacket, blocks.size());
for (auto const& b: blocks)
ts.append(b);
Guard l(p->x_knownBlocks);
p->sealAndSend(ts);
p->m_knownBlocks.clear();
}
}
m_latestBlockSent = _currentHash;
}
}
BlockChainSync* EthereumHost::sync()
{
if (m_sync)
return m_sync.get(); // We only chose sync strategy once
bool pv61 = false;
foreachPeer([&](std::shared_ptr<EthereumPeer> _p)
{
if (_p->m_protocolVersion == protocolVersion())
pv61 = true;
return !pv61;
});
if (pv61)
{
m_syncStart = 0;
m_sync.reset(new PV61Sync(*this));
}
else if (!m_syncStart)
m_syncStart = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
return m_sync.get();
}
void EthereumHost::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
{
RecursiveGuard l(x_sync);
if (sync())
sync()->onPeerStatus(_peer);
}
void EthereumHost::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
if (sync())
sync()->onPeerHashes(_peer, _hashes);
}
void EthereumHost::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
{
RecursiveGuard l(x_sync);
if (sync())
sync()->onPeerBlocks(_peer, _r);
}
void EthereumHost::onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
if (sync())
sync()->onPeerNewHashes(_peer, _hashes);
}
void EthereumHost::onPeerNewBlock(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
{
RecursiveGuard l(x_sync);
if (sync())
sync()->onPeerNewBlock(_peer, _r);
}
void EthereumHost::onPeerTransactions(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
{
unsigned itemCount = _r.itemCount();
clog(EthereumHostTrace) << "Transactions (" << dec << itemCount << "entries)";
m_tq.enqueue(_r, _peer->session()->id());
}
void EthereumHost::onPeerAborting()
{
RecursiveGuard l(x_sync);
try
{
if (m_sync)
m_sync->onPeerAborting();
}
catch (Exception&)
{
cwarn << "Exception on peer destruciton: " << boost::current_exception_diagnostic_information();
}
}
bool EthereumHost::isSyncing() const
{
RecursiveGuard l(x_sync);
if (!m_sync)
return false;
return m_sync->isSyncing();
}
SyncStatus EthereumHost::status() const
{
RecursiveGuard l(x_sync);
if (!m_sync)
return SyncStatus();
return m_sync->status();
}
void EthereumHost::onTransactionImported(ImportResult _ir, h256 const& _h, h512 const& _nodeId)
{
auto session = host()->peerSession(_nodeId);
if (!session)
return;
std::shared_ptr<EthereumPeer> peer = session->cap<EthereumPeer>();
if (!peer)
peer = session->cap<EthereumPeer>(c_oldProtocolVersion);
if (!peer)
return;
Guard l(peer->x_knownTransactions);
peer->m_knownTransactions.insert(_h);
switch (_ir)
{
case ImportResult::Malformed:
peer->addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
DEV_GUARDED(x_transactions)
m_transactionsSent.insert(_h);
peer->addRating(0);
break;
case ImportResult::Success:
peer->addRating(100);
break;
default:;
}
}

145
libethereum/EthereumHost.h

@ -1,145 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthereumHost.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <mutex>
#include <unordered_map>
#include <vector>
#include <unordered_set>
#include <memory>
#include <utility>
#include <thread>
#include <libdevcore/Guards.h>
#include <libdevcore/Worker.h>
#include <libethcore/Common.h>
#include <libp2p/Common.h>
#include "CommonNet.h"
#include "EthereumPeer.h"
#include "DownloadMan.h"
namespace dev
{
class RLPStream;
namespace eth
{
class TransactionQueue;
class BlockQueue;
class BlockChainSync;
struct EthereumHostTrace: public LogChannel { static const char* name(); static const int verbosity = 6; };
/**
* @brief The EthereumHost class
* @warning None of this is thread-safe. You have been warned.
* @doWork Syncs to peers and sends new blocks and transactions.
*/
class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker
{
public:
/// Start server, but don't listen.
EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId);
/// Will block on network process events.
virtual ~EthereumHost();
unsigned protocolVersion() const { return c_protocolVersion; }
u256 networkId() const { return m_networkId; }
void setNetworkId(u256 _n) { m_networkId = _n; }
void reset();
DownloadMan const& downloadMan() const { return m_man; }
DownloadMan& downloadMan() { return m_man; }
bool isSyncing() const;
bool isBanned(p2p::NodeID const& _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; }
void noteNewBlocks() { m_newBlocks = true; }
BlockChain const& chain() const { return m_chain; }
BlockQueue& bq() { return m_bq; }
BlockQueue const& bq() const { return m_bq; }
SyncStatus status() const;
h256 latestBlockSent() { return m_latestBlockSent; }
static char const* stateName(SyncState _s) { return s_stateNames[static_cast<int>(_s)]; }
static unsigned const c_oldProtocolVersion;
void foreachPeer(std::function<bool(std::shared_ptr<EthereumPeer>)> const& _f) const;
void onPeerStatus(std::shared_ptr<EthereumPeer> _peer);
void onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes);
void onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const& _r);
void onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes);
void onPeerNewBlock(std::shared_ptr<EthereumPeer> _peer, RLP const& _r);
void onPeerTransactions(std::shared_ptr<EthereumPeer> _peer, RLP const& _r);
void onPeerAborting();
private:
static char const* const s_stateNames[static_cast<int>(SyncState::Size)];
std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
virtual void doWork() override;
void maintainTransactions();
void maintainBlocks(h256 const& _currentBlock);
void onTransactionImported(ImportResult _ir, h256 const& _h, h512 const& _nodeId);
/// Check to see if the network peer-state initialisation has happened.
bool isInitialised() const { return (bool)m_latestBlockSent; }
/// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first.
bool ensureInitialised();
virtual void onStarting() override { startWorking(); }
virtual void onStopping() override { stopWorking(); }
BlockChainSync* sync();
BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue& m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
u256 m_networkId;
h256 m_latestBlockSent;
h256Hash m_transactionsSent;
std::unordered_set<p2p::NodeID> m_banned;
bool m_newTransactions = false;
bool m_newBlocks = false;
mutable RecursiveMutex x_sync;
mutable Mutex x_transactions;
DownloadMan m_man;
std::unique_ptr<BlockChainSync> m_sync;
std::atomic<time_t> m_syncStart = { 0 };
};
}
}

390
libethereum/EthereumPeer.cpp

@ -1,390 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthereumPeer.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "EthereumPeer.h"
#include <chrono>
#include <libdevcore/Common.h>
#include <libethcore/Exceptions.h>
#include <libp2p/Session.h>
#include <libp2p/Host.h>
#include "BlockChain.h"
#include "EthereumHost.h"
#include "TransactionQueue.h"
#include "BlockQueue.h"
#include "BlockChainSync.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;
string toString(Asking _a)
{
switch (_a)
{
case Asking::Blocks: return "Blocks";
case Asking::Hashes: return "Hashes";
case Asking::Nothing: return "Nothing";
case Asking::State: return "State";
}
return "?";
}
EthereumPeer::EthereumPeer(std::shared_ptr<Session> _s, HostCapabilityFace* _h, unsigned _i, CapDesc const& _cap):
Capability(_s, _h, _i),
m_sub(host()->downloadMan()),
m_peerCapabilityVersion(_cap.second)
{
session()->addNote("manners", isRude() ? "RUDE" : "nice");
m_syncHashNumber = host()->chain().number() + 1;
requestStatus();
}
EthereumPeer::~EthereumPeer()
{
if (m_asking != Asking::Nothing)
{
clog(NetAllDetail) << "Peer aborting while being asked for " << ::toString(m_asking);
setRude();
}
abortSync();
}
bool EthereumPeer::isRude() const
{
auto s = session();
if (s)
return repMan().isRude(*s, name());
return false;
}
unsigned EthereumPeer::askOverride() const
{
std::string static const badGeth = "Geth/v0.9.27";
auto s = session();
if (!s)
return c_maxBlocksAsk;
if (s->info().clientVersion.substr(0, badGeth.size()) == badGeth)
return 1;
bytes const& d = repMan().data(*s, name());
return d.empty() ? c_maxBlocksAsk : RLP(d).toInt<unsigned>(RLP::LaissezFaire);
}
void EthereumPeer::setRude()
{
auto s = session();
if (!s)
return;
auto old = askOverride();
repMan().setData(*s, name(), rlp(askOverride() / 2 + 1));
cnote << "Rude behaviour; askOverride now" << askOverride() << ", was" << old;
repMan().noteRude(*s, name());
session()->addNote("manners", "RUDE");
}
void EthereumPeer::abortSync()
{
host()->onPeerAborting();
}
EthereumHost* EthereumPeer::host() const
{
return static_cast<EthereumHost*>(Capability::hostCapability());
}
/*
* Possible asking/syncing states for two peers:
*/
void EthereumPeer::setIdle()
{
setAsking(Asking::Nothing);
}
void EthereumPeer::requestStatus()
{
assert(m_asking == Asking::Nothing);
setAsking(Asking::State);
m_requireTransactions = true;
RLPStream s;
bool latest = m_peerCapabilityVersion == host()->protocolVersion();
prep(s, StatusPacket, 5)
<< (latest ? host()->protocolVersion() : EthereumHost::c_oldProtocolVersion)
<< host()->networkId()
<< host()->chain().details().totalDifficulty
<< host()->chain().currentHash()
<< host()->chain().genesisHash();
sealAndSend(s);
}
void EthereumPeer::requestHashes(u256 _number, unsigned _count)
{
assert(m_asking == Asking::Nothing);
assert(m_protocolVersion == host()->protocolVersion());
m_syncHashNumber = _number;
m_syncHash = h256();
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << _count;
clog(NetMessageDetail) << "Requesting block hashes for numbers " << m_syncHashNumber << "-" << m_syncHashNumber + _count - 1;
sealAndSend(s);
}
void EthereumPeer::requestHashes(h256 const& _lastHash)
{
if (m_asking != Asking::Nothing)
{
clog(NetWarn) << "Asking hashes while requesting " << (m_asking == Asking::Nothing ? "nothing" : m_asking == Asking::State ? "state" : m_asking == Asking::Hashes ? "hashes" : m_asking == Asking::Blocks ? "blocks" : "?");
// TODO: fix.
}
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk;
clog(NetMessageDetail) << "Requesting block hashes staring from " << _lastHash;
m_syncHash = _lastHash;
m_syncHashNumber = 0;
sealAndSend(s);
}
void EthereumPeer::requestBlocks(h256s const& _blocks)
{
setAsking(Asking::Blocks);
if (_blocks.size())
{
RLPStream s;
prep(s, GetBlocksPacket, _blocks.size());
for (auto const& i: _blocks)
s << i;
sealAndSend(s);
}
else
setIdle();
}
void EthereumPeer::requestBlocks()
{
setAsking(Asking::Blocks);
auto blocks = m_sub.nextFetch(askOverride());
if (blocks.size())
{
RLPStream s;
prep(s, GetBlocksPacket, blocks.size());
for (auto const& i: blocks)
s << i;
sealAndSend(s);
}
else
setIdle();
}
void EthereumPeer::setAsking(Asking _a)
{
m_asking = _a;
m_lastAsk = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
auto s = session();
if (s)
{
s->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?");
s->addNote("sync", string(isCriticalSyncing() ? "ONGOING" : "holding") + (needsSyncing() ? " & needed" : ""));
}
}
void EthereumPeer::tick()
{
auto s = session();
time_t now = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
if (s && (now - m_lastAsk > 10 && m_asking != Asking::Nothing))
// timeout
s->disconnect(PingTimeout);
}
bool EthereumPeer::isConversing() const
{
return m_asking != Asking::Nothing;
}
bool EthereumPeer::isCriticalSyncing() const
{
return m_asking == Asking::Hashes || m_asking == Asking::State || (m_asking == Asking::Blocks && m_protocolVersion == 60);
}
bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
m_lastAsk = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
try
{
switch (_id)
{
case StatusPacket:
{
m_protocolVersion = _r[0].toInt<unsigned>();
m_networkId = _r[1].toInt<u256>();
m_totalDifficulty = _r[2].toInt<u256>();
m_latestHash = _r[3].toHash<h256>();
m_genesisHash = _r[4].toHash<h256>();
if (m_peerCapabilityVersion == host()->protocolVersion())
m_protocolVersion = host()->protocolVersion();
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << m_genesisHash << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
setIdle();
host()->onPeerStatus(dynamic_pointer_cast<EthereumPeer>(dynamic_pointer_cast<EthereumPeer>(shared_from_this())));
break;
}
case TransactionsPacket:
{
host()->onPeerTransactions(dynamic_pointer_cast<EthereumPeer>(dynamic_pointer_cast<EthereumPeer>(shared_from_this())), _r);
break;
}
case GetBlockHashesPacket:
{
h256 later = _r[0].toHash<h256>();
unsigned limit = _r[1].toInt<unsigned>();
clog(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later << ")";
unsigned c = min<unsigned>(host()->chain().number(later), limit);
RLPStream s;
prep(s, BlockHashesPacket, c);
h256 p = host()->chain().details(later).parent;
for (unsigned i = 0; i < c && p; ++i, p = host()->chain().details(p).parent)
s << p;
sealAndSend(s);
addRating(0);
break;
}
case GetBlockHashesByNumberPacket:
{
u256 number256 = _r[0].toInt<u256>();
unsigned number = (unsigned) number256;
unsigned limit = _r[1].toInt<unsigned>();
clog(NetMessageSummary) << "GetBlockHashesByNumber (" << number << "-" << number + limit - 1 << ")";
RLPStream s;
if (number <= host()->chain().number())
{
unsigned c = min<unsigned>(host()->chain().number() - number + 1, limit);
prep(s, BlockHashesPacket, c);
for (unsigned n = number; n < number + c; n++)
{
h256 p = host()->chain().numberHash(n);
s << p;
}
}
else
prep(s, BlockHashesPacket, 0);
sealAndSend(s);
addRating(0);
break;
}
case BlockHashesPacket:
{
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "BlockHashes (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreHashes");
if (m_asking != Asking::Hashes)
{
clog(NetAllDetail) << "Peer giving us hashes when we didn't ask for them.";
break;
}
setIdle();
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
hashes[i] = _r[i].toHash<h256>();
host()->onPeerHashes(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), hashes);
break;
}
case GetBlocksPacket:
{
unsigned count = _r.itemCount();
clog(NetMessageSummary) << "GetBlocks (" << dec << count << "entries)";
if (!count)
{
clog(NetImpolite) << "Zero-entry GetBlocks: Not replying.";
addRating(-10);
break;
}
// return the requested blocks.
bytes rlp;
unsigned n = 0;
for (unsigned i = 0; i < min(count, c_maxBlocks) && rlp.size() < c_maxPayload; ++i)
{
auto h = _r[i].toHash<h256>();
if (host()->chain().isKnown(h))
{
rlp += host()->chain().block(_r[i].toHash<h256>());
++n;
}
}
if (count > 20 && n == 0)
clog(NetWarn) << "all" << count << "unknown blocks requested; peer on different chain?";
else
clog(NetMessageSummary) << n << "blocks known and returned;" << (min(count, c_maxBlocks) - n) << "blocks unknown;" << (count > c_maxBlocks ? count - c_maxBlocks : 0) << "blocks ignored";
addRating(0);
RLPStream s;
prep(s, BlocksPacket, n).appendRaw(rlp, n);
sealAndSend(s);
break;
}
case BlocksPacket:
{
if (m_asking != Asking::Blocks)
clog(NetImpolite) << "Peer giving us blocks when we didn't ask for them.";
else
{
setIdle();
host()->onPeerBlocks(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), _r);
}
break;
}
case NewBlockPacket:
{
host()->onPeerNewBlock(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), _r);
break;
}
case NewBlockHashesPacket:
{
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "BlockHashes (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreHashes");
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
hashes[i] = _r[i].toHash<h256>();
host()->onPeerNewHashes(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), hashes);
break;
}
default:
return false;
}
}
catch (Exception const&)
{
clog(NetWarn) << "Peer causing an Exception:" << boost::current_exception_diagnostic_information() << _r;
}
catch (std::exception const& _e)
{
clog(NetWarn) << "Peer causing an exception:" << _e.what() << _r;
}
return true;
}

165
libethereum/EthereumPeer.h

@ -1,165 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthereumPeer.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <mutex>
#include <array>
#include <memory>
#include <utility>
// Make sure boost/asio.hpp is included before windows.h.
#include <boost/asio.hpp>
#include <libdevcore/RLP.h>
#include <libdevcore/Guards.h>
#include <libethcore/Common.h>
#include <libp2p/Capability.h>
#include "CommonNet.h"
#include "DownloadMan.h"
namespace dev
{
namespace eth
{
/**
* @brief The EthereumPeer class
* @todo Document fully.
* @todo make state transitions thread-safe.
*/
class EthereumPeer: public p2p::Capability
{
friend class EthereumHost; //TODO: remove this
friend class BlockChainSync; //TODO: remove this
friend class PV60Sync; //TODO: remove this
friend class PV61Sync; //TODO: remove this
public:
/// Basic constructor.
EthereumPeer(std::shared_ptr<p2p::Session> _s, p2p::HostCapabilityFace* _h, unsigned _i, p2p::CapDesc const& _cap);
/// Basic destructor.
virtual ~EthereumPeer();
/// What is our name?
static std::string name() { return "eth"; }
/// What is our version?
static u256 version() { return c_protocolVersion; }
/// How many message types do we have?
static unsigned messageCount() { return PacketCount; }
/// What is the ethereum subprotocol host object.
EthereumHost* host() const;
/// Abort sync and reset fetch
void setIdle();
/// Request hashes by number. v61+ protocol version only
void requestHashes(u256 _number, unsigned _count);
/// Request hashes for given parent hash.
void requestHashes(h256 const& _lastHash);
/// Request blocks. Uses block download manager.
void requestBlocks();
/// Request specified blocks from peer.
void requestBlocks(h256s const& _blocks);
/// Check if this node is rude.
bool isRude() const;
/// Set that it's a rude node.
void setRude();
private:
using p2p::Capability::sealAndSend;
/// Figure out the amount of blocks we should be asking for.
unsigned askOverride() const;
/// Interpret an incoming message.
virtual bool interpret(unsigned _id, RLP const& _r);
/// Request status. Called from constructor
void requestStatus();
/// Abort the sync operation.
void abortSync();
/// Clear all known transactions.
void clearKnownTransactions() { std::lock_guard<std::mutex> l(x_knownTransactions); m_knownTransactions.clear(); }
/// Update our asking state.
void setAsking(Asking _g);
/// Do we presently need syncing with this peer?
bool needsSyncing() const { return !isRude() && !!m_latestHash; }
/// Are we presently in the process of communicating with this peer?
bool isConversing() const;
/// Are we presently in a critical part of the syncing process with this peer?
bool isCriticalSyncing() const;
/// Runs period checks to check up on the peer.
void tick();
/// Peer's protocol version.
unsigned m_protocolVersion;
/// Peer's network id.
u256 m_networkId;
/// What, if anything, we last asked the other peer for.
Asking m_asking = Asking::Nothing;
/// When we asked for it. Allows a time out.
std::atomic<time_t> m_lastAsk;
/// These are determined through either a Status message or from NewBlock.
h256 m_latestHash; ///< Peer's latest block's hash that we know about or default null value if no need to sync.
u256 m_totalDifficulty; ///< Peer's latest block's total difficulty.
h256 m_genesisHash; ///< Peer's genesis hash
/// This is built as we ask for hashes. Once no more hashes are given, we present this to the
/// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks.
unsigned m_expectedHashes = 0; ///< Estimated upper bound of hashes to expect from this peer.
u256 m_syncHashNumber = 0; ///< Number of latest hash we sync to (PV61+)
h256 m_syncHash; ///< Latest hash we sync to (PV60)
/// Once we're asking for blocks, this becomes in use.
DownloadSub m_sub;
u256 m_peerCapabilityVersion; ///< Protocol version this peer supports received as capability
/// Have we received a GetTransactions packet that we haven't yet answered?
bool m_requireTransactions = false;
Mutex x_knownBlocks;
h256Hash m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them).
Mutex x_knownTransactions;
h256Hash m_knownTransactions; ///< Transactions that the peer already knows of.
};
}
}

447
libethereum/Executive.cpp

@ -1,447 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Executive.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Executive.h"
#include <boost/timer.hpp>
#if ETH_JSONRPC || !ETH_TRUE
#include <json/json.h>
#endif
#include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h>
#include <libevm/VM.h>
#include "Interface.h"
#include "State.h"
#include "ExtVM.h"
#include "Precompiled.h"
#include "BlockChain.h"
#include "Block.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
const char* VMTraceChannel::name() { return "EVM"; }
const char* ExecutiveWarnChannel::name() { return WarnChannel::name(); }
StandardTrace::StandardTrace():
m_trace(make_shared<Json::Value>(Json::arrayValue))
{}
bool changesMemory(Instruction _inst)
{
return
_inst == Instruction::MSTORE ||
_inst == Instruction::MSTORE8 ||
_inst == Instruction::MLOAD ||
_inst == Instruction::CREATE ||
_inst == Instruction::CALL ||
_inst == Instruction::CALLCODE ||
_inst == Instruction::SHA3 ||
_inst == Instruction::CALLDATACOPY ||
_inst == Instruction::CODECOPY ||
_inst == Instruction::EXTCODECOPY;
}
bool changesStorage(Instruction _inst)
{
return _inst == Instruction::SSTORE;
}
void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
Json::Value r(Json::objectValue);
Json::Value stack(Json::arrayValue);
for (auto const& i: vm.stack())
stack.append(toHex(toCompactBigEndian(i), 1));
r["stack"] = stack;
bool returned = false;
bool newContext = false;
Instruction lastInst = Instruction::STOP;
if (m_lastInst.size() == ext.depth)
{
// starting a new context
assert(m_lastInst.size() == ext.depth);
m_lastInst.push_back(inst);
newContext = true;
}
else if (m_lastInst.size() == ext.depth + 2)
{
// returned from old context
returned = true;
m_lastInst.pop_back();
lastInst = m_lastInst.back();
}
else if (m_lastInst.size() == ext.depth + 1)
{
// continuing in previous context
lastInst = m_lastInst.back();
m_lastInst.back() = inst;
}
else
{
cwarn << "GAA!!! Tracing VM and more than one new/deleted stack frame between steps!";
cwarn << "Attmepting naive recovery...";
m_lastInst.resize(ext.depth + 1);
}
if (changesMemory(lastInst) || newContext)
{
if (vm.memory().size() < 1024)
r["memory"] = toHex(vm.memory());
else
r["sha3memory"] = sha3(vm.memory()).hex();
}
if (changesStorage(lastInst) || newContext)
{
Json::Value storage(Json::objectValue);
for (auto const& i: ext.state().storage(ext.myAddress))
storage[toHex(toCompactBigEndian(i.first), 1)] = toHex(toCompactBigEndian(i.second), 1);
r["storage"] = storage;
}
if (returned || newContext)
r["depth"] = ext.depth;
if (newContext)
r["address"] = ext.myAddress.hex();
r["steps"] = (unsigned)_steps;
r["inst"] = (unsigned)inst;
if (m_showMnemonics)
r["instname"] = instructionInfo(inst).name;
r["pc"] = toString(vm.curPC());
r["gas"] = toString(gas);
r["gascost"] = toString(gasCost);
if (!!newMemSize)
r["memexpand"] = toString(newMemSize);
m_trace->append(r);
}
string StandardTrace::json(bool _styled) const
{
return _styled ? Json::StyledWriter().write(*m_trace) : Json::FastWriter().write(*m_trace);
}
Executive::Executive(Block& _s, BlockChain const& _bc, unsigned _level):
Executive(_s, _bc.lastHashes(unsigned(_s.info().number() - 1)), _level)
{}
Executive::Executive(Block& _s, LastHashes const& _lh, unsigned _level):
m_s(_s.mutableState()),
m_envInfo(_s.info(), _lh),
m_depth(_level)
{}
Executive::Executive(State& _s, BlockChain const& _bc, EnvInfo const& _envInfo, unsigned _level):
m_s(_s),
m_envInfo(_envInfo),
m_depth(_level)
{
m_envInfo.setLastHashes(_bc.lastHashes((unsigned)m_envInfo.number() - 1));
}
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _number, unsigned _level):
m_s(_s),
m_envInfo(_bc.info(_bc.numberHash(_number)), _bc.lastHashes(_number - 1)),
m_depth(_level)
{}
u256 Executive::gasUsed() const
{
return m_t.gas() - m_gas;
}
u256 Executive::gasUsedNoRefunds() const
{
return m_t.gas() - m_gas + m_refunded;
}
void Executive::accrueSubState(SubState& _parentContext)
{
if (m_ext)
_parentContext += m_ext->sub;
}
void Executive::initialize(Transaction const& _transaction)
{
m_t = _transaction;
// Avoid transactions that would take us beyond the block gas limit.
u256 startGasUsed = m_envInfo.gasUsed();
if (startGasUsed + (bigint)m_t.gas() > m_envInfo.gasLimit())
{
clog(ExecutiveWarnChannel) << "Too much gas used in this block: Require <" << (m_envInfo.gasLimit() - startGasUsed) << " Got" << m_t.gas();
m_excepted = TransactionException::BlockGasLimitReached;
BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_envInfo.gasLimit() - startGasUsed), (bigint)m_t.gas()));
}
// Check gas cost is enough.
if (!m_t.checkPayment())
{
clog(ExecutiveWarnChannel) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas();
m_excepted = TransactionException::OutOfGasBase;
BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_t.gasRequired(), (bigint)m_t.gas()));
}
// Avoid invalid transactions.
u256 nonceReq;
try
{
nonceReq = m_s.transactionsFrom(m_t.sender());
}
catch (...)
{
clog(ExecutiveWarnChannel) << "Invalid Signature";
m_excepted = TransactionException::InvalidSignature;
throw;
}
if (m_t.nonce() != nonceReq)
{
clog(ExecutiveWarnChannel) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
m_excepted = TransactionException::InvalidNonce;
BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));
}
// Avoid unaffordable transactions.
m_gasCost = (bigint)m_t.gas() * m_t.gasPrice();
bigint totalCost = m_t.value() + m_gasCost;
if (m_s.balance(m_t.sender()) < totalCost)
{
clog(ExecutiveWarnChannel) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender()) << "for sender: " << m_t.sender();
m_excepted = TransactionException::NotEnoughCash;
BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().abridged()));
}
}
bool Executive::execute()
{
// Entry point for a user-executed transaction.
// Increment associated nonce for sender.
m_s.noteSending(m_t.sender());
// Pay...
clog(StateDetail) << "Paying" << formatBalance(u256(m_gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")";
m_s.subBalance(m_t.sender(), m_gasCost);
if (m_t.isCreation())
return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.gasRequired(), &m_t.data(), m_t.sender());
else
return call(m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired());
}
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas)
{
CallParameters params{_senderAddress, _receiveAddress, _receiveAddress, _gas, _value, _data, {}, {}};
return call(params, _gasPrice, _senderAddress);
}
bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin)
{
m_isCreation = false;
auto it = !(_p.codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_p.codeAddress) : precompiled().end();
if (it != precompiled().end())
{
bigint g = it->second.gas(_p.data);
if (_p.gas < g)
{
m_excepted = TransactionException::OutOfGasBase;
// Bail from exception.
return true; // true actually means "all finished - nothing more to be done regarding go().
}
else
{
m_gas = (u256)(_p.gas - g);
it->second.exec(_p.data, _p.out);
}
}
else
{
m_gas = _p.gas;
if (m_s.addressHasCode(_p.codeAddress))
{
m_outRef = _p.out; // Save ref to expected output buffer to be used in go()
bytes const& c = m_s.code(_p.codeAddress);
h256 codeHash = m_s.codeHash(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_envInfo, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, codeHash, m_depth);
}
}
m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.value);
return !m_ext;
}
bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
{
m_isCreation = true;
// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
// we delete it explicitly if we decide we need to revert.
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
m_gas = _gas;
// Execute _init.
if (!_init.empty())
m_ext = make_shared<ExtVM>(m_s, m_envInfo, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, sha3(_init), m_depth);
m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception);
m_s.transferBalance(_sender, m_newAddress, _endowment);
if (_init.empty())
m_s.m_cache[m_newAddress].setCode({});
return !m_ext;
}
OnOpFunc Executive::simpleTrace()
{
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
ostringstream o;
o << endl << " STACK" << endl;
for (auto i: vm.stack())
o << (h256)i << endl;
o << " MEMORY" << endl << ((vm.memory().size() > 1000) ? " mem size greater than 1000 bytes " : memDump(vm.memory()));
o << " STORAGE" << endl;
for (auto const& i: ext.state().storage(ext.myAddress))
o << showbase << hex << i.first << ": " << i.second << endl;
dev::LogOutputStream<VMTraceChannel, false>() << o.str();
dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
bool Executive::go(OnOpFunc const& _onOp)
{
if (m_ext)
{
#if ETH_TIMED_EXECUTIONS
Timer t;
#endif
try
{
// Create VM instance. Force Interpreter if tracing requested.
auto vm = _onOp ? VMFactory::create(VMKind::Interpreter) : VMFactory::create();
if (m_isCreation)
{
auto out = vm->exec(m_gas, *m_ext, _onOp);
if (m_res)
{
m_res->gasForDeposit = m_gas;
m_res->depositSize = out.size();
}
if (out.size() * c_createDataGas <= m_gas)
{
if (m_res)
m_res->codeDeposit = CodeDeposit::Success;
m_gas -= out.size() * c_createDataGas;
}
else
{
if (m_res)
m_res->codeDeposit = CodeDeposit::Failed;
out.clear();
}
if (m_res)
m_res->output = out; // copy output to execution result
m_s.m_cache[m_newAddress].setCode(std::move(out)); // FIXME: Set only if Success?
}
else
{
if (m_res)
{
m_res->output = vm->exec(m_gas, *m_ext, _onOp); // take full output
bytesConstRef{&m_res->output}.copyTo(m_outRef);
}
else
vm->exec(m_gas, *m_ext, m_outRef, _onOp); // take only expected output
}
}
catch (VMException const& _e)
{
clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
m_gas = 0;
m_excepted = toTransactionException(_e);
m_ext->revert();
if (m_isCreation)
m_newAddress = Address();
}
catch (Exception const& _e)
{
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
}
catch (std::exception const& _e)
{
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
#if ETH_TIMED_EXECUTIONS
cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
#endif
}
return true;
}
void Executive::finalize()
{
// Accumulate refunds for suicides.
if (m_ext)
m_ext->sub.refunds += c_suicideRefundGas * m_ext->sub.suicides.size();
// SSTORE refunds...
// must be done before the miner gets the fees.
m_refunded = m_ext ? min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds) : 0;
m_gas += m_refunded;
if (m_t)
{
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());
u256 feesEarned = (m_t.gas() - m_gas) * m_t.gasPrice();
m_s.addBalance(m_envInfo.beneficiary(), feesEarned);
}
// Suicides...
if (m_ext)
for (auto a: m_ext->sub.suicides)
m_s.m_cache[a].kill();
// Logs..
if (m_ext)
m_logs = m_ext->sub.logs;
if (m_res) // Collect results
{
m_res->gasUsed = gasUsed();
m_res->excepted = m_excepted; // TODO: m_except is used only in ExtVM::call
m_res->newAddress = m_newAddress;
m_res->gasRefunded = m_ext ? m_ext->sub.refunds : 0;
}
}

196
libethereum/Executive.h

@ -1,196 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Executive.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <functional>
#include <libdevcore/Log.h>
#include <libevmcore/Instruction.h>
#include <libethcore/Common.h>
#include <libevm/VMFace.h>
#include "Transaction.h"
namespace Json
{
class Value;
}
namespace dev
{
class OverlayDB;
namespace eth
{
class State;
class Block;
class BlockChain;
class ExtVM;
struct Manifest;
struct VMTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 11; };
struct ExecutiveWarnChannel: public LogChannel { static const char* name(); static const int verbosity = 6; };
class StandardTrace
{
public:
StandardTrace();
void operator()(uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM);
void setShowMnemonics() { m_showMnemonics = true; }
std::string json(bool _styled = false) const;
OnOpFunc onOp() { return [=](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { (*this)(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }; }
private:
bool m_showMnemonics = false;
std::vector<Instruction> m_lastInst;
std::shared_ptr<Json::Value> m_trace;
};
/**
* @brief Message-call/contract-creation executor; useful for executing transactions.
*
* Two ways of using this class - either as a transaction executive or a CALL/CREATE executive.
*
* In the first use, after construction, begin with initialize(), then execute() and end with finalize(). Call go()
* after execute() only if it returns false.
*
* In the second use, after construction, begin with call() or create() and end with
* accrueSubState(). Call go() after call()/create() only if it returns false.
*
* Example:
* @code
* Executive e(state, blockchain, 0);
* e.initialize(transaction);
* if (!e.execute())
* e.go();
* e.finalize();
* @endcode
*/
class Executive
{
public:
/// Simple constructor; executive will operate on given state, with the given environment info.
Executive(State& _s, EnvInfo const& _envInfo, unsigned _level = 0): m_s(_s), m_envInfo(_envInfo), m_depth(_level) {}
/** Easiest constructor.
* Creates executive to operate on the state of end of the given block, populating environment
* info from given Block and the LastHashes portion from the BlockChain.
*/
Executive(Block& _s, BlockChain const& _bc, unsigned _level = 0);
/** LastHashes-split constructor.
* Creates executive to operate on the state of end of the given block, populating environment
* info accordingly, with last hashes given explicitly.
*/
Executive(Block& _s, LastHashes const& _lh = LastHashes(), unsigned _level = 0);
/** Partially-automated split constructor; executive will operate on given state, with the given
* environment info, populating the last hashes from the given chain.
*/
Executive(State& _s, BlockChain const& _bc, EnvInfo const& _envInfo, unsigned _level = 0);
/**
* Automated split constructor; executive will operate on given state, with the environment info
* populated from the given chain.
* @note This will only work when the state to be operated on is already in the chain.
*/
Executive(State& _s, BlockChain const& _bc, unsigned _number, unsigned _level = 0);
Executive(Executive const&) = delete;
void operator=(Executive) = delete;
/// Initializes the executive for evaluating a transaction. You must call finalize() at some point following this.
void initialize(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckTransaction::None)); }
void initialize(Transaction const& _transaction);
/// Finalise a transaction previously set up with initialize().
/// @warning Only valid after initialize() and execute(), and possibly go().
void finalize();
/// Begins execution of a transaction. You must call finalize() following this.
/// @returns true if the transaction is done, false if go() must be called.
bool execute();
/// @returns the transaction from initialize().
/// @warning Only valid after initialize().
Transaction const& t() const { return m_t; }
/// @returns the log entries created by this operation.
/// @warning Only valid after finalise().
LogEntries const& logs() const { return m_logs; }
/// @returns total gas used in the transaction/operation.
/// @warning Only valid after finalise().
u256 gasUsed() const;
/// @returns total gas used in the transaction/operation, excluding anything refunded.
/// @warning Only valid after finalise().
u256 gasUsedNoRefunds() const;
/// Set up the executive for evaluating a bare CREATE (contract-creation) operation.
/// @returns false iff go() must be called (and thus a VM execution in required).
bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
/// Set up the executive for evaluating a bare CALL (message call) operation.
/// @returns false iff go() must be called (and thus a VM execution in required).
bool call(Address _receiveAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas);
bool call(CallParameters const& _cp, u256 const& _gasPrice, Address const& _origin);
/// Finalise an operation through accruing the substate into the parent context.
void accrueSubState(SubState& _parentContext);
/// Executes (or continues execution of) the VM.
/// @returns false iff go() must be called again to finish the transaction.
bool go(OnOpFunc const& _onOp = OnOpFunc());
/// Operation function for providing a simple trace of the VM execution.
static OnOpFunc simpleTrace();
/// Operation function for providing a simple trace of the VM execution.
static OnOpFunc standardTrace(std::ostream& o_output);
/// @returns gas remaining after the transaction/operation. Valid after the transaction has been executed.
u256 gas() const { return m_gas; }
/// @returns the new address for the created contract in the CREATE operation.
h160 newAddress() const { return m_newAddress; }
/// @returns true iff the operation ended with a VM exception.
bool excepted() const { return m_excepted != TransactionException::None; }
/// Collect execution results in the result storage provided.
void setResultRecipient(ExecutionResult& _res) { m_res = &_res; }
private:
State& m_s; ///< The state to which this operation/transaction is applied.
// TODO: consider changign to EnvInfo const& to avoid LastHashes copy at every CALL/CREATE
EnvInfo m_envInfo; ///< Information on the runtime environment.
std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required. shared_ptr used only to allow ExtVM forward reference. This field does *NOT* survive this object.
bytesRef m_outRef; ///< Reference to "expected output" buffer.
ExecutionResult* m_res = nullptr; ///< Optional storage for execution results.
Address m_newAddress; ///< The address of the created contract in the case of create() being called.
unsigned m_depth = 0; ///< The context's call-depth.
bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called.
TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception.
u256 m_gas = 0; ///< The gas for EVM code execution. Initial amount before go() execution, final amount after go() execution.
u256 m_refunded = 0; ///< The amount of gas refunded.
Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().
bigint m_gasCost;
};
}
}

125
libethereum/ExtVM.cpp

@ -1,125 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ExtVM.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "ExtVM.h"
#include <exception>
#include <boost/thread.hpp>
#include "Executive.h"
using namespace dev;
using namespace dev::eth;
namespace
{
static unsigned const c_depthLimit = 1024;
/// Upper bound of stack space needed by single CALL/CREATE execution. Set experimentally.
static size_t const c_singleExecutionStackSize =
#ifdef NDEBUG
10 * 1024;
#else
16 * 1024;
#endif
/// Standard thread stack size.
static size_t const c_defaultStackSize =
#if defined(__linux)
8 * 1024 * 1024;
#elif defined(_WIN32)
16 * 1024 * 1024;
#else
512 * 1024; // OSX and other OSs
#endif
/// Stack overhead prior to allocation.
static size_t const c_entryOverhead = 128 * 1024;
/// On what depth execution should be offloaded to additional separated stack space.
static unsigned const c_offloadPoint = (c_defaultStackSize - c_entryOverhead) / c_singleExecutionStackSize;
void goOnOffloadedStack(Executive& _e, OnOpFunc const& _onOp)
{
// Set new stack size enouth to handle the rest of the calls up to the limit.
boost::thread::attributes attrs;
attrs.set_stack_size((c_depthLimit - c_offloadPoint) * c_singleExecutionStackSize);
// Create new thread with big stack and join immediately.
// TODO: It is possible to switch the implementation to Boost.Context or similar when the API is stable.
std::exception_ptr exception;
boost::thread{attrs, [&]{
try
{
_e.go(_onOp);
}
catch (...)
{
exception = std::current_exception(); // Catch all exceptions to be rethrown in parent thread.
}
}}.join();
if (exception)
std::rethrow_exception(exception);
}
void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp)
{
// If in the offloading point we need to switch to additional separated stack space.
// Current stack is too small to handle more CALL/CREATE executions.
// It needs to be done only once as newly allocated stack space it enough to handle
// the rest of the calls up to the depth limit (c_depthLimit).
if (_depth == c_offloadPoint)
{
cnote << "Stack offloading (depth: " << c_offloadPoint << ")";
goOnOffloadedStack(_e, _onOp);
}
else
_e.go(_onOp);
}
}
bool ExtVM::call(CallParameters& _p)
{
Executive e(m_s, envInfo(), depth + 1);
if (!e.call(_p, gasPrice, origin))
{
go(depth, e, _p.onOp);
e.accrueSubState(sub);
}
_p.gas = e.gas();
return !e.excepted();
}
h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp)
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
Executive e(m_s, envInfo(), depth + 1);
if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin))
{
go(depth, e, _onOp);
e.accrueSubState(sub);
}
io_gas = e.gas();
return e.newAddress();
}

100
libethereum/ExtVM.h

@ -1,100 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ExtVM.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <map>
#include <functional>
#include <libethcore/Common.h>
#include <libevm/ExtVMFace.h>
#include "State.h"
namespace dev
{
namespace eth
{
/**
* @brief Externality interface for the Virtual Machine providing access to world state.
*/
class ExtVM: public ExtVMFace
{
public:
/// Full constructor.
ExtVM(State& _s, EnvInfo const& _envInfo, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, h256 const& _codeHash, unsigned _depth = 0):
ExtVMFace(_envInfo, _myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _codeHash, _depth), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
}
/// Read storage location.
virtual u256 store(u256 _n) override final { return m_s.storage(myAddress, _n); }
/// Write a value in storage.
virtual void setStore(u256 _n, u256 _v) override final { m_s.setStorage(myAddress, _n, _v); }
/// Read address's code.
virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); }
/// Create a new contract.
virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = {}) override final;
/// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller.
virtual bool call(CallParameters& _params) override final;
/// Read address's balance.
virtual u256 balance(Address _a) override final { return m_s.balance(_a); }
/// Subtract amount from account's balance.
virtual void subBalance(u256 _a) override final { m_s.subBalance(myAddress, _a); }
/// Determine account's TX count.
virtual u256 txCount(Address _a) override final { return m_s.transactionsFrom(_a); }
/// Does the account exist?
virtual bool exists(Address _a) override final { return m_s.addressInUse(_a); }
/// Suicide the associated contract to the given address.
virtual void suicide(Address _a) override final
{
m_s.addBalance(_a, m_s.balance(myAddress));
m_s.subBalance(myAddress, m_s.balance(myAddress));
ExtVMFace::suicide(_a);
}
/// Revert any changes made (by any of the other calls).
/// @TODO check call site for the parent manifest being discarded.
virtual void revert() override final
{
m_s.m_cache = m_origCache;
sub.clear();
}
State& state() const { return m_s; }
private:
State& m_s; ///< A reference to the base state.
std::unordered_map<Address, Account> m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution.
};
}
}

26
libethereum/GasPricer.cpp

@ -1,26 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file GasPricer.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#include "GasPricer.h"
using namespace std;
using namespace dev;
using namespace dev::eth;

76
libethereum/GasPricer.h

@ -1,76 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file GasPricer.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libethcore/Common.h>
namespace dev
{
namespace eth
{
class Block;
class BlockChain;
enum class TransactionPriority
{
Lowest = 0,
Low = 2,
Medium = 4,
High = 6,
Highest = 8
};
static const u256 DefaultGasPrice = 50 * shannon;
class GasPricer
{
public:
GasPricer() = default;
virtual ~GasPricer() = default;
virtual u256 ask(Block const&) const = 0;
virtual u256 bid(TransactionPriority _p = TransactionPriority::Medium) const = 0;
virtual void update(BlockChain const&) {}
};
class TrivialGasPricer: public GasPricer
{
public:
TrivialGasPricer() = default;
TrivialGasPricer(u256 const& _ask, u256 const& _bid): m_ask(_ask), m_bid(_bid) {}
void setAsk(u256 const& _ask) { m_ask = _ask; }
void setBid(u256 const& _bid) { m_bid = _bid; }
u256 ask() const { return m_ask; }
u256 ask(Block const&) const override { return m_ask; }
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return m_bid; }
private:
u256 m_ask = DefaultGasPrice;
u256 m_bid = DefaultGasPrice;
};
}
}

27012
libethereum/GenesisInfo.cpp

File diff suppressed because it is too large

33
libethereum/GenesisInfo.h

@ -1,33 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file GenesisInfo.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <string>
namespace dev {
namespace eth {
extern std::string const c_genesisInfoOlympic;
extern std::string const c_genesisInfoFrontier;
}
}

50
libethereum/Interface.cpp

@ -1,50 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Interface.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Interface.h"
using namespace std;
using namespace dev;
using namespace eth;
void Interface::submitTransaction(Secret const& _secret, u256 const& _value, Address const& _dest, bytes const& _data, u256 const& _gas, u256 const& _gasPrice, u256 const& _nonce)
{
TransactionSkeleton ts;
ts.creation = false;
ts.value = _value;
ts.to = _dest;
ts.data = _data;
ts.gas = _gas;
ts.gasPrice = _gasPrice;
ts.nonce = _nonce;
submitTransaction(ts, _secret);
}
Address Interface::submitTransaction(Secret const& _secret, u256 const& _endowment, bytes const& _init, u256 const& _gas, u256 const& _gasPrice, u256 const& _nonce)
{
TransactionSkeleton ts;
ts.creation = true;
ts.value = _endowment;
ts.data = _init;
ts.gas = _gas;
ts.gasPrice = _gasPrice;
ts.nonce = _nonce;
return submitTransaction(ts, _secret).second;
}

272
libethereum/Interface.h

@ -1,272 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Interface.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Guards.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Ethash.h>
#include <libethereum/GasPricer.h>
#include "LogFilter.h"
#include "Transaction.h"
#include "AccountDiff.h"
#include "BlockDetails.h"
namespace dev
{
namespace eth
{
using TransactionHashes = h256s;
using UncleHashes = h256s;
enum class Reaping
{
Automatic,
Manual
};
enum class FudgeFactor
{
Strict,
Lenient
};
/**
* @brief Main API hub for interfacing with Ethereum.
*/
class Interface
{
public:
/// Constructor.
Interface() {}
/// Destructor.
virtual ~Interface() {}
// [TRANSACTION API]
/// Submits a new transaction.
/// @returns the transaction's hash.
virtual std::pair<h256, Address> submitTransaction(TransactionSkeleton const& _t, Secret const& _secret) = 0;
/// Submits the given message-call transaction.
void submitTransaction(Secret const& _secret, u256 const& _value, Address const& _dest, bytes const& _data = bytes(), u256 const& _gas = 1000000, u256 const& _gasPrice = DefaultGasPrice, u256 const& _nonce = UndefinedU256);
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
Address submitTransaction(Secret const& _secret, u256 const& _endowment, bytes const& _init, u256 const& _gas = 1000000, u256 const& _gasPrice = DefaultGasPrice, u256 const& _nonce = UndefinedU256);
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() = 0;
/// Makes the given call. Nothing is recorded into the state.
virtual ExecutionResult call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) = 0;
ExecutionResult call(Address const& _from, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 1000000, u256 _gasPrice = DefaultGasPrice, FudgeFactor _ff = FudgeFactor::Strict) { return call(_from, _value, _dest, _data, _gas, _gasPrice, m_default, _ff); }
ExecutionResult call(Secret const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) { return call(toAddress(_secret), _value, _dest, _data, _gas, _gasPrice, _blockNumber, _ff); }
ExecutionResult call(Secret const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, FudgeFactor _ff = FudgeFactor::Strict) { return call(toAddress(_secret), _value, _dest, _data, _gas, _gasPrice, _ff); }
/// Does the given creation. Nothing is recorded into the state.
/// @returns the pair of the Address of the created contract together with its code.
virtual ExecutionResult create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) = 0;
ExecutionResult create(Address const& _from, u256 _value, bytes const& _data = bytes(), u256 _gas = 1000000, u256 _gasPrice = DefaultGasPrice, FudgeFactor _ff = FudgeFactor::Strict) { return create(_from, _value, _data, _gas, _gasPrice, m_default, _ff); }
ExecutionResult create(Secret const& _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) { return create(toAddress(_secret), _value, _data, _gas, _gasPrice, _blockNumber, _ff); }
ExecutionResult create(Secret const& _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, FudgeFactor _ff = FudgeFactor::Strict) { return create(toAddress(_secret), _value, _data, _gas, _gasPrice, _ff); }
/// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly.
virtual ImportResult injectTransaction(bytes const& _rlp, IfDropped _id = IfDropped::Ignore) = 0;
/// Injects the RLP-encoded block given by the _rlp into the block queue directly.
virtual ImportResult injectBlock(bytes const& _block) = 0;
// [STATE-QUERY API]
int getDefault() const { return m_default; }
void setDefault(BlockNumber _block) { m_default = _block; }
u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); }
u256 countAt(Address _a) const { return countAt(_a, m_default); }
u256 stateAt(Address _a, u256 _l) const { return stateAt(_a, _l, m_default); }
bytes codeAt(Address _a) const { return codeAt(_a, m_default); }
h256 codeHashAt(Address _a) const { return codeHashAt(_a, m_default); }
std::unordered_map<u256, u256> storageAt(Address _a) const { return storageAt(_a, m_default); }
virtual u256 balanceAt(Address _a, BlockNumber _block) const = 0;
virtual u256 countAt(Address _a, BlockNumber _block) const = 0;
virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const = 0;
virtual bytes codeAt(Address _a, BlockNumber _block) const = 0;
virtual h256 codeHashAt(Address _a, BlockNumber _block) const = 0;
virtual std::unordered_map<u256, u256> storageAt(Address _a, BlockNumber _block) const = 0;
// [LOGS API]
virtual LocalisedLogEntries logs(unsigned _watchId) const = 0;
virtual LocalisedLogEntries logs(LogFilter const& _filter) const = 0;
/// Install, uninstall and query watches.
virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) = 0;
virtual unsigned installWatch(h256 _filterId, Reaping _r = Reaping::Automatic) = 0;
virtual bool uninstallWatch(unsigned _watchId) = 0;
LocalisedLogEntries peekWatchSafe(unsigned _watchId) const { try { return peekWatch(_watchId); } catch (...) { return LocalisedLogEntries(); } }
LocalisedLogEntries checkWatchSafe(unsigned _watchId) { try { return checkWatch(_watchId); } catch (...) { return LocalisedLogEntries(); } }
virtual LocalisedLogEntries peekWatch(unsigned _watchId) const = 0;
virtual LocalisedLogEntries checkWatch(unsigned _watchId) = 0;
// [BLOCK QUERY API]
virtual bool isKnownTransaction(h256 const& _transactionHash) const = 0;
virtual bool isKnownTransaction(h256 const& _blockHash, unsigned _i) const = 0;
virtual Transaction transaction(h256 _transactionHash) const = 0;
virtual LocalisedTransaction localisedTransaction(h256 const& _transactionHash) const = 0;
virtual TransactionReceipt transactionReceipt(h256 const& _transactionHash) const = 0;
virtual LocalisedTransactionReceipt localisedTransactionReceipt(h256 const& _transactionHash) const = 0;
virtual std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const = 0;
virtual h256 hashFromNumber(BlockNumber _number) const = 0;
virtual BlockNumber numberFromHash(h256 _blockHash) const = 0;
virtual int compareBlockHashes(h256 _h1, h256 _h2) const = 0;
virtual bool isKnown(BlockNumber _block) const = 0;
virtual bool isKnown(h256 const& _hash) const = 0;
virtual BlockInfo blockInfo(h256 _hash) const = 0;
virtual BlockDetails blockDetails(h256 _hash) const = 0;
virtual Transaction transaction(h256 _blockHash, unsigned _i) const = 0;
virtual LocalisedTransaction localisedTransaction(h256 const& _blockHash, unsigned _i) const = 0;
virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const = 0;
virtual UncleHashes uncleHashes(h256 _blockHash) const = 0;
virtual unsigned transactionCount(h256 _blockHash) const = 0;
virtual unsigned uncleCount(h256 _blockHash) const = 0;
virtual Transactions transactions(h256 _blockHash) const = 0;
virtual TransactionHashes transactionHashes(h256 _blockHash) const = 0;
BlockInfo blockInfo(BlockNumber _block) const { return blockInfo(hashFromNumber(_block)); }
BlockDetails blockDetails(BlockNumber _block) const { return blockDetails(hashFromNumber(_block)); }
Transaction transaction(BlockNumber _block, unsigned _i) const { auto p = transactions(_block); return _i < p.size() ? p[_i] : Transaction(); }
unsigned transactionCount(BlockNumber _block) const { if (_block == PendingBlock) { auto p = pending(); return p.size(); } return transactionCount(hashFromNumber(_block)); }
Transactions transactions(BlockNumber _block) const { if (_block == PendingBlock) return pending(); return transactions(hashFromNumber(_block)); }
TransactionHashes transactionHashes(BlockNumber _block) const { if (_block == PendingBlock) return pendingHashes(); return transactionHashes(hashFromNumber(_block)); }
BlockInfo uncle(BlockNumber _block, unsigned _i) const { return uncle(hashFromNumber(_block), _i); }
UncleHashes uncleHashes(BlockNumber _block) const { return uncleHashes(hashFromNumber(_block)); }
unsigned uncleCount(BlockNumber _block) const { return uncleCount(hashFromNumber(_block)); }
// [EXTRA API]:
/// @returns The height of the chain.
virtual unsigned number() const = 0;
/// Get a map containing each of the pending transactions.
/// @TODO: Remove in favour of transactions().
virtual Transactions pending() const = 0;
virtual h256s pendingHashes() const = 0;
/// Differences between transactions.
StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); }
virtual StateDiff diff(unsigned _txi, h256 _block) const = 0;
virtual StateDiff diff(unsigned _txi, BlockNumber _block) const = 0;
/// Get a list of all active addresses.
/// NOTE: This only works when compiled with ETH_FATDB; otherwise will throw InterfaceNotSupported.
virtual Addresses addresses() const { return addresses(m_default); }
virtual Addresses addresses(BlockNumber _block) const = 0;
/// Get the remaining gas limit in this block.
virtual u256 gasLimitRemaining() const = 0;
// Get the gas bidding price
virtual u256 gasBidPrice() const = 0;
// [MINING API]:
/// Set the coinbase address.
virtual void setBeneficiary(Address _us) = 0;
/// Get the coinbase address.
virtual Address address() const = 0;
/// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread
virtual void startMining() = 0;
/// Stop mining.
/// NOT thread-safe
virtual void stopMining() = 0;
/// Are we mining now?
virtual bool isMining() const = 0;
/// Would we like to mine now?
virtual bool wouldMine() const = 0;
/// Current hash rate.
virtual u256 hashrate() const = 0;
/// Get hash of the current block to be mined minus the nonce (the 'work hash').
virtual std::tuple<h256, h256, h256> getEthashWork() { BOOST_THROW_EXCEPTION(InterfaceNotSupported("Interface::getEthashWork")); }
/// Submit the nonce for the proof-of-work.
virtual bool submitEthashWork(h256 const&, h64 const&) { BOOST_THROW_EXCEPTION(InterfaceNotSupported("Interface::submitEthashWork")); }
/// Submit the ongoing hashrate of a particular external miner.
virtual void submitExternalHashrate(u256 const&, h256 const&) { BOOST_THROW_EXCEPTION(InterfaceNotSupported("Interface::submitExternalHashrate")); }
/// Check the progress of the mining.
virtual WorkingProgress miningProgress() const = 0;
protected:
int m_default = PendingBlock;
};
class Watch;
}
}
namespace std { void swap(dev::eth::Watch& _a, dev::eth::Watch& _b); }
namespace dev
{
namespace eth
{
class Watch: public boost::noncopyable
{
friend void std::swap(Watch& _a, Watch& _b);
public:
Watch() {}
Watch(Interface& _c, h256 _f): m_c(&_c), m_id(_c.installWatch(_f)) {}
Watch(Interface& _c, LogFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {}
~Watch() { if (m_c) m_c->uninstallWatch(m_id); }
LocalisedLogEntries check() { return m_c ? m_c->checkWatch(m_id) : LocalisedLogEntries(); }
LocalisedLogEntries peek() { return m_c ? m_c->peekWatch(m_id) : LocalisedLogEntries(); }
LocalisedLogEntries logs() const { return m_c->logs(m_id); }
private:
Interface* m_c = nullptr;
unsigned m_id = 0;
};
}
}
namespace std
{
inline void swap(dev::eth::Watch& _a, dev::eth::Watch& _b)
{
swap(_a.m_c, _b.m_c);
swap(_a.m_id, _b.m_id);
}
}

166
libethereum/LogFilter.cpp

@ -1,166 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file LogFilter.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "LogFilter.h"
#include <libdevcore/SHA3.h>
#include "Block.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
std::ostream& dev::eth::operator<<(std::ostream& _out, LogFilter const& _s)
{
// TODO
_out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< )";
return _out;
}
void LogFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(4) << m_addresses << m_topics << m_earliest << m_latest;
}
h256 LogFilter::sha3() const
{
RLPStream s;
streamRLP(s);
return dev::sha3(s.out());
}
bool LogFilter::isRangeFilter() const
{
if (m_addresses.size())
return false;
for (auto const& t: m_topics)
if (t.size())
return false;
return true;
}
bool LogFilter::matches(LogBloom _bloom) const
{
if (m_addresses.size())
{
for (auto const& i: m_addresses)
if (_bloom.containsBloom<3>(dev::sha3(i)))
goto OK1;
return false;
}
OK1:
for (auto const& t: m_topics)
if (t.size())
{
for (auto const& i: t)
if (_bloom.containsBloom<3>(dev::sha3(i)))
goto OK2;
return false;
OK2:;
}
return true;
}
bool LogFilter::matches(Block const& _s, unsigned _i) const
{
return matches(_s.receipt(_i)).size() > 0;
}
vector<LogBloom> LogFilter::bloomPossibilities() const
{
// return combination of each of the addresses/topics
vector<LogBloom> ret;
// | every address with every topic
for (auto const& i: m_addresses)
{
// 1st case, there are addresses and topics
//
// m_addresses = [a0, a1];
// m_topics = [[t0], [t1a, t1b], [], []];
//
// blooms = [
// a0 | t0, a0 | t1a | t1b,
// a1 | t0, a1 | t1a | t1b
// ]
//
for (auto const& t: m_topics)
if (t.size())
{
LogBloom b = LogBloom().shiftBloom<3>(dev::sha3(i));
for (auto const &j: t)
b = b.shiftBloom<3>(dev::sha3(j));
ret.push_back(b);
}
}
// 2nd case, there are no topics
//
// m_addresses = [a0, a1];
// m_topics = [[t0], [t1a, t1b], [], []];
//
// blooms = [a0, a1];
//
if (!ret.size())
for (auto const& i: m_addresses)
ret.push_back(LogBloom().shiftBloom<3>(dev::sha3(i)));
// 3rd case, there are no addresses, at least create blooms from topics
//
// m_addresses = [];
// m_topics = [[t0], [t1a, t1b], [], []];
//
// blooms = [t0, t1a | t1b];
//
if (!m_addresses.size())
for (auto const& t: m_topics)
if (t.size())
{
LogBloom b;
for (auto const &j: t)
b = b.shiftBloom<3>(dev::sha3(j));
ret.push_back(b);
}
return ret;
}
LogEntries LogFilter::matches(TransactionReceipt const& _m) const
{
// there are no addresses or topics to filter
if (isRangeFilter())
return _m.log();
LogEntries ret;
if (matches(_m.bloom()))
for (LogEntry const& e: _m.log())
{
if (!m_addresses.empty() && !m_addresses.count(e.address))
goto continue2;
for (unsigned i = 0; i < 4; ++i)
if (!m_topics[i].empty() && (e.topics.size() < i || !m_topics[i].count(e.topics[i])))
goto continue2;
ret.push_back(e);
continue2:;
}
return ret;
}

88
libethereum/LogFilter.h

@ -1,88 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file LogFilter.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libethcore/Common.h>
#include "TransactionReceipt.h"
namespace dev
{
namespace eth
{
class LogFilter;
}
namespace eth
{
/// Simple stream output for the StateDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
class State;
class Block;
class LogFilter
{
public:
LogFilter(h256 _earliest = EarliestBlockHash, h256 _latest = PendingBlockHash): m_earliest(_earliest), m_latest(_latest) {}
void streamRLP(RLPStream& _s) const;
h256 sha3() const;
/// hash of earliest block which should be filtered
h256 earliest() const { return m_earliest; }
/// hash of latest block which should be filtered
h256 latest() const { return m_latest; }
/// Range filter is a filter which doesn't care about addresses or topics
/// Matches are all entries from earliest to latest
/// @returns true if addresses and topics are unspecified
bool isRangeFilter() const;
/// @returns bloom possibilities for all addresses and topics
std::vector<LogBloom> bloomPossibilities() const;
bool matches(LogBloom _bloom) const;
bool matches(Block const& _b, unsigned _i) const;
LogEntries matches(TransactionReceipt const& _r) const;
LogFilter address(Address _a) { m_addresses.insert(_a); return *this; }
LogFilter topic(unsigned _index, h256 const& _t) { if (_index < 4) m_topics[_index].insert(_t); return *this; }
LogFilter withEarliest(h256 _e) { m_earliest = _e; return *this; }
LogFilter withLatest(h256 _e) { m_latest = _e; return *this; }
friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
private:
AddressHash m_addresses;
std::array<h256Hash, 4> m_topics;
h256 m_earliest = EarliestBlockHash;
h256 m_latest = PendingBlockHash;
};
}
}

99
libethereum/Precompiled.cpp

@ -1,99 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Precompiled.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Precompiled.h"
#include <libdevcore/Log.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/Hash.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Common.h>
#include <libevmcore/Params.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace
{
void ecrecoverCode(bytesConstRef _in, bytesRef _out)
{
struct inType
{
h256 hash;
h256 v;
h256 r;
h256 s;
} in;
memcpy(&in, _in.data(), min(_in.size(), sizeof(in)));
h256 ret;
u256 v = (u256)in.v;
if (v >= 27 && v <= 28)
{
SignatureStruct sig(in.r, in.s, (byte)((int)v - 27));
if (sig.isValid())
{
try
{
Public rec = recover(sig, in.hash);
if (rec)
ret = dev::sha3(rec);
else
return;
}
catch (...) { return; }
}
}
memset(ret.data(), 0, 12);
ret.ref().copyTo(_out);
}
void sha256Code(bytesConstRef _in, bytesRef _out)
{
sha256(_in).ref().copyTo(_out);
}
void ripemd160Code(bytesConstRef _in, bytesRef _out)
{
h256(ripemd160(_in), h256::AlignRight).ref().copyTo(_out);
}
void identityCode(bytesConstRef _in, bytesRef _out)
{
_in.copyTo(_out);
}
}
std::unordered_map<unsigned, PrecompiledAddress> const& dev::eth::precompiled()
{
static const std::unordered_map<unsigned, PrecompiledAddress> c_precompiled =
{
{ 1, { [](bytesConstRef) -> bigint { return c_ecrecoverGas; }, ecrecoverCode }},
{ 2, { [](bytesConstRef i) -> bigint { return c_sha256Gas + (i.size() + 31) / 32 * c_sha256WordGas; }, sha256Code }},
{ 3, { [](bytesConstRef i) -> bigint { return c_ripemd160Gas + (i.size() + 31) / 32 * c_ripemd160WordGas; }, ripemd160Code }},
{ 4, { [](bytesConstRef i) -> bigint { return c_identityGas + (i.size() + 31) / 32 * c_identityWordGas; }, identityCode }}
};
return c_precompiled;
}

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

Loading…
Cancel
Save