Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5216,6 +5216,21 @@ int64_t controller_impl::set_proposed_producers( vector<producer_authority> prod

assert(pending);

// see if one already proposed in this block
auto& gpo = db.get<global_property_object>();
if (gpo.proposed_schedule_block_num) {
if (std::equal(producers.begin(), producers.end(),
gpo.proposed_schedule.producers.begin(), gpo.proposed_schedule.producers.end())) {
return gpo.proposed_schedule.version; // the proposed producer schedule does not change
}
// clear gpo proposed_schedule as we may determine no diff between proposed producers and next proposer schedule
db.modify( gpo, [&]( auto& gp ) {
gp.proposed_schedule_block_num.reset();
gp.proposed_schedule.version = 0;
gp.proposed_schedule.producers.clear();
});
}

auto [version, diff] = pending->get_next_proposer_schedule_version(producers);
if (!diff)
return version;
Expand All @@ -5228,7 +5243,6 @@ int64_t controller_impl::set_proposed_producers( vector<producer_authority> prod

// overwrite any existing proposed_schedule set earlier in this block
auto cur_block_num = chain_head.block_num() + 1;
auto& gpo = db.get<global_property_object>();
db.modify( gpo, [&]( auto& gp ) {
gp.proposed_schedule_block_num = cur_block_num;
gp.proposed_schedule = sch;
Expand Down
5 changes: 5 additions & 0 deletions libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ struct block_header_state {
proposer_policy_ptr active_proposer_policy; // producer authority schedule, supports `digest()`

// block time when proposer_policy will become active
// The active time is the next,next producer round. For example,
// round A [1,2,..12], next_round B [1,2,..12], next_next_round C [1,2,..12], D [1,2,..12]
// If proposed in A1, A2, .. A12 becomes active in C1
// If proposed in B1, B2, .. B12 becomes active in D1
// If proposed in A2 and another proposed in B3 then there can be 2 in the map
flat_map<block_timestamp_type, proposer_policy_ptr> proposer_policies;

// track in-flight finalizer policies. This is a `multimap` because the same block number
Expand Down
53 changes: 45 additions & 8 deletions unittests/producer_schedule_if_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,19 @@ bool compare_schedules( const vector<producer_authority>& a, const producer_auth
BOOST_FIXTURE_TEST_CASE( proposer_policy_progression_test, validating_tester ) try {
create_accounts( {"alice"_n,"bob"_n,"carol"_n} );

// set_producers in same block, do it the explicit way to use a diff expiration and avoid duplicate trx
auto set_producers_force = [&](const std::vector<account_name>& producers) {
static int unique = 0; // used to force uniqueness of transaction
auto schedule = get_producer_authorities( producers );
fc::variants schedule_variant;
schedule_variant.reserve(schedule.size());
for( const auto& e: schedule ) {
schedule_variant.emplace_back(e.get_abi_variant());
}
push_action( config::system_account_name, "setprods"_n, config::system_account_name,
fc::mutable_variant_object()("schedule", schedule_variant), DEFAULT_EXPIRATION_DELTA + (++unique));
};

while (control->head_block_num() < 3) {
produce_block();
}
Expand All @@ -132,7 +145,7 @@ BOOST_FIXTURE_TEST_CASE( proposer_policy_progression_test, validating_tester ) t

// set a new proposer policy sch1
set_producers( {"alice"_n} );
vector<producer_authority> sch1 = {
vector<producer_authority> alice_sch = {
producer_authority{"alice"_n, block_signing_authority_v0{1, {{get_public_key("alice"_n, "active"), 1}}}}
};

Expand All @@ -145,15 +158,15 @@ BOOST_FIXTURE_TEST_CASE( proposer_policy_progression_test, validating_tester ) t

// set another ploicy to have multiple pending different active time policies
set_producers( {"bob"_n,"carol"_n} );
vector<producer_authority> sch2 = {
vector<producer_authority> bob_carol_sch = {
producer_authority{"bob"_n, block_signing_authority_v0{ 1, {{get_public_key("bob"_n, "active"),1}}}},
producer_authority{"carol"_n, block_signing_authority_v0{ 1, {{get_public_key("carol"_n, "active"),1}}}}
};
produce_block();

// set another ploicy should replace sch2
set_producers( {"bob"_n,"alice"_n} );
vector<producer_authority> sch3 = {
vector<producer_authority> bob_alice_sch = {
producer_authority{"bob"_n, block_signing_authority_v0{ 1, {{get_public_key("bob"_n, "active"),1}}}},
producer_authority{"alice"_n, block_signing_authority_v0{ 1, {{get_public_key("alice"_n, "active"),1}}}}
};
Expand All @@ -163,13 +176,13 @@ BOOST_FIXTURE_TEST_CASE( proposer_policy_progression_test, validating_tester ) t

// sch1 must become active no later than 2 rounds but sch2 cannot become active yet
BOOST_CHECK_EQUAL( control->active_producers().version, 1u );
BOOST_CHECK_EQUAL( true, compare_schedules( sch1, control->active_producers() ) );
BOOST_CHECK_EQUAL( true, compare_schedules( alice_sch, control->active_producers() ) );

produce_blocks(config::producer_repetitions);

// sch3 becomes active
BOOST_CHECK_EQUAL( 2u, control->active_producers().version ); // should be 2 as sch2 was replaced by sch3
BOOST_CHECK_EQUAL( true, compare_schedules( sch3, control->active_producers() ) );
BOOST_CHECK_EQUAL( true, compare_schedules( bob_alice_sch, control->active_producers() ) );

// get to next producer round
auto prod = produce_block()->producer;
Expand All @@ -180,18 +193,42 @@ BOOST_FIXTURE_TEST_CASE( proposer_policy_progression_test, validating_tester ) t
produce_blocks(config::producer_repetitions);
produce_blocks(config::producer_repetitions);
BOOST_CHECK_EQUAL( 2u, control->active_producers().version ); // should be 2 as not different so no change
BOOST_CHECK_EQUAL( true, compare_schedules( sch3, control->active_producers() ) );
BOOST_CHECK_EQUAL( true, compare_schedules( bob_alice_sch, control->active_producers() ) );

// test no change to proposed schedule, only the first one will take affect
for (size_t i = 0; i < config::producer_repetitions*2-1; ++i) {
BOOST_CHECK_EQUAL( 2u, control->active_producers().version ); // should be 2 as not taken affect yet
BOOST_CHECK_EQUAL( true, compare_schedules( sch3, control->active_producers() ) );
BOOST_CHECK_EQUAL( true, compare_schedules( bob_alice_sch, control->active_producers() ) );
set_producers( {"bob"_n,"carol"_n} );
set_producers_force({"bob"_n,"carol"_n} );
set_producers_force({"bob"_n,"carol"_n} );
produce_block();
}
produce_block();
BOOST_CHECK_EQUAL( 3u, control->active_producers().version ); // should be 3 now as bob,carol now active
BOOST_CHECK_EQUAL( true, compare_schedules( sch2, control->active_producers() ) );
BOOST_CHECK_EQUAL( true, compare_schedules( bob_carol_sch, control->active_producers() ) );

// get to next producer round
prod = produce_block()->producer;
for (auto b = produce_block(); b->producer == prod; b = produce_block());

// test change in same block where there is an existing proposed that is the same
set_producers( {"bob"_n,"alice"_n} );
produce_block();
set_producers( {"bob"_n,"carol"_n} );
set_producers_force({"bob"_n,"carol"_n} );
produce_blocks(config::producer_repetitions-1);
produce_blocks(config::producer_repetitions);
BOOST_CHECK_EQUAL( 4u, control->active_producers().version ); // should be 4 now as bob,alice now active
BOOST_CHECK_EQUAL( true, compare_schedules( bob_alice_sch, control->active_producers() ) );

// test change in same block where there is no change
set_producers( {"bob"_n,"carol"_n} );
set_producers_force({"bob"_n,"alice"_n} ); // put back, no change expected
produce_blocks(config::producer_repetitions);
produce_blocks(config::producer_repetitions);
BOOST_CHECK_EQUAL( 4u, control->active_producers().version ); // should be 4 now as bob,alice is still active
BOOST_CHECK_EQUAL( true, compare_schedules( bob_alice_sch, control->active_producers() ) );

} FC_LOG_AND_RETHROW()

Expand Down