-
Notifications
You must be signed in to change notification settings - Fork 0
/
producer_schedule_tests.cpp
263 lines (219 loc) · 12.8 KB
/
producer_schedule_tests.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#include <boost/test/unit_test.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <boost/range/algorithm.hpp>
#ifdef NON_VALIDATING_TEST
#define TESTER tester
#else
#define TESTER validating_tester
#endif
using namespace eosio::testing;
using namespace eosio::chain;
using mvo = fc::mutable_variant_object;
BOOST_AUTO_TEST_SUITE(producer_schedule_tests)
// Calculate expected producer given the schedule and slot number
account_name get_expected_producer(const vector<producer_key>& schedule, const uint64_t slot) {
const auto& index = (slot % (schedule.size() * config::producer_repetitions)) / config::producer_repetitions;
return schedule.at(index).producer_name;
};
// Check if two schedule is equal
bool is_schedule_equal(const vector<producer_key>& first, const vector<producer_key>& second) {
bool is_equal = first.size() == second.size();
for (uint32_t i = 0; i < first.size(); i++) {
is_equal = is_equal && first.at(i) == second.at(i);
}
return is_equal;
};
// Calculate the block num of the next round first block
// The new producer schedule will become effective when it's in the block of the next round first block
// However, it won't be applied until the effective block num is deemed irreversible
uint64_t calc_block_num_of_next_round_first_block(const controller& control){
auto res = control.head_block_num() + 1;
const auto blocks_per_round = control.head_block_state()->active_schedule.producers.size() * config::producer_repetitions;
while((res % blocks_per_round) != 0) {
res++;
}
return res;
};
#if 0
BOOST_FIXTURE_TEST_CASE( verify_producer_schedule, TESTER ) try {
// Utility function to ensure that producer schedule work as expected
const auto& confirm_schedule_correctness = [&](const vector<producer_key>& new_prod_schd, const uint64_t eff_new_prod_schd_block_num) {
const uint32_t check_duration = 1000; // number of blocks
for (uint32_t i = 0; i < check_duration; ++i) {
const auto current_schedule = control->head_block_state()->active_schedule.producers;
const auto& current_absolute_slot = control->get_global_properties().proposed_schedule_block_num;
// Determine expected producer
const auto& expected_producer = get_expected_producer(current_schedule, *current_absolute_slot + 1);
// The new schedule will only be applied once the effective block num is deemed irreversible
const bool is_new_schedule_applied = control->last_irreversible_block_num() > eff_new_prod_schd_block_num;
// Ensure that we have the correct schedule at the right time
if (is_new_schedule_applied) {
BOOST_TEST(is_schedule_equal(new_prod_schd, current_schedule));
} else {
BOOST_TEST(!is_schedule_equal(new_prod_schd, current_schedule));
}
// Produce block
produce_block();
// Check if the producer is the same as what we expect
BOOST_TEST(control->head_block_producer() == expected_producer);
}
};
// Create producer accounts
vector<account_name> producers = {
"inita", "initb", "initc", "initd", "inite", "initf", "initg",
"inith", "initi", "initj", "initk", "initl", "initm", "initn",
"inito", "initp", "initq", "initr", "inits", "initt", "initu"
};
create_accounts(producers);
// ---- Test first set of producers ----
// Send set prods action and confirm schedule correctness
set_producers(producers);
const auto first_prod_schd = get_producer_keys(producers);
const auto eff_first_prod_schd_block_num = calc_block_num_of_next_round_first_block(*control);
confirm_schedule_correctness(first_prod_schd, eff_first_prod_schd_block_num);
// ---- Test second set of producers ----
vector<account_name> second_set_of_producer = {
producers[3], producers[6], producers[9], producers[12], producers[15], producers[18], producers[20]
};
// Send set prods action and confirm schedule correctness
set_producers(second_set_of_producer);
const auto second_prod_schd = get_producer_keys(second_set_of_producer);
const auto& eff_second_prod_schd_block_num = calc_block_num_of_next_round_first_block(*control);
confirm_schedule_correctness(second_prod_schd, eff_second_prod_schd_block_num);
// ---- Test deliberately miss some blocks ----
const int64_t num_of_missed_blocks = 5000;
produce_block(fc::microseconds(500 * 1000 * num_of_missed_blocks));
// Ensure schedule is still correct
confirm_schedule_correctness(second_prod_schd, eff_second_prod_schd_block_num);
produce_block();
// ---- Test third set of producers ----
vector<account_name> third_set_of_producer = {
producers[2], producers[5], producers[8], producers[11], producers[14], producers[17], producers[20],
producers[0], producers[3], producers[6], producers[9], producers[12], producers[15], producers[18],
producers[1], producers[4], producers[7], producers[10], producers[13], producers[16], producers[19]
};
// Send set prods action and confirm schedule correctness
set_producers(third_set_of_producer);
const auto third_prod_schd = get_producer_keys(third_set_of_producer);
const auto& eff_third_prod_schd_block_num = calc_block_num_of_next_round_first_block(*control);
confirm_schedule_correctness(third_prod_schd, eff_third_prod_schd_block_num);
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( verify_producers, TESTER ) try {
vector<account_name> valid_producers = {
"inita", "initb", "initc", "initd", "inite", "initf", "initg",
"inith", "initi", "initj", "initk", "initl", "initm", "initn",
"inito", "initp", "initq", "initr", "inits", "initt", "initu"
};
create_accounts(valid_producers);
set_producers(valid_producers);
// account initz does not exist
vector<account_name> nonexisting_producer = { "initz" };
BOOST_CHECK_THROW(set_producers(nonexisting_producer), wasm_execution_error);
// replace initg with inita, inita is now duplicate
vector<account_name> invalid_producers = {
"inita", "initb", "initc", "initd", "inite", "initf", "inita",
"inith", "initi", "initj", "initk", "initl", "initm", "initn",
"inito", "initp", "initq", "initr", "inits", "initt", "initu"
};
BOOST_CHECK_THROW(set_producers(invalid_producers), wasm_execution_error);
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( verify_header_schedule_version, TESTER ) try {
// Utility function to ensure that producer schedule version in the header is correct
const auto& confirm_header_schd_ver_correctness = [&](const uint64_t expected_version, const uint64_t eff_new_prod_schd_block_num) {
const uint32_t check_duration = 1000; // number of blocks
for (uint32_t i = 0; i < check_duration; ++i) {
// The new schedule will only be applied once the effective block num is deemed irreversible
const bool is_new_schedule_applied = control->last_irreversible_block_num() > eff_new_prod_schd_block_num;
// Produce block
produce_block();
// Ensure that the head block header is updated at the right time
const auto current_schd_ver = control->head_block_header().schedule_version;
if (is_new_schedule_applied) {
BOOST_TEST(current_schd_ver == expected_version);
} else {
BOOST_TEST(current_schd_ver != expected_version);
}
}
};
// Create producer accounts
vector<account_name> producers = {
"inita", "initb", "initc", "initd", "inite", "initf", "initg",
"inith", "initi", "initj", "initk", "initl", "initm", "initn",
"inito", "initp", "initq", "initr", "inits", "initt", "initu"
};
create_accounts(producers);
// Send set prods action and confirm schedule correctness
set_producers(producers, 1);
const auto& eff_first_prod_schd_block_num = calc_block_num_of_next_round_first_block(*control);
// Confirm the version is correct
confirm_header_schd_ver_correctness(1, eff_first_prod_schd_block_num);
// Shuffle the producers and set the new one with smaller version
boost::range::random_shuffle(producers);
set_producers(producers, 0);
const auto& eff_second_prod_schd_block_num = calc_block_num_of_next_round_first_block(*control);
// Even though we set it with smaller version number, the version should be incremented instead
confirm_header_schd_ver_correctness(2, eff_second_prod_schd_block_num);
// Shuffle the producers and set the new one with much larger version number
boost::range::random_shuffle(producers);
set_producers(producers, 1000);
const auto& eff_third_prod_schd_block_num = calc_block_num_of_next_round_first_block(*control);
// Even though we set it with much large version number, the version should be incremented instead
confirm_header_schd_ver_correctness(3, eff_third_prod_schd_block_num);
} FC_LOG_AND_RETHROW()
#endif
BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, TESTER ) try {
create_accounts( {N(alice),N(bob),N(carol)} );
produce_block();
auto compare_schedules = [&]( const vector<producer_key>& a, const producer_schedule_type& b ) {
return std::equal( a.begin(), a.end(), b.producers.begin(), b.producers.end() );
};
auto res = set_producers( {N(alice),N(bob)} );
vector<producer_key> sch1 = {
{N(alice), get_public_key(N(alice), "active")},
{N(bob), get_public_key(N(bob), "active")}
};
//wdump((fc::json::to_pretty_string(res)));
wlog("set producer schedule to [alice,bob]");
BOOST_REQUIRE_EQUAL( true, control->proposed_producers().valid() );
BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *control->proposed_producers() ) );
BOOST_CHECK_EQUAL( control->pending_producers().version, 0 );
produce_block(); // Starts new block which promotes the proposed schedule to pending
BOOST_CHECK_EQUAL( control->pending_producers().version, 1 );
BOOST_CHECK_EQUAL( true, compare_schedules( sch1, control->pending_producers() ) );
BOOST_CHECK_EQUAL( control->active_producers().version, 0 );
produce_block();
produce_block(); // Starts new block which promotes the pending schedule to active
BOOST_CHECK_EQUAL( control->active_producers().version, 1 );
BOOST_CHECK_EQUAL( true, compare_schedules( sch1, control->active_producers() ) );
produce_blocks(7);
res = set_producers( {N(alice),N(bob),N(carol)} );
vector<producer_key> sch2 = {
{N(alice), get_public_key(N(alice), "active")},
{N(bob), get_public_key(N(bob), "active")},
{N(carol), get_public_key(N(carol), "active")}
};
wlog("set producer schedule to [alice,bob,carol]");
BOOST_REQUIRE_EQUAL( true, control->proposed_producers().valid() );
BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *control->proposed_producers() ) );
produce_block();
produce_blocks(23); // Alice produces the last block of her first round.
// Bob's first block (which advances LIB to Alice's last block) is started but not finalized.
BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(alice) );
BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(bob) );
BOOST_CHECK_EQUAL( control->pending_producers().version, 2 );
produce_blocks(12); // Bob produces his first 11 blocks
BOOST_CHECK_EQUAL( control->active_producers().version, 1 );
produce_blocks(12); // Bob produces his 12th block.
// Alice's first block of the second round is started but not finalized (which advances LIB to Bob's last block).
BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(alice) );
BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(bob) );
BOOST_CHECK_EQUAL( control->active_producers().version, 2 );
BOOST_CHECK_EQUAL( true, compare_schedules( sch2, control->active_producers() ) );
produce_block(); // Alice produces the first block of her second round which has changed the active schedule.
// The next block will be produced according to the new schedule
produce_block();
BOOST_CHECK_EQUAL( control->head_block_producer(), N(carol) ); // And that next block happens to be produced by Carol.
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_SUITE_END()