2019-09-07 10:03:22 +00:00
|
|
|
;; Simple configuration smart contract
|
|
|
|
|
|
|
|
() set_conf_param(int index, cell value) impure {
|
2019-10-23 13:43:50 +00:00
|
|
|
var cs = get_data().begin_parse();
|
2019-09-07 10:03:22 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:43:50 +00:00
|
|
|
(cell, int, int, cell) load_data() inline {
|
|
|
|
var cs = get_data().begin_parse();
|
|
|
|
var (cfg_dict, stored_seqno, public_key) = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256));
|
|
|
|
var vote_dict = cs.slice_empty?() ? new_dict() : cs~load_dict();
|
|
|
|
cs.end_parse();
|
|
|
|
return (cfg_dict, stored_seqno, public_key, vote_dict);
|
|
|
|
}
|
|
|
|
|
|
|
|
() 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());
|
|
|
|
}
|
|
|
|
|
2019-09-07 10:03:22 +00:00
|
|
|
(int, int) check_validator_set(cell vset) {
|
|
|
|
var cs = vset.begin_parse();
|
2019-11-18 18:15:14 +00:00
|
|
|
throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only
|
2019-09-07 10:03:22 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-11-18 18:15:14 +00:00
|
|
|
() after_code_upgrade(slice param, cont old_code) impure method_id(1666) {
|
2019-10-23 13:43:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_ perform_action(cfg_dict, public_key, action, cs) {
|
2019-09-07 10:03:22 +00:00
|
|
|
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);
|
2019-10-23 13:43:50 +00:00
|
|
|
return (cfg_dict, public_key);
|
2019-09-07 10:03:22 +00:00
|
|
|
} elseif (action == 0x4e436f64) {
|
|
|
|
;; change configuration smart contract code
|
|
|
|
var new_code = cs~load_ref();
|
|
|
|
set_code(new_code);
|
2019-10-23 13:43:50 +00:00
|
|
|
var old_code = get_c3();
|
2019-11-18 18:15:14 +00:00
|
|
|
set_c3(new_code.begin_parse().bless());
|
2019-10-23 13:43:50 +00:00
|
|
|
after_code_upgrade(cs, old_code);
|
|
|
|
throw(0);
|
|
|
|
return (cfg_dict, public_key);
|
2019-09-07 10:03:22 +00:00
|
|
|
} elseif (action == 0x50624b21) {
|
|
|
|
;; change configuration master public key
|
|
|
|
public_key = cs~load_uint(256);
|
|
|
|
cs.end_parse();
|
2019-10-23 13:43:50 +00:00
|
|
|
return (cfg_dict, public_key);
|
2019-09-07 10:03:22 +00:00
|
|
|
} elseif (action == 0x4e43ef05) {
|
|
|
|
;; change election smart contract code
|
|
|
|
change_elector_code(cs);
|
2019-10-23 13:43:50 +00:00
|
|
|
return (cfg_dict, public_key);
|
2019-09-07 10:03:22 +00:00
|
|
|
} else {
|
|
|
|
throw_if(32, action);
|
2019-10-23 13:43:50 +00:00
|
|
|
return (cfg_dict, public_key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
slice get_validator_descr(int idx) inline_ref {
|
|
|
|
var vset = config_param(34);
|
|
|
|
if (vset.null?()) {
|
|
|
|
return null();
|
|
|
|
}
|
|
|
|
var cs = begin_parse(vset);
|
|
|
|
cs~skip_bits(8 + 32 + 32 + 16 + 16);
|
|
|
|
var dict = begin_cell().store_slice(cs).end_cell();
|
|
|
|
var (value, _) = dict.udict_get?(16, idx);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
(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));
|
|
|
|
}
|
|
|
|
|
|
|
|
slice create_new_entry(cs) inline {
|
|
|
|
return begin_cell().store_int(false, 1).store_uint(0, 64).store_uint(0, 256).store_slice(cs).end_cell().begin_parse();
|
|
|
|
}
|
|
|
|
|
2019-10-31 10:26:04 +00:00
|
|
|
(cell, int, int, slice) unpack_suggestion(slice cs) inline {
|
|
|
|
return (cs~load_dict(), cs~load_uint(64), cs~load_uint(256), cs);
|
|
|
|
}
|
|
|
|
|
|
|
|
builder pack_suggestion(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);
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:43:50 +00:00
|
|
|
cell register_vote(vote_dict, action, cs, idx, weight) {
|
|
|
|
int hash = 0;
|
|
|
|
var entry = null();
|
|
|
|
if (action & 1) {
|
|
|
|
hash = slice_hash(cs);
|
|
|
|
(entry, var found?) = vote_dict.udict_get?(256, hash);
|
|
|
|
ifnot (found?) {
|
|
|
|
entry = create_new_entry(cs);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
hash = cs.preload_uint(256);
|
|
|
|
(entry, var found?) = vote_dict.udict_get?(256, hash);
|
|
|
|
throw_unless(42, found?);
|
|
|
|
}
|
|
|
|
return vote_dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
() 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 = 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 = register_vote(vote_dict, action, cs, idx, weight);
|
|
|
|
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
|
|
|
return ();
|
2019-09-07 10:03:22 +00:00
|
|
|
}
|
2019-10-23 13:43:50 +00:00
|
|
|
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);
|
2019-09-07 10:03:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
() run_ticktock(int is_tock) impure {
|
|
|
|
var cs = begin_parse(get_data());
|
|
|
|
var cfg_dict = cs~load_ref();
|
|
|
|
int kl = 32;
|
2019-09-14 14:14:55 +00:00
|
|
|
;; cfg_dict~idict_set_ref(kl, -17, begin_cell().store_uint(now() >> 16, 32).end_cell());
|
2019-09-07 10:03:22 +00:00
|
|
|
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);
|
2019-11-18 18:15:14 +00:00
|
|
|
if ((since <= now()) & (tag == 0x12)) {
|
2019-09-07 10:03:22 +00:00
|
|
|
;; 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());
|
|
|
|
}
|
2019-11-18 18:15:14 +00:00
|
|
|
|
|
|
|
int seqno() impure method_id {
|
|
|
|
return get_data().begin_parse().preload_uint(32);
|
|
|
|
}
|