mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-15 04:32:21 +00:00
* Add legacy_tester for existing funC contracts * Add storage-contracts and pragma options
414 lines
No EOL
10 KiB
Text
414 lines
No EOL
10 KiB
Text
;;
|
|
;; Stake Sending
|
|
;;
|
|
|
|
() op_controller_stake_send(int value, slice in_msg) impure {
|
|
|
|
;; Parse message
|
|
var stake = in_msg~load_coins();
|
|
var validator_pubkey = in_msg~load_uint(256);
|
|
var stake_at = in_msg~load_uint(32);
|
|
var max_factor = in_msg~load_uint(32);
|
|
var adnl_addr = in_msg~load_uint(256);
|
|
var signature_ref = in_msg~load_ref();
|
|
var signature = signature_ref.begin_parse().preload_bits(512);
|
|
in_msg.end_parse();
|
|
|
|
;; Check message value
|
|
throw_unless(error::invalid_message(), value >= fees::stake_fees());
|
|
|
|
;; Allow only single request to elector
|
|
if (proxy_stored_query_id != 0) {
|
|
throw(error::invalid_message());
|
|
}
|
|
|
|
;; Allow update only for current stake
|
|
if ((proxy_stake_at != 0) & (proxy_stake_at != stake_at)) {
|
|
throw(error::invalid_message());
|
|
}
|
|
|
|
;; Check stake value
|
|
var availableStake = available_to_stake();
|
|
throw_unless(error::invalid_stake_value(), availableStake >= stake);
|
|
|
|
;; Parameters
|
|
var (electedFor, stakeHeldFor) = get_stake_parameters();
|
|
|
|
;; Lock stakes
|
|
on_locked();
|
|
|
|
;; Update operation state
|
|
proxy_stake_at = stake_at;
|
|
proxy_stake_until = stake_at + electedFor + stakeHeldFor;
|
|
proxy_stake_sent = proxy_stake_sent + stake;
|
|
proxy_stored_query_id = ctx_query_id;
|
|
proxy_stored_query_op = elector::stake::request();
|
|
proxy_stored_query_stake = stake;
|
|
|
|
;; Update balances
|
|
on_stake_sent(stake);
|
|
|
|
;; Send message to elector
|
|
send_std_message(
|
|
ctx_proxy,
|
|
stake + coins::1(),
|
|
send_mode::separate_gas(),
|
|
elector::stake::request(),
|
|
proxy_stored_query_id,
|
|
begin_cell()
|
|
.store_uint(validator_pubkey, 256)
|
|
.store_uint(stake_at, 32)
|
|
.store_uint(max_factor, 32)
|
|
.store_uint(adnl_addr, 256)
|
|
.store_ref(signature_ref)
|
|
);
|
|
|
|
;; Persist
|
|
store_validator_data();
|
|
store_base_data();
|
|
}
|
|
|
|
() op_elector_stake_response(int value, slice in_msg) impure {
|
|
|
|
;; Check response
|
|
if (ctx_query_id != proxy_stored_query_id) {
|
|
;; How to handle invalid? How it is possible?
|
|
return ();
|
|
}
|
|
if (proxy_stored_query_op != elector::stake::request()) {
|
|
;; How to handle invalid? How it is possible?
|
|
return ();
|
|
}
|
|
|
|
;; Reset active query
|
|
proxy_stored_query_id = 0;
|
|
proxy_stored_query_op = 0;
|
|
proxy_stored_query_stake = 0;
|
|
|
|
;; Persist
|
|
store_validator_data();
|
|
store_base_data();
|
|
}
|
|
|
|
() op_elector_stake_response_fail(int value, slice in_msg) impure {
|
|
|
|
;; Load reason
|
|
var reason = in_msg~load_uint(32);
|
|
|
|
;; Check response
|
|
if (ctx_query_id != proxy_stored_query_id) {
|
|
;; How to handle invalid? How it is possible?
|
|
return ();
|
|
}
|
|
if (proxy_stored_query_op != elector::stake::request()) {
|
|
;; How to handle invalid? How it is possible?
|
|
return ();
|
|
}
|
|
|
|
;; Update balances
|
|
on_stake_sent_failed(proxy_stored_query_stake);
|
|
|
|
;; Update proxy state
|
|
proxy_stake_sent = proxy_stake_sent - proxy_stored_query_stake;
|
|
|
|
;; Reset stake at since sent stake became zero
|
|
if (proxy_stake_sent == 0) {
|
|
proxy_stake_at = 0;
|
|
proxy_stake_until = 0;
|
|
proxy_stake_sent = 0;
|
|
on_unlocked();
|
|
}
|
|
|
|
;; Reset query
|
|
proxy_stored_query_id = 0;
|
|
proxy_stored_query_op = 0;
|
|
proxy_stored_query_stake = 0;
|
|
|
|
;; Persist
|
|
store_validator_data();
|
|
store_base_data();
|
|
}
|
|
|
|
;;
|
|
;; Recover
|
|
;;
|
|
|
|
() op_stake_recover(int value) impure {
|
|
|
|
;; NOTE: We never block stake recover operation
|
|
;; in case of misbehaviour of something anyone always can get
|
|
;; coins from elector after lockup period is up
|
|
|
|
;; Allow request only if stake is exited lockup period
|
|
if ((proxy_stake_until != 0) & (now() < proxy_stake_until)) {
|
|
throw(error::invalid_message());
|
|
}
|
|
|
|
;; Double check that validation session and lockup was lifted
|
|
if ((proxy_stake_until != 0) & (proxy_stake_at != 0)) {
|
|
throw_unless(error::invalid_message(), lockup_lift_time(proxy_stake_at, proxy_stake_until) <= now());
|
|
}
|
|
|
|
;; Check value
|
|
throw_unless(error::too_low_value(), value >= fees::stake_fees());
|
|
|
|
;; Send message to elector
|
|
send_empty_std_message(
|
|
ctx_proxy,
|
|
0,
|
|
send_mode::carry_remaining_value(),
|
|
elector::refund::request(),
|
|
proxy_stored_query_id
|
|
);
|
|
|
|
;; Persist
|
|
store_validator_data();
|
|
store_base_data();
|
|
}
|
|
|
|
() op_elector_recover_response(int value, slice in_msg) impure {
|
|
|
|
if ((proxy_stake_until != 0) & (now() > proxy_stake_until)) {
|
|
|
|
;; Reset state: all stake is returned
|
|
proxy_stake_sent = 0;
|
|
proxy_stake_at = 0;
|
|
proxy_stake_until = 0;
|
|
|
|
;; Reset query too
|
|
proxy_stored_query_id = 0;
|
|
proxy_stored_query_op = 0;
|
|
proxy_stored_query_stake = 0;
|
|
|
|
;; Handle stake recovered event
|
|
;; NOTE: Any stake recovery outside this condition might be just a noise and
|
|
;; effect of various race condirtions that doesn't carry any substantianal vakue
|
|
on_stake_recovered(value - fees::stake_fees());
|
|
|
|
;; Reset lock state
|
|
;; NOTE: MUST be after on_stake_recovered since it adjusts withdrawals and
|
|
;; modifies global balance
|
|
on_unlocked();
|
|
}
|
|
|
|
;; Persist
|
|
store_validator_data();
|
|
store_base_data();
|
|
}
|
|
|
|
;;
|
|
;; Withdraw unowned
|
|
;;
|
|
|
|
() op_controller_withdraw_unowned(int value, slice in_msg) impure {
|
|
|
|
;; Reserve owned
|
|
raw_reserve(owned_balance(), 0);
|
|
|
|
;; Send message to controller
|
|
send_empty_std_message(
|
|
ctx_controller,
|
|
0,
|
|
send_mode::carry_remaining_balance(),
|
|
op::withdraw_unowned::response(),
|
|
ctx_query_id
|
|
);
|
|
}
|
|
|
|
;;
|
|
;; Process pending
|
|
;;
|
|
|
|
() op_controller_accept_stakes(int value, slice in_msg) impure {
|
|
|
|
;; Check if enought value
|
|
throw_unless(error::invalid_message(), value >= params::pending_op());
|
|
|
|
;; Check if not locked
|
|
throw_if(error::invalid_message(), ctx_locked);
|
|
|
|
;; Parse message
|
|
var members = in_msg~load_dict();
|
|
in_msg.end_parse();
|
|
|
|
;; Process operations
|
|
var member = -1;
|
|
do {
|
|
(member, var cs, var f) = members.udict_get_next?(256, member);
|
|
if (f) {
|
|
;; Accept member's stake
|
|
load_member(member);
|
|
member_accept_stake();
|
|
store_member();
|
|
}
|
|
} until (~ f);
|
|
|
|
;; Persist
|
|
store_base_data();
|
|
}
|
|
|
|
() op_controller_accept_withdraws(int value, slice in_msg) impure {
|
|
|
|
;; Check if enought value
|
|
throw_unless(error::invalid_message(), value >= params::pending_op());
|
|
|
|
;; Check if not locked
|
|
throw_if(error::invalid_message(), ctx_locked);
|
|
|
|
;; Parse message
|
|
var members = in_msg~load_dict();
|
|
in_msg.end_parse();
|
|
|
|
;; Process operations
|
|
var member = -1;
|
|
do {
|
|
(member, var cs, var f) = members.udict_get_next?(256, member);
|
|
if (f) {
|
|
;; Accept member's stake
|
|
load_member(member);
|
|
member_accept_withdraw();
|
|
store_member();
|
|
}
|
|
} until (~ f);
|
|
|
|
;; Persist
|
|
store_base_data();
|
|
}
|
|
|
|
() op_controller_force_kick(int value, slice in_msg) impure {
|
|
|
|
;; Check if enought value
|
|
throw_unless(error::invalid_message(), value >= params::pending_op());
|
|
|
|
;; Check if not locked
|
|
throw_if(error::invalid_message(), ctx_locked);
|
|
|
|
;; Parse message
|
|
var members = in_msg~load_dict();
|
|
in_msg.end_parse();
|
|
|
|
;; Process operations
|
|
var member = -1;
|
|
do {
|
|
(member, var cs, var f) = members.udict_get_next?(256, member);
|
|
if (f) {
|
|
|
|
;; Reject owner kicking
|
|
throw_if(error::invalid_message(), member == owner_id());
|
|
|
|
;; Kick member from a pool
|
|
load_member(member);
|
|
|
|
;; Withdraw everything
|
|
var (withdrawed, all) = member_stake_withdraw(0);
|
|
throw_unless(error::invalid_message(), withdrawed > 0);
|
|
throw_unless(error::invalid_message(), all);
|
|
|
|
;; Forced kick
|
|
send_empty_std_message(
|
|
serialize_work_addr(member),
|
|
withdrawed,
|
|
send_mode::default(),
|
|
op::force_kick::notification(),
|
|
ctx_query_id
|
|
);
|
|
|
|
;; Persist membership
|
|
store_member();
|
|
}
|
|
} until (~ f);
|
|
|
|
;; Persist
|
|
store_base_data();
|
|
}
|
|
|
|
;;
|
|
;; Top Level
|
|
;;
|
|
|
|
() op_controller(int flags, int value, slice in_msg) impure {
|
|
if (flags & 1) {
|
|
return ();
|
|
}
|
|
|
|
;; Check value
|
|
throw_unless(error::invalid_message(), value >= params::min_op());
|
|
|
|
;; Parse operation
|
|
int op = in_msg~load_uint(32);
|
|
int query_id = in_msg~load_uint(64);
|
|
int gas_limit = in_msg~load_coins();
|
|
set_gas_limit(gas_limit);
|
|
ctx_query_id = query_id;
|
|
throw_unless(error::invalid_message(), ctx_query_id > 0);
|
|
|
|
;; Send stake
|
|
if (op == op::stake_send()) {
|
|
op_controller_stake_send(value, in_msg);
|
|
return ();
|
|
}
|
|
|
|
;; Recover stake
|
|
if (op == op::stake_recover()) {
|
|
op_stake_recover(value);
|
|
return ();
|
|
}
|
|
|
|
;; Withdraw unowned
|
|
if (op == op::withdraw_unowned()) {
|
|
op_controller_withdraw_unowned(value, in_msg);
|
|
return ();
|
|
}
|
|
|
|
;; Accept stakes
|
|
if (op == op::accept_stakes()) {
|
|
op_controller_accept_stakes(value, in_msg);
|
|
return ();
|
|
}
|
|
|
|
;; Accept withdraws
|
|
if (op == op::accept_withdraws()) {
|
|
op_controller_accept_withdraws(value, in_msg);
|
|
return ();
|
|
}
|
|
|
|
;; Kick from pool
|
|
if (op == op::force_kick()) {
|
|
op_controller_force_kick(value, in_msg);
|
|
return ();
|
|
}
|
|
|
|
;; Unknown message
|
|
throw(error::invalid_message());
|
|
}
|
|
|
|
() op_elector(int flags, int value, slice in_msg) impure {
|
|
int op = in_msg~load_uint(32);
|
|
int query_id = in_msg~load_uint(64);
|
|
ctx_query_id = query_id;
|
|
|
|
;; Bounced
|
|
;; It seems that handling doesn't make sence sicne there are no throws (?)
|
|
;; in elector contract
|
|
if (flags & 1) {
|
|
return ();
|
|
}
|
|
|
|
;; Stake response
|
|
if (op == elector::stake::response()) {
|
|
op_elector_stake_response(value, in_msg);
|
|
return ();
|
|
}
|
|
if (op == elector::stake::response::fail()) {
|
|
op_elector_stake_response_fail(value, in_msg);
|
|
return ();
|
|
}
|
|
|
|
;; Refund response
|
|
if (op == elector::refund::response()) {
|
|
op_elector_recover_response(value, in_msg);
|
|
return ();
|
|
}
|
|
|
|
;; Ignore
|
|
} |