delegate_bandwith.hpp 10.5 KB
Newer Older
1 2 3 4 5
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once
6
#include "common.hpp"
7 8 9 10 11 12 13 14 15 16 17 18 19 20

#include <eosiolib/eosio.hpp>
#include <eosiolib/token.hpp>
#include <eosiolib/print.hpp>

#include <eosiolib/generic_currency.hpp>
#include <eosiolib/datastream.hpp>
#include <eosiolib/serialize.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/privileged.h>

#include <map>

namespace eosiosystem {
21
   using eosio::asset;
22 23 24 25 26 27 28 29 30 31
   using eosio::indexed_by;
   using eosio::const_mem_fun;
   using eosio::bytes;
   using eosio::print;
   using std::map;
   using std::pair;

   template<account_name SystemAccount>
   class delegate_bandwith {
      public:
32 33 34 35
         static constexpr account_name system_account = SystemAccount;
         using currency = typename common<SystemAccount>::currency;
         using system_token_type = typename common<SystemAccount>::system_token_type;
         using eosio_parameters = typename common<SystemAccount>::eosio_parameters;
A
Anton Perkov 已提交
36
         using global_state_singleton = typename common<SystemAccount>::global_state_singleton;
37 38 39

         struct total_resources {
            account_name owner;
40 41 42 43
            typename currency::token_type net_weight;
            typename currency::token_type cpu_weight;
            typename currency::token_type storage_stake;
            uint64_t storage_bytes = 0;
44 45 46

            uint64_t primary_key()const { return owner; }

47
            EOSLIB_SERIALIZE( total_resources, (owner)(net_weight)(cpu_weight)(storage_stake)(storage_bytes) )
48 49 50 51 52 53 54 55 56 57
         };


         /**
          *  Every user 'from' has a scope/table that uses every receipient 'to' as the primary key.
          */
         struct delegated_bandwidth {
            account_name from;
            account_name to;
            typename currency::token_type net_weight; 
58 59 60
            typename currency::token_type cpu_weight;
            typename currency::token_type storage_stake;
            uint64_t storage_bytes = 0;
61
            /*
62 63 64 65 66 67 68
            uint32_t start_pending_net_withdraw = 0;
            typename currency::token_type pending_net_withdraw;
            uint64_t deferred_net_withdraw_handler = 0;

            uint32_t start_pending_cpu_withdraw = 0;
            typename currency::token_type pending_cpu_withdraw;
            uint64_t deferred_cpu_withdraw_handler = 0;
69
            */
70 71 72
            
            uint64_t  primary_key()const { return to; }

73
            EOSLIB_SERIALIZE( delegated_bandwidth, (from)(to)(net_weight)(cpu_weight)(storage_stake)(storage_bytes)
74 75
                              /* (start_pending_net_withdraw)(pending_net_withdraw)(deferred_net_withdraw_handler)
                                 (start_pending_cpu_withdraw)(pending_cpu_withdraw)(deferred_cpu_withdraw_handler)*/ )
76 77 78 79 80 81 82

         };

         typedef eosio::multi_index< N(totalband), total_resources>  total_resources_index_type;
         typedef eosio::multi_index< N(delband), delegated_bandwidth> del_bandwidth_index_type;

         ACTION( SystemAccount, delegatebw ) {
83 84 85 86 87
            account_name from;
            account_name receiver;
            asset        stake_net_quantity;
            asset        stake_cpu_quantity;
            asset        stake_storage_quantity;
88 89


90
            EOSLIB_SERIALIZE( delegatebw, (from)(receiver)(stake_net_quantity)(stake_cpu_quantity)(stake_storage_quantity) )
91 92 93
         };

         ACTION( SystemAccount, undelegatebw ) {
94 95 96 97 98
            account_name from;
            account_name receiver;
            asset        unstake_net_quantity;
            asset        unstake_cpu_quantity;
            uint64_t     unstake_storage_bytes;
99

100
            EOSLIB_SERIALIZE( undelegatebw, (from)(receiver)(unstake_net_quantity)(unstake_cpu_quantity)(unstake_storage_bytes) )
101 102 103
         };

         static void on( const delegatebw& del ) {
104 105 106
            eosio_assert( del.stake_cpu_quantity.amount >= 0, "must stake a positive amount" );
            eosio_assert( del.stake_net_quantity.amount >= 0, "must stake a positive amount" );
            eosio_assert( del.stake_storage_quantity.amount >= 0, "must stake a positive amount" );
107

108 109 110
            system_token_type total_stake = system_token_type(del.stake_cpu_quantity)
               + system_token_type(del.stake_net_quantity) + system_token_type(del.stake_storage_quantity);
            eosio_assert( total_stake.quantity > 0, "must stake a positive amount" );
111 112 113 114 115

            require_auth( del.from );

            //eosio_assert( is_account( del.receiver ), "can only delegate resources to an existing account" );

A
Anton Perkov 已提交
116
            auto parameters = global_state_singleton::exists() ? global_state_singleton::get()
117
               : common<SystemAccount>::get_default_parameters();
118 119 120 121
            auto token_supply = currency::get_total_supply();//.quantity;

            //make sure that there is no posibility of overflow here
            uint64_t storage_bytes_estimated = ( parameters.max_storage_size - parameters.total_storage_bytes_reserved )
122
               * parameters.storage_reserve_ratio * system_token_type(del.stake_cpu_quantity)
123 124 125
               / ( token_supply - parameters.total_storage_stake ) / 1000 /* reserve ratio coefficient */;

            uint64_t storage_bytes = ( parameters.max_storage_size - parameters.total_storage_bytes_reserved - storage_bytes_estimated )
126
               * parameters.storage_reserve_ratio * system_token_type(del.stake_cpu_quantity)
127 128
               / ( token_supply - del.stake_storage_quantity - parameters.total_storage_stake ) / 1000 /* reserve ratio coefficient */;

129
            eosio_assert( 0 < storage_bytes, "stake is too small to increase storage even by 1 byte" );
130

131
            del_bandwidth_index_type     del_index( SystemAccount, del.from );
A
Anton Perkov 已提交
132
            auto itr = del_index.find( del.receiver );
133
            if( itr == nullptr ) {
134
               del_index.emplace( del.from, [&]( auto& dbo ){
135 136 137 138 139 140
                  dbo.from          = del.from;
                  dbo.to            = del.receiver;
                  dbo.net_weight    = del.stake_net_quantity;
                  dbo.cpu_weight    = del.stake_cpu_quantity;
                  dbo.storage_stake = del.stake_storage_quantity;
                  dbo.storage_bytes = storage_bytes;
141 142 143 144
               });
            }
            else {
               del_index.update( *itr, del.from, [&]( auto& dbo ){
145 146 147 148
                  dbo.net_weight    += del.stake_net_quantity;
                  dbo.cpu_weight    += del.stake_cpu_quantity;
                  dbo.storage_stake += del.stake_storage_quantity;
                  dbo.storage_bytes += storage_bytes;
149 150 151
               });
            }

152
            total_resources_index_type   total_index( SystemAccount, del.receiver );
153 154 155 156
            auto tot_itr = total_index.find( del.receiver );
            if( tot_itr == nullptr ) {
               tot_itr = &total_index.emplace( del.from, [&]( auto& tot ) {
                  tot.owner = del.receiver;
157 158 159 160
                  tot.net_weight    = del.stake_net_quantity;
                  tot.cpu_weight    = del.stake_cpu_quantity;
                  tot.storage_stake = del.stake_storage_quantity;
                  tot.storage_bytes = storage_bytes;
161 162
               });
            } else {
163
               total_index.update( *tot_itr, del.from == del.receiver ? del.from : 0, [&]( auto& tot ) {
164 165 166 167
                  tot.net_weight    += del.stake_net_quantity;
                  tot.cpu_weight    += del.stake_cpu_quantity;
                  tot.storage_stake += del.stake_storage_quantity;
                  tot.storage_bytes += storage_bytes;
168 169 170
               });
            }

171
            set_resource_limits( tot_itr->owner, tot_itr->storage_bytes, tot_itr->net_weight.quantity, tot_itr->cpu_weight.quantity, 0 );
172 173

            currency::inline_transfer( del.from, SystemAccount, total_stake, "stake bandwidth" );
174 175 176

            parameters.total_storage_bytes_reserved += storage_bytes;
            parameters.total_storage_stake += del.stake_storage_quantity;
A
Anton Perkov 已提交
177
            global_state_singleton::set(parameters);
178 179 180
         } // delegatebw

         static void on( const undelegatebw& del ) {
181 182
            eosio_assert( del.unstake_cpu_quantity.amount >= 0, "must unstake a positive amount" );
            eosio_assert( del.unstake_net_quantity.amount >= 0, "must unstake a positive amount" );
183 184 185 186 187

            require_auth( del.from );

            //eosio_assert( is_account( del.receiver ), "can only delegate resources to an existing account" );

188
            del_bandwidth_index_type     del_index( SystemAccount, del.from );
A
Anton Perkov 已提交
189
            const auto& dbw = del_index.get( del.receiver );
190 191
            eosio_assert( dbw.net_weight >= del.unstake_net_quantity, "insufficient staked net bandwidth" );
            eosio_assert( dbw.cpu_weight >= del.unstake_cpu_quantity, "insufficient staked cpu bandwidth" );
192
            eosio_assert( dbw.storage_bytes >= del.unstake_storage_bytes, "insufficient staked storage" );
193

194 195 196
            system_token_type storage_stake_decrease = 0 < dbw.storage_bytes ?
               dbw.storage_stake * del.unstake_storage_bytes / dbw.storage_bytes
               : system_token_type(0);
197

198 199
            auto total_refund = system_token_type(del.unstake_cpu_quantity)
               + system_token_type(del.unstake_net_quantity) + storage_stake_decrease;
200

201
            eosio_assert( total_refund.quantity > 0, "must unstake a positive amount" );
202

203 204 205
            del_index.update( dbw, del.from, [&]( auto& dbo ){
               dbo.net_weight -= del.unstake_net_quantity;
               dbo.cpu_weight -= del.unstake_cpu_quantity;
206 207
               dbo.storage_stake -= storage_stake_decrease;
               dbo.storage_bytes -= del.unstake_storage_bytes;
208 209
            });

210 211
            total_resources_index_type   total_index( SystemAccount, del.receiver );
            const auto& totals = total_index.get( del.receiver );
212
            total_index.update( totals, 0, [&]( auto& tot ) {
213 214 215 216
               tot.net_weight -= del.unstake_net_quantity;
               tot.cpu_weight -= del.unstake_cpu_quantity;
               tot.storage_stake -= storage_stake_decrease;
               tot.storage_bytes -= del.unstake_storage_bytes;
217 218
            });

219
            set_resource_limits( totals.owner, totals.storage_bytes, totals.net_weight.quantity, totals.cpu_weight.quantity, 0 );
220 221

            /// TODO: implement / enforce time delays on withdrawing
A
Anton Perkov 已提交
222
            currency::inline_transfer( SystemAccount, del.from, asset( static_cast<int64_t>( total_refund.quantity )), "unstake bandwidth" );
223

A
Anton Perkov 已提交
224
            auto parameters = global_state_singleton::get();
225 226
            parameters.total_storage_bytes_reserved -= del.unstake_storage_bytes;
            parameters.total_storage_stake -= storage_stake_decrease;
A
Anton Perkov 已提交
227
            global_state_singleton::set( parameters );
228 229 230
         } // undelegatebw
   };
}