;; Simple configuration smart contract () set_conf_param(int index, cell value) impure { var cs = get_data().begin_parse(); var cfg_dict = cs~load_ref(); cfg_dict~idict_set_ref(32, index, value); set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell()); } (cell, int, int, cell) load_data() inline { var cs = get_data().begin_parse(); var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict()); cs.end_parse(); return res; } () store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline { set_data(begin_cell() .store_ref(cfg_dict) .store_uint(stored_seqno, 32) .store_uint(public_key, 256) .store_dict(vote_dict) .end_cell()); } (int, int) check_validator_set(cell vset) { var cs = vset.begin_parse(); throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only int utime_since = cs~load_uint(32); int utime_until = cs~load_uint(32); int total = cs~load_uint(16); int main = cs~load_uint(16); throw_unless(9, main > 0); throw_unless(9, total >= main); return (utime_since, utime_until); } () send_answer(addr, query_id, ans_tag, mode) impure { ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000 send_raw_message(begin_cell().store_uint(0x18, 6).store_slice(addr).store_uint(0, 5 + 4 + 4 + 64 + 32 + 1 + 1).store_uint(ans_tag, 32).store_uint(query_id, 64).end_cell(), mode); } () send_confirmation(addr, query_id, ans_tag) impure { return send_answer(addr, query_id, ans_tag, 64); } () send_error(addr, query_id, ans_tag) impure { return send_answer(addr, query_id, ans_tag, 64); } () recv_internal(cell in_msg_cell, slice in_msg) impure { var cs = in_msg_cell.begin_parse(); var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool var s_addr = cs~load_msg_addr(); (int src_wc, int src_addr) = s_addr.parse_std_addr(); if ((src_wc + 1) | (flags & 1) | in_msg.slice_empty?()) { ;; source not in masterchain, or a bounced message, or a simple transfer return (); } int tag = in_msg~load_uint(32); int query_id = in_msg~load_uint(64); if (tag == 0x4e565354) { ;; set next validator set var vset = in_msg~load_ref(); in_msg.end_parse(); var elector_param = config_param(1); var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256); var ok = false; if (src_addr == elector_addr) { ;; message from elector smart contract ;; set next validator set (var t_since, var t_until) = check_validator_set(vset); var t = now(); ok = (t_since > t) & (t_until > t_since); } if (ok) { set_conf_param(36, vset); ;; send confirmation return send_confirmation(s_addr, query_id, 0xee764f4b); } else { return send_error(s_addr, query_id, 0xee764f6f); } } ;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query) ;; to bounce message back to sender throw_unless(37, (tag == 0) | (tag & (1 << 31))); ;; do nothing for other internal messages } ;; forward a message to elector smart contract to make it upgrade its code () change_elector_code(slice cs) impure { var dest_addr = config_param(1).begin_parse().preload_uint(256); var query_id = now(); send_raw_message(begin_cell() .store_uint(0xc4ff, 17) .store_uint(dest_addr, 256) .store_grams(1 << 30) ;; ~ 1 Gram (will be returned back) .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) .store_uint(0x4e436f64, 32) ;; action .store_uint(query_id, 64) .store_slice(cs) .end_cell(), 0); } () after_code_upgrade(slice param, cont old_code) impure method_id(1666) { } _ perform_action(cfg_dict, public_key, action, cs) { if (action == 0x43665021) { ;; change one configuration parameter var param_index = cs~load_uint(32); var param_value = cs~load_ref(); cs.end_parse(); cfg_dict~idict_set_ref(32, param_index, param_value); return (cfg_dict, public_key); } elseif (action == 0x4e436f64) { ;; change configuration smart contract code var new_code = cs~load_ref(); set_code(new_code); var old_code = get_c3(); set_c3(new_code.begin_parse().bless()); after_code_upgrade(cs, old_code); throw(0); return (cfg_dict, public_key); } elseif (action == 0x50624b21) { ;; change configuration master public key public_key = cs~load_uint(256); cs.end_parse(); return (cfg_dict, public_key); } elseif (action == 0x4e43ef05) { ;; change election smart contract code change_elector_code(cs); return (cfg_dict, public_key); } else { throw_if(32, action); return (cfg_dict, public_key); } } (slice, int) get_validator_descr(int idx) inline_ref { var vset = config_param(34); if (vset.null?()) { return (null(), 0); } var cs = begin_parse(vset); ;; validators_ext#12 utime_since:uint32 utime_until:uint32 ;; total:(## 16) main:(## 16) { main <= total } { main >= 1 } ;; total_weight:uint64 throw_unless(40, cs~load_uint(8) == 0x12); cs~skip_bits(32 + 32 + 16 + 16); int total_weight = cs~load_uint(64); var dict = begin_cell().store_slice(cs).end_cell(); var (value, _) = dict.udict_get?(16, idx); return (value, total_weight); } (int, int) unpack_validator_descr(slice cs) inline { ;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey; ;; validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr; ;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr; throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53); throw_unless(41, cs~load_uint(32) == 0x8e81278a); return (cs~load_uint(256), cs~load_uint(64)); } (cell, int, int, slice) new_proposal(cs) inline { return (null(), 0, 0, cs); } (cell, int, int, slice) unpack_proposal(slice cs) inline { return (cs~load_dict(), cs~load_uint(64), cs~load_uint(256), cs); } builder pack_proposal(cell voters, int sum_weight, int vset_id, slice body) inline { return begin_cell().store_dict(voters).store_uint(sum_weight, 64).store_uint(vset_id, 256).store_slice(body); } (cell, slice) register_vote(vote_dict, action, cs, idx, weight, total_weight, cur_vset_id) { int hash = 0; int found? = 0; var entry = null(); if (action & 1) { hash = slice_hash(cs); (entry, found?) = vote_dict.udict_get?(256, hash); } else { hash = cs.preload_uint(256); (entry, found?) = vote_dict.udict_get?(256, hash); throw_unless(42, found?); } var (voters, sum_weight, vset_id, body) = found? ? unpack_proposal(entry) : (null(), 0, cur_vset_id, cs); if (vset_id != cur_vset_id) { voters = null(); sum_weight = 0; vset_id = cur_vset_id; } var (_, found?) = voters.udict_get?(16, idx); ifnot (found?) { voters~udict_set_builder(16, idx, begin_cell().store_uint(32, now())); sum_weight += weight; if (sum_weight * 3 > total_weight * 2) { ;; proposal accepted vote_dict~udict_delete?(256, hash); return (vote_dict, body); } else { vote_dict~udict_set_builder(256, hash, pack_proposal(voters, sum_weight, cur_vset_id, body)); return (vote_dict, null()); } } else { return (vote_dict, null()); } } () recv_external(slice in_msg) impure { var signature = in_msg~load_bits(512); var cs = in_msg; int action = cs~load_uint(32); int msg_seqno = cs~load_uint(32); var valid_until = cs~load_uint(32); throw_if(35, valid_until < now()); var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data(); throw_unless(33, msg_seqno == stored_seqno); ifnot ((action - 0x566f7465) & -2) { var idx = cs~load_uint(16); var (vdescr, total_weight) = get_validator_descr(idx); var (val_pubkey, weight) = unpack_validator_descr(vdescr); throw_unless(34, check_signature(slice_hash(in_msg), signature, val_pubkey)); accept_message(); stored_seqno += 1; store_data(cfg_dict, stored_seqno, public_key, vote_dict); commit(); (vote_dict, var accepted) = register_vote(vote_dict, action, cs, idx, weight, total_weight, config_param(34).cell_hash()); store_data(cfg_dict, stored_seqno, public_key, vote_dict); ifnot (accepted.null?()) { (cfg_dict, public_key) = perform_action(cfg_dict, public_key, accepted~load_uint(32), accepted); store_data(cfg_dict, stored_seqno, public_key, vote_dict); } return (); } throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); accept_message(); stored_seqno += 1; store_data(cfg_dict, stored_seqno, public_key, vote_dict); commit(); (cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs); store_data(cfg_dict, stored_seqno, public_key, vote_dict); } () run_ticktock(int is_tock) impure { var cs = begin_parse(get_data()); var cfg_dict = cs~load_ref(); int kl = 32; ;; cfg_dict~idict_set_ref(kl, -17, begin_cell().store_uint(now() >> 16, 32).end_cell()); var next_vset = cfg_dict.idict_get_ref(kl, 36); ifnot (next_vset.null?()) { ;; check whether we have to set next_vset as the current validator set var ds = next_vset.begin_parse(); if (ds.slice_bits() >= 40) { var tag = ds~load_uint(8); var since = ds.preload_uint(32); if ((since <= now()) & (tag == 0x12)) { ;; next validator set becomes active! var cur_vset = cfg_dict~idict_set_get_ref(kl, 34, next_vset); ;; next_vset -> cur_vset cfg_dict~idict_set_get_ref(kl, 32, cur_vset); ;; cur_vset -> prev_vset cfg_dict~idict_delete?(kl, 36); ;; (null) -> next_vset } } } set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell()); } int seqno() impure method_id { return get_data().begin_parse().preload_uint(32); }