BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
account_evaluator.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  */
24 
31 #include <graphene/chain/hardfork.hpp>
37 
38 #include <algorithm>
39 
40 namespace graphene { namespace chain {
41 
42 void verify_authority_accounts( const database& db, const authority& a )
43 {
44  const auto& chain_params = db.get_global_properties().parameters;
46  a.num_auths() <= chain_params.maximum_authority_membership,
47  internal_verify_auth_max_auth_exceeded,
48  "Maximum authority membership exceeded" );
49  for( const auto& acnt : a.account_auths )
50  {
51  GRAPHENE_ASSERT( db.find_object( acnt.first ) != nullptr,
52  internal_verify_auth_account_not_found,
53  "Account ${a} specified in authority does not exist",
54  ("a", acnt.first) );
55  }
56 }
57 
58 void verify_account_votes( const database& db, const account_options& options )
59 {
60  // ensure account's votes satisfy requirements
61  // NB only the part of vote checking that requires chain state is here,
62  // the rest occurs in account_options::validate()
63 
64  const auto& gpo = db.get_global_properties();
65  const auto& chain_params = gpo.parameters;
66 
67  FC_ASSERT( options.num_witness <= chain_params.maximum_witness_count,
68  "Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) );
69  FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,
70  "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
71 
72  FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." );
73 
74  uint32_t max_vote_id = gpo.next_available_vote_id;
75  bool has_worker_votes = false;
76  for( auto id : options.votes )
77  {
78  FC_ASSERT( id < max_vote_id, "Can not vote for ${id} which does not exist.", ("id",id) );
79  has_worker_votes |= (id.type() == vote_id_type::worker);
80  }
81 
82  if( has_worker_votes && (db.head_block_time() >= HARDFORK_607_TIME) )
83  {
84  const auto& against_worker_idx = db.get_index_type<worker_index>().indices().get<by_vote_against>();
85  for( auto id : options.votes )
86  {
87  if( id.type() == vote_id_type::worker )
88  {
89  FC_ASSERT( against_worker_idx.find( id ) == against_worker_idx.end(),
90  "Can no longer vote against a worker." );
91  }
92  }
93  }
94  if ( db.head_block_time() >= HARDFORK_CORE_143_TIME ) {
95  const auto& approve_worker_idx = db.get_index_type<worker_index>().indices().get<by_vote_for>();
96  const auto& committee_idx = db.get_index_type<committee_member_index>().indices().get<by_vote_id>();
97  const auto& witness_idx = db.get_index_type<witness_index>().indices().get<by_vote_id>();
98  for ( auto id : options.votes ) {
99  switch ( id.type() ) {
101  FC_ASSERT( committee_idx.find(id) != committee_idx.end(),
102  "Can not vote for ${id} which does not exist.", ("id",id) );
103  break;
105  FC_ASSERT( witness_idx.find(id) != witness_idx.end(),
106  "Can not vote for ${id} which does not exist.", ("id",id) );
107  break;
109  FC_ASSERT( approve_worker_idx.find( id ) != approve_worker_idx.end(),
110  "Can not vote for ${id} which does not exist.", ("id",id) );
111  break;
112  default:
113  FC_THROW( "Invalid Vote Type: ${id}", ("id", id) );
114  break;
115  }
116  }
117  }
118 }
119 
121 { try {
122  database& d = db();
123 
124  FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." );
125  FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." );
126 
127  try
128  {
131  }
132  GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_create_max_auth_exceeded )
133  GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_create_auth_account_not_found )
134 
135  if( op.extensions.value.owner_special_authority.valid() )
136  evaluate_special_authority( d, *op.extensions.value.owner_special_authority );
137  if( op.extensions.value.active_special_authority.valid() )
138  evaluate_special_authority( d, *op.extensions.value.active_special_authority );
139  if( op.extensions.value.buyback_options.valid() )
140  evaluate_buyback_account_options( d, *op.extensions.value.buyback_options );
141  verify_account_votes( d, op.options );
142 
143  auto& acnt_indx = d.get_index_type<account_index>();
144  if( op.name.size() )
145  {
146  auto current_account_itr = acnt_indx.indices().get<by_name>().find( op.name );
147  FC_ASSERT( current_account_itr == acnt_indx.indices().get<by_name>().end(),
148  "Account '${a}' already exists.", ("a",op.name) );
149  }
150 
151  return void_result();
152 } FC_CAPTURE_AND_RETHROW( (op) ) }
153 
155 { try {
156 
157  database& d = db();
158  uint16_t referrer_percent = o.referrer_percent;
159  bool has_small_percent = (
160  (db().head_block_time() <= HARDFORK_453_TIME)
161  && (o.referrer != o.registrar )
162  && (o.referrer_percent != 0 )
163  && (o.referrer_percent <= 0x100)
164  );
165 
166  if( has_small_percent )
167  {
168  if( referrer_percent >= 100 )
169  {
170  wlog( "between 100% and 0x100%: ${o}", ("o", o) );
171  }
172  referrer_percent = referrer_percent*100;
173  if( referrer_percent > GRAPHENE_100_PERCENT )
174  referrer_percent = GRAPHENE_100_PERCENT;
175  }
176 
177  const auto& global_properties = d.get_global_properties();
178 
179  const auto& new_acnt_object = d.create<account_object>( [&o,&d,&global_properties,referrer_percent]( account_object& obj )
180  {
181  obj.registrar = o.registrar;
182  obj.referrer = o.referrer;
183  obj.lifetime_referrer = o.referrer(d).lifetime_referrer;
184 
185  const auto& params = global_properties.parameters;
186  obj.network_fee_percentage = params.network_percent_of_fee;
187  obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee;
188  obj.referrer_rewards_percentage = referrer_percent;
189 
190  obj.name = o.name;
191  obj.owner = o.owner;
192  obj.active = o.active;
193  obj.options = o.options;
195  obj.statistics = d.create<account_statistics_object>([&obj](account_statistics_object& s){
196  s.owner = obj.id;
197  s.name = obj.name;
198  s.is_voting = obj.options.is_voting();
199  }).id;
200 
201  if( o.extensions.value.owner_special_authority.valid() )
202  obj.owner_special_authority = *(o.extensions.value.owner_special_authority);
203  if( o.extensions.value.active_special_authority.valid() )
204  obj.active_special_authority = *(o.extensions.value.active_special_authority);
205  if( o.extensions.value.buyback_options.valid() )
206  {
207  obj.allowed_assets = o.extensions.value.buyback_options->markets;
208  obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy );
209  }
210  });
211 
212  const auto& dynamic_properties = d.get_dynamic_global_properties();
213  d.modify(dynamic_properties, [](dynamic_global_property_object& p) {
215  });
216 
217  if( dynamic_properties.accounts_registered_this_interval % global_properties.parameters.accounts_per_fee_scale == 0
218  && global_properties.parameters.account_fee_scale_bitshifts != 0 )
219  {
220  d.modify(global_properties, [](global_property_object& p) {
222  });
223  }
224 
225  if( o.extensions.value.owner_special_authority.valid()
226  || o.extensions.value.active_special_authority.valid() )
227  {
229  {
230  sa.account = new_acnt_object.id;
231  } );
232  }
233 
234  if( o.extensions.value.buyback_options.valid() )
235  {
236  asset_id_type asset_to_buy = o.extensions.value.buyback_options->asset_to_buy;
237 
238  d.create< buyback_object >( [&]( buyback_object& bo )
239  {
240  bo.asset_to_buy = asset_to_buy;
241  } );
242 
243  d.modify( asset_to_buy(d), [&]( asset_object& a )
244  {
245  a.buyback_account = new_acnt_object.id;
246  } );
247  }
248 
249  return new_acnt_object.id;
250 } FC_CAPTURE_AND_RETHROW((o)) }
251 
252 
254 { try {
255  database& d = db();
256 
257  try
258  {
259  if( o.owner ) verify_authority_accounts( d, *o.owner );
260  if( o.active ) verify_authority_accounts( d, *o.active );
261  }
262  GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_update_max_auth_exceeded )
263  GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_update_auth_account_not_found )
264 
265  if( o.extensions.value.owner_special_authority.valid() )
266  evaluate_special_authority( d, *o.extensions.value.owner_special_authority );
267  if( o.extensions.value.active_special_authority.valid() )
268  evaluate_special_authority( d, *o.extensions.value.active_special_authority );
269 
270  acnt = &o.account(d);
271 
272  if( o.new_options.valid() )
274 
275  return void_result();
276 } FC_CAPTURE_AND_RETHROW( (o) ) }
277 
279 { try {
280  database& d = db();
281 
282  bool sa_before = acnt->has_special_authority();
283 
284  // update account statistics
285  if( o.new_options.valid() )
286  {
287  if ( o.new_options->voting_account != acnt->options.voting_account
288  || o.new_options->votes != acnt->options.votes )
289  {
290  d.modify( acnt->statistics( d ), [&d,&o]( account_statistics_object& aso )
291  {
292  aso.is_voting = o.new_options->is_voting();
293  aso.last_vote_time = d.head_block_time();
294  } );
295  }
296  }
297 
298  // update account object
299  d.modify( *acnt, [&o](account_object& a){
300  if( o.owner )
301  {
302  a.owner = *o.owner;
303  a.top_n_control_flags = 0;
304  }
305  if( o.active )
306  {
307  a.active = *o.active;
308  a.top_n_control_flags = 0;
309  }
310  if( o.new_options )
311  {
312  a.options = *o.new_options;
313  a.num_committee_voted = a.options.num_committee_voted();
314  }
315  if( o.extensions.value.owner_special_authority.valid() )
316  {
317  a.owner_special_authority = *(o.extensions.value.owner_special_authority);
318  a.top_n_control_flags = 0;
319  }
320  if( o.extensions.value.active_special_authority.valid() )
321  {
322  a.active_special_authority = *(o.extensions.value.active_special_authority);
323  a.top_n_control_flags = 0;
324  }
325  });
326 
327  bool sa_after = acnt->has_special_authority();
328 
329  if( sa_before && (!sa_after) )
330  {
331  const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get<by_account>();
332  auto sa_it = sa_idx.find( o.account );
333  assert( sa_it != sa_idx.end() );
334  d.remove( *sa_it );
335  }
336  else if( (!sa_before) && sa_after )
337  {
339  {
340  sa.account = o.account;
341  } );
342  }
343 
344  return void_result();
345 } FC_CAPTURE_AND_RETHROW( (o) ) }
346 
348 { try {
349  database& d = db();
350 
351  listed_account = &o.account_to_list(d);
353  FC_ASSERT( o.authorizing_account(d).is_lifetime_member(), "The authorizing account must be a lifetime member." );
354 
355  return void_result();
356 } FC_CAPTURE_AND_RETHROW( (o) ) }
357 
359 { try {
360  database& d = db();
361 
362  d.modify(*listed_account, [&o](account_object& a) {
363  if( o.new_listing & o.white_listed )
365  else
367 
368  if( o.new_listing & o.black_listed )
370  else
372  });
373 
375  d.modify( o.authorizing_account(d), [&]( account_object& a ) {
376  if( o.new_listing & o.white_listed )
378  else
380 
381  if( o.new_listing & o.black_listed )
383  else
385  });
386 
387  return void_result();
388 } FC_CAPTURE_AND_RETHROW( (o) ) }
389 
391 { try {
392  database& d = db();
393 
394  account = &d.get(o.account_to_upgrade);
395  FC_ASSERT(!account->is_lifetime_member());
396 
397  return {};
398 //} FC_CAPTURE_AND_RETHROW( (o) ) }
399 } FC_RETHROW_EXCEPTIONS( error, "Unable to upgrade account '${a}'", ("a",o.account_to_upgrade(db()).name) ) }
400 
402 { try {
403  database& d = db();
404 
405  d.modify(*account, [&](account_object& a) {
407  {
408  // Upgrade to lifetime member. I don't care what the account was before.
409  a.statistics(d).process_fees(a, d);
410  a.membership_expiration_date = time_point_sec::maximum();
411  a.referrer = a.registrar = a.lifetime_referrer = a.get_id();
412  a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage;
413  } else if( a.is_annual_member(d.head_block_time()) ) {
414  // Renew an annual subscription that's still in effect.
415  FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );
417  "May not extend annual membership more than a decade into the future.");
419  } else {
420  // Upgrade from basic account.
421  FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );
422  a.statistics(d).process_fees(a, d);
423  assert(a.is_basic_account(d.head_block_time()));
424  a.referrer = a.get_id();
426  }
427  });
428 
429  return {};
430 } FC_RETHROW_EXCEPTIONS( error, "Unable to upgrade account '${a}'", ("a",o.account_to_upgrade(db()).name) ) }
431 
432 } } // graphene::chain
void evaluate_buyback_account_options(const database &db, const buyback_account_options &bbo)
Definition: buyback.cpp:32
account_id_type registrar
This account pays the fee. Must be a lifetime member.
Definition: account.hpp:100
void modify(const T &obj, const Lambda &m)
flat_set< account_id_type > blacklisting_accounts
bool upgrade_to_lifetime_member
If true, the account will be upgraded to a lifetime member; otherwise, it will add a year to the subs...
Definition: account.hpp:245
Identifies a weighted set of keys and accounts that must approve operations.
Definition: authority.hpp:34
Manage an account&#39;s membership statusThis operation is used to upgrade an account to a member...
Definition: account.hpp:234
const object * find_object(object_id_type id) const
special_authority active_special_authority
This class represents an account on the object graphAccounts are the primary unit of authority on the...
Maintains global state information (committee_member list, current fees)This is an implementation det...
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
Definition: api.cpp:56
Update an existing account.
Definition: account.hpp:135
void_result do_evaluate(const operation_type &o)
account_id_type account_to_upgrade
The account to upgrade; must not already be a lifetime member.
Definition: account.hpp:243
object_id_type do_apply(const account_create_operation &o)
optional< authority > active
New active authority. This can be updated by the current active authority.
Definition: account.hpp:157
uint16_t network_fee_percentage
Percentage of fee which should go to network.
const account_object * fee_paying_account
Definition: evaluator.hpp:116
bool is_annual_member(time_point_sec now) const
void_result do_evaluate(const account_whitelist_operation &o)
optional< authority > owner
New owner authority. If set, this operation requires owner authority to execute.
Definition: account.hpp:155
optional< account_id_type > buyback_account
#define FC_THROW(...)
Definition: exception.hpp:366
optional< account_options > new_options
New account options.
Definition: account.hpp:160
uint8_t account_fee_scale_bitshifts
number of times to left bitshift account registration fee at each scaling
#define FC_RETHROW_EXCEPTIONS(LOG_LEVEL, FORMAT,...)
Catchs all exception&#39;s, std::exceptions, and ... and rethrows them after appending the provided log m...
Definition: exception.hpp:463
set< account_id_type > whitelisted_accounts
void_result do_apply(const account_update_operation &o)
#define wlog(FORMAT,...)
Definition: logger.hpp:123
const global_property_object & get_global_properties() const
Definition: db_getter.cpp:44
const T & get(object_id_type id) const
optional< flat_set< asset_id_type > > allowed_assets
account_id_type authorizing_account
The account which is specifying an opinion of another account.
Definition: account.hpp:209
microseconds days(int64_t d)
Definition: time.hpp:38
This account is blacklisted, but not whitelisted.
Definition: account.hpp:202
object_id_type id
Definition: object.hpp:69
These are the fields which can be updated by the active authority.
Definition: account.hpp:39
flat_set< vote_id_type > votes
Definition: account.hpp:58
account_id_type get_id() const
#define GRAPHENE_100_PERCENT
Definition: config.hpp:102
uint16_t num_committee_voted() const
Definition: account.hpp:67
account_id_type referrer
This account receives a portion of the fee split between registrar and referrer. Must be a member...
Definition: account.hpp:103
void verify_authority_accounts(const database &db, const authority &a)
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
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
string name
The account&#39;s name. This name must be unique among all account names on the graph. May not be empty.
void_result do_apply(const account_whitelist_operation &o)
bool allow_non_member_whitelists
true if non-member accounts may set whitelists and blacklists; false otherwise
This account is whitelisted, but not blacklisted.
Definition: account.hpp:201
flat_set< account_id_type > whitelisting_accounts
set< account_id_type > blacklisted_accounts
bool is_basic_account(time_point_sec now) const
This operation is used to whitelist and blacklist accounts, primarily for transacting in whitelisted ...
Definition: account.hpp:196
const Operation::fee_parameters_type & get() const
void verify_account_votes(const database &db, const account_options &options)
tracks the parameters of an assetAll assets have a globally unique symbol name that controls how they...
account_id_type account
The account to update.
Definition: account.hpp:152
void_result do_apply(const operation_type &o)
const object & get(object_id_type id) const
Definition: index.hpp:111
account_statistics_id_type statistics
void evaluate_special_authority(const database &db, const special_authority &auth)
void remove(const object &obj)
Maintains global state information (committee_member list, current fees)This is an implementation det...
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
account_id_type registrar
The account that paid the fee to register this account. Receives a percentage of referral rewards...
void_result do_evaluate(const account_create_operation &o)
#define GRAPHENE_RECODE_EXC(cause_type, effect_type)
Definition: exceptions.cpp:167
uint32_t num_auths() const
Definition: authority.hpp:111
account_id_type referrer
The account credited as referring this account. Receives a percentage of referral rewards...
const T & create(F &&constructor)
account_id_type account_to_list
The account being opined about.
Definition: account.hpp:211
const IndexType & get_index_type() const
void_result do_evaluate(const account_update_operation &o)
special_authority owner_special_authority
const index_type & indices() const