2#include <sysio/action.hpp>
19void system_contract::adjust_resources(
name payer,
name account, symbol core_symbol,
int64_t net_delta,
20 int64_t cpu_delta,
bool must_not_be_managed) {
21 if (!net_delta && !cpu_delta)
25 auto tot_itr = totals_tbl.find(account.value);
26 if (tot_itr == totals_tbl.end()) {
27 tot_itr = totals_tbl.emplace(payer, [&](
auto& tot) {
29 tot.net_weight =
asset{ net_delta, core_symbol };
30 tot.cpu_weight =
asset{ cpu_delta, core_symbol };
33 totals_tbl.modify(tot_itr, same_payer, [&](
auto& tot) {
34 tot.net_weight.amount += net_delta;
35 tot.cpu_weight.amount += cpu_delta;
38 check(0 <= tot_itr->net_weight.amount,
"insufficient staked total net bandwidth");
39 check(0 <= tot_itr->cpu_weight.amount,
"insufficient staked total cpu bandwidth");
42 bool ram_managed =
false;
43 bool net_managed =
false;
44 bool cpu_managed =
false;
46 auto voter_itr = _voters.find(account.value);
47 if (voter_itr != _voters.end()) {
53 if (must_not_be_managed)
54 sysio::check(!net_managed && !cpu_managed,
"something is managed which shouldn't be");
56 if (!(net_managed && cpu_managed)) {
58 get_resource_limits(account, ram_bytes, net, cpu);
60 account, ram_managed ? ram_bytes : std::max(tot_itr->ram_bytes + ram_gift_bytes, ram_bytes),
61 net_managed ? net : tot_itr->net_weight.amount, cpu_managed ? cpu : tot_itr->cpu_weight.amount);
65 if (tot_itr->is_empty()) {
66 totals_tbl.erase(tot_itr);
70void system_contract::process_powerup_queue(time_point_sec now, symbol core_symbol, powerup_state&
state,
75 auto idx = orders.get_index<
"byexpires"_n>();
77 auto it = idx.begin();
78 if (it == idx.end() || it->expires > now)
80 net_delta_available += it->net_weight;
81 cpu_delta_available += it->cpu_weight;
82 adjust_resources(get_self(), it->owner, core_symbol, -it->net_weight, -it->cpu_weight);
85 state.net.utilization -= net_delta_available;
86 state.cpu.utilization -= cpu_delta_available;
101 delta_available += new_weight - res.
weight;
113 delta = std::clamp( delta, 0ll,
diff);
120 require_auth(get_self());
124 auto state = state_sing.get_or_default();
126 sysio::check(sysio::is_account(
reserve_account),
"sysio.reserv account must first be created");
128 int64_t net_delta_available = 0;
129 int64_t cpu_delta_available = 0;
130 if (state_sing.exists()) {
136 state.net.utilization_timestamp = now;
137 state.cpu.utilization_timestamp = now;
140 auto is_default_asset = [](
const sysio::asset&
a ) ->
bool {
141 return a.amount == 0 &&
a.symbol ==
symbol{};
144 auto update = [&](
auto&
state,
auto& args) {
145 if (!args.current_weight_ratio) {
146 if (
state.weight_ratio) {
147 *args.current_weight_ratio =
state.weight_ratio;
149 *args.current_weight_ratio =
state.initial_weight_ratio;
153 if (!args.target_weight_ratio) {
154 *args.target_weight_ratio =
state.target_weight_ratio;
157 if (!args.assumed_stake_weight) {
158 sysio::check(
state.assumed_stake_weight != 0,
"assumed_stake_weight does not have a default value");
159 *args.assumed_stake_weight =
state.assumed_stake_weight;
162 if (*args.current_weight_ratio == *args.target_weight_ratio) {
163 *args.target_timestamp = now;
165 if (!args.target_timestamp) {
166 sysio::check(
state.target_timestamp.utc_seconds != 0,
"target_timestamp does not have a default value");
167 *args.target_timestamp =
state.target_timestamp;
169 sysio::check(*args.target_timestamp > now,
"target_timestamp must be in the future");
172 if (!args.exponent) {
173 *args.exponent =
state.exponent;
176 if (!args.decay_secs) {
177 *args.decay_secs =
state.decay_secs;
180 if (!args.max_price) {
181 sysio::check(!is_default_asset(
state.max_price),
"max_price does not have a default value");
182 *args.max_price =
state.max_price;
185 if (!args.min_price) {
186 if (is_default_asset(
state.min_price)) {
187 *args.min_price = *args.max_price;
188 args.min_price->amount = 0;
190 *args.min_price =
state.min_price;
194 sysio::check(*args.current_weight_ratio > 0,
"current_weight_ratio is too small");
195 sysio::check(*args.current_weight_ratio <=
powerup_frac,
"current_weight_ratio is too large");
196 sysio::check(*args.target_weight_ratio > 0,
"target_weight_ratio is too small");
197 sysio::check(*args.target_weight_ratio <= *args.current_weight_ratio,
"weight can't grow over time");
198 sysio::check(*args.assumed_stake_weight >= 1,
199 "assumed_stake_weight must be at least 1; a much larger value is recommended");
200 sysio::check(*args.assumed_stake_weight * int128_t(
powerup_frac) / *args.target_weight_ratio <=
201 std::numeric_limits<int64_t>::max(),
202 "assumed_stake_weight/target_weight_ratio is too large");
203 sysio::check(*args.exponent >= 1.0,
"exponent must be >= 1");
204 sysio::check(*args.decay_secs >= 1,
"decay_secs must be >= 1");
205 sysio::check(args.max_price->symbol == core_symbol,
"max_price doesn't match core symbol");
206 sysio::check(args.max_price->amount > 0,
"max_price must be positive");
207 sysio::check(args.min_price->symbol == core_symbol,
"min_price doesn't match core symbol");
208 sysio::check(args.min_price->amount >= 0,
"min_price must be non-negative");
209 sysio::check(args.min_price->amount <= args.max_price->amount,
"min_price cannot exceed max_price");
210 if (*args.exponent == 1.0) {
211 sysio::check(args.min_price->amount == args.max_price->amount,
"min_price and max_price must be the same if the exponent is 1");
214 state.assumed_stake_weight = *args.assumed_stake_weight;
215 state.initial_weight_ratio = *args.current_weight_ratio;
216 state.target_weight_ratio = *args.target_weight_ratio;
217 state.initial_timestamp = now;
218 state.target_timestamp = *args.target_timestamp;
219 state.exponent = *args.exponent;
220 state.decay_secs = *args.decay_secs;
221 state.min_price = *args.min_price;
222 state.max_price = *args.max_price;
230 sysio::check(!is_default_asset(
state.min_powerup_fee),
"min_powerup_fee does not have a default value");
234 sysio::check(*args.
powerup_days > 0,
"powerup_days must be > 0");
235 sysio::check(args.
min_powerup_fee->symbol == core_symbol,
"min_powerup_fee doesn't match core symbol");
236 sysio::check(args.
min_powerup_fee->amount > 0,
"min_powerup_fee must be positive");
246 sysio::check(
state.net.weight >=
state.net.utilization,
"weight can't shrink below utilization");
247 sysio::check(
state.cpu.weight >=
state.cpu.utilization,
"weight can't shrink below utilization");
248 state.net.adjusted_utilization = std::min(
state.net.adjusted_utilization,
state.net.weight);
249 state.cpu.adjusted_utilization = std::min(
state.cpu.adjusted_utilization,
state.cpu.weight);
251 adjust_resources(get_self(),
reserve_account, core_symbol, net_delta_available, cpu_delta_available,
true);
252 state_sing.set(
state, get_self());
263 if( utilization_increase <= 0 )
return 0;
274 auto price_integral_delta = [&
state](
int64_t start_utilization,
int64_t end_utilization) ->
double {
275 double coefficient = (
state.max_price.amount -
state.min_price.amount) /
state.exponent;
276 double start_u = double(start_utilization) /
state.weight;
277 double end_u = double(end_utilization) /
state.weight;
278 return state.min_price.amount * end_u -
state.min_price.amount * start_u +
279 coefficient * std::pow(end_u,
state.exponent) - coefficient * std::pow(start_u,
state.exponent);
284 auto price_function = [&
state](
int64_t utilization) ->
double {
285 double price =
state.min_price.amount;
290 double new_exponent =
state.exponent - 1.0;
291 if (new_exponent <= 0.0) {
292 return state.max_price.amount;
294 price += (
state.max_price.amount -
state.min_price.amount) * std::pow(
double(utilization) /
state.weight, new_exponent);
302 int64_t end_utilization = start_utilization + utilization_increase;
304 if (start_utilization <
state.adjusted_utilization) {
305 fee += price_function(
state.adjusted_utilization) *
306 std::min(utilization_increase,
state.adjusted_utilization - start_utilization) /
state.weight;
307 start_utilization =
state.adjusted_utilization;
310 if (start_utilization < end_utilization) {
311 fee += price_integral_delta(start_utilization, end_utilization);
314 return std::ceil(fee);
321 sysio::check(state_sing.exists(),
"powerup hasn't been initialized");
322 auto state = state_sing.get();
326 int64_t net_delta_available = 0;
327 int64_t cpu_delta_available = 0;
328 process_powerup_queue(now, core_symbol,
state, orders, max, net_delta_available, cpu_delta_available);
330 adjust_resources(get_self(),
reserve_account, core_symbol, net_delta_available, cpu_delta_available,
true);
331 state_sing.set(
state, get_self());
335 const asset& max_payment) {
339 sysio::check(state_sing.exists(),
"powerup hasn't been initialized");
340 auto state = state_sing.get();
343 sysio::check(max_payment.symbol == core_symbol,
"max_payment doesn't match core symbol");
344 sysio::check(days ==
state.powerup_days,
"days doesn't match configuration");
345 sysio::check(net_frac >= 0,
"net_frac can't be negative");
346 sysio::check(cpu_frac >= 0,
"cpu_frac can't be negative");
347 sysio::check(net_frac <=
powerup_frac,
"net can't be more than 100%");
348 sysio::check(cpu_frac <=
powerup_frac,
"cpu can't be more than 100%");
350 int64_t net_delta_available = 0;
351 int64_t cpu_delta_available = 0;
352 process_powerup_queue(now, core_symbol,
state, orders, 2, net_delta_available, cpu_delta_available);
359 sysio::check(
state.weight,
"market doesn't have resources available");
360 sysio::check(
state.utilization + amount <=
state.weight,
"market doesn't have enough resources available");
362 sysio::check(
f > 0,
"calculated fee is below minimum; try powering up with more resources");
364 state.utilization += amount;
369 process(net_frac, net_amount,
state.net);
370 process(cpu_frac, cpu_amount,
state.cpu);
371 if (fee > max_payment) {
372 std::string error_msg =
"max_payment is less than calculated fee: ";
373 error_msg += fee.to_string();
374 sysio::check(
false, error_msg);
376 sysio::check(fee >=
state.min_powerup_fee,
"calculated fee is below minimum; try powering up with more resources");
378 orders.emplace(payer, [&](
auto& order) {
379 order.id = orders.available_primary_key();
380 order.owner = receiver;
381 order.net_weight = net_amount;
382 order.cpu_weight = cpu_amount;
385 net_delta_available -= net_amount;
386 cpu_delta_available -= cpu_amount;
388 adjust_resources(payer, receiver, core_symbol, net_amount, cpu_amount,
true);
389 adjust_resources(get_self(),
reserve_account, core_symbol, net_delta_available, cpu_delta_available,
true);
390 channel_to_rex(payer, fee,
true);
391 state_sing.set(
state, get_self());
395 powupresult_act.send( fee, net_amount, cpu_amount );
action_wrapper<"powupresult"_n, &powup_results::powupresult > powupresult_action
void powerupexec(const name &user, uint16_t max)
void powerup(const name &payer, const name &receiver, uint32_t days, int64_t net_frac, int64_t cpu_frac, const asset &max_payment)
static symbol get_core_symbol(name system_account="sysio"_n)
void cfgpowerup(powerup_config &args)
static constexpr sysio::name reserve_account
void diff(const std::string &a, const std::string &b)
constexpr microseconds days(int64_t d)
void update_utilization(time_point_sec now, powerup_state_resource &res)
constexpr int64_t powerup_frac
sysio::singleton<"powup.state"_n, powerup_state > powerup_state_singleton
void update_weight(time_point_sec now, powerup_state_resource &res, int64_t &delta_available)
sysio::multi_index< "userres"_n, user_resources > user_resources_table
int64_t calc_powerup_fee(const powerup_state_resource &state, int64_t utilization_increase)
sysio::multi_index< "powup.order"_n, powerup_order, indexed_by<"byowner"_n, const_mem_fun< powerup_order, uint64_t, &powerup_order::by_owner > >, indexed_by<"byexpires"_n, const_mem_fun< powerup_order, uint64_t, &powerup_order::by_expires > > > powerup_order_table
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Immutable except for fc::from_variant.
powerup_config_resource cpu
powerup_config_resource net
std::optional< asset > min_powerup_fee
std::optional< uint32_t > powerup_days
int64_t initial_weight_ratio
int64_t adjusted_utilization
time_point_sec initial_timestamp
time_point_sec utilization_timestamp
time_point_sec target_timestamp
int64_t target_weight_ratio
int64_t assumed_stake_weight