Browse Source

Merge pull request #2075 from winsvega/fuzz

FuzzTests
cl-refactor
Gav Wood 10 years ago
parent
commit
8b38d3e636
  1. 5
      CMakeLists.txt
  2. 47
      test/TestHelper.cpp
  3. 30
      test/TestHelper.h
  4. 6
      test/fuzzTesting/CMakeLists.txt
  5. 397
      test/fuzzTesting/createRandomTest.cpp
  6. 13
      test/fuzzTesting/fuzzHelper.cpp
  7. 8
      test/fuzzTesting/fuzzHelper.h
  8. 128
      test/libethereum/blockchain.cpp
  9. 14
      test/libethereum/state.cpp
  10. 43
      test/libethereum/transaction.cpp
  11. 26
      test/libevm/vm.cpp

5
CMakeLists.txt

@ -31,6 +31,7 @@ option(FATDB "Build with ability to list entries in the Trie. Doubles DB size, s
option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF) option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF)
option(PROFILING "Build in support for profiling" OFF) option(PROFILING "Build in support for profiling" OFF)
set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).") set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).")
option(MINER "Build the CLI miner component" ON) option(MINER "Build the CLI miner component" ON)
option(ETHKEY "Build the CLI key manager component" ON) option(ETHKEY "Build the CLI key manager component" ON)
@ -40,6 +41,7 @@ option(TOOLS "Build the tools components" ON)
option(NCURSES "Build the NCurses components" OFF) option(NCURSES "Build the NCurses components" OFF)
option(GUI "Build GUI components (AlethZero, Mix)" ON) option(GUI "Build GUI components (AlethZero, Mix)" ON)
option(TESTS "Build the tests." ON) option(TESTS "Build the tests." ON)
option(NOBOOST "No use of boost macros in test functions" OFF)
option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF)
option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF) option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF)
option(JSCONSOLE "Build in javascript console" OFF) option(JSCONSOLE "Build in javascript console" OFF)
@ -82,6 +84,7 @@ function(configureProject)
add_definitions(-DETH_CURL) add_definitions(-DETH_CURL)
endif() endif()
add_definitions(-DNOBOOST)
add_definitions(-DETH_TRUE) add_definitions(-DETH_TRUE)
endfunction() endfunction()
@ -195,6 +198,7 @@ eth_format_option(PROFILING)
eth_format_option(SOLIDITY) eth_format_option(SOLIDITY)
eth_format_option(GUI) eth_format_option(GUI)
eth_format_option(TESTS) eth_format_option(TESTS)
eth_format_option(NOBOOST)
eth_format_option(TOOLS) eth_format_option(TOOLS)
eth_format_option(ETHASHCL) eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE) eth_format_option(JSCONSOLE)
@ -316,6 +320,7 @@ message("-- SERPENT Build Serpent language components ${SERPENT}
message("-- GUI Build GUI components ${GUI}") message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}") message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}") message("-- TESTS Build tests ${TESTS}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}") message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}") message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}") message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")

47
test/TestHelper.cpp

@ -178,7 +178,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptio
{ {
stateOptions.m_bHasBalance = true; stateOptions.m_bHasBalance = true;
if (bigint(o["balance"].get_str()) >= c_max256plus1) if (bigint(o["balance"].get_str()) >= c_max256plus1)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'balance' is equal or greater than 2**256") ); TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'balance' is equal or greater than 2**256") );
balance = toInt(o["balance"]); balance = toInt(o["balance"]);
} }
@ -186,7 +186,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptio
{ {
stateOptions.m_bHasNonce = true; stateOptions.m_bHasNonce = true;
if (bigint(o["nonce"].get_str()) >= c_max256plus1) if (bigint(o["nonce"].get_str()) >= c_max256plus1)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'nonce' is equal or greater than 2**256") ); TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'nonce' is equal or greater than 2**256") );
nonce = toInt(o["nonce"]); nonce = toInt(o["nonce"]);
} }
@ -230,7 +230,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state)
{ {
//check that every parameter was declared in state object //check that every parameter was declared in state object
if (!stateOptionMap.second.isAllSet()) if (!stateOptionMap.second.isAllSet())
BOOST_THROW_EXCEPTION(MissingFields() << errinfo_comment("Import State: Missing state fields!")); TBOOST_THROW_EXCEPTION(MissingFields() << errinfo_comment("Import State: Missing state fields!"));
} }
} }
@ -246,13 +246,13 @@ void ImportTest::importTransaction(json_spirit::mObject& _o)
assert(_o.count("data") > 0); assert(_o.count("data") > 0);
if (bigint(_o["nonce"].get_str()) >= c_max256plus1) if (bigint(_o["nonce"].get_str()) >= c_max256plus1)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'nonce' is equal or greater than 2**256") ); TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'nonce' is equal or greater than 2**256") );
if (bigint(_o["gasPrice"].get_str()) >= c_max256plus1) if (bigint(_o["gasPrice"].get_str()) >= c_max256plus1)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'gasPrice' is equal or greater than 2**256") ); TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'gasPrice' is equal or greater than 2**256") );
if (bigint(_o["gasLimit"].get_str()) >= c_max256plus1) if (bigint(_o["gasLimit"].get_str()) >= c_max256plus1)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'gasLimit' is equal or greater than 2**256") ); TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'gasLimit' is equal or greater than 2**256") );
if (bigint(_o["value"].get_str()) >= c_max256plus1) if (bigint(_o["value"].get_str()) >= c_max256plus1)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'value' is equal or greater than 2**256") ); TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'value' is equal or greater than 2**256") );
m_transaction = _o["to"].get_str().empty() ? m_transaction = _o["to"].get_str().empty() ?
Transaction(toInt(_o["value"]), toInt(_o["gasPrice"]), toInt(_o["gasLimit"]), importData(_o), toInt(_o["nonce"]), Secret(_o["secretKey"].get_str())) : Transaction(toInt(_o["value"]), toInt(_o["gasPrice"]), toInt(_o["gasLimit"]), importData(_o), toInt(_o["nonce"]), Secret(_o["secretKey"].get_str())) :
@ -285,9 +285,9 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta
#define CHECK(a,b) \ #define CHECK(a,b) \
{ \ { \
if (_throw == WhenError::Throw) \ if (_throw == WhenError::Throw) \
BOOST_CHECK_MESSAGE(a,b); \ {TBOOST_CHECK_MESSAGE(a,b);}\
else \ else \
BOOST_WARN_MESSAGE(a,b); \ {TBOOST_WARN_MESSAGE(a,b);} \
} }
for (auto const& a: _stateExpect.addresses()) for (auto const& a: _stateExpect.addresses())
@ -304,35 +304,35 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta
} }
catch(std::out_of_range const&) catch(std::out_of_range const&)
{ {
BOOST_ERROR("expectedStateOptions map does not match expectedState in checkExpectedState!"); TBOOST_ERROR("expectedStateOptions map does not match expectedState in checkExpectedState!");
break; break;
} }
} }
if (addressOptions.m_bHasBalance) if (addressOptions.m_bHasBalance)
CHECK(_stateExpect.balance(a.first) == _statePost.balance(a.first), CHECK((_stateExpect.balance(a.first) == _statePost.balance(a.first)),
"Check State: " << a.first << ": incorrect balance " << _statePost.balance(a.first) << ", expected " << _stateExpect.balance(a.first)); "Check State: " << a.first << ": incorrect balance " << _statePost.balance(a.first) << ", expected " << _stateExpect.balance(a.first));
if (addressOptions.m_bHasNonce) if (addressOptions.m_bHasNonce)
CHECK(_stateExpect.transactionsFrom(a.first) == _statePost.transactionsFrom(a.first), CHECK((_stateExpect.transactionsFrom(a.first) == _statePost.transactionsFrom(a.first)),
"Check State: " << a.first << ": incorrect nonce " << _statePost.transactionsFrom(a.first) << ", expected " << _stateExpect.transactionsFrom(a.first)); "Check State: " << a.first << ": incorrect nonce " << _statePost.transactionsFrom(a.first) << ", expected " << _stateExpect.transactionsFrom(a.first));
if (addressOptions.m_bHasStorage) if (addressOptions.m_bHasStorage)
{ {
unordered_map<u256, u256> stateStorage = _statePost.storage(a.first); unordered_map<u256, u256> stateStorage = _statePost.storage(a.first);
for (auto const& s: _stateExpect.storage(a.first)) for (auto const& s: _stateExpect.storage(a.first))
CHECK(stateStorage[s.first] == s.second, CHECK((stateStorage[s.first] == s.second),
"Check State: " << a.first << ": incorrect storage [" << s.first << "] = " << toHex(stateStorage[s.first]) << ", expected [" << s.first << "] = " << toHex(s.second)); "Check State: " << a.first << ": incorrect storage [" << s.first << "] = " << toHex(stateStorage[s.first]) << ", expected [" << s.first << "] = " << toHex(s.second));
//Check for unexpected storage values //Check for unexpected storage values
stateStorage = _stateExpect.storage(a.first); stateStorage = _stateExpect.storage(a.first);
for (auto const& s: _statePost.storage(a.first)) for (auto const& s: _statePost.storage(a.first))
CHECK(stateStorage[s.first] == s.second, CHECK((stateStorage[s.first] == s.second),
"Check State: " << a.first << ": incorrect storage [" << s.first << "] = " << toHex(s.second) << ", expected [" << s.first << "] = " << toHex(stateStorage[s.first])); "Check State: " << a.first << ": incorrect storage [" << s.first << "] = " << toHex(s.second) << ", expected [" << s.first << "] = " << toHex(stateStorage[s.first]));
} }
if (addressOptions.m_bHasCode) if (addressOptions.m_bHasCode)
CHECK(_stateExpect.code(a.first) == _statePost.code(a.first), CHECK((_stateExpect.code(a.first) == _statePost.code(a.first)),
"Check State: " << a.first << ": incorrect code '" << toHex(_statePost.code(a.first)) << "', expected '" << toHex(_stateExpect.code(a.first)) << "'"); "Check State: " << a.first << ": incorrect code '" << toHex(_statePost.code(a.first)) << "', expected '" << toHex(_stateExpect.code(a.first)) << "'");
} }
} }
@ -518,18 +518,17 @@ void checkOutput(bytes const& _output, json_spirit::mObject& _o)
int j = 0; int j = 0;
if (_o["out"].get_str().find("#") == 0) if (_o["out"].get_str().find("#") == 0)
BOOST_CHECK((u256)_output.size() == toInt(_o["out"].get_str().substr(1))); {TBOOST_CHECK(((u256)_output.size() == toInt(_o["out"].get_str().substr(1))));}
else if (_o["out"].type() == json_spirit::array_type) else if (_o["out"].type() == json_spirit::array_type)
for (auto const& d: _o["out"].get_array()) for (auto const& d: _o["out"].get_array())
{ {
BOOST_CHECK_MESSAGE(_output[j] == toInt(d), "Output byte [" << j << "] different!"); TBOOST_CHECK_MESSAGE((_output[j] == toInt(d)), "Output byte [" << j << "] different!");
++j; ++j;
} }
else if (_o["out"].get_str().find("0x") == 0) else if (_o["out"].get_str().find("0x") == 0)
BOOST_CHECK(_output == fromHex(_o["out"].get_str().substr(2))); {TBOOST_CHECK((_output == fromHex(_o["out"].get_str().substr(2))));}
else else
BOOST_CHECK(_output == fromHex(_o["out"].get_str())); TBOOST_CHECK((_output == fromHex(_o["out"].get_str())));
} }
void checkStorage(map<u256, u256> _expectedStore, map<u256, u256> _resultStore, Address _expectedAddr) void checkStorage(map<u256, u256> _expectedStore, map<u256, u256> _resultStore, Address _expectedAddr)
@ -557,13 +556,13 @@ void checkStorage(map<u256, u256> _expectedStore, map<u256, u256> _resultStore,
void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs) void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs)
{ {
BOOST_REQUIRE_EQUAL(_resultLogs.size(), _expectedLogs.size()); TBOOST_REQUIRE_EQUAL(_resultLogs.size(), _expectedLogs.size());
for (size_t i = 0; i < _resultLogs.size(); ++i) for (size_t i = 0; i < _resultLogs.size(); ++i)
{ {
BOOST_CHECK_EQUAL(_resultLogs[i].address, _expectedLogs[i].address); TBOOST_CHECK_EQUAL(_resultLogs[i].address, _expectedLogs[i].address);
BOOST_CHECK_EQUAL(_resultLogs[i].topics, _expectedLogs[i].topics); TBOOST_CHECK_EQUAL(_resultLogs[i].topics, _expectedLogs[i].topics);
BOOST_CHECK(_resultLogs[i].data == _expectedLogs[i].data); TBOOST_CHECK((_resultLogs[i].data == _expectedLogs[i].data));
} }
} }

30
test/TestHelper.h

@ -31,6 +31,26 @@
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>
#include <libtestutils/Common.h> #include <libtestutils/Common.h>
#ifdef NOBOOST
#define TBOOST_THROW_EXCEPTION(arg) throw dev::Exception();
#define TBOOST_REQUIRE(arg) if(arg == false) throw dev::Exception();
#define TBOOST_REQUIRE_EQUAL(arg1, arg2) if(arg1 != arg2) throw dev::Exception();
#define TBOOST_CHECK_EQUAL(arg1, arg2) if(arg1 != arg2) throw dev::Exception();
#define TBOOST_CHECK(arg) if(arg == false) throw dev::Exception();
#define TBOOST_CHECK_MESSAGE(arg1, arg2) if(arg1 == false) throw dev::Exception();
#define TBOOST_WARN_MESSAGE(arg1, arg2) throw dev::Exception();
#define TBOOST_ERROR(arg) throw dev::Exception();
#else
#define TBOOST_THROW_EXCEPTION(arg) BOOST_THROW_EXCEPTION(arg)
#define TBOOST_REQUIRE(arg) BOOST_REQUIRE(arg)
#define TBOOST_REQUIRE_EQUAL(arg1, arg2) BOOST_REQUIRE_EQUAL(arg1, arg2)
#define TBOOST_CHECK(arg) BOOST_CHECK(arg)
#define TBOOST_CHECK_EQUAL(arg1, arg2) BOOST_CHECK_EQUAL(arg1, arg2)
#define TBOOST_CHECK_MESSAGE(arg1, arg2) BOOST_CHECK_MESSAGE(arg1, arg2)
#define TBOOST_WARN_MESSAGE(arg1, arg2) BOOST_WARN_MESSAGE(arg1, arg2)
#define TBOOST_ERROR(arg) BOOST_ERROR(arg)
#endif
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -163,6 +183,12 @@ eth::LastHashes lastHashes(u256 _currentBlockNumber);
json_spirit::mObject fillJsonWithState(eth::State _state); json_spirit::mObject fillJsonWithState(eth::State _state);
json_spirit::mObject fillJsonWithTransaction(eth::Transaction _txn); json_spirit::mObject fillJsonWithTransaction(eth::Transaction _txn);
//Fill Test Functions
void doTransactionTests(json_spirit::mValue& _v, bool _fillin);
void doStateTests(json_spirit::mValue& v, bool _fillin);
void doVMTests(json_spirit::mValue& v, bool _fillin);
void doBlockchainTests(json_spirit::mValue& _v, bool _fillin);
template<typename mapType> template<typename mapType>
void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)
{ {
@ -171,9 +197,9 @@ void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)
auto& resultAddr = resultPair.first; auto& resultAddr = resultPair.first;
auto expectedAddrIt = _expectedAddrs.find(resultAddr); auto expectedAddrIt = _expectedAddrs.find(resultAddr);
if (expectedAddrIt == _expectedAddrs.end()) if (expectedAddrIt == _expectedAddrs.end())
BOOST_ERROR("Missing result address " << resultAddr); TBOOST_ERROR("Missing result address " << resultAddr);
} }
BOOST_CHECK(_expectedAddrs == _resultAddrs); TBOOST_CHECK((_expectedAddrs == _resultAddrs));
} }
class Options class Options

6
test/fuzzTesting/CMakeLists.txt

@ -8,6 +8,8 @@ include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp" "../libethereum/state.cpp" "../libevm/vm.cpp" "../libethereum/blockchain.cpp")
add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp")
add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp") add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp")
add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp" ) add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp" )
@ -32,3 +34,7 @@ target_link_libraries(checkRandomStateTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES
target_link_libraries(checkRandomStateTest ethereum) target_link_libraries(checkRandomStateTest ethereum)
target_link_libraries(checkRandomStateTest ethcore) target_link_libraries(checkRandomStateTest ethcore)
target_link_libraries(checkRandomStateTest testutils) target_link_libraries(checkRandomStateTest testutils)
target_link_libraries(createRandomTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomTest ethereum)
target_link_libraries(createRandomTest ethcore)
target_link_libraries(createRandomTest testutils)

397
test/fuzzTesting/createRandomTest.cpp

@ -0,0 +1,397 @@
/*
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 createRandomTest.cpp
* @author Dimitry Khokhlov <winsvega@mail.ru>
* @date 2015
*/
///This file require #define DONTUSE_BOOST_MACROS compile flag to run!
#include <string>
#include <iostream>
#include <test/TestHelper.h>
#include <test/fuzzTesting/fuzzHelper.h>
#include <libevm/VMFactory.h>
#include <libdevcore/Common.h>
//String Variables
extern std::string const c_testExampleStateTest;
extern std::string const c_testExampleTransactionTest;
extern std::string const c_testExampleVMTest;
extern std::string const c_testExampleBlockchainTest;
//Main Test functinos
void fillRandomTest(std::function<void(json_spirit::mValue&, bool)> doTests, std::string const& testString);
int checkRandomTest(std::function<void(json_spirit::mValue&, bool)> doTests, json_spirit::mValue& value);
//Helper Functions
std::vector<std::string> getTypes();
void parseTestWithTypes(std::string& test);
int main(int argc, char *argv[])
{
std::string testSuite;
json_spirit::mValue testmValue;
bool checktest = false;
for (auto i = 0; i < argc; ++i)
{
auto arg = std::string{argv[i]};
dev::test::Options& options = const_cast<dev::test::Options&>(dev::test::Options::get());
if (arg == "--fulloutput")
options.fulloutput = true;
else
if (arg == "-t" && i + 1 < argc)
{
testSuite = argv[i + 1];
if (testSuite != "BlockChainTests" && testSuite != "TransactionTests" && testSuite != "StateTests" && testSuite != "VMTests")
testSuite = "";
}
else
if (arg == "-checktest" && i + 1 < argc)
{
std::string s;
for (int j = i+1; j < argc; ++j)
s += argv[j];
if (asserts(s.length() > 0))
{
std::cout << "Error! Content of argument is empty! (Usage -checktest textstream) \n";
return 1;
}
read_string(s, testmValue);
checktest = true;
}
}
if (testSuite == "")
{
std::cout << "Error! Test suite not supported! (Usage -t TestSuite)";
return 1;
}
else
if (testSuite == "BlockChainTests")
{
if (checktest)
return checkRandomTest(dev::test::doBlockchainTests, testmValue);
else
fillRandomTest(dev::test::doBlockchainTests, c_testExampleBlockchainTest);
}
else
if (testSuite == "TransactionTests")
{
if (checktest)
return checkRandomTest(dev::test::doTransactionTests, testmValue);
else
fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest);
}
else
if (testSuite == "StateTests")
{
if (checktest)
return checkRandomTest(dev::test::doStateTests, testmValue);
else
fillRandomTest(dev::test::doStateTests, c_testExampleStateTest);
}
else
if (testSuite == "VMTests")
{
if (checktest)
{
dev::eth::VMFactory::setKind(dev::eth::VMKind::JIT);
return checkRandomTest(dev::test::doVMTests, testmValue);
}
else
fillRandomTest(dev::test::doVMTests, c_testExampleVMTest);
}
return 0;
}
int checkRandomTest(std::function<void(json_spirit::mValue&, bool)> doTests, json_spirit::mValue& value)
{
bool ret = 0;
try
{
//redirect all output to the stream
std::ostringstream strCout;
std::streambuf* oldCoutStreamBuf = std::cout.rdbuf();
std::cout.rdbuf( strCout.rdbuf() );
std::cerr.rdbuf( strCout.rdbuf() );
doTests(value, false);
//restroe output
std::cout.rdbuf(oldCoutStreamBuf);
std::cerr.rdbuf(oldCoutStreamBuf);
}
catch (dev::Exception const& _e)
{
std::cout << "Failed test with Exception: " << diagnostic_information(_e) << std::endl;
ret = 1;
}
catch (std::exception const& _e)
{
std::cout << "Failed test with Exception: " << _e.what() << std::endl;
ret = 1;
}
return ret;
}
void fillRandomTest(std::function<void(json_spirit::mValue&, bool)> doTests, std::string const& testString)
{
//redirect all output to the stream
std::ostringstream strCout;
std::streambuf* oldCoutStreamBuf = std::cout.rdbuf();
std::cout.rdbuf( strCout.rdbuf() );
std::cerr.rdbuf( strCout.rdbuf() );
json_spirit::mValue v;
try
{
std::string newTest = testString;
parseTestWithTypes(newTest);
json_spirit::read_string(newTest, v);
doTests(v, true);
}
catch(...)
{
std::cerr << "Test fill exception!";
}
//restroe output
std::cout.rdbuf(oldCoutStreamBuf);
std::cerr.rdbuf(oldCoutStreamBuf);
std::cout << json_spirit::write_string(v, true);
}
/// Parse Test string replacing keywords to fuzzed values
void parseTestWithTypes(std::string& _test)
{
dev::test::RandomCodeOptions options;
options.setWeight(dev::eth::Instruction::STOP, 10); //default 50
options.setWeight(dev::eth::Instruction::SSTORE, 70);
options.setWeight(dev::eth::Instruction::CALL, 75);
options.addAddress(dev::Address("0xffffffffffffffffffffffffffffffffffffffff"));
options.addAddress(dev::Address("0x1000000000000000000000000000000000000000"));
options.addAddress(dev::Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87"));
options.addAddress(dev::Address("0x945304eb96065b2a98b57a48a06ae28d285a71b5"));
options.addAddress(dev::Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"));
options.addAddress(dev::Address("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"));
options.addAddress(dev::Address("0x0000000000000000000000000000000000000001"));
options.addAddress(dev::Address("0x0000000000000000000000000000000000000002"));
options.addAddress(dev::Address("0x0000000000000000000000000000000000000003"));
options.addAddress(dev::Address("0x0000000000000000000000000000000000000004"));
options.smartCodeProbability = 35;
std::vector<std::string> types = getTypes();
for (unsigned i = 0; i < types.size(); i++)
{
std::size_t pos = _test.find(types.at(i));
while (pos != std::string::npos)
{
if (types.at(i) == "[CODE]")
_test.replace(pos, 6, "0x"+dev::test::RandomCode::generate(10, options));
else
if (types.at(i) == "[HEX]")
_test.replace(pos, 5, dev::test::RandomCode::randomUniIntHex());
else
if (types.at(i) == "[GASLIMIT]")
_test.replace(pos, 10, dev::test::RandomCode::randomUniIntHex(dev::u256("3000000000")));
else
if (types.at(i) == "[HASH20]")
_test.replace(pos, 8, dev::test::RandomCode::rndByteSequence(20));
else
if (types.at(i) == "[0xHASH32]")
_test.replace(pos, 10, "0x" + dev::test::RandomCode::rndByteSequence(32));
else
if (types.at(i) == "[HASH32]")
_test.replace(pos, 8, dev::test::RandomCode::rndByteSequence(32));
else
if (types.at(i) == "[V]")
{
int random = dev::test::RandomCode::randomUniInt() % 100;
if (random < 30)
_test.replace(pos, 3, "0x1c");
else
if (random < 60)
_test.replace(pos, 3, "0x1d");
else
_test.replace(pos, 3, "0x" + dev::test::RandomCode::rndByteSequence(1));
}
pos = _test.find(types.at(i));
}
}
}
std::vector<std::string> getTypes()
{
return {"[CODE]", "[HEX]", "[HASH20]", "[HASH32]", "[0xHASH32]", "[V]", "[GASLIMIT]"};
}
std::string const c_testExampleTransactionTest = R"(
{
"randomTransactionTest" : {
"transaction" :
{
"data" : "[CODE]",
"gasLimit" : "[HEX]",
"gasPrice" : "[HEX]",
"nonce" : "[HEX]",
"to" : "[HASH20]",
"value" : "[HEX]",
"v" : "[V]",
"r" : "[0xHASH32]",
"s" : "[0xHASH32]"
}
}
}
)";
std::string const c_testExampleStateTest = R"(
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "[HASH20]",
"currentDifficulty" : "[HEX]",
"currentGasLimit" : "[GASLIMIT]",
"currentNumber" : "[HEX]",
"currentTimestamp" : "[HEX]",
"previousHash" : "[HASH32]"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "[HEX]",
"code" : "[CODE]",
"nonce" : "[V]",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "[HEX]",
"code" : "[CODE]",
"nonce" : "[V]",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "[HEX]",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "[CODE]",
"gasLimit" : "[HEX]",
"gasPrice" : "[V]",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "[HEX]"
}
}
}
)";
std::string const c_testExampleVMTest = R"(
{
"randomVMTest": {
"env" : {
"previousHash" : "[HASH32]",
"currentNumber" : "[HEX]",
"currentGasLimit" : "[GASLIMIT]",
"currentDifficulty" : "[HEX]",
"currentTimestamp" : "[HEX]",
"currentCoinbase" : "[HASH20]"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "[HEX]",
"nonce" : "[HEX]",
"code" : "[CODE]",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "[HASH20]",
"caller" : "[HASH20]",
"value" : "[HEX]",
"data" : "[CODE]",
"gasPrice" : "[V]",
"gas" : "[HEX]"
}
}
}
)";
std::string const c_testExampleBlockchainTest = R"(
{
"randomBlockTest" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "[HASH20]",
"difficulty" : "131072",
"extraData" : "[CODE]",
"gasLimit" : "3141592",
"gasUsed" : "0",
"mixHash" : "[0xHASH32]",
"nonce" : "0x0102030405060708",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "[0xHASH32]",
"stateRoot" : "[0xHASH32]",
"timestamp" : "[HEX]",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "[HEX]",
"nonce" : "0",
"code" : "",
"storage": {}
},
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "[HEX]",
"nonce" : "0",
"code" : "[CODE]",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "[CODE]",
"gasLimit" : "[HEX]",
"gasPrice" : "[V]",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "[V]"
}
],
"uncleHeaders" : [
]
}
]
}
}
)";

13
test/fuzzTesting/fuzzHelper.cpp

@ -35,10 +35,12 @@ boost::random::mt19937 RandomCode::gen;
boostIntDistrib RandomCode::opCodeDist = boostIntDistrib (0, 255); boostIntDistrib RandomCode::opCodeDist = boostIntDistrib (0, 255);
boostIntDistrib RandomCode::opLengDist = boostIntDistrib (1, 32); boostIntDistrib RandomCode::opLengDist = boostIntDistrib (1, 32);
boostIntDistrib RandomCode::uniIntDist = boostIntDistrib (0, 0x7fffffff); boostIntDistrib RandomCode::uniIntDist = boostIntDistrib (0, 0x7fffffff);
boostUint64Distrib RandomCode::uInt64Dist = boostUint64Distrib (0, std::numeric_limits<uint64_t>::max());
boostIntGenerator RandomCode::randOpCodeGen = boostIntGenerator(gen, opCodeDist); boostIntGenerator RandomCode::randOpCodeGen = boostIntGenerator(gen, opCodeDist);
boostIntGenerator RandomCode::randOpLengGen = boostIntGenerator(gen, opLengDist); boostIntGenerator RandomCode::randOpLengGen = boostIntGenerator(gen, opLengDist);
boostIntGenerator RandomCode::randUniIntGen = boostIntGenerator(gen, uniIntDist); boostIntGenerator RandomCode::randUniIntGen = boostIntGenerator(gen, uniIntDist);
boostUInt64Generator RandomCode::randUInt64Gen = boostUInt64Generator(gen, uInt64Dist);
std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType) std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType)
{ {
@ -48,7 +50,7 @@ std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType)
for (auto i = 0; i < _length; i++) for (auto i = 0; i < _length; i++)
{ {
uint8_t byte = randOpCodeGen(); uint8_t byte = randOpCodeGen();
hash += toCompactHex(byte); hash += toCompactHex(byte, HexPrefix::DontAdd, 1);
} }
return hash; return hash;
} }
@ -89,10 +91,15 @@ std::string RandomCode::generate(int _maxOpNumber, RandomCodeOptions _options)
return code; return code;
} }
std::string RandomCode::randomUniIntHex() std::string RandomCode::randomUniIntHex(u256 _maxVal)
{ {
if (_maxVal == 0)
_maxVal = std::numeric_limits<uint64_t>::max();
refreshSeed(); refreshSeed();
return "0x" + toCompactHex((int)randUniIntGen()); int rand = randUniIntGen() % 100;
if (rand < 50)
return "0x" + toCompactHex((u256)randUniIntGen() % _maxVal);
return "0x" + toCompactHex((u256)randUInt64Gen() % _maxVal);
} }
int RandomCode::randomUniInt() int RandomCode::randomUniInt()

8
test/fuzzTesting/fuzzHelper.h

@ -37,9 +37,11 @@ namespace test
typedef boost::random::uniform_int_distribution<> boostIntDistrib; typedef boost::random::uniform_int_distribution<> boostIntDistrib;
typedef boost::random::discrete_distribution<> boostDescreteDistrib; typedef boost::random::discrete_distribution<> boostDescreteDistrib;
typedef boost::uniform_int<uint64_t> boostUint64Distrib;
typedef boost::random::variate_generator<boost::mt19937&, boostIntDistrib > boostIntGenerator; typedef boost::random::variate_generator<boost::mt19937&, boostIntDistrib > boostIntGenerator;
typedef boost::random::variate_generator<boost::mt19937&, boostDescreteDistrib > boostWeightGenerator; typedef boost::random::variate_generator<boost::mt19937&, boostDescreteDistrib > boostWeightGenerator;
typedef boost::random::variate_generator<boost::mt19937&, boostUint64Distrib > boostUInt64Generator;
struct RandomCodeOptions struct RandomCodeOptions
{ {
@ -73,8 +75,8 @@ public:
/// Generate random byte string of a given length /// Generate random byte string of a given length
static std::string rndByteSequence(int _length = 1, SizeStrictness _sizeType = SizeStrictness::Strict); static std::string rndByteSequence(int _length = 1, SizeStrictness _sizeType = SizeStrictness::Strict);
/// Generate random uniForm Int with reasonable value 0..0x7fffffff /// Generate random int64
static std::string randomUniIntHex(); static std::string randomUniIntHex(u256 _maxVal = 0);
static int randomUniInt(); static int randomUniInt();
private: private:
@ -87,10 +89,12 @@ private:
static boostIntDistrib opCodeDist; ///< 0..255 opcodes static boostIntDistrib opCodeDist; ///< 0..255 opcodes
static boostIntDistrib opLengDist; ///< 1..32 byte string static boostIntDistrib opLengDist; ///< 1..32 byte string
static boostIntDistrib uniIntDist; ///< 0..0x7fffffff static boostIntDistrib uniIntDist; ///< 0..0x7fffffff
static boostUint64Distrib uInt64Dist; ///< 0..2**64
static boostIntGenerator randUniIntGen; ///< Generate random UniformInt from uniIntDist static boostIntGenerator randUniIntGen; ///< Generate random UniformInt from uniIntDist
static boostIntGenerator randOpCodeGen; ///< Generate random value from opCodeDist static boostIntGenerator randOpCodeGen; ///< Generate random value from opCodeDist
static boostIntGenerator randOpLengGen; ///< Generate random length from opLengDist static boostIntGenerator randOpLengGen; ///< Generate random length from opLengDist
static boostUInt64Generator randUInt64Gen; ///< Generate random uInt64
}; };
} }

128
test/libethereum/blockchain.cpp

@ -59,10 +59,10 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
} }
cerr << i.first << endl; cerr << i.first << endl;
BOOST_REQUIRE(o.count("genesisBlockHeader")); TBOOST_REQUIRE(o.count("genesisBlockHeader"));
BlockInfo biGenesisBlock = constructBlock(o["genesisBlockHeader"].get_obj()); BlockInfo biGenesisBlock = constructBlock(o["genesisBlockHeader"].get_obj());
BOOST_REQUIRE(o.count("pre")); TBOOST_REQUIRE(o.count("pre"));
ImportTest importer(o["pre"].get_obj()); ImportTest importer(o["pre"].get_obj());
TransientDirectory td_stateDB_tmp; TransientDirectory td_stateDB_tmp;
State trueState(OverlayDB(State::openDB(td_stateDB_tmp.path())), BaseState::Empty, biGenesisBlock.coinbaseAddress); State trueState(OverlayDB(State::openDB(td_stateDB_tmp.path())), BaseState::Empty, biGenesisBlock.coinbaseAddress);
@ -77,7 +77,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
if (_fillin) if (_fillin)
biGenesisBlock.stateRoot = trueState.rootHash(); biGenesisBlock.stateRoot = trueState.rootHash();
else else
BOOST_CHECK_MESSAGE(biGenesisBlock.stateRoot == trueState.rootHash(), "root hash does not match"); TBOOST_CHECK_MESSAGE((biGenesisBlock.stateRoot == trueState.rootHash()), "root hash does not match");
if (_fillin) if (_fillin)
{ {
@ -99,7 +99,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
if (_fillin) if (_fillin)
{ {
BOOST_REQUIRE(o.count("blocks")); TBOOST_REQUIRE(o.count("blocks"));
mArray blArray; mArray blArray;
blockSet genesis; blockSet genesis;
@ -145,7 +145,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
// get txs // get txs
TransactionQueue txs; TransactionQueue txs;
ZeroGasPricer gp; ZeroGasPricer gp;
BOOST_REQUIRE(blObj.count("transactions")); TBOOST_REQUIRE(blObj.count("transactions"));
for (auto const& txObj: blObj["transactions"].get_array()) for (auto const& txObj: blObj["transactions"].get_array())
{ {
mObject tx = txObj.get_obj(); mObject tx = txObj.get_obj();
@ -337,29 +337,29 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
catch (Exception const& _e) catch (Exception const& _e)
{ {
cnote << "state sync or block import did throw an exception: " << diagnostic_information(_e); cnote << "state sync or block import did throw an exception: " << diagnostic_information(_e);
BOOST_CHECK(blObj.count("blockHeader") == 0); TBOOST_CHECK((blObj.count("blockHeader") == 0));
BOOST_CHECK(blObj.count("transactions") == 0); TBOOST_CHECK((blObj.count("transactions") == 0));
BOOST_CHECK(blObj.count("uncleHeaders") == 0); TBOOST_CHECK((blObj.count("uncleHeaders") == 0));
continue; continue;
} }
catch (std::exception const& _e) catch (std::exception const& _e)
{ {
cnote << "state sync or block import did throw an exception: " << _e.what(); cnote << "state sync or block import did throw an exception: " << _e.what();
BOOST_CHECK(blObj.count("blockHeader") == 0); TBOOST_CHECK((blObj.count("blockHeader") == 0));
BOOST_CHECK(blObj.count("transactions") == 0); TBOOST_CHECK((blObj.count("transactions") == 0));
BOOST_CHECK(blObj.count("uncleHeaders") == 0); TBOOST_CHECK((blObj.count("uncleHeaders") == 0));
continue; continue;
} }
catch (...) catch (...)
{ {
cnote << "state sync or block import did throw an exception\n"; cnote << "state sync or block import did throw an exception\n";
BOOST_CHECK(blObj.count("blockHeader") == 0); TBOOST_CHECK((blObj.count("blockHeader") == 0));
BOOST_CHECK(blObj.count("transactions") == 0); TBOOST_CHECK((blObj.count("transactions") == 0));
BOOST_CHECK(blObj.count("uncleHeaders") == 0); TBOOST_CHECK((blObj.count("uncleHeaders") == 0));
continue; continue;
} }
BOOST_REQUIRE(blObj.count("blockHeader")); TBOOST_REQUIRE(blObj.count("blockHeader"));
mObject tObj = blObj["blockHeader"].get_obj(); mObject tObj = blObj["blockHeader"].get_obj();
BlockInfo blockHeaderFromFields; BlockInfo blockHeaderFromFields;
@ -372,24 +372,24 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
if (importedAndBest) if (importedAndBest)
{ {
//Check the fields restored from RLP to original fields //Check the fields restored from RLP to original fields
BOOST_CHECK_MESSAGE(blockHeaderFromFields.headerHash(WithNonce) == blockFromRlp.headerHash(WithNonce), "hash in given RLP not matching the block hash!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.headerHash(WithNonce) == blockFromRlp.headerHash(WithNonce)), "hash in given RLP not matching the block hash!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.parentHash == blockFromRlp.parentHash, "parentHash in given RLP not matching the block parentHash!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.parentHash == blockFromRlp.parentHash), "parentHash in given RLP not matching the block parentHash!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.sha3Uncles == blockFromRlp.sha3Uncles, "sha3Uncles in given RLP not matching the block sha3Uncles!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.sha3Uncles == blockFromRlp.sha3Uncles), "sha3Uncles in given RLP not matching the block sha3Uncles!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.coinbaseAddress == blockFromRlp.coinbaseAddress,"coinbaseAddress in given RLP not matching the block coinbaseAddress!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.coinbaseAddress == blockFromRlp.coinbaseAddress),"coinbaseAddress in given RLP not matching the block coinbaseAddress!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.stateRoot == blockFromRlp.stateRoot, "stateRoot in given RLP not matching the block stateRoot!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.stateRoot == blockFromRlp.stateRoot), "stateRoot in given RLP not matching the block stateRoot!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.transactionsRoot == blockFromRlp.transactionsRoot, "transactionsRoot in given RLP not matching the block transactionsRoot!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.transactionsRoot == blockFromRlp.transactionsRoot), "transactionsRoot in given RLP not matching the block transactionsRoot!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.receiptsRoot == blockFromRlp.receiptsRoot, "receiptsRoot in given RLP not matching the block receiptsRoot!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.receiptsRoot == blockFromRlp.receiptsRoot), "receiptsRoot in given RLP not matching the block receiptsRoot!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.logBloom == blockFromRlp.logBloom, "logBloom in given RLP not matching the block logBloom!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.logBloom == blockFromRlp.logBloom), "logBloom in given RLP not matching the block logBloom!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.difficulty == blockFromRlp.difficulty, "difficulty in given RLP not matching the block difficulty!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.difficulty == blockFromRlp.difficulty), "difficulty in given RLP not matching the block difficulty!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.number == blockFromRlp.number, "number in given RLP not matching the block number!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.number == blockFromRlp.number), "number in given RLP not matching the block number!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.gasLimit == blockFromRlp.gasLimit,"gasLimit in given RLP not matching the block gasLimit!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.gasLimit == blockFromRlp.gasLimit),"gasLimit in given RLP not matching the block gasLimit!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.gasUsed == blockFromRlp.gasUsed, "gasUsed in given RLP not matching the block gasUsed!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.gasUsed == blockFromRlp.gasUsed), "gasUsed in given RLP not matching the block gasUsed!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.timestamp == blockFromRlp.timestamp, "timestamp in given RLP not matching the block timestamp!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.timestamp == blockFromRlp.timestamp), "timestamp in given RLP not matching the block timestamp!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.extraData == blockFromRlp.extraData, "extraData in given RLP not matching the block extraData!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.extraData == blockFromRlp.extraData), "extraData in given RLP not matching the block extraData!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.mixHash == blockFromRlp.mixHash, "mixHash in given RLP not matching the block mixHash!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.mixHash == blockFromRlp.mixHash), "mixHash in given RLP not matching the block mixHash!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields.nonce == blockFromRlp.nonce, "nonce in given RLP not matching the block nonce!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields.nonce == blockFromRlp.nonce), "nonce in given RLP not matching the block nonce!");
BOOST_CHECK_MESSAGE(blockHeaderFromFields == blockFromRlp, "However, blockHeaderFromFields != blockFromRlp!"); TBOOST_CHECK_MESSAGE((blockHeaderFromFields == blockFromRlp), "However, blockHeaderFromFields != blockFromRlp!");
//Check transaction list //Check transaction list
@ -399,15 +399,15 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
{ {
mObject tx = txObj.get_obj(); mObject tx = txObj.get_obj();
BOOST_REQUIRE(tx.count("nonce")); TBOOST_REQUIRE(tx.count("nonce"));
BOOST_REQUIRE(tx.count("gasPrice")); TBOOST_REQUIRE(tx.count("gasPrice"));
BOOST_REQUIRE(tx.count("gasLimit")); TBOOST_REQUIRE(tx.count("gasLimit"));
BOOST_REQUIRE(tx.count("to")); TBOOST_REQUIRE(tx.count("to"));
BOOST_REQUIRE(tx.count("value")); TBOOST_REQUIRE(tx.count("value"));
BOOST_REQUIRE(tx.count("v")); TBOOST_REQUIRE(tx.count("v"));
BOOST_REQUIRE(tx.count("r")); TBOOST_REQUIRE(tx.count("r"));
BOOST_REQUIRE(tx.count("s")); TBOOST_REQUIRE(tx.count("s"));
BOOST_REQUIRE(tx.count("data")); TBOOST_REQUIRE(tx.count("data"));
try try
{ {
@ -416,7 +416,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {
BOOST_ERROR("Failed transaction constructor with Exception: " << diagnostic_information(_e)); TBOOST_ERROR("Failed transaction constructor with Exception: " << diagnostic_information(_e));
} }
catch (exception const& _e) catch (exception const& _e)
{ {
@ -432,22 +432,22 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
txsFromRlp.push_back(tx); txsFromRlp.push_back(tx);
} }
BOOST_CHECK_MESSAGE(txsFromRlp.size() == txsFromField.size(), "transaction list size does not match"); TBOOST_CHECK_MESSAGE((txsFromRlp.size() == txsFromField.size()), "transaction list size does not match");
for (size_t i = 0; i < txsFromField.size(); ++i) for (size_t i = 0; i < txsFromField.size(); ++i)
{ {
BOOST_CHECK_MESSAGE(txsFromField[i].data() == txsFromRlp[i].data(), "transaction data in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].data() == txsFromRlp[i].data()), "transaction data in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].gas() == txsFromRlp[i].gas(), "transaction gasLimit in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].gas() == txsFromRlp[i].gas()), "transaction gasLimit in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].gasPrice() == txsFromRlp[i].gasPrice(), "transaction gasPrice in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].gasPrice() == txsFromRlp[i].gasPrice()), "transaction gasPrice in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].nonce() == txsFromRlp[i].nonce(), "transaction nonce in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].nonce() == txsFromRlp[i].nonce()), "transaction nonce in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].signature().r == txsFromRlp[i].signature().r, "transaction r in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].signature().r == txsFromRlp[i].signature().r), "transaction r in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].signature().s == txsFromRlp[i].signature().s, "transaction s in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].signature().s == txsFromRlp[i].signature().s), "transaction s in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].signature().v == txsFromRlp[i].signature().v, "transaction v in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].signature().v == txsFromRlp[i].signature().v), "transaction v in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].receiveAddress() == txsFromRlp[i].receiveAddress(), "transaction receiveAddress in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].receiveAddress() == txsFromRlp[i].receiveAddress()), "transaction receiveAddress in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].value() == txsFromRlp[i].value(), "transaction receiveAddress in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].value() == txsFromRlp[i].value()), "transaction receiveAddress in rlp and in field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i] == txsFromRlp[i], "transactions from rlp and transaction from field do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i] == txsFromRlp[i]), "transactions from rlp and transaction from field do not match");
BOOST_CHECK_MESSAGE(txsFromField[i].rlp() == txsFromRlp[i].rlp(), "transactions rlp do not match"); TBOOST_CHECK_MESSAGE((txsFromField[i].rlp() == txsFromRlp[i].rlp()), "transactions rlp do not match");
} }
// check uncle list // check uncle list
@ -458,7 +458,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
for (auto const& uBlHeaderObj: blObj["uncleHeaders"].get_array()) for (auto const& uBlHeaderObj: blObj["uncleHeaders"].get_array())
{ {
mObject uBlH = uBlHeaderObj.get_obj(); mObject uBlH = uBlHeaderObj.get_obj();
BOOST_REQUIRE(uBlH.size() == 16); TBOOST_REQUIRE((uBlH.size() == 16));
bytes uncleRLP = createBlockRLPFromFields(uBlH); bytes uncleRLP = createBlockRLPFromFields(uBlH);
const RLP c_uRLP(uncleRLP); const RLP c_uRLP(uncleRLP);
BlockInfo uncleBlockHeader; BlockInfo uncleBlockHeader;
@ -468,7 +468,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
} }
catch(...) catch(...)
{ {
BOOST_ERROR("invalid uncle header"); TBOOST_ERROR("invalid uncle header");
} }
uBlHsFromField.push_back(uncleBlockHeader); uBlHsFromField.push_back(uncleBlockHeader);
} }
@ -482,15 +482,15 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
uBlHsFromRlp.push_back(uBl); uBlHsFromRlp.push_back(uBl);
} }
BOOST_REQUIRE_EQUAL(uBlHsFromField.size(), uBlHsFromRlp.size()); TBOOST_REQUIRE_EQUAL(uBlHsFromField.size(), uBlHsFromRlp.size());
for (size_t i = 0; i < uBlHsFromField.size(); ++i) for (size_t i = 0; i < uBlHsFromField.size(); ++i)
BOOST_CHECK_MESSAGE(uBlHsFromField[i] == uBlHsFromRlp[i], "block header in rlp and in field do not match"); TBOOST_CHECK_MESSAGE((uBlHsFromField[i] == uBlHsFromRlp[i]), "block header in rlp and in field do not match");
}//importedAndBest }//importedAndBest
}//all blocks }//all blocks
BOOST_REQUIRE(o.count("lastblockhash") > 0); TBOOST_REQUIRE((o.count("lastblockhash") > 0));
BOOST_CHECK_MESSAGE(toString(trueBc.info().hash()) == o["lastblockhash"].get_str(), TBOOST_CHECK_MESSAGE((toString(trueBc.info().hash()) == o["lastblockhash"].get_str()),
"Boost check: " + i.first + " lastblockhash does not match " + toString(trueBc.info().hash()) + " expected: " + o["lastblockhash"].get_str()); "Boost check: " + i.first + " lastblockhash does not match " + toString(trueBc.info().hash()) + " expected: " + o["lastblockhash"].get_str());
} }
} }
@ -722,11 +722,11 @@ BlockInfo constructBlock(mObject& _o)
} }
catch (std::exception const& _e) catch (std::exception const& _e)
{ {
BOOST_ERROR("Failed block population with Exception: " << _e.what()); TBOOST_ERROR("Failed block population with Exception: " << _e.what());
} }
catch(...) catch(...)
{ {
BOOST_ERROR("block population did throw an unknown exception\n"); TBOOST_ERROR("block population did throw an unknown exception\n");
} }
return ret; return ret;
} }

14
test/libethereum/state.cpp

@ -51,9 +51,9 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
} }
std::cout << " " << i.first << std::endl; std::cout << " " << i.first << std::endl;
BOOST_REQUIRE(o.count("env") > 0); TBOOST_REQUIRE((o.count("env") > 0));
BOOST_REQUIRE(o.count("pre") > 0); TBOOST_REQUIRE((o.count("pre") > 0));
BOOST_REQUIRE(o.count("transaction") > 0); TBOOST_REQUIRE((o.count("transaction") > 0));
ImportTest importer(o, _fillin); ImportTest importer(o, _fillin);
@ -80,13 +80,13 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
#if ETH_FATDB #if ETH_FATDB
importer.exportTest(output, theState); importer.exportTest(output, theState);
#else #else
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("You can not fill tests when FATDB is switched off")); TBOOST_THROW_EXCEPTION(Exception() << errinfo_comment("You can not fill tests when FATDB is switched off"));
#endif #endif
} }
else else
{ {
BOOST_REQUIRE(o.count("post") > 0); TBOOST_REQUIRE((o.count("post") > 0));
BOOST_REQUIRE(o.count("out") > 0); TBOOST_REQUIRE((o.count("out") > 0));
// check output // check output
checkOutput(output, o); checkOutput(output, o);
@ -101,7 +101,7 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
auto resultAddrs = theState.addresses(); auto resultAddrs = theState.addresses();
checkAddresses(expectedAddrs, resultAddrs); checkAddresses(expectedAddrs, resultAddrs);
#endif #endif
BOOST_CHECK_MESSAGE(theState.rootHash() == h256(o["postStateRoot"].get_str()), "wrong post state root"); TBOOST_CHECK_MESSAGE((theState.rootHash() == h256(o["postStateRoot"].get_str())), "wrong post state root");
} }
} }
} }

43
test/libethereum/transaction.cpp

@ -21,6 +21,7 @@
*/ */
#include "../TestHelper.h" #include "../TestHelper.h"
#include "test/fuzzTesting/fuzzHelper.h"
using namespace std; using namespace std;
using namespace json_spirit; using namespace json_spirit;
@ -38,7 +39,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
if (_fillin) if (_fillin)
{ {
BOOST_REQUIRE(o.count("transaction") > 0); TBOOST_REQUIRE((o.count("transaction") > 0));
mObject tObj = o["transaction"].get_obj(); mObject tObj = o["transaction"].get_obj();
//Construct Rlp of the given transaction //Construct Rlp of the given transaction
@ -49,7 +50,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
{ {
Transaction txFromFields(rlpStream.out(), CheckTransaction::Everything); Transaction txFromFields(rlpStream.out(), CheckTransaction::Everything);
if (!txFromFields.signature().isValid()) if (!txFromFields.signature().isValid())
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); TBOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") );
o["sender"] = toString(txFromFields.sender()); o["sender"] = toString(txFromFields.sender());
o["transaction"] = ImportTest::makeAllFieldsHex(tObj); o["transaction"] = ImportTest::makeAllFieldsHex(tObj);
@ -63,9 +64,9 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
{ {
bool expectInValid = (o["expect"].get_str() == "invalid"); bool expectInValid = (o["expect"].get_str() == "invalid");
if (Options::get().checkState) if (Options::get().checkState)
BOOST_CHECK_MESSAGE(expectInValid, "Check state: Transaction '" << i.first << "' is expected to be valid!"); {TBOOST_CHECK_MESSAGE(expectInValid, "Check state: Transaction '" << i.first << "' is expected to be valid!");}
else else
BOOST_WARN_MESSAGE(expectInValid, "Check state: Transaction '" << i.first << "' is expected to be valid!"); {TBOOST_WARN_MESSAGE(expectInValid, "Check state: Transaction '" << i.first << "' is expected to be valid!");}
o.erase(o.find("expect")); o.erase(o.find("expect"));
} }
@ -76,16 +77,16 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
{ {
bool expectValid = (o["expect"].get_str() == "valid"); bool expectValid = (o["expect"].get_str() == "valid");
if (Options::get().checkState) if (Options::get().checkState)
BOOST_CHECK_MESSAGE(expectValid, "Check state: Transaction '" << i.first << "' is expected to be invalid!"); {TBOOST_CHECK_MESSAGE(expectValid, "Check state: Transaction '" << i.first << "' is expected to be invalid!");}
else else
BOOST_WARN_MESSAGE(expectValid, "Check state: Transaction '" << i.first << "' is expected to be invalid!"); {TBOOST_WARN_MESSAGE(expectValid, "Check state: Transaction '" << i.first << "' is expected to be invalid!");}
o.erase(o.find("expect")); o.erase(o.find("expect"));
} }
} }
else else
{ {
BOOST_REQUIRE(o.count("rlp") > 0); TBOOST_REQUIRE((o.count("rlp") > 0));
Transaction txFromRlp; Transaction txFromRlp;
try try
{ {
@ -93,39 +94,39 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
RLP rlp(stream); RLP rlp(stream);
txFromRlp = Transaction(rlp.data(), CheckTransaction::Everything); txFromRlp = Transaction(rlp.data(), CheckTransaction::Everything);
if (!txFromRlp.signature().isValid()) if (!txFromRlp.signature().isValid())
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); TBOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") );
} }
catch(Exception const& _e) catch(Exception const& _e)
{ {
cnote << i.first; cnote << i.first;
cnote << "Transaction Exception: " << diagnostic_information(_e); cnote << "Transaction Exception: " << diagnostic_information(_e);
BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!"); TBOOST_CHECK_MESSAGE((o.count("transaction") == 0), "A transaction object should not be defined because the RLP is invalid!");
continue; continue;
} }
catch(...) catch(...)
{ {
BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!"); TBOOST_CHECK_MESSAGE((o.count("transaction") == 0), "A transaction object should not be defined because the RLP is invalid!");
continue; continue;
} }
BOOST_REQUIRE(o.count("transaction") > 0); TBOOST_REQUIRE((o.count("transaction") > 0));
mObject tObj = o["transaction"].get_obj(); mObject tObj = o["transaction"].get_obj();
Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckTransaction::Everything); Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckTransaction::Everything);
//Check the fields restored from RLP to original fields //Check the fields restored from RLP to original fields
BOOST_CHECK_MESSAGE(txFromFields.data() == txFromRlp.data(), "Data in given RLP not matching the Transaction data!"); TBOOST_CHECK_MESSAGE((txFromFields.data() == txFromRlp.data()), "Data in given RLP not matching the Transaction data!");
BOOST_CHECK_MESSAGE(txFromFields.value() == txFromRlp.value(), "Value in given RLP not matching the Transaction value!"); TBOOST_CHECK_MESSAGE((txFromFields.value() == txFromRlp.value()), "Value in given RLP not matching the Transaction value!");
BOOST_CHECK_MESSAGE(txFromFields.gasPrice() == txFromRlp.gasPrice(), "GasPrice in given RLP not matching the Transaction gasPrice!"); TBOOST_CHECK_MESSAGE((txFromFields.gasPrice() == txFromRlp.gasPrice()), "GasPrice in given RLP not matching the Transaction gasPrice!");
BOOST_CHECK_MESSAGE(txFromFields.gas() == txFromRlp.gas(),"Gas in given RLP not matching the Transaction gas!"); TBOOST_CHECK_MESSAGE((txFromFields.gas() == txFromRlp.gas()),"Gas in given RLP not matching the Transaction gas!");
BOOST_CHECK_MESSAGE(txFromFields.nonce() == txFromRlp.nonce(),"Nonce in given RLP not matching the Transaction nonce!"); TBOOST_CHECK_MESSAGE((txFromFields.nonce() == txFromRlp.nonce()),"Nonce in given RLP not matching the Transaction nonce!");
BOOST_CHECK_MESSAGE(txFromFields.receiveAddress() == txFromRlp.receiveAddress(), "Receive address in given RLP not matching the Transaction 'to' address!"); TBOOST_CHECK_MESSAGE((txFromFields.receiveAddress() == txFromRlp.receiveAddress()), "Receive address in given RLP not matching the Transaction 'to' address!");
BOOST_CHECK_MESSAGE(txFromFields.sender() == txFromRlp.sender(), "Transaction sender address in given RLP not matching the Transaction 'vrs' signature!"); TBOOST_CHECK_MESSAGE((txFromFields.sender() == txFromRlp.sender()), "Transaction sender address in given RLP not matching the Transaction 'vrs' signature!");
BOOST_CHECK_MESSAGE(txFromFields == txFromRlp, "However, txFromFields != txFromRlp!"); TBOOST_CHECK_MESSAGE((txFromFields == txFromRlp), "However, txFromFields != txFromRlp!");
BOOST_REQUIRE (o.count("sender") > 0); TBOOST_REQUIRE ((o.count("sender") > 0));
Address addressReaded = Address(o["sender"].get_str()); Address addressReaded = Address(o["sender"].get_str());
BOOST_CHECK_MESSAGE(txFromFields.sender() == addressReaded || txFromRlp.sender() == addressReaded, "Signature address of sender does not match given sender address!"); TBOOST_CHECK_MESSAGE((txFromFields.sender() == addressReaded || txFromRlp.sender() == addressReaded), "Signature address of sender does not match given sender address!");
} }
}//for }//for
}//doTransactionTests }//doTransactionTests

26
test/libevm/vm.cpp

@ -306,9 +306,9 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
} }
std::cout << " " << i.first << "\n"; std::cout << " " << i.first << "\n";
BOOST_REQUIRE(o.count("env") > 0); TBOOST_REQUIRE((o.count("env") > 0));
BOOST_REQUIRE(o.count("pre") > 0); TBOOST_REQUIRE((o.count("pre") > 0));
BOOST_REQUIRE(o.count("exec") > 0); TBOOST_REQUIRE((o.count("exec") > 0));
FakeExtVM fev; FakeExtVM fev;
fev.importEnv(o["env"].get_obj()); fev.importEnv(o["env"].get_obj());
@ -344,12 +344,12 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
catch (Exception const& _e) catch (Exception const& _e)
{ {
cnote << "VM did throw an exception: " << diagnostic_information(_e); cnote << "VM did throw an exception: " << diagnostic_information(_e);
BOOST_ERROR("Failed VM Test with Exception: " << _e.what()); TBOOST_ERROR("Failed VM Test with Exception: " << _e.what());
} }
catch (std::exception const& _e) catch (std::exception const& _e)
{ {
cnote << "VM did throw an exception: " << _e.what(); cnote << "VM did throw an exception: " << _e.what();
BOOST_ERROR("Failed VM Test with Exception: " << _e.what()); TBOOST_ERROR("Failed VM Test with Exception: " << _e.what());
} }
// delete null entries in storage for the sake of comparison // delete null entries in storage for the sake of comparison
@ -410,13 +410,13 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
{ {
if (o.count("post") > 0) // No exceptions expected if (o.count("post") > 0) // No exceptions expected
{ {
BOOST_CHECK(!vmExceptionOccured); TBOOST_CHECK(!vmExceptionOccured);
BOOST_REQUIRE(o.count("post") > 0); TBOOST_REQUIRE((o.count("post") > 0));
BOOST_REQUIRE(o.count("callcreates") > 0); TBOOST_REQUIRE((o.count("callcreates") > 0));
BOOST_REQUIRE(o.count("out") > 0); TBOOST_REQUIRE((o.count("out") > 0));
BOOST_REQUIRE(o.count("gas") > 0); TBOOST_REQUIRE((o.count("gas") > 0));
BOOST_REQUIRE(o.count("logs") > 0); TBOOST_REQUIRE((o.count("logs") > 0));
dev::test::FakeExtVM test; dev::test::FakeExtVM test;
test.importState(o["post"].get_obj()); test.importState(o["post"].get_obj());
@ -425,7 +425,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
checkOutput(output, o); checkOutput(output, o);
BOOST_CHECK_EQUAL(toInt(o["gas"]), fev.gas); TBOOST_CHECK_EQUAL(toInt(o["gas"]), fev.gas);
State postState, expectState; State postState, expectState;
mObject mPostState = fev.exportState(); mObject mPostState = fev.exportState();
@ -435,7 +435,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses); checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
checkCallCreates(test.callcreates, fev.callcreates); checkCallCreates(fev.callcreates, test.callcreates);
checkLog(fev.sub.logs, test.sub.logs); checkLog(fev.sub.logs, test.sub.logs);
} }

Loading…
Cancel
Save