提交 f24e7176 编写于 作者: B Bart Wyatt

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
上级 e3f22656
......@@ -93,7 +93,7 @@ static block_schedule from_entries(vector<schedule_entry> &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;
......
......@@ -341,12 +341,12 @@ signed_block chain_controller::_generate_block(
try
{
auto temp_session = _db.start_undo_session(true);
if (trx->contains<SignedTransaction const *>()) {
auto processed = _apply_transaction(*trx->get<SignedTransaction const *>());
if (trx.contains<SignedTransaction const *>()) {
auto processed = _apply_transaction(*trx.get<SignedTransaction const *>());
block_thread.user_input.emplace_back(processed);
} else if (trx->contains<GeneratedTransaction const *>()) {
} else if (trx.contains<GeneratedTransaction const *>()) {
#warning TODO: Process generated transaction
// auto processed = _apply_transaction(*trx->get<GeneratedTransaction const *>());
// auto processed = _apply_transaction(*trx.get<GeneratedTransaction const *>());
// 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<SignedTransaction const *>()) {
wlog( "The transaction was ${t}", ("t", *trx->get<SignedTransaction const *>()) );
} else if (trx->contains<GeneratedTransaction const *>()) {
wlog( "The transaction was ${t}", ("t", *trx->get<GeneratedTransaction const *>()) );
if (trx.contains<SignedTransaction const *>()) {
wlog( "The transaction was ${t}", ("t", *trx.get<SignedTransaction const *>()) );
} else if (trx.contains<GeneratedTransaction const *>()) {
wlog( "The transaction was ${t}", ("t", *trx.get<GeneratedTransaction const *>()) );
}
invalid_transaction_count++;
}
......
......@@ -29,7 +29,7 @@ namespace eos { namespace chain {
using pending_transaction = static_variant<SignedTransaction const *, GeneratedTransaction const *>;
struct thread_schedule {
vector<pending_transaction const *> transactions;
vector<pending_transaction> transactions;
};
using cycle_schedule = vector<thread_schedule>;
......@@ -57,6 +57,56 @@ namespace eos { namespace chain {
* @return the block scheduler
*/
static block_schedule by_cycling_conflicts(vector<pending_transaction> const &transactions, const global_property_object& properties);
/*
* templated meta schedulers for fuzzing
*/
template<typename NEXT>
struct shuffled_functor {
shuffled_functor(NEXT &&_next) : next(_next){};
block_schedule operator()(vector<pending_transaction> const &transactions, const global_property_object& properties) {
std::random_device rd;
std::mt19937 rng(rd());
auto copy = std::vector<pending_transaction>(transactions);
std::shuffle(copy.begin(), copy.end(), rng);
return next(copy, properties);
}
NEXT &&next;
};
template<typename NEXT>
static shuffled_functor<NEXT> shuffled(NEXT &&next) {
return shuffled_functor<NEXT>(next);
}
template<int NUM, int DEN, typename NEXT>
struct lossy_functor {
lossy_functor(NEXT &&_next) : next(_next){};
block_schedule operator()(vector<pending_transaction> 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<pending_transaction>();
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<int NUM, int DEN, typename NEXT>
static lossy_functor<NUM, DEN, NEXT> lossy(NEXT &&next) {
return lossy_functor<NUM, DEN, NEXT>(next);
}
};
struct scope_extracting_visitor : public fc::visitor<vector<AccountName> const &> {
......
......@@ -149,8 +149,8 @@ static bool schedule_is_valid(block_schedule const &schedule) {
for (auto const &t: c) {
std::set<size_t> 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<size_t>((uint64_t)s);
thread_bits.emplace(bit);
......@@ -262,4 +262,70 @@ BOOST_FIXTURE_TEST_CASE(small_block, compose_fixture<small_block_properties>) {
);
}
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<uint>, cycle_count, 1)
);
}
}
BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册