2015-06-08 15:50:35 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2015, Cryptonomex, Inc.
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
|
|
|
|
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
|
* are permitted until September 8, 2015, provided that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
|
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
2015-08-13 18:27:39 +00:00
|
|
|
|
|
|
|
|
#include <fc/smart_ref_impl.hpp>
|
2015-06-08 15:50:35 +00:00
|
|
|
#include <graphene/chain/database.hpp>
|
2015-07-28 21:08:21 +00:00
|
|
|
#include <graphene/chain/exceptions.hpp>
|
|
|
|
|
#include <graphene/chain/internal_exceptions.hpp>
|
2015-06-08 15:50:35 +00:00
|
|
|
#include <graphene/chain/account_evaluator.hpp>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
namespace graphene { namespace chain {
|
|
|
|
|
|
2015-07-28 21:08:21 +00:00
|
|
|
void verify_authority_accounts( const database& db, const authority& a )
|
|
|
|
|
{
|
|
|
|
|
const auto& chain_params = db.get_global_properties().parameters;
|
|
|
|
|
GRAPHENE_ASSERT(
|
|
|
|
|
a.num_auths() <= chain_params.maximum_authority_membership,
|
|
|
|
|
internal_verify_auth_max_auth_exceeded,
|
|
|
|
|
"Maximum authority membership exceeded" );
|
|
|
|
|
for( const auto& acnt : a.account_auths )
|
|
|
|
|
{
|
|
|
|
|
GRAPHENE_ASSERT( db.find_object( acnt.first ) != nullptr,
|
|
|
|
|
internal_verify_auth_account_not_found,
|
|
|
|
|
"Account ${a} specified in authority does not exist",
|
|
|
|
|
("a", acnt.first) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
|
2015-06-08 15:50:35 +00:00
|
|
|
{ try {
|
2015-06-09 20:46:00 +00:00
|
|
|
database& d = db();
|
2015-06-16 18:42:02 +00:00
|
|
|
FC_ASSERT( d.find_object(op.options.voting_account) );
|
2015-06-09 20:46:00 +00:00
|
|
|
FC_ASSERT( fee_paying_account->is_lifetime_member() );
|
|
|
|
|
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()) );
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-09 20:46:00 +00:00
|
|
|
const auto& global_props = d.get_global_properties();
|
2015-06-08 15:50:35 +00:00
|
|
|
const auto& chain_params = global_props.parameters;
|
2015-06-16 18:42:02 +00:00
|
|
|
|
2015-07-28 21:08:21 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
verify_authority_accounts( d, op.owner );
|
|
|
|
|
verify_authority_accounts( d, op.active );
|
|
|
|
|
}
|
|
|
|
|
GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_create_max_auth_exceeded )
|
|
|
|
|
GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_create_auth_account_not_found )
|
2015-06-16 18:42:02 +00:00
|
|
|
|
|
|
|
|
uint32_t max_vote_id = global_props.next_available_vote_id;
|
|
|
|
|
FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count );
|
|
|
|
|
FC_ASSERT( op.options.num_committee <= chain_params.maximum_committee_count );
|
2015-06-08 15:50:35 +00:00
|
|
|
safe<uint32_t> counts[vote_id_type::VOTE_TYPE_COUNT];
|
2015-06-16 18:42:02 +00:00
|
|
|
for( auto id : op.options.votes )
|
2015-06-08 15:50:35 +00:00
|
|
|
{
|
|
|
|
|
FC_ASSERT( id < max_vote_id );
|
|
|
|
|
counts[id.type()]++;
|
|
|
|
|
}
|
2015-06-16 18:42:02 +00:00
|
|
|
FC_ASSERT(counts[vote_id_type::witness] <= op.options.num_witness,
|
2015-06-08 15:50:35 +00:00
|
|
|
"",
|
2015-06-16 18:42:02 +00:00
|
|
|
("count", counts[vote_id_type::witness])("num", op.options.num_witness));
|
|
|
|
|
FC_ASSERT(counts[vote_id_type::committee] <= op.options.num_committee,
|
2015-06-08 15:50:35 +00:00
|
|
|
"",
|
2015-06-16 18:42:02 +00:00
|
|
|
("count", counts[vote_id_type::committee])("num", op.options.num_committee));
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-09 20:46:00 +00:00
|
|
|
auto& acnt_indx = d.get_index_type<account_index>();
|
2015-06-08 15:50:35 +00:00
|
|
|
if( op.name.size() )
|
|
|
|
|
{
|
|
|
|
|
auto current_account_itr = acnt_indx.indices().get<by_name>().find( op.name );
|
|
|
|
|
FC_ASSERT( current_account_itr == acnt_indx.indices().get<by_name>().end() );
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
return void_result();
|
2015-06-08 15:50:35 +00:00
|
|
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
|
|
|
|
|
|
|
|
|
object_id_type account_create_evaluator::do_apply( const account_create_operation& o )
|
|
|
|
|
{ try {
|
|
|
|
|
const auto& new_acnt_object = db().create<account_object>( [&]( account_object& obj ){
|
2015-06-09 20:46:00 +00:00
|
|
|
obj.registrar = o.registrar;
|
|
|
|
|
obj.referrer = o.referrer;
|
|
|
|
|
obj.lifetime_referrer = o.referrer(db()).lifetime_referrer;
|
|
|
|
|
|
|
|
|
|
auto& params = db().get_global_properties().parameters;
|
|
|
|
|
obj.network_fee_percentage = params.network_percent_of_fee;
|
|
|
|
|
obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee;
|
|
|
|
|
obj.referrer_rewards_percentage = o.referrer_percent;
|
|
|
|
|
|
2015-06-08 15:50:35 +00:00
|
|
|
obj.name = o.name;
|
2015-07-02 05:52:45 +00:00
|
|
|
obj.owner = o.owner;
|
|
|
|
|
obj.active = o.active;
|
2015-06-16 18:42:02 +00:00
|
|
|
obj.options = o.options;
|
2015-07-31 13:53:49 +00:00
|
|
|
obj.statistics = db().create<account_statistics_object>([&](account_statistics_object& s){s.owner = obj.id;}).id;
|
2015-06-08 15:50:35 +00:00
|
|
|
});
|
|
|
|
|
|
2015-06-26 20:43:39 +00:00
|
|
|
const auto& dynamic_properties = db().get_dynamic_global_properties();
|
|
|
|
|
db().modify(dynamic_properties, [](dynamic_global_property_object& p) {
|
|
|
|
|
++p.accounts_registered_this_interval;
|
|
|
|
|
});
|
2015-07-08 20:39:23 +00:00
|
|
|
|
2015-08-10 20:39:09 +00:00
|
|
|
const auto& global_properties = db().get_global_properties();
|
2015-06-26 20:43:39 +00:00
|
|
|
if( dynamic_properties.accounts_registered_this_interval %
|
|
|
|
|
global_properties.parameters.accounts_per_fee_scale == 0 )
|
|
|
|
|
db().modify(global_properties, [&dynamic_properties](global_property_object& p) {
|
2015-08-10 20:39:09 +00:00
|
|
|
p.parameters.current_fees->get<account_create_operation>().basic_fee <<= p.parameters.account_fee_scale_bitshifts;
|
2015-06-26 20:43:39 +00:00
|
|
|
});
|
|
|
|
|
|
2015-06-08 15:50:35 +00:00
|
|
|
return new_acnt_object.id;
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW((o)) }
|
|
|
|
|
|
|
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
void_result account_update_evaluator::do_evaluate( const account_update_operation& o )
|
2015-06-23 21:33:06 +00:00
|
|
|
{ try {
|
2015-06-09 20:46:00 +00:00
|
|
|
database& d = db();
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-07-28 21:08:21 +00:00
|
|
|
const auto& chain_params = d.get_global_properties().parameters;
|
2015-06-16 18:42:02 +00:00
|
|
|
|
2015-07-28 21:08:21 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if( o.owner ) verify_authority_accounts( d, *o.owner );
|
|
|
|
|
if( o.active ) verify_authority_accounts( d, *o.active );
|
|
|
|
|
}
|
|
|
|
|
GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_update_max_auth_exceeded )
|
|
|
|
|
GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_update_auth_account_not_found )
|
2015-06-08 15:50:35 +00:00
|
|
|
|
|
|
|
|
acnt = &o.account(d);
|
|
|
|
|
|
2015-06-16 18:42:02 +00:00
|
|
|
if( o.new_options )
|
2015-06-08 15:50:35 +00:00
|
|
|
{
|
2015-06-16 18:42:02 +00:00
|
|
|
FC_ASSERT( o.new_options->num_witness <= chain_params.maximum_witness_count );
|
|
|
|
|
FC_ASSERT( o.new_options->num_committee <= chain_params.maximum_committee_count );
|
2015-06-08 15:50:35 +00:00
|
|
|
uint32_t max_vote_id = d.get_global_properties().next_available_vote_id;
|
2015-06-16 18:42:02 +00:00
|
|
|
for( auto id : o.new_options->votes )
|
2015-06-08 15:50:35 +00:00
|
|
|
{
|
|
|
|
|
FC_ASSERT( id < max_vote_id );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
return void_result();
|
2015-06-23 21:33:06 +00:00
|
|
|
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
|
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
void_result account_update_evaluator::do_apply( const account_update_operation& o )
|
2015-06-23 21:33:06 +00:00
|
|
|
{ try {
|
2015-06-16 18:42:02 +00:00
|
|
|
db().modify( *acnt, [&](account_object& a){
|
2015-07-02 05:52:45 +00:00
|
|
|
if( o.owner ) a.owner = *o.owner;
|
|
|
|
|
if( o.active ) a.active = *o.active;
|
|
|
|
|
if( o.new_options ) a.options = *o.new_options;
|
2015-06-16 18:42:02 +00:00
|
|
|
});
|
2015-06-09 20:49:42 +00:00
|
|
|
return void_result();
|
2015-06-23 21:33:06 +00:00
|
|
|
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_operation& o)
|
2015-06-08 15:50:35 +00:00
|
|
|
{ try {
|
|
|
|
|
database& d = db();
|
|
|
|
|
|
|
|
|
|
listed_account = &o.account_to_list(d);
|
2015-06-09 20:46:00 +00:00
|
|
|
if( !d.get_global_properties().parameters.allow_non_member_whitelists )
|
|
|
|
|
FC_ASSERT(o.authorizing_account(d).is_lifetime_member());
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
return void_result();
|
2015-06-08 15:50:35 +00:00
|
|
|
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
|
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
void_result account_whitelist_evaluator::do_apply(const account_whitelist_operation& o)
|
2015-06-23 21:33:06 +00:00
|
|
|
{ try {
|
2015-06-08 15:50:35 +00:00
|
|
|
database& d = db();
|
|
|
|
|
|
|
|
|
|
d.modify(*listed_account, [&o](account_object& a) {
|
|
|
|
|
if( o.new_listing & o.white_listed )
|
|
|
|
|
a.whitelisting_accounts.insert(o.authorizing_account);
|
|
|
|
|
else
|
|
|
|
|
a.whitelisting_accounts.erase(o.authorizing_account);
|
|
|
|
|
if( o.new_listing & o.black_listed )
|
|
|
|
|
a.blacklisting_accounts.insert(o.authorizing_account);
|
|
|
|
|
else
|
|
|
|
|
a.blacklisting_accounts.erase(o.authorizing_account);
|
|
|
|
|
});
|
|
|
|
|
|
2015-06-09 20:49:42 +00:00
|
|
|
return void_result();
|
2015-06-23 21:33:06 +00:00
|
|
|
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
2015-06-08 15:50:35 +00:00
|
|
|
|
2015-06-10 14:36:01 +00:00
|
|
|
void_result account_upgrade_evaluator::do_evaluate(const account_upgrade_evaluator::operation_type& o)
|
2015-06-23 21:33:06 +00:00
|
|
|
{ try {
|
2015-06-10 14:36:01 +00:00
|
|
|
database& d = db();
|
|
|
|
|
|
|
|
|
|
account = &d.get(o.account_to_upgrade);
|
|
|
|
|
FC_ASSERT(!account->is_lifetime_member());
|
|
|
|
|
|
|
|
|
|
return {};
|
2015-07-09 19:40:19 +00:00
|
|
|
//} FC_CAPTURE_AND_RETHROW( (o) ) }
|
|
|
|
|
} FC_RETHROW_EXCEPTIONS( error, "Unable to upgrade account '${a}'", ("a",o.account_to_upgrade(db()).name) ) }
|
2015-06-10 14:36:01 +00:00
|
|
|
|
|
|
|
|
void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator::operation_type& o)
|
2015-06-23 21:33:06 +00:00
|
|
|
{ try {
|
2015-06-10 14:36:01 +00:00
|
|
|
database& d = db();
|
|
|
|
|
|
|
|
|
|
d.modify(*account, [&](account_object& a) {
|
|
|
|
|
if( o.upgrade_to_lifetime_member )
|
|
|
|
|
{
|
|
|
|
|
// Upgrade to lifetime member. I don't care what the account was before.
|
2015-06-15 21:28:20 +00:00
|
|
|
a.statistics(d).process_fees(a, d);
|
2015-06-10 14:36:01 +00:00
|
|
|
a.membership_expiration_date = time_point_sec::maximum();
|
|
|
|
|
a.referrer = a.registrar = a.lifetime_referrer = a.get_id();
|
2015-06-10 18:17:13 +00:00
|
|
|
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage;
|
2015-06-10 14:36:01 +00:00
|
|
|
} else if( a.is_annual_member(d.head_block_time()) ) {
|
|
|
|
|
// Renew an annual subscription that's still in effect.
|
|
|
|
|
FC_ASSERT(a.membership_expiration_date - d.head_block_time() < fc::days(3650),
|
|
|
|
|
"May not extend annual membership more than a decade into the future.");
|
|
|
|
|
a.membership_expiration_date += fc::days(365);
|
|
|
|
|
} else {
|
|
|
|
|
// Upgrade from basic account.
|
2015-06-15 21:28:20 +00:00
|
|
|
a.statistics(d).process_fees(a, d);
|
2015-06-10 14:36:01 +00:00
|
|
|
assert(a.is_basic_account(d.head_block_time()));
|
|
|
|
|
a.membership_expiration_date = d.head_block_time() + fc::days(365);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {};
|
2015-07-09 19:40:19 +00:00
|
|
|
} FC_RETHROW_EXCEPTIONS( error, "Unable to upgrade account '${a}'", ("a",o.account_to_upgrade(db()).name) ) }
|
2015-06-10 14:36:01 +00:00
|
|
|
|
2015-06-08 15:50:35 +00:00
|
|
|
} } // graphene::chain
|