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
|
// Copyright (c) 2015, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "syncobj.h"
#include "cryptonote_core/cryptonote_basic.h"
namespace cryptonote
{
class BlockchainDB;
class HardFork
{
public:
typedef enum {
LikelyForked,
UpdateNeeded,
Ready,
} State;
static const uint64_t DEFAULT_ORIGINAL_VERSION_TILL_HEIGHT = 0; // <= actual height
static const time_t DEFAULT_FORKED_TIME = 31557600; // a year in seconds
static const time_t DEFAULT_UPDATE_TIME = 31557600 / 2;
static const uint64_t DEFAULT_WINDOW_SIZE = 10080; // supermajority window check length - a week
static const int DEFAULT_THRESHOLD_PERCENT = 80;
/**
* @brief creates a new HardFork object
*
* @param original_version the block version for blocks 0 through to the first fork
* @param forked_time the time in seconds before thinking we're forked
* @param update_time the time in seconds before thinking we need to update
* @param window_size the size of the window in blocks to consider for version voting
* @param threshold_percent the size of the majority in percents
*/
HardFork(cryptonote::BlockchainDB &db, uint8_t original_version = 1, uint64_t original_version_till_height = DEFAULT_ORIGINAL_VERSION_TILL_HEIGHT, time_t forked_time = DEFAULT_FORKED_TIME, time_t update_time = DEFAULT_UPDATE_TIME, uint64_t window_size = DEFAULT_WINDOW_SIZE, int threshold_percent = DEFAULT_THRESHOLD_PERCENT);
/**
* @brief add a new hardfork height
*
* returns true if no error, false otherwise
*
* @param version the major block version for the fork
* @param height The height the hardfork takes effect
* @param time Approximate time of the hardfork (seconds since epoch)
*/
bool add(uint8_t version, uint64_t height, time_t time);
/**
* @brief initialize the object
*
* Must be done after adding all the required hardforks via add above
*/
void init();
/**
* @brief check whether a new block would be accepted
*
* returns true if the block is accepted, false otherwise
*
* @param block the new block
*
* This check is made by add. It is exposed publicly to allow
* the caller to inexpensively check whether a block would be
* accepted or rejected by its version number. Indeed, if this
* check could only be done as part of add, the caller would
* either have to add the block to the blockchain first, then
* call add, then have to pop the block from the blockchain if
* its version did not satisfy the hard fork requirements, or
* call add first, then, if the hard fork requirements are met,
* add the block to the blockchain, upon which a failure (the
* block being invalid, double spending, etc) would cause the
* hardfork object to reorganize.
*/
bool check(const cryptonote::block &block) const;
/**
* @brief add a new block
*
* returns true if no error, false otherwise
*
* @param block the new block
*/
bool add(const cryptonote::block &block, uint64_t height);
/**
* @brief called when the blockchain is reorganized
*
* This will rescan the blockchain to determine which hard forks
* have been triggered
*
* returns true if no error, false otherwise
*
* @param blockchain the blockchain
* @param height of the last block kept from the previous blockchain
*/
bool reorganize_from_block_height(uint64_t height);
bool reorganize_from_chain_height(uint64_t height);
/**
* @brief returns current state at the given time
*
* Based on the approximate time of the last known hard fork,
* estimate whether we need to update, or if we're way behind
*
* @param t the time to consider
*/
State get_state(time_t t) const;
State get_state() const;
/**
* @brief returns the hard fork version for the given block height
*
* @param height height of the block to check
*/
uint8_t get(uint64_t height) const;
/**
* @brief returns the height of the first block on the fork with th given version
*
* @param version version of the fork to query the starting block for
*/
uint64_t get_start_height(uint8_t version) const;
/**
* @brief returns the latest "ideal" version
*
* This is the latest version that's been scheduled
*/
uint8_t get_ideal_version() const;
/**
* @brief returns the current version
*
* This is the latest version that's past its trigger date and had enough votes
* at one point in the past.
*/
uint8_t get_current_version() const;
/**
* @brief returns information about current voting state
*
* returns true if the given version is enabled (ie, the current version
* is at least the passed version), false otherwise
*
* @param version the version to check voting for
* @param window the number of blocks considered in voting
* @param votes number of votes for next version
* @param threshold number of votes needed to switch to next version
*/
bool get_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const;
/**
* @brief returns the size of the voting window in blocks
*/
uint64_t get_window_size() const { return window_size; }
private:
uint8_t get_block_version(uint64_t height) const;
bool do_check(uint8_t version) const;
int get_voted_fork_index(uint64_t height) const;
uint8_t get_effective_version(uint8_t version) const;
bool add(uint8_t block_version, uint64_t height);
bool rescan_from_block_height(uint64_t height);
bool rescan_from_chain_height(uint64_t height);
private:
BlockchainDB &db;
time_t forked_time;
time_t update_time;
uint64_t window_size;
int threshold_percent;
uint8_t original_version;
uint64_t original_version_till_height;
struct Params {
uint8_t version;
uint64_t height;
time_t time;
Params(uint8_t version, uint64_t height, time_t time): version(version), height(height), time(time) {}
};
std::vector<Params> heights;
std::deque<uint8_t> versions; /* rolling window of the last N blocks' versions */
unsigned int last_versions[256]; /* count of the block versions in the last N blocks */
uint32_t current_fork_index;
uint32_t vote_threshold;
mutable epee::critical_section lock;
};
} // namespace cryptonote
|