-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
reconfiguration.move
222 lines (194 loc) · 8.61 KB
/
reconfiguration.move
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
/// Publishes configuration information for validators, and issues reconfiguration events
/// to synchronize configuration changes for the validators.
module aptos_framework::reconfiguration {
use std::error;
use std::features;
use std::signer;
use aptos_framework::account;
use aptos_framework::event;
use aptos_framework::stake;
use aptos_framework::system_addresses;
use aptos_framework::timestamp;
use aptos_framework::chain_status;
use aptos_framework::reconfiguration_state;
use aptos_framework::storage_gas;
friend aptos_framework::aptos_governance;
friend aptos_framework::block;
friend aptos_framework::consensus_config;
friend aptos_framework::execution_config;
friend aptos_framework::gas_schedule;
friend aptos_framework::genesis;
friend aptos_framework::version;
friend aptos_framework::reconfiguration_with_dkg;
#[event]
/// Event that signals consensus to start a new epoch,
/// with new configuration information. This is also called a
/// "reconfiguration event"
struct NewEpochEvent has drop, store {
epoch: u64,
}
#[event]
/// Event that signals consensus to start a new epoch,
/// with new configuration information. This is also called a
/// "reconfiguration event"
struct NewEpoch has drop, store {
epoch: u64,
}
/// Holds information about state of reconfiguration
struct Configuration has key {
/// Epoch number
epoch: u64,
/// Time of last reconfiguration. Only changes on reconfiguration events.
last_reconfiguration_time: u64,
/// Event handle for reconfiguration events
events: event::EventHandle<NewEpochEvent>,
}
/// Reconfiguration will be disabled if this resource is published under the
/// aptos_framework system address
struct DisableReconfiguration has key {}
/// The `Configuration` resource is in an invalid state
const ECONFIGURATION: u64 = 1;
/// A `Reconfiguration` resource is in an invalid state
const ECONFIG: u64 = 2;
/// A `ModifyConfigCapability` is in a different state than was expected
const EMODIFY_CAPABILITY: u64 = 3;
/// An invalid block time was encountered.
const EINVALID_BLOCK_TIME: u64 = 4;
/// An invalid block time was encountered.
const EINVALID_GUID_FOR_EVENT: u64 = 5;
/// Only called during genesis.
/// Publishes `Configuration` resource. Can only be invoked by aptos framework account, and only a single time in Genesis.
public(friend) fun initialize(aptos_framework: &signer) {
system_addresses::assert_aptos_framework(aptos_framework);
// assert it matches `new_epoch_event_key()`, otherwise the event can't be recognized
assert!(account::get_guid_next_creation_num(signer::address_of(aptos_framework)) == 2, error::invalid_state(EINVALID_GUID_FOR_EVENT));
move_to<Configuration>(
aptos_framework,
Configuration {
epoch: 0,
last_reconfiguration_time: 0,
events: account::new_event_handle<NewEpochEvent>(aptos_framework),
}
);
}
/// Private function to temporarily halt reconfiguration.
/// This function should only be used for offline WriteSet generation purpose and should never be invoked on chain.
fun disable_reconfiguration(aptos_framework: &signer) {
system_addresses::assert_aptos_framework(aptos_framework);
assert!(reconfiguration_enabled(), error::invalid_state(ECONFIGURATION));
move_to(aptos_framework, DisableReconfiguration {})
}
/// Private function to resume reconfiguration.
/// This function should only be used for offline WriteSet generation purpose and should never be invoked on chain.
fun enable_reconfiguration(aptos_framework: &signer) acquires DisableReconfiguration {
system_addresses::assert_aptos_framework(aptos_framework);
assert!(!reconfiguration_enabled(), error::invalid_state(ECONFIGURATION));
DisableReconfiguration {} = move_from<DisableReconfiguration>(signer::address_of(aptos_framework));
}
fun reconfiguration_enabled(): bool {
!exists<DisableReconfiguration>(@aptos_framework)
}
/// Signal validators to start using new configuration. Must be called from friend config modules.
public(friend) fun reconfigure() acquires Configuration {
// Do not do anything if genesis has not finished.
if (chain_status::is_genesis() || timestamp::now_microseconds() == 0 || !reconfiguration_enabled()) {
return
};
let config_ref = borrow_global_mut<Configuration>(@aptos_framework);
let current_time = timestamp::now_microseconds();
// Do not do anything if a reconfiguration event is already emitted within this transaction.
//
// This is OK because:
// - The time changes in every non-empty block
// - A block automatically ends after a transaction that emits a reconfiguration event, which is guaranteed by
// VM spec that all transactions comming after a reconfiguration transaction will be returned as Retry
// status.
// - Each transaction must emit at most one reconfiguration event
//
// Thus, this check ensures that a transaction that does multiple "reconfiguration required" actions emits only
// one reconfiguration event.
//
if (current_time == config_ref.last_reconfiguration_time) {
return
};
reconfiguration_state::on_reconfig_start();
// Call stake to compute the new validator set and distribute rewards and transaction fees.
stake::on_new_epoch();
storage_gas::on_reconfig();
assert!(current_time > config_ref.last_reconfiguration_time, error::invalid_state(EINVALID_BLOCK_TIME));
config_ref.last_reconfiguration_time = current_time;
spec {
assume config_ref.epoch + 1 <= MAX_U64;
};
config_ref.epoch = config_ref.epoch + 1;
if (std::features::module_event_migration_enabled()) {
event::emit(
NewEpoch {
epoch: config_ref.epoch,
},
);
};
event::emit_event<NewEpochEvent>(
&mut config_ref.events,
NewEpochEvent {
epoch: config_ref.epoch,
},
);
reconfiguration_state::on_reconfig_finish();
}
public fun last_reconfiguration_time(): u64 acquires Configuration {
borrow_global<Configuration>(@aptos_framework).last_reconfiguration_time
}
public fun current_epoch(): u64 acquires Configuration {
borrow_global<Configuration>(@aptos_framework).epoch
}
/// Emit a `NewEpochEvent` event. This function will be invoked by genesis directly to generate the very first
/// reconfiguration event.
fun emit_genesis_reconfiguration_event() acquires Configuration {
let config_ref = borrow_global_mut<Configuration>(@aptos_framework);
assert!(config_ref.epoch == 0 && config_ref.last_reconfiguration_time == 0, error::invalid_state(ECONFIGURATION));
config_ref.epoch = 1;
if (std::features::module_event_migration_enabled()) {
event::emit(
NewEpoch {
epoch: config_ref.epoch,
},
);
};
event::emit_event<NewEpochEvent>(
&mut config_ref.events,
NewEpochEvent {
epoch: config_ref.epoch,
},
);
}
// For tests, skips the guid validation.
#[test_only]
public fun initialize_for_test(account: &signer) {
system_addresses::assert_aptos_framework(account);
move_to<Configuration>(
account,
Configuration {
epoch: 0,
last_reconfiguration_time: 0,
events: account::new_event_handle<NewEpochEvent>(account),
}
);
}
#[test_only]
public fun reconfigure_for_test() acquires Configuration {
reconfigure();
}
// This is used together with stake::end_epoch() for testing with last_reconfiguration_time
// It must be called each time an epoch changes
#[test_only]
public fun reconfigure_for_test_custom() acquires Configuration {
let config_ref = borrow_global_mut<Configuration>(@aptos_framework);
let current_time = timestamp::now_microseconds();
if (current_time == config_ref.last_reconfiguration_time) {
return
};
config_ref.last_reconfiguration_time = current_time;
config_ref.epoch = config_ref.epoch + 1;
}
}