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
open Alpha_context
type error +=
| Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate
let () =
let description =
"A contract tries to stake to its delegate while having unstake requests \
to a previous delegate that cannot be finalized yet. Try again in a later \
cycle (no more than preserved_cycles + max_slashing_period)."
in
register_error_kind
`Permanent
~id:
"operation.cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate"
~title:
"Cannot stake with unfinalizable unstake requests to another delegate"
~description
Data_encoding.unit
(function
| Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate ->
Some ()
| _ -> None)
(fun () ->
Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate)
let perform_finalizable_unstake_transfers ctxt contract finalizable =
let open Lwt_result_syntax in
List.fold_left_es
(fun (ctxt, balance_updates) (delegate, cycle, amount) ->
let+ ctxt, new_balance_updates =
Token.transfer
ctxt
(`Unstaked_frozen_deposits
(Receipt.Single (contract, delegate), cycle))
(`Contract contract)
amount
in
(ctxt, new_balance_updates @ balance_updates))
(ctxt, [])
finalizable
let finalize_unstake_and_check ~check_unfinalizable ctxt contract =
let open Lwt_result_syntax in
let*? ctxt =
Gas.consume ctxt Adaptive_issuance_costs.prepare_finalize_unstake_cost
in
let* prepared_opt = Unstake_requests.prepare_finalize_unstake ctxt contract in
match prepared_opt with
| None -> return (ctxt, [])
| Some {finalizable; unfinalizable} -> (
let* ctxt = check_unfinalizable ctxt unfinalizable in
match finalizable with
| [] -> return (ctxt, [])
| _ ->
let*? ctxt =
Gas.consume
ctxt
Adaptive_issuance_costs.finalize_unstake_and_check_cost
in
let* ctxt = Unstake_requests.update ctxt contract unfinalizable in
perform_finalizable_unstake_transfers ctxt contract finalizable)
let finalize_unstake ctxt contract =
let check_unfinalizable ctxt _unfinalizable = return ctxt in
finalize_unstake_and_check ~check_unfinalizable ctxt contract
let punish_delegate ctxt delegate level mistake ~rewarded =
let open Lwt_result_syntax in
let punish =
match mistake with
| `Double_baking -> Delegate.punish_double_baking
| `Double_attesting -> Delegate.punish_double_attesting
in
let* ctxt, {staked; unstaked} = punish ctxt delegate level in
let init_to_burn_to_reward =
let Delegate.{amount_to_burn; reward} = staked in
let giver = `Frozen_deposits (Receipt.Shared delegate) in
([(giver, amount_to_burn)], [(giver, reward)])
in
let to_burn, to_reward =
List.fold_left
(fun (to_burn, to_reward) (cycle, Delegate.{amount_to_burn; reward}) ->
let giver =
`Unstaked_frozen_deposits (Receipt.Shared delegate, cycle)
in
((giver, amount_to_burn) :: to_burn, (giver, reward) :: to_reward))
init_to_burn_to_reward
unstaked
in
let* ctxt, punish_balance_updates =
Token.transfer_n ctxt to_burn `Double_signing_punishments
in
let+ ctxt, reward_balance_updates =
Token.transfer_n ctxt to_reward (`Contract rewarded)
in
(ctxt, reward_balance_updates @ punish_balance_updates)
let stake ctxt ~sender ~delegate amount =
let open Lwt_result_syntax in
let check_unfinalizable ctxt
Unstake_requests.{delegate = unstake_delegate; requests} =
match requests with
| [] -> return ctxt
| _ :: _ ->
if Signature.Public_key_hash.(delegate <> unstake_delegate) then
tzfail
Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate
else return ctxt
in
let sender_contract = Contract.Implicit sender in
let* ctxt, finalize_balance_updates =
finalize_unstake_and_check ~check_unfinalizable ctxt sender_contract
in
let* ctxt =
Staking_pseudotokens.stake ctxt ~contract:sender_contract ~delegate amount
in
let+ ctxt, stake_balance_updates =
Token.transfer
ctxt
(`Contract sender_contract)
(`Frozen_deposits (Receipt.Single (sender_contract, delegate)))
amount
in
(ctxt, stake_balance_updates @ finalize_balance_updates)
let request_unstake ctxt ~sender_contract ~delegate requested_amount =
let open Lwt_result_syntax in
let* ctxt, tez_to_unstake =
Staking_pseudotokens.request_unstake
ctxt
~contract:sender_contract
~delegate
requested_amount
in
if Tez.(tez_to_unstake = zero) then return (ctxt, [])
else
let*? ctxt =
Gas.consume ctxt Adaptive_issuance_costs.request_unstake_cost
in
let current_cycle = (Level.current ctxt).cycle in
let* ctxt, balance_updates =
Token.transfer
ctxt
(`Frozen_deposits (Receipt.Single (sender_contract, delegate)))
(`Unstaked_frozen_deposits
(Receipt.Single (sender_contract, delegate), current_cycle))
tez_to_unstake
in
let* ctxt, finalize_balance_updates =
finalize_unstake ctxt sender_contract
in
let+ ctxt =
Unstake_requests.add
ctxt
~contract:sender_contract
~delegate
current_cycle
tez_to_unstake
in
(ctxt, balance_updates @ finalize_balance_updates)