BitShares-Core  4.0.0
BitShares blockchain implementation and command-line interface software
account_object.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
26 
27 #include <fc/io/raw.hpp>
28 #include <fc/uint128.hpp>
29 
30 namespace graphene { namespace chain {
31 
33 {
34  if( a == 0 || p == 0 )
35  return 0;
36  if( p == GRAPHENE_100_PERCENT )
37  return a;
38 
39  fc::uint128_t r = a.value;
40  r *= p;
42  return static_cast<uint64_t>(r);
43 }
44 
46 {
47  assert(delta.asset_id == asset_type);
48  balance += delta.amount;
49  if( asset_type == asset_id_type() ) // CORE asset
50  maintenance_flag = true;
51 }
52 
54 {
55  if( pending_fees > 0 || pending_vested_fees > 0 )
56  {
57  auto pay_out_fees = [&](const account_object& account, share_type core_fee_total, bool require_vesting)
58  {
59  // Check the referrer -- if he's no longer a member, pay to the lifetime referrer instead.
60  // No need to check the registrar; registrars are required to be lifetime members.
61  if( account.referrer(d).is_basic_account(d.head_block_time()) )
62  d.modify( account, [](account_object& acc) {
63  acc.referrer = acc.lifetime_referrer;
64  });
65 
66  share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage);
67  assert( network_cut <= core_fee_total );
68 
69 #ifndef NDEBUG
70  const auto& props = d.get_global_properties();
71 
72  share_type reserveed = cut_fee(network_cut, props.parameters.reserve_percent_of_fee);
73  share_type accumulated = network_cut - reserveed;
74  assert( accumulated + reserveed == network_cut );
75 #endif
76  share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage);
77  share_type referral = core_fee_total - network_cut - lifetime_cut;
78 
79  d.modify( d.get_core_dynamic_data(), [network_cut](asset_dynamic_data_object& addo) {
80  addo.accumulated_fees += network_cut;
81  });
82 
83  // Potential optimization: Skip some of this math and object lookups by special casing on the account type.
84  // For example, if the account is a lifetime member, we can skip all this and just deposit the referral to
85  // it directly.
86  share_type referrer_cut = cut_fee(referral, account.referrer_rewards_percentage);
87  share_type registrar_cut = referral - referrer_cut;
88 
89  d.deposit_cashback(d.get(account.lifetime_referrer), lifetime_cut, require_vesting);
90  d.deposit_cashback(d.get(account.referrer), referrer_cut, require_vesting);
91  d.deposit_cashback(d.get(account.registrar), registrar_cut, require_vesting);
92 
93  assert( referrer_cut + registrar_cut + accumulated + reserveed + lifetime_cut == core_fee_total );
94  };
95 
96  pay_out_fees(a, pending_fees, true);
97  pay_out_fees(a, pending_vested_fees, false);
98 
99  d.modify(*this, [&](account_statistics_object& s) {
100  s.lifetime_fees_paid += pending_fees + pending_vested_fees;
101  s.pending_fees = 0;
102  s.pending_vested_fees = 0;
103  });
104  }
105 }
106 
107 void account_statistics_object::pay_fee( share_type core_fee, share_type cashback_vesting_threshold )
108 {
109  if( core_fee > cashback_vesting_threshold )
110  pending_fees += core_fee;
111  else
112  pending_vested_fees += core_fee;
113 }
114 
116 {
117  set<account_id_type> result;
118  for( auto auth : a.owner.account_auths )
119  result.insert(auth.first);
120  for( auto auth : a.active.account_auths )
121  result.insert(auth.first);
122  return result;
123 }
124 set<public_key_type, pubkey_comparator> account_member_index::get_key_members(const account_object& a)const
125 {
126  set<public_key_type, pubkey_comparator> result;
127  for( auto auth : a.owner.key_auths )
128  result.insert(auth.first);
129  for( auto auth : a.active.key_auths )
130  result.insert(auth.first);
131  result.insert( a.options.memo_key );
132  return result;
133 }
135 {
136  set<address> result;
137  for( auto auth : a.owner.address_auths )
138  result.insert(auth.first);
139  for( auto auth : a.active.address_auths )
140  result.insert(auth.first);
141  result.insert( a.options.memo_key );
142  return result;
143 }
144 
146 {
147  assert( dynamic_cast<const account_object*>(&obj) ); // for debug only
148  const account_object& a = static_cast<const account_object&>(obj);
149 
150  auto account_members = get_account_members(a);
151  for( auto item : account_members )
152  account_to_account_memberships[item].insert(obj.id);
153 
154  auto key_members = get_key_members(a);
155  for( auto item : key_members )
156  account_to_key_memberships[item].insert(obj.id);
157 
158  auto address_members = get_address_members(a);
159  for( auto item : address_members )
160  account_to_address_memberships[item].insert(obj.id);
161 }
162 
164 {
165  assert( dynamic_cast<const account_object*>(&obj) ); // for debug only
166  const account_object& a = static_cast<const account_object&>(obj);
167 
168  auto key_members = get_key_members(a);
169  for( auto item : key_members )
170  account_to_key_memberships[item].erase( obj.id );
171 
172  auto address_members = get_address_members(a);
173  for( auto item : address_members )
174  account_to_address_memberships[item].erase( obj.id );
175 
176  auto account_members = get_account_members(a);
177  for( auto item : account_members )
178  account_to_account_memberships[item].erase( obj.id );
179 }
180 
181 void account_member_index::about_to_modify(const object& before)
182 {
183  before_key_members.clear();
184  before_account_members.clear();
185  assert( dynamic_cast<const account_object*>(&before) ); // for debug only
186  const account_object& a = static_cast<const account_object&>(before);
187  before_key_members = get_key_members(a);
188  before_address_members = get_address_members(a);
189  before_account_members = get_account_members(a);
190 }
191 
192 void account_member_index::object_modified(const object& after)
193 {
194  assert( dynamic_cast<const account_object*>(&after) ); // for debug only
195  const account_object& a = static_cast<const account_object&>(after);
196 
197  {
198  set<account_id_type> after_account_members = get_account_members(a);
199  vector<account_id_type> removed; removed.reserve(before_account_members.size());
200  std::set_difference(before_account_members.begin(), before_account_members.end(),
201  after_account_members.begin(), after_account_members.end(),
202  std::inserter(removed, removed.end()));
203 
204  for( auto itr = removed.begin(); itr != removed.end(); ++itr )
205  account_to_account_memberships[*itr].erase(after.id);
206 
207  vector<object_id_type> added; added.reserve(after_account_members.size());
208  std::set_difference(after_account_members.begin(), after_account_members.end(),
209  before_account_members.begin(), before_account_members.end(),
210  std::inserter(added, added.end()));
211 
212  for( auto itr = added.begin(); itr != added.end(); ++itr )
213  account_to_account_memberships[*itr].insert(after.id);
214  }
215 
216 
217  {
218  set<public_key_type, pubkey_comparator> after_key_members = get_key_members(a);
219 
220  vector<public_key_type> removed; removed.reserve(before_key_members.size());
221  std::set_difference(before_key_members.begin(), before_key_members.end(),
222  after_key_members.begin(), after_key_members.end(),
223  std::inserter(removed, removed.end()));
224 
225  for( auto itr = removed.begin(); itr != removed.end(); ++itr )
226  account_to_key_memberships[*itr].erase(after.id);
227 
228  vector<public_key_type> added; added.reserve(after_key_members.size());
229  std::set_difference(after_key_members.begin(), after_key_members.end(),
230  before_key_members.begin(), before_key_members.end(),
231  std::inserter(added, added.end()));
232 
233  for( auto itr = added.begin(); itr != added.end(); ++itr )
234  account_to_key_memberships[*itr].insert(after.id);
235  }
236 
237  {
238  set<address> after_address_members = get_address_members(a);
239 
240  vector<address> removed; removed.reserve(before_address_members.size());
241  std::set_difference(before_address_members.begin(), before_address_members.end(),
242  after_address_members.begin(), after_address_members.end(),
243  std::inserter(removed, removed.end()));
244 
245  for( auto itr = removed.begin(); itr != removed.end(); ++itr )
246  account_to_address_memberships[*itr].erase(after.id);
247 
248  vector<address> added; added.reserve(after_address_members.size());
249  std::set_difference(after_address_members.begin(), after_address_members.end(),
250  before_address_members.begin(), before_address_members.end(),
251  std::inserter(added, added.end()));
252 
253  for( auto itr = added.begin(); itr != added.end(); ++itr )
254  account_to_address_memberships[*itr].insert(after.id);
255  }
256 
257 }
258 
259 const uint8_t balances_by_account_index::bits = 20;
260 const uint64_t balances_by_account_index::mask = (1ULL << balances_by_account_index::bits) - 1;
261 
263 {
264  const auto& abo = dynamic_cast< const account_balance_object& >( obj );
265  while( balances.size() < (abo.owner.instance.value >> bits) + 1 )
266  {
267  balances.reserve( (abo.owner.instance.value >> bits) + 1 );
268  balances.resize( balances.size() + 1 );
269  balances.back().resize( 1ULL << bits );
270  }
271  balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask][abo.asset_type] = &abo;
272 }
273 
275 {
276  const auto& abo = dynamic_cast< const account_balance_object& >( obj );
277  if( balances.size() < (abo.owner.instance.value >> bits) + 1 ) return;
278  balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask].erase( abo.asset_type );
279 }
280 
281 void balances_by_account_index::about_to_modify( const object& before )
282 {
283  ids_being_modified.emplace( before.id );
284 }
285 
287 {
288  FC_ASSERT( ids_being_modified.top() == after.id, "Modification of ID is not supported!");
289  ids_being_modified.pop();
290 }
291 
292 const map< asset_id_type, const account_balance_object* >& balances_by_account_index::get_account_balances( const account_id_type& acct )const
293 {
294  static const map< asset_id_type, const account_balance_object* > _empty;
295 
296  if( balances.size() < (acct.instance.value >> bits) + 1 ) return _empty;
297  return balances[acct.instance.value >> bits][acct.instance.value & mask];
298 }
299 
300 const account_balance_object* balances_by_account_index::get_account_balance( const account_id_type& acct, const asset_id_type& asset )const
301 {
302  if( balances.size() < (acct.instance.value >> bits) + 1 ) return nullptr;
303  const auto& mine = balances[acct.instance.value >> bits][acct.instance.value & mask];
304  const auto itr = mine.find( asset );
305  if( mine.end() == itr ) return nullptr;
306  return itr->second;
307 }
308 
309 } } // graphene::chain
310 
313  (membership_expiration_date)(registrar)(referrer)(lifetime_referrer)
314  (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage)
315  (name)(owner)(active)(options)(num_committee_voted)(statistics)
316  (whitelisting_accounts)(blacklisting_accounts)
317  (whitelisted_accounts)(blacklisted_accounts)
318  (cashback_vb)
319  (owner_special_authority)(active_special_authority)
320  (top_n_control_flags)
321  (allowed_assets)
322  )
323 
327 
330  (owner)(name)
331  (most_recent_op)
332  (total_ops)(removed_ops)
333  (total_core_in_orders)
334  (total_core_inactive)(total_core_pob)(total_core_pol)
335  (total_pob_value)(total_pol_value)
336  (core_in_balance)
337  (has_cashback_vb)
338  (is_voting)
339  (last_vote_time)
340  (lifetime_fees_paid)
341  (pending_fees)(pending_vested_fees)
342  )
343 
Tracks the balance of a single account/asset pairThis object is indexed on owner and asset_type so th...
const account_balance_object * get_account_balance(const account_id_type &acct, const asset_id_type &asset) const
void modify(const T &obj, const Lambda &m)
virtual void about_to_modify(const object &before) override
const map< asset_id_type, const account_balance_object * > & get_account_balances(const account_id_type &acct) const
This class represents an account on the object graphAccounts are the primary unit of authority on the...
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
Definition: api.cpp:56
virtual void object_modified(const object &after) override
flat_map< address, weight_type > address_auths
Definition: authority.hpp:123
#define FC_REFLECT_DERIVED_NO_TYPENAME(TYPE, INHERITS, MEMBERS)
Definition: reflect.hpp:356
uint16_t network_fee_percentage
Percentage of fee which should go to network.
bool maintenance_flag
Whether need to process this balance object in maintenance interval.
const global_property_object & get_global_properties() const
Definition: db_getter.cpp:44
const T & get(object_id_type id) const
flat_map< public_key_type, weight_type > key_auths
Definition: authority.hpp:121
virtual void object_removed(const object &obj) override
virtual void object_inserted(const object &obj) override
set< address > get_address_members(const account_object &a) const
object_id_type id
Definition: object.hpp:73
uint16_t lifetime_referrer_fee_percentage
Percentage of fee which should go to lifetime referrer.
#define GRAPHENE_100_PERCENT
Definition: config.hpp:102
virtual void object_modified(const object &after) override
time_point_sec head_block_time() const
Definition: db_getter.cpp:64
flat_map< account_id_type, weight_type > account_auths
Definition: authority.hpp:120
account_id_type lifetime_referrer
The lifetime member at the top of the referral tree. Receives a percentage of referral rewards...
share_type cut_fee(share_type a, uint16_t p)
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
virtual void about_to_modify(const object &before) override
tracks the asset information that changes frequentlyBecause the asset_object is very large it doesn&#39;t...
#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type)
Definition: types.hpp:74
void pay_fee(share_type core_fee, share_type cashback_vesting_threshold)
virtual void object_inserted(const object &obj) override
set< public_key_type, pubkey_comparator > get_key_members(const account_object &a) const
share_type accumulated_fees
fees accumulate to be paid out over time
const asset_dynamic_data_object & get_core_dynamic_data() const
Definition: db_getter.cpp:39
set< account_id_type > get_account_members(const account_object &a) const
asset_id_type asset_id
Definition: asset.hpp:39
void process_fees(const account_object &a, database &d) const
Split up and pay out pending_fees and pending_vested_fees.
virtual void object_removed(const object &obj) override
void deposit_cashback(const account_object &acct, share_type amount, bool require_vesting=true)
Definition: db_balance.cpp:206
account_id_type registrar
The account that paid the fee to register this account. Receives a percentage of referral rewards...
base for all database objects
Definition: object.hpp:62
account_id_type referrer
The account credited as referring this account. Receives a percentage of referral rewards...
T value
Definition: safe.hpp:22
void adjust_balance(const asset &delta)