Source file state_ai_flags.ml
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
264
265
266
267
268
269
270
271
272
273
(** This module handles the logic of flags for AI/AS in the [State] *)
open State
open State_account
module AI_Activation = struct
(** This module is responsible for the field [state.ai_activation_cycle],
which depends on three protocol parameters:
[adaptive_issuance.force_activation], [adaptive_issuance.activation_vote_enable], and
[adaptive_issuance.launch_ema_threshold]. *)
(** AI can be activated with both flags set to false if the threshold is set to 0.
If the vote is enabled, but the threshold is above the maximum EMA, then the vote
cannot trigger the activation. *)
let enabled state =
state.constants.adaptive_issuance.force_activation
|| (state.constants.adaptive_issuance.activation_vote_enable
&& Compare.Int32.(
state.constants.adaptive_issuance.launch_ema_threshold
<= Protocol.Per_block_votes_repr.Internal_for_tests.ema_max))
|| Compare.Int32.(
state.constants.adaptive_issuance.launch_ema_threshold = 0l)
let set_activation_cycle block state block_launch_cycle =
let current_cycle = Block.current_cycle block in
let offset =
if state.constants.adaptive_issuance.force_activation then 0
else
1 + state.constants.consensus_rights_delay
+ Protocol.Constants_repr.max_slashing_period
in
assert (
Protocol.Alpha_context.Cycle.(
add current_cycle offset = block_launch_cycle)) ;
{state with ai_activation_cycle = Some block_launch_cycle}
(** Check the activation_cycle is only ever set once.
Run every block *)
let check_activation_cycle block state =
let open Lwt_result_syntax in
let open Protocol.Alpha_context in
let* block_launch_cycle =
Context.get_adaptive_issuance_launch_cycle (B block)
in
match (enabled state, state.ai_activation_cycle, block_launch_cycle) with
| _, None, None -> return state
| true, Some x, Some y ->
if Cycle.(x = y) then return state else assert false
| _, Some _, None -> assert false
| false, _, Some _ ->
assert false
| true, None, Some block_launch_cycle ->
return @@ set_activation_cycle block state block_launch_cycle
end
module AI = struct
let enabled (block : Block.t) (state : State.t) =
match state.ai_activation_cycle with
| None -> false
| Some activation_cycle ->
let current_cycle = Block.current_cycle block in
Protocol.Alpha_context.Cycle.(current_cycle >= activation_cycle)
end
module Autostake = struct
let enabled (block : Block.t) (state : State.t) =
(not (AI.enabled block state))
&& state.constants.adaptive_issuance.autostaking_enable
let log_model_autostake name pkh old_cycle op ~optimal amount =
let open Protocol.Alpha_context in
Log.debug
"Model Autostaking: at end of cycle %a, %s(%a) to reach optimal stake %a \
%s %a"
Cycle.pp
old_cycle
name
Signature.Public_key_hash.pp
pkh
Tez.pp
optimal
op
Tez.pp
(Tez_helpers.of_mutez amount)
let apply_autostake ~name ~old_cycle
({
pkh;
contract = _;
delegate;
parameters = _;
liquid;
bonds = _;
frozen_deposits;
unstaked_frozen;
unstaked_finalizable;
staking_delegator_numerator = _;
staking_delegate_denominator = _;
frozen_rights = _;
slashed_cycles = _;
last_active_cycle;
} :
account_state) state =
let open Result_syntax in
let tolerated_inactivity_period =
(2 * state.constants.consensus_rights_delay) + 1
in
if Some name <> delegate then (
Log.debug
"Model Autostaking: %s <> %s, noop@."
name
(Option.value ~default:"None" delegate) ;
return state)
else if
Cycle.(old_cycle = add last_active_cycle tolerated_inactivity_period)
then
return
@@ update_map
~f:(apply_unstake (Cycle.succ old_cycle) Tez.max_tez name)
state
else if
Cycle.(old_cycle > add last_active_cycle tolerated_inactivity_period)
then return state
else
let* current_liquid_delegated = liquid_delegated ~name state in
let current_frozen = Frozen_tez.total_current frozen_deposits in
let current_unstaked_frozen_delegated =
Unstaked_frozen.sum_current unstaked_frozen
in
let current_unstaked_final_delegated =
Unstaked_finalizable.total unstaked_finalizable
in
let power =
Tez.(
current_liquid_delegated +! current_frozen
+! current_unstaked_frozen_delegated
+! current_unstaked_final_delegated
|> to_mutez |> Z.of_int64)
in
let optimal =
Tez.of_z
(Z.cdiv
power
(Z.of_int (state.constants.limit_of_delegation_over_baking + 1)))
in
let autostaked =
Int64.(sub (Tez.to_mutez optimal) (Tez.to_mutez current_frozen))
in
let state = State.apply_unslashable (Cycle.succ old_cycle) name state in
let state = State.apply_finalize name state in
let new_state =
if autostaked > 0L then (
log_model_autostake ~optimal name pkh old_cycle "stake" autostaked ;
State.apply_stake
Tez.(min liquid (of_mutez autostaked))
(Cycle.succ old_cycle)
name
state)
else if autostaked < 0L then (
log_model_autostake
~optimal
name
pkh
old_cycle
"unstake"
(Int64.neg autostaked) ;
State.apply_unstake
(Cycle.succ old_cycle)
(Tez_helpers.of_mutez Int64.(neg autostaked))
name
state)
else (
log_model_autostake
~optimal
name
pkh
old_cycle
"only finalize"
autostaked ;
state)
in
return new_state
let run_at_cycle_end block state =
let open Result_syntax in
if enabled block state then
let current_cycle = Block.current_cycle block in
String.Map.fold_e
(fun name account state ->
apply_autostake ~name ~old_cycle:current_cycle account state)
state.account_map
state
else return state
end
module Delayed_slashing = struct
let enabled (state : State.t) = state.constants.adaptive_issuance.ns_enable
let partition_slashes state current_cycle =
if not (enabled state) then ([], state.pending_slashes)
else
List.partition
(fun (_, Protocol.Denunciations_repr.{misbehaviour; _}) ->
let cycle =
Block.current_cycle_of_level
~blocks_per_cycle:
state.constants
.Protocol.Alpha_context.Constants.Parametric.blocks_per_cycle
~current_level:
(Protocol.Raw_level_repr.to_int32 misbehaviour.level)
in
Protocol.Alpha_context.Cycle.(cycle = current_cycle))
state.pending_slashes
end
module NS = struct
let enabled (block : Block.t) (state : State.t) =
AI.enabled block state && state.constants.adaptive_issuance.ns_enable
let get_double_attestation_slashing_percentage all_denunciations_to_apply
block_before_slash state (misbehaviour : Protocol.Misbehaviour_repr.t) =
let open Lwt_result_wrap_syntax in
if not (enabled block_before_slash state) then
return
state.constants
.percentage_of_frozen_deposits_slashed_per_double_attestation
else
let* alpha_ctxt =
Context.(
get_alpha_ctxt ?policy:state.baking_policy (B block_before_slash))
in
let raw_ctxt =
Protocol.Alpha_context.Internal_for_tests.to_raw alpha_ctxt
in
let level =
Protocol.Level_repr.level_from_raw
~cycle_eras:(Protocol.Raw_context.cycle_eras raw_ctxt)
misbehaviour.level
in
let delegates =
List.filter
(fun (_, (den : Protocol.Denunciations_repr.item)) ->
Compare.Int.(
Protocol.Misbehaviour_repr.compare misbehaviour den.misbehaviour
= 0))
all_denunciations_to_apply
|> List.map fst
|> List.sort_uniq Signature.Public_key_hash.compare
in
let*@ _, pct =
Protocol.Slash_percentage.get
raw_ctxt
~kind:misbehaviour.kind
~level
delegates
in
return pct
end