Ton
414 строк · 10.4 Кб
1;;
2;; Stake Sending
3;;
4
5() op_controller_stake_send(int value, slice in_msg) impure {
6
7;; Parse message
8var stake = in_msg~load_coins();
9var validator_pubkey = in_msg~load_uint(256);
10var stake_at = in_msg~load_uint(32);
11var max_factor = in_msg~load_uint(32);
12var adnl_addr = in_msg~load_uint(256);
13var signature_ref = in_msg~load_ref();
14var signature = signature_ref.begin_parse().preload_bits(512);
15in_msg.end_parse();
16
17;; Check message value
18throw_unless(error::invalid_message(), value >= fees::stake_fees());
19
20;; Allow only single request to elector
21if (proxy_stored_query_id != 0) {
22throw(error::invalid_message());
23}
24
25;; Allow update only for current stake
26if ((proxy_stake_at != 0) & (proxy_stake_at != stake_at)) {
27throw(error::invalid_message());
28}
29
30;; Check stake value
31var availableStake = available_to_stake();
32throw_unless(error::invalid_stake_value(), availableStake >= stake);
33
34;; Parameters
35var (electedFor, stakeHeldFor) = get_stake_parameters();
36
37;; Lock stakes
38on_locked();
39
40;; Update operation state
41proxy_stake_at = stake_at;
42proxy_stake_until = stake_at + electedFor + stakeHeldFor;
43proxy_stake_sent = proxy_stake_sent + stake;
44proxy_stored_query_id = ctx_query_id;
45proxy_stored_query_op = elector::stake::request();
46proxy_stored_query_stake = stake;
47
48;; Update balances
49on_stake_sent(stake);
50
51;; Send message to elector
52send_std_message(
53ctx_proxy,
54stake + coins::1(),
55send_mode::separate_gas(),
56elector::stake::request(),
57proxy_stored_query_id,
58begin_cell()
59.store_uint(validator_pubkey, 256)
60.store_uint(stake_at, 32)
61.store_uint(max_factor, 32)
62.store_uint(adnl_addr, 256)
63.store_ref(signature_ref)
64);
65
66;; Persist
67store_validator_data();
68store_base_data();
69}
70
71() op_elector_stake_response(int value, slice in_msg) impure {
72
73;; Check response
74if (ctx_query_id != proxy_stored_query_id) {
75;; How to handle invalid? How it is possible?
76return ();
77}
78if (proxy_stored_query_op != elector::stake::request()) {
79;; How to handle invalid? How it is possible?
80return ();
81}
82
83;; Reset active query
84proxy_stored_query_id = 0;
85proxy_stored_query_op = 0;
86proxy_stored_query_stake = 0;
87
88;; Persist
89store_validator_data();
90store_base_data();
91}
92
93() op_elector_stake_response_fail(int value, slice in_msg) impure {
94
95;; Load reason
96var reason = in_msg~load_uint(32);
97
98;; Check response
99if (ctx_query_id != proxy_stored_query_id) {
100;; How to handle invalid? How it is possible?
101return ();
102}
103if (proxy_stored_query_op != elector::stake::request()) {
104;; How to handle invalid? How it is possible?
105return ();
106}
107
108;; Update balances
109on_stake_sent_failed(proxy_stored_query_stake);
110
111;; Update proxy state
112proxy_stake_sent = proxy_stake_sent - proxy_stored_query_stake;
113
114;; Reset stake at since sent stake became zero
115if (proxy_stake_sent == 0) {
116proxy_stake_at = 0;
117proxy_stake_until = 0;
118proxy_stake_sent = 0;
119on_unlocked();
120}
121
122;; Reset query
123proxy_stored_query_id = 0;
124proxy_stored_query_op = 0;
125proxy_stored_query_stake = 0;
126
127;; Persist
128store_validator_data();
129store_base_data();
130}
131
132;;
133;; Recover
134;;
135
136() op_stake_recover(int value) impure {
137
138;; NOTE: We never block stake recover operation
139;; in case of misbehaviour of something anyone always can get
140;; coins from elector after lockup period is up
141
142;; Allow request only if stake is exited lockup period
143if ((proxy_stake_until != 0) & (now() < proxy_stake_until)) {
144throw(error::invalid_message());
145}
146
147;; Double check that validation session and lockup was lifted
148if ((proxy_stake_until != 0) & (proxy_stake_at != 0)) {
149throw_unless(error::invalid_message(), lockup_lift_time(proxy_stake_at, proxy_stake_until) <= now());
150}
151
152;; Check value
153throw_unless(error::too_low_value(), value >= fees::stake_fees());
154
155;; Send message to elector
156send_empty_std_message(
157ctx_proxy,
1580,
159send_mode::carry_remaining_value(),
160elector::refund::request(),
161proxy_stored_query_id
162);
163
164;; Persist
165store_validator_data();
166store_base_data();
167}
168
169() op_elector_recover_response(int value, slice in_msg) impure {
170
171if ((proxy_stake_until != 0) & (now() > proxy_stake_until)) {
172
173;; Reset state: all stake is returned
174proxy_stake_sent = 0;
175proxy_stake_at = 0;
176proxy_stake_until = 0;
177
178;; Reset query too
179proxy_stored_query_id = 0;
180proxy_stored_query_op = 0;
181proxy_stored_query_stake = 0;
182
183;; Handle stake recovered event
184;; NOTE: Any stake recovery outside this condition might be just a noise and
185;; effect of various race condirtions that doesn't carry any substantianal vakue
186on_stake_recovered(value - fees::stake_fees());
187
188;; Reset lock state
189;; NOTE: MUST be after on_stake_recovered since it adjusts withdrawals and
190;; modifies global balance
191on_unlocked();
192}
193
194;; Persist
195store_validator_data();
196store_base_data();
197}
198
199;;
200;; Withdraw unowned
201;;
202
203() op_controller_withdraw_unowned(int value, slice in_msg) impure {
204
205;; Reserve owned
206raw_reserve(owned_balance(), 0);
207
208;; Send message to controller
209send_empty_std_message(
210ctx_controller,
2110,
212send_mode::carry_remaining_balance(),
213op::withdraw_unowned::response(),
214ctx_query_id
215);
216}
217
218;;
219;; Process pending
220;;
221
222() op_controller_accept_stakes(int value, slice in_msg) impure {
223
224;; Check if enought value
225throw_unless(error::invalid_message(), value >= params::pending_op());
226
227;; Check if not locked
228throw_if(error::invalid_message(), ctx_locked);
229
230;; Parse message
231var members = in_msg~load_dict();
232in_msg.end_parse();
233
234;; Process operations
235var member = -1;
236do {
237(member, var cs, var f) = members.udict_get_next?(256, member);
238if (f) {
239;; Accept member's stake
240load_member(member);
241member_accept_stake();
242store_member();
243}
244} until (~ f);
245
246;; Persist
247store_base_data();
248}
249
250() op_controller_accept_withdraws(int value, slice in_msg) impure {
251
252;; Check if enought value
253throw_unless(error::invalid_message(), value >= params::pending_op());
254
255;; Check if not locked
256throw_if(error::invalid_message(), ctx_locked);
257
258;; Parse message
259var members = in_msg~load_dict();
260in_msg.end_parse();
261
262;; Process operations
263var member = -1;
264do {
265(member, var cs, var f) = members.udict_get_next?(256, member);
266if (f) {
267;; Accept member's stake
268load_member(member);
269member_accept_withdraw();
270store_member();
271}
272} until (~ f);
273
274;; Persist
275store_base_data();
276}
277
278() op_controller_force_kick(int value, slice in_msg) impure {
279
280;; Check if enought value
281throw_unless(error::invalid_message(), value >= params::pending_op());
282
283;; Check if not locked
284throw_if(error::invalid_message(), ctx_locked);
285
286;; Parse message
287var members = in_msg~load_dict();
288in_msg.end_parse();
289
290;; Process operations
291var member = -1;
292do {
293(member, var cs, var f) = members.udict_get_next?(256, member);
294if (f) {
295
296;; Reject owner kicking
297throw_if(error::invalid_message(), member == owner_id());
298
299;; Kick member from a pool
300load_member(member);
301
302;; Withdraw everything
303var (withdrawed, all) = member_stake_withdraw(0);
304throw_unless(error::invalid_message(), withdrawed > 0);
305throw_unless(error::invalid_message(), all);
306
307;; Forced kick
308send_empty_std_message(
309serialize_work_addr(member),
310withdrawed,
311send_mode::default(),
312op::force_kick::notification(),
313ctx_query_id
314);
315
316;; Persist membership
317store_member();
318}
319} until (~ f);
320
321;; Persist
322store_base_data();
323}
324
325;;
326;; Top Level
327;;
328
329() op_controller(int flags, int value, slice in_msg) impure {
330if (flags & 1) {
331return ();
332}
333
334;; Check value
335throw_unless(error::invalid_message(), value >= params::min_op());
336
337;; Parse operation
338int op = in_msg~load_uint(32);
339int query_id = in_msg~load_uint(64);
340int gas_limit = in_msg~load_coins();
341set_gas_limit(gas_limit);
342ctx_query_id = query_id;
343throw_unless(error::invalid_message(), ctx_query_id > 0);
344
345;; Send stake
346if (op == op::stake_send()) {
347op_controller_stake_send(value, in_msg);
348return ();
349}
350
351;; Recover stake
352if (op == op::stake_recover()) {
353op_stake_recover(value);
354return ();
355}
356
357;; Withdraw unowned
358if (op == op::withdraw_unowned()) {
359op_controller_withdraw_unowned(value, in_msg);
360return ();
361}
362
363;; Accept stakes
364if (op == op::accept_stakes()) {
365op_controller_accept_stakes(value, in_msg);
366return ();
367}
368
369;; Accept withdraws
370if (op == op::accept_withdraws()) {
371op_controller_accept_withdraws(value, in_msg);
372return ();
373}
374
375;; Kick from pool
376if (op == op::force_kick()) {
377op_controller_force_kick(value, in_msg);
378return ();
379}
380
381;; Unknown message
382throw(error::invalid_message());
383}
384
385() op_elector(int flags, int value, slice in_msg) impure {
386int op = in_msg~load_uint(32);
387int query_id = in_msg~load_uint(64);
388ctx_query_id = query_id;
389
390;; Bounced
391;; It seems that handling doesn't make sence sicne there are no throws (?)
392;; in elector contract
393if (flags & 1) {
394return ();
395}
396
397;; Stake response
398if (op == elector::stake::response()) {
399op_elector_stake_response(value, in_msg);
400return ();
401}
402if (op == elector::stake::response::fail()) {
403op_elector_stake_response_fail(value, in_msg);
404return ();
405}
406
407;; Refund response
408if (op == elector::refund::response()) {
409op_elector_recover_response(value, in_msg);
410return ();
411}
412
413;; Ignore
414}