diff --git a/CMakeLists.txt b/CMakeLists.txt index 40a3e8e63..f3d528886 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,6 +194,7 @@ add_subdirectory(libevm) add_subdirectory(libethereum) add_subdirectory(libwebthree) +add_subdirectory(libtestutils) add_subdirectory(test) if (NOT JUSTTESTS) diff --git a/libtestutils/BlockChainLoader.cpp b/libtestutils/BlockChainLoader.cpp new file mode 100644 index 000000000..eafab190e --- /dev/null +++ b/libtestutils/BlockChainLoader.cpp @@ -0,0 +1,100 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file BlockChainLoader.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include "BlockChainLoader.h" +#include "StateLoader.h" +#include "Common.h" + +using namespace std; +using namespace dev; +using namespace dev::test; +using namespace dev::eth; + +namespace dev +{ +namespace test +{ +dev::eth::BlockInfo toBlockInfo(Json::Value const& _json); +bytes toGenesisBlock(Json::Value const& _json); +} +} + +dev::eth::BlockInfo dev::test::toBlockInfo(Json::Value const& _json) +{ + RLPStream rlpStream; + auto size = _json.getMemberNames().size(); + rlpStream.appendList(_json["hash"].empty() ? size : (size - 1)); + rlpStream << fromHex(_json["parentHash"].asString()); + rlpStream << fromHex(_json["uncleHash"].asString()); + rlpStream << fromHex(_json["coinbase"].asString()); + rlpStream << fromHex(_json["stateRoot"].asString()); + rlpStream << fromHex(_json["transactionsTrie"].asString()); + rlpStream << fromHex(_json["receiptTrie"].asString()); + rlpStream << fromHex(_json["bloom"].asString()); + rlpStream << bigint(_json["difficulty"].asString()); + rlpStream << bigint(_json["number"].asString()); + rlpStream << bigint(_json["gasLimit"].asString()); + rlpStream << bigint(_json["gasUsed"].asString()); + rlpStream << bigint(_json["timestamp"].asString()); + rlpStream << fromHex(_json["extraData"].asString()); + rlpStream << fromHex(_json["mixHash"].asString()); + rlpStream << fromHex(_json["nonce"].asString()); + + BlockInfo result; + RLP rlp(rlpStream.out()); + result.populateFromHeader(rlp, IgnoreNonce); + return result; +} + +bytes dev::test::toGenesisBlock(Json::Value const& _json) +{ + BlockInfo bi = toBlockInfo(_json); + RLPStream rlpStream; + bi.streamRLP(rlpStream, WithNonce); + + RLPStream fullStream(3); + fullStream.appendRaw(rlpStream.out()); + fullStream.appendRaw(RLPEmptyList); + fullStream.appendRaw(RLPEmptyList); + bi.verifyInternals(&fullStream.out()); + + return fullStream.out(); +} + +BlockChainLoader::BlockChainLoader(Json::Value const& _json) +{ + // load pre state + StateLoader sl(_json["pre"]); + m_state = sl.state(); + + // load genesisBlock + m_bc.reset(new BlockChain(toGenesisBlock(_json["genesisBlockHeader"]), m_dir.path(), true)); + + // load blocks + for (auto const& block: _json["blocks"]) + { + bytes rlp = fromHex(block["rlp"].asString()); + m_bc->import(rlp, m_state.db()); + } + + // sync state + m_state.sync(*m_bc); +} diff --git a/libtestutils/BlockChainLoader.h b/libtestutils/BlockChainLoader.h new file mode 100644 index 000000000..6cb04c53c --- /dev/null +++ b/libtestutils/BlockChainLoader.h @@ -0,0 +1,52 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file BlockChainLoader.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once +#include +#include +#include +#include +#include "TransientDirectory.h" + +namespace dev +{ +namespace test +{ + +/** + * @brief Should be used to load test blockchain from json file + * Loads the blockchain from json, creates temporary directory to store it, removes the directory on dealloc + */ +class BlockChainLoader +{ +public: + BlockChainLoader(Json::Value const& _json); + eth::BlockChain const& bc() const { return *m_bc; } + eth::State const& state() const { return m_state; } + +private: + TransientDirectory m_dir; + std::auto_ptr m_bc; + eth::State m_state; +}; + +} +} diff --git a/libtestutils/CMakeLists.txt b/libtestutils/CMakeLists.txt new file mode 100644 index 000000000..202b9823c --- /dev/null +++ b/libtestutils/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_policy(SET CMP0015 NEW) +# this policy was introduced in cmake 3.0 +# remove if, once 3.0 will be used on unix +if (${CMAKE_MAJOR_VERSION} GREATER 2) + # old policy do not use MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) +endif() +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) +include_directories(BEFORE ..) +include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIRS}) + +set(EXECUTABLE testutils) + +file(GLOB HEADERS "*.h") + +if (ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) +endif() + +target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) +target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) +target_link_libraries(${EXECUTABLE} ethereum) +target_link_libraries(${EXECUTABLE} web3jsonrpc) + +install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libtestutils/Common.cpp b/libtestutils/Common.cpp new file mode 100644 index 000000000..f15a2da12 --- /dev/null +++ b/libtestutils/Common.cpp @@ -0,0 +1,80 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file Common.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include +#include +#include +#include "Common.h" + +using namespace std; +using namespace dev; +using namespace dev::test; + +std::string dev::test::getTestPath() +{ + string testPath; + const char* ptestPath = getenv("ETHEREUM_TEST_PATH"); + + if (ptestPath == NULL) + { + ctest << " could not find environment variable ETHEREUM_TEST_PATH \n"; + testPath = "../../../tests"; + } + else + testPath = ptestPath; + + return testPath; +} + +int dev::test::randomNumber() +{ + static std::mt19937 randomGenerator(time(0)); + randomGenerator.seed(std::random_device()()); + return std::uniform_int_distribution(1)(randomGenerator); +} + +Json::Value dev::test::loadJsonFromFile(std::string const& _path) +{ + Json::Reader reader; + Json::Value result; + string s = asString(dev::contents(_path)); + if (!s.length()) + ctest << "Contents of " + _path + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"; + else + ctest << "FIXTURE: loaded test from file: " << _path; + + reader.parse(s, result); + return result; +} + +std::string dev::test::toTestFilePath(std::string const& _filename) +{ + return getTestPath() + "/" + _filename + ".json"; +} + +std::string dev::test::getRandomPath() +{ + std::stringstream stream; + stream << getDataDir() << "/EthereumTests/" << randomNumber(); + return stream.str(); +} + diff --git a/libtestutils/Common.h b/libtestutils/Common.h new file mode 100644 index 000000000..4757a3b7a --- /dev/null +++ b/libtestutils/Common.h @@ -0,0 +1,44 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file Common.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace test +{ + +struct TestChannel: public LogChannel { static const char* name() { return "TEST"; } }; +#define ctest dev::LogOutputStream() + +std::string getTestPath(); +int randomNumber(); +Json::Value loadJsonFromFile(std::string const& _path); +std::string toTestFilePath(std::string const& _filename); +std::string getRandomPath(); + +} + +} diff --git a/libtestutils/FixedClient.cpp b/libtestutils/FixedClient.cpp new file mode 100644 index 000000000..052141039 --- /dev/null +++ b/libtestutils/FixedClient.cpp @@ -0,0 +1,32 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file FixedClient.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include "FixedClient.h" + +using namespace dev; +using namespace dev::eth; +using namespace dev::test; + +eth::State FixedClient::asOf(h256 const& _h) const +{ + ReadGuard l(x_stateDB); + return State(m_state.db(), bc(), _h); +} diff --git a/libtestutils/FixedClient.h b/libtestutils/FixedClient.h new file mode 100644 index 000000000..daca444fb --- /dev/null +++ b/libtestutils/FixedClient.h @@ -0,0 +1,59 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file FixedClient.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace test +{ + +/** + * @brief mvp implementation of ClientBase + * Doesn't support mining interface + */ +class FixedClient: public dev::eth::ClientBase +{ +public: + FixedClient(eth::BlockChain const& _bc, eth::State _state) : m_bc(_bc), m_state(_state) {} + virtual ~FixedClient() {} + + // stub + virtual void flushTransactions() override {} + virtual eth::BlockChain const& bc() const override { return m_bc; } + using ClientBase::asOf; + virtual eth::State asOf(h256 const& _h) const override; + virtual eth::State preMine() const override { ReadGuard l(x_stateDB); return m_state; } + virtual eth::State postMine() const override { ReadGuard l(x_stateDB); return m_state; } + virtual void prepareForTransaction() override {} + +private: + eth::BlockChain const& m_bc; + eth::State m_state; + mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. +}; + +} +} diff --git a/libtestutils/FixedWebThreeServer.cpp b/libtestutils/FixedWebThreeServer.cpp new file mode 100644 index 000000000..c72a106c6 --- /dev/null +++ b/libtestutils/FixedWebThreeServer.cpp @@ -0,0 +1,22 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file FixedWebThreeStubServer.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include "FixedWebThreeServer.h" diff --git a/libtestutils/FixedWebThreeServer.h b/libtestutils/FixedWebThreeServer.h new file mode 100644 index 000000000..33ccbf71e --- /dev/null +++ b/libtestutils/FixedWebThreeServer.h @@ -0,0 +1,57 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file FixedWebThreeStubServer.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include + +/** + * @brief dummy JSON-RPC api implementation + * Should be used for test purposes only + * Supports eth && db interfaces + * Doesn't support shh && net interfaces + */ +class FixedWebThreeServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace +{ +public: + FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client): WebThreeStubServerBase(_conn, _accounts), m_client(_client) {}; + +private: + dev::eth::Interface* client() override { return m_client; } + std::shared_ptr face() override { BOOST_THROW_EXCEPTION(dev::InterfaceNotSupported("dev::shh::Interface")); } + dev::WebThreeNetworkFace* network() override { BOOST_THROW_EXCEPTION(dev::InterfaceNotSupported("dev::WebThreeNetworkFace")); } + dev::WebThreeStubDatabaseFace* db() override { return this; } + std::string get(std::string const& _name, std::string const& _key) override + { + std::string k(_name + "/" + _key); + return m_db[k]; + } + void put(std::string const& _name, std::string const& _key, std::string const& _value) override + { + std::string k(_name + "/" + _key); + m_db[k] = _value; + } + +private: + dev::eth::Interface* m_client; + std::map m_db; +}; diff --git a/libtestutils/StateLoader.cpp b/libtestutils/StateLoader.cpp new file mode 100644 index 000000000..b56475b5a --- /dev/null +++ b/libtestutils/StateLoader.cpp @@ -0,0 +1,56 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file StateLoader.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include "StateLoader.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace dev::test; + +StateLoader::StateLoader(Json::Value const& _json) : m_state(Address(), OverlayDB(), BaseState::Empty) +{ + for (string const& name: _json.getMemberNames()) + { + Json::Value o = _json[name]; + + Address address = Address(name); + bytes code = fromHex(o["code"].asString().substr(2)); + + if (code.size()) + { + m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::ContractConception); + m_state.m_cache[address].setCode(code); + } + else + m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::NormalCreation); + + for (string const& j: o["storage"].getMemberNames()) + m_state.setStorage(address, u256(j), u256(o["storage"][j].asString())); + + for (auto i = 0; i < u256(o["nonce"].asString()); ++i) + m_state.noteSending(address); + + m_state.ensureCached(address, false, false); + } + + m_state.commit(); +} diff --git a/libtestutils/StateLoader.h b/libtestutils/StateLoader.h new file mode 100644 index 000000000..e5843d0b4 --- /dev/null +++ b/libtestutils/StateLoader.h @@ -0,0 +1,45 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file StateLoader.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include + +namespace dev +{ +namespace test +{ + +/** + * @brief Friend of State, loads State from given JSON object + */ +class StateLoader +{ +public: + StateLoader(Json::Value const& _json); + eth::State const& state() const { return m_state; } + +private: + eth::State m_state; +}; +} +} diff --git a/libtestutils/TransientDirectory.cpp b/libtestutils/TransientDirectory.cpp new file mode 100644 index 000000000..48beca7e3 --- /dev/null +++ b/libtestutils/TransientDirectory.cpp @@ -0,0 +1,42 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file TransientDirectory.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include +#include "TransientDirectory.h" + +using namespace std; +using namespace dev; +using namespace dev::test; + +TransientDirectory::TransientDirectory(std::string const& _path) : m_path(_path) +{ + // we never ever want to delete a directory (including all its contents) that we did not create ourselves. + if (boost::filesystem::exists(m_path)) + BOOST_THROW_EXCEPTION(FileError()); + + boost::filesystem::create_directories(m_path); +} + +TransientDirectory::~TransientDirectory() +{ + boost::filesystem::remove_all(m_path); +} diff --git a/libtestutils/TransientDirectory.h b/libtestutils/TransientDirectory.h new file mode 100644 index 000000000..328b4c92b --- /dev/null +++ b/libtestutils/TransientDirectory.h @@ -0,0 +1,50 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file TransientDirectory.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include "Common.h" + +namespace dev +{ +namespace test +{ + +/** + * @brief temporary directory implementation + * It creates temporary directory in the given path. On dealloc it removes the directory + * @throws if the given path already exists, throws an exception + */ +class TransientDirectory +{ +public: + TransientDirectory(std::string const& _path = getRandomPath()); + ~TransientDirectory(); + + std::string const& path() const { return m_path; } + +private: + std::string m_path; +}; + +} +}