From f24e717607ad0d21830ae694c096c6ad6f09a8e1 Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Tue, 8 Aug 2017 10:20:22 -0400 Subject: [PATCH] add some fuzzing meta-schedulers for testing changed the output of the schedulers to be just pending_transactions as those are tagged unions of pointers making them small and easily copyable without needing the additional indirection implemented basic test cases to stochastically verify that order of transaction delivery does not affect schedulability --- libraries/chain/block_schedule.cpp | 2 +- libraries/chain/chain_controller.cpp | 16 ++--- .../include/eos/chain/block_schedule.hpp | 52 +++++++++++++- tests/tests/block_schedule_tests.cpp | 70 ++++++++++++++++++- 4 files changed, 128 insertions(+), 12 deletions(-) diff --git a/libraries/chain/block_schedule.cpp b/libraries/chain/block_schedule.cpp index 43d87003f..765d41043 100644 --- a/libraries/chain/block_schedule.cpp +++ b/libraries/chain/block_schedule.cpp @@ -93,7 +93,7 @@ static block_schedule from_entries(vector &entries) { // allocations, we cannot emplace_back as that would reverse // the transactions in a thread auto &thread = cycle.at(entry.thread); - thread.transactions.emplace(thread.transactions.begin(), entry.transaction); + thread.transactions.emplace(thread.transactions.begin(), *entry.transaction); } return result; diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 512ca505e..b545b51ac 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -341,12 +341,12 @@ signed_block chain_controller::_generate_block( try { auto temp_session = _db.start_undo_session(true); - if (trx->contains()) { - auto processed = _apply_transaction(*trx->get()); + if (trx.contains()) { + auto processed = _apply_transaction(*trx.get()); block_thread.user_input.emplace_back(processed); - } else if (trx->contains()) { + } else if (trx.contains()) { #warning TODO: Process generated transaction - // auto processed = _apply_transaction(*trx->get()); + // auto processed = _apply_transaction(*trx.get()); // block_thread.generated_input.emplace_back(processed); } else { FC_THROW_EXCEPTION(tx_scheduling_exception, "Unknown transaction type in block_schedule"); @@ -359,10 +359,10 @@ signed_block chain_controller::_generate_block( { // Do nothing, transaction will not be re-applied wlog( "Transaction was not processed while generating block due to ${e}", ("e", e) ); - if (trx->contains()) { - wlog( "The transaction was ${t}", ("t", *trx->get()) ); - } else if (trx->contains()) { - wlog( "The transaction was ${t}", ("t", *trx->get()) ); + if (trx.contains()) { + wlog( "The transaction was ${t}", ("t", *trx.get()) ); + } else if (trx.contains()) { + wlog( "The transaction was ${t}", ("t", *trx.get()) ); } invalid_transaction_count++; } diff --git a/libraries/chain/include/eos/chain/block_schedule.hpp b/libraries/chain/include/eos/chain/block_schedule.hpp index e481b52bf..38b52b636 100644 --- a/libraries/chain/include/eos/chain/block_schedule.hpp +++ b/libraries/chain/include/eos/chain/block_schedule.hpp @@ -29,7 +29,7 @@ namespace eos { namespace chain { using pending_transaction = static_variant; struct thread_schedule { - vector transactions; + vector transactions; }; using cycle_schedule = vector; @@ -57,6 +57,56 @@ namespace eos { namespace chain { * @return the block scheduler */ static block_schedule by_cycling_conflicts(vector const &transactions, const global_property_object& properties); + + /* + * templated meta schedulers for fuzzing + */ + template + struct shuffled_functor { + shuffled_functor(NEXT &&_next) : next(_next){}; + + block_schedule operator()(vector const &transactions, const global_property_object& properties) { + std::random_device rd; + std::mt19937 rng(rd()); + auto copy = std::vector(transactions); + std::shuffle(copy.begin(), copy.end(), rng); + return next(copy, properties); + } + + NEXT &&next; + }; + + template + static shuffled_functor shuffled(NEXT &&next) { + return shuffled_functor(next); + } + + template + struct lossy_functor { + lossy_functor(NEXT &&_next) : next(_next){}; + + block_schedule operator()(vector const &transactions, const global_property_object& properties) { + std::random_device rd; + std::mt19937 rng(rd()); + std::uniform_real_distribution<> dist(0, 1); + double const cutoff = (double)NUM / (double)DEN; + + auto copy = std::vector(); + copy.reserve(transactions.size()); + std::copy_if (transactions.begin(), transactions.end(), copy.begin(), [&](pending_transaction const& trx){ + return dist(rng) >= cutoff; + }); + + return next(copy, properties); + } + + NEXT &&next; + }; + + template + static lossy_functor lossy(NEXT &&next) { + return lossy_functor(next); + } }; struct scope_extracting_visitor : public fc::visitor const &> { diff --git a/tests/tests/block_schedule_tests.cpp b/tests/tests/block_schedule_tests.cpp index 67adbb803..2d4d33f8e 100644 --- a/tests/tests/block_schedule_tests.cpp +++ b/tests/tests/block_schedule_tests.cpp @@ -149,8 +149,8 @@ static bool schedule_is_valid(block_schedule const &schedule) { for (auto const &t: c) { std::set thread_bits; size_t max_bit = 0; - for(auto pt: t.transactions) { - auto scopes = pt->visit(scope_extracting_visitor()); + for(auto const &pt: t.transactions) { + auto scopes = pt.visit(scope_extracting_visitor()); for (auto const &s : scopes) { size_t bit = boost::numeric_cast((uint64_t)s); thread_bits.emplace(bit); @@ -262,4 +262,70 @@ BOOST_FIXTURE_TEST_CASE(small_block, compose_fixture) { ); } +BOOST_FIXTURE_TEST_CASE(no_conflicts_shuffled, default_fixture) { + // stochastically verify that the order of non-conflicting transactions + // does not affect the ability to schedule them in a single cycle + for (int i = 0; i < 3000; i++) { + schedule_and_validate( + block_schedule::shuffled(block_schedule::by_threading_conflicts), + { + {0x1ULL, 0x2ULL}, + {0x3ULL, 0x4ULL}, + {0x5ULL, 0x6ULL}, + {0x7ULL, 0x8ULL}, + {0x9ULL, 0xAULL}, + {0xBULL, 0xCULL}, + {0xDULL, 0xEULL}, + {0x11ULL, 0x12ULL}, + {0x13ULL, 0x14ULL}, + {0x15ULL, 0x16ULL}, + {0x17ULL, 0x18ULL}, + {0x19ULL, 0x1AULL}, + {0x1BULL, 0x1CULL}, + {0x1DULL, 0x1EULL}, + {0x21ULL, 0x22ULL}, + {0x23ULL, 0x24ULL}, + {0x25ULL, 0x26ULL}, + {0x27ULL, 0x28ULL}, + {0x29ULL, 0x2AULL}, + {0x2BULL, 0x2CULL}, + {0x2DULL, 0x2EULL}, + }, + EXPECT(schedule_is_valid), + EXPECT(transaction_count, 21), + EXPECT(cycle_count, 1) + ); + } +} + +BOOST_FIXTURE_TEST_CASE(some_conflicts_shuffled, default_fixture) { + // stochastically verify that the order of conflicted transactions + // does not affect the ability to schedule them in multiple cycles + for (int i = 0; i < 3000; i++) { + schedule_and_validate( + block_schedule::shuffled(block_schedule::by_threading_conflicts), + { + {0x1ULL, 0x2ULL}, + {0x3ULL, 0x2ULL}, + {0x5ULL, 0x1ULL}, + {0x7ULL, 0x1ULL}, + {0x1ULL, 0x7ULL}, + {0x11ULL, 0x12ULL}, + {0x13ULL, 0x12ULL}, + {0x15ULL, 0x11ULL}, + {0x17ULL, 0x11ULL}, + {0x11ULL, 0x17ULL}, + {0x21ULL, 0x22ULL}, + {0x23ULL, 0x22ULL}, + {0x25ULL, 0x21ULL}, + {0x27ULL, 0x21ULL}, + {0x21ULL, 0x27ULL} + }, + EXPECT(schedule_is_valid), + EXPECT(transaction_count, 15), + EXPECT(std::greater, cycle_count, 1) + ); + } +} + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file -- GitLab