Genoil
9 years ago
430 changed files with 25 additions and 160789 deletions
@ -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) |
|||
|
@ -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() |
@ -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_
|
@ -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_
|
@ -1,4 +0,0 @@ |
|||
[ |
|||
{ "name": "eth_getWork", "params": [], "order": [], "returns": []}, |
|||
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true} |
|||
] |
File diff suppressed because it is too large
@ -1,3 +0,0 @@ |
|||
[ |
|||
{ "name": "report_benchmark", "params": [ "", 0 ], "order": [], "returns": 0 } |
|||
] |
@ -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 ) |
|||
|
@ -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)); |
|||
} |
@ -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; |
|||
}; |
|||
|
@ -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,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(); |
|||
} |
|||
} |
@ -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()); |
|||
|
|||
} |
@ -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} ) |
|||
|
@ -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); |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
@ -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); |
|||
} |
|||
|
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
@ -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); |
|||
} |
|||
|
@ -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.
|
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
@ -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); |
|||
|
|||
} |
|||
} |
@ -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 |
|||
} |
|||
|
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
|
File diff suppressed because it is too large
@ -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; |
|||
|
|||
} |
@ -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; |
|||
|
@ -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); |
|||
} |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
|
@ -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,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")); |
|||
} |
|||
|
|||
} |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
@ -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()); |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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); |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -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); |
|||
|
|||
} |
|||
|
@ -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" |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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); |
|||
|
|||
|
|||
} |
|||
|
|||
} |
File diff suppressed because it is too large
@ -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); |
|||
|
|||
} |
|||
} |
File diff suppressed because it is too large
@ -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); |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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); |
|||
|
|||
} |
|||
} |
@ -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} ) |
|||
|
@ -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; |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
|||
|
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
}} |
@ -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; } |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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(); |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
|||
|
@ -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:; |
|||
} |
|||
} |
@ -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 }; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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.
|
|||
}; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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(); |
|||
} |
@ -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.
|
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
@ -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; |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
File diff suppressed because it is too large
@ -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; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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); |
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
@ -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…
Reference in new issue