Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions libraries/chain/account_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,4 +424,55 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator:
return {};
} FC_RETHROW_EXCEPTIONS( error, "Unable to upgrade account '${a}'", ("a",o.account_to_upgrade(db()).name) ) }

void_result account_update_votes_evaluator::do_evaluate(const account_update_votes_operation& o)
{ try {
database& d = db();
FC_ASSERT(d.head_block_time() >= HARDFORK_BSIP_47_TIME, "Not allowed until BSIP47 HARDFORK"); // can remove after HF
account = &o.account(d);
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }

void_result account_update_votes_evaluator::do_apply(const account_update_votes_operation& o)
{ try {
database& d = db();
const auto& chain_parameters = d.get_global_properties().parameters;

d.modify(*account, [o, chain_parameters](account_object& a) {

if(o.voting_account.valid())
a.options.voting_account = *o.voting_account;

if(o.committee_voting_account.valid() || o.witness_voting_account.valid() || o.worker_voting_account.valid())
a.options.voting_account = GRAPHENE_PROXY_PER_CATEGORY_ACCOUNT;

if(o.worker_voting_account.valid())
a.options.extensions.value.worker_voting_account = *o.worker_voting_account;
if(o.witness_voting_account.valid())
a.options.extensions.value.witness_voting_account = *o.witness_voting_account;
if(o.committee_voting_account.valid())
a.options.extensions.value.committee_voting_account = *o.committee_voting_account;

if(o.num_witness.valid())
a.options.num_witness = std::min(*o.num_witness, chain_parameters.maximum_witness_count);
if(o.num_committee.valid())
a.options.num_committee = std::min(*o.num_committee, chain_parameters.maximum_committee_count);

auto current_votes = a.options.votes;
if(o.votes_to_add.valid()) {
for(auto const& add: *o.votes_to_add) {
current_votes.insert(add);
a.options.votes = current_votes;
}
}
if(o.votes_to_remove.valid()) {
for (auto const &remove: *o.votes_to_remove) {
current_votes.erase(remove);
a.options.votes = current_votes;
}
}
});
return void_result();

} FC_CAPTURE_AND_RETHROW( (o) ) }

} } // graphene::chain
1 change: 1 addition & 0 deletions libraries/chain/db_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ void database::initialize_evaluators()
register_evaluator<custom_authority_create_evaluator>();
register_evaluator<custom_authority_update_evaluator>();
register_evaluator<custom_authority_delete_evaluator>();
register_evaluator<account_update_votes_evaluator>();
}

void database::initialize_indexes()
Expand Down
55 changes: 43 additions & 12 deletions libraries/chain/db_maint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1110,28 +1110,59 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
d._total_voting_stake = 0;
}

void fill_buffer(flat_set<vote_id_type> votes, uint64_t stake, optional<vote_id_type> type = {})
{
for (vote_id_type id : votes) {
if (!type.valid() || (type.valid() && id.type() == type->type())) {
uint32_t offset = id.instance();
// if they somehow managed to specify an illegal offset, ignore it.
if (offset < d._vote_tally_buffer.size())
d._vote_tally_buffer[offset] += stake;
}
}
}

void operator()( const account_object& stake_account, const account_statistics_object& stats )
{
if( props.parameters.count_non_member_votes || stake_account.is_member(d.head_block_time()) )
{
// There may be a difference between the account whose stake is voting and the one specifying opinions.
// Usually they're the same, but if the stake account has specified a voting_account, that account is the one
// specifying the opinions.
const account_object& opinion_account =
(stake_account.options.voting_account ==
GRAPHENE_PROXY_TO_SELF_ACCOUNT)? stake_account
: d.get(stake_account.options.voting_account);
// Furthermore after BSIP47 users can delegate opinions for each referendum category
// If the stake account has specified a voting_account, that account is the one specifying the opinions.
// If the stake account has specified a committee_voting_account, witness_voting_account or
// worker_voting_account, those will be the accounts voting for the corresponding referendum category.
// A user specifying 1 or 2 category voting accounts will delegate opinion only for specified referendums
// while can express own opinions in the others.

const auto& dgpo = d.get_dynamic_global_properties();
const auto& opinion_account = (stake_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT) ?
stake_account : d.get(stake_account.options.voting_account);

uint64_t voting_stake = stats.total_core_in_orders.value
+ (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0)
+ stats.core_in_balance.value;

for( vote_id_type id : opinion_account.options.votes )
{
uint32_t offset = id.instance();
// if they somehow managed to specify an illegal offset, ignore it.
if( offset < d._vote_tally_buffer.size() )
d._vote_tally_buffer[offset] += voting_stake;
if(dgpo.next_maintenance_time >= HARDFORK_BSIP_47_TIME &&
stake_account.options.voting_account == GRAPHENE_PROXY_PER_CATEGORY_ACCOUNT) {
const auto& extensions = stake_account.options.extensions.value;
if (extensions.committee_voting_account.valid()) {
auto committee_voting_account = *extensions.committee_voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT ?
stake_account : d.get(*extensions.committee_voting_account);
fill_buffer(committee_voting_account.options.votes, voting_stake, vote_id_type::committee);
}
if (extensions.witness_voting_account.valid()) {
auto witness_voting_account = *extensions.witness_voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT ?
stake_account : d.get(*extensions.witness_voting_account);
fill_buffer(witness_voting_account.options.votes, voting_stake, vote_id_type::witness);
}
if (extensions.worker_voting_account.valid()) {
auto worker_voting_account = *extensions.worker_voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT ?
stake_account : d.get(*extensions.worker_voting_account);
fill_buffer(worker_voting_account.options.votes, voting_stake, vote_id_type::worker);
}
}
else {
fill_buffer(opinion_account.options.votes, voting_stake);
}

if( opinion_account.options.num_witness <= props.parameters.maximum_witness_count )
Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/db_notify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ struct get_impacted_account_visitor
{
_impacted.insert( op.fee_payer() ); // account
}
void operator()( const account_update_votes_operation& op )
{
_impacted.insert( op.fee_payer() );
}
};

} // namespace detail
Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/hardfork.d/BSIP_47.hf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// BSIP47 - Vote Proxies for Different Referendum Categories and explicit voting operation
#ifndef HARDFORK_BSIP_47_TIME
#define HARDFORK_BSIP_47_TIME (fc::time_point_sec( 1600000000 ) ) // Sep 2020
#endif
11 changes: 11 additions & 0 deletions libraries/chain/include/graphene/chain/account_evaluator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,15 @@ class account_whitelist_evaluator : public evaluator<account_whitelist_evaluator
const account_object* listed_account;
};

class account_update_votes_evaluator: public evaluator<account_update_votes_evaluator>
{
public:
typedef account_update_votes_operation operation_type;

void_result do_evaluate( const account_update_votes_operation& o);
void_result do_apply( const account_update_votes_operation& o);

const account_object* account;
};

} } // graphene::chain
3 changes: 3 additions & 0 deletions libraries/chain/proposal_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ struct proposal_operation_hardfork_visitor
void operator()(const graphene::chain::custom_authority_delete_operation&) const {
FC_ASSERT( HARDFORK_BSIP_40_PASSED(block_time), "Not allowed until hardfork BSIP 40" );
}
void operator()(const graphene::chain::account_update_votes_operation &op) const {
FC_ASSERT( block_time >= HARDFORK_BSIP_47_TIME, "Not allowed until hardfork BSIP 47" );
}
// loop and self visit in proposals
void operator()(const graphene::chain::proposal_create_operation &v) const {
bool already_contains_proposal_update = false;
Expand Down
7 changes: 7 additions & 0 deletions libraries/protocol/account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ void account_transfer_operation::validate()const
FC_ASSERT( fee.amount >= 0 );
}

void account_update_votes_operation::validate()const
{
FC_ASSERT( fee.amount >= 0 );
}

} } // graphene::protocol

GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_options )
Expand All @@ -279,8 +284,10 @@ GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_votes_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_votes_operation )
63 changes: 62 additions & 1 deletion libraries/protocol/include/graphene/protocol/account.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@
#include <graphene/protocol/vote.hpp>

namespace graphene { namespace protocol {
struct additional_account_options
{
optional<account_id_type> committee_voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;
optional<account_id_type> witness_voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;
optional<account_id_type> worker_voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;
};
typedef extension<additional_account_options> additional_account_options_t;

bool is_valid_name( const string& s );
bool is_cheap_name( const string& n );
Expand All @@ -56,7 +63,7 @@ namespace graphene { namespace protocol {
/// This is the list of vote IDs this account votes for. The weight of these votes is determined by this
/// account's balance of core asset.
flat_set<vote_id_type> votes;
extensions_type extensions;
additional_account_options_t extensions;

/// Whether this account is voting
inline bool is_voting() const
Expand Down Expand Up @@ -268,8 +275,53 @@ namespace graphene { namespace protocol {
void validate()const;
};

/*
* @brief explicit operation for voting
* @ingroup operations
*
* Vote for the 3 referendum categories(committee, witness, worker), the number of witnesses
* and committee members the blockchain can have, update your proxy.
*
* Additionally the operation will allow the user to select specific proxies for each of the 3 referendum categories.
*/
struct account_update_votes_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };

asset fee;

/// The account to update
account_id_type account;

/// Votes to add or remove
optional<flat_set<vote_id_type>> votes_to_add;
optional<flat_set<vote_id_type>> votes_to_remove;

// A new voting account
optional<account_id_type> voting_account;

// Voting accounts by referendum category
optional<account_id_type> committee_voting_account;
optional<account_id_type> witness_voting_account;
optional<account_id_type> worker_voting_account;

// A new number of witness
optional<uint16_t> num_witness;

// A new number of committee member
optional<uint16_t> num_committee;

// For future extensions
extensions_type extensions;

account_id_type fee_payer()const { return account; }
void validate()const;
};

} } // graphene::protocol

FC_REFLECT( graphene::protocol::additional_account_options,
(committee_voting_account)(witness_voting_account)(worker_voting_account))
FC_REFLECT(graphene::protocol::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions))
FC_REFLECT_ENUM( graphene::protocol::account_whitelist_operation::account_listing,
(no_listing)(white_listed)(black_listed)(white_and_black_listed))
Expand Down Expand Up @@ -298,17 +350,26 @@ FC_REFLECT( graphene::protocol::account_whitelist_operation::fee_parameters_type
FC_REFLECT( graphene::protocol::account_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::protocol::account_upgrade_operation::fee_parameters_type, (membership_annual_fee)(membership_lifetime_fee) )
FC_REFLECT( graphene::protocol::account_transfer_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::protocol::account_update_votes_operation::fee_parameters_type, (fee) )

FC_REFLECT( graphene::protocol::account_transfer_operation, (fee)(account_id)(new_owner)(extensions) )
FC_REFLECT( graphene::protocol::account_update_votes_operation, (fee)(account)(votes_to_add)
(votes_to_remove)(voting_account)
(committee_voting_account)(witness_voting_account)
(worker_voting_account)(num_witness)(num_committee)
(extensions))

GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::additional_account_options )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_options )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_votes_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_votes_operation )
2 changes: 2 additions & 0 deletions libraries/protocol/include/graphene/protocol/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@
#define GRAPHENE_TEMP_ACCOUNT (graphene::protocol::account_id_type(4))
/// Represents the canonical account for specifying you will vote directly (as opposed to a proxy)
#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::protocol::account_id_type(5))
/// Represents the canonical account for specifying you will vote per referendum category
#define GRAPHENE_PROXY_PER_CATEGORY_ACCOUNT (graphene::protocol::account_id_type(6))
/// Sentinel value used in the scheduler.
#define GRAPHENE_NULL_WITNESS (graphene::protocol::witness_id_type(0))
///@}
Expand Down
3 changes: 2 additions & 1 deletion libraries/protocol/include/graphene/protocol/operations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,11 @@ namespace graphene { namespace protocol {
htlc_redeemed_operation, // VIRTUAL
htlc_extend_operation,
htlc_refund_operation, // VIRTUAL
account_update_votes_operation,
custom_authority_create_operation,
custom_authority_update_operation,
custom_authority_delete_operation
> operation;
> operation;

/// @} // operations group

Expand Down
Loading