mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
167
crypto/smartcont/CreateState.fif
Normal file
167
crypto/smartcont/CreateState.fif
Normal file
|
@ -0,0 +1,167 @@
|
|||
"Asm.fif" include
|
||||
|
||||
31 -1<< constant wc_undef
|
||||
0 constant wc_base
|
||||
-1 constant wc_master
|
||||
|
||||
{ wcid@ 2dup <> swap wc_undef <> and
|
||||
abort"workchain_id already set to another value"
|
||||
wcid! } : setworkchain
|
||||
{ globalid@ 2dup <> swap and
|
||||
abort"global_id already set to another value"
|
||||
dup 0= abort"global_id cannot be zero"
|
||||
globalid! } : setglobalid
|
||||
|
||||
<b b> constant empty_cell
|
||||
|
||||
// b x --> b' ( serializes a Gram amount )
|
||||
{ -1 { 1+ 2dup 8 * ufits } until
|
||||
rot over 4 u, -rot 8 * u, } : Gram,
|
||||
|
||||
// workchain-id -- wc-zero-state
|
||||
{ <b x{9023afe2} s, globalid@ 32 i, 0 8 i, swap 32 i, 1 63 << 64 u,
|
||||
0 64 i, now 32 u, 0 64 i, -1 32 i, <b 0 67 u, b> ref, 0 1 u,
|
||||
<b 0 11 u, b> ref,
|
||||
<b 0 128 10 + 1+ 1+ u, b> ref, 0 1 u, b>
|
||||
dup isShardState? not abort"invalid ShardState created"
|
||||
} : mkemptyShardState
|
||||
|
||||
' dictnew : Libs{
|
||||
'nop : }Libs
|
||||
{ <b swap 1 u, over ref, b> <s swap hash rot 256 udict!+
|
||||
0= abort"duplicate library in collection" } : lib+
|
||||
{ 0 lib+ } : private_lib
|
||||
{ 1 lib+ } : public_lib
|
||||
|
||||
{ (configdict) 0= abort"Configuration dictionary is empty" } : configdict
|
||||
|
||||
// max-validators masterchain-validators min-validators --
|
||||
{ swap rot <b swap 16 u, swap 16 u, swap 16 u, b> 16 config! } : config.validator_num!
|
||||
|
||||
// min-stake max-stake min-total-stake max-factor --
|
||||
{ 4 0 reverse <b swap Gram, swap Gram, swap Gram, swap 32 u, b>
|
||||
17 config!
|
||||
} : config.validator_stake_limits!
|
||||
|
||||
// elected-for elections-begin-before elections-end-before stakes-frozen
|
||||
{ 4 0 reverse <b { swap 32 u, } 4 times b> 15 config! } : config.election_params!
|
||||
|
||||
dictnew 0 2constant validator-dict
|
||||
{ @' validator-dict } : validator-dict@
|
||||
{ validator-dict@ nip } : validator#
|
||||
// val-pubkey weight --
|
||||
{ dup 0<= abort"validator weight must be non-negative"
|
||||
dup 64 ufits not abort"validator weight must fit into 64 bits"
|
||||
over Blen 32 <> abort"validator public key must be 32 bytes long"
|
||||
<b x{538e81278a} s, rot B, swap 64 u, b> <s
|
||||
validator-dict@ dup 1+ 3 -roll swap
|
||||
16 udict!+ 0= abort"cannot add validator"
|
||||
swap 2 'nop does : validator-dict
|
||||
} : add-validator
|
||||
// since-ut until-ut main-val-cnt-or-0 --
|
||||
{ ?dup 0= { validator# } if
|
||||
validator# 0= abort"no initial validators defined"
|
||||
rot <b x{11} s, swap 32 u, rot 32 u, validator# 16 u, swap 16 u,
|
||||
validator-dict@ drop <s s, b>
|
||||
34 config!
|
||||
} : config.validators!
|
||||
|
||||
dictnew constant workchain-dict
|
||||
// root-hash file-hash enable-utime actual-min-split min-split max-split workchain-id --
|
||||
{ <b x{a6} s, 5 roll 32 u, 4 roll 8 u, 3 roll 8 u, rot 8 u, x{e000} s,
|
||||
3 roll 256 u, rot 256 u, 0 32 u, x{1} s, -1 32 i, 0 64 u, b>
|
||||
dup isWorkchainDescr? not abort"invalid WorkchainDescr created"
|
||||
<s swap @' workchain-dict 32 idict!+ 0= abort"cannot add workchain"
|
||||
=: workchain-dict
|
||||
} : add-std-workchain
|
||||
// --
|
||||
{ @' workchain-dict dict>s s>c 12 config! } : config.workchains!
|
||||
|
||||
dictnew constant special-dict
|
||||
// special-smc-addr --
|
||||
{ x{} swap @' special-dict 256 udict! not abort"cannot add a new special smart contract"
|
||||
=: special-dict
|
||||
} : make_special
|
||||
{ @' special-dict dict>s s>c 31 config! } : config.special!
|
||||
|
||||
// bit-pps cell-pps mc-bit-pps mc-cell-pps --
|
||||
{ <b x{cc} s, 0 32 u, 4 roll 64 u, 3 roll 64 u, rot 64 u, swap 64 u,
|
||||
0 dictnew 32 b>udict! 0= abort"cannot create storage prices dictionary"
|
||||
<b swap <s s, b> 18 config!
|
||||
} : config.storage_prices!
|
||||
{ 16 << } : sg*
|
||||
{ 16 <</r } : sg*/
|
||||
{ bl word (number) ?dup 0= abort"not a valid 2^16-based fraction"
|
||||
1- { sg*/ } { sg* } cond
|
||||
1 'nop
|
||||
} ::_ sg~
|
||||
|
||||
// gas_price gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit -- c
|
||||
{ 6 0 reverse <b x{dd} s, swap 64 u, swap 64 u, swap 64 u, swap 64 u, swap 64 u, swap 64 u, b>
|
||||
} : make-gas-prices
|
||||
{ make-gas-prices 20 config! } : config.mc_gas_prices!
|
||||
{ make-gas-prices 21 config! } : config.gas_prices!
|
||||
|
||||
// lump_price bit_price cell_price ihr_factor first_frac next_frac
|
||||
{ 6 0 reverse <b x{ea} s, swap 64 u, swap 64 u, swap 64 u, swap 32 u, swap 16 u, swap 16 u, b>
|
||||
} : make-msg-fwd-prices
|
||||
{ make-msg-fwd-prices 24 config! } : config.mc_fwd_prices!
|
||||
{ make-msg-fwd-prices 25 config! } : config.fwd_prices!
|
||||
|
||||
// mc-cc-lifetime sh-cc-lifetime sh-val-lifetime sh-val-num
|
||||
{ 4 0 reverse <b x{c1} s, swap 32 u, swap 32 u, swap 32 u, swap 32 u, b>
|
||||
} : make-catchain-params
|
||||
{ make-catchain-params 28 config! } : config.catchain_params!
|
||||
// round-candidates next-cand-delay-ms consensus-timeout-ms fast-attempts attempt-duration cc-max-deps max-block-size max-collated-size
|
||||
{ 8 0 reverse <b x{d6} s, { swap 32 u, } 8 times b> } : make-vsession-params
|
||||
{ make-vsession-params 29 config! } : config.consensus_params!
|
||||
|
||||
// b [underload soft hard] -- b'
|
||||
{ untriple 3 roll x{c3} s, 3 roll 32 u, rot 32 u, swap 32 u, } : param_limits,
|
||||
// bytes-limits gas-limits lt-limits -- c
|
||||
{ <b x{5d} s, 3 roll param_limits, rot param_limits, swap param_limits, b>
|
||||
} : make-block-limits
|
||||
{ make-block-limits 22 config! } : config.mc_block_limits!
|
||||
{ make-block-limits 23 config! } : config.block_limits!
|
||||
|
||||
// mc-block-create-fee bc-block-create-fee
|
||||
{ <b x{6b} s, rot Gram, swap Gram, b> } : make-block-create-fees
|
||||
{ make-block-create-fees 14 config! } : config.block_create_fees!
|
||||
|
||||
{ <b swap 256 u, b> } : make-smc-addr-cell
|
||||
{ make-smc-addr-cell 0 config! } : config.config_smc!
|
||||
{ make-smc-addr-cell 1 config! } : config.elector_smc!
|
||||
{ make-smc-addr-cell 2 config! } : config.minter_smc!
|
||||
{ make-smc-addr-cell 3 config! } : config.collector_smc!
|
||||
|
||||
{ <b swap dict, b> 7 config! } : config.to_mint!
|
||||
|
||||
|
||||
1000000000 constant Gram
|
||||
1000000 constant mGram
|
||||
1000 constant uGram
|
||||
1 constant nGram
|
||||
{ Gram * } : Gram*
|
||||
{ mGram * } : mGram*
|
||||
{ uGram * } : uGram*
|
||||
'nop : nGram*
|
||||
{ Gram swap */r } : Gram*/
|
||||
{ mGram swap */r } : mGram*/
|
||||
{ uGram swap */r } : uGram*/
|
||||
{ /r } : nGram*/
|
||||
// GR$.17 is equivalent to 170000000
|
||||
{ bl word (number) ?dup 0= abort"not a valid Gram amount"
|
||||
1- { Gram swap */r } { Gram * } cond
|
||||
1 'nop
|
||||
} ::_ GR$
|
||||
|
||||
{ 10 << } : *Ki
|
||||
{ 20 << } : *Mi
|
||||
{ 10 <</r } : */Ki
|
||||
{ 20 <</r } : */Mi
|
||||
|
||||
0 constant recv_internal
|
||||
-1 constant recv_external
|
||||
-2 constant run_ticktock
|
||||
-3 constant split_prepare
|
||||
-4 constant split_install
|
149
crypto/smartcont/config-code.fc
Normal file
149
crypto/smartcont/config-code.fc
Normal file
|
@ -0,0 +1,149 @@
|
|||
;; Simple configuration smart contract
|
||||
|
||||
() set_conf_param(int index, cell value) impure {
|
||||
var cs = begin_parse(get_data());
|
||||
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());
|
||||
}
|
||||
|
||||
(int, int) check_validator_set(cell vset) {
|
||||
var cs = vset.begin_parse();
|
||||
throw_unless(9, cs~load_uint(8) == 0x11); ;; validators#11
|
||||
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);
|
||||
}
|
||||
|
||||
() 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 cs2 = begin_parse(get_data());
|
||||
var cfg_dict = cs2~load_ref();
|
||||
var stored_seqno = cs2~load_uint(32);
|
||||
var public_key = cs2~load_uint(256);
|
||||
cs2.end_parse();
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
accept_message();
|
||||
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);
|
||||
} elseif (action == 0x4e436f64) {
|
||||
;; change configuration smart contract code
|
||||
var new_code = cs~load_ref();
|
||||
cs.end_parse();
|
||||
set_code(new_code);
|
||||
} elseif (action == 0x50624b21) {
|
||||
;; change configuration master public key
|
||||
public_key = cs~load_uint(256);
|
||||
cs.end_parse();
|
||||
} elseif (action == 0x4e43ef05) {
|
||||
;; change election smart contract code
|
||||
change_elector_code(cs);
|
||||
} else {
|
||||
throw_if(32, action);
|
||||
}
|
||||
set_data(begin_cell().store_ref(cfg_dict).store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell());
|
||||
}
|
||||
|
||||
() 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() >> 8, 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 ((tag == 0x11) & (since >= now())) {
|
||||
;; 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());
|
||||
}
|
798
crypto/smartcont/elector-code.fc
Normal file
798
crypto/smartcont/elector-code.fc
Normal file
|
@ -0,0 +1,798 @@
|
|||
;; Elector smartcontract
|
||||
|
||||
;; cur_elect credits past_elect grams active_id active_hash
|
||||
(cell, cell, cell, int, int, int) load_data() {
|
||||
var cs = get_data().begin_parse();
|
||||
var res = (cs~load_dict(), cs~load_dict(), cs~load_dict(), cs~load_grams(), cs~load_uint(32), cs~load_uint(256));
|
||||
cs.end_parse();
|
||||
return res;
|
||||
}
|
||||
|
||||
;; cur_elect credits past_elect grams active_id active_hash
|
||||
() store_data(elect, credits, past_elect, grams, active_id, active_hash) impure {
|
||||
set_data(begin_cell()
|
||||
.store_dict(elect)
|
||||
.store_dict(credits)
|
||||
.store_dict(past_elect)
|
||||
.store_grams(grams)
|
||||
.store_uint(active_id, 32)
|
||||
.store_uint(active_hash, 256)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
;; elect -> elect_at elect_close min_stake total_stake members failed finished
|
||||
_ unpack_elect(elect) {
|
||||
var es = elect.begin_parse();
|
||||
var res = (es~load_uint(32), es~load_uint(32), es~load_grams(), es~load_grams(), es~load_dict(), es~load_int(1), es~load_int(1));
|
||||
es.end_parse();
|
||||
return res;
|
||||
}
|
||||
|
||||
cell pack_elect(elect_at, elect_close, min_stake, total_stake, members, failed, finished) {
|
||||
return begin_cell()
|
||||
.store_uint(elect_at, 32)
|
||||
.store_uint(elect_close, 32)
|
||||
.store_grams(min_stake)
|
||||
.store_grams(total_stake)
|
||||
.store_dict(members)
|
||||
.store_int(failed, 1)
|
||||
.store_int(finished, 1)
|
||||
.end_cell();
|
||||
}
|
||||
|
||||
;; elected_for elections_begin_before elections_end_before stake_held_for
|
||||
(int, int, int, int) get_validator_conf() {
|
||||
var cs = config_param(15).begin_parse();
|
||||
return (cs~load_int(32), cs~load_int(32), cs~load_int(32), cs.preload_int(32));
|
||||
}
|
||||
|
||||
() send_message_back(addr, ans_tag, query_id, body, grams, mode) impure {
|
||||
;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
|
||||
var msg = begin_cell()
|
||||
.store_uint(0x18, 6)
|
||||
.store_slice(addr)
|
||||
.store_grams(grams)
|
||||
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
||||
.store_uint(ans_tag, 32)
|
||||
.store_uint(query_id, 64);
|
||||
if (body >= 0) {
|
||||
msg~store_uint(body, 32);
|
||||
}
|
||||
send_raw_message(msg.end_cell(), mode);
|
||||
}
|
||||
|
||||
() return_stake(addr, query_id, reason) impure {
|
||||
return send_message_back(addr, 0xee6f454c, query_id, reason, 0, 64);
|
||||
}
|
||||
|
||||
() send_confirmation(addr, query_id, comment) impure {
|
||||
return send_message_back(addr, 0xf374484c, query_id, comment, 1000000000, 2);
|
||||
}
|
||||
|
||||
() send_validator_set_to_config(config_addr, vset, query_id) impure {
|
||||
var msg = begin_cell()
|
||||
.store_uint(0xc4ff, 17) ;; 0 11000100 0xff
|
||||
.store_uint(config_addr, 256)
|
||||
.store_grams(1 << 30) ;; ~1 gram of value to process and obtain answer
|
||||
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
||||
.store_uint(0x4e565354, 32)
|
||||
.store_uint(query_id, 64)
|
||||
.store_ref(vset);
|
||||
send_raw_message(msg.end_cell(), 1);
|
||||
}
|
||||
|
||||
;; credits 'amount' to 'addr' inside credit dictionary 'credits'
|
||||
_ ~credit_to(credits, addr, amount) {
|
||||
var (val, f) = credits.udict_get?(256, addr);
|
||||
if (f) {
|
||||
amount += val~load_grams();
|
||||
}
|
||||
credits~udict_set_builder(256, addr, begin_cell().store_grams(amount));
|
||||
return (credits, ());
|
||||
}
|
||||
|
||||
() process_new_stake(s_addr, msg_value, cs, query_id) impure {
|
||||
var (src_wc, src_addr) = parse_std_addr(s_addr);
|
||||
var ds = get_data().begin_parse();
|
||||
var elect = ds~load_dict();
|
||||
if (null?(elect) | (src_wc + 1)) {
|
||||
;; no elections active, or source is not in masterchain
|
||||
;; bounce message
|
||||
return return_stake(s_addr, query_id, 0);
|
||||
}
|
||||
;; parse the remainder of new stake message
|
||||
var validator_pubkey = cs~load_uint(256);
|
||||
var stake_at = cs~load_uint(32);
|
||||
var max_factor = cs~load_uint(32);
|
||||
var adnl_addr = cs~load_uint(256);
|
||||
var signature = cs~load_ref().begin_parse().preload_bits(512);
|
||||
cs.end_parse();
|
||||
ifnot (check_data_signature(begin_cell()
|
||||
.store_uint(0x654c5074, 32)
|
||||
.store_uint(stake_at, 32)
|
||||
.store_uint(max_factor, 32)
|
||||
.store_uint(src_addr, 256)
|
||||
.store_uint(adnl_addr, 256)
|
||||
.end_cell().begin_parse(), signature, validator_pubkey)) {
|
||||
;; incorrect signature, return stake
|
||||
return return_stake(s_addr, query_id, 1);
|
||||
}
|
||||
if (max_factor < 0x10000) {
|
||||
;; factor must be >= 1. = 65536/65536
|
||||
return return_stake(s_addr, query_id, 6);
|
||||
}
|
||||
;; parse current election data
|
||||
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
|
||||
elect_at~dump();
|
||||
msg_value -= 1000000000; ;; deduct GR$1 for sending confirmation
|
||||
if ((msg_value << 12) < total_stake) {
|
||||
;; stake smaller than 1/4096 of the total accumulated stakes, return
|
||||
return return_stake(s_addr, query_id, 2);
|
||||
}
|
||||
total_stake += msg_value; ;; (provisionally) increase total stake
|
||||
if (stake_at != elect_at) {
|
||||
;; stake for some other elections, return
|
||||
return return_stake(s_addr, query_id, 3);
|
||||
}
|
||||
if (finished) {
|
||||
;; elections already finished, return stake
|
||||
return return_stake(s_addr, query_id, 0);
|
||||
}
|
||||
var (mem, found) = members.udict_get?(256, validator_pubkey);
|
||||
if (found) {
|
||||
;; entry found, merge stakes
|
||||
msg_value += mem~load_grams();
|
||||
mem~load_uint(64); ;; skip timestamp and max_factor
|
||||
found = (src_addr != mem~load_uint(256));
|
||||
}
|
||||
if (found) {
|
||||
;; can make stakes for a public key from one address only
|
||||
return return_stake(s_addr, query_id, 4);
|
||||
}
|
||||
if (msg_value < min_stake) {
|
||||
;; stake too small, return it
|
||||
return return_stake(s_addr, query_id, 5);
|
||||
}
|
||||
throw_unless(44, msg_value);
|
||||
accept_message();
|
||||
;; store stake in the dictionary
|
||||
members~udict_set_builder(256, validator_pubkey, begin_cell()
|
||||
.store_grams(msg_value)
|
||||
.store_uint(now(), 32)
|
||||
.store_uint(max_factor, 32)
|
||||
.store_uint(src_addr, 256)
|
||||
.store_uint(adnl_addr, 256));
|
||||
;; gather and save election data
|
||||
elect = pack_elect(elect_at, elect_close, min_stake, total_stake, members, false, false);
|
||||
set_data(begin_cell().store_dict(elect).store_slice(ds).end_cell());
|
||||
;; return confirmation message
|
||||
if (query_id) {
|
||||
return send_confirmation(s_addr, query_id, 0);
|
||||
}
|
||||
return ();
|
||||
}
|
||||
|
||||
(cell, int) unfreeze_without_bonuses(credits, freeze_dict, tot_stakes) {
|
||||
var total = var recovered = 0;
|
||||
var pubkey = -1;
|
||||
do {
|
||||
(pubkey, var cs, var f) = freeze_dict.udict_get_next?(256, pubkey);
|
||||
if (f) {
|
||||
var (addr, weight, stake, banned) = (cs~load_uint(256), cs~load_uint(64), cs~load_grams(), cs~load_int(1));
|
||||
cs.end_parse();
|
||||
if (banned) {
|
||||
recovered += stake;
|
||||
} else {
|
||||
credits~credit_to(addr, stake);
|
||||
}
|
||||
total += stake;
|
||||
}
|
||||
} until (~ f);
|
||||
throw_unless(59, total == tot_stakes);
|
||||
return (credits, recovered);
|
||||
}
|
||||
|
||||
(cell, int) unfreeze_with_bonuses(credits, freeze_dict, tot_stakes, tot_bonuses) {
|
||||
var total = var recovered = var returned_bonuses = 0;
|
||||
var pubkey = -1;
|
||||
do {
|
||||
(pubkey, var cs, var f) = freeze_dict.udict_get_next?(256, pubkey);
|
||||
if (f) {
|
||||
var (addr, weight, stake, banned) = (cs~load_uint(256), cs~load_uint(64), cs~load_grams(), cs~load_int(1));
|
||||
cs.end_parse();
|
||||
if (banned) {
|
||||
recovered += stake;
|
||||
} else {
|
||||
var bonus = muldiv(tot_bonuses, stake, tot_stakes);
|
||||
returned_bonuses += bonus;
|
||||
credits~credit_to(addr, stake + bonus);
|
||||
}
|
||||
total += stake;
|
||||
}
|
||||
} until (~ f);
|
||||
throw_unless(59, (total == tot_stakes) & (returned_bonuses <= tot_bonuses));
|
||||
return (credits, recovered + tot_bonuses - returned_bonuses);
|
||||
}
|
||||
|
||||
_ unfreeze_all(credits, past_elections, elect_id) {
|
||||
var (fs, f) = past_elections~udict_delete_get?(32, elect_id);
|
||||
ifnot (f) {
|
||||
;; no elections with this id
|
||||
return (credits, past_elections, 0);
|
||||
}
|
||||
var (data1, vset_hash, fdict, tot_stakes, bonuses, complaints) = (fs~load_uint(64), fs~load_uint(256), fs~load_dict(), fs~load_grams(), fs~load_grams(), fs~load_dict());
|
||||
fs.end_parse();
|
||||
var unused_prizes = (bonuses > 0) ?
|
||||
credits~unfreeze_with_bonuses(fdict, tot_stakes, bonuses) :
|
||||
credits~unfreeze_without_bonuses(fdict, tot_stakes);
|
||||
return (credits, past_elections, unused_prizes);
|
||||
}
|
||||
|
||||
() config_set_confirmed(s_addr, cs, query_id, ok) impure {
|
||||
var (src_wc, src_addr) = parse_std_addr(s_addr);
|
||||
var config_addr = config_param(0).begin_parse().preload_uint(256);
|
||||
var ds = get_data().begin_parse();
|
||||
var elect = ds~load_dict();
|
||||
if ((src_wc + 1) | (src_addr != config_addr) | elect.null?()) {
|
||||
;; not from config smc, somebody's joke?
|
||||
;; or no elections active (or just completed)
|
||||
return ();
|
||||
}
|
||||
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
|
||||
if ((elect_at != query_id) | ~ finished) {
|
||||
;; not these elections, or elections not finished yet
|
||||
return ();
|
||||
}
|
||||
accept_message();
|
||||
ifnot (ok) {
|
||||
;; cancel elections, return stakes
|
||||
var (credits, past_elections, grams) = (ds~load_dict(), ds~load_dict(), ds~load_grams());
|
||||
(credits, past_elections, var unused_prizes) = unfreeze_all(credits, past_elections, elect_at);
|
||||
set_data(begin_cell()
|
||||
.store_int(false, 1)
|
||||
.store_dict(credits)
|
||||
.store_dict(past_elections)
|
||||
.store_grams(grams + unused_prizes)
|
||||
.store_slice(ds)
|
||||
.end_cell());
|
||||
}
|
||||
;; ... do not remove elect until we see this set as the next elected validator set
|
||||
}
|
||||
|
||||
() process_simple_transfer(s_addr, msg_value) impure {
|
||||
var (elect, credits, past_elect, grams, active_id, active_hash) = load_data();
|
||||
(int src_wc, int src_addr) = parse_std_addr(s_addr);
|
||||
if (src_addr | (src_wc + 1) | (active_id == 0)) {
|
||||
;; simple transfer to us (credit "nobody's" account)
|
||||
;; (or no known active validator set)
|
||||
grams += msg_value;
|
||||
return store_data(elect, credits, past_elect, grams, active_id, active_hash);
|
||||
}
|
||||
;; zero source address -1:00..00 (collecting validator fees)
|
||||
var (fs, f) = past_elect.udict_get?(32, active_id);
|
||||
ifnot (f) {
|
||||
;; active validator set not found (?)
|
||||
grams += msg_value;
|
||||
} else {
|
||||
;; credit active validator set bonuses
|
||||
var (data, hash, dict, total_stake, bonuses) = (fs~load_uint(64), fs~load_uint(256), fs~load_dict(), fs~load_grams(), fs~load_grams());
|
||||
bonuses += msg_value;
|
||||
past_elect~udict_set_builder(32, active_id, begin_cell()
|
||||
.store_uint(data, 64)
|
||||
.store_uint(hash, 256)
|
||||
.store_dict(dict)
|
||||
.store_grams(total_stake)
|
||||
.store_grams(bonuses)
|
||||
.store_slice(fs));
|
||||
}
|
||||
store_data(elect, credits, past_elect, grams, active_id, active_hash);
|
||||
return ();
|
||||
}
|
||||
|
||||
() recover_stake(op, s_addr, cs, query_id) impure {
|
||||
(int src_wc, int src_addr) = parse_std_addr(s_addr);
|
||||
if (src_wc + 1) {
|
||||
;; not from masterchain, return error
|
||||
return send_message_back(s_addr, 0xfffffffe, query_id, op, 0, 64);
|
||||
}
|
||||
var ds = get_data().begin_parse();
|
||||
var (elect, credits) = (ds~load_dict(), ds~load_dict());
|
||||
var (cs, f) = credits~udict_delete_get?(256, src_addr);
|
||||
ifnot (f) {
|
||||
;; no credit for sender, return error
|
||||
return send_message_back(s_addr, 0xfffffffe, query_id, op, 0, 64);
|
||||
}
|
||||
var amount = cs~load_grams();
|
||||
cs.end_parse();
|
||||
;; save data
|
||||
set_data(begin_cell().store_dict(elect).store_dict(credits).store_slice(ds).end_cell());
|
||||
;; send amount to sender in a new message
|
||||
send_raw_message(begin_cell()
|
||||
.store_uint(0x18, 6)
|
||||
.store_slice(s_addr)
|
||||
.store_grams(amount)
|
||||
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
||||
.store_uint(0xf96f7324, 32)
|
||||
.store_uint(query_id, 64)
|
||||
.end_cell(), 64);
|
||||
}
|
||||
|
||||
() after_code_upgrade(slice s_addr, slice cs, int query_id) impure method_id(1666) {
|
||||
var op = 0x4e436f64;
|
||||
return send_message_back(s_addr, 0xce436f64, query_id, op, 0, 64);
|
||||
}
|
||||
|
||||
int upgrade_code(s_addr, cs, query_id) {
|
||||
var c_addr = config_param(0);
|
||||
if (c_addr.null?()) {
|
||||
;; no configuration smart contract known
|
||||
return false;
|
||||
}
|
||||
var config_addr = c_addr.begin_parse().preload_uint(256);
|
||||
var (src_wc, src_addr) = parse_std_addr(s_addr);
|
||||
if ((src_wc + 1) | (src_addr != config_addr)) {
|
||||
;; not from configuration smart contract, return error
|
||||
return false;
|
||||
}
|
||||
accept_message();
|
||||
var code = cs~load_ref();
|
||||
set_code(code);
|
||||
ifnot(cs.slice_empty?()) {
|
||||
set_c3(code);
|
||||
;; run_method3(1666, s_addr, cs, query_id);
|
||||
after_code_upgrade(s_addr, cs, query_id);
|
||||
throw(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
var cs = in_msg_cell.begin_parse();
|
||||
var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
|
||||
if (flags & 1) {
|
||||
;; ignore all bounced messages
|
||||
return ();
|
||||
}
|
||||
var s_addr = cs~load_msg_addr();
|
||||
if (in_msg.slice_empty?()) {
|
||||
;; inbound message has empty body
|
||||
return process_simple_transfer(s_addr, msg_value);
|
||||
}
|
||||
int op = in_msg~load_uint(32);
|
||||
if (op == 0) {
|
||||
;; simple transfer with comment, return
|
||||
return process_simple_transfer(s_addr, msg_value);
|
||||
}
|
||||
int query_id = in_msg~load_uint(64);
|
||||
if (op == 0x4e73744b) {
|
||||
;; new stake message
|
||||
return process_new_stake(s_addr, msg_value, in_msg, query_id);
|
||||
}
|
||||
if (op == 0x47657424) {
|
||||
;; recover stake request
|
||||
return recover_stake(op, s_addr, in_msg, query_id);
|
||||
}
|
||||
if (op == 0x4e436f64) {
|
||||
;; upgrade code (accepted only from configuration smart contract)
|
||||
var ok = upgrade_code(s_addr, in_msg, query_id);
|
||||
return send_message_back(s_addr, ok ? 0xce436f64 : 0xffffffff, query_id, op, 0, 64);
|
||||
}
|
||||
var cfg_ok = (op == 0xee764f4b);
|
||||
if (cfg_ok | (op == 0xee764f6f)) {
|
||||
;; confirmation from configuration smart contract
|
||||
return config_set_confirmed(s_addr, in_msg, query_id, cfg_ok);
|
||||
}
|
||||
ifnot (op & (1 << 31)) {
|
||||
;; unknown query, return error
|
||||
return send_message_back(s_addr, 0xffffffff, query_id, op, 0, 64);
|
||||
}
|
||||
;; unknown answer, ignore
|
||||
return ();
|
||||
}
|
||||
|
||||
int postpone_elections() impure {
|
||||
return false;
|
||||
}
|
||||
|
||||
;; computes the total stake out of the first n entries of list l
|
||||
_ compute_total_stake(l, n, m_stake) {
|
||||
int tot_stake = 0;
|
||||
repeat (n) {
|
||||
(var h, l) = uncons(l);
|
||||
var stake = h.at(0);
|
||||
var max_f = h.at(1);
|
||||
stake = min(stake, (max_f * m_stake) >> 16);
|
||||
tot_stake += stake;
|
||||
}
|
||||
return tot_stake;
|
||||
}
|
||||
|
||||
(cell, cell, cell, int, int) try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor) {
|
||||
var cs = 16.config_param().begin_parse();
|
||||
var (max_validators, _, min_validators) = (cs~load_uint(16), cs~load_uint(16), cs~load_uint(16));
|
||||
cs.end_parse();
|
||||
min_validators = max(min_validators, 1);
|
||||
int n = 0;
|
||||
var sdict = new_dict();
|
||||
var pubkey = -1;
|
||||
do {
|
||||
(pubkey, var cs, var f) = members.udict_get_next?(256, pubkey);
|
||||
if (f) {
|
||||
var (stake, time, max_factor, addr, adnl_addr) = (cs~load_grams(), cs~load_uint(32), cs~load_uint(32), cs~load_uint(256), cs~load_uint(256));
|
||||
cs.end_parse();
|
||||
var key = begin_cell()
|
||||
.store_uint(stake, 128)
|
||||
.store_int(- time, 32)
|
||||
.store_uint(pubkey, 256)
|
||||
.end_cell().begin_parse();
|
||||
sdict~dict_set_builder(128 + 32 + 256, key, begin_cell()
|
||||
.store_uint(min(max_factor, max_stake_factor), 32)
|
||||
.store_uint(addr, 256)
|
||||
.store_uint(adnl_addr, 256));
|
||||
n += 1;
|
||||
}
|
||||
} until (~ f);
|
||||
n = min(n, max_validators);
|
||||
if (n < min_validators) {
|
||||
return (credits, new_dict(), new_dict(), 0, 0);
|
||||
}
|
||||
var l = nil;
|
||||
do {
|
||||
var (key, cs, f) = sdict~dict::delete_get_min(128 + 32 + 256);
|
||||
if (f) {
|
||||
var (stake, _, pubkey) = (min(key~load_uint(128), max_stake), key~load_uint(32), key.preload_uint(256));
|
||||
var (max_f, _, adnl_addr) = (cs~load_uint(32), cs~load_uint(256), cs.preload_uint(256));
|
||||
l = cons(tuple4(stake, max_f, pubkey, adnl_addr), l);
|
||||
}
|
||||
} until (~ f);
|
||||
;; l is the list of all stakes in decreasing order
|
||||
int i = min_validators - 1;
|
||||
var l1 = l;
|
||||
repeat (i) {
|
||||
l1 = cdr(l1);
|
||||
}
|
||||
var (best_stake, m) = (0, 0);
|
||||
do {
|
||||
var stake = l1~list_next().at(0);
|
||||
i += 1;
|
||||
if (stake >= min_stake) {
|
||||
var tot_stake = compute_total_stake(l, i, stake);
|
||||
if (tot_stake > best_stake) {
|
||||
(best_stake, m) = (tot_stake, i);
|
||||
}
|
||||
}
|
||||
} until (i >= n);
|
||||
if ((m == 0) | (best_stake < min_total_stake)) {
|
||||
return (credits, new_dict(), new_dict(), 0, 0);
|
||||
}
|
||||
;; we have to select first m validators from list l
|
||||
l1 = touch(l);
|
||||
l1~dump(); ;; DEBUG
|
||||
repeat (m - 1) {
|
||||
l1 = cdr(l1);
|
||||
}
|
||||
var m_stake = car(l1).at(0); ;; minimal stake
|
||||
;; create both the new validator set and the refund set
|
||||
int i = 0;
|
||||
var tot_stake = 0;
|
||||
var vset = new_dict();
|
||||
var frozen = new_dict();
|
||||
do {
|
||||
var (stake, max_f, pubkey, adnl_addr) = l~list_next().untuple4();
|
||||
;; lookup source address first
|
||||
var (val, f) = members.udict_get?(256, pubkey);
|
||||
throw_unless(61, f);
|
||||
(_, _, var src_addr) = (val~load_grams(), val~load_uint(64), val.preload_uint(256));
|
||||
if (i < m) {
|
||||
;; one of the first m members, include into validator set
|
||||
var true_stake = min(stake, (max_f * m_stake) >> 16);
|
||||
stake -= true_stake;
|
||||
;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey; // 288 bits
|
||||
;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
|
||||
var weight = (true_stake << 60) / best_stake;
|
||||
tot_stake += true_stake;
|
||||
var vinfo = begin_cell()
|
||||
.store_uint(adnl_addr ? 0x73 : 0x53, 8) ;; validator_addr#73 or validator#53
|
||||
.store_uint(0x8e81278a, 32) ;; ed25519_pubkey#8e81278a
|
||||
.store_uint(pubkey, 256) ;; pubkey:bits256
|
||||
.store_uint(weight, 64); ;; weight:uint64
|
||||
if (adnl_addr) {
|
||||
vinfo~store_uint(adnl_addr, 256); ;; adnl_addr:bits256
|
||||
}
|
||||
vset~udict_set_builder(16, i, vinfo);
|
||||
frozen~udict_set_builder(256, pubkey, begin_cell()
|
||||
.store_uint(src_addr, 256)
|
||||
.store_uint(weight, 64)
|
||||
.store_grams(true_stake)
|
||||
.store_int(false, 1));
|
||||
}
|
||||
if (stake) {
|
||||
;; non-zero unused part of the stake, credit to the source address
|
||||
credits~credit_to(src_addr, stake);
|
||||
}
|
||||
i += 1;
|
||||
} until (l.null?());
|
||||
throw_unless(49, tot_stake == best_stake);
|
||||
return (credits, vset, frozen, tot_stake, m);
|
||||
}
|
||||
|
||||
int conduct_elections(ds, elect, credits) impure {
|
||||
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
|
||||
if (now() < elect_close) {
|
||||
;; elections not finished yet
|
||||
return false;
|
||||
}
|
||||
if (config_param(0).null?()) {
|
||||
;; no configuration smart contract to send result to
|
||||
return postpone_elections();
|
||||
}
|
||||
var cs = config_param(17).begin_parse();
|
||||
min_stake = cs~load_grams();
|
||||
var max_stake = cs~load_grams();
|
||||
var min_total_stake = cs~load_grams();
|
||||
var max_stake_factor = cs~load_uint(32);
|
||||
cs.end_parse();
|
||||
if (total_stake < min_total_stake) {
|
||||
;; insufficient total stake, postpone elections
|
||||
return postpone_elections();
|
||||
}
|
||||
if (failed) {
|
||||
;; do not retry failed elections until new stakes arrive
|
||||
return postpone_elections();
|
||||
}
|
||||
if (finished) {
|
||||
;; elections finished
|
||||
return false;
|
||||
}
|
||||
(credits, var vdict, var frozen, var total_stakes, var cnt) = try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor);
|
||||
;; pack elections; if cnt==0, set failed=true, finished=false.
|
||||
failed = (cnt == 0);
|
||||
finished = ~ failed;
|
||||
elect = pack_elect(elect_at, elect_close, min_stake, total_stake, members, failed, finished);
|
||||
ifnot (cnt) {
|
||||
;; elections failed, set elect_failed to true
|
||||
set_data(begin_cell().store_dict(elect).store_dict(credits).store_slice(ds).end_cell());
|
||||
return postpone_elections();
|
||||
}
|
||||
;; serialize a query to the configuration smart contract
|
||||
;; to install the computed validator set as the next validator set
|
||||
var (elect_for, elect_begin_before, elect_end_before, stake_held) = get_validator_conf();
|
||||
var start = max(now() + elect_end_before - 60, elect_at);
|
||||
var main_validators = config_param(16).begin_parse().skip_bits(16).preload_uint(16);
|
||||
var vset = begin_cell()
|
||||
.store_uint(0x11, 8) ;; validators#11
|
||||
.store_uint(start, 32) ;; utime_since:uint32
|
||||
.store_uint(start + elect_for, 32) ;; utime_until:uint32
|
||||
.store_uint(cnt, 16) ;; total:(## 16)
|
||||
.store_uint(min(cnt, main_validators), 16) ;; main:(## 16)
|
||||
.store_slice(vdict.begin_parse()) ;; list:(Hashmap 16 ValidatorDescr)
|
||||
.end_cell();
|
||||
var config_addr = config_param(0).begin_parse().preload_uint(256);
|
||||
send_validator_set_to_config(config_addr, vset, elect_at);
|
||||
;; add frozen to the dictionary of past elections
|
||||
var past_elect = ds~load_dict();
|
||||
past_elect~udict_set_builder(32, elect_at, begin_cell()
|
||||
.store_uint(start + elect_for + stake_held, 32)
|
||||
.store_uint(stake_held, 32)
|
||||
.store_uint(cell_hash(vset), 256)
|
||||
.store_dict(frozen)
|
||||
.store_grams(total_stakes)
|
||||
.store_grams(0)
|
||||
.store_int(false, 1));
|
||||
;; store credits and frozen until end
|
||||
set_data(begin_cell()
|
||||
.store_dict(elect)
|
||||
.store_dict(credits)
|
||||
.store_dict(past_elect)
|
||||
.store_slice(ds)
|
||||
.end_cell());
|
||||
return true;
|
||||
}
|
||||
|
||||
int update_active_vset_id() impure {
|
||||
var (elect, credits, past_elect, grams, active_id, active_hash) = load_data();
|
||||
var cur_hash = config_param(34).cell_hash();
|
||||
if (cur_hash == active_hash) {
|
||||
;; validator set unchanged
|
||||
return false;
|
||||
}
|
||||
if (active_id) {
|
||||
;; active_id becomes inactive
|
||||
var (fs, f) = past_elect.udict_get?(32, active_id);
|
||||
if (f) {
|
||||
;; adjust unfreeze time of this validator set
|
||||
var unfreeze_time = fs~load_uint(32);
|
||||
var fs0 = fs;
|
||||
var (stake_held, hash) = (fs~load_uint(32), fs~load_uint(256));
|
||||
throw_unless(57, hash == active_hash);
|
||||
unfreeze_time = now() + stake_held;
|
||||
past_elect~udict_set_builder(32, active_id, begin_cell()
|
||||
.store_uint(unfreeze_time, 32)
|
||||
.store_slice(fs0));
|
||||
}
|
||||
}
|
||||
;; look up new active_id by hash
|
||||
var id = -1;
|
||||
do {
|
||||
(id, var fs, var f) = past_elect.udict_get_next?(32, id);
|
||||
if (f) {
|
||||
var (tm, hash) = (fs~load_uint(64), fs~load_uint(256));
|
||||
if (hash == cur_hash) {
|
||||
;; parse more of this record
|
||||
var (dict, total_stake, bonuses) = (fs~load_dict(), fs~load_grams(), fs~load_grams());
|
||||
;; transfer 1/8 of accumulated everybody's grams to this validator set as bonuses
|
||||
var amount = (grams >> 3);
|
||||
grams -= amount;
|
||||
bonuses += amount;
|
||||
;; serialize back
|
||||
past_elect~udict_set_builder(32, id, begin_cell()
|
||||
.store_uint(tm, 64)
|
||||
.store_uint(hash, 256)
|
||||
.store_dict(dict)
|
||||
.store_grams(total_stake)
|
||||
.store_grams(bonuses)
|
||||
.store_slice(fs));
|
||||
;; found
|
||||
f = false;
|
||||
}
|
||||
}
|
||||
} until (~ f);
|
||||
active_id = (id.null?() ? 0 : id);
|
||||
active_hash = cur_hash;
|
||||
store_data(elect, credits, past_elect, grams, active_id, active_hash);
|
||||
return true;
|
||||
}
|
||||
|
||||
int cell_hash_eq?(cell vset, int expected_vset_hash) {
|
||||
return vset.null?() ? false : cell_hash(vset) == expected_vset_hash;
|
||||
}
|
||||
|
||||
int validator_set_installed(ds, elect, credits) impure {
|
||||
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
|
||||
ifnot (finished) {
|
||||
;; elections not finished yet
|
||||
return false;
|
||||
}
|
||||
var past_elections = ds~load_dict();
|
||||
var (fs, f) = past_elections.udict_get?(32, elect_at);
|
||||
ifnot (f) {
|
||||
;; no election data in dictionary
|
||||
return false;
|
||||
}
|
||||
;; recover validator set hash
|
||||
var vset_hash = fs.skip_bits(64).preload_uint(256);
|
||||
if (config_param(34).cell_hash_eq?(vset_hash) | config_param(36).cell_hash_eq?(vset_hash)) {
|
||||
;; this validator set has been installed, forget elections
|
||||
set_data(begin_cell()
|
||||
.store_int(false, 1) ;; forget current elections
|
||||
.store_dict(credits)
|
||||
.store_dict(past_elections)
|
||||
.store_slice(ds)
|
||||
.end_cell());
|
||||
update_active_vset_id();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int check_unfreeze() impure {
|
||||
var (elect, credits, past_elect, grams, active_id, active_hash) = load_data();
|
||||
int id = -1;
|
||||
do {
|
||||
(id, var fs, var f) = past_elect.udict_get_next?(32, id);
|
||||
if (f) {
|
||||
var unfreeze_at = fs~load_uint(32);
|
||||
if ((unfreeze_at <= now()) & (id != active_id)) {
|
||||
;; unfreeze!
|
||||
(credits, past_elect, var unused_prizes) = unfreeze_all(credits, past_elect, id);
|
||||
grams += unused_prizes;
|
||||
;; unfreeze only one at time, exit loop
|
||||
store_data(elect, credits, past_elect, grams, active_id, active_hash);
|
||||
;; exit loop
|
||||
f = false;
|
||||
}
|
||||
}
|
||||
} until (~ f);
|
||||
return ~ id.null?();
|
||||
}
|
||||
|
||||
int announce_new_elections(ds, elect, credits) {
|
||||
var next_vset = config_param(36); ;; next validator set
|
||||
ifnot (next_vset.null?()) {
|
||||
;; next validator set exists, no elections needed
|
||||
return false;
|
||||
}
|
||||
var elector_addr = config_param(1).begin_parse().preload_uint(256);
|
||||
var (my_wc, my_addr) = my_address().parse_std_addr();
|
||||
if ((my_wc + 1) | (my_addr != elector_addr)) {
|
||||
;; this smart contract is not the elections smart contract anymore, no new elections
|
||||
return false;
|
||||
}
|
||||
var cur_vset = config_param(34); ;; current validator set
|
||||
if (cur_vset.null?()) {
|
||||
return false;
|
||||
}
|
||||
var (elect_for, elect_begin_before, elect_end_before, stake_held) = get_validator_conf();
|
||||
var cur_valid_until = cur_vset.begin_parse().skip_bits(8 + 32).preload_uint(32);
|
||||
var t = now();
|
||||
var t0 = cur_valid_until - elect_begin_before;
|
||||
if (t < t0) {
|
||||
;; too early for the next elections
|
||||
return false;
|
||||
}
|
||||
;; less than elect_before_begin seconds left, create new elections
|
||||
if (t - t0 < 60) {
|
||||
;; pretend that the elections started at t0
|
||||
t = t0;
|
||||
}
|
||||
;; get stake parameters
|
||||
(_, var min_stake) = config_param(17).begin_parse().load_grams();
|
||||
;; announce new elections
|
||||
var elect_at = t + elect_begin_before;
|
||||
elect_at~dump();
|
||||
var elect_close = elect_at - elect_end_before;
|
||||
elect = pack_elect(elect_at, elect_close, min_stake, 0, new_dict(), false, false);
|
||||
set_data(begin_cell().store_dict(elect).store_dict(credits).store_slice(ds).end_cell());
|
||||
return true;
|
||||
}
|
||||
|
||||
() run_ticktock(int is_tock) impure {
|
||||
;; check whether an election is being conducted
|
||||
var ds = get_data().begin_parse();
|
||||
var (elect, credits) = (ds~load_dict(), ds~load_dict());
|
||||
ifnot (elect.null?()) {
|
||||
;; have an active election
|
||||
throw_if(0, conduct_elections(ds, elect, credits)); ;; elections conducted, exit
|
||||
throw_if(0, validator_set_installed(ds, elect, credits)); ;; validator set installed, current elections removed
|
||||
} else {
|
||||
throw_if(0, announce_new_elections(ds, elect, credits)); ;; new elections announced, exit
|
||||
}
|
||||
throw_if(0, update_active_vset_id()); ;; active validator set id updated, exit
|
||||
check_unfreeze();
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
;; returns active election id or 0
|
||||
int active_election_id() method_id {
|
||||
var elect = get_data().begin_parse().preload_dict();
|
||||
return elect.null?() ? 0 : elect.begin_parse().preload_uint(32);
|
||||
}
|
||||
|
||||
;; checks whether a public key participates in current elections
|
||||
int participates_in(int validator_pubkey) method_id {
|
||||
var elect = get_data().begin_parse().preload_dict();
|
||||
if (elect.null?()) {
|
||||
return 0;
|
||||
}
|
||||
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
|
||||
var (mem, found) = members.udict_get?(256, validator_pubkey);
|
||||
return found ? mem~load_grams() : 0;
|
||||
}
|
||||
|
||||
;; returns the list of all participants of current elections with their stakes
|
||||
_ participant_list() method_id {
|
||||
var elect = get_data().begin_parse().preload_dict();
|
||||
if (elect.null?()) {
|
||||
return nil;
|
||||
}
|
||||
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
|
||||
var l = nil;
|
||||
var id = (1 << 255) + ((1 << 255) - 1);
|
||||
do {
|
||||
(id, var fs, var f) = members.udict_get_prev?(256, id);
|
||||
if (f) {
|
||||
l = cons(pair(id, fs~load_grams()), l);
|
||||
}
|
||||
} until (~ f);
|
||||
return l;
|
||||
}
|
||||
|
||||
;; computes the return stake
|
||||
int compute_returned_stake(int wallet_addr) method_id {
|
||||
var cs = get_data().begin_parse();
|
||||
(_, var credits) = (cs~load_dict(), cs~load_dict());
|
||||
var (val, f) = credits.udict_get?(256, wallet_addr);
|
||||
return f ? val~load_grams() : 0;
|
||||
}
|
238
crypto/smartcont/gen-zerostate-test.fif
Normal file
238
crypto/smartcont/gen-zerostate-test.fif
Normal file
|
@ -0,0 +1,238 @@
|
|||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
def? $1 { @' $1 } { "" } cond constant suffix
|
||||
{ suffix $+ } : +suffix
|
||||
|
||||
wc_master setworkchain
|
||||
-17 setglobalid // negative value means a test instance of the blockchain
|
||||
|
||||
// Initial state of Workchain 0 (Basic workchain)
|
||||
|
||||
0 mkemptyShardState
|
||||
|
||||
cr ."initial basechain state is:" cr dup <s csr. cr
|
||||
dup dup 31 boc+>B dup Bx. cr
|
||||
dup "basestate0" +suffix +".boc" tuck B>file
|
||||
."(Initial basechain state saved to file " type .")" cr
|
||||
Bhash dup =: basestate0_fhash
|
||||
."file hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"basestate0" +suffix +".fhash" B>file
|
||||
hash dup =: basestate0_rhash
|
||||
."root hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"basestate0" +suffix +".rhash" B>file
|
||||
|
||||
basestate0_rhash basestate0_fhash now 0 2 32 0 add-std-workchain
|
||||
config.workchains!
|
||||
|
||||
// SmartContract #1 (Simple wallet)
|
||||
|
||||
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
|
||||
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
|
||||
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
|
||||
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
|
||||
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
s2 PUSH HASHSU // sign cs cnt pubk hash
|
||||
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
|
||||
CHKSIGNU // pubk cs cnt ?
|
||||
34 THROWIFNOT // signature mismatch
|
||||
ACCEPT
|
||||
SWAP 32 LDU NIP 8 LDU LDREF ENDS // pubk cnt mode msg
|
||||
SWAP SENDRAWMSG // pubk cnt ; ( message sent )
|
||||
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
|
||||
}>c
|
||||
// code
|
||||
<b 0 32 u,
|
||||
"main-wallet" +suffix +".pk" load-generate-keypair drop
|
||||
B,
|
||||
b> // data
|
||||
Libs{
|
||||
x{ABACABADABACABA} s>c public_lib
|
||||
x{1234} x{5678} |_ s>c private_lib
|
||||
}Libs // libraries
|
||||
GR$1700000000 // balance
|
||||
0 // split_depth
|
||||
0 // ticktock
|
||||
2 // mode: create
|
||||
register_smc
|
||||
dup make_special dup constant smc1_addr
|
||||
Masterchain over
|
||||
2dup ."wallet address = " .addr cr 2dup 6 .Addr cr
|
||||
"main-wallet" +suffix +".addr" save-address-verbose
|
||||
|
||||
// SmartContract #2 (Simple money giver for test network)
|
||||
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
|
||||
32 LDU SWAP // cs cnt
|
||||
c4 PUSHCTR CTOS 32 LDU ENDS // cs cnt cnt'
|
||||
TUCK EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
ACCEPT // cs cnt'
|
||||
SWAP 8 LDU LDREF ENDS // cnt'' mode msg
|
||||
GR$20 INT 3 INT RAWRESERVE // reserve all but 20 Grams from the balance
|
||||
SWAP SENDRAWMSG
|
||||
INC NEWC 32 STU ENDC c4 POPCTR // store cnt''
|
||||
}>c
|
||||
// code
|
||||
<b 0 32 u, b> // data
|
||||
empty_cell // libraries
|
||||
GR$1000000 // initial balance (1m test Grams)
|
||||
0 0 2 register_smc
|
||||
dup make_special dup constant smc2_addr
|
||||
Masterchain over
|
||||
2dup ."free test gram giver address = " .addr cr 2dup 6 .Addr cr
|
||||
"testgiver" +suffix +".addr" save-address-verbose
|
||||
|
||||
// SmartContract #3
|
||||
PROGRAM{
|
||||
recv_internal x{} PROC
|
||||
run_ticktock PROC:<{
|
||||
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS
|
||||
NEWC ROT INC 32 STUR OVER 256 STUR ENDC
|
||||
c4 POPCTR
|
||||
// first 32 bits of persistent data have been increased
|
||||
// remaining 256 bits with an address have been fetched
|
||||
// create new empty message with 0.1 Grams to that address
|
||||
NEWC b{00100010011111111} STSLICECONST TUCK 256 STU
|
||||
100000000 INT STGRAMS // store 0.1 Grams
|
||||
1 4 + 4 + 64 + 32 + 1+ 1+ INT STZEROES ENDC
|
||||
// send raw message from Cell
|
||||
ZERO SENDRAWMSG
|
||||
-17 INT 256 STIR 130000000 INT STGRAMS
|
||||
107 INT STZEROES ENDC
|
||||
ZERO // another message with 0.13 Grams to account -17
|
||||
NEWC b{11000100100000} "test" $>s |+ STSLICECONST
|
||||
123456789 INT STGRAMS
|
||||
107 INT STZEROES "Hello, world!" $>s STSLICECONST ENDC
|
||||
ZERO SENDRAWMSG SENDRAWMSG // external message to address "test"
|
||||
}>
|
||||
}END>c
|
||||
// code
|
||||
<b x{11EF55AA} s, smc1_addr 256 u, b> // data
|
||||
// empty_cell // libraries
|
||||
Libs{
|
||||
x{ABACABADABACABA} s>c public_lib
|
||||
x{1234} x{5678} |_ s>c public_lib
|
||||
}Libs // libraries
|
||||
0x333333333 // balance
|
||||
0 // split_depth
|
||||
3 // ticktock: tick
|
||||
2 // mode: create
|
||||
register_smc
|
||||
dup make_special dup constant smc3_addr
|
||||
."address = " x. cr
|
||||
|
||||
// SmartContract #4 (elector)
|
||||
"elector-code.fif" include // code in separate source file
|
||||
<b 0 1 1+ 1+ 5 + u, 0 256 u, b> // data
|
||||
GR$10 // balance: 10 grams
|
||||
0 // split_depth
|
||||
1 // ticktock: tick
|
||||
2 // mode: create
|
||||
register_smc
|
||||
dup make_special dup constant smc4_addr dup constant elector_addr
|
||||
|
||||
// Configuration Parameters
|
||||
|
||||
// max-validators max-main-validators min-validators
|
||||
// 9 3 2 config.validator_num!
|
||||
1000 100 5 config.validator_num!
|
||||
// min-stake max-stake min-total-stake max-factor
|
||||
GR$10000 GR$10000000 GR$1000000 sg~10 config.validator_stake_limits!
|
||||
// elected-for elect-start-before elect-end-before stakes-frozen-for
|
||||
400000 200000 4000 400000 config.election_params!
|
||||
// elector-addr
|
||||
elector_addr config.elector_smc!
|
||||
|
||||
1 sg* 100 sg* 1000 sg* 1000000 sg* config.storage_prices!
|
||||
config.special!
|
||||
|
||||
// gas_price gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit --
|
||||
1000 sg* 1000000 10000 10000000 GR$0.1 GR$1.0 config.gas_prices!
|
||||
10000 sg* 1000000 10000 10000000 GR$0.1 GR$1.0 config.mc_gas_prices!
|
||||
// lump_price bit_price cell_price ihr_factor first_frac next_frac
|
||||
1000000 1000 sg* 100000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.fwd_prices!
|
||||
10000000 10000 sg* 1000000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.mc_fwd_prices!
|
||||
// mc-cc-lifetime sh-cc-lifetime sh-val-lifetime sh-val-num
|
||||
250 250 1000 7 config.catchain_params!
|
||||
|
||||
128 *Ki 512 *Ki 1 *Mi triple // [ underload soft hard ] : block bytes limit
|
||||
100000 500000 1000000 triple // gas limits
|
||||
1000 5000 10000 triple // lt limits
|
||||
triple dup untriple config.mc_block_limits!
|
||||
untriple config.block_limits!
|
||||
|
||||
GR$1.7 GR$1 config.block_create_fees!
|
||||
smc1_addr config.collector_smc!
|
||||
smc1_addr config.minter_smc!
|
||||
|
||||
1000000000000 -17 of-cc 666666666666 239 of-cc cc+ config.to_mint!
|
||||
|
||||
"validator-keys" +suffix +".pub" file>B
|
||||
{ dup Blen } { 32 B| swap dup ."Validator public key = " Bx. cr
|
||||
17 add-validator } while drop
|
||||
// newkeypair nip dup ."Validator #1 public key = " Bx. cr
|
||||
// 17 add-validator
|
||||
// newkeypair nip dup ."Validator #2 public key = " Bx. cr
|
||||
// 239 add-validator
|
||||
now dup 1000000 + 0 config.validators!
|
||||
|
||||
// SmartContract #5 (Config)
|
||||
PROGRAM{
|
||||
recv_internal x{} PROC
|
||||
recv_external PROC:<{
|
||||
512 INT LDSLICEX DUP 32 LDU 32 PLDU // sign cs cnt valid-until
|
||||
NOW LEQ 35 THROWIF // ( fail if now >= valid-until )
|
||||
c4 PUSH CTOS LDREF 32 LDU 256 LDU ENDS // sign cs cnt dict cnt' pubk
|
||||
s1 s3 XCPU // sign cs cnt dict pubk cnt' cnt
|
||||
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
s3 PUSH HASHSU // sign cs cnt dict pubk hash
|
||||
s0 s5 s5 XC2PU // pubk cs cnt dict hash sign pubk
|
||||
CHKSIGNU // pubk cs cnt dict ?
|
||||
34 THROWIFNOT // signature mismatch
|
||||
ACCEPT
|
||||
ROT 64 LDU NIP 32 LDI LDREF ENDS // pubk cnt dict index value
|
||||
s0 s2 XCHG 32 INT // pubk cnt value index dict 32
|
||||
DICTISETREF ROTREV // dict' pubk cnt
|
||||
INC NEWC 32 STU 256 STU STREF ENDC c4 POP
|
||||
}>
|
||||
run_ticktock PROC:<{
|
||||
// store (now >> 8) into config param #-17, if the new value is different
|
||||
c4 PUSH CTOS LDREF SWAP // s' D
|
||||
NOW 8 RSHIFT# TUCK // s' t D t
|
||||
NEWC 32 STU ENDC // s' t D c
|
||||
-17 INT ROT 32 INT // s' t c -17 D 32
|
||||
DICTISETGETREF // s' t D' c' -1 or s' t D' 0
|
||||
IF:<{ CTOS 32 LDU ENDS }>ELSE<{ ZERO }> // s' t D' y'
|
||||
ROT EQUAL IFNOT: // s' D'
|
||||
NEWC STREF STSLICE ENDC // c
|
||||
c4 POP
|
||||
}>
|
||||
}END>c
|
||||
// code
|
||||
<b 0 32 u,
|
||||
"config-master" +suffix +".pk" load-generate-keypair drop
|
||||
B,
|
||||
configdict ref,
|
||||
b> // data
|
||||
empty_cell // libraries
|
||||
1 // balance
|
||||
0 1 2 register_smc // tock
|
||||
dup set_config_smc
|
||||
Masterchain swap
|
||||
."config smart contract address = " 2dup .addr cr 2dup 7 .Addr cr
|
||||
"config-master" +suffix +".addr" save-address-verbose
|
||||
// Other data
|
||||
|
||||
create_state
|
||||
cr cr ."new state is:" cr dup <s csr. cr
|
||||
dup 31 boc+>B dup Bx. cr
|
||||
dup "zerostate" +suffix +".boc" tuck B>file
|
||||
."(Initial masterchain state saved to file " type .")" cr
|
||||
Bhash dup =: zerostate_fhash
|
||||
."file hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"zerostate" +suffix +".fhash" B>file
|
||||
hash dup =: zerostate_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"zerostate" +suffix +".rhash" B>file
|
||||
basestate0_rhash ."Basestate0 root hash=" dup x. space 256 u>B B>base64url type cr
|
||||
basestate0_fhash ."Basestate0 file hash=" dup x. space 256 u>B B>base64url type cr
|
||||
zerostate_rhash ."Zerostate root hash=" dup x. space 256 u>B B>base64url type cr
|
||||
zerostate_fhash ."Zerostate file hash=" dup x. space 256 u>B B>base64url type cr
|
231
crypto/smartcont/gen-zerostate.fif
Normal file
231
crypto/smartcont/gen-zerostate.fif
Normal file
|
@ -0,0 +1,231 @@
|
|||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
def? $1 { @' $1 } { "" } cond constant suffix
|
||||
{ suffix $+ } : +suffix
|
||||
|
||||
wc_master setworkchain
|
||||
-17 setglobalid // negative value means a test instance of the blockchain
|
||||
|
||||
// Initial state of Workchain 0 (Basic workchain)
|
||||
|
||||
0 mkemptyShardState
|
||||
|
||||
cr ."initial basechain state is:" cr dup <s csr. cr
|
||||
dup dup 31 boc+>B dup Bx. cr
|
||||
dup "basestate0" +suffix +".boc" tuck B>file
|
||||
."(Initial basechain state saved to file " type .")" cr
|
||||
Bhash dup =: basestate0_fhash
|
||||
."file hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"basestate0" +suffix +".fhash" B>file
|
||||
hash dup =: basestate0_rhash
|
||||
."root hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"basestate0" +suffix +".rhash" B>file
|
||||
|
||||
basestate0_rhash basestate0_fhash now 0 2 32 0 add-std-workchain
|
||||
config.workchains!
|
||||
|
||||
// SmartContract #1 (Simple wallet)
|
||||
|
||||
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
|
||||
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
|
||||
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
|
||||
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
|
||||
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
s2 PUSH HASHSU // sign cs cnt pubk hash
|
||||
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
|
||||
CHKSIGNU // pubk cs cnt ?
|
||||
34 THROWIFNOT // signature mismatch
|
||||
ACCEPT
|
||||
SWAP 32 LDU NIP 8 LDU LDREF ENDS // pubk cnt mode msg
|
||||
SWAP SENDRAWMSG // pubk cnt ; ( message sent )
|
||||
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
|
||||
}>c
|
||||
// code
|
||||
<b 0 32 u,
|
||||
"main-wallet" +suffix +".pk" load-generate-keypair drop
|
||||
B,
|
||||
b> // data
|
||||
Libs{
|
||||
x{ABACABADABACABA} s>c public_lib
|
||||
x{1234} x{5678} |_ s>c private_lib
|
||||
}Libs // libraries
|
||||
GR$1700000000 // balance
|
||||
0 // split_depth
|
||||
0 // ticktock
|
||||
2 // mode: create
|
||||
register_smc
|
||||
dup make_special dup constant smc1_addr
|
||||
Masterchain over
|
||||
2dup ."wallet address = " .addr cr 2dup 6 .Addr cr
|
||||
"main-wallet" +suffix +".addr" save-address-verbose
|
||||
|
||||
// SmartContract #2 (Simple money giver for test network)
|
||||
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
|
||||
32 LDU SWAP // cs cnt
|
||||
c4 PUSHCTR CTOS 32 LDU ENDS // cs cnt cnt'
|
||||
TUCK EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
ACCEPT // cs cnt'
|
||||
SWAP 8 LDU LDREF ENDS // cnt'' mode msg
|
||||
GR$20 INT 3 INT RAWRESERVE // reserve all but 20 Grams from the balance
|
||||
SWAP SENDRAWMSG
|
||||
INC NEWC 32 STU ENDC c4 POPCTR // store cnt''
|
||||
}>c
|
||||
// code
|
||||
<b 0 32 u, b> // data
|
||||
empty_cell // libraries
|
||||
GR$1000000 // initial balance (1m test Grams)
|
||||
0 0 2 register_smc
|
||||
dup make_special dup constant smc2_addr
|
||||
Masterchain over
|
||||
2dup ."free test gram giver address = " .addr cr 2dup 6 .Addr cr
|
||||
"testgiver" +suffix +".addr" save-address-verbose
|
||||
|
||||
// SmartContract #3
|
||||
PROGRAM{
|
||||
recv_internal x{} PROC
|
||||
run_ticktock PROC:<{
|
||||
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS
|
||||
NEWC ROT INC 32 STUR OVER 256 STUR ENDC
|
||||
c4 POPCTR
|
||||
// first 32 bits of persistent data have been increased
|
||||
// remaining 256 bits with an address have been fetched
|
||||
// create new empty message with 0.1 Grams to that address
|
||||
NEWC b{00100010011111111} STSLICECONST TUCK 256 STU
|
||||
100000000 INT STGRAMS // store 0.1 Grams
|
||||
1 4 + 4 + 64 + 32 + 1+ 1+ INT STZEROES ENDC
|
||||
// send raw message from Cell
|
||||
ZERO SENDRAWMSG
|
||||
-17 INT 256 STIR 130000000 INT STGRAMS
|
||||
107 INT STZEROES ENDC
|
||||
ZERO // another message with 0.13 Grams to account -17
|
||||
NEWC b{11000100100000} "test" $>s |+ STSLICECONST
|
||||
123456789 INT STGRAMS
|
||||
107 INT STZEROES "Hello, world!" $>s STSLICECONST ENDC
|
||||
ZERO SENDRAWMSG SENDRAWMSG // external message to address "test"
|
||||
}>
|
||||
}END>c
|
||||
// code
|
||||
<b x{11EF55AA} s, smc1_addr 256 u, b> // data
|
||||
// empty_cell // libraries
|
||||
Libs{
|
||||
x{ABACABADABACABA} s>c public_lib
|
||||
x{1234} x{5678} |_ s>c public_lib
|
||||
}Libs // libraries
|
||||
0x333333333 // balance
|
||||
0 // split_depth
|
||||
3 // ticktock: tick
|
||||
2 // mode: create
|
||||
register_smc
|
||||
dup make_special dup constant smc3_addr
|
||||
."address = " x. cr
|
||||
|
||||
/*
|
||||
*
|
||||
* SmartContract #4 (elector)
|
||||
*
|
||||
*/
|
||||
"elector-code.fif" include // code in separate source file
|
||||
<b 0 1 1+ 1+ 4 + 32 + u, 0 256 u, b> // data: dict dict dict grams uint32 uint256
|
||||
empty_cell // libraries
|
||||
GR$10 // balance: 10 grams
|
||||
0 // split_depth
|
||||
2 // ticktock: tick
|
||||
2 // mode: create
|
||||
register_smc
|
||||
dup make_special dup constant smc4_addr dup constant elector_addr
|
||||
Masterchain swap
|
||||
."elector smart contract address = " 2dup .addr cr 2dup 7 .Addr cr
|
||||
"elector" +suffix +".addr" save-address-verbose
|
||||
|
||||
/*
|
||||
*
|
||||
* Configuration Parameters
|
||||
*
|
||||
*/
|
||||
// max-validators max-main-validators min-validators
|
||||
// 9 4 1 config.validator_num!
|
||||
1000 100 5 config.validator_num!
|
||||
// min-stake max-stake min-total-stake max-factor
|
||||
GR$10000 GR$10000000 GR$1000000 sg~10 config.validator_stake_limits!
|
||||
// elected-for elect-start-before elect-end-before stakes-frozen-for
|
||||
// 400000 200000 4000 400000 config.election_params!
|
||||
4000 2000 500 1000 config.election_params! // DEBUG
|
||||
// config-addr = -1:5555...5555
|
||||
256 1<<1- 3 / constant config_addr
|
||||
config_addr config.config_smc!
|
||||
// elector-addr
|
||||
elector_addr config.elector_smc!
|
||||
|
||||
1 sg* 100 sg* 1000 sg* 1000000 sg* config.storage_prices!
|
||||
config.special!
|
||||
|
||||
// gas_price gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit --
|
||||
1000 sg* 1000000 10000 10000000 GR$0.1 GR$1.0 config.gas_prices!
|
||||
10000 sg* 1000000 10000 10000000 GR$0.1 GR$1.0 config.mc_gas_prices!
|
||||
// lump_price bit_price cell_price ihr_factor first_frac next_frac
|
||||
1000000 1000 sg* 100000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.fwd_prices!
|
||||
10000000 10000 sg* 1000000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.mc_fwd_prices!
|
||||
// mc-cc-lifetime sh-cc-lifetime sh-val-lifetime sh-val-num
|
||||
250 250 1000 7 config.catchain_params!
|
||||
// round-candidates next-cand-delay-ms consensus-timeout-ms fast-attempts attempt-duration cc-max-deps max-block-size max-collated-size
|
||||
3 2000 16000 3 8 4 2 *Mi 2 *Mi config.consensus_params!
|
||||
|
||||
|
||||
128 *Ki 512 *Ki 1 *Mi triple // [ underload soft hard ] : block bytes limit
|
||||
100000 500000 1000000 triple // gas limits
|
||||
1000 5000 10000 triple // lt limits
|
||||
triple dup untriple config.mc_block_limits!
|
||||
untriple config.block_limits!
|
||||
|
||||
GR$1.7 GR$1 config.block_create_fees!
|
||||
// smc1_addr config.collector_smc!
|
||||
smc1_addr config.minter_smc!
|
||||
|
||||
1000000000000 -17 of-cc 666666666666 239 of-cc cc+ config.to_mint!
|
||||
|
||||
"validator-keys" +suffix +".pub" file>B
|
||||
{ dup Blen } { 32 B| swap dup ."Validator public key = " Bx. cr
|
||||
17 add-validator } while drop
|
||||
// newkeypair nip dup ."Validator #1 public key = " Bx. cr
|
||||
// 17 add-validator
|
||||
// newkeypair nip dup ."Validator #2 public key = " Bx. cr
|
||||
// 239 add-validator
|
||||
// 100000 =: orig_vset_valid_for
|
||||
100 =: orig_vset_valid_for // original validator set valid 100 seconds only (DEBUG)
|
||||
now dup orig_vset_valid_for + 0 config.validators!
|
||||
|
||||
/*
|
||||
*
|
||||
* SmartContract #5 (Configuration smart contract)
|
||||
*
|
||||
*/
|
||||
"config-code.fif" include // code in separate source file
|
||||
<b 0 32 u,
|
||||
"config-master" +suffix +".pk" load-generate-keypair drop
|
||||
B,
|
||||
configdict ref,
|
||||
b> // data
|
||||
empty_cell // libraries
|
||||
GR$10 // balance
|
||||
0 1 config_addr 6 register_smc // tock
|
||||
dup set_config_smc
|
||||
Masterchain swap
|
||||
."config smart contract address = " 2dup .addr cr 2dup 7 .Addr cr
|
||||
"config-master" +suffix +".addr" save-address-verbose
|
||||
// Other data
|
||||
|
||||
create_state
|
||||
cr cr ."new state is:" cr dup <s csr. cr
|
||||
dup 31 boc+>B dup Bx. cr
|
||||
dup "zerostate" +suffix +".boc" tuck B>file
|
||||
."(Initial masterchain state saved to file " type .")" cr
|
||||
Bhash dup =: zerostate_fhash
|
||||
."file hash= " dup X. space 256 u>B dup B>base64url type cr
|
||||
"zerostate" +suffix +".fhash" B>file
|
||||
hash dup =: zerostate_rhash ."root hash= " dup X. space 256 u>B dup B>base64url type cr
|
||||
"zerostate" +suffix +".rhash" B>file
|
||||
basestate0_rhash ."Basestate0 root hash= " dup X. space 256 u>B B>base64url type cr
|
||||
basestate0_fhash ."Basestate0 file hash= " dup X. space 256 u>B B>base64url type cr
|
||||
zerostate_rhash ."Zerostate root hash= " dup X. space 256 u>B B>base64url type cr
|
||||
zerostate_fhash ."Zerostate file hash= " dup X. space 256 u>B B>base64url type cr
|
53
crypto/smartcont/new-pinger.fif
Normal file
53
crypto/smartcont/new-pinger.fif
Normal file
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <workchain-id> <dest-addr> [<filename-base>]" cr
|
||||
."Creates a new pinger in specified workchain, with destination address <dest-addr>. " cr
|
||||
."Resulting initialization query is saved into <filename-base>-query.boc ('new-pinger-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# dup 1 < swap 3 > or ' usage if } if
|
||||
|
||||
Basechain 256 1<<1- 3 15 */ 2constant dest-addr
|
||||
|
||||
Basechain constant wc // create a wallet in workchain 0 (basechain)
|
||||
def? $1 { @' $1 parse-workchain-id =: wc } if // set workchain id from command line argument
|
||||
def? $2 { @' $2 false parse-load-address drop 2=: dest-addr } if
|
||||
def? $3 { @' $3 } { "new-pinger" } cond constant file-base
|
||||
|
||||
."Creating new pinger in workchain " wc . cr
|
||||
|
||||
// Create new simple pinger
|
||||
<{ SETCP0 DUP INC 1 RSHIFT# 32 THROWIF // fail unless recv_internal or recv_external
|
||||
c4 PUSHCTR CTOS 32 LDU TUCK 8 LDI 256 LDU ENDS // body sel s seqno wc addr : destination address loaded
|
||||
s4 PUSH IF:<{
|
||||
s2 s5 XCHG2 // addr sel s wc seqno body
|
||||
32 PLDU OVER EQUAL 33 THROWIFNOT // seqno mismatch?
|
||||
ACCEPT // addr sel s wc seqno
|
||||
INC NEWC 32 STU // addr sel s wc b
|
||||
s1 s2 XCHG STSLICE // addr sel wc b'
|
||||
ENDC c4 POPCTR // addr sel wc ; persistent data updated
|
||||
ROT // sel wc addr
|
||||
}>
|
||||
// create new empty message with 0.1 Grams to that address
|
||||
SWAP NEWC b{001000100} STSLICECONST 8 STI 256 STU
|
||||
GR$10/9 INT STGRAMS // store 1.111..1 Grams
|
||||
1 4 + 4 + 64 + 32 + 1+ 1+ INT STZEROES ENDC
|
||||
// send raw message from Cell
|
||||
ZERO SENDRAWMSG
|
||||
}>c
|
||||
// code
|
||||
<b 0 32 u, dest-addr addr, b> // data
|
||||
// no libraries
|
||||
<b b{00110} s, rot ref, swap ref, b> // create StateInit
|
||||
dup ."StateInit: " <s csr. cr
|
||||
dup hash wc swap 2dup 2constant pinger_addr
|
||||
."new pinger address = " 2dup .addr cr
|
||||
2dup file-base +".addr" save-address-verbose
|
||||
."Non-bounceable address (for init): " 2dup 7 .Addr cr
|
||||
."Bounceable address (for later access): " 6 .Addr cr
|
||||
<b b{1000100} s, pinger_addr addr, b{000010} s, swap <s s, b{0} s, 0 32 u, "PING" $, b>
|
||||
dup ."External message for initialization is " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
file-base +"-query.boc" tuck B>file
|
||||
."(Saved pinger creating query to file " type .")" cr
|
58
crypto/smartcont/new-wallet.fif
Normal file
58
crypto/smartcont/new-wallet.fif
Normal file
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <workchain-id> [<filename-base>]" cr
|
||||
."Creates a new wallet in specified workchain, with private key saved to or loaded from <filename-base>.pk" cr
|
||||
."('new-wallet.pk' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# 1- -2 and ' usage if } if
|
||||
|
||||
Basechain constant wc // create a wallet in workchain 0 (basechain)
|
||||
def? $1 { @' $1 parse-workchain-id =: wc } if // set workchain id from command line argument
|
||||
def? $2 { @' $2 } { "new-wallet" } cond constant file-base
|
||||
|
||||
."Creating new wallet in workchain " wc . cr
|
||||
|
||||
// Create new simple wallet
|
||||
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
|
||||
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
|
||||
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
|
||||
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
|
||||
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
s2 PUSH HASHSU // sign cs cnt pubk hash
|
||||
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
|
||||
CHKSIGNU // pubk cs cnt ?
|
||||
34 THROWIFNOT // signature mismatch
|
||||
ACCEPT
|
||||
SWAP 32 LDU NIP
|
||||
DUP SREFS IF:<{
|
||||
8 LDU LDREF // pubk cnt mode msg cs
|
||||
s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent )
|
||||
}>
|
||||
ENDS
|
||||
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
|
||||
}>c // >libref
|
||||
// code
|
||||
<b 0 32 u,
|
||||
file-base +".pk" load-generate-keypair
|
||||
constant wallet_pk
|
||||
B,
|
||||
b> // data
|
||||
null // no libraries
|
||||
// Libs{ x{ABACABADABACABA} drop x{AAAA} s>c public_lib x{1234} x{5678} |_ s>c public_lib }Libs
|
||||
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
|
||||
dup ."StateInit: " <s csr. cr
|
||||
dup hash wc swap 2dup 2constant wallet_addr
|
||||
."new wallet address = " 2dup .addr cr
|
||||
2dup file-base +".addr" save-address-verbose
|
||||
."Non-bounceable address (for init): " 2dup 7 .Addr cr
|
||||
."Bounceable address (for later access): " 6 .Addr cr
|
||||
<b 0 32 u, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash wallet_pk ed25519_sign_uint rot
|
||||
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
|
||||
dup ."External message for initialization is " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
file-base +"-query.boc" tuck B>file
|
||||
."(Saved wallet creating query to file " type .")" cr
|
17
crypto/smartcont/recover-stake.fif
Normal file
17
crypto/smartcont/recover-stake.fif
Normal file
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." [<savefile>]" cr
|
||||
."Creates the message body to be sent from a validator controlling smart contract (wallet) to recover its share of unfrozen stakes and bonuses." cr
|
||||
."The result is saved into <savefile> (`recover-query.boc` by default) and output in hexadecimal form, to be sent later as the body of a message from the wallet to elections smart contract, along with a small value (say, one Gram) to cover forwarding and processing fees" cr 1 halt
|
||||
} : usage
|
||||
|
||||
$# dup 0 < swap 1 > or ' usage if
|
||||
def? $1 { @' $1 } { "recover-query.boc" } cond constant output_fname
|
||||
now constant query_id
|
||||
."query_id for stake recovery message is set to " query_id . cr
|
||||
|
||||
<b x{47657424} s, query_id 64 u, b>
|
||||
cr ."Message body is " dup <s csr. cr
|
||||
|
||||
2 boc+>B output_fname tuck B>file ."Saved to file " type cr
|
21
crypto/smartcont/show-addr.fif
Normal file
21
crypto/smartcont/show-addr.fif
Normal file
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base>" cr
|
||||
."Shows the address of a simple wallet created by new-wallet.fif, with address in <filename-base>.addr "
|
||||
."and private key in file <filename-base>.pk" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# 1 > ' usage if } if
|
||||
def? $1 { @' $1 } { "new-wallet" } cond constant file-base
|
||||
|
||||
file-base +".addr" dup ."Loading wallet address from " type cr file>B 32 B|
|
||||
dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc
|
||||
256 B>u@ dup constant wallet_addr
|
||||
."Source wallet address = " wallet_wc ._ .":" x. cr
|
||||
wallet_wc wallet_addr 2dup 7 smca>$ ."Non-bounceable address (for init only): " type cr
|
||||
6 smca>$ ."Bounceable address (for later access): " type cr
|
||||
|
||||
file-base +".pk" dup file-exists? {
|
||||
dup file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
|
||||
=: wallet_pk ."Private key available in file " type cr
|
||||
} { ."Private key file " type ." not found" cr } cond
|
25
crypto/smartcont/simple-wallet-code.fc
Normal file
25
crypto/smartcont/simple-wallet-code.fc
Normal file
|
@ -0,0 +1,25 @@
|
|||
;; Simple wallet smart contract
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
int msg_seqno = cs~load_uint(32);
|
||||
var cs2 = begin_parse(get_data());
|
||||
var stored_seqno = cs2~load_uint(32);
|
||||
var public_key = cs2~load_uint(256);
|
||||
cs2.end_parse();
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
accept_message();
|
||||
cs~touch_slice();
|
||||
if (cs.slice_refs()) {
|
||||
var mode = cs~load_uint(8);
|
||||
send_raw_message(cs~load_ref(), mode);
|
||||
}
|
||||
cs.end_parse();
|
||||
set_data(begin_cell().store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell());
|
||||
}
|
101
crypto/smartcont/stdlib.fc
Normal file
101
crypto/smartcont/stdlib.fc
Normal file
|
@ -0,0 +1,101 @@
|
|||
;; Standard library for funC
|
||||
;;
|
||||
|
||||
int now() asm "NOW";
|
||||
slice my_address() asm "MYADDR";
|
||||
|
||||
int cell_hash(cell c) asm "HASHCU";
|
||||
int slice_hash(slice s) asm "HASHSU";
|
||||
int string_hash(slice s) asm "SHA256U";
|
||||
|
||||
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
|
||||
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
|
||||
|
||||
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
|
||||
|
||||
() dump_stack() impure asm "DUMPSTK";
|
||||
|
||||
cell get_data() asm "c4 PUSH";
|
||||
() set_data(cell c) impure asm "c4 POP";
|
||||
() set_c3(cell c) impure asm "c3 POP";
|
||||
() accept_message() impure asm "ACCEPT";
|
||||
|
||||
int min(int x, int y) asm "MIN";
|
||||
int max(int x, int y) asm "MAX";
|
||||
|
||||
slice begin_parse(cell c) asm "CTOS";
|
||||
() end_parse(slice s) impure asm "ENDS";
|
||||
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
|
||||
cell preload_ref(slice s) asm "PLDREF";
|
||||
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
|
||||
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
|
||||
;; int preload_int(slice s, int len) asm "PLDIX";
|
||||
;; int preload_uint(slice s, int len) asm "PLDUX";
|
||||
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
|
||||
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
|
||||
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
|
||||
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
|
||||
slice first_bits(slice s, int len) asm "SDCUTFIRST";
|
||||
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
|
||||
cell preload_dict(slice s) asm "PLDDICT";
|
||||
slice skip_dict(slice s) asm "SKIPDICT";
|
||||
|
||||
int slice_refs(slice s) asm "SREFS";
|
||||
int slice_bits(slice s) asm "SBITS";
|
||||
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
|
||||
int slice_empty?(slice s) asm "SEMPTY";
|
||||
int slice_data_empty?(slice s) asm "SDEMPTY";
|
||||
int slice_refs_empty?(slice s) asm "SREMPTY";
|
||||
|
||||
builder begin_cell() asm "NEWC";
|
||||
cell end_cell(builder b) asm "ENDC";
|
||||
builder store_ref(builder b, cell c) asm(c b) "STREF";
|
||||
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
|
||||
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
|
||||
builder store_slice(builder b, slice s) asm "STSLICER";
|
||||
builder store_grams(builder b, int x) asm "STGRAMS";
|
||||
builder store_dict(builder b, cell c) asm(c b) "STDICT";
|
||||
|
||||
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
|
||||
tuple parse_addr(slice s) asm "PARSEMSGADDR";
|
||||
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
|
||||
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
|
||||
|
||||
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
|
||||
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
|
||||
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
|
||||
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
|
||||
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
|
||||
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
|
||||
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
|
||||
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
|
||||
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
|
||||
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
|
||||
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
|
||||
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
|
||||
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
|
||||
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
|
||||
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
|
||||
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
|
||||
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
|
||||
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
|
||||
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
|
||||
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
|
||||
cell new_dict() asm "NEWDICT";
|
||||
int dict_empty?(cell c) asm "DICTEMPTY";
|
||||
|
||||
cell config_param(int x) asm "CONFIGOPTPARAM";
|
||||
int cell_null?(cell c) asm "ISNULL";
|
||||
|
||||
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
|
||||
() raw_reserve_extra(slice currencies, int mode) impure asm "RAWRESERVEX";
|
||||
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
|
||||
() set_code(cell new_code) impure asm "SETCODE";
|
||||
|
||||
slice touch_slice(slice s) asm "NOP";
|
||||
(slice,()) ~touch_slice(slice s) asm "NOP";
|
43
crypto/smartcont/testgiver.fif
Normal file
43
crypto/smartcont/testgiver.fif
Normal file
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <dest-addr> <seqno> <amount> [<savefile>]" cr
|
||||
."Creates a request to TestGiver and saves it into <savefile>.boc" cr
|
||||
."('testgiver-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# 3 - -2 and ' usage if } if
|
||||
|
||||
// "testgiver.addr" load-address
|
||||
Masterchain 0x8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
|
||||
2constant giver_addr
|
||||
."Test giver address = " giver_addr 2dup .addr cr 6 .Addr cr
|
||||
|
||||
Basechain 0x60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
|
||||
2constant dest_addr
|
||||
false constant bounce
|
||||
|
||||
0 constant seqno
|
||||
GR$6.666 constant amount
|
||||
|
||||
def? $3 {
|
||||
@' $1 bounce parse-load-address =: bounce 2=: dest_addr
|
||||
@' $2 parse-int =: seqno
|
||||
@' $3 $>GR =: amount
|
||||
} if
|
||||
def? $4 { @' $4 } { "testgiver-query" } cond constant savefile
|
||||
|
||||
."Requesting " amount .GR ."to account "
|
||||
dest_addr 2dup bounce 7 + .Addr ." = " .addr
|
||||
."seqno=0x" seqno x. ."bounce=" bounce . cr
|
||||
|
||||
// create a message (NB: 01b00.., b = bounce)
|
||||
<b b{01} s, bounce 1 i, b{000100} s, dest_addr addr,
|
||||
amount Gram, 0 9 64 32 + + 1+ 1+ u, "GIFT" $, b>
|
||||
<b seqno 32 u, 1 8 u, swap ref, b>
|
||||
dup ."enveloping message: " <s csr. cr
|
||||
<b b{1000100} s, giver_addr addr, 0 Gram, b{00} s,
|
||||
swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
43
crypto/smartcont/update-config-smc.fif
Normal file
43
crypto/smartcont/update-config-smc.fif
Normal file
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <seqno> [<savefile>]" cr
|
||||
."Creates a request to simple configuration smart contract requesting to change configuration smart contract code to the one currently stored in config-code.fif, "
|
||||
."with private key loaded from file <filename-base>.pk, "
|
||||
."and saves it into <savefile>.boc ('config-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# dup 2 < swap 3 > or ' usage if } if
|
||||
|
||||
"config-master" constant file-base
|
||||
0 constant seqno
|
||||
-1 constant idx
|
||||
true constant bounce
|
||||
"config-code.fif" constant config-source
|
||||
100 constant interval // valid for 100 seconds
|
||||
|
||||
def? $2 {
|
||||
@' $1 =: file-base
|
||||
@' $2 parse-int =: seqno
|
||||
} if
|
||||
def? $5 { @' $5 } { "config-query" } cond constant savefile
|
||||
|
||||
file-base +".addr" load-address
|
||||
2dup 2constant config_addr
|
||||
."Configuration smart contract address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant config_pk
|
||||
|
||||
."Loading new configuration smart contract code from file file " config-source type cr
|
||||
"Asm.fif" include
|
||||
config-source include
|
||||
dup <s csr. cr
|
||||
|
||||
// create a message
|
||||
<b x{4e436f64} s, seqno 32 u, now interval + 32 u, swap ref, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash config_pk ed25519_sign_uint
|
||||
<b b{1000100} s, config_addr addr, 0 Gram, b{00} s,
|
||||
swap B, swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
48
crypto/smartcont/update-config.fif
Normal file
48
crypto/smartcont/update-config.fif
Normal file
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <seqno> <index> <new-value-boc> [<savefile>]" cr
|
||||
."Creates a request to simple configuration smart contract created by mc0.fif requesting to change configuration parameter <index> to <new-value-boc>, "
|
||||
."with private key loaded from file <filename-base>.pk, "
|
||||
."and saves it into <savefile>.boc ('config-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# dup 4 < swap 5 > or ' usage if } if
|
||||
|
||||
"config-master" constant file-base
|
||||
0 constant seqno
|
||||
-1 constant idx
|
||||
true constant bounce
|
||||
"new-value.boc" constant boc-filename
|
||||
100 constant interval // valid for 100 seconds
|
||||
|
||||
def? $4 {
|
||||
@' $1 =: file-base
|
||||
@' $2 parse-int =: seqno
|
||||
@' $3 parse-int =: idx
|
||||
@' $4 =: boc-filename
|
||||
} if
|
||||
def? $5 { @' $5 } { "config-query" } cond constant savefile
|
||||
|
||||
file-base +".addr" load-address
|
||||
2dup 2constant config_addr
|
||||
."Configuration smart contract address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant config_pk
|
||||
|
||||
."Loading new value of configuration parameter " idx . ."from file " boc-filename type cr
|
||||
boc-filename file>B B>boc
|
||||
dup <s csr. cr
|
||||
|
||||
{ 2drop true } : is-valid-config?
|
||||
|
||||
dup idx is-valid-config? not abort"not a valid value for chosen configuration parameter"
|
||||
|
||||
// create a message
|
||||
<b x{43665021} s, seqno 32 u, now interval + 32 u, idx 32 i, swap ref, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash config_pk ed25519_sign_uint
|
||||
<b b{1000100} s, config_addr addr, 0 Gram, b{00} s,
|
||||
swap B, swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
43
crypto/smartcont/update-elector-smc.fif
Normal file
43
crypto/smartcont/update-elector-smc.fif
Normal file
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <seqno> [<savefile>]" cr
|
||||
."Creates a request to simple configuration smart contract requesting to change elector smart contract code to the one currently stored in elector-code.fif, "
|
||||
."with private key loaded from file <filename-base>.pk, "
|
||||
."and saves it into <savefile>.boc ('config-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# dup 2 < swap 3 > or ' usage if } if
|
||||
|
||||
"config-master" constant file-base
|
||||
0 constant seqno
|
||||
-1 constant idx
|
||||
true constant bounce
|
||||
"elector-code.fif" constant elector-source
|
||||
100 constant interval // valid for 100 seconds
|
||||
|
||||
def? $2 {
|
||||
@' $1 =: file-base
|
||||
@' $2 parse-int =: seqno
|
||||
} if
|
||||
def? $5 { @' $5 } { "config-query" } cond constant savefile
|
||||
|
||||
file-base +".addr" load-address
|
||||
2dup 2constant config_addr
|
||||
."Configuration smart contract address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant config_pk
|
||||
|
||||
."Loading new elector smart contract code from file file " elector-source type cr
|
||||
"Asm.fif" include
|
||||
elector-source include
|
||||
dup <s csr. cr
|
||||
|
||||
// create a message
|
||||
<b x{4e43ef05} s, seqno 32 u, now interval + 32 u, swap ref, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash config_pk ed25519_sign_uint
|
||||
<b b{1000100} s, config_addr addr, 0 Gram, b{00} s,
|
||||
swap B, swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
30
crypto/smartcont/validator-elect-req.fif
Normal file
30
crypto/smartcont/validator-elect-req.fif
Normal file
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <wallet-addr> <elect-utime> <max-factor> <adnl-addr> [<savefile>]" cr
|
||||
."Creates a request to participate in validator elections starting at <elect-utime> on behalf of smart-contract with address <wallet-addr> (prefix with '@' to load address from file) and hexadecimal adnl address <adnl-addr> (empty string or '0' for none)." cr
|
||||
."The result is saved into <savefile> and output in hexadecimal form, to be signed later by the validator public key" cr 1 halt
|
||||
} : usage
|
||||
|
||||
$# dup 3 < swap 5 > or ' usage if
|
||||
$1 true parse-load-address drop swap 1+ abort"only masterchain smartcontracts may participate in validator elections"
|
||||
constant src_addr
|
||||
$2 (number) 1 <> { 0 } if dup 0<= abort"<elect-utime> must be a positive integer"
|
||||
constant elect_time
|
||||
$3 (number) dup 0= abort"<max-factor> must be a real number 1..100"
|
||||
1 = { 16 << } { 16 <</r } cond
|
||||
dup 65536 < over 6553600 > or abort"<max-factor> must be a real number 1..100"
|
||||
constant max_factor
|
||||
def? $4 { @' $4 dup $len 1 > { parse-adnl-address } { drop 0 } cond } ' 0 cond
|
||||
constant adnl_addr
|
||||
def? $5 { @' $5 } { "validator-to-sign.bin" } cond constant output_fname
|
||||
|
||||
."Creating a request to participate in validator elections at time " elect_time .
|
||||
."from smart contract " -1 src_addr 2dup 1 .Addr ." = " .addr
|
||||
." with maximal stake factor with respect to the minimal stake " max_factor ._
|
||||
."/65536 and validator ADNL address " adnl_addr x. cr
|
||||
|
||||
B{654c5074} elect_time 32 u>B B+ max_factor 32 u>B B+ src_addr 256 u>B B+ adnl_addr 256 u>B B+
|
||||
dup Bx. cr
|
||||
dup B>base64url type cr
|
||||
output_fname tuck B>file ."Saved to file " type cr
|
43
crypto/smartcont/validator-elect-signed.fif
Normal file
43
crypto/smartcont/validator-elect-signed.fif
Normal file
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <wallet-addr> <elect-utime> <max-factor> <adnl-addr> <validator-pubkey> <validator-signature> [<savefile>]" cr
|
||||
."Creates a message body for participating in validator elections starting at <elect-utime> on behalf of smart-contract with address <wallet-addr> (prefix with '@' to load address from file) and hexadecimal adnl address <adnl-addr> (empty string or '0' for none)." cr
|
||||
."<validator-pubkey> is the main public key of the future validator (as a Base64 string), and <validator-signature> is the signature of the previously created validator request by that key (also Base64)" cr
|
||||
."The result is saved into <savefile> (`validator-query.boc` by default) and output in hexadecimal form, to be sent later as the body of a message from <wallet-addr> to elections smart contract, along with the desired stake" cr 1 halt
|
||||
} : usage
|
||||
|
||||
$# dup 6 < swap 7 > or ' usage if
|
||||
$1 true parse-load-address drop swap 1+ abort"only masterchain smartcontracts may participate in validator elections"
|
||||
constant src_addr
|
||||
$2 (number) 1 <> { 0 } if dup 0<= abort"<elect-utime> must be a positive integer"
|
||||
constant elect_time
|
||||
$3 (number) dup 0= abort"<max-factor> must be a real number 1..100"
|
||||
1 = { 16 << } { 16 <</r } cond
|
||||
dup 65536 < over 6553600 > or abort"<max-factor> must be a real number 1..100"
|
||||
constant max_factor
|
||||
$4 dup $len 1 > { parse-adnl-address } { drop 0 } cond constant adnl_addr
|
||||
$5 base64>B dup Blen 36 <> abort"validator Ed25519 public key must be exactly 36 bytes long"
|
||||
32 B>u@+ 0xC6B41348 <> abort"invalid Ed25519 public key: unknown magic number"
|
||||
constant pubkey
|
||||
$6 base64>B dup Blen 64 <> abort"validator Ed25519 signature must be exactly 64 bytes long"
|
||||
constant signature
|
||||
def? $7 { @' $7 } { "validator-query.boc" } cond constant output_fname
|
||||
|
||||
."Creating a request to participate in validator elections at time " elect_time .
|
||||
."from smart contract " -1 src_addr 2dup 1 .Addr ." = " .addr
|
||||
." with maximal stake factor with respect to the minimal stake " max_factor ._
|
||||
."/65536 and validator ADNL address " adnl_addr x. cr
|
||||
|
||||
B{654c5074} elect_time 32 u>B B+ max_factor 32 u>B B+ src_addr 256 u>B B+ adnl_addr 256 u>B B+
|
||||
."String to sign is: " dup Bx. cr constant to_sign
|
||||
|
||||
to_sign signature pubkey ed25519_chksign not abort"Ed25519 signature is invalid"
|
||||
."Provided a valid Ed25519 signature " signature Bx. ." with validator public key " pubkey Bx. cr
|
||||
now dup constant query_id ."query_id set to " . cr
|
||||
|
||||
<b x{4e73744b} s, query_id 64 u, pubkey B, elect_time 32 u, max_factor 32 u, adnl_addr 256 u,
|
||||
<b signature B, b> ref, b>
|
||||
cr ."Message body is " dup <s csr. cr
|
||||
|
||||
2 boc+>B output_fname tuck B>file ."Saved to file " type cr
|
27
crypto/smartcont/wallet-code.fc
Normal file
27
crypto/smartcont/wallet-code.fc
Normal file
|
@ -0,0 +1,27 @@
|
|||
;; Simple wallet smart contract
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
int msg_seqno = cs~load_uint(32);
|
||||
var valid_until = cs~load_uint(32);
|
||||
throw_if(35, valid_until < now());
|
||||
var cs2 = begin_parse(get_data());
|
||||
var stored_seqno = cs2~load_uint(32);
|
||||
var public_key = cs2~load_uint(256);
|
||||
cs2.end_parse();
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
accept_message();
|
||||
cs~touch();
|
||||
while (cs.slice_refs()) {
|
||||
var mode = cs~load_uint(8);
|
||||
send_raw_message(cs~load_ref(), mode);
|
||||
}
|
||||
cs.end_parse();
|
||||
set_data(begin_cell().store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell());
|
||||
}
|
51
crypto/smartcont/wallet.fif
Normal file
51
crypto/smartcont/wallet.fif
Normal file
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <seqno> <amount> [-B <body-boc>] [<savefile>]" cr
|
||||
."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file <filename-base>.pk "
|
||||
."and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond
|
||||
@' $# 2- =: $# } if } if
|
||||
def? $# { @' $# dup 4 < swap 5 > or ' usage if } if
|
||||
|
||||
"new-wallet" constant file-base
|
||||
Basechain 0x13CB612A00A7C092C7DFD2EA45D603A9B54591BA4C88F71E707E009B879F0FB2 2constant dest_addr
|
||||
0 constant seqno
|
||||
GR$.666 constant amount
|
||||
true constant bounce
|
||||
|
||||
def? $4 {
|
||||
@' $1 =: file-base
|
||||
@' $2 bounce parse-load-address =: bounce 2=: dest_addr
|
||||
@' $3 parse-int =: seqno
|
||||
@' $4 $>GR =: amount
|
||||
} if
|
||||
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
|
||||
|
||||
file-base +".addr" load-address
|
||||
2dup 2constant wallet_addr
|
||||
."Source wallet address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant wallet_pk
|
||||
|
||||
def? body-boc-file { @' body-boc-file file>B B>boc } { <b "TEST" $, b> } cond
|
||||
constant body-cell
|
||||
|
||||
."Transferring " amount .GR ."to account "
|
||||
dest_addr 2dup bounce 7 + .Addr ." = " .addr
|
||||
."seqno=0x" seqno x. ."bounce=" bounce . cr
|
||||
."Body of transfer message is " body-cell <s csr. cr
|
||||
|
||||
// create a message
|
||||
<b b{01} s, bounce 1 i, b{000100} s, dest_addr addr, amount Gram, 0 9 64 32 + + 1+ u,
|
||||
body-cell <s 2dup s-fits? not rot over 1 i, -rot { drop body-cell ref, } { s, } cond
|
||||
b>
|
||||
<b seqno 32 u, 1 8 u, swap ref, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash wallet_pk ed25519_sign_uint
|
||||
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
|
||||
swap B, swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
Loading…
Add table
Add a link
Reference in a new issue