Electroneum
block_reward.cpp
Go to the documentation of this file.
1 // Copyrights(c) 2017-2021, The Electroneum Project
2 // Copyrights(c) 2014-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
31 
32 #include "chaingen.h"
33 #include "block_reward.h"
34 
35 using namespace epee;
36 using namespace cryptonote;
37 
38 namespace
39 {
40  bool construct_miner_tx_by_weight(transaction& miner_tx, uint64_t height, uint64_t already_generated_coins,
41  const account_public_address& miner_address, std::vector<size_t>& block_weights, size_t target_tx_weight,
42  size_t target_block_weight, uint64_t fee = 0)
43  {
44  if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, fee, miner_address, miner_tx))
45  return false;
46 
47  size_t current_weight = get_transaction_weight(miner_tx);
48  size_t try_count = 0;
49  while (target_tx_weight != current_weight)
50  {
51  ++try_count;
52  if (10 < try_count)
53  return false;
54 
55  if (target_tx_weight < current_weight)
56  {
57  size_t diff = current_weight - target_tx_weight;
58  if (diff <= miner_tx.extra.size())
59  miner_tx.extra.resize(miner_tx.extra.size() - diff);
60  else
61  return false;
62  }
63  else
64  {
65  size_t diff = target_tx_weight - current_weight;
66  miner_tx.extra.resize(miner_tx.extra.size() + diff);
67  }
68 
69  current_weight = get_transaction_weight(miner_tx);
70  }
71 
72  return true;
73  }
74 
75  bool construct_max_weight_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account,
76  size_t median_block_count = CRYPTONOTE_REWARD_BLOCKS_WINDOW)
77  {
78  std::vector<size_t> block_weights;
79  generator.get_last_n_block_weights(block_weights, get_block_hash(blk_prev), median_block_count);
80 
81  size_t median = misc_utils::median(block_weights);
82  median = std::max(median, static_cast<size_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1));
83 
84  transaction miner_tx;
85  bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev),
86  miner_account.get_keys().m_account_address, block_weights, 2 * median, 2 * median);
87  if (!r)
88  return false;
89 
90  return generator.construct_block_manually(blk, blk_prev, miner_account, test_generator::bf_miner_tx, 0, 0, 0,
91  crypto::hash(), 0, miner_tx);
92  }
93 
94  bool rewind_blocks(std::vector<test_event_entry>& events, test_generator& generator, block& blk, const block& blk_prev,
95  const account_base& miner_account, size_t block_count)
96  {
97  blk = blk_prev;
98  for (size_t i = 0; i < block_count; ++i)
99  {
100  block blk_i;
101  if (!construct_max_weight_block(generator, blk_i, blk, miner_account))
102  return false;
103 
104  events.push_back(blk_i);
105  blk = blk_i;
106  }
107 
108  return true;
109  }
110 
111  uint64_t get_tx_out_amount(const transaction& tx)
112  {
113  uint64_t amount = 0;
114  BOOST_FOREACH(auto& o, tx.vout)
115  amount += o.amount;
116  return amount;
117  }
118 }
119 
121  : m_invalid_block_index(0)
122 {
123  REGISTER_CALLBACK_METHOD(gen_block_reward, mark_invalid_block);
124  REGISTER_CALLBACK_METHOD(gen_block_reward, mark_checked_block);
125  REGISTER_CALLBACK_METHOD(gen_block_reward, check_block_rewards);
126 }
127 
128 bool gen_block_reward::generate(std::vector<test_event_entry>& events) const
129 {
130  uint64_t ts_start = 1338224400;
131 
132  GENERATE_ACCOUNT(miner_account);
133  MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
134  DO_CALLBACK(events, "mark_checked_block");
135  MAKE_ACCOUNT(events, bob_account);
136 
137  // Test: miner transactions without outputs (block reward == 0)
138  block blk_0r;
139  if (!rewind_blocks(events, generator, blk_0r, blk_0, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW))
140  return false;
141 
142  // Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks
143  DO_CALLBACK(events, "mark_invalid_block");
144  block blk_1_bad_1;
145  if (!construct_max_weight_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1))
146  return false;
147  events.push_back(blk_1_bad_1);
148 
149  DO_CALLBACK(events, "mark_invalid_block");
150  block blk_1_bad_2;
151  if (!construct_max_weight_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1))
152  return false;
153  events.push_back(blk_1_bad_2);
154 
155  block blk_1;
156  if (!construct_max_weight_block(generator, blk_1, blk_0r, miner_account))
157  return false;
158  events.push_back(blk_1);
159 
161 
162  MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account);
163  DO_CALLBACK(events, "mark_checked_block");
164  MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_account);
165  DO_CALLBACK(events, "mark_checked_block");
166  MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_account);
167  DO_CALLBACK(events, "mark_checked_block");
168  MAKE_NEXT_BLOCK(events, blk_5, blk_4, miner_account);
169  DO_CALLBACK(events, "mark_checked_block");
170 
171  block blk_5r;
172  if (!rewind_blocks(events, generator, blk_5r, blk_5, miner_account, CRYPTONOTE_MINED_ETN_UNLOCK_WINDOW))
173  return false;
174 
175  // Test: fee increases block reward
176  transaction tx_0(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 3 * TESTS_DEFAULT_FEE));
177  MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner_account, tx_0);
178  DO_CALLBACK(events, "mark_checked_block");
179 
180  // Test: fee from all block transactions increase block reward
181  std::list<transaction> txs_0;
182  txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 5 * TESTS_DEFAULT_FEE));
183  txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 7 * TESTS_DEFAULT_FEE));
184  MAKE_NEXT_BLOCK_TX_LIST(events, blk_7, blk_6, miner_account, txs_0);
185  DO_CALLBACK(events, "mark_checked_block");
186 
187  // Test: block reward == transactions fee
188  {
189  transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE);
190  transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE);
191  size_t txs_1_weight = get_transaction_weight(tx_1) + get_transaction_weight(tx_2);
192  uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2);
193 
194  std::vector<size_t> block_weights;
196  size_t median = misc_utils::median(block_weights);
197 
198  transaction miner_tx;
199  bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_7) + 1, generator.get_already_generated_coins(blk_7),
200  miner_account.get_keys().m_account_address, block_weights, 2 * median - txs_1_weight, 2 * median, txs_fee);
201  if (!r)
202  return false;
203 
204  std::vector<crypto::hash> txs_1_hashes;
205  txs_1_hashes.push_back(get_transaction_hash(tx_1));
206  txs_1_hashes.push_back(get_transaction_hash(tx_2));
207 
208  block blk_8;
210  0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_weight);
211 
212  events.push_back(blk_8);
213  DO_CALLBACK(events, "mark_checked_block");
214  }
215 
216  DO_CALLBACK(events, "check_block_rewards");
217 
218  return true;
219 }
220 
222 {
223  if (m_invalid_block_index == event_idx)
224  {
225  m_invalid_block_index = 0;
226  return bvc.m_verification_failed;
227  }
228  else
229  {
230  return !bvc.m_verification_failed;
231  }
232 }
233 
234 bool gen_block_reward::mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
235 {
236  m_invalid_block_index = ev_index + 1;
237  return true;
238 }
239 
240 bool gen_block_reward::mark_checked_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
241 {
242  m_checked_blocks_indices.push_back(ev_index - 1);
243  return true;
244 }
245 
246 bool gen_block_reward::check_block_rewards(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector<test_event_entry>& events)
247 {
248  DEFINE_TESTS_ERROR_CONTEXT("gen_block_reward_without_txs::check_block_rewards");
249 
250  std::array<uint64_t, 7> blk_rewards;
251  blk_rewards[0] = ETN_SUPPLY >> EMISSION_SPEED_FACTOR_PER_MINUTE;
252  // Take into account the premine.
253  blk_rewards[1] = 1260000000000;
254  uint64_t cumulative_reward = blk_rewards[0] + blk_rewards[1];
255  for (size_t i = 2; i < blk_rewards.size(); ++i)
256  {
257  blk_rewards[i] = (ETN_SUPPLY - cumulative_reward) >> EMISSION_SPEED_FACTOR_PER_MINUTE;
258  cumulative_reward += blk_rewards[i];
259  }
260 
261  for (size_t i = 0; i < 5; ++i)
262  {
263  block blk_i = boost::get<block>(events[m_checked_blocks_indices[i]]);
264  CHECK_EQ(blk_rewards[i], get_tx_out_amount(blk_i.miner_tx));
265  }
266 
267  block blk_n1 = boost::get<block>(events[m_checked_blocks_indices[5]]);
268  CHECK_EQ(blk_rewards[5] + 3 * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n1.miner_tx));
269 
270  block blk_n2 = boost::get<block>(events[m_checked_blocks_indices[6]]);
271  CHECK_EQ(blk_rewards[6] + (5 + 7) * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n2.miner_tx));
272 
273  block blk_n3 = boost::get<block>(events[m_checked_blocks_indices[7]]);
274  CHECK_EQ((11 + 13) * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n3.miner_tx));
275 
276  return true;
277 }
#define CRYPTONOTE_MINED_ETN_UNLOCK_WINDOW
#define EMISSION_SPEED_FACTOR_PER_MINUTE
#define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1)
Definition: chaingen.h:849
bool get_tx_fee(const transaction &tx, uint64_t &fee)
uint64_t height
Definition: blockchain.cpp:91
#define MAKE_NEXT_BLOCK(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC)
Definition: chaingen.h:839
#define TESTS_DEFAULT_FEE
Definition: chaingen.h:1061
#define ETN_SUPPLY
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
bool check_block_rewards(cryptonote::core &c, size_t ev_index, const std::vector< test_event_entry > &events)
transaction construct_tx_with_fee(std::vector< test_event_entry > &events, const block &blk_head, const account_base &acc_from, const var_addr_t &to, uint64_t amount, uint64_t fee)
Definition: chaingen.cpp:931
std::vector< uint8_t > extra
const account_keys & get_keys() const
Definition: account.cpp:264
Holds cryptonote related classes and helpers.
Definition: ban.cpp:40
bool construct_block_manually(cryptonote::block &blk, const cryptonote::block &prev_block, const cryptonote::account_base &miner_acc, int actual_params=bf_none, uint8_t major_ver=0, uint8_t minor_ver=0, uint64_t timestamp=0, const crypto::hash &prev_id=crypto::hash(), const cryptonote::difficulty_type &diffic=1, const cryptonote::transaction &miner_tx=cryptonote::transaction(), const std::vector< crypto::hash > &tx_hashes=std::vector< crypto::hash >(), size_t txs_sizes=0, size_t max_outs=999, uint8_t hf_version=1)
Definition: chaingen.cpp:221
bool generate(std::vector< test_event_entry > &events) const
handles core cryptonote functionality
#define MAKE_GENESIS_BLOCK(VEC_EVENTS, BLK_NAME, MINER_ACC, TS)
Definition: chaingen.h:833
unsigned __int64 uint64_t
Definition: stdint.h:136
#define REGISTER_CALLBACK_METHOD(CLASS, METHOD)
Definition: chaingen.h:830
bool check_block_verification_context(const cryptonote::block_verification_context &bvc, size_t event_idx, const cryptonote::block &blk)
type_vec_type median(std::vector< type_vec_type > &v)
#define CRYPTONOTE_REWARD_BLOCKS_WINDOW
account_public_address m_account_address
Definition: account.h:43
#define MAKE_ACCOUNT(VEC_EVENTS, account)
Definition: chaingen.h:815
crypto::hash get_transaction_hash(const transaction &t)
uint64_t get_already_generated_coins(const crypto::hash &blk_id) const
Definition: chaingen.cpp:88
crypto::hash get_block_hash(uint64_t height)
POD_CLASS hash
Definition: hash.h:50
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction &tx, const blobdata &extra_nonce, size_t max_outs, uint8_t hard_fork_version, network_type nettype)
#define MK_COINS(amount)
Definition: chaingen.h:1060
#define CHECK_EQ(v1, v2)
Definition: chaingen.h:1058
#define GENERATE_ACCOUNT(account)
Definition: chaingen.h:801
bool mark_checked_block(cryptonote::core &c, size_t ev_index, const std::vector< test_event_entry > &events)
bool mark_invalid_block(cryptonote::core &c, size_t ev_index, const std::vector< test_event_entry > &events)
#define DEFINE_TESTS_ERROR_CONTEXT(text)
Definition: chaingen.h:1056
#define DO_CALLBACK(VEC_EVENTS, CB_NAME)
Definition: chaingen.h:820
#define MAKE_NEXT_BLOCK_TX_LIST(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST)
Definition: chaingen.h:867
void get_last_n_block_weights(std::vector< size_t > &block_weights, const crypto::hash &head, size_t n) const
Definition: chaingen.cpp:78
uint64_t get_block_height(const block &b)
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1