Implement top_n special authority #516

This commit is contained in:
theoreticalbts 2016-01-27 10:30:32 -05:00
parent ef68375fab
commit a1e8fc0741
18 changed files with 784 additions and 17 deletions

View file

@ -344,6 +344,8 @@ namespace graphene { namespace app {
break;
case impl_budget_record_object_type:
break;
case impl_special_authority_object_type:
break;
}
}
return result;

View file

@ -76,6 +76,7 @@ add_library( graphene_chain
withdraw_permission_evaluator.cpp
worker_evaluator.cpp
confidential_evaluator.cpp
special_authority.cpp
account_object.cpp
asset_object.cpp

View file

@ -29,6 +29,8 @@
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/internal_exceptions.hpp>
#include <graphene/chain/special_authority.hpp>
#include <graphene/chain/special_authority_object.hpp>
#include <algorithm>
@ -53,6 +55,12 @@ void verify_authority_accounts( const database& db, const authority& a )
void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
{ try {
database& d = db();
if( d.head_block_time() < HARDFORK_516_TIME )
{
FC_ASSERT( !op.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !op.extensions.value.active_special_authority.valid() );
}
FC_ASSERT( d.find_object(op.options.voting_account), "Invalid proxy account specified." );
FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." );
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." );
@ -68,6 +76,11 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
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 )
if( op.extensions.value.owner_special_authority.valid() )
evaluate_special_authority( d, *op.extensions.value.owner_special_authority );
if( op.extensions.value.active_special_authority.valid() )
evaluate_special_authority( d, *op.extensions.value.active_special_authority );
uint32_t max_vote_id = global_props.next_available_vote_id;
FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count,
@ -136,6 +149,11 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
obj.active = o.active;
obj.options = o.options;
obj.statistics = db().create<account_statistics_object>([&](account_statistics_object& s){s.owner = obj.id;}).id;
if( o.extensions.value.owner_special_authority.valid() )
obj.owner_special_authority = *(o.extensions.value.owner_special_authority);
if( o.extensions.value.active_special_authority.valid() )
obj.active_special_authority = *(o.extensions.value.active_special_authority);
});
if( has_small_percent )
@ -159,6 +177,15 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
p.parameters.current_fees->get<account_create_operation>().basic_fee <<= p.parameters.account_fee_scale_bitshifts;
});
if( o.extensions.value.owner_special_authority.valid()
|| o.extensions.value.active_special_authority.valid() )
{
db().create< special_authority_object >( [&]( special_authority_object& sa )
{
sa.account = new_acnt_object.id;
} );
}
return new_acnt_object.id;
} FC_CAPTURE_AND_RETHROW((o)) }
@ -166,6 +193,11 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
void_result account_update_evaluator::do_evaluate( const account_update_operation& o )
{ try {
database& d = db();
if( d.head_block_time() < HARDFORK_516_TIME )
{
FC_ASSERT( !o.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !o.extensions.value.active_special_authority.valid() );
}
const auto& chain_params = d.get_global_properties().parameters;
@ -177,6 +209,11 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
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 )
if( o.extensions.value.owner_special_authority.valid() )
evaluate_special_authority( d, *o.extensions.value.owner_special_authority );
if( o.extensions.value.active_special_authority.valid() )
evaluate_special_authority( d, *o.extensions.value.active_special_authority );
acnt = &o.account(d);
if( o.new_options )
@ -195,11 +232,35 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
void_result account_update_evaluator::do_apply( const account_update_operation& o )
{ try {
db().modify( *acnt, [&](account_object& a){
database& d = db();
bool sa_before, sa_after;
d.modify( *acnt, [&](account_object& a){
if( o.owner ) a.owner = *o.owner;
if( o.active ) a.active = *o.active;
if( o.new_options ) a.options = *o.new_options;
sa_before = a.has_special_authority();
if( o.extensions.value.owner_special_authority.valid() )
a.owner_special_authority = *(o.extensions.value.owner_special_authority);
if( o.extensions.value.active_special_authority.valid() )
a.active_special_authority = *(o.extensions.value.active_special_authority);
sa_after = a.has_special_authority();
});
if( sa_before & (!sa_after) )
{
const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get<by_account>();
auto sa_it = sa_idx.find( o.account );
assert( sa_it != sa_idx.end() );
d.remove( *sa_it );
}
else if( (!sa_before) & sa_after )
{
d.create< special_authority_object >( [&]( special_authority_object& sa )
{
sa.account = o.account;
} );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }

View file

@ -36,6 +36,7 @@
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/special_authority_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
@ -208,6 +209,7 @@ void database::initialize_indexes()
add_index< primary_index<simple_index<chain_property_object > > >();
add_index< primary_index<simple_index<witness_schedule_object > > >();
add_index< primary_index<simple_index<budget_record_object > > >();
add_index< primary_index< special_authority_index > >();
}
void database::init_genesis(const genesis_state_type& genesis_state)

View file

@ -36,6 +36,7 @@
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/special_authority_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/vote_count.hpp>
#include <graphene/chain/witness_object.hpp>
@ -465,6 +466,62 @@ void database::process_budget()
FC_CAPTURE_AND_RETHROW()
}
template< typename Visitor >
void visit_special_authorities( const database& db, Visitor visit )
{
const auto& sa_idx = db.get_index_type< special_authority_index >().indices().get<by_id>();
for( const special_authority_object& sao : sa_idx )
{
const account_object& acct = sao.account(db);
if( acct.owner_special_authority.which() != special_authority::tag< no_special_authority >::value )
{
visit( acct, true, acct.owner_special_authority );
}
if( acct.active_special_authority.which() != special_authority::tag< no_special_authority >::value )
{
visit( acct, false, acct.active_special_authority );
}
}
}
void update_top_n_authorities( database& db )
{
visit_special_authorities( db,
[&]( const account_object& acct, bool is_owner, const special_authority& auth )
{
if( auth.which() == special_authority::tag< top_holders_special_authority >::value )
{
// use index to grab the top N holders of the asset and vote_counter to obtain the weights
const top_holders_special_authority& tha = auth.get< top_holders_special_authority >();
vote_counter vc;
const auto& bal_idx = db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
uint8_t num_needed = tha.num_top_holders;
if( num_needed == 0 )
return;
// find accounts
const auto range = bal_idx.equal_range( boost::make_tuple( tha.asset ) );
for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) )
{
assert( bal.asset_type == tha.asset );
if( bal.owner == acct.id )
continue;
vc.add( bal.owner, bal.balance.value );
--num_needed;
if( num_needed == 0 )
break;
}
db.modify( acct, [&]( account_object& a )
{
vc.finish( is_owner ? a.owner : a.active );
} );
}
} );
}
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
{
const auto& gpo = get_global_properties();
@ -545,7 +602,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
}
} fee_helper(*this, gpo);
perform_account_maintenance(std::tie(tally_helper, fee_helper));
perform_account_maintenance(std::tie(
tally_helper,
fee_helper
));
struct clear_canary {
clear_canary(vector<uint64_t>& target): target(target){}
@ -557,6 +617,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
b(_committee_count_histogram_buffer),
c(_vote_tally_buffer);
update_top_n_authorities(*this);
update_active_witnesses();
update_active_committee_members();
update_worker_votes();

View file

@ -0,0 +1,4 @@
// #516 Special authorities
#ifndef HARDFORK_516_TIME
#define HARDFORK_516_TIME (fc::time_point_sec( 1455127200 ))
#endif

View file

@ -204,6 +204,16 @@ namespace graphene { namespace chain {
* Vesting balance which receives cashback_reward deposits.
*/
optional<vesting_balance_id_type> cashback_vb;
special_authority owner_special_authority = no_special_authority();
special_authority active_special_authority = no_special_authority();
bool has_special_authority()const
{
return (owner_special_authority.which() != special_authority::tag< no_special_authority >::value)
|| (active_special_authority.which() != special_authority::tag< no_special_authority >::value);
}
template<typename DB>
const vesting_balance_object& cashback_balance(const DB& db)const
{
@ -352,7 +362,9 @@ FC_REFLECT_DERIVED( graphene::chain::account_object,
(network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage)
(name)(owner)(active)(options)(statistics)(whitelisting_accounts)(blacklisting_accounts)
(whitelisting_accounts)(blacklisted_accounts)
(cashback_vb) )
(cashback_vb)
(owner_special_authority)(active_special_authority)
)
FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
(graphene::db::object),

View file

@ -143,7 +143,7 @@
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
#define GRAPHENE_CURRENT_DB_VERSION "GPH2.4"
#define GRAPHENE_CURRENT_DB_VERSION "GPH2.5"
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)

View file

@ -23,15 +23,18 @@
*/
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/ext.hpp>
#include <graphene/chain/protocol/special_authority.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/protocol/vote.hpp>
namespace graphene { namespace chain {
namespace graphene { namespace chain {
bool is_valid_name( const string& s );
bool is_cheap_name( const string& n );
/// These are the fields which can be updated by the active authority.
struct account_options
struct account_options
{
/// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-
/// validated account activities. This field is here to prevent confusion if the active authority has zero or
@ -61,7 +64,15 @@ namespace graphene { namespace chain {
*/
struct account_create_operation : public base_operation
{
struct fee_parameters_type {
struct ext
{
optional< void_t > null_ext;
optional< special_authority > owner_special_authority;
optional< special_authority > active_special_authority;
};
struct fee_parameters_type
{
uint64_t basic_fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
uint64_t premium_fee = 2000*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
@ -82,7 +93,7 @@ namespace graphene { namespace chain {
authority active;
account_options options;
extensions_type extensions;
extension< ext > extensions;
account_id_type fee_payer()const { return registrar; }
void validate()const;
@ -98,8 +109,16 @@ namespace graphene { namespace chain {
*/
struct account_update_operation : public base_operation
{
struct fee_parameters_type {
share_type fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
struct ext
{
optional< void_t > null_ext;
optional< special_authority > owner_special_authority;
optional< special_authority > active_special_authority;
};
struct fee_parameters_type
{
share_type fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
@ -114,17 +133,20 @@ namespace graphene { namespace chain {
/// New account options
optional<account_options> new_options;
extensions_type extensions;
extension< ext > extensions;
account_id_type fee_payer()const { return account; }
void validate()const;
share_type calculate_fee( const fee_parameters_type& k )const;
bool is_owner_update()const
{ return owner || extensions.value.owner_special_authority.valid(); }
void get_required_owner_authorities( flat_set<account_id_type>& a )const
{ if( owner ) a.insert( account ); }
{ if( is_owner_update() ) a.insert( account ); }
void get_required_active_authorities( flat_set<account_id_type>& a )const
{ if( !owner ) a.insert( account ); }
{ if( !is_owner_update() ) a.insert( account ); }
};
/**
@ -236,16 +258,19 @@ FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listi
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,
(no_listing)(white_listed)(black_listed)(white_and_black_listed))
FC_REFLECT(graphene::chain::account_create_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) )
FC_REFLECT( graphene::chain::account_create_operation,
(fee)(registrar)
(referrer)(referrer_percent)
(name)(owner)(active)(options)(extensions)
)
FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) )
FC_REFLECT( graphene::chain::account_update_operation,
(fee)(account)(owner)(active)(new_options)(extensions)
)
FC_REFLECT( graphene::chain::account_upgrade_operation,
FC_REFLECT( graphene::chain::account_upgrade_operation,
(fee)(account_to_upgrade)(upgrade_to_lifetime_member)(extensions) )
FC_REFLECT( graphene::chain::account_whitelist_operation, (fee)(authorizing_account)(account_to_list)(new_listing)(extensions))

View file

@ -0,0 +1,200 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fc/io/varint.hpp>
#include <fc/reflect/reflect.hpp>
namespace graphene { namespace chain {
template< typename T >
struct extension
{
extension() {}
T value;
};
template< typename T >
struct graphene_extension_pack_count_visitor
{
graphene_extension_pack_count_visitor( const T& v ) : value(v) {}
template<typename Member, class Class, Member (Class::*member)>
void operator()( const char* name )const
{
count += ((value.*member).valid()) ? 1 : 0;
}
const T& value;
mutable uint32_t count = 0;
};
template< typename Stream, typename T >
struct graphene_extension_pack_read_visitor
{
graphene_extension_pack_read_visitor( Stream& s, const T& v ) : stream(s), value(v) {}
template<typename Member, class Class, Member (Class::*member)>
void operator()( const char* name )const
{
if( (value.*member).valid() )
{
fc::raw::pack( stream, unsigned_int( which ) );
fc::raw::pack( stream, *(value.*member) );
}
++which;
}
Stream& stream;
const T& value;
mutable uint32_t which = 0;
};
template< typename Stream, class T >
void operator<<( Stream& stream, const graphene::chain::extension<T>& value )
{
graphene_extension_pack_count_visitor<T> count_vtor( value.value );
fc::reflector<T>::visit( count_vtor );
fc::raw::pack( stream, unsigned_int( count_vtor.count ) );
graphene_extension_pack_read_visitor<Stream,T> read_vtor( stream, value.value );
fc::reflector<T>::visit( read_vtor );
}
template< typename Stream, typename T >
struct graphene_extension_unpack_visitor
{
graphene_extension_unpack_visitor( Stream& s, T& v ) : stream(s), value(v)
{
unsigned_int c;
fc::raw::unpack( stream, c );
count_left = c.value;
maybe_read_next_which();
}
void maybe_read_next_which()const
{
if( count_left > 0 )
{
unsigned_int w;
fc::raw::unpack( stream, w );
next_which = w.value;
}
}
template< typename Member, class Class, Member (Class::*member)>
void operator()( const char* name )const
{
if( (count_left > 0) && (which == next_which) )
{
Member temp;
fc::raw::unpack( stream, temp );
(value.*member) = temp;
--count_left;
maybe_read_next_which();
}
else
(value.*member).reset();
++which;
}
mutable uint32_t which = 0;
mutable uint32_t next_which = 0;
mutable uint32_t count_left = 0;
Stream& stream;
T& value;
};
template< typename Stream, typename T >
void operator>>( Stream& s, graphene::chain::extension<T>& value )
{
graphene_extension_unpack_visitor<Stream, T> vtor( s, value.value );
fc::reflector<T>::visit( vtor );
FC_ASSERT( vtor.count_left == 0 ); // unrecognized extension throws here
}
} } // graphene::chain
namespace fc {
template< typename T >
struct graphene_extension_from_variant_visitor
{
graphene_extension_from_variant_visitor( const variant_object& v, T& val )
: vo( v ), value( val )
{
count_left = vo.size();
}
template<typename Member, class Class, Member (Class::*member)>
void operator()( const char* name )const
{
auto it = vo.find(name);
if( it != vo.end() )
{
from_variant( it->value(), (value.*member) );
assert( count_left > 0 ); // x.find(k) returns true for n distinct values of k only if x.size() >= n
--count_left;
}
}
const variant_object& vo;
T& value;
mutable uint32_t count_left = 0;
};
template< typename T >
void from_variant( const fc::variant& var, graphene::chain::extension<T>& value )
{
graphene_extension_from_variant_visitor<T> vtor( var.get_object(), value.value );
fc::reflector<T>::visit( vtor );
FC_ASSERT( vtor.count_left == 0 ); // unrecognized extension throws here
}
template< typename T >
struct graphene_extension_to_variant_visitor
{
graphene_extension_to_variant_visitor( const T& v ) : value(v) {}
template<typename Member, class Class, Member (Class::*member)>
void operator()( const char* name )const
{
if( (value.*member).valid() )
mvo[ name ] = (value.*member);
}
const T& value;
mutable mutable_variant_object mvo;
};
template< typename T >
void to_variant( const graphene::chain::extension<T>& value, fc::variant& var )
{
graphene_extension_to_variant_visitor<T> vtor( value.value );
fc::reflector<T>::visit( vtor );
var = vtor.mvo;
}
} // fc

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <fc/reflect/reflect.hpp>
namespace graphene { namespace chain {
struct no_special_authority {};
struct top_holders_special_authority
{
asset_id_type asset;
uint8_t num_top_holders = 1;
};
typedef static_variant<
no_special_authority,
top_holders_special_authority
> special_authority;
void validate_special_authority( const special_authority& auth );
} } // graphene::chain
FC_REFLECT( graphene::chain::no_special_authority, )
FC_REFLECT( graphene::chain::top_holders_special_authority, (asset)(num_top_holders) )
FC_REFLECT_TYPENAME( graphene::chain::special_authority )

View file

@ -152,7 +152,8 @@ namespace graphene { namespace chain {
impl_blinded_balance_object_type,
impl_chain_property_object_type,
impl_witness_schedule_object_type,
impl_budget_record_object_type
impl_budget_record_object_type,
impl_special_authority_object_type
};
//typedef fc::unsigned_int object_id_type;
@ -201,6 +202,7 @@ namespace graphene { namespace chain {
class chain_property_object;
class witness_schedule_object;
class budget_record_object;
class special_authority_object;
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
@ -218,6 +220,7 @@ namespace graphene { namespace chain {
typedef object_id< implementation_ids, impl_witness_schedule_object_type, witness_schedule_object> witness_schedule_id_type;
typedef object_id< implementation_ids, impl_budget_record_object_type, budget_record_object > budget_record_id_type;
typedef object_id< implementation_ids, impl_blinded_balance_object_type, blinded_balance_object > blinded_balance_id_type;
typedef object_id< implementation_ids, impl_special_authority_object_type, special_authority_object > special_authority_id_type;
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
typedef fc::ripemd160 block_id_type;
@ -347,6 +350,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_chain_property_object_type)
(impl_witness_schedule_object_type)
(impl_budget_record_object_type)
(impl_special_authority_object_type)
)
FC_REFLECT_TYPENAME( graphene::chain::share_type )
@ -375,6 +379,7 @@ FC_REFLECT_TYPENAME( graphene::chain::transaction_obj_id_type )
FC_REFLECT_TYPENAME( graphene::chain::block_summary_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::budget_record_id_type )
FC_REFLECT_TYPENAME( graphene::chain::special_authority_id_type )
FC_REFLECT( graphene::chain::void_t, )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags,

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/special_authority.hpp>
namespace graphene { namespace chain {
class database;
void evaluate_special_authority( const database& db, const special_authority& auth );
} } // graphene::chain

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
/**
* special_authority_object only exists to help with a specific indexing problem.
* We want to be able to iterate over all accounts that contain a special authority.
* However, accounts which have a special_authority are very rare. So rather
* than indexing account_object by the special_authority fields (requiring additional
* bookkeeping for every account), we instead maintain a special_authority_object
* pointing to each account which has special_authority (requiring additional
* bookkeeping only for every account which has special_authority).
*
* This class is an implementation detail.
*/
class special_authority_object : public graphene::db::abstract_object<special_authority_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_special_authority_object_type;
account_id_type account;
};
struct by_account;
typedef multi_index_container<
special_authority_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_account>, member< special_authority_object, account_id_type, &special_authority_object::account> >
>
> special_authority_multi_index_type;
typedef generic_index< special_authority_object, special_authority_multi_index_type > special_authority_index;
} } // graphene::chain
FC_REFLECT_DERIVED(
graphene::chain::special_authority_object,
(graphene::db::object),
(account)
)

View file

@ -36,6 +36,8 @@ struct vote_counter
template< typename Component >
void add( Component who, uint64_t votes )
{
if( votes == 0 )
return;
assert( votes <= last_votes );
last_votes = votes;
if( bitshift == -1 )

View file

@ -186,6 +186,10 @@ void account_create_operation::validate()const
FC_ASSERT( !owner.is_impossible(), "cannot create an account with an imposible owner authority threshold" );
FC_ASSERT( !active.is_impossible(), "cannot create an account with an imposible active authority threshold" );
options.validate();
if( extensions.value.owner_special_authority.valid() )
validate_special_authority( *extensions.value.owner_special_authority );
if( extensions.value.active_special_authority.valid() )
validate_special_authority( *extensions.value.active_special_authority );
}
@ -204,7 +208,17 @@ void account_update_operation::validate()const
FC_ASSERT( account != GRAPHENE_TEMP_ACCOUNT );
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( account != account_id_type() );
FC_ASSERT( owner || active || new_options );
bool has_action = (
owner.valid()
|| active.valid()
|| new_options.valid()
|| extensions.value.owner_special_authority.valid()
|| extensions.value.active_special_authority.valid()
);
FC_ASSERT( has_action );
if( owner )
{
FC_ASSERT( owner->num_auths() != 0 );
@ -220,9 +234,12 @@ void account_update_operation::validate()const
if( new_options )
new_options->validate();
if( extensions.value.owner_special_authority.valid() )
validate_special_authority( *extensions.value.owner_special_authority );
if( extensions.value.active_special_authority.valid() )
validate_special_authority( *extensions.value.active_special_authority );
}
share_type account_upgrade_operation::calculate_fee(const fee_parameters_type& k) const
{
if( upgrade_to_lifetime_member )

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/protocol/special_authority.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
struct special_authority_validate_visitor
{
typedef void result_type;
void operator()( const no_special_authority& a ) {}
void operator()( const top_holders_special_authority& a )
{
FC_ASSERT( a.num_top_holders > 0 );
}
};
void validate_special_authority( const special_authority& a )
{
special_authority_validate_visitor vtor;
a.visit( vtor );
}
struct special_authority_evaluate_visitor
{
typedef void result_type;
special_authority_evaluate_visitor( const database& d ) : db(d) {}
void operator()( const no_special_authority& a ) {}
void operator()( const top_holders_special_authority& a )
{
a.asset(db); // require asset to exist
}
const database& db;
};
void evaluate_special_authority( const database& db, const special_authority& a )
{
special_authority_evaluate_visitor vtor( db );
a.visit( vtor );
}
} } // graphene::chain

View file

@ -26,6 +26,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
@ -1332,4 +1333,154 @@ BOOST_AUTO_TEST_CASE(zero_second_vbo)
// TODO: Write linear VBO tests
BOOST_AUTO_TEST_CASE( top_n_special )
{
ACTORS( (alice)(bob)(chloe)(dan)(izzy)(stan) );
generate_blocks( HARDFORK_516_TIME );
try
{
{
//
// Izzy (issuer)
// Stan (special authority)
// Alice, Bob, Chloe, Dan (ABCD)
//
asset_id_type topn_id = create_user_issued_asset( "TOPN", izzy_id(db), 0 ).id;
authority stan_owner_auth = stan_id(db).owner;
authority stan_active_auth = stan_id(db).active;
// set SA, wait for maint interval
// TODO: account_create_operation
// TODO: multiple accounts with different n for same asset
{
top_holders_special_authority top2, top3;
top2.num_top_holders = 2;
top2.asset = topn_id;
top3.num_top_holders = 3;
top3.asset = topn_id;
account_update_operation op;
op.account = stan_id;
op.extensions.value.active_special_authority = top3;
op.extensions.value.owner_special_authority = top2;
signed_transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
sign( tx, stan_private_key );
PUSH_TX( db, tx );
// TODO: Check special_authority is properly set
// TODO: Do it in steps
}
// wait for maint interval
// make sure we don't have any authority as account hasn't gotten distributed yet
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK( stan_id(db).owner == stan_owner_auth );
BOOST_CHECK( stan_id(db).active == stan_active_auth );
// issue some to Alice, make sure she gets control of Stan
// we need to set_expiration() before issue_uia() because the latter doens't call it #11
set_expiration( db, trx ); // #11
issue_uia( alice_id, asset( 1000, topn_id ) );
BOOST_CHECK( stan_id(db).owner == stan_owner_auth );
BOOST_CHECK( stan_id(db).active == stan_active_auth );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
/* NOTE - this was an old check from an earlier implementation that only allowed SA for LTM's
// no boost yet, we need to upgrade to LTM before mechanics apply to Stan
BOOST_CHECK( stan_id(db).owner == stan_owner_auth );
BOOST_CHECK( stan_id(db).active == stan_active_auth );
set_expiration( db, trx ); // #11
upgrade_to_lifetime_member(stan_id);
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
*/
BOOST_CHECK( stan_id(db).owner == authority( 501, alice_id, 1000 ) );
BOOST_CHECK( stan_id(db).active == authority( 501, alice_id, 1000 ) );
// give asset to Stan, make sure owner doesn't change at all
set_expiration( db, trx ); // #11
transfer( alice_id, stan_id, asset( 1000, topn_id ) );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK( stan_id(db).owner == authority( 501, alice_id, 1000 ) );
BOOST_CHECK( stan_id(db).active == authority( 501, alice_id, 1000 ) );
set_expiration( db, trx ); // #11
issue_uia( chloe_id, asset( 131000, topn_id ) );
// now Chloe has 131,000 and Stan has 1k. Make sure change occurs at next maintenance interval.
// NB, 131072 is a power of 2; the number 131000 was chosen so that we need a bitshift, but
// if we put the 1000 from Stan's balance back into play, we need a different bitshift.
// we use Chloe so she can be displaced by Bob later (showing the tiebreaking logic).
// Check Alice is still in control, because we're deferred to next maintenance interval
BOOST_CHECK( stan_id(db).owner == authority( 501, alice_id, 1000 ) );
BOOST_CHECK( stan_id(db).active == authority( 501, alice_id, 1000 ) );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK( stan_id(db).owner == authority( 32751, chloe_id, 65500 ) );
BOOST_CHECK( stan_id(db).active == authority( 32751, chloe_id, 65500 ) );
// put Alice's stake back in play
set_expiration( db, trx ); // #11
transfer( stan_id, alice_id, asset( 1000, topn_id ) );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK( stan_id(db).owner == authority( 33001, alice_id, 500, chloe_id, 65500 ) );
BOOST_CHECK( stan_id(db).active == authority( 33001, alice_id, 500, chloe_id, 65500 ) );
// issue 200,000 to Dan to cause another bitshift.
set_expiration( db, trx ); // #11
issue_uia( dan_id, asset( 200000, topn_id ) );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// 200000 Dan
// 131000 Chloe
// 1000 Alice
BOOST_CHECK( stan_id(db).owner == authority( 41376, chloe_id, 32750, dan_id, 50000 ) );
BOOST_CHECK( stan_id(db).active == authority( 41501, alice_id, 250, chloe_id, 32750, dan_id, 50000 ) );
// have Alice send all but 1 back to Stan, verify that we clamp Alice at one vote
set_expiration( db, trx ); // #11
transfer( alice_id, stan_id, asset( 999, topn_id ) );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK( stan_id(db).owner == authority( 41376, chloe_id, 32750, dan_id, 50000 ) );
BOOST_CHECK( stan_id(db).active == authority( 41376, alice_id, 1, chloe_id, 32750, dan_id, 50000 ) );
// send 131k to Bob so he's tied with Chloe, verify he displaces Chloe in top2
set_expiration( db, trx ); // #11
issue_uia( bob_id, asset( 131000, topn_id ) );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK( stan_id(db).owner == authority( 41376, bob_id, 32750, dan_id, 50000 ) );
BOOST_CHECK( stan_id(db).active == authority( 57751, bob_id, 32750, chloe_id, 32750, dan_id, 50000 ) );
// TODO more rounding checks
}
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_SUITE_END()