diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 476d1ecf9..793e05652 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -328,7 +328,7 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost) { // export output - m_TestObject["out"] = _output.size() > 4096 ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add); + m_TestObject["out"] = (_output.size() > 4096 && !Options::get().fulloutput) ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add); // export logs m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); @@ -588,7 +588,7 @@ void userDefinedTest(std::function doTests) oSingleTest[pos->first] = pos->second; json_spirit::mValue v_singleTest(oSingleTest); - doTests(v_singleTest, false); + doTests(v_singleTest, test::Options::get().fillTests); } catch (Exception const& _e) { @@ -760,6 +760,8 @@ Options::Options() else singleTestName = std::move(name1); } + else if (arg == "--fulloutput") + fulloutput = true; } } @@ -769,7 +771,6 @@ Options const& Options::get() return instance; } - LastHashes lastHashes(u256 _currentBlockNumber) { LastHashes ret; diff --git a/test/TestHelper.h b/test/TestHelper.h index fc6c77fad..8f0c73bf3 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -184,6 +184,7 @@ public: bool stats = false; ///< Execution time stats std::string statsOutFile; ///< Stats output file. "out" for standard output bool checkState = false;///< Throw error when checking test states + bool fulloutput = false;///< Replace large output to just it's length /// Test selection /// @{ diff --git a/test/fuzzTesting/CMakeLists.txt b/test/fuzzTesting/CMakeLists.txt index b0b3b0776..371d6504d 100644 --- a/test/fuzzTesting/CMakeLists.txt +++ b/test/fuzzTesting/CMakeLists.txt @@ -9,10 +9,13 @@ include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") -add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp") -add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.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(checkRandomStateTest "./checkRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp") +list(APPEND SRCS "./fuzzHelper.cpp") +add_sources(${SRCS}) + target_link_libraries(createRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(createRandomVMTest ethereum) target_link_libraries(createRandomVMTest ethcore) diff --git a/test/fuzzTesting/createRandomStateTest.cpp b/test/fuzzTesting/createRandomStateTest.cpp index e0cb35dd1..d533ac2da 100644 --- a/test/fuzzTesting/createRandomStateTest.cpp +++ b/test/fuzzTesting/createRandomStateTest.cpp @@ -37,24 +37,88 @@ #include #include #include +#include using namespace std; using namespace json_spirit; using namespace dev; void doStateTests(json_spirit::mValue& _v); +void doChristophAlgo(); +void doRandomCodeAlgo(); + +string const c_testExample = R"( +{ + "randomStatetest" : { + "env" : { + "currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5", + "currentDifficulty" : "5623894562375", + "currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "0", + "code" : "0x6001600101600055", + "nonce" : "0", + "storage" : { + } + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "46", + "code" : "0x6000355415600957005b60203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "code" : "0x", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : { + "data" : "0x42", + "gasLimit" : "400000", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000" + } + } +} +)"; int main(int argc, char *argv[]) +{ + for (auto i = 0; i < argc; ++i) + { + auto arg = std::string{argv[i]}; + dev::test::Options& options = const_cast(dev::test::Options::get()); + if (arg == "--fulloutput") + options.fulloutput = true; + } + + //doChristophAlgo(); + doRandomCodeAlgo(); + return 0; +} + +void doChristophAlgo() { g_logVerbosity = 0; // create random code - boost::random::mt19937 gen; - auto now = chrono::steady_clock::now().time_since_epoch(); auto timeSinceEpoch = chrono::duration_cast(now).count(); gen.seed(static_cast(timeSinceEpoch)); + // set min and max length of the random evm code boost::random::uniform_int_distribution<> lengthOfCodeDist(8, 24); boost::random::uniform_int_distribution<> reasonableInputValuesSize(0, 7); @@ -77,18 +141,16 @@ int main(int argc, char *argv[]) reasonableInputValues.push_back(u256("0x945304eb96065b2a98b57a48a06ae28d285a71b5")); reasonableInputValues.push_back(randGenUniformInt()); - int lengthOfCode = lengthOfCodeDist(gen); + int lengthOfCode = lengthOfCodeDist(gen); string randomCode; - for (int i = 0; i < lengthOfCode; ++i) { // pre-fill stack to avoid that most of the test fail with a stackunderflow if (i < 8 && (randGen() < 192)) { - randomCode += randGen() < 32 ? toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode())) : "7f" + toHex(reasonableInputValues[randGenInputValue()]); + randomCode += randGen() < 32 ? toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode())) : "7f" + toHex(reasonableInputValues[randGenInputValue()]); continue; } - uint8_t opcode = randGen(); // disregard all invalid commands, except of one (0x0c) if ((dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || (randGen() > 250))) @@ -97,74 +159,58 @@ int main(int argc, char *argv[]) i--; } - string const s = R"( - { - "randomStatetest" : { - "env" : { - "currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5", - "currentDifficulty" : "5623894562375", - "currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935", - "currentNumber" : "0", - "currentTimestamp" : "1", - "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" - }, - "pre" : { - "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { - "balance" : "0", - "code" : "0x6001600101600055", - "nonce" : "0", - "storage" : { - } - }, - "945304eb96065b2a98b57a48a06ae28d285a71b5" : { - "balance" : "46", - "code" : "0x6000355415600957005b60203560003555", - "nonce" : "0", - "storage" : { - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "1000000000000000000", - "code" : "0x", - "nonce" : "0", - "storage" : { - } - } - }, - "transaction" : { - "data" : "0x42", - "gasLimit" : "400000", - "gasPrice" : "1", - "nonce" : "0", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", - "value" : "100000" - } - } - } -)"; mValue v; - read_string(s, v); - + read_string(c_testExample, v); // insert new random code v.get_obj().find("randomStatetest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode + (randGen() > 128 ? "55" : "") + (randGen() > 128 ? "60005155" : ""); - // insert new data in tx v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["data"] = "0x" + randomCode; - // insert new value in tx v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["value"] = toString(randGenUniformInt()); - // insert new gasLimit in tx v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["gasLimit"] = "0x" + toHex(toCompactBigEndian((int)randGenUniformInt())); + // fill test + doStateTests(v); + // stream to output for further handling by the bash script + cout << json_spirit::write_string(v, true); +} + +void doRandomCodeAlgo() +{ + g_logVerbosity = 0; + 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(Address("0xffffffffffffffffffffffffffffffffffffffff")); + options.addAddress(Address("0x1000000000000000000000000000000000000000")); + options.addAddress(Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87")); //coinbase + options.addAddress(Address("0x945304eb96065b2a98b57a48a06ae28d285a71b5")); + options.addAddress(Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")); + options.smartCodeProbability = 35; + string randomCode = dev::test::RandomCode::generate(10, options); + string randomData = dev::test::RandomCode::generate(10, options); + + mValue v; + read_string(c_testExample, v); + + // insert new random code + v.get_obj().find("randomStatetest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode; + + // insert new data in tx + v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["data"] = "0x" + randomData; + + // insert new value in tx + v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["value"] = dev::test::RandomCode::randomUniIntHex(); + + // insert new gasLimit in tx + v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["gasLimit"] = dev::test::RandomCode::randomUniIntHex(); // fill test doStateTests(v); // stream to output for further handling by the bash script cout << json_spirit::write_string(v, true); - - return 0; } void doStateTests(json_spirit::mValue& _v) @@ -179,30 +225,34 @@ void doStateTests(json_spirit::mValue& _v) assert(o.count("env") > 0); assert(o.count("pre") > 0); assert(o.count("transaction") > 0); - - test::ImportTest importer(o, true); - - eth::State theState = importer.m_statePre; bytes output; try { - output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; - } - catch (Exception const& _e) - { - cnote << "state execution did throw an exception: " << diagnostic_information(_e); - theState.commit(); - } - catch (std::exception const& _e) - { - cnote << "state execution did throw an exception: " << _e.what(); - } + test::ImportTest importer(o, true); + eth::State theState = importer.m_statePre; + try + { + output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; + } + catch (Exception const& _e) + { + cnote << "state execution did throw an exception: " << diagnostic_information(_e); + theState.commit(); + } + catch (std::exception const& _e) + { + cnote << "state execution did throw an exception: " << _e.what(); + } #if ETH_FATDB - importer.exportTest(output, theState); + importer.exportTest(output, theState); #else - cout << "You can not fill tests when FATDB is switched off"; + cout << "You can not fill tests when FATDB is switched off"; #endif + } + catch(...) + { + cnote << "Error filling test, probably..."; + } } } - diff --git a/test/fuzzTesting/fuzzHelper.cpp b/test/fuzzTesting/fuzzHelper.cpp new file mode 100644 index 000000000..3b6cf19c9 --- /dev/null +++ b/test/fuzzTesting/fuzzHelper.cpp @@ -0,0 +1,224 @@ +/* + 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 fuzzHelper.cpp + * @author Dimitry Khokhlov + * @date 2015 + */ + +#include "fuzzHelper.h" + +#include +#include +#include +#include + +namespace dev +{ +namespace test +{ + +boost::random::mt19937 RandomCode::gen; +boostIntDistrib RandomCode::opCodeDist = boostIntDistrib (0, 255); +boostIntDistrib RandomCode::opLengDist = boostIntDistrib (1, 32); +boostIntDistrib RandomCode::uniIntDist = boostIntDistrib (0, 0x7fffffff); + +boostIntGenerator RandomCode::randOpCodeGen = boostIntGenerator(gen, opCodeDist); +boostIntGenerator RandomCode::randOpLengGen = boostIntGenerator(gen, opLengDist); +boostIntGenerator RandomCode::randUniIntGen = boostIntGenerator(gen, uniIntDist); + +std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType) +{ + refreshSeed(); + std::string hash; + _length = (_sizeType == SizeStrictness::Strict) ? std::max(1, _length) : randomUniInt() % _length; + for (auto i = 0; i < _length; i++) + { + uint8_t byte = randOpCodeGen(); + hash += toCompactHex(byte); + } + return hash; +} + +//generate smart random code +std::string RandomCode::generate(int _maxOpNumber, RandomCodeOptions _options) +{ + refreshSeed(); + std::string code; + + //random opCode amount + boostIntDistrib sizeDist (0, _maxOpNumber); + boostIntGenerator rndSizeGen(gen, sizeDist); + int size = (int)rndSizeGen(); + + boostWeightGenerator randOpCodeWeight (gen, _options.opCodeProbability); + bool weightsDefined = _options.opCodeProbability.probabilities().size() == 255; + + for (auto i = 0; i < size; i++) + { + uint8_t opcode = weightsDefined ? randOpCodeWeight() : randOpCodeGen(); + dev::eth::InstructionInfo info = dev::eth::instructionInfo((dev::eth::Instruction) opcode); + + if (info.name.find_first_of("INVALID_INSTRUCTION") > 0) + { + //Byte code is yet not implemented + if (_options.useUndefinedOpCodes == false) + { + i--; + continue; + } + } + else + code += fillArguments((dev::eth::Instruction) opcode, _options); + std::string byte = toCompactHex(opcode); + code += (byte == "") ? "00" : byte; + } + return code; +} + +std::string RandomCode::randomUniIntHex() +{ + refreshSeed(); + return "0x" + toCompactHex((int)randUniIntGen()); +} + +int RandomCode::randomUniInt() +{ + refreshSeed(); + return (int)randUniIntGen(); +} + +void RandomCode::refreshSeed() +{ + auto now = std::chrono::steady_clock::now().time_since_epoch(); + auto timeSinceEpoch = std::chrono::duration_cast(now).count(); + gen.seed(static_cast(timeSinceEpoch)); +} + +std::string RandomCode::getPushCode(std::string const& _hex) +{ + int length = _hex.length() / 2; + int pushCode = 96 + length - 1; + return toCompactHex(pushCode) + _hex; +} + +std::string RandomCode::getPushCode(int _value) +{ + std::string hexString = toCompactHex(_value); + return getPushCode(hexString); +} + +std::string RandomCode::fillArguments(dev::eth::Instruction _opcode, RandomCodeOptions const& _options) +{ + dev::eth::InstructionInfo info = dev::eth::instructionInfo(_opcode); + + std::string code; + bool smart = false; + unsigned num = info.args; + int rand = randUniIntGen() % 100; + if (rand < _options.smartCodeProbability) + smart = true; + + if (smart) + { + switch (_opcode) + { + case dev::eth::Instruction::CALL: + //(CALL gaslimit address value memstart1 memlen1 memstart2 memlen2) + code += getPushCode(randUniIntGen() % 32); //memlen2 + code += getPushCode(randUniIntGen() % 32); //memstart2 + code += getPushCode(randUniIntGen() % 32); //memlen1 + code += getPushCode(randUniIntGen() % 32); //memlen1 + code += getPushCode(randUniIntGen()); //value + code += getPushCode(toString(_options.getRandomAddress()));//address + code += getPushCode(randUniIntGen()); //gaslimit + break; + default: + smart = false; + } + } + + if (smart == false) + for (unsigned i = 0; i < num; i++) + { + //generate random parameters + int length = randOpLengGen(); + code += getPushCode(rndByteSequence(length)); + } + return code; +} + + +//Ramdom Code Options +RandomCodeOptions::RandomCodeOptions() : useUndefinedOpCodes(false), smartCodeProbability(50) +{ + //each op code with same weight-probability + for (auto i = 0; i < 255; i++) + mapWeights.insert(std::pair(i, 50)); + setWeights(); +} + +void RandomCodeOptions::setWeight(dev::eth::Instruction _opCode, int _weight) +{ + mapWeights.at((int)_opCode) = _weight; + setWeights(); +} + +void RandomCodeOptions::addAddress(dev::Address const& _address) +{ + addressList.push_back(_address); +} + +dev::Address RandomCodeOptions::getRandomAddress() const +{ + if (addressList.size() > 0) + { + int index = RandomCode::randomUniInt() % addressList.size(); + return addressList[index]; + } + return Address(RandomCode::rndByteSequence(20)); +} + +void RandomCodeOptions::setWeights() +{ + std::vector weights; + for (auto const& element: mapWeights) + weights.push_back(element.second); + opCodeProbability = boostDescreteDistrib(weights); +} + + +BOOST_AUTO_TEST_SUITE(RandomCodeTests) + +BOOST_AUTO_TEST_CASE(rndCode) +{ + std::string code; + std::cerr << "Testing Random Code: "; + try + { + code = dev::test::RandomCode::generate(10); + } + catch(...) + { + BOOST_ERROR("Exception thrown when generating random code!"); + } + std::cerr << code; +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/fuzzTesting/fuzzHelper.h b/test/fuzzTesting/fuzzHelper.h new file mode 100644 index 000000000..371e40fbf --- /dev/null +++ b/test/fuzzTesting/fuzzHelper.h @@ -0,0 +1,97 @@ +/* + 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 fuzzHelper.h + * @author Dimitry Khokhlov + * @date 2015 + */ + +#include +#include +#include + +#include +#include +#include +#include + +#pragma once + +namespace dev +{ +namespace test +{ + +typedef boost::random::uniform_int_distribution<> boostIntDistrib; +typedef boost::random::discrete_distribution<> boostDescreteDistrib; + +typedef boost::random::variate_generator boostIntGenerator; +typedef boost::random::variate_generator boostWeightGenerator; + +struct RandomCodeOptions +{ +public: + RandomCodeOptions(); + void setWeight(dev::eth::Instruction _opCode, int _weight); + void addAddress(dev::Address const& _address); + dev::Address getRandomAddress() const; + + bool useUndefinedOpCodes; + int smartCodeProbability; + boostDescreteDistrib opCodeProbability; +private: + void setWeights(); + std::map mapWeights; + std::vector addressList; +}; + +enum class SizeStrictness +{ + Strict, + Random +}; + +class RandomCode +{ +public: + /// Generate random vm code + static std::string generate(int _maxOpNumber = 1, RandomCodeOptions _options = RandomCodeOptions()); + + /// Generate random byte string of a given length + static std::string rndByteSequence(int _length = 1, SizeStrictness _sizeType = SizeStrictness::Strict); + + /// Generate random uniForm Int with reasonable value 0..0x7fffffff + static std::string randomUniIntHex(); + static int randomUniInt(); + +private: + static std::string fillArguments(dev::eth::Instruction _opcode, RandomCodeOptions const& _options); + static std::string getPushCode(int _value); + static std::string getPushCode(std::string const& _hex); + static void refreshSeed(); + + static boost::random::mt19937 gen; ///< Random generator + static boostIntDistrib opCodeDist; ///< 0..255 opcodes + static boostIntDistrib opLengDist; ///< 1..32 byte string + static boostIntDistrib uniIntDist; ///< 0..0x7fffffff + + static boostIntGenerator randUniIntGen; ///< Generate random UniformInt from uniIntDist + static boostIntGenerator randOpCodeGen; ///< Generate random value from opCodeDist + static boostIntGenerator randOpLengGen; ///< Generate random length from opLengDist +}; + +} +} diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index 0ff7e57f3..30087397b 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -19,7 +19,7 @@ * @date 2015 * block test functions. */ - +#include "test/fuzzTesting/fuzzHelper.h" #include #include #include