mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Add legacy_tester for existing funC contracts (#588)
* Add legacy_tester for existing funC contracts * Add storage-contracts and pragma options
This commit is contained in:
parent
13b9f460af
commit
6b49d6a382
70 changed files with 14495 additions and 0 deletions
746
crypto/func/auto-tests/legacy_tests/nominator-pool/pool.fc
Normal file
746
crypto/func/auto-tests/legacy_tests/nominator-pool/pool.fc
Normal file
|
@ -0,0 +1,746 @@
|
|||
;; The validator has his own wallet in the masterchain, on which he holds his own coins for operating.
|
||||
;; From this wallet he sends commands to this nominator pool (mostly `new_stake`, `update_validator_set` and `recover_stake`).
|
||||
;; Register/vote_for complaints and register/vote_for config proposals are sent from validator's wallet.
|
||||
;;
|
||||
;; Pool contract must be in masterchain.
|
||||
;; Nominators' wallets must be in the basechain.
|
||||
;; The validator in most cases have two pools (for even and odd validation rounds).
|
||||
|
||||
#include "stdlib.fc";
|
||||
|
||||
int op::new_stake() asm "0x4e73744b PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L621
|
||||
int op::new_stake_error() asm "0xee6f454c PUSHINT"; ;; return_stake https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L169
|
||||
int op::new_stake_ok() asm "0xf374484c PUSHINT"; ;; send_confirmation https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L173
|
||||
|
||||
int op::recover_stake() asm "0x47657424 PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L625
|
||||
int op::recover_stake_error() asm "0xfffffffe PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L407
|
||||
int op::recover_stake_ok() asm "0xf96f7324 PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L426
|
||||
|
||||
int ADDR_SIZE() asm "256 PUSHINT";
|
||||
int BOUNCEABLE() asm "0x18 PUSHINT";
|
||||
int NON_BOUNCEABLE() asm "0x10 PUSHINT";
|
||||
int SEND_MODE_PAY_FEE_SEPARATELY() asm "1 PUSHINT"; ;; means that the sender wants to pay transfer fees separately
|
||||
int SEND_MODE_IGNORE_ERRORS() asm "2 PUSHINT"; ;; means that any errors arising while processing this message during the action phase should be ignored
|
||||
int SEND_MODE_REMAINING_AMOUNT() asm "64 PUSHINT"; ;; is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message
|
||||
int ONE_TON() asm "1000000000 PUSHINT";
|
||||
int MIN_TONS_FOR_STORAGE() asm "10000000000 PUSHINT"; ;; 10 TON
|
||||
int DEPOSIT_PROCESSING_FEE() asm "1000000000 PUSHINT"; ;; 1 TON
|
||||
int MIN_STAKE_TO_SEND() asm "500000000000 PUSHINT"; ;; 500 TON
|
||||
int VOTES_LIFETIME() asm "2592000 PUSHINT"; ;; 30 days
|
||||
|
||||
int binary_log_ceil(int x) asm "UBITSIZE";
|
||||
|
||||
;; hex parse same with bridge https://github.com/ton-blockchain/bridge-func/blob/d03dbdbe9236e01efe7f5d344831bf770ac4c613/func/text_utils.fc
|
||||
(slice, int) ~load_hex_symbol(slice comment) {
|
||||
int n = comment~load_uint(8);
|
||||
n = n - 48;
|
||||
throw_unless(329, n >= 0);
|
||||
if (n < 10) {
|
||||
return (comment, (n));
|
||||
}
|
||||
n = n - 7;
|
||||
throw_unless(329, n >= 0);
|
||||
if (n < 16) {
|
||||
return (comment, (n));
|
||||
}
|
||||
n = n - 32;
|
||||
throw_unless(329, (n >= 0) & (n < 16));
|
||||
return (comment, n);
|
||||
}
|
||||
|
||||
(slice, int) ~load_text_hex_number(slice comment, int byte_length) {
|
||||
int current_slice_length = comment.slice_bits() / 8;
|
||||
int result = 0;
|
||||
int counter = 0;
|
||||
repeat (2 * byte_length) {
|
||||
result = result * 16 + comment~load_hex_symbol();
|
||||
counter = counter + 1;
|
||||
if (counter == current_slice_length) {
|
||||
if (comment.slice_refs() == 1) {
|
||||
cell _cont = comment~load_ref();
|
||||
comment = _cont.begin_parse();
|
||||
current_slice_length = comment.slice_bits() / 8;
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (comment, result);
|
||||
}
|
||||
|
||||
slice make_address(int wc, int addr) inline_ref {
|
||||
return begin_cell()
|
||||
.store_uint(4, 3).store_int(wc, 8).store_uint(addr, ADDR_SIZE()).end_cell().begin_parse();
|
||||
}
|
||||
|
||||
;; https://github.com/ton-blockchain/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/block.tlb#L584
|
||||
int is_elector_address(int wc, int addr) inline_ref {
|
||||
return (wc == -1) & (config_param(1).begin_parse().preload_uint(ADDR_SIZE()) == addr);
|
||||
}
|
||||
|
||||
slice elector_address() inline_ref {
|
||||
int elector = config_param(1).begin_parse().preload_uint(ADDR_SIZE());
|
||||
return make_address(-1, elector);
|
||||
}
|
||||
|
||||
;; https://github.com/ton-blockchain/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/block.tlb#L721
|
||||
int max_recommended_punishment_for_validator_misbehaviour(int stake) inline_ref {
|
||||
cell cp = config_param(40);
|
||||
if (cell_null?(cp)) {
|
||||
return 101000000000; ;; 101 TON - https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/lite-client/lite-client.cpp#L3678
|
||||
}
|
||||
|
||||
slice cs = cp.begin_parse();
|
||||
|
||||
(int prefix,
|
||||
int default_flat_fine, int default_proportional_fine,
|
||||
int severity_flat_mult, int severity_proportional_mult,
|
||||
int unpunishable_interval,
|
||||
int long_interval, int long_flat_mult, int long_proportional_mult) =
|
||||
(cs~load_uint(8),
|
||||
cs~load_coins(), cs~load_uint(32),
|
||||
cs~load_uint(16), cs~load_uint(16),
|
||||
cs~load_uint(16),
|
||||
cs~load_uint(16), cs~load_uint(16), cs~load_uint(16)
|
||||
);
|
||||
|
||||
;; https://github.com/ton-blockchain/ton/blob/master/lite-client/lite-client.cpp#L3721
|
||||
int fine = default_flat_fine;
|
||||
int fine_part = default_proportional_fine;
|
||||
|
||||
fine *= severity_flat_mult; fine >>= 8;
|
||||
fine_part *= severity_proportional_mult; fine_part >>= 8;
|
||||
|
||||
fine *= long_flat_mult; fine >>= 8;
|
||||
fine_part *= long_proportional_mult; fine_part >>= 8;
|
||||
|
||||
return min(stake, fine + muldiv(stake, fine_part, 1 << 32)); ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L529
|
||||
}
|
||||
|
||||
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/block/block.tlb#L632
|
||||
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L118
|
||||
int get_validator_config() inline_ref {
|
||||
slice cs = config_param(15).begin_parse();
|
||||
(int validators_elected_for, int elections_start_before, int elections_end_before, int stake_held_for) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs.preload_uint(32));
|
||||
return stake_held_for;
|
||||
}
|
||||
|
||||
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/block/block.tlb#L712
|
||||
(int, int, cell) get_current_validator_set() inline_ref {
|
||||
cell vset = config_param(34); ;; current validator set
|
||||
slice cs = vset.begin_parse();
|
||||
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/block/block.tlb#L579
|
||||
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/config-code.fc#L49
|
||||
throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only
|
||||
int utime_since = cs~load_uint(32); ;; actual start unixtime of current validation round
|
||||
int utime_until = cs~load_uint(32); ;; supposed end unixtime of current validation round (utime_until = utime_since + validators_elected_for); unfreeze_at = utime_until + stake_held_for
|
||||
return (utime_since, utime_until, vset);
|
||||
}
|
||||
|
||||
;; check the validity of the new_stake message
|
||||
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L208
|
||||
int check_new_stake_msg(slice cs) impure inline_ref {
|
||||
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();
|
||||
return stake_at; ;; supposed start of next validation round (utime_since)
|
||||
}
|
||||
|
||||
builder pack_nominator(int amount, int pending_deposit_amount) inline_ref {
|
||||
return begin_cell().store_coins(amount).store_coins(pending_deposit_amount);
|
||||
}
|
||||
|
||||
(int, int) unpack_nominator(slice ds) inline_ref {
|
||||
return (
|
||||
ds~load_coins(), ;; amount
|
||||
ds~load_coins() ;; pending_deposit_amount
|
||||
);
|
||||
}
|
||||
|
||||
cell pack_config(int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake) inline_ref {
|
||||
return begin_cell()
|
||||
.store_uint(validator_address, ADDR_SIZE())
|
||||
.store_uint(validator_reward_share, 16)
|
||||
.store_uint(max_nominators_count, 16)
|
||||
.store_coins(min_validator_stake)
|
||||
.store_coins(min_nominator_stake)
|
||||
.end_cell();
|
||||
}
|
||||
|
||||
(int, int, int, int, int) unpack_config(slice ds) inline_ref {
|
||||
return (
|
||||
ds~load_uint(ADDR_SIZE()), ;; validator_address
|
||||
ds~load_uint(16), ;; validator_reward_share
|
||||
ds~load_uint(16), ;; max_nominators_count
|
||||
ds~load_coins(), ;; min_validator_stake
|
||||
ds~load_coins() ;; min_nominator_stake
|
||||
);
|
||||
}
|
||||
|
||||
() save_data(int state, int nominators_count, int stake_amount_sent, int validator_amount, cell config, cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) impure inline_ref {
|
||||
set_data(begin_cell()
|
||||
.store_uint(state, 8)
|
||||
.store_uint(nominators_count, 16)
|
||||
.store_coins(stake_amount_sent)
|
||||
.store_coins(validator_amount)
|
||||
.store_ref(config)
|
||||
.store_dict(nominators)
|
||||
.store_dict(withdraw_requests)
|
||||
.store_uint(stake_at, 32)
|
||||
.store_uint(saved_validator_set_hash, 256)
|
||||
.store_uint(validator_set_changes_count, 8)
|
||||
.store_uint(validator_set_change_time, 32)
|
||||
.store_uint(stake_held_for, 32)
|
||||
.store_dict(config_proposal_votings)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
(int, int, int, int, (int, int, int, int, int), cell, cell, int, int, int, int, int, cell) load_data() inline_ref {
|
||||
slice ds = get_data().begin_parse();
|
||||
return (
|
||||
ds~load_uint(8), ;; state
|
||||
ds~load_uint(16), ;; nominators_count
|
||||
ds~load_coins(), ;; stake_amount_sent
|
||||
ds~load_coins(), ;; validator_amount
|
||||
unpack_config(ds~load_ref().begin_parse()), ;; config
|
||||
ds~load_dict(), ;; nominators
|
||||
ds~load_dict(), ;; withdraw_requests
|
||||
ds~load_uint(32), ;; stake_at
|
||||
ds~load_uint(256), ;; saved_validator_set_hash
|
||||
ds~load_uint(8), ;; validator_set_changes_count
|
||||
ds~load_uint(32), ;; validator_set_change_time
|
||||
ds~load_uint(32), ;; stake_held_for
|
||||
ds~load_dict() ;; config_proposal_votings
|
||||
);
|
||||
}
|
||||
|
||||
() send_msg(slice to_address, int amount, cell payload, int flags, int send_mode) impure inline_ref {
|
||||
int has_payload = ~ cell_null?(payload);
|
||||
|
||||
builder msg = begin_cell()
|
||||
.store_uint(flags, 6)
|
||||
.store_slice(to_address)
|
||||
.store_coins(amount)
|
||||
.store_uint(has_payload ? 1 : 0, 1 + 4 + 4 + 64 + 32 + 1 + 1);
|
||||
|
||||
if (has_payload) {
|
||||
msg = msg.store_ref(payload);
|
||||
}
|
||||
|
||||
send_raw_message(msg.end_cell(), send_mode);
|
||||
}
|
||||
|
||||
() send_excesses(slice sender_address) impure inline_ref {
|
||||
send_msg(sender_address, 0, null(), NON_BOUNCEABLE(), SEND_MODE_REMAINING_AMOUNT() + SEND_MODE_IGNORE_ERRORS()); ;; non-bouneable, remaining inbound message amount, fee deducted from amount, ignore errors
|
||||
}
|
||||
|
||||
(cell, cell, int, int) withdraw_nominator(int address, cell nominators, cell withdraw_requests, int balance, int nominators_count) impure inline_ref {
|
||||
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), address);
|
||||
throw_unless(60, found);
|
||||
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
|
||||
int withdraw_amount = amount + pending_deposit_amount;
|
||||
|
||||
if (withdraw_amount > balance - MIN_TONS_FOR_STORAGE()) {
|
||||
return (nominators, withdraw_requests, balance, nominators_count);
|
||||
}
|
||||
|
||||
nominators~udict_delete?(ADDR_SIZE(), address);
|
||||
withdraw_requests~udict_delete?(ADDR_SIZE(), address);
|
||||
nominators_count -= 1;
|
||||
balance -= withdraw_amount;
|
||||
|
||||
if (withdraw_amount >= ONE_TON()) {
|
||||
send_msg(make_address(0, address), withdraw_amount, null(), NON_BOUNCEABLE(), 0); ;; non-bouneable, fee deducted from amount, revert on errors
|
||||
}
|
||||
return (nominators, withdraw_requests, balance, nominators_count);
|
||||
}
|
||||
|
||||
(cell, cell, int, int) process_withdraw_requests(cell nominators, cell withdraw_requests, int balance, int nominators_count, int limit) impure inline_ref {
|
||||
int count = 0;
|
||||
int address = -1;
|
||||
int need_break = 0;
|
||||
do {
|
||||
(address, slice cs, int f) = withdraw_requests.udict_get_next?(ADDR_SIZE(), address);
|
||||
if (f) {
|
||||
(nominators, withdraw_requests, int new_balance, nominators_count) = withdraw_nominator(address, nominators, withdraw_requests, balance, nominators_count);
|
||||
need_break = (new_balance == balance);
|
||||
balance = new_balance;
|
||||
count += 1;
|
||||
if (count >= limit) {
|
||||
need_break = -1;
|
||||
}
|
||||
}
|
||||
} until ((~ f) | (need_break));
|
||||
|
||||
return (nominators, withdraw_requests, nominators_count, balance);
|
||||
}
|
||||
|
||||
int calculate_total_nominators_amount(cell nominators) inline_ref {
|
||||
int total = 0;
|
||||
int address = -1;
|
||||
do {
|
||||
(address, slice cs, int f) = nominators.udict_get_next?(ADDR_SIZE(), address);
|
||||
if (f) {
|
||||
(int amount, int pending_deposit_amount) = unpack_nominator(cs);
|
||||
total += (amount + pending_deposit_amount);
|
||||
}
|
||||
} until (~ f);
|
||||
return total;
|
||||
}
|
||||
|
||||
cell distribute_share(int reward, cell nominators) inline_ref {
|
||||
int total_amount = 0;
|
||||
int address = -1;
|
||||
do {
|
||||
(address, slice cs, int f) = nominators.udict_get_next?(ADDR_SIZE(), address);
|
||||
if (f) {
|
||||
(int amount, int pending_deposit_amount) = unpack_nominator(cs);
|
||||
total_amount += amount;
|
||||
}
|
||||
} until (~ f);
|
||||
|
||||
cell new_nominators = new_dict();
|
||||
address = -1;
|
||||
do {
|
||||
(address, slice cs, int f) = nominators.udict_get_next?(ADDR_SIZE(), address);
|
||||
if (f) {
|
||||
(int amount, int pending_deposit_amount) = unpack_nominator(cs);
|
||||
if (total_amount > 0) {
|
||||
amount += muldiv(reward, amount, total_amount);
|
||||
if (amount < 0) {
|
||||
amount = 0;
|
||||
}
|
||||
}
|
||||
amount += pending_deposit_amount;
|
||||
new_nominators~udict_set_builder(ADDR_SIZE(), address, pack_nominator(amount, 0));
|
||||
}
|
||||
} until (~ f);
|
||||
|
||||
return new_nominators;
|
||||
}
|
||||
|
||||
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
|
||||
int balance = pair_first(get_balance());
|
||||
|
||||
slice cs = in_msg_full.begin_parse();
|
||||
int flags = cs~load_uint(4);
|
||||
|
||||
slice sender_address = cs~load_msg_addr();
|
||||
(int sender_wc, int sender_addr) = parse_std_addr(sender_address);
|
||||
|
||||
if (flags & 1) { ;; bounced messages
|
||||
if (in_msg_body.slice_bits() >= 64) {
|
||||
in_msg_body~skip_bits(32); ;; skip 0xFFFFFFFF bounced prefix
|
||||
int op = in_msg_body~load_uint(32);
|
||||
if ((op == op::new_stake()) & (is_elector_address(sender_wc, sender_addr))) {
|
||||
;; `new_stake` from nominator-pool should always be handled without throws by elector
|
||||
;; because nominator-pool do `check_new_stake_msg` and `msg_value` checks before sending `new_stake`.
|
||||
;; If the stake is not accepted elector will send `new_stake_error` response message.
|
||||
;; Nevertheless we do process theoretically possible bounced `new_stake`.
|
||||
|
||||
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
|
||||
if (state == 1) {
|
||||
state = 0;
|
||||
}
|
||||
save_data(
|
||||
state,
|
||||
nominators_count,
|
||||
stake_amount_sent,
|
||||
validator_amount,
|
||||
pack_config(validator_address, validator_reward_share, max_nominators_count, min_validator_stake, min_nominator_stake),
|
||||
nominators,
|
||||
withdraw_requests,
|
||||
stake_at,
|
||||
saved_validator_set_hash,
|
||||
validator_set_changes_count,
|
||||
validator_set_change_time,
|
||||
stake_held_for,
|
||||
config_proposal_votings
|
||||
);
|
||||
}
|
||||
}
|
||||
return (); ;; ignore other bounces messages
|
||||
}
|
||||
|
||||
int op = in_msg_body~load_uint(32);
|
||||
|
||||
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
|
||||
|
||||
if (op == 0) {
|
||||
;; We use simple text comments for nominator operations so nominators can do it from any wallet app.
|
||||
;; In other cases, they will need to put a stake on a browser extension, or use scripts, which can be inconvenient.
|
||||
|
||||
;; Throw on any unexpected request so that the stake is bounced back to the nominator in case of a typo.
|
||||
|
||||
int action = in_msg_body~load_uint(8);
|
||||
int is_vote = (action == 121) | (action == 110); ;; "y" or "n"
|
||||
throw_unless(64, (action == 100) | (action == 119) | is_vote); ;; "d" or "w" or "y" or "n"
|
||||
|
||||
if (~ is_vote) {
|
||||
in_msg_body.end_parse();
|
||||
throw_unless(61, sender_wc == 0); ;; nominators only in basechain
|
||||
throw_unless(62, sender_addr != validator_address);
|
||||
}
|
||||
|
||||
if (action == 100) { ;; "d" - deposit nominator (any time, will take effect in the next round)
|
||||
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), sender_addr);
|
||||
|
||||
if (~ found) {
|
||||
nominators_count += 1;
|
||||
}
|
||||
throw_unless(65, nominators_count <= max_nominators_count);
|
||||
|
||||
msg_value -= DEPOSIT_PROCESSING_FEE();
|
||||
throw_unless(66, msg_value > 0);
|
||||
|
||||
(int amount, int pending_deposit_amount) = found ? unpack_nominator(nominator) : (0, 0);
|
||||
if (state == 0) {
|
||||
amount += msg_value;
|
||||
} else {
|
||||
pending_deposit_amount += msg_value;
|
||||
}
|
||||
throw_unless(67, amount + pending_deposit_amount >= min_nominator_stake);
|
||||
throw_unless(68, cell_depth(nominators) < max(5, binary_log_ceil(nominators_count) * 2) ); ;; prevent dict depth ddos
|
||||
nominators~udict_set_builder(ADDR_SIZE(), sender_addr, pack_nominator(amount, pending_deposit_amount));
|
||||
}
|
||||
|
||||
if (action == 119) { ;; "w" - withdraw request (any time)
|
||||
if (state == 0) {
|
||||
(nominators, withdraw_requests, int new_balance, nominators_count) = withdraw_nominator(sender_addr, nominators, withdraw_requests, balance, nominators_count);
|
||||
if (new_balance - msg_value >= MIN_TONS_FOR_STORAGE()) {
|
||||
send_excesses(sender_address);
|
||||
}
|
||||
} else {
|
||||
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), sender_addr);
|
||||
throw_unless(69, found);
|
||||
withdraw_requests~udict_set_builder(ADDR_SIZE(), sender_addr, begin_cell());
|
||||
send_excesses(sender_address);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_vote) {
|
||||
int authorized = (sender_wc == -1) & (sender_addr == validator_address);
|
||||
|
||||
if (~ authorized) {
|
||||
throw_unless(121, sender_wc == 0);
|
||||
(slice nominator, authorized) = nominators.udict_get?(ADDR_SIZE(), sender_addr);
|
||||
throw_unless(122, authorized);
|
||||
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
|
||||
throw_unless(123, amount > 0);
|
||||
}
|
||||
|
||||
int proposal_hash = in_msg_body~load_text_hex_number(32);
|
||||
in_msg_body.end_parse();
|
||||
int support = action == 121;
|
||||
|
||||
(slice votes_slice, int found) = config_proposal_votings.udict_get?(256, proposal_hash);
|
||||
|
||||
if (~ found) {
|
||||
;; require higher fee to prevent dictionary spam
|
||||
int fee = ONE_TON();
|
||||
int power = cell_depth(config_proposal_votings);
|
||||
repeat (power) {
|
||||
fee = muldiv(fee, 15, 10);
|
||||
}
|
||||
throw_unless(123, msg_value >= fee);
|
||||
}
|
||||
|
||||
(cell votes_dict, int votes_create_time) = found ? (votes_slice~load_dict(), votes_slice~load_uint(32)) : (new_dict(), now());
|
||||
|
||||
(_, int vote_found) = votes_dict.udict_get?(256, sender_addr);
|
||||
throw_if(124, vote_found);
|
||||
votes_dict~udict_set_builder(256, sender_addr, begin_cell().store_int(support, 1).store_uint(now(), 32));
|
||||
|
||||
builder new_votes = begin_cell().store_dict(votes_dict).store_uint(votes_create_time, 32);
|
||||
config_proposal_votings~udict_set_builder(256, proposal_hash, new_votes);
|
||||
|
||||
if (found) {
|
||||
send_excesses(sender_address);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
int query_id = in_msg_body~load_uint(64);
|
||||
|
||||
if (is_elector_address(sender_wc, sender_addr)) { ;; response from elector
|
||||
|
||||
accept_message();
|
||||
|
||||
if (op == op::recover_stake_ok()) {
|
||||
state = 0;
|
||||
|
||||
int reward = msg_value - stake_amount_sent;
|
||||
int nominators_reward = 0;
|
||||
|
||||
if (reward <= 0) {
|
||||
validator_amount += reward;
|
||||
if (validator_amount < 0) {
|
||||
;; even this should never happen
|
||||
nominators_reward = validator_amount;
|
||||
validator_amount = 0;
|
||||
}
|
||||
} else {
|
||||
int validator_reward = (reward * validator_reward_share) / 10000;
|
||||
if (validator_reward > reward) { ;; Theoretical invalid case if validator_reward_share > 10000
|
||||
validator_reward = reward;
|
||||
}
|
||||
validator_amount += validator_reward;
|
||||
nominators_reward = reward - validator_reward;
|
||||
}
|
||||
|
||||
nominators = distribute_share(nominators_reward, nominators); ;; call even if there was no reward to process deposit requests
|
||||
stake_amount_sent = 0;
|
||||
}
|
||||
|
||||
if (state == 1) {
|
||||
if (op == op::new_stake_error()) { ;; error when new_stake; stake returned
|
||||
state = 0;
|
||||
}
|
||||
|
||||
if (op == op::new_stake_ok()) {
|
||||
state = 2;
|
||||
}
|
||||
}
|
||||
|
||||
;; else just accept coins from elector
|
||||
|
||||
} else {
|
||||
|
||||
;; throw on any unexpected request so that the coins is bounced back to the sender in case of a typo
|
||||
throw_unless(70, ((op >= 1) & (op <= 7)) | (op == op::recover_stake()) | (op == op::new_stake()));
|
||||
|
||||
if (op == 1) {
|
||||
;; just accept coins
|
||||
}
|
||||
|
||||
if (op == 2) { ;; process withdraw requests (at any time while the balance is enough)
|
||||
int limit = in_msg_body~load_uint(8);
|
||||
|
||||
(nominators, withdraw_requests, nominators_count, int new_balance) = process_withdraw_requests(nominators, withdraw_requests, balance, nominators_count, limit);
|
||||
|
||||
if (new_balance - msg_value >= MIN_TONS_FOR_STORAGE()) {
|
||||
send_excesses(sender_address);
|
||||
}
|
||||
}
|
||||
|
||||
if (op == 3) { ;; emergency process withdraw request (at any time if the balance is enough)
|
||||
int request_address = in_msg_body~load_uint(ADDR_SIZE());
|
||||
(slice withdraw_request, int found) = withdraw_requests.udict_get?(ADDR_SIZE(), request_address);
|
||||
throw_unless(71, found);
|
||||
(nominators, withdraw_requests, int new_balance, nominators_count) = withdraw_nominator(request_address, nominators, withdraw_requests, balance, nominators_count);
|
||||
if (new_balance - msg_value >= MIN_TONS_FOR_STORAGE()) {
|
||||
send_excesses(sender_address);
|
||||
}
|
||||
}
|
||||
|
||||
if (op == 6) { ;; update current valudator set hash (anyone can invoke)
|
||||
throw_unless(113, validator_set_changes_count < 3);
|
||||
(int utime_since, int utime_until, cell vset) = get_current_validator_set();
|
||||
int current_hash = cell_hash(vset);
|
||||
if (saved_validator_set_hash != current_hash) {
|
||||
saved_validator_set_hash = current_hash;
|
||||
validator_set_changes_count += 1;
|
||||
validator_set_change_time = now();
|
||||
}
|
||||
send_excesses(sender_address);
|
||||
}
|
||||
|
||||
if (op == 7) { ;; clean up outdating votings
|
||||
int t = now();
|
||||
int proposal_hash = -1;
|
||||
do {
|
||||
(proposal_hash, slice votes_slice, int found) = config_proposal_votings.udict_get_next?(256, proposal_hash);
|
||||
if (found) {
|
||||
(cell votes_dict, int votes_create_time) = (votes_slice~load_dict(), votes_slice~load_uint(32));
|
||||
if (t - votes_create_time > VOTES_LIFETIME()) {
|
||||
config_proposal_votings~udict_delete?(256, proposal_hash);
|
||||
}
|
||||
}
|
||||
} until (~ found);
|
||||
send_excesses(sender_address);
|
||||
}
|
||||
|
||||
if (op == op::recover_stake()) { ;; send recover_stake to elector (anyone can send)
|
||||
|
||||
;; We need to take all credits from the elector at once,
|
||||
;; because if we do not take all at once, then it will be processed as a fine by pool.
|
||||
;; In the elector, credits (`credit_to`) are accrued in three places:
|
||||
;; 1) return of surplus stake in elections (`try_elect`)
|
||||
;; 2) reward for complaint when punish (`punish`) - before unfreezing
|
||||
;; 3) unfreeze round (`unfreeze_without_bonuses`/`unfreeze_with_bonuses`)
|
||||
;; We need to be guaranteed to wait for unfreezing round and only then send `recover_stake`.
|
||||
;; So we are waiting for the change of 3 validator sets.
|
||||
|
||||
;; ADDITIONAL NOTE:
|
||||
;; In a special case (if the network was down), the config theoretically can refuse the elector to save a new round after election - https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/config-code.fc#L494
|
||||
;; and the elector will start a new election - https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L364
|
||||
;; in this case, our pool will have to skip the round, but it will be able to recover stake later
|
||||
|
||||
throw_unless(111, validator_set_changes_count >= 2);
|
||||
throw_unless(112, (validator_set_changes_count > 2) | (now() - validator_set_change_time > stake_held_for + 60));
|
||||
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L887
|
||||
|
||||
cell payload = begin_cell().store_uint(op::recover_stake(), 32).store_uint(query_id, 64).end_cell();
|
||||
send_msg(elector_address(), 0, payload, BOUNCEABLE(), SEND_MODE_REMAINING_AMOUNT()); ;; bounceable, carry all the remaining value of the inbound message, fee deducted from amount, revert on errors
|
||||
}
|
||||
|
||||
;; message from validator
|
||||
|
||||
if (op == 4) { ;; deposit validator (any time)
|
||||
throw_unless(73, (sender_wc == -1) & (sender_addr == validator_address));
|
||||
msg_value -= DEPOSIT_PROCESSING_FEE();
|
||||
throw_unless(74, msg_value > 0);
|
||||
validator_amount += msg_value;
|
||||
}
|
||||
|
||||
if (op == 5) { ;; withdraw validator (after recover_stake and before new_stake)
|
||||
throw_unless(74, state == 0); ;; no withdraw request because validator software can wait right time
|
||||
throw_unless(75, (sender_wc == -1) & (sender_addr == validator_address));
|
||||
int request_amount = in_msg_body~load_coins();
|
||||
throw_unless(78, request_amount > 0);
|
||||
|
||||
int total_nominators_amount = calculate_total_nominators_amount(nominators);
|
||||
;; the validator can withdraw everything that does not belong to the nominators
|
||||
throw_unless(76, request_amount <= balance - MIN_TONS_FOR_STORAGE() - total_nominators_amount);
|
||||
validator_amount -= request_amount;
|
||||
if (validator_amount < 0) {
|
||||
validator_amount = 0;
|
||||
}
|
||||
send_msg(make_address(-1, validator_address), request_amount, null(), NON_BOUNCEABLE(), 0); ;; non-bouneable, fee deducted from amount, revert on errors
|
||||
int new_balance = balance - request_amount;
|
||||
if (new_balance - msg_value >= MIN_TONS_FOR_STORAGE()) {
|
||||
send_excesses(sender_address);
|
||||
}
|
||||
}
|
||||
|
||||
if (op == op::new_stake()) {
|
||||
throw_unless(78, (sender_wc == -1) & (sender_addr == validator_address));
|
||||
|
||||
throw_unless(79, state == 0);
|
||||
|
||||
throw_unless(80, query_id); ;; query_id must be greater then 0 to receive confirmation message from elector
|
||||
|
||||
throw_unless(86, msg_value >= ONE_TON()); ;; must be greater then new_stake sending to elector fee
|
||||
|
||||
int value = in_msg_body~load_coins();
|
||||
|
||||
slice msg = in_msg_body;
|
||||
|
||||
stake_at = check_new_stake_msg(in_msg_body);
|
||||
|
||||
stake_amount_sent = value - ONE_TON();
|
||||
|
||||
throw_unless(81, value >= MIN_STAKE_TO_SEND());
|
||||
|
||||
throw_unless(82, value <= balance - MIN_TONS_FOR_STORAGE());
|
||||
|
||||
throw_unless(83, validator_amount >= min_validator_stake);
|
||||
|
||||
throw_unless(84, validator_amount >= max_recommended_punishment_for_validator_misbehaviour(stake_amount_sent));
|
||||
|
||||
throw_unless(85, cell_null?(withdraw_requests)); ;; no withdraw requests
|
||||
|
||||
state = 1;
|
||||
(int utime_since, int utime_until, cell vset) = get_current_validator_set();
|
||||
saved_validator_set_hash = cell_hash(vset); ;; current validator set, we will be in next validator set
|
||||
validator_set_changes_count = 0;
|
||||
validator_set_change_time = utime_since;
|
||||
stake_held_for = get_validator_config(); ;; save `stake_held_for` in case the config changes in the process
|
||||
|
||||
send_msg(elector_address(), value, begin_cell().store_uint(op, 32).store_uint(query_id, 64).store_slice(msg).end_cell(), BOUNCEABLE(), SEND_MODE_PAY_FEE_SEPARATELY()); ;; pay fee separately, rever on errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
save_data(
|
||||
state,
|
||||
nominators_count,
|
||||
stake_amount_sent,
|
||||
validator_amount,
|
||||
pack_config(validator_address, validator_reward_share, max_nominators_count, min_validator_stake, min_nominator_stake),
|
||||
nominators,
|
||||
withdraw_requests,
|
||||
stake_at,
|
||||
saved_validator_set_hash,
|
||||
validator_set_changes_count,
|
||||
validator_set_change_time,
|
||||
stake_held_for,
|
||||
config_proposal_votings
|
||||
);
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
_ get_pool_data() method_id {
|
||||
return load_data();
|
||||
}
|
||||
|
||||
int has_withdraw_requests() method_id {
|
||||
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
|
||||
return ~ cell_null?(withdraw_requests);
|
||||
}
|
||||
|
||||
(int, int, int) get_nominator_data(int nominator_address) method_id {
|
||||
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
|
||||
|
||||
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), nominator_address);
|
||||
throw_unless(86, found);
|
||||
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
|
||||
(slice withdraw_request, int withdraw_found) = withdraw_requests.udict_get?(ADDR_SIZE(), nominator_address);
|
||||
|
||||
return (amount, pending_deposit_amount, withdraw_found);
|
||||
}
|
||||
|
||||
int get_max_punishment(int stake) method_id {
|
||||
return max_recommended_punishment_for_validator_misbehaviour(stake);
|
||||
}
|
||||
|
||||
tuple list_nominators() method_id {
|
||||
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
|
||||
var list = null();
|
||||
int address = -1;
|
||||
do {
|
||||
(address, slice nominator, int found) = nominators.udict_get_next?(ADDR_SIZE(), address);
|
||||
if (found) {
|
||||
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
|
||||
(_, int withdraw_requested) = withdraw_requests.udict_get?(ADDR_SIZE(), address);
|
||||
list = cons(tuple4(address, amount, pending_deposit_amount, withdraw_requested), list);
|
||||
}
|
||||
} until (~ found);
|
||||
return list;
|
||||
}
|
||||
|
||||
tuple list_votes() method_id {
|
||||
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
|
||||
var list = null();
|
||||
int proposal_hash = -1;
|
||||
do {
|
||||
(proposal_hash, slice votes_slice, int found) = config_proposal_votings.udict_get_next?(256, proposal_hash);
|
||||
if (found) {
|
||||
(cell votes_dict, int votes_create_time) = (votes_slice~load_dict(), votes_slice~load_uint(32));
|
||||
list = cons(pair(proposal_hash, votes_create_time), list);
|
||||
}
|
||||
} until (~ found);
|
||||
return list;
|
||||
}
|
||||
|
||||
tuple list_voters(int proposal_hash) method_id {
|
||||
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
|
||||
var list = null();
|
||||
(slice votes_slice, int found) = config_proposal_votings.udict_get?(256, proposal_hash);
|
||||
throw_unless(133, found);
|
||||
cell votes_dict = votes_slice~load_dict();
|
||||
|
||||
int address = -1;
|
||||
do {
|
||||
(address, slice cs, int found) = votes_dict.udict_get_next?(ADDR_SIZE(), address);
|
||||
if (found) {
|
||||
(int support, int vote_time) = (cs~load_int(1), cs~load_uint(32));
|
||||
list = cons(triple(address, support, vote_time), list);
|
||||
}
|
||||
} until (~ found);
|
||||
return list;
|
||||
}
|
211
crypto/func/auto-tests/legacy_tests/nominator-pool/stdlib.fc
Normal file
211
crypto/func/auto-tests/legacy_tests/nominator-pool/stdlib.fc
Normal file
|
@ -0,0 +1,211 @@
|
|||
;; Standard library for funC
|
||||
;;
|
||||
|
||||
forall X -> tuple cons(X head, tuple tail) asm "CONS";
|
||||
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
|
||||
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
|
||||
forall X -> X car(tuple list) asm "CAR";
|
||||
tuple cdr(tuple list) asm "CDR";
|
||||
tuple empty_tuple() asm "NIL";
|
||||
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
|
||||
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
|
||||
forall X -> [X] single(X x) asm "SINGLE";
|
||||
forall X -> X unsingle([X] t) asm "UNSINGLE";
|
||||
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
|
||||
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
|
||||
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
|
||||
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
|
||||
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
|
||||
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
|
||||
forall X -> X first(tuple t) asm "FIRST";
|
||||
forall X -> X second(tuple t) asm "SECOND";
|
||||
forall X -> X third(tuple t) asm "THIRD";
|
||||
forall X -> X fourth(tuple t) asm "3 INDEX";
|
||||
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
|
||||
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
|
||||
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
|
||||
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
|
||||
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
|
||||
forall X -> X null() asm "PUSHNULL";
|
||||
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
|
||||
|
||||
int now() asm "NOW";
|
||||
slice my_address() asm "MYADDR";
|
||||
[int, cell] get_balance() asm "BALANCE";
|
||||
int cur_lt() asm "LTIME";
|
||||
int block_lt() asm "BLOCKLT";
|
||||
|
||||
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";
|
||||
|
||||
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
|
||||
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
|
||||
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
|
||||
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
|
||||
|
||||
;; () 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";
|
||||
cont get_c3() impure asm "c3 PUSH";
|
||||
() set_c3(cont c) impure asm "c3 POP";
|
||||
cont bless(slice s) impure asm "BLESS";
|
||||
|
||||
() accept_message() impure asm "ACCEPT";
|
||||
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
|
||||
() commit() impure asm "COMMIT";
|
||||
() buy_gas(int gram) impure asm "BUYGAS";
|
||||
|
||||
int min(int x, int y) asm "MIN";
|
||||
int max(int x, int y) asm "MAX";
|
||||
(int, int) minmax(int x, int y) asm "MINMAX";
|
||||
int abs(int x) asm "ABS";
|
||||
|
||||
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, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
|
||||
slice first_bits(slice s, int len) asm "SDCUTFIRST";
|
||||
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
|
||||
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
|
||||
slice slice_last(slice s, int len) asm "SDCUTLAST";
|
||||
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
|
||||
cell preload_dict(slice s) asm "PLDDICT";
|
||||
slice skip_dict(slice s) asm "SKIPDICT";
|
||||
|
||||
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
|
||||
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
|
||||
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
|
||||
|
||||
int cell_depth(cell c) asm "CDEPTH";
|
||||
|
||||
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";
|
||||
int slice_depth(slice s) asm "SDEPTH";
|
||||
|
||||
int builder_refs(builder b) asm "BREFS";
|
||||
int builder_bits(builder b) asm "BBITS";
|
||||
int builder_depth(builder b) asm "BDEPTH";
|
||||
|
||||
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 udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
|
||||
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
|
||||
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
|
||||
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
|
||||
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
|
||||
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
|
||||
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
|
||||
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
|
||||
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
|
||||
(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(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
|
||||
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
|
||||
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
|
||||
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
|
||||
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
|
||||
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
|
||||
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
|
||||
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
|
||||
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
|
||||
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
|
||||
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) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
|
||||
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
|
||||
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
|
||||
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
|
||||
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
|
||||
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
|
||||
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
|
||||
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
|
||||
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
|
||||
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
|
||||
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
|
||||
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
|
||||
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
|
||||
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
|
||||
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
|
||||
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
|
||||
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
|
||||
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
|
||||
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
|
||||
cell new_dict() asm "NEWDICT";
|
||||
int dict_empty?(cell c) asm "DICTEMPTY";
|
||||
|
||||
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
|
||||
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
|
||||
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
|
||||
|
||||
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(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
|
||||
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
|
||||
() set_code(cell new_code) impure asm "SETCODE";
|
||||
|
||||
int random() impure asm "RANDU256";
|
||||
int rand(int range) impure asm "RAND";
|
||||
int get_seed() impure asm "RANDSEED";
|
||||
int set_seed() impure asm "SETRAND";
|
||||
() randomize(int x) impure asm "ADDRAND";
|
||||
() randomize_lt() impure asm "LTIME" "ADDRAND";
|
||||
|
||||
builder store_coins(builder b, int x) asm "STVARUINT16";
|
||||
(slice, int) load_coins(slice s) asm( -> 1 0) "LDVARUINT16";
|
Loading…
Add table
Add a link
Reference in a new issue