Ton

Форк
0
/
config-code.fc 
646 строк · 23.5 Кб
1
;; Simple configuration smart contract
2
;; Currently deployed config-contract in mainnet can be found
3
;; on https://verifier.ton.org/Ef9VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVbxn
4

5
() set_conf_param(int index, cell value) impure {
6
  var cs = get_data().begin_parse();
7
  var cfg_dict = cs~load_ref();
8
  cfg_dict~idict_set_ref(32, index, value);
9
  set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell());
10
}
11

12
(cell, int, int, cell) load_data() inline {
13
  var cs = get_data().begin_parse();
14
  var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict());
15
  cs.end_parse();
16
  return res;
17
}
18

19
() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline {
20
  set_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 {
30
  var cs = c.begin_parse();
31
  throw_unless(44, cs~load_uint(8) == 0x36);
32
  var 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));
33
  cs.end_parse();
34
  return 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 {
39
  var cs = cparam11.begin_parse();
40
  throw_unless(44, cs~load_uint(8) == 0x91);
41
  if (critical?) {
42
    cs~load_ref();
43
  }
44
  return parse_vote_config(cs.preload_ref());
45
}
46

47
_ get_vote_config(int critical?) inline {
48
  return get_vote_config_internal(critical?, config_param(11));
49
}
50

51
(int, int) check_validator_set(cell vset) {
52
  var cs = vset.begin_parse();
53
  throw_unless(9, cs~load_uint(8) == 0x12);  ;; validators_ext#12 only
54
  int utime_since = cs~load_uint(32);
55
  int utime_until = cs~load_uint(32);
56
  int total = cs~load_uint(16);
57
  int main = cs~load_uint(16);
58
  throw_unless(9, main > 0);
59
  throw_unless(9, total >= main);
60
  return (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
65
  send_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 {
75
  return send_answer(addr, query_id, ans_tag, 64);
76
}
77

78
() send_error(addr, query_id, ans_tag) impure inline {
79
  return 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 {
84
  var dest_addr = config_param(1).begin_parse().preload_uint(256);
85
  var query_id = now();
86
  send_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 {
101
  if (action == 0x43665021) {
102
    ;; change one configuration parameter
103
    var param_index = cs~load_int(32);
104
    var param_value = cs~load_ref();
105
    cs.end_parse();
106
    cfg_dict~idict_set_ref(32, param_index, param_value);
107
    return (cfg_dict, public_key);
108
  } elseif (action == 0x4e436f64) {
109
    ;; change configuration smart contract code
110
    var new_code = cs~load_ref();
111
    set_code(new_code);
112
    var old_code = get_c3();
113
    set_c3(new_code.begin_parse().bless());
114
    after_code_upgrade(cs, old_code);
115
    throw(0);
116
    return (cfg_dict, public_key);
117
  } elseif (action == 0x50624b21) {
118
    ;; change configuration master public key
119
    public_key = cs~load_uint(256);
120
    cs.end_parse();
121
    return (cfg_dict, public_key);
122
  } elseif (action == 0x4e43ef05) {
123
    ;; change election smart contract code
124
    change_elector_code(cs);
125
    return (cfg_dict, public_key);
126
  } else {
127
    throw_if(32, action);
128
    return (cfg_dict, public_key);
129
  }
130
}
131

132
(cell, int, cell) get_current_vset() inline_ref {
133
  var vset = config_param(34);
134
  var 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
138
  throw_unless(40, cs~load_uint(8) == 0x12);
139
  cs~skip_bits(32 + 32 + 16 + 16);
140
  var (total_weight, dict) = (cs~load_uint(64), cs~load_dict());
141
  cs.end_parse();
142
  return (vset, total_weight, dict);
143
}
144

145
(slice, int) get_validator_descr(int idx) inline_ref {
146
  var (vset, total_weight, dict) = get_current_vset();
147
  var (value, _) = dict.udict_get?(16, idx);
148
  return (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;
155
  throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53);
156
  throw_unless(41, cs~load_uint(32) == 0x8e81278a);
157
  return (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 {
163
  var cs = c.begin_parse();
164
  throw_unless(44, cs~load_int(8) == 0xf3 - 0x100);
165
  var (id, val, hash) = (cs~load_int(32), cs~load_maybe_ref(), cs~load_int(1));
166
  if (hash) {
167
    hash = cs~load_uint(256);
168
  } else {
169
    hash = -1;
170
  }
171
  cs.end_parse();
172
  return (id, val, hash);
173
}
174

175
(cell, int, cell) accept_proposal(cell cfg_dict, cell proposal, int critical?) inline_ref {
176
  var (param_id, param_val, req_hash) = parse_config_proposal(proposal);
177
  cell cur_val = cfg_dict.idict_get_ref(32, param_id);
178
  int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val);
179
  if ((cur_hash != req_hash) & (req_hash >= 0)) {
180
    ;; current value has incorrect hash, do not apply changes
181
    return (cfg_dict, 0, null());
182
  }
183
  cell mparams = cfg_dict.idict_get_ref(32, 9);  ;; mandatory parameters
184
  var (_, found?) = mparams.idict_get?(32, param_id);
185
  if (found? & param_val.null?()) {
186
    ;; cannot set a mandatory parameter to (null)
187
    return (cfg_dict, 0, null());
188
  }
189
  cell cparams = cfg_dict.idict_get_ref(32, 10);  ;; critical parameters
190
  (_, found?) = cparams.idict_get?(32, param_id);
191
  if (found? < critical?) {
192
    ;; trying to set a critical parameter after a non-critical voting
193
    return (cfg_dict, 0, null());
194
  }
195
  ;; CHANGE ONE CONFIGURATION PARAMETER (!)
196
  cfg_dict~idict_set_ref(32, param_id, param_val);
197
  return (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 {
201
  if (param_id == -999) {
202
    ;; appoint or depose dictator
203
    return (cfg_dict, param_val.null?() ? 0 : param_val.begin_parse().preload_uint(256));
204
  }
205
  if (param_val.null?()) {
206
    return (cfg_dict, public_key);
207
  }
208
  if (param_id == -1000) {
209
    ;; upgrade code
210
    var cs = param_val.begin_parse();
211
    var new_code = cs~load_ref();
212
    set_code(new_code);
213
    var old_code = get_c3();
214
    set_c3(new_code.begin_parse().bless());
215
    after_code_upgrade(cs, old_code);
216
    throw(0);
217
    return (cfg_dict, public_key);
218
  }
219
  if (param_id == -1001) {
220
    ;; update elector code
221
    var cs = param_val.begin_parse();
222
    change_elector_code(cs);
223
  }
224
  return (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 {
231
  throw_unless(44, cs~load_int(8) == 0xce - 0x100);
232
  return (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

235
slice update_proposal_status(slice rest, int weight_remaining, int critical?) inline_ref {
236
  var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?);
237
  var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
238
  losses -= (weight_remaining >= 0);
239
  if (losses > max_losses) {
240
    ;; lost too many times
241
    return null();
242
  }
243
  rounds_remaining -= 1;
244
  if (rounds_remaining < 0) {
245
    ;; existed for too many rounds
246
    return null();
247
  }
248
  return 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

255
builder begin_pack_proposal_status(int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id) inline {
256
  return 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 {
267
  var (pstatus, found?) = vote_dict.udict_get?(256, phash);
268
  ifnot (found?) {
269
    ;; config proposal not found
270
    return (vote_dict, null(), -1);
271
  }
272
  var (cur_vset, total_weight, _) = get_current_vset();
273
  int cur_vset_id = cur_vset.cell_hash();
274
  var (expires, proposal, critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
275
  if (expires <= now()) {
276
    ;; config proposal expired, delete and report not found
277
    vote_dict~udict_delete?(256, phash);
278
    return (vote_dict, null(), -1);
279
  }
280
  if (vset_id != cur_vset_id) {
281
    ;; config proposal belongs to a previous validator set
282
    vset_id = cur_vset_id;
283
    rest = update_proposal_status(rest, weight_remaining, critical?);
284
    voters = null();
285
    weight_remaining = muldiv(total_weight, 3, 4);
286
  }
287
  if (rest.null?()) {
288
    ;; discard proposal (existed for too many rounds, or too many losses)
289
    vote_dict~udict_delete?(256, phash);
290
    return (vote_dict, null(), -1);
291
  }
292
  var (_, found?) = voters.udict_get?(16, idx);
293
  if (found?) {
294
    ;; already voted for this proposal, ignore vote
295
    return (vote_dict, null(), -2);
296
  }
297
  ;; register vote
298
  voters~udict_set_builder(16, idx, begin_cell().store_uint(now(), 32));
299
  int old_wr = weight_remaining;
300
  weight_remaining -= weight;
301
  if ((weight_remaining ^ old_wr) >= 0) {
302
    ;; not enough votes, or proposal already accepted in this round
303
    ;; simply update weight_remaining
304
    vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest));
305
    return (vote_dict, null(), 2);
306
  }
307
  ;; proposal wins in this round
308
  var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?);
309
  var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
310
  wins += 1;
311
  if (wins >= min_wins) {
312
    ;; proposal is accepted, remove and process
313
    vote_dict~udict_delete?(256, phash);
314
    return (vote_dict, proposal, 6 - critical?);
315
  }
316
  ;; update proposal info
317
  vote_dict~udict_set_builder(256, phash,
318
    begin_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));
322
  return (vote_dict, null(), 2);
323
}
324

325
int proceed_register_vote(phash, idx, weight) impure inline_ref {
326
  var (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);
328
  store_data(cfg_dict, stored_seqno, public_key, vote_dict);
329
  ifnot (accepted_proposal.null?()) {
330
    var critical? = 6 - status;
331
    (cfg_dict, var param_id, var param_val) = accept_proposal(cfg_dict, accepted_proposal, critical?);
332
    store_data(cfg_dict, stored_seqno, public_key, vote_dict);
333
    if (param_id) {
334
      commit();
335
      (cfg_dict, public_key) = perform_proposed_action(cfg_dict, public_key, param_id, param_val);
336
      store_data(cfg_dict, stored_seqno, public_key, vote_dict);
337
    }
338
  }
339
  return status;
340
}
341

342
(slice, int) scan_proposal(int phash, slice pstatus) inline_ref {
343
  var (cur_vset, total_weight, _) = get_current_vset();
344
  int cur_vset_id = cur_vset.cell_hash();
345
  var (expires, proposal, critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
346
  if (expires <= now()) {
347
    ;; config proposal expired, delete
348
    return (null(), true);
349
  }
350
  if (vset_id == cur_vset_id) {
351
    ;; config proposal already processed or voted for in this round, change nothing
352
    return (pstatus, false);
353
  }
354
  ;; config proposal belongs to a previous validator set
355
  vset_id = cur_vset_id;
356
  rest = update_proposal_status(rest, weight_remaining, critical?);
357
  voters = null();
358
  weight_remaining = muldiv(total_weight, 3, 4);
359
  if (rest.null?()) {
360
    ;; discard proposal (existed for too many rounds, or too many losses)
361
    return (null(), true);
362
  }
363
  ;; return updated proposal
364
  return (begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest).end_cell().begin_parse(), true);
365
}
366

367
cell scan_random_proposal(cell vote_dict) inline_ref {
368
  var (phash, pstatus, found?) = vote_dict.udict_get_nexteq?(256, random());
369
  ifnot (found?) {
370
    return vote_dict;
371
  }
372
  (pstatus, var changed?) = scan_proposal(phash, pstatus);
373
  if (changed?) {
374
    if (pstatus.null?()) {
375
      vote_dict~udict_delete?(256, phash);
376
    } else {
377
      vote_dict~udict_set(256, phash, pstatus);
378
    }
379
  }
380
  return vote_dict;
381
}
382

383
int register_voting_proposal(slice cs, int msg_value) impure inline_ref {
384
  var (expire_at, proposal, critical?) = (cs~load_uint(32), cs~load_ref(), cs~load_int(1));
385
  if (expire_at >> 30) {
386
    expire_at -= now();
387
  }
388
  var (param_id, param_val, hash) = parse_config_proposal(proposal);
389
  if (hash >= 0) {
390
    cell cur_val = config_param(param_id);
391
    int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val);
392
    if (cur_hash != hash) {
393
      hash = -0xe2646356;  ;; bad current value
394
    }
395
  } else {
396
    var m_params = config_param(9);
397
    var (_, found?) = m_params.idict_get?(32, param_id);
398
    if (found?) {
399
      hash = -0xcd506e6c;  ;; cannot set mandatory parameter to null
400
    }
401
  }
402
  ;; Note, in config contract currently deployed in mainnet, this limit is 256
403
  if (param_val.cell_depth() >= 128) {
404
    hash = -0xc2616456;  ;; bad value
405
  }
406
  if (hash < -1) {
407
    return hash;  ;; return error if any
408
  }
409
  ifnot (critical?) {
410
    var crit_params = config_param(10);
411
    var (_, found?) = crit_params.idict_get?(32, param_id);
412
    if (found?) {
413
      hash = -0xc3726954;  ;; trying to set a critical parameter without critical flag
414
    }
415
  }
416
  if (hash < -1) {
417
    return hash;
418
  }
419
  ;; obtain vote proposal configuration
420
  var vote_cfg = get_vote_config(critical?);
421
  var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = vote_cfg;
422
  if (expire_at < min_store_sec) {
423
    return -0xc5787069;   ;; expired
424
  }
425
  expire_at = min(expire_at, max_store_sec);
426
  ;; compute price
427
  var (_, bits, refs) = compute_data_size(param_val, 1024);
428
  var pps = bit_price * (bits + 1024) + cell_price * (refs + 2);
429
  var price = pps * expire_at;
430
  expire_at += now();
431
  var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
432
  int phash = proposal.cell_hash();
433
  var (pstatus, found?) = vote_dict.udict_get?(256, phash);
434
  if (found?) {
435
    ;; proposal already exists; we can only extend it
436
    var (expires, r_proposal, r_critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
437
    if (r_critical? != critical?) {
438
      return -0xc3726955;  ;; cannot upgrade critical parameter to non-critical...
439
    }
440
    if (expires >= expire_at) {
441
      return -0xc16c7245;  ;; proposal already exists
442
    }
443
    ;; recompute price
444
    price = pps * (expire_at - expires + 16384);
445
    if (msg_value - price < (1 << 30)) {
446
      return -0xf0617924;   ;; need more money
447
    }
448
    ;; update expiration time
449
    vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expire_at, r_proposal, r_critical?, voters, weight_remaining, vset_id).store_slice(rest));
450
    store_data(cfg_dict, stored_seqno, public_key, vote_dict);
451
    return price;
452
  }
453
  if (msg_value - price < (1 << 30)) {
454
    return -0xf0617924;   ;; need more money
455
  }
456
  ;; obtain current validator set data
457
  var (vset, total_weight, _) = get_current_vset();
458
  int weight_remaining = muldiv(total_weight, 3, 4);
459
  ;; create new proposal
460
  vote_dict~udict_set_builder(256, phash,
461
    begin_pack_proposal_status(expire_at, proposal, critical?, null(), weight_remaining, vset.cell_hash())
462
    .store_uint(max_tot_rounds, 8).store_uint(0, 16));
463
  store_data(cfg_dict, stored_seqno, public_key, vote_dict);
464
  return price;
465
}
466

467
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
468
  var cs = in_msg_cell.begin_parse();
469
  var flags = cs~load_uint(4);  ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
470
  var s_addr = cs~load_msg_addr();
471
  (int src_wc, int src_addr) = s_addr.parse_std_addr();
472
  if ((src_wc + 1) | (flags & 1) | in_msg.slice_empty?()) {
473
    ;; source not in masterchain, or a bounced message, or a simple transfer
474
    return ();
475
  }
476
  int tag = in_msg~load_uint(32);
477
  int query_id = in_msg~load_uint(64);
478
  if (tag == 0x4e565354) {
479
    ;; set next validator set
480
    var vset = in_msg~load_ref();
481
    in_msg.end_parse();
482
    var elector_param = config_param(1);
483
    var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256);
484
    var ok = false;
485
    if (src_addr == elector_addr) {
486
      ;; message from elector smart contract
487
      ;; set next validator set
488
      (var t_since, var t_until) = check_validator_set(vset);
489
      var t = now();
490
      ok = (t_since > t) & (t_until > t_since);
491
    }
492
    if (ok) {
493
      set_conf_param(36, vset);
494
      ;; send confirmation
495
      return send_confirmation(s_addr, query_id, 0xee764f4b);
496
    } else {
497
      return send_error(s_addr, query_id, 0xee764f6f);
498
    }
499
  }
500
  if (tag == 0x6e565052) {
501
    ;; new voting proposal
502
    var price = register_voting_proposal(in_msg, msg_value);
503
    int mode = 64;
504
    int ans_tag = - price;
505
    if (price >= 0) {
506
      ;; ok, debit price
507
      raw_reserve(price, 4);
508
      ans_tag = 0xee565052;
509
      mode = 128;
510
    }
511
    return send_answer(s_addr, query_id, ans_tag, mode);
512
  }
513
  if (tag == 0x566f7465) {
514
    ;; vote for a configuration proposal
515
    var signature = in_msg~load_bits(512);
516
    var msg_body = in_msg;
517
    var (sign_tag, idx, phash) = (in_msg~load_uint(32), in_msg~load_uint(16), in_msg~load_uint(256));
518
    in_msg.end_parse();
519
    throw_unless(37, sign_tag == 0x566f7445);
520
    var (vdescr, total_weight) = get_validator_descr(idx);
521
    var (val_pubkey, weight) = unpack_validator_descr(vdescr);
522
    throw_unless(34, check_data_signature(msg_body, signature, val_pubkey));
523
    int res = proceed_register_vote(phash, idx, weight);
524
    return send_confirmation(s_addr, query_id, res + 0xd6745240);
525
  }
526
  ;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query)
527
  ;; to bounce message back to sender
528
  throw_unless(37, (tag == 0) | (tag & (1 << 31)));
529
  ;; do nothing for other internal messages
530
}
531

532
() recv_external(slice in_msg) impure {
533
  var signature = in_msg~load_bits(512);
534
  var cs = in_msg;
535
  int action = cs~load_uint(32);
536
  int msg_seqno = cs~load_uint(32);
537
  var valid_until = cs~load_uint(32);
538
  throw_if(35, valid_until < now());
539
  throw_if(39, slice_depth(cs) > 128);
540
  var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
541
  throw_unless(33, msg_seqno == stored_seqno);
542
  if (action == 0x566f7465) {
543
    ;; vote for a configuration proposal
544
    var (idx, phash) = (cs~load_uint(16), cs~load_uint(256));
545
    cs.end_parse();
546
    var (vdescr, total_weight) = get_validator_descr(idx);
547
    var (val_pubkey, weight) = unpack_validator_descr(vdescr);
548
    throw_unless(34, check_data_signature(in_msg, signature, val_pubkey));
549
    accept_message();
550
    stored_seqno = (stored_seqno + 1) % (1 << 32);
551
    store_data(cfg_dict, stored_seqno, public_key, vote_dict);
552
    commit();
553
    proceed_register_vote(phash, idx, weight);
554
    return ();
555
  }
556
  throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
557
  accept_message();
558
  stored_seqno = (stored_seqno + 1) % (1 << 32);
559
  store_data(cfg_dict, stored_seqno, public_key, vote_dict);
560
  commit();
561
  (cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs);
562
  store_data(cfg_dict, stored_seqno, public_key, vote_dict);
563
}
564

565
() run_ticktock(int is_tock) impure {
566
  var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
567
  int kl = 32;
568
  var next_vset = cfg_dict.idict_get_ref(kl, 36);
569
  var updated? = false;
570
  ifnot (next_vset.null?()) {
571
    ;; check whether we have to set next_vset as the current validator set
572
    var ds = next_vset.begin_parse();
573
    if (ds.slice_bits() >= 40) {
574
      var tag = ds~load_uint(8);
575
      var since = ds.preload_uint(32);
576
      if ((since <= now()) & (tag == 0x12)) {
577
        ;; next validator set becomes active!
578
        var cur_vset = cfg_dict~idict_set_get_ref(kl, 34, next_vset);  ;; next_vset -> cur_vset
579
        cfg_dict~idict_set_get_ref(kl, 32, cur_vset);   ;; cur_vset -> prev_vset
580
        cfg_dict~idict_delete?(kl, 36);             ;; (null) -> next_vset
581
        updated? = true;
582
      }
583
    }
584
  }
585
  ifnot (updated?) {
586
    ;; if nothing has been done so far, scan a random voting proposal instead
587
    vote_dict = scan_random_proposal(vote_dict);
588
  }
589
  ;; save data and return
590
  return store_data(cfg_dict, stored_seqno, public_key, vote_dict);
591
}
592

593
int seqno() method_id {
594
  return get_data().begin_parse().preload_uint(32);
595
}
596

597
_ unpack_proposal(slice pstatus) inline_ref {
598
  (int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id, slice rest) = unpack_proposal_status(pstatus);
599
  var voters_list = null();
600
  var voter_id = (1 << 32);
601
  do {
602
    (voter_id, _, var f) = voters.udict_get_prev?(16, voter_id);
603
    if (f) {
604
      voters_list = cons(voter_id, voters_list);
605
    }
606
  } until (~ f);
607
  ;; Note there is a bug in config contract currently deployed in mainnet:
608
  ;; wins and losses are messed up
609
  var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
610
  rest.end_parse();
611
  var (param_id, param_val, param_hash) = parse_config_proposal(proposal);
612
  return [expires, critical?, [param_id, param_val, param_hash], vset_id, voters_list, weight_remaining, rounds_remaining, losses, wins];
613
}
614

615
_ get_proposal(int phash) method_id {
616
  (_, _, _, var vote_dict) = load_data();
617
  var (pstatus, found?) = vote_dict.udict_get?(256, phash);
618
  ifnot (found?) {
619
    return null();
620
  }
621
  return unpack_proposal(pstatus);
622
}
623

624
_ list_proposals() method_id {
625
  (_, _, _, var vote_dict) = load_data();
626
  var phash = (1 << 255) + ((1 << 255) - 1);
627
  var list = null();
628
  do {
629
    (phash, var pstatus, var f) = vote_dict.udict_get_prev?(256, phash);
630
    if (f) {
631
      list = cons([phash, unpack_proposal(pstatus)], list);
632
    }
633
  } until (~ f);
634
  return list;
635
}
636

637
_ proposal_storage_price(int critical?, int seconds, int bits, int refs) method_id {
638
  var cfg_dict = get_data().begin_parse().preload_ref();
639
  var cparam11 = cfg_dict.idict_get_ref(32, 11);
640
  var (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);
641
  if (seconds < min_store_sec) {
642
    return -1;
643
  }
644
  seconds = min(seconds, max_store_sec);
645
  return (bit_price * (bits + 1024) + cell_price * (refs + 2)) * seconds;
646
}
647

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.