Electroneum
Loading...
Searching...
No Matches
long_term_block_weight.cpp
Go to the documentation of this file.
1// Copyright (c) 2019, The Monero Project
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification, are
6// permitted provided that the following conditions are met:
7//
8// 1. Redistributions of source code must retain the above copyright notice, this list of
9// conditions and the following disclaimer.
10//
11// 2. Redistributions in binary form must reproduce the above copyright notice, this list
12// of conditions and the following disclaimer in the documentation and/or other
13// materials provided with the distribution.
14//
15// 3. Neither the name of the copyright holder nor the names of its contributors may be
16// used to endorse or promote products derived from this software without specific
17// prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#define IN_UNIT_TESTS
30
31#include "gtest/gtest.h"
35#include "blockchain_db/testdb.h"
36
37#define TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW 5000
38
39namespace
40{
41
42class TestDB: public cryptonote::BaseTestDB
43{
44private:
45 struct block_t
46 {
47 size_t weight;
48 uint64_t long_term_weight;
49 };
50
51public:
52 TestDB() { m_open = true; }
53
54 virtual void add_block( const cryptonote::block& blk
55 , size_t block_weight
56 , uint64_t long_term_block_weight
57 , const cryptonote::difficulty_type& cumulative_difficulty
58 , const uint64_t& coins_generated
59 , uint64_t num_rct_outs
60 , const crypto::hash& blk_hash
61 ) override {
62 blocks.push_back({block_weight, long_term_block_weight});
63 }
64 virtual uint64_t height() const override { return blocks.size(); }
65 virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; }
66 virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; }
67 virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override {
68 std::vector<uint64_t> ret;
69 ret.reserve(count);
70 while (count-- && start_height < blocks.size()) ret.push_back(blocks[start_height++].weight);
71 return ret;
72 }
73 virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const override {
74 std::vector<uint64_t> ret;
75 ret.reserve(count);
76 while (count-- && start_height < blocks.size()) ret.push_back(blocks[start_height++].long_term_weight);
77 return ret;
78 }
79 virtual crypto::hash get_block_hash_from_height(const uint64_t &height) const override {
80 crypto::hash hash = crypto::null_hash;
81 *(uint64_t*)&hash = height;
82 return hash;
83 }
84 virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override {
85 uint64_t h = height();
86 crypto::hash top = crypto::null_hash;
87 if (h)
88 *(uint64_t*)&top = h - 1;
89 if (block_height)
90 *block_height = h - 1;
91 return top;
92 }
93 virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); }
94
95private:
96 std::vector<block_t> blocks;
97};
98
99static uint32_t lcg_seed = 0;
100
101static uint32_t lcg()
102{
103 lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff;
104 return lcg_seed;
105}
106
107}
108
109#define PREFIX_WINDOW(hf_version,window) \
110 std::unique_ptr<cryptonote::Blockchain> bc; \
111 cryptonote::tx_memory_pool txpool(*bc); \
112 bc.reset(new cryptonote::Blockchain(txpool)); \
113 struct get_test_options { \
114 const std::pair<uint8_t, uint64_t> hard_forks[3]; \
115 const cryptonote::test_options test_options = { \
116 hard_forks, \
117 window, \
118 }; \
119 get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)1), std::make_pair((uint8_t)0, (uint64_t)0)} {} \
120 } opts; \
121 cryptonote::Blockchain *blockchain = bc.get(); \
122 bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \
123 ASSERT_TRUE(r)
124
125#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW)
126
127TEST(long_term_block_weight, empty_short)
128{
129 PREFIX(8);
130
131 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
132
133 ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8);
134 ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 * 2);
135}
136
137TEST(long_term_block_weight, identical_before_fork)
138{
139 PREFIX(8);
140
141 for (uint64_t h = 1; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h)
142 {
143 size_t w = h < CRYPTONOTE_REWARD_BLOCKS_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit();
144 uint64_t ltw = bc->get_next_long_term_block_weight(w);
145 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
146 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
147 }
148 for (uint64_t h = 0; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h)
149 {
150 ASSERT_EQ(bc->get_db().get_block_long_term_weight(h), bc->get_db().get_block_weight(h));
151 }
152}
153
154TEST(long_term_block_weight, identical_after_fork_before_long_term_window)
155{
157
158 if(bc->get_current_hard_fork_version() < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) return;
159
160 for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h)
161 {
162 size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit();
163 uint64_t ltw = bc->get_next_long_term_block_weight(w);
164 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
165 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
166 }
167 for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h)
168 {
169 ASSERT_EQ(bc->get_db().get_block_long_term_weight(h), bc->get_db().get_block_weight(h));
170 }
171}
172
173TEST(long_term_block_weight, ceiling_at_30000000)
174{
176
178 {
179 size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : bc->get_current_cumulative_block_weight_limit();
180 uint64_t ltw = bc->get_next_long_term_block_weight(w);
181 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
182 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
183 }
184 ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), 15000000);
185 ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), 30000000);
186}
187
188TEST(long_term_block_weight, multi_pop)
189{
191
192 for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + 20; ++h)
193 {
194 size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : bc->get_current_cumulative_block_weight_limit();
195 uint64_t ltw = bc->get_next_long_term_block_weight(w);
196 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
197 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
198 }
199
200 const uint64_t effective_median = bc->get_current_cumulative_block_weight_median();
201 const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit();
202
203 const uint64_t num_pop = 4;
204 for (uint64_t h = 0; h < num_pop; ++h)
205 {
206 size_t w = bc->get_current_cumulative_block_weight_limit();
207 uint64_t ltw = bc->get_next_long_term_block_weight(w);
208 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
209 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
210 }
211
213 std::vector<cryptonote::transaction> txs;
214 for (uint64_t h = 0; h < num_pop; ++h)
215 bc->get_db().pop_block(b, txs);
216 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
217
218 ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median());
219 ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit());
220}
221
222TEST(long_term_block_weight, multiple_updates)
223{
225
226 for (uint64_t h = 1; h <= 3 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h)
227 {
228 size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : bc->get_current_cumulative_block_weight_limit();
229 uint64_t ltw = bc->get_next_long_term_block_weight(w);
230 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
231 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
232 const uint64_t effective_median = bc->get_current_cumulative_block_weight_median();
233 const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit();
234 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
235 ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median());
236 ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit());
237 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
238 ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median());
239 ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit());
240 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
241 ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median());
242 ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit());
243 }
244}
245
246TEST(long_term_block_weight, pop_invariant_max)
247{
249
250 for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h)
251 {
252 size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : bc->get_current_cumulative_block_weight_limit();
253 uint64_t ltw = bc->get_next_long_term_block_weight(w);
254 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
255 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
256 }
257
258 for (int n = 0; n < 1000; ++n)
259 {
260 // pop some blocks, then add some more
261 int remove = 1 + (n * 17) % 8;
262 int add = (n * 23) % 12;
263
264 // save long term block weights we're about to remove
265 uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1;
266 for (int i = -2; i < remove; ++i)
267 {
268 old_ltbw[i + 2] = bc->get_db().get_block_long_term_weight(h0 + i);
269 }
270
271 for (int i = 0; i < remove; ++i)
272 {
274 std::vector<cryptonote::transaction> txs;
275 bc->get_db().pop_block(b, txs);
276 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
277 }
278 for (int i = 0; i < add; ++i)
279 {
280 size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : bc->get_current_cumulative_block_weight_limit();
281 uint64_t ltw = bc->get_next_long_term_block_weight(w);
282 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {});
283 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
284 }
285
286 // check the new values are the same as the old ones
287 for (int i = -2; i < std::min(add, remove); ++i)
288 {
289 ASSERT_EQ(bc->get_db().get_block_long_term_weight(h0 + i), old_ltbw[i + 2]);
290 }
291 }
292}
293
294TEST(long_term_block_weight, pop_invariant_random)
295{
297
298 for (uint64_t h = 1; h < 2 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h)
299 {
300 lcg_seed = bc->get_db().height();
301 uint32_t r = lcg();
302 size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : (r % bc->get_current_cumulative_block_weight_limit());
303 uint64_t ltw = bc->get_next_long_term_block_weight(w);
304 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
305 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
306 }
307
308 for (int n = 0; n < 1000; ++n)
309 {
310 // pop some blocks, then add some more
311 int remove = 1 + (n * 17) % 8;
312 int add = (n * 23) % 123;
313
314 // save long term block weights we're about to remove
315 uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1;
316 for (int i = -2; i < remove; ++i)
317 {
318 old_ltbw[i + 2] = bc->get_db().get_block_long_term_weight(h0 + i);
319 }
320
321 for (int i = 0; i < remove; ++i)
322 {
324 std::vector<cryptonote::transaction> txs;
325 bc->get_db().pop_block(b, txs);
326 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
327 const uint64_t effective_median = bc->get_current_cumulative_block_weight_median();
328 const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit();
329 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
330 ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median());
331 ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit());
332 }
333 for (int i = 0; i < add; ++i)
334 {
335 lcg_seed = bc->get_db().height();
336 uint32_t r = lcg();
337 size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : (r % bc->get_current_cumulative_block_weight_limit());
338 uint64_t ltw = bc->get_next_long_term_block_weight(w);
339 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {});
340 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
341 const uint64_t effective_median = bc->get_current_cumulative_block_weight_median();
342 const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit();
343 ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
344 ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median());
345 ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit());
346 }
347
348 // check the new values are the same as the old ones
349 for (int i = -2; i < std::min(add, remove); ++i)
350 {
351 ASSERT_EQ(bc->get_db().get_block_long_term_weight(h0 + i), old_ltbw[i + 2]);
352 }
353 }
354}
355
356TEST(long_term_block_weight, long_growth_spike_and_drop)
357{
359
360 uint64_t long_term_effective_median_block_weight;
361
362 // constant init
363 for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h)
364 {
366 uint64_t ltw = bc->get_next_long_term_block_weight(w);
367 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
368 ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight));
369 }
370 ASSERT_EQ(long_term_effective_median_block_weight, 300000);
371
372 // slow 10% yearly for a year (scaled down by 100000 / TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW) -> 8% change
373 for (uint64_t h = 0; h < 365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h)
374 {
375 //size_t w = bc->get_current_cumulative_block_weight_median() * rate;
376 float t = h / float(365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000);
377 size_t w = 300000 + t * 30000;
378 uint64_t ltw = bc->get_next_long_term_block_weight(w);
379 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
380 ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight));
381 }
382 ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07);
383 ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09);
384
385 // spike over three weeks - does not move much
386 for (uint64_t h = 0; h < 21 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h)
387 {
388 size_t w = bc->get_current_cumulative_block_weight_limit();
389 uint64_t ltw = bc->get_next_long_term_block_weight(w);
390 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
391 ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight));
392 }
393 ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07);
394 ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09);
395
396 // drop - does not move much
397 for (uint64_t h = 0; h < 21 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h)
398 {
399 size_t w = bc->get_current_cumulative_block_weight_median() * .25;
400 uint64_t ltw = bc->get_next_long_term_block_weight(w);
401 bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
402 ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight));
403 }
404 ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07);
405 ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09);
406}
uint64_t height
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT
#define CRYPTONOTE_REWARD_BLOCKS_WINDOW
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8
#define ASSERT_GT(val1, val2)
Definition gtest.h:1976
#define ASSERT_EQ(val1, val2)
Definition gtest.h:1956
#define TEST(test_case_name, test_name)
Definition gtest.h:2187
#define ASSERT_TRUE(condition)
Definition gtest.h:1865
#define ASSERT_LT(val1, val2)
Definition gtest.h:1968
#define TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW
#define PREFIX(hf_version)
POD_CLASS hash
Definition hash.h:50
boost::multiprecision::uint128_t difficulty_type
Definition difficulty.h:43
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136