diff --git a/test/fuzzTesting/CMakeLists.txt b/test/fuzzTesting/CMakeLists.txt index 9bd2b5540..ec56282bf 100644 --- a/test/fuzzTesting/CMakeLists.txt +++ b/test/fuzzTesting/CMakeLists.txt @@ -8,7 +8,7 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(${CRYPTOPP_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(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp" "../libethereum/state.cpp" "../libevm/vm.cpp" "../libethereum/blockchain.cpp" "../libdevcore/rlp.cpp") add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp") diff --git a/test/fuzzTesting/createRandomTest.cpp b/test/fuzzTesting/createRandomTest.cpp index c26c9b7f7..1a422c8a9 100644 --- a/test/fuzzTesting/createRandomTest.cpp +++ b/test/fuzzTesting/createRandomTest.cpp @@ -34,6 +34,7 @@ extern std::string const c_testExampleStateTest; extern std::string const c_testExampleTransactionTest; extern std::string const c_testExampleVMTest; extern std::string const c_testExampleBlockchainTest; +extern std::string const c_testExampleRLPTest; //Main Test functinos void fillRandomTest(std::function _doTests, std::string const& _testString, bool _debug = false); @@ -62,7 +63,8 @@ int main(int argc, char *argv[]) if (arg == "-t" && i + 1 < argc) { testSuite = argv[i + 1]; - if (testSuite != "BlockChainTests" && testSuite != "TransactionTests" && testSuite != "StateTests" && testSuite != "VMTests") + if (testSuite != "BlockChainTests" && testSuite != "TransactionTests" && testSuite != "StateTests" + && testSuite != "VMTests" && testSuite != "RLPTests") testSuite = ""; } else @@ -139,6 +141,14 @@ int main(int argc, char *argv[]) else fillRandomTest(dev::test::doVMTests, (filltest) ? testFillString : c_testExampleVMTest, filldebug); } + else + if (testSuite == "RLPTests") + { + if (checktest) + return checkRandomTest(dev::test::doRlpTests, testmValue, debug); + else + fillRandomTest(dev::test::doRlpTests, (filltest) ? testFillString : c_testExampleRLPTest, filldebug); + } } return 0; @@ -239,12 +249,23 @@ void parseTestWithTypes(std::string& _test) std::size_t pos = _test.find(types.at(i)); while (pos != std::string::npos) { + if (types.at(i) == "[RLP]") + { + std::string debug; + int randomDepth = 1 + dev::test::RandomCode::randomUniInt() % 10; + _test.replace(pos, 5, dev::test::RandomCode::rndRLPSequence(randomDepth, debug)); + cnote << debug; + } + else 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) == "[HEX32]") + _test.replace(pos, 7, dev::test::RandomCode::randomUniIntHex(std::numeric_limits::max())); + else if (types.at(i) == "[GASLIMIT]") _test.replace(pos, 10, dev::test::RandomCode::randomUniIntHex(dev::u256("3000000000"))); else @@ -276,7 +297,7 @@ void parseTestWithTypes(std::string& _test) std::vector getTypes() { - return {"[CODE]", "[HEX]", "[HASH20]", "[HASH32]", "[0xHASH32]", "[V]", "[GASLIMIT]"}; + return {"[RLP]", "[CODE]", "[HEX]", "[HEX32]", "[HASH20]", "[HASH32]", "[0xHASH32]", "[V]", "[GASLIMIT]"}; } std::string const c_testExampleTransactionTest = R"( @@ -305,7 +326,7 @@ std::string const c_testExampleStateTest = R"( "currentCoinbase" : "[HASH20]", "currentDifficulty" : "[HEX]", "currentGasLimit" : "[GASLIMIT]", - "currentNumber" : "[HEX]", + "currentNumber" : "[HEX32]", "currentTimestamp" : "[HEX]", "previousHash" : "[HASH32]" }, @@ -335,7 +356,7 @@ std::string const c_testExampleStateTest = R"( "transaction" : { "data" : "[CODE]", "gasLimit" : "[HEX]", - "gasPrice" : "[V]", + "gasPrice" : "[HEX32]", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", @@ -377,6 +398,14 @@ std::string const c_testExampleVMTest = R"( } )"; +std::string const c_testExampleRLPTest = R"( +{ + "randomRLPTest" : { + "out" : "[RLP]" + } +} +)"; + std::string const c_testExampleBlockchainTest = R"( { "randomBlockTest" : { diff --git a/test/fuzzTesting/fuzzHelper.cpp b/test/fuzzTesting/fuzzHelper.cpp index b6a3bd700..863c2477c 100644 --- a/test/fuzzTesting/fuzzHelper.cpp +++ b/test/fuzzTesting/fuzzHelper.cpp @@ -42,11 +42,157 @@ boostIntGenerator RandomCode::randOpLengGen = boostIntGenerator(gen, opLengDist) boostIntGenerator RandomCode::randUniIntGen = boostIntGenerator(gen, uniIntDist); boostUInt64Generator RandomCode::randUInt64Gen = boostUInt64Generator(gen, uInt64Dist); -std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType) +int RandomCode::recursiveRLP(std::string& _result, int _depth, std::string& _debug) +{ + bool genValidRlp = true; + int bugProbability = randUniIntGen() % 100; + if (bugProbability < 80) + genValidRlp = false; + + if (_depth > 1) + { + //create rlp blocks + int size = 1 + randUniIntGen() % 4; + for (auto i = 0; i < size; i++) + { + std::string blockstr; + std::string blockDebug; + recursiveRLP(blockstr, _depth - 1, blockDebug); + _result += blockstr; + _debug += blockDebug; + } + + //make rlp header + int length = _result.size() / 2; + std::string header; + int rtype = 0; + int rnd = randUniIntGen() % 100; + if (rnd < 10) + { + //make header as array + if (length <= 55) + { + header = toCompactHex(128 + length); + rtype = 1; + } + else + { + std::string hexlength = toCompactHex(length); + header = toCompactHex(183 + hexlength.size() / 2) + hexlength; + rtype = 2; + } + } + else + { + //make header as list + if (length <= 55) + { + header = toCompactHex(192 + length); + rtype = 3; + } + else + { + std::string hexlength = toCompactHex(length, HexPrefix::DontAdd, 1); + header = toCompactHex(247 + hexlength.size() / 2) + hexlength; + rtype = 4; + } + } + _result = header + _result; + _debug = "[" + header + "(" + toString(length) + "){" + toString(rtype) + "}]" + _debug; + return _result.size() / 2; + } + if (_depth == 1) + { + bool genbug = false; + bool genbug2 = false; + int bugProbability = randUniIntGen() % 100; + if (bugProbability < 50 && !genValidRlp) + genbug = true; + bugProbability = randUniIntGen() % 100; //more randomness + if (bugProbability < 50 && !genValidRlp) + genbug2 = true; + + std::string emptyZeros = genValidRlp ? "" : genbug ? "00" : ""; + std::string emptyZeros2 = genValidRlp ? "" : genbug2 ? "00" : ""; + + int rnd = randUniIntGen() % 5; + switch (rnd) + { + case 0: + { + //single byte [0x00, 0x7f] + std::string rlp = emptyZeros + toCompactHex(genbug ? randUniIntGen() % 255 : randUniIntGen() % 128, HexPrefix::DontAdd, 1); + _result.insert(0, rlp); + _debug.insert(0, "[" + rlp + "]"); + return 1; + } + case 1: + { + //string 0-55 [0x80, 0xb7] + string + int len = genbug ? randUniIntGen() % 255 : randUniIntGen() % 55; + std::string hex = rndByteSequence(len); + if (len == 1) + if (genValidRlp && fromHex(hex)[0] < 128) + hex = toCompactHex((u64)128); + + _result.insert(0, toCompactHex(128 + len) + emptyZeros + hex); + _debug.insert(0, "[" + toCompactHex(128 + len) + "(" + toString(len) + ")]" + emptyZeros + hex); + return len + 1; + } + case 2: + { + //string more 55 [0xb8, 0xbf] + length + string + int len = randUniIntGen() % 100; + if (len < 56 && genValidRlp) + len = 56; + + std::string hex = rndByteSequence(len); + std::string hexlen = emptyZeros2 + toCompactHex(len, HexPrefix::DontAdd, 1); + std::string rlpblock = toCompactHex(183 + hexlen.size() / 2) + hexlen + emptyZeros + hex; + _debug.insert(0, "[" + toCompactHex(183 + hexlen.size() / 2) + hexlen + "(" + toString(len) + "){2}]" + emptyZeros + hex); + _result.insert(0, rlpblock); + return rlpblock.size() / 2; + } + case 3: + { + //list 0-55 [0xc0, 0xf7] + data + int len = genbug ? randUniIntGen() % 255 : randUniIntGen() % 55; + std::string hex = emptyZeros + rndByteSequence(len); + _result.insert(0, toCompactHex(192 + len) + hex); + _debug.insert(0, "[" + toCompactHex(192 + len) + "(" + toString(len) + "){3}]" + hex); + return len + 1; + } + case 4: + { + //list more 55 [0xf8, 0xff] + length + data + int len = randUniIntGen() % 100; + if (len < 56 && genValidRlp) + len = 56; + std::string hexlen = emptyZeros2 + toCompactHex(len, HexPrefix::DontAdd, 1); + std::string rlpblock = toCompactHex(247 + hexlen.size() / 2) + hexlen + emptyZeros + rndByteSequence(len); + _debug.insert(0, "[" + toCompactHex(247 + hexlen.size() / 2) + hexlen + "(" + toString(len) + "){4}]" + emptyZeros + rndByteSequence(len)); + _result.insert(0, rlpblock); + return rlpblock.size() / 2; + } + } + } + return 0; +} + +std::string RandomCode::rndRLPSequence(int _depth, std::string& _debug) { refreshSeed(); std::string hash; - _length = (_sizeType == SizeStrictness::Strict) ? std::max(1, _length) : randomUniInt() % _length; + _depth = std::min(std::max(1, _depth), 7); //limit depth to avoid overkill + recursiveRLP(hash, _depth, _debug); + return hash; +} + +std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType) +{ + refreshSeed(); + std::string hash = ""; + _length = (_sizeType == SizeStrictness::Strict) ? std::max(0, _length) : randomUniInt() % _length; for (auto i = 0; i < _length; i++) { uint8_t byte = randOpCodeGen(); diff --git a/test/fuzzTesting/fuzzHelper.h b/test/fuzzTesting/fuzzHelper.h index 309037e3d..a061333bb 100644 --- a/test/fuzzTesting/fuzzHelper.h +++ b/test/fuzzTesting/fuzzHelper.h @@ -66,6 +66,12 @@ enum class SizeStrictness Random }; +struct RlpDebug +{ + std::string rlp; + int insertions; +}; + class RandomCode { public: @@ -75,6 +81,16 @@ public: /// Generate random byte string of a given length static std::string rndByteSequence(int _length = 1, SizeStrictness _sizeType = SizeStrictness::Strict); + /// Generate random rlp byte sequence of a given depth (e.g [[[]],[]]). max depth level = 20. + /// The _debug string contains returned rlp string with analysed sections + /// [] - length section/ or single byte rlp encoding + /// () - decimal representation of length + /// {1} - Array + /// {2} - Array more than 55 + /// {3} - List + /// {4} - List more than 55 + static std::string rndRLPSequence(int _depth, std::string& _debug); + /// Generate random int64 static std::string randomUniIntHex(u256 _maxVal = 0); static int randomUniInt(); @@ -83,6 +99,7 @@ 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 int recursiveRLP(std::string& _result, int _depth, std::string& _debug); static void refreshSeed(); static boost::random::mt19937 gen; ///< Random generator diff --git a/test/libdevcore/rlp.cpp b/test/libdevcore/rlp.cpp index 016b183e2..e64621053 100644 --- a/test/libdevcore/rlp.cpp +++ b/test/libdevcore/rlp.cpp @@ -30,8 +30,8 @@ #include #include #include -#include "../JsonSpiritHeaders.h" -#include "../TestHelper.h" +#include "test/JsonSpiritHeaders.h" +#include "test/TestHelper.h" using namespace std; using namespace dev; @@ -72,8 +72,6 @@ namespace dev bytes payloadToDecode = fromHex(o["out"].get_str()); RLP payload(payloadToDecode); ostringstream() << payload; - if (payload.isEmpty()) - BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Decoded Empty RLP!")); o["in"] = "VALID"; } catch (Exception const& _e) @@ -129,6 +127,8 @@ namespace dev { bytes payloadToDecode = fromHex(o["out"].get_str()); RLP payload(payloadToDecode); + + //attempt to read all the contents of RLP ostringstream() << payload; if (rlpType == RlpType::Test) @@ -144,6 +144,10 @@ namespace dev cnote << "rlp exception: " << _e.what(); was_exception = true; } + catch (...) + { + was_exception = true; + } //Expect exception as input is INVALID if (rlpType == RlpType::Invalid && was_exception) @@ -238,6 +242,28 @@ namespace dev BOOST_AUTO_TEST_SUITE(RlpTests) +BOOST_AUTO_TEST_CASE(EmptyArrayList) +{ + try + { + bytes payloadToDecode = fromHex("80"); + RLP payload(payloadToDecode); + ostringstream() << payload; + + payloadToDecode = fromHex("с0"); + RLP payload2(payloadToDecode); + ostringstream() << payload2; + } + catch (Exception const& _e) + { + TBOOST_ERROR("Failed test with Exception: " << _e.what()); + } + catch (exception const& _e) + { + TBOOST_ERROR("Failed test with Exception: " << _e.what()); + } +} + BOOST_AUTO_TEST_CASE(invalidRLPtest) { dev::test::executeTests("invalidRLPTest", "/RLPTests", dev::test::getFolder(__FILE__) + "/RLPTestsFiller", dev::test::doRlpTests);