Ton

Форк
0
382 строки · 11.1 Кб
1
;; Simple wallet smart contract
2

3
#include "stdlib.fc";
4

5
(int, int) get_bridge_config() impure inline_ref {
6
  cell bridge_config = config_param(71);
7
  if (bridge_config.cell_null?()) {
8
    bridge_config = config_param(-71);
9
  }
10
  if (bridge_config.cell_null?()) {
11
    return (0, 0);
12
  }
13
  slice ds = bridge_config.begin_parse();
14
  if (ds.slice_bits() < 512) {
15
    return (0, 0);
16
  }
17
  ;; wc always equals to -1
18
  int bridge_address = ds~load_uint(256);
19
  int oracles_address = ds~load_uint(256);
20
  return (bridge_address, oracles_address);
21
}
22

23
_ unpack_state() inline_ref {
24
  var ds = begin_parse(get_data());
25
  var res = (ds~load_uint(32), ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict(), ds~load_uint(32));
26
  ds.end_parse();
27
  return res;
28
}
29

30
_ pack_state(cell pending_queries, cell owner_infos, int last_cleaned, int k, int n, int wallet_id, int spend_delay) inline_ref {
31
  return begin_cell()
32
    .store_uint(wallet_id, 32)
33
    .store_uint(n, 8)
34
    .store_uint(k, 8)
35
    .store_uint(last_cleaned, 64)
36
    .store_dict(owner_infos)
37
    .store_dict(pending_queries)
38
    .store_uint(spend_delay,32)
39
  .end_cell();
40
}
41

42
_ pack_owner_info(int public_key, int flood) inline_ref {
43
  return begin_cell()
44
    .store_uint(public_key, 256)
45
    .store_uint(flood, 8);
46
}
47

48
_ unpack_owner_info(slice cs) inline_ref {
49
  return (cs~load_uint(256), cs~load_uint(8));
50
}
51

52
(int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref {
53
  int cnt = 0;
54

55
  do {
56
    slice cs = signatures.begin_parse();
57
    slice signature = cs~load_bits(512);
58

59
    int i = cs~load_uint(8);
60
    signatures = cs~load_dict();
61

62
    (slice public_key, var found?) = public_keys.udict_get?(8, i);
63
    throw_unless(37, found?);
64
    throw_unless(38, check_signature(hash, signature, public_key.preload_uint(256)));
65

66
    int mask = (1 << i);
67
    int old_cnt_bits = cnt_bits;
68
    cnt_bits |= mask;
69
    int should_check = cnt_bits != old_cnt_bits;
70
    cnt -= should_check;
71
  } until (cell_null?(signatures));
72

73
  return (cnt, cnt_bits);
74
}
75

76
() recv_internal(slice in_msg) impure {
77
  ;; do nothing for internal messages
78
}
79

80
(int, cell, int, int) parse_msg(slice in_msg) inline_ref {
81
  int mode = in_msg~load_uint(8);
82
  var msg = in_msg~load_ref();
83
  var msg' = msg.begin_parse();
84
  msg'~load_uint(4); ;; flags
85
  msg'~load_msg_addr(); ;; src
86
  (int wc, int addr) = parse_std_addr(msg'~load_msg_addr()); ;; dest
87
  return (mode, msg, wc, addr);
88
}
89

90
() check_proposed_query(slice in_msg) impure inline {
91
  throw_unless(43, (slice_refs(in_msg) == 1) & (slice_bits(in_msg) == 8));
92
  (_, _, int wc, _) = parse_msg(in_msg);
93
  wc~impure_touch();
94
}
95

96
(int, int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?, int root_i) inline_ref {
97
  if (found?) {
98
    throw_unless(35, query~load_int(1));
99
    (int creator_i, int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(8), query~load_uint(n), query);
100
    throw_unless(36, slice_hash(msg) == slice_hash(in_msg));
101
    return (creator_i, cnt, cnt_bits, msg);
102
  }
103
  check_proposed_query(in_msg);
104

105
  return (root_i, 0, 0, in_msg);
106
}
107

108
(cell, ()) dec_flood(cell owner_infos, int creator_i) {
109
  (slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i);
110
  (int public_key, int flood) = unpack_owner_info(owner_info);
111
  owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1));
112
  return (owner_infos, ());
113
}
114

115
() try_init() impure inline_ref {
116
  ;; first query without signatures is always accepted
117
  (int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries, int spend_delay) = unpack_state();
118
  throw_if(37, last_cleaned);
119
  accept_message();
120
  set_data(pack_state(pending_queries, owner_infos, 1, k, n, wallet_id, spend_delay));
121
}
122

123
(cell, cell) update_pending_queries(cell pending_queries, cell owner_infos, slice msg, int query_id, int creator_i, int cnt, int cnt_bits, int n, int k) impure inline_ref {
124
  if (cnt >= k) {
125
    accept_message();
126
    (int bridge_address, int oracles_address) = get_bridge_config();
127
    (_, int my_addr) = parse_std_addr(my_address());
128
    var (mode, msg', wc, addr) = parse_msg(msg);
129
    if ( ((wc == -1) & (addr == bridge_address)) | (oracles_address != my_addr) ) {
130
       send_raw_message(msg', mode);
131
    }
132
    pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1));
133
    owner_infos~dec_flood(creator_i);
134
  } else {
135
    pending_queries~udict_set_builder(64, query_id, begin_cell()
136
      .store_uint(1, 1)
137
      .store_uint(creator_i, 8)
138
      .store_uint(cnt, 8)
139
      .store_uint(cnt_bits, n)
140
      .store_slice(msg));
141
  }
142
  return (pending_queries, owner_infos);
143
}
144

145
(int, int) calc_boc_size(int cells, int bits, slice root) {
146
  cells += 1;
147
  bits += root.slice_bits();
148

149
  while (root.slice_refs()) {
150
    (cells, bits) = calc_boc_size(cells, bits, root~load_ref().begin_parse());
151
  }
152

153
  return (cells, bits);
154
}
155

156
() recv_external(slice in_msg) impure {
157
  ;; empty message triggers init
158
  if (slice_empty?(in_msg)) {
159
    return try_init();
160
  }
161

162
  ;; Check root signature
163
  slice root_signature = in_msg~load_bits(512);
164
  int root_hash = slice_hash(in_msg);
165
  int root_i = in_msg~load_uint(8);
166

167
  (int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries, int spend_delay) = unpack_state();
168

169
  throw_unless(38, now() > spend_delay);
170
  last_cleaned -= last_cleaned == 0;
171

172
  (slice owner_info, var found?) = owner_infos.udict_get?(8, root_i);
173
  throw_unless(31, found?);
174
  (int public_key, int flood) = unpack_owner_info(owner_info);
175
  throw_unless(32, check_signature(root_hash, root_signature, public_key));
176

177
  cell signatures = in_msg~load_dict();
178

179
  var hash = slice_hash(in_msg);
180
  int query_wallet_id = in_msg~load_uint(32);
181
  throw_unless(42, query_wallet_id == wallet_id);
182

183
  int query_id = in_msg~load_uint(64);
184

185
  (int cnt, int bits) = calc_boc_size(0, 0, in_msg);
186
  throw_if(40, (cnt > 8) | (bits > 2048));
187

188
  (slice query, var found?) = pending_queries.udict_get?(64, query_id);
189

190
  ifnot (found?) {
191
    flood += 1;
192
    throw_if(39, flood > 10);
193
  }
194

195
  var bound = (now() << 32);
196
  throw_if(33, query_id < bound);
197

198
  (int creator_i, int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?, root_i);
199
  int mask = 1 << root_i;
200
  throw_if(34, cnt_bits & mask);
201
  cnt_bits |= mask;
202
  cnt += 1;
203

204
  throw_if(41, ~ found? & (cnt < k) & (bound + ((60 * 60) << 32) > query_id));
205

206
  set_gas_limit(100000);
207

208
  ifnot (found?) {
209
    owner_infos~udict_set_builder(8, root_i, pack_owner_info(public_key, flood));
210
  }
211

212
  (pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
213
  set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id, spend_delay));
214

215
  commit();
216

217
  int need_save = 0;
218
  ifnot (cell_null?(signatures) | (cnt >= k)) {
219
    (int new_cnt, cnt_bits) = check_signatures(owner_infos, signatures, hash, cnt_bits);
220
    cnt += new_cnt;
221
    (pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
222
    need_save = -1;
223
  }
224

225
  accept_message();
226
  bound -= (64 << 32);   ;; clean up records expired more than 64 seconds ago
227
  int old_last_cleaned = last_cleaned;
228
  do {
229
    var (pending_queries', i, query, f) = pending_queries.udict_delete_get_min(64);
230
    f~touch();
231
    if (f) {
232
      f = (i < bound);
233
    }
234
    if (f) {
235
      if (query~load_int(1)) {
236
        owner_infos~dec_flood(query~load_uint(8));
237
      }
238
      pending_queries = pending_queries';
239
      last_cleaned = i;
240
      need_save = -1;
241
    }
242
  } until (~ f);
243

244
  if (need_save) {
245
    set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id, spend_delay));
246
  }
247
}
248

249
;; Get methods
250
;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten)
251
(int, int) get_query_state(int query_id) method_id {
252
  (_, int n, _, int last_cleaned, _, cell pending_queries, _) = unpack_state();
253
  (slice cs, var found) = pending_queries.udict_get?(64, query_id);
254
  if (found) {
255
    if (cs~load_int(1)) {
256
      cs~load_uint(8 + 8);
257
      return (0, cs~load_uint(n));
258
    } else {
259
      return (-1, 0);
260
    }
261
  }  else {
262
    return (-(query_id <= last_cleaned), 0);
263
  }
264
}
265

266
int processed?(int query_id) method_id {
267
  (int x, _) = get_query_state(query_id);
268
  return x;
269
}
270

271
cell create_init_state(int wallet_id, int n, int k, cell owners_info, int spend_delay) method_id {
272
  return pack_state(new_dict(), owners_info, 0, k, n, wallet_id, spend_delay);
273
}
274

275
cell merge_list(cell a, cell b) {
276
  if (cell_null?(a)) {
277
    return b;
278
  }
279
  if (cell_null?(b)) {
280
    return a;
281
  }
282
  slice as = a.begin_parse();
283
  if (as.slice_refs() != 0) {
284
    cell tail = merge_list(as~load_ref(), b);
285
    return begin_cell().store_slice(as).store_ref(tail).end_cell();
286
  }
287

288
  as~skip_last_bits(1);
289
  ;; as~skip_bits(1);
290
  return begin_cell().store_slice(as).store_dict(b).end_cell();
291

292
}
293

294
cell get_public_keys() method_id {
295
  (_, _, _, _, cell public_keys, _, _) = unpack_state();
296
  return public_keys;
297
}
298

299
(int, int) check_query_signatures(cell query) method_id {
300
  slice cs = query.begin_parse();
301
  slice root_signature = cs~load_bits(512);
302
  int root_hash = slice_hash(cs);
303
  int root_i = cs~load_uint(8);
304

305
  cell public_keys = get_public_keys();
306
  (slice public_key, var found?) = public_keys.udict_get?(8, root_i);
307
  throw_unless(31, found?);
308
  throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256)));
309

310
  int mask = 1 << root_i;
311

312
  cell signatures = cs~load_dict();
313
  if (cell_null?(signatures)) {
314
    return (1, mask);
315
  }
316
  (int cnt, mask) = check_signatures(public_keys, signatures, slice_hash(cs), mask);
317
  return (cnt + 1, mask);
318
}
319

320
int message_signed_by_id?(int id, int query_id) method_id {
321
  (_, int n, _, _, _, cell pending_queries, _) = unpack_state();
322
  (var cs, var f) = pending_queries.udict_get?(64, query_id);
323
  if (f) {
324
    if (cs~load_int(1)) {
325
      int cnt_bits = cs.skip_bits(8 + 8).preload_uint(n);
326
      if (cnt_bits & (1 << id)) {
327
        return -1;
328
      }
329
      return 0;
330
    }
331
    return -1;
332
  }
333
  return 0;
334
}
335

336
cell messages_by_mask(int mask) method_id {
337
  (_, int n, _, _, _, cell pending_queries, _) = unpack_state();
338
  int i = -1;
339
  cell a = new_dict();
340
  do {
341
    (i, var cs, var f) = pending_queries.udict_get_next?(64, i);
342
    if (f) {
343
      if (cs~load_int(1)) {
344
        int cnt_bits = cs.skip_bits(8 + 8).preload_uint(n);
345
        if (cnt_bits & mask) {
346
          a~udict_set_builder(64, i, begin_cell().store_slice(cs));
347
        }
348
      }
349
    }
350
  } until (~ f);
351
  return a;
352
}
353

354
cell get_messages_unsigned_by_id(int id) method_id {
355
  return messages_by_mask(1 << id);
356
}
357

358
cell get_messages_unsigned() method_id {
359
  return messages_by_mask(~ 0);
360
}
361

362
(int, int) get_n_k() method_id {
363
  (_, int n, int k, _, _, _, _) = unpack_state();
364
  return (n, k);
365
}
366

367
cell merge_inner_queries(cell a, cell b) method_id {
368
  slice ca = a.begin_parse();
369
  slice cb = b.begin_parse();
370
  cell list_a = ca~load_dict();
371
  cell list_b = cb~load_dict();
372
  throw_unless(31, slice_hash(ca) == slice_hash(cb));
373
  return begin_cell()
374
    .store_dict(merge_list(list_a, list_b))
375
    .store_slice(ca)
376
  .end_cell();
377
}
378

379
int get_lock_timeout() method_id {
380
  (_, _, _, _, _, _, int spend_delay) = unpack_state();
381
  return spend_delay;
382
}
383

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

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

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

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