Source file account_helpers.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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
(** [Account_helpers] defines a type abstracting the information of an account
in the protocol. This includes its pkh, delegate, any funds, staking
parameters, etc...
A type [balance] is also defined, as an observed state of funds for a
given account, i.e balance information that one might get from calling
RPCs. *)
open Adaptive_issuance_helpers
open Tez_staking_helpers
let fail_account_not_found func_name account_name =
Log.error "State_account.%s: account %s not found" func_name account_name ;
assert false
module CycleMap = Map.Make (Cycle)
(** Abstract information of accounts *)
type account_state = {
pkh : Signature.Public_key_hash.t;
contract : Protocol.Alpha_context.Contract.t;
delegate : string option;
parameters : staking_parameters;
liquid : Tez.t;
bonds : Tez.t;
frozen_deposits : Frozen_tez.t;
unstaked_frozen : Unstaked_frozen.t;
unstaked_finalizable : Unstaked_finalizable.t;
staking_delegator_numerator : Z.t;
staking_delegate_denominator : Z.t;
frozen_rights : Tez.t CycleMap.t;
(** The portion of rights that comes from staking, used for
baking/attesting during the specified cycle.
At the end of cycle [c], the current frozen deposits of the
delegate (own + co-staked, taking
limit_of_staking_over_baking into account) are added to this
table for cycle [c + consensus_rights_delay + 1]. The table
is unmodified if at that time, the account is not a delegate
or is a deactivated delegate. *)
slashed_cycles : Cycle.t list;
last_active_cycle : Cycle.t;
}
let init_account ?delegate ~pkh ~contract ~parameters ?(liquid = Tez.zero)
?(bonds = Tez.zero) ?(frozen_deposits = Frozen_tez.zero)
?(unstaked_frozen = Unstaked_frozen.zero)
?(unstaked_finalizable = Unstaked_finalizable.zero)
?(staking_delegator_numerator = Z.zero)
?(staking_delegate_denominator = Z.zero) ?(frozen_rights = CycleMap.empty)
?(slashed_cycles = []) ?(last_active_cycle = Cycle.root) () =
{
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;
}
type account_map = account_state String.Map.t
(** Balance returned by RPCs. Partial tez are rounded down *)
type balance = {
liquid_b : Tez.t;
bonds_b : Tez.t;
staked_b : Partial_tez.t;
unstaked_frozen_b : Tez.t;
unstaked_finalizable_b : Tez.t;
staking_delegator_numerator_b : Z.t;
staking_delegate_denominator_b : Z.t;
}
let balance_zero =
{
liquid_b = Tez.zero;
bonds_b = Tez.zero;
staked_b = Partial_tez.zero;
unstaked_frozen_b = Tez.zero;
unstaked_finalizable_b = Tez.zero;
staking_delegator_numerator_b = Z.zero;
staking_delegate_denominator_b = Z.zero;
}
let balance_of_account account_name (account_map : account_map) =
match String.Map.find account_name account_map with
| None -> fail_account_not_found "balance_of_account.src" account_name
| Some
{
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 = _;
} ->
let balance =
{
balance_zero with
liquid_b = liquid;
bonds_b = bonds;
staking_delegator_numerator_b = staking_delegator_numerator;
staking_delegate_denominator_b = staking_delegate_denominator;
}
in
let balance =
match delegate with
| None -> balance
| Some d -> (
match String.Map.find d account_map with
| None -> fail_account_not_found "balance_of_account.delegate" d
| Some delegate_account ->
{
balance with
staked_b =
Frozen_tez.get account_name delegate_account.frozen_deposits;
})
in
let unstaked_frozen_b, unstaked_finalizable_b =
String.Map.fold
(fun _delegate_name delegate (frozen, finalzbl) ->
let frozen =
Tez.(
frozen
+! Unstaked_frozen.get_total
account_name
delegate.unstaked_frozen)
in
let finalzbl =
Tez.(
finalzbl
+! Unstaked_finalizable.get
account_name
delegate.unstaked_finalizable)
in
(frozen, finalzbl))
account_map
(Tez.zero, Tez.zero)
in
{balance with unstaked_frozen_b; unstaked_finalizable_b}
let balance_pp fmt
{
liquid_b;
bonds_b;
staked_b;
unstaked_frozen_b;
unstaked_finalizable_b;
staking_delegator_numerator_b;
staking_delegate_denominator_b;
} =
Format.fprintf
fmt
"{@;\
@[<v 2> liquid : %a@;\
bonds : %a@;\
staked : %a@;\
unstaked_frozen : %a@;\
unstaked_finalizable : %a@;\
staking_delegator_numerator : %a@;\
staking_delegate_denominator : %a@;\
}@."
Tez.pp
liquid_b
Tez.pp
bonds_b
Partial_tez.pp
staked_b
Tez.pp
unstaked_frozen_b
Tez.pp
unstaked_finalizable_b
Z.pp_print
staking_delegator_numerator_b
Z.pp_print
staking_delegate_denominator_b
let balance_update_pp fmt
( {
liquid_b = a_liquid_b;
bonds_b = a_bonds_b;
staked_b = a_staked_b;
unstaked_frozen_b = a_unstaked_frozen_b;
unstaked_finalizable_b = a_unstaked_finalizable_b;
staking_delegator_numerator_b = a_staking_delegator_numerator_b;
staking_delegate_denominator_b = a_staking_delegate_denominator_b;
},
{
liquid_b = b_liquid_b;
bonds_b = b_bonds_b;
staked_b = b_staked_b;
unstaked_frozen_b = b_unstaked_frozen_b;
unstaked_finalizable_b = b_unstaked_finalizable_b;
staking_delegator_numerator_b = b_staking_delegator_numerator_b;
staking_delegate_denominator_b = b_staking_delegate_denominator_b;
} ) =
Format.fprintf
fmt
"{@;\
@[<v 2> liquid : %a -> %a@;\
bonds : %a -> %a@;\
staked : %a -> %a@;\
unstaked_frozen : %a -> %a@;\
unstaked_finalizable : %a -> %a@;\
staking_delegator_numerator : %a -> %a@;\
staking_delegate_denominator : %a -> %a@;\
}@."
Tez.pp
a_liquid_b
Tez.pp
b_liquid_b
Tez.pp
a_bonds_b
Tez.pp
b_bonds_b
Partial_tez.pp
a_staked_b
Partial_tez.pp
b_staked_b
Tez.pp
a_unstaked_frozen_b
Tez.pp
b_unstaked_frozen_b
Tez.pp
a_unstaked_finalizable_b
Tez.pp
b_unstaked_finalizable_b
Z.pp_print
a_staking_delegator_numerator_b
Z.pp_print
b_staking_delegator_numerator_b
Z.pp_print
a_staking_delegate_denominator_b
Z.pp_print
b_staking_delegate_denominator_b
let assert_balance_equal ~loc account_name
{
liquid_b = a_liquid_b;
bonds_b = a_bonds_b;
staked_b = a_staked_b;
unstaked_frozen_b = a_unstaked_frozen_b;
unstaked_finalizable_b = a_unstaked_finalizable_b;
staking_delegator_numerator_b = a_staking_delegator_numerator_b;
staking_delegate_denominator_b = a_staking_delegate_denominator_b;
}
{
liquid_b = b_liquid_b;
bonds_b = b_bonds_b;
staked_b = b_staked_b;
unstaked_frozen_b = b_unstaked_frozen_b;
unstaked_finalizable_b = b_unstaked_finalizable_b;
staking_delegator_numerator_b = b_staking_delegator_numerator_b;
staking_delegate_denominator_b = b_staking_delegate_denominator_b;
} =
let open Lwt_result_syntax in
let f s = Format.asprintf "%s: %s" account_name s in
let* () =
List.fold_left
(fun a b ->
let*! a in
let*! b in
Assert.join_errors a b)
return_unit
[
Assert.equal
~loc
Tez.equal
(f "Liquid balances do not match")
Tez.pp
a_liquid_b
b_liquid_b;
Assert.equal
~loc
Tez.equal
(f "Bonds balances do not match")
Tez.pp
a_bonds_b
b_bonds_b;
Assert.equal
~loc
Tez.equal
(f "Staked balances do not match")
Tez.pp
(Partial_tez.to_tez ~round:`Down a_staked_b)
(Partial_tez.to_tez ~round:`Down b_staked_b);
Assert.equal
~loc
Tez.equal
(f "Unstaked frozen balances do not match")
Tez.pp
a_unstaked_frozen_b
b_unstaked_frozen_b;
Assert.equal
~loc
Tez.equal
(f "Unstaked finalizable balances do not match")
Tez.pp
a_unstaked_finalizable_b
b_unstaked_finalizable_b;
Assert.equal
~loc
Z.equal
(f "Staking delegator numerators do not match")
Z.pp_print
a_staking_delegator_numerator_b
b_staking_delegator_numerator_b;
Assert.equal
~loc
Z.equal
(f "Staking delegate denominators do not match")
Z.pp_print
a_staking_delegate_denominator_b
b_staking_delegate_denominator_b;
]
in
return_unit
let update_account ~f account_name account_map =
String.Map.update
account_name
(function
| None -> fail_account_not_found "update_account" account_name
| Some x -> Some (f x))
account_map
let balance_and_total_balance_of_account account_name account_map =
let ({
liquid_b;
bonds_b;
staked_b;
unstaked_frozen_b;
unstaked_finalizable_b;
staking_delegator_numerator_b = _;
staking_delegate_denominator_b = _;
} as balance) =
balance_of_account account_name account_map
in
( balance,
Tez.(
liquid_b +! bonds_b
+! Partial_tez.to_tez ~round:`Down staked_b
+! unstaked_frozen_b +! unstaked_finalizable_b) )
let assert_pseudotokens_consistency ~loc balance account account_name
account_map =
let open Lwt_result_syntax in
let {delegate; staking_delegator_numerator = num_pt; _} = account in
let exact_staking_balance = balance.staked_b in
match delegate with
| None -> return_unit
| Some delegate_name -> (
if account_name = delegate_name then return_unit
else
match String.Map.find delegate_name account_map with
| None ->
fail_account_not_found
"assert_pseudotokens_consistency"
delegate_name
| Some delegate_account ->
let total_co =
Frozen_tez.total_co_current_q
delegate_account.frozen_deposits.co_current
in
let den_pt = delegate_account.staking_delegate_denominator in
if Z.(equal den_pt zero) then
Assert.equal
~loc
Q.equal
(Format.asprintf
"%s : Delegate should not have external stake with a 0 \
staking denominator"
account_name)
Q.pp_print
total_co
Q.zero
else
let expected = Q.(num_pt /// den_pt * total_co) in
Assert.equal
~loc
Q.equal
(Format.asprintf
"%s : Pseudotokens do not match exact staking balance"
account_name)
Q.pp_print
exact_staking_balance
expected)
let get_balance_from_context ctxt contract =
let open Lwt_result_syntax in
let* liquid_b = Context.Contract.balance ctxt contract in
let* bonds_b = Context.Contract.frozen_bonds ctxt contract in
let* staked_b = Context.Contract.staked_balance ctxt contract in
let staked_b =
Option.value ~default:Tez.zero staked_b |> Partial_tez.of_tez
in
let* unstaked_frozen_b =
Context.Contract.unstaked_frozen_balance ctxt contract
in
let unstaked_frozen_b = Option.value ~default:Tez.zero unstaked_frozen_b in
let* unstaked_finalizable_b =
Context.Contract.unstaked_finalizable_balance ctxt contract
in
let unstaked_finalizable_b =
Option.value ~default:Tez.zero unstaked_finalizable_b
in
let* total_balance = Context.Contract.full_balance ctxt contract in
let* staking_delegator_numerator_b =
Context.Contract.staking_numerator ctxt contract
in
let*! staking_delegate_denominator_b =
match (contract : Protocol.Alpha_context.Contract.t) with
| Implicit pkh ->
let*! result = Context.Delegate.staking_denominator ctxt pkh in
Lwt.return
(match result with
| Ok v -> v
| Error _ -> Z.zero)
| Originated _ -> Lwt.return Z.zero
in
let bd =
{
liquid_b;
bonds_b;
staked_b;
unstaked_frozen_b;
unstaked_finalizable_b;
staking_delegator_numerator_b;
staking_delegate_denominator_b;
}
in
return (bd, total_balance)
let assert_balance_check ~loc ctxt account_name account_map =
let open Lwt_result_syntax in
match String.Map.find account_name account_map with
| None -> fail_account_not_found "assert_balance_check" account_name
| Some account ->
let* balance_ctxt, total_balance_ctxt =
get_balance_from_context ctxt account.contract
in
let balance, total_balance =
balance_and_total_balance_of_account account_name account_map
in
let*! r0 =
assert_pseudotokens_consistency
~loc
balance
account
account_name
account_map
in
let*! r1 = assert_balance_equal ~loc account_name balance_ctxt balance in
let*! r1 = Assert.join_errors r0 r1 in
let*! r2 =
Assert.equal
~loc
Tez.equal
(Format.asprintf "%s : Total balances do not match" account_name)
Tez.pp
total_balance_ctxt
total_balance
in
Assert.join_errors r1 r2
let log_debug_balance account_name account_map : unit =
let balance, total_balance =
balance_and_total_balance_of_account account_name account_map
in
Log.debug
"Model balance of %s:\n%aTotal balance: %a\n"
account_name
balance_pp
balance
Tez.pp
total_balance
let log_debug_rpc_balance name contract block : unit tzresult Lwt.t =
let open Lwt_result_syntax in
let* balance, total_balance = get_balance_from_context (B block) contract in
Log.debug
"RPC balance of %s:\n%aTotal balance: %a\n"
name
balance_pp
balance
Tez.pp
total_balance ;
return_unit
let log_debug_balance_update account_name old_account_map new_account_map : unit
=
let old_balance, old_total_balance =
balance_and_total_balance_of_account account_name old_account_map
in
let new_balance, new_total_balance =
balance_and_total_balance_of_account account_name new_account_map
in
Log.debug
"Balance update of %s:\n%aTotal balance: %a -> %a\n"
account_name
balance_update_pp
(old_balance, new_balance)
Tez.pp
old_total_balance
Tez.pp
new_total_balance
let current_total_frozen_deposits_with_limits account_state =
Frozen_tez.total_current_with_limits
~limit_of_staking_over_baking:
account_state.parameters.limit_of_staking_over_baking
account_state.frozen_deposits