Ton
643 строки · 23.2 Кб
1;; Simple configuration smart contract
2
3#include "stdlib.fc";
4
5() set_conf_param(int index, cell value) impure {
6var cs = get_data().begin_parse();
7var cfg_dict = cs~load_ref();
8cfg_dict~idict_set_ref(32, index, value);
9set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell());
10}
11
12(cell, int, int, cell) load_data() inline {
13var cs = get_data().begin_parse();
14var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict());
15cs.end_parse();
16return res;
17}
18
19() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline {
20set_data(begin_cell()
21.store_ref(cfg_dict)
22.store_uint(stored_seqno, 32)
23.store_uint(public_key, 256)
24.store_dict(vote_dict)
25.end_cell());
26}
27
28;; (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price)
29_ parse_vote_config(cell c) inline {
30var cs = c.begin_parse();
31throw_unless(44, cs~load_uint(8) == 0x36);
32var res = (cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
33cs.end_parse();
34return res;
35}
36
37;; cfg_vote_setup#91 normal_params:^ConfigProposalSetup critical_params:^ConfigProposalSetup = ConfigVotingSetup;
38_ get_vote_config_internal(int critical?, cell cparam11) inline_ref {
39var cs = cparam11.begin_parse();
40throw_unless(44, cs~load_uint(8) == 0x91);
41if (critical?) {
42cs~load_ref();
43}
44return parse_vote_config(cs.preload_ref());
45}
46
47_ get_vote_config(int critical?) inline {
48return get_vote_config_internal(critical?, config_param(11));
49}
50
51(int, int) check_validator_set(cell vset) {
52var cs = vset.begin_parse();
53throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only
54int utime_since = cs~load_uint(32);
55int utime_until = cs~load_uint(32);
56int total = cs~load_uint(16);
57int main = cs~load_uint(16);
58throw_unless(9, main > 0);
59throw_unless(9, total >= main);
60return (utime_since, utime_until);
61}
62
63() send_answer(addr, query_id, ans_tag, mode) impure {
64;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
65send_raw_message(begin_cell()
66.store_uint(0x18, 6)
67.store_slice(addr)
68.store_uint(0, 5 + 4 + 4 + 64 + 32 + 1 + 1)
69.store_uint(ans_tag, 32)
70.store_uint(query_id, 64)
71.end_cell(), mode);
72}
73
74() send_confirmation(addr, query_id, ans_tag) impure inline {
75return send_answer(addr, query_id, ans_tag, 64);
76}
77
78() send_error(addr, query_id, ans_tag) impure inline {
79return send_answer(addr, query_id, ans_tag, 64);
80}
81
82;; forward a message to elector smart contract to make it upgrade its code
83() change_elector_code(slice cs) impure {
84var dest_addr = config_param(1).begin_parse().preload_uint(256);
85var query_id = now();
86send_raw_message(begin_cell()
87.store_uint(0xc4ff, 17)
88.store_uint(dest_addr, 256)
89.store_grams(1 << 30) ;; ~ 1 Gram (will be returned back)
90.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
91.store_uint(0x4e436f64, 32) ;; action
92.store_uint(query_id, 64)
93.store_slice(cs)
94.end_cell(), 0);
95}
96
97() after_code_upgrade(slice param, cont old_code) impure method_id(1666) {
98}
99
100_ perform_action(cfg_dict, public_key, action, cs) inline_ref {
101if (action == 0x43665021) {
102;; change one configuration parameter
103var param_index = cs~load_int(32);
104var param_value = cs~load_ref();
105cs.end_parse();
106cfg_dict~idict_set_ref(32, param_index, param_value);
107return (cfg_dict, public_key);
108} elseif (action == 0x4e436f64) {
109;; change configuration smart contract code
110var new_code = cs~load_ref();
111set_code(new_code);
112var old_code = get_c3();
113set_c3(new_code.begin_parse().bless());
114after_code_upgrade(cs, old_code);
115throw(0);
116return (cfg_dict, public_key);
117} elseif (action == 0x50624b21) {
118;; change configuration master public key
119public_key = cs~load_uint(256);
120cs.end_parse();
121return (cfg_dict, public_key);
122} elseif (action == 0x4e43ef05) {
123;; change election smart contract code
124change_elector_code(cs);
125return (cfg_dict, public_key);
126} else {
127throw_if(32, action);
128return (cfg_dict, public_key);
129}
130}
131
132(cell, int, cell) get_current_vset() inline_ref {
133var vset = config_param(34);
134var cs = begin_parse(vset);
135;; validators_ext#12 utime_since:uint32 utime_until:uint32
136;; total:(## 16) main:(## 16) { main <= total } { main >= 1 }
137;; total_weight:uint64
138throw_unless(40, cs~load_uint(8) == 0x12);
139cs~skip_bits(32 + 32 + 16 + 16);
140var (total_weight, dict) = (cs~load_uint(64), cs~load_dict());
141cs.end_parse();
142return (vset, total_weight, dict);
143}
144
145(slice, int) get_validator_descr(int idx) inline_ref {
146var (vset, total_weight, dict) = get_current_vset();
147var (value, _) = dict.udict_get?(16, idx);
148return (value, total_weight);
149}
150
151(int, int) unpack_validator_descr(slice cs) inline {
152;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey;
153;; validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr;
154;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
155throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53);
156throw_unless(41, cs~load_uint(32) == 0x8e81278a);
157return (cs~load_uint(256), cs~load_uint(64));
158}
159
160;; cfg_proposal#f3 param_id:int32 param_value:(Maybe ^Cell) if_hash_equal:(Maybe uint256)
161;; c -> (param-id param-cell maybe-hash)
162(int, cell, int) parse_config_proposal(cell c) inline_ref {
163var cs = c.begin_parse();
164throw_unless(44, cs~load_int(8) == 0xf3 - 0x100);
165var (id, val, hash) = (cs~load_int(32), cs~load_maybe_ref(), cs~load_int(1));
166if (hash) {
167hash = cs~load_uint(256);
168} else {
169hash = -1;
170}
171cs.end_parse();
172return (id, val, hash);
173}
174
175(cell, int, cell) accept_proposal(cell cfg_dict, cell proposal, int critical?) inline_ref {
176var (param_id, param_val, req_hash) = parse_config_proposal(proposal);
177cell cur_val = cfg_dict.idict_get_ref(32, param_id);
178int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val);
179if ((cur_hash != req_hash) & (req_hash >= 0)) {
180;; current value has incorrect hash, do not apply changes
181return (cfg_dict, 0, null());
182}
183cell mparams = cfg_dict.idict_get_ref(32, 9); ;; mandatory parameters
184var (_, found?) = mparams.idict_get?(32, param_id);
185if (found? & param_val.null?()) {
186;; cannot set a mandatory parameter to (null)
187return (cfg_dict, 0, null());
188}
189cell cparams = cfg_dict.idict_get_ref(32, 10); ;; critical parameters
190(_, found?) = cparams.idict_get?(32, param_id);
191if (found? < critical?) {
192;; trying to set a critical parameter after a non-critical voting
193return (cfg_dict, 0, null());
194}
195;; CHANGE ONE CONFIGURATION PARAMETER (!)
196cfg_dict~idict_set_ref(32, param_id, param_val);
197return (cfg_dict, param_id, param_val);
198}
199
200(cell, int) perform_proposed_action(cell cfg_dict, int public_key, int param_id, cell param_val) inline_ref {
201if (param_id == -999) {
202;; appoint or depose dictator
203return (cfg_dict, param_val.null?() ? 0 : param_val.begin_parse().preload_uint(256));
204}
205if (param_val.null?()) {
206return (cfg_dict, public_key);
207}
208if (param_id == -1000) {
209;; upgrade code
210var cs = param_val.begin_parse();
211var new_code = cs~load_ref();
212set_code(new_code);
213var old_code = get_c3();
214set_c3(new_code.begin_parse().bless());
215after_code_upgrade(cs, old_code);
216throw(0);
217return (cfg_dict, public_key);
218}
219if (param_id == -1001) {
220;; update elector code
221var cs = param_val.begin_parse();
222change_elector_code(cs);
223}
224return (cfg_dict, public_key);
225}
226
227;; cfg_proposal_status#ce expires:uint32 proposal:^ConfigProposal is_critical:Bool
228;; voters:(HashmapE 16 True) remaining_weight:int64 validator_set_id:uint256
229;; rounds_remaining:uint8 wins:uint8 losses:uint8 = ConfigProposalStatus;
230(int, cell, int, cell, int, int, slice) unpack_proposal_status(slice cs) inline_ref {
231throw_unless(44, cs~load_int(8) == 0xce - 0x100);
232return (cs~load_uint(32), cs~load_ref(), cs~load_int(1), cs~load_dict(), cs~load_int(64), cs~load_uint(256), cs);
233}
234
235slice update_proposal_status(slice rest, int weight_remaining, int critical?) inline_ref {
236var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?);
237var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
238losses -= (weight_remaining >= 0);
239if (losses > max_losses) {
240;; lost too many times
241return null();
242}
243rounds_remaining -= 1;
244if (rounds_remaining < 0) {
245;; existed for too many rounds
246return null();
247}
248return begin_cell()
249.store_uint(rounds_remaining, 8)
250.store_uint(wins, 8)
251.store_uint(losses, 8)
252.end_cell().begin_parse();
253}
254
255builder begin_pack_proposal_status(int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id) inline {
256return begin_cell()
257.store_int(0xce - 0x100, 8)
258.store_uint(expires, 32)
259.store_ref(proposal)
260.store_int(critical?, 1)
261.store_dict(voters)
262.store_int(weight_remaining, 64)
263.store_uint(vset_id, 256);
264}
265
266(cell, cell, int) register_vote(vote_dict, phash, idx, weight) inline_ref {
267var (pstatus, found?) = vote_dict.udict_get?(256, phash);
268ifnot (found?) {
269;; config proposal not found
270return (vote_dict, null(), -1);
271}
272var (cur_vset, total_weight, _) = get_current_vset();
273int cur_vset_id = cur_vset.cell_hash();
274var (expires, proposal, critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
275if (expires <= now()) {
276;; config proposal expired, delete and report not found
277vote_dict~udict_delete?(256, phash);
278return (vote_dict, null(), -1);
279}
280if (vset_id != cur_vset_id) {
281;; config proposal belongs to a previous validator set
282vset_id = cur_vset_id;
283rest = update_proposal_status(rest, weight_remaining, critical?);
284voters = null();
285weight_remaining = muldiv(total_weight, 3, 4);
286}
287if (rest.null?()) {
288;; discard proposal (existed for too many rounds, or too many losses)
289vote_dict~udict_delete?(256, phash);
290return (vote_dict, null(), -1);
291}
292var (_, found?) = voters.udict_get?(16, idx);
293if (found?) {
294;; already voted for this proposal, ignore vote
295return (vote_dict, null(), -2);
296}
297;; register vote
298voters~udict_set_builder(16, idx, begin_cell().store_uint(now(), 32));
299int old_wr = weight_remaining;
300weight_remaining -= weight;
301if ((weight_remaining ^ old_wr) >= 0) {
302;; not enough votes, or proposal already accepted in this round
303;; simply update weight_remaining
304vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest));
305return (vote_dict, null(), 2);
306}
307;; proposal wins in this round
308var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?);
309var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
310wins += 1;
311if (wins >= min_wins) {
312;; proposal is accepted, remove and process
313vote_dict~udict_delete?(256, phash);
314return (vote_dict, proposal, 6 - critical?);
315}
316;; update proposal info
317vote_dict~udict_set_builder(256, phash,
318begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id)
319.store_uint(rounds_remaining, 8)
320.store_uint(wins, 8)
321.store_uint(losses, 8));
322return (vote_dict, null(), 2);
323}
324
325int proceed_register_vote(phash, idx, weight) impure inline_ref {
326var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
327(vote_dict, var accepted_proposal, var status) = register_vote(vote_dict, phash, idx, weight);
328store_data(cfg_dict, stored_seqno, public_key, vote_dict);
329ifnot (accepted_proposal.null?()) {
330var critical? = 6 - status;
331(cfg_dict, var param_id, var param_val) = accept_proposal(cfg_dict, accepted_proposal, critical?);
332store_data(cfg_dict, stored_seqno, public_key, vote_dict);
333if (param_id) {
334commit();
335(cfg_dict, public_key) = perform_proposed_action(cfg_dict, public_key, param_id, param_val);
336store_data(cfg_dict, stored_seqno, public_key, vote_dict);
337}
338}
339return status;
340}
341
342(slice, int) scan_proposal(int phash, slice pstatus) inline_ref {
343var (cur_vset, total_weight, _) = get_current_vset();
344int cur_vset_id = cur_vset.cell_hash();
345var (expires, proposal, critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
346if (expires <= now()) {
347;; config proposal expired, delete
348return (null(), true);
349}
350if (vset_id == cur_vset_id) {
351;; config proposal already processed or voted for in this round, change nothing
352return (pstatus, false);
353}
354;; config proposal belongs to a previous validator set
355vset_id = cur_vset_id;
356rest = update_proposal_status(rest, weight_remaining, critical?);
357voters = null();
358weight_remaining = muldiv(total_weight, 3, 4);
359if (rest.null?()) {
360;; discard proposal (existed for too many rounds, or too many losses)
361return (null(), true);
362}
363;; return updated proposal
364return (begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest).end_cell().begin_parse(), true);
365}
366
367cell scan_random_proposal(cell vote_dict) inline_ref {
368var (phash, pstatus, found?) = vote_dict.udict_get_nexteq?(256, random());
369ifnot (found?) {
370return vote_dict;
371}
372(pstatus, var changed?) = scan_proposal(phash, pstatus);
373if (changed?) {
374if (pstatus.null?()) {
375vote_dict~udict_delete?(256, phash);
376} else {
377vote_dict~udict_set(256, phash, pstatus);
378}
379}
380return vote_dict;
381}
382
383int register_voting_proposal(slice cs, int msg_value) impure inline_ref {
384var (expire_at, proposal, critical?) = (cs~load_uint(32), cs~load_ref(), cs~load_int(1));
385if (expire_at >> 30) {
386expire_at -= now();
387}
388var (param_id, param_val, hash) = parse_config_proposal(proposal);
389if (hash >= 0) {
390cell cur_val = config_param(param_id);
391int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val);
392if (cur_hash != hash) {
393hash = -0xe2646356; ;; bad current value
394}
395} else {
396var m_params = config_param(9);
397var (_, found?) = m_params.idict_get?(32, param_id);
398if (found?) {
399hash = -0xcd506e6c; ;; cannot set mandatory parameter to null
400}
401}
402if (param_val.cell_depth() >= 256) {
403hash = -0xc2616456; ;; bad value
404}
405if (hash < -1) {
406return hash; ;; return error if any
407}
408ifnot (critical?) {
409var crit_params = config_param(10);
410var (_, found?) = crit_params.idict_get?(32, param_id);
411if (found?) {
412hash = -0xc3726954; ;; trying to set a critical parameter without critical flag
413}
414}
415if (hash < -1) {
416return hash;
417}
418;; obtain vote proposal configuration
419var vote_cfg = get_vote_config(critical?);
420var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = vote_cfg;
421if (expire_at < min_store_sec) {
422return -0xc5787069; ;; expired
423}
424expire_at = min(expire_at, max_store_sec);
425;; compute price
426var (_, bits, refs) = compute_data_size(param_val, 1024);
427var pps = bit_price * (bits + 1024) + cell_price * (refs + 2);
428var price = pps * expire_at;
429expire_at += now();
430var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
431int phash = proposal.cell_hash();
432var (pstatus, found?) = vote_dict.udict_get?(256, phash);
433if (found?) {
434;; proposal already exists; we can only extend it
435var (expires, r_proposal, r_critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
436if (r_critical? != critical?) {
437return -0xc3726955; ;; cannot upgrade critical parameter to non-critical...
438}
439if (expires >= expire_at) {
440return -0xc16c7245; ;; proposal already exists
441}
442;; recompute price
443price = pps * (expire_at - expires + 16384);
444if (msg_value - price < (1 << 30)) {
445return -0xf0617924; ;; need more money
446}
447;; update expiration time
448vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expire_at, r_proposal, r_critical?, voters, weight_remaining, vset_id).store_slice(rest));
449store_data(cfg_dict, stored_seqno, public_key, vote_dict);
450return price;
451}
452if (msg_value - price < (1 << 30)) {
453return -0xf0617924; ;; need more money
454}
455;; obtain current validator set data
456var (vset, total_weight, _) = get_current_vset();
457int weight_remaining = muldiv(total_weight, 3, 4);
458;; create new proposal
459vote_dict~udict_set_builder(256, phash,
460begin_pack_proposal_status(expire_at, proposal, critical?, null(), weight_remaining, vset.cell_hash())
461.store_uint(max_tot_rounds, 8).store_uint(0, 16));
462store_data(cfg_dict, stored_seqno, public_key, vote_dict);
463return price;
464}
465
466() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
467var cs = in_msg_cell.begin_parse();
468var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
469var s_addr = cs~load_msg_addr();
470(int src_wc, int src_addr) = s_addr.parse_std_addr();
471if ((src_wc + 1) | (flags & 1) | in_msg.slice_empty?()) {
472;; source not in masterchain, or a bounced message, or a simple transfer
473return ();
474}
475int tag = in_msg~load_uint(32);
476int query_id = in_msg~load_uint(64);
477if (tag == 0x4e565354) {
478;; set next validator set
479var vset = in_msg~load_ref();
480in_msg.end_parse();
481var elector_param = config_param(1);
482var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256);
483var ok = false;
484if (src_addr == elector_addr) {
485;; message from elector smart contract
486;; set next validator set
487(var t_since, var t_until) = check_validator_set(vset);
488var t = now();
489ok = (t_since > t) & (t_until > t_since);
490}
491if (ok) {
492set_conf_param(36, vset);
493;; send confirmation
494return send_confirmation(s_addr, query_id, 0xee764f4b);
495} else {
496return send_error(s_addr, query_id, 0xee764f6f);
497}
498}
499if (tag == 0x6e565052) {
500;; new voting proposal
501var price = register_voting_proposal(in_msg, msg_value);
502int mode = 64;
503int ans_tag = - price;
504if (price >= 0) {
505;; ok, debit price
506raw_reserve(price, 4);
507ans_tag = 0xee565052;
508mode = 128;
509}
510return send_answer(s_addr, query_id, ans_tag, mode);
511}
512if (tag == 0x566f7465) {
513;; vote for a configuration proposal
514var signature = in_msg~load_bits(512);
515var msg_body = in_msg;
516var (sign_tag, idx, phash) = (in_msg~load_uint(32), in_msg~load_uint(16), in_msg~load_uint(256));
517in_msg.end_parse();
518throw_unless(37, sign_tag == 0x566f7445);
519var (vdescr, total_weight) = get_validator_descr(idx);
520var (val_pubkey, weight) = unpack_validator_descr(vdescr);
521throw_unless(34, check_data_signature(msg_body, signature, val_pubkey));
522int res = proceed_register_vote(phash, idx, weight);
523return send_confirmation(s_addr, query_id, res + 0xd6745240);
524}
525;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query)
526;; to bounce message back to sender
527throw_unless(37, (tag == 0) | (tag & (1 << 31)));
528;; do nothing for other internal messages
529}
530
531() recv_external(slice in_msg) impure {
532var signature = in_msg~load_bits(512);
533var cs = in_msg;
534int action = cs~load_uint(32);
535int msg_seqno = cs~load_uint(32);
536var valid_until = cs~load_uint(32);
537throw_if(35, valid_until < now());
538throw_if(39, slice_depth(cs) > 128);
539var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
540throw_unless(33, msg_seqno == stored_seqno);
541if (action == 0x566f7465) {
542;; vote for a configuration proposal
543var (idx, phash) = (cs~load_uint(16), cs~load_uint(256));
544cs.end_parse();
545var (vdescr, total_weight) = get_validator_descr(idx);
546var (val_pubkey, weight) = unpack_validator_descr(vdescr);
547throw_unless(34, check_data_signature(in_msg, signature, val_pubkey));
548accept_message();
549stored_seqno = (stored_seqno + 1) % (1 << 32);
550store_data(cfg_dict, stored_seqno, public_key, vote_dict);
551commit();
552proceed_register_vote(phash, idx, weight);
553return ();
554}
555throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
556accept_message();
557stored_seqno = (stored_seqno + 1) % (1 << 32);
558store_data(cfg_dict, stored_seqno, public_key, vote_dict);
559commit();
560(cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs);
561store_data(cfg_dict, stored_seqno, public_key, vote_dict);
562}
563
564() run_ticktock(int is_tock) impure {
565var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
566int kl = 32;
567var next_vset = cfg_dict.idict_get_ref(kl, 36);
568var updated? = false;
569ifnot (next_vset.null?()) {
570;; check whether we have to set next_vset as the current validator set
571var ds = next_vset.begin_parse();
572if (ds.slice_bits() >= 40) {
573var tag = ds~load_uint(8);
574var since = ds.preload_uint(32);
575if ((since <= now()) & (tag == 0x12)) {
576;; next validator set becomes active!
577var cur_vset = cfg_dict~idict_set_get_ref(kl, 34, next_vset); ;; next_vset -> cur_vset
578cfg_dict~idict_set_get_ref(kl, 32, cur_vset); ;; cur_vset -> prev_vset
579cfg_dict~idict_delete?(kl, 36); ;; (null) -> next_vset
580updated? = true;
581}
582}
583}
584ifnot (updated?) {
585;; if nothing has been done so far, scan a random voting proposal instead
586vote_dict = scan_random_proposal(vote_dict);
587}
588;; save data and return
589return store_data(cfg_dict, stored_seqno, public_key, vote_dict);
590}
591
592int seqno() method_id {
593return get_data().begin_parse().preload_uint(32);
594}
595
596_ unpack_proposal(slice pstatus) inline_ref {
597(int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id, slice rest) = unpack_proposal_status(pstatus);
598var voters_list = null();
599var voter_id = (1 << 32);
600do {
601(voter_id, _, var f) = voters.udict_get_prev?(16, voter_id);
602if (f) {
603voters_list = cons(voter_id, voters_list);
604}
605} until (~ f);
606var (rounds_remaining, losses, wins) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
607rest.end_parse();
608var (param_id, param_val, param_hash) = parse_config_proposal(proposal);
609return [expires, critical?, [param_id, param_val, param_hash], vset_id, voters_list, weight_remaining, rounds_remaining, losses, wins];
610}
611
612_ get_proposal(int phash) method_id {
613(_, _, _, var vote_dict) = load_data();
614var (pstatus, found?) = vote_dict.udict_get?(256, phash);
615ifnot (found?) {
616return null();
617}
618return unpack_proposal(pstatus);
619}
620
621_ list_proposals() method_id {
622(_, _, _, var vote_dict) = load_data();
623var phash = (1 << 255) + ((1 << 255) - 1);
624var list = null();
625do {
626(phash, var pstatus, var f) = vote_dict.udict_get_prev?(256, phash);
627if (f) {
628list = cons([phash, unpack_proposal(pstatus)], list);
629}
630} until (~ f);
631return list;
632}
633
634_ proposal_storage_price(int critical?, int seconds, int bits, int refs) method_id {
635var cfg_dict = get_data().begin_parse().preload_ref();
636var cparam11 = cfg_dict.idict_get_ref(32, 11);
637var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = get_vote_config_internal(critical?, cparam11);
638if (seconds < min_store_sec) {
639return -1;
640}
641seconds = min(seconds, max_store_sec);
642return (bit_price * (bits + 1024) + cell_price * (refs + 2)) * seconds;
643}
644