Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
delegate_bandwidth.cpp
Go to the documentation of this file.
1#include <sysio/datastream.hpp>
2#include <sysio/sysio.hpp>
3#include <sysio/multi_index.hpp>
4#include <sysio/privileged.hpp>
5#include <sysio/serialize.hpp>
6#include <sysio/transaction.hpp>
7
10
11namespace sysiosystem {
12
13 using sysio::asset;
14 using sysio::const_mem_fun;
15 using sysio::current_time_point;
16 using sysio::indexed_by;
18 using sysio::seconds;
20 using sysio::token;
21
25 void system_contract::buyrambytes( const name& payer, const name& receiver, uint32_t bytes ) {
26 auto itr = _rammarket.find(ramcore_symbol.raw());
27 const int64_t ram_reserve = itr->base.balance.amount;
28 const int64_t eos_reserve = itr->quote.balance.amount;
29 const int64_t cost = exchange_state::get_bancor_input( ram_reserve, eos_reserve, bytes );
30 const int64_t cost_plus_fee = cost / double(0.995);
31 buyram( payer, receiver, asset{ cost_plus_fee, core_symbol() } );
32 }
33
34
43 void system_contract::buyram( const name& payer, const name& receiver, const asset& quant )
44 {
45 require_auth( payer );
46 update_ram_supply();
47
48 check( quant.symbol == core_symbol(), "must buy ram with core token" );
49 check( quant.amount > 0, "must purchase a positive amount" );
50
51 auto fee = quant;
52 fee.amount = ( fee.amount + 199 ) / 200;
53 // fee.amount cannot be 0 since that is only possible if quant.amount is 0 which is not allowed by the assert above.
54 // If quant.amount == 1, then fee.amount == 1,
55 // otherwise if quant.amount > 1, then 0 < fee.amount < quant.amount.
56 auto quant_after_fee = quant;
57 quant_after_fee.amount -= fee.amount;
58 // quant_after_fee.amount should be > 0 if quant.amount > 1.
59 // If quant.amount == 1, then quant_after_fee.amount == 0 and the next inline transfer will fail causing the buyram action to fail.
60 {
62 transfer_act.send( payer, ram_account, quant_after_fee, "buy ram" );
63 }
64 if ( fee.amount > 0 ) {
65 token::transfer_action transfer_act{ token_account, { {payer, active_permission} } };
66 transfer_act.send( payer, ramfee_account, fee, "ram fee" );
67 channel_to_rex( ramfee_account, fee );
68 }
69
70 int64_t bytes_out;
71
72 const auto& market = _rammarket.get(ramcore_symbol.raw(), "ram market does not exist");
73 _rammarket.modify( market, same_payer, [&]( auto& es ) {
74 bytes_out = es.direct_convert( quant_after_fee, ram_symbol ).amount;
75 });
76
77 check( bytes_out > 0, "must reserve a positive amount" );
78
79 _gstate.total_ram_bytes_reserved += uint64_t(bytes_out);
80 _gstate.total_ram_stake += quant_after_fee.amount;
81
82 user_resources_table userres( get_self(), receiver.value );
83 auto res_itr = userres.find( receiver.value );
84 if( res_itr == userres.end() ) {
85 res_itr = userres.emplace( receiver, [&]( auto& res ) {
86 res.owner = receiver;
87 res.net_weight = asset( 0, core_symbol() );
88 res.cpu_weight = asset( 0, core_symbol() );
89 res.ram_bytes = bytes_out;
90 });
91 } else {
92 userres.modify( res_itr, receiver, [&]( auto& res ) {
93 res.ram_bytes += bytes_out;
94 });
95 }
96
97 auto voter_itr = _voters.find( res_itr->owner.value );
98 if( voter_itr == _voters.end() || !has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ) ) {
99 int64_t ram_bytes, net, cpu;
100 get_resource_limits( res_itr->owner, ram_bytes, net, cpu );
101 set_resource_limits( res_itr->owner, res_itr->ram_bytes + ram_gift_bytes, net, cpu );
102 }
103 }
104
111 void system_contract::sellram( const name& account, int64_t bytes ) {
112 require_auth( account );
113 update_ram_supply();
114
115 check( bytes > 0, "cannot sell negative byte" );
116
117 user_resources_table userres( get_self(), account.value );
118 auto res_itr = userres.find( account.value );
119 check( res_itr != userres.end(), "no resource row" );
120 check( res_itr->ram_bytes >= bytes, "insufficient quota" );
121
122 asset tokens_out;
123 auto itr = _rammarket.find(ramcore_symbol.raw());
124 _rammarket.modify( itr, same_payer, [&]( auto& es ) {
126 tokens_out = es.direct_convert( asset(bytes, ram_symbol), core_symbol());
127 });
128
129 check( tokens_out.amount > 1, "token amount received from selling ram is too low" );
130
131 _gstate.total_ram_bytes_reserved -= static_cast<decltype(_gstate.total_ram_bytes_reserved)>(bytes); // bytes > 0 is asserted above
132 _gstate.total_ram_stake -= tokens_out.amount;
133
135 check( _gstate.total_ram_stake >= 0, "error, attempt to unstake more tokens than previously staked" );
136
137 userres.modify( res_itr, account, [&]( auto& res ) {
138 res.ram_bytes -= bytes;
139 });
140
141 auto voter_itr = _voters.find( res_itr->owner.value );
142 if( voter_itr == _voters.end() || !has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ) ) {
143 int64_t ram_bytes, net, cpu;
144 get_resource_limits( res_itr->owner, ram_bytes, net, cpu );
145 set_resource_limits( res_itr->owner, res_itr->ram_bytes + ram_gift_bytes, net, cpu );
146 }
147
148 {
150 transfer_act.send( ram_account, account, asset(tokens_out), "sell ram" );
151 }
152 auto fee = ( tokens_out.amount + 199 ) / 200;
153 // since tokens_out.amount was asserted to be at least 2 earlier, fee.amount < tokens_out.amount
154 if ( fee > 0 ) {
155 token::transfer_action transfer_act{ token_account, { {account, active_permission} } };
156 transfer_act.send( account, ramfee_account, asset(fee, core_symbol()), "sell ram fee" );
157 channel_to_rex( ramfee_account, asset(fee, core_symbol() ));
158 }
159 }
160
162 const int64_t base_time = 1527811200;
163 const int64_t current_time = 1638921540;
164 const int64_t max_claimable = 100'000'000'0000ll;
165 const int64_t claimable = int64_t(max_claimable * double(current_time - base_time) / (10*seconds_per_year) );
166
167 check( max_claimable - claimable <= stake, "b1 can only claim their tokens over 10 years" );
168 }
169
170 void system_contract::changebw( name from, const name& receiver,
171 const asset& stake_net_delta, const asset& stake_cpu_delta, bool transfer )
172 {
173 require_auth( from );
174 check( stake_net_delta.amount != 0 || stake_cpu_delta.amount != 0, "should stake non-zero amount" );
175 check( std::abs( (stake_net_delta + stake_cpu_delta).amount )
176 >= std::max( std::abs( stake_net_delta.amount ), std::abs( stake_cpu_delta.amount ) ),
177 "net and cpu deltas cannot be opposite signs" );
178
179 name source_stake_from = from;
180 if ( transfer ) {
181 from = receiver;
182 }
183
184 // update stake delegated from "from" to "receiver"
185 {
186 del_bandwidth_table del_tbl( get_self(), from.value );
187 auto itr = del_tbl.find( receiver.value );
188 if( itr == del_tbl.end() ) {
189 itr = del_tbl.emplace( from, [&]( auto& dbo ){
190 dbo.from = from;
191 dbo.to = receiver;
192 dbo.net_weight = stake_net_delta;
193 dbo.cpu_weight = stake_cpu_delta;
194 });
195 }
196 else {
197 del_tbl.modify( itr, same_payer, [&]( auto& dbo ){
198 dbo.net_weight += stake_net_delta;
199 dbo.cpu_weight += stake_cpu_delta;
200 });
201 }
202 check( 0 <= itr->net_weight.amount, "insufficient staked net bandwidth" );
203 check( 0 <= itr->cpu_weight.amount, "insufficient staked cpu bandwidth" );
204 if ( itr->is_empty() ) {
205 del_tbl.erase( itr );
206 }
207 } // itr can be invalid, should go out of scope
208
209 // update totals of "receiver"
210 {
211 user_resources_table totals_tbl( get_self(), receiver.value );
212 auto tot_itr = totals_tbl.find( receiver.value );
213 if( tot_itr == totals_tbl.end() ) {
214 tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) {
215 tot.owner = receiver;
216 tot.net_weight = stake_net_delta;
217 tot.cpu_weight = stake_cpu_delta;
218 });
219 } else {
220 totals_tbl.modify( tot_itr, from == receiver ? from : same_payer, [&]( auto& tot ) {
221 tot.net_weight += stake_net_delta;
222 tot.cpu_weight += stake_cpu_delta;
223 });
224 }
225 check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" );
226 check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" );
227
228 {
229 bool ram_managed = false;
230 bool net_managed = false;
231 bool cpu_managed = false;
232
233 auto voter_itr = _voters.find( receiver.value );
234 if( voter_itr != _voters.end() ) {
235 ram_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed );
236 net_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::net_managed );
237 cpu_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::cpu_managed );
238 }
239
240 if( !(net_managed && cpu_managed) ) {
241 int64_t ram_bytes, net, cpu;
242 get_resource_limits( receiver, ram_bytes, net, cpu );
243
244 set_resource_limits( receiver,
245 ram_managed ? ram_bytes : std::max( tot_itr->ram_bytes + ram_gift_bytes, ram_bytes ),
246 net_managed ? net : tot_itr->net_weight.amount,
247 cpu_managed ? cpu : tot_itr->cpu_weight.amount );
248 }
249 }
250
251 if ( tot_itr->is_empty() ) {
252 totals_tbl.erase( tot_itr );
253 }
254 } // tot_itr can be invalid, should go out of scope
255
256 // create refund or update from existing refund
257 if ( stake_account != source_stake_from ) { //for sysio both transfer and refund make no sense
258 refunds_table refunds_tbl( get_self(), from.value );
259 auto req = refunds_tbl.find( from.value );
260
261 //create/update/delete refund
262 auto net_balance = stake_net_delta;
263 auto cpu_balance = stake_cpu_delta;
264 bool need_deferred_trx = false;
265
266
267 // net and cpu are same sign by assertions in delegatebw and undelegatebw
268 // redundant assertion also at start of changebw to protect against misuse of changebw
269 bool is_undelegating = (net_balance.amount + cpu_balance.amount ) < 0;
270 bool is_delegating_to_self = (!transfer && from == receiver);
271
272 if( is_delegating_to_self || is_undelegating ) {
273 if ( req != refunds_tbl.end() ) { //need to update refund
274 refunds_tbl.modify( req, same_payer, [&]( refund_request& r ) {
275 if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) {
276 r.request_time = current_time_point();
277 }
278 r.net_amount -= net_balance;
279 if ( r.net_amount.amount < 0 ) {
280 net_balance = -r.net_amount;
281 r.net_amount.amount = 0;
282 } else {
283 net_balance.amount = 0;
284 }
285 r.cpu_amount -= cpu_balance;
286 if ( r.cpu_amount.amount < 0 ){
287 cpu_balance = -r.cpu_amount;
288 r.cpu_amount.amount = 0;
289 } else {
290 cpu_balance.amount = 0;
291 }
292 });
293
294 check( 0 <= req->net_amount.amount, "negative net refund amount" ); //should never happen
295 check( 0 <= req->cpu_amount.amount, "negative cpu refund amount" ); //should never happen
296
297 if ( req->is_empty() ) {
298 refunds_tbl.erase( req );
299 need_deferred_trx = false;
300 } else {
301 need_deferred_trx = true;
302 }
303 } else if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) { //need to create refund
304 refunds_tbl.emplace( from, [&]( refund_request& r ) {
305 r.owner = from;
306 if ( net_balance.amount < 0 ) {
307 r.net_amount = -net_balance;
308 net_balance.amount = 0;
309 } else {
310 r.net_amount = asset( 0, core_symbol() );
311 }
312 if ( cpu_balance.amount < 0 ) {
313 r.cpu_amount = -cpu_balance;
314 cpu_balance.amount = 0;
315 } else {
316 r.cpu_amount = asset( 0, core_symbol() );
317 }
318 r.request_time = current_time_point();
319 });
320 need_deferred_trx = true;
321 } // else stake increase requested with no existing row in refunds_tbl -> nothing to do with refunds_tbl
322 }
323
324 if ( need_deferred_trx ) {
326 out.actions.emplace_back( permission_level{from, active_permission},
327 get_self(), "refund"_n,
328 from
329 );
330 out.delay_sec = refund_delay_sec;
331 sysio::cancel_deferred( from.value ); // TODO: Remove this line when replacing deferred transactions is fixed
332 out.send( from.value, from, true );
333 } else {
334 sysio::cancel_deferred( from.value );
335 }
336
337 auto transfer_amount = net_balance + cpu_balance;
338 if ( 0 < transfer_amount.amount ) {
339 token::transfer_action transfer_act{ token_account, { {source_stake_from, active_permission} } };
340 transfer_act.send( source_stake_from, stake_account, asset(transfer_amount), "stake bandwidth" );
341 }
342 }
343
344 vote_stake_updater( from );
345 update_voting_power( from, stake_net_delta + stake_cpu_delta );
346 }
347
348 void system_contract::update_voting_power( const name& voter, const asset& total_update )
349 {
350 auto voter_itr = _voters.find( voter.value );
351 if( voter_itr == _voters.end() ) {
352 voter_itr = _voters.emplace( voter, [&]( auto& v ) {
353 v.owner = voter;
354 v.staked = total_update.amount;
355 });
356 } else {
357 _voters.modify( voter_itr, same_payer, [&]( auto& v ) {
358 v.staked += total_update.amount;
359 });
360 }
361
362 check( 0 <= voter_itr->staked, "stake for voting cannot be negative" );
363
364 if( voter == "b1"_n ) {
365 validate_b1_vesting( voter_itr->staked );
366 }
367
368 if( voter_itr->producers.size() || voter_itr->proxy ) {
369 update_votes( voter, voter_itr->proxy, voter_itr->producers, false );
370 }
371 }
372
373 void system_contract::delegatebw( const name& from, const name& receiver,
374 const asset& stake_net_quantity,
375 const asset& stake_cpu_quantity, bool transfer )
376 {
377 asset zero_asset( 0, core_symbol() );
378 check( stake_cpu_quantity >= zero_asset, "must stake a positive amount" );
379 check( stake_net_quantity >= zero_asset, "must stake a positive amount" );
380 check( stake_net_quantity.amount + stake_cpu_quantity.amount > 0, "must stake a positive amount" );
381 check( !transfer || from != receiver, "cannot use transfer flag if delegating to self" );
382
383 changebw( from, receiver, stake_net_quantity, stake_cpu_quantity, transfer);
384 } // delegatebw
385
386 void system_contract::undelegatebw( const name& from, const name& receiver,
387 const asset& unstake_net_quantity, const asset& unstake_cpu_quantity )
388 {
389 asset zero_asset( 0, core_symbol() );
390 check( unstake_cpu_quantity >= zero_asset, "must unstake a positive amount" );
391 check( unstake_net_quantity >= zero_asset, "must unstake a positive amount" );
392 check( unstake_cpu_quantity.amount + unstake_net_quantity.amount > 0, "must unstake a positive amount" );
393 check( _gstate.thresh_activated_stake_time != time_point(),
394 "cannot undelegate bandwidth until the chain is activated (at least 15% of all tokens participate in voting)" );
395
396 changebw( from, receiver, -unstake_net_quantity, -unstake_cpu_quantity, false);
397 } // undelegatebw
398
399
400 void system_contract::refund( const name& owner ) {
401 require_auth( owner );
402
403 refunds_table refunds_tbl( get_self(), owner.value );
404 auto req = refunds_tbl.find( owner.value );
405 check( req != refunds_tbl.end(), "refund request not found" );
406 check( req->request_time + seconds(refund_delay_sec) <= current_time_point(),
407 "refund is not available yet" );
409 transfer_act.send( stake_account, req->owner, req->net_amount + req->cpu_amount, "unstake" );
410 refunds_tbl.erase( req );
411 }
412
413
414} //namespace sysiosystem
const mie::Vuint & r
Definition bn.cpp:28
std::string name
sysio::action_wrapper<"transfer"_n, &token::transfer > transfer_action
static constexpr sysio::name ram_account
static constexpr symbol ram_symbol
void buyram(const name &payer, const name &receiver, const asset &quant)
void buyrambytes(const name &payer, const name &receiver, uint32_t bytes)
static constexpr sysio::name stake_account
static constexpr sysio::name token_account
static constexpr sysio::name active_permission
void undelegatebw(const name &from, const name &receiver, const asset &unstake_net_quantity, const asset &unstake_cpu_quantity)
static constexpr symbol ramcore_symbol
static constexpr sysio::name ramfee_account
void sellram(const name &account, int64_t bytes)
void delegatebw(const name &from, const name &receiver, const asset &stake_net_quantity, const asset &stake_cpu_quantity, bool transfer)
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
sysio::multi_index< "delband"_n, delegated_bandwidth > del_bandwidth_table
sysio::multi_index< "refunds"_n, refund_request > refunds_table
void validate_b1_vesting(int64_t stake)
sysio::multi_index< "userres"_n, user_resources > user_resources_table
signed __int64 int64_t
Definition stdint.h:135
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136
Immutable except for fc::from_variant.
Definition name.hpp:43
vector< action > actions
static int64_t get_bancor_input(int64_t out_reserve, int64_t inp_reserve, int64_t out)