Compare commits
37 commits
master
...
feature/SO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8e4a9a160 | ||
|
|
af0e0e4d30 | ||
|
|
388d00b310 | ||
|
|
bdfbf5c87b | ||
|
|
54599cc22b | ||
|
|
896b4df417 | ||
|
|
659bfcb7cf | ||
|
|
d06b2361ba | ||
|
|
7b6562ec85 | ||
|
|
3b167ffd03 | ||
|
|
3e4322b08d | ||
|
|
8bc204c69b | ||
|
|
e4bf00e349 | ||
|
|
5ecbba8717 | ||
|
|
4d1bec05bc | ||
|
|
ecfcd8eda1 | ||
|
|
203ee89218 | ||
|
|
e0ff813879 | ||
|
|
53666a9431 | ||
|
|
49564af60a | ||
|
|
457c8b9d9f | ||
|
|
44f185e0c2 | ||
|
|
3a65102e56 | ||
|
|
e2c579bb02 | ||
|
|
4d0a5b683f | ||
|
|
17dfc6af64 | ||
|
|
3a4445b20c | ||
|
|
723c802006 | ||
|
|
0a5d694568 | ||
|
|
6efbf84339 | ||
|
|
5dcb55c992 | ||
|
|
ba77a33711 | ||
|
|
52e9c2a5a6 | ||
|
|
38ec553773 | ||
|
|
7d6b2156e4 | ||
|
|
d36168b71e | ||
|
|
988a1771ed |
32 changed files with 1131 additions and 25 deletions
|
|
@ -19,10 +19,10 @@ build:
|
|||
- tests/
|
||||
tags:
|
||||
- builder
|
||||
|
||||
|
||||
test:
|
||||
stage: test
|
||||
dependencies:
|
||||
dependencies:
|
||||
- build
|
||||
script:
|
||||
- ./tests/betting_test
|
||||
|
|
@ -30,7 +30,7 @@ test:
|
|||
- ./tests/cli_test
|
||||
tags:
|
||||
- builder
|
||||
|
||||
|
||||
code_quality:
|
||||
stage: test
|
||||
image: docker:stable
|
||||
|
|
|
|||
6
.gitmodules
vendored
6
.gitmodules
vendored
|
|
@ -3,6 +3,6 @@
|
|||
url = https://github.com/bitshares/bitshares-core.wiki.git
|
||||
ignore = dirty
|
||||
[submodule "libraries/fc"]
|
||||
path = libraries/fc
|
||||
url = https://github.com/PBSA/peerplays-fc.git
|
||||
ignore = dirty
|
||||
path = libraries/fc
|
||||
url = https://github.com/PBSA/peerplays-fc.git
|
||||
ignore = dirty
|
||||
|
|
|
|||
|
|
@ -438,7 +438,12 @@ namespace graphene { namespace app {
|
|||
} case balance_object_type:{
|
||||
/** these are free from any accounts */
|
||||
break;
|
||||
}
|
||||
} case son_object_type:{
|
||||
const auto& aobj = dynamic_cast<const son_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->son_account );
|
||||
break;
|
||||
}
|
||||
case sport_object_type:
|
||||
case event_group_object_type:
|
||||
case event_object_type:
|
||||
|
|
|
|||
|
|
@ -146,6 +146,12 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
fc::optional<committee_member_object> get_committee_member_by_account(account_id_type account)const;
|
||||
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
|
||||
// SON members
|
||||
vector<optional<son_object>> get_sons(const vector<son_id_type>& son_ids)const;
|
||||
fc::optional<son_object> get_son_by_account(account_id_type account)const;
|
||||
map<string, son_id_type> lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
uint64_t get_son_count()const;
|
||||
|
||||
// Votes
|
||||
vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;
|
||||
|
||||
|
|
@ -1682,6 +1688,81 @@ map<string, committee_member_id_type> database_api_impl::lookup_committee_member
|
|||
return committee_members_by_account_name;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// SON members //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
vector<optional<son_object>> database_api::get_sons(const vector<son_id_type>& son_ids)const
|
||||
{
|
||||
return my->get_sons( son_ids );
|
||||
}
|
||||
|
||||
vector<optional<son_object>> database_api_impl::get_sons(const vector<son_id_type>& son_ids)const
|
||||
{
|
||||
vector<optional<son_object>> result; result.reserve(son_ids.size());
|
||||
std::transform(son_ids.begin(), son_ids.end(), std::back_inserter(result),
|
||||
[this](son_id_type id) -> optional<son_object> {
|
||||
if(auto o = _db.find(id))
|
||||
return *o;
|
||||
return {};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
fc::optional<son_object> database_api::get_son_by_account(account_id_type account)const
|
||||
{
|
||||
return my->get_son_by_account( account );
|
||||
}
|
||||
|
||||
fc::optional<son_object> database_api_impl::get_son_by_account(account_id_type account) const
|
||||
{
|
||||
const auto& idx = _db.get_index_type<son_index>().indices().get<by_account>();
|
||||
auto itr = idx.find(account);
|
||||
if( itr != idx.end() )
|
||||
return *itr;
|
||||
return {};
|
||||
}
|
||||
|
||||
map<string, son_id_type> database_api::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const
|
||||
{
|
||||
return my->lookup_son_accounts( lower_bound_name, limit );
|
||||
}
|
||||
|
||||
map<string, son_id_type> database_api_impl::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const
|
||||
{
|
||||
FC_ASSERT( limit <= 1000 );
|
||||
const auto& sons_by_id = _db.get_index_type<son_index>().indices().get<by_id>();
|
||||
|
||||
// we want to order sons by account name, but that name is in the account object
|
||||
// so the son_index doesn't have a quick way to access it.
|
||||
// get all the names and look them all up, sort them, then figure out what
|
||||
// records to return. This could be optimized, but we expect the
|
||||
// number of witnesses to be few and the frequency of calls to be rare
|
||||
std::map<std::string, son_id_type> sons_by_account_name;
|
||||
for (const son_object& son : sons_by_id)
|
||||
if (auto account_iter = _db.find(son.son_account))
|
||||
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
|
||||
sons_by_account_name.insert(std::make_pair(account_iter->name, son.id));
|
||||
|
||||
auto end_iter = sons_by_account_name.begin();
|
||||
while (end_iter != sons_by_account_name.end() && limit--)
|
||||
++end_iter;
|
||||
sons_by_account_name.erase(end_iter, sons_by_account_name.end());
|
||||
return sons_by_account_name;
|
||||
}
|
||||
|
||||
uint64_t database_api::get_son_count()const
|
||||
{
|
||||
return my->get_son_count();
|
||||
}
|
||||
|
||||
uint64_t database_api_impl::get_son_count()const
|
||||
{
|
||||
return _db.get_index_type<son_index>().indices().size();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Votes //
|
||||
|
|
@ -1701,6 +1782,7 @@ vector<variant> database_api_impl::lookup_vote_ids( const vector<vote_id_type>&
|
|||
const auto& committee_idx = _db.get_index_type<committee_member_index>().indices().get<by_vote_id>();
|
||||
const auto& for_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_for>();
|
||||
const auto& against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();
|
||||
const auto& son_idx = _db.get_index_type<son_index>().indices().get<by_vote_id>();
|
||||
|
||||
vector<variant> result;
|
||||
result.reserve( votes.size() );
|
||||
|
|
@ -1743,6 +1825,16 @@ vector<variant> database_api_impl::lookup_vote_ids( const vector<vote_id_type>&
|
|||
}
|
||||
break;
|
||||
}
|
||||
case vote_id_type::son:
|
||||
{
|
||||
auto itr = son_idx.find( id );
|
||||
if( itr != son_idx.end() )
|
||||
result.emplace_back( variant( *itr, 1 ) );
|
||||
else
|
||||
result.emplace_back( variant() );
|
||||
break;
|
||||
}
|
||||
|
||||
case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings
|
||||
default:
|
||||
FC_CAPTURE_AND_THROW( fc::out_of_range_exception, (id) );
|
||||
|
|
|
|||
|
|
@ -298,6 +298,15 @@ struct get_impacted_account_visitor
|
|||
void operator()( const sweeps_vesting_claim_operation& op ) {
|
||||
_impacted.insert( op.account );
|
||||
}
|
||||
void operator()( const son_create_operation& op ){
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_update_operation& op ){
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_delete_operation& op ){
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
|
@ -568,6 +569,38 @@ class database_api
|
|||
*/
|
||||
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
|
||||
/////////////////
|
||||
// SON members //
|
||||
/////////////////
|
||||
|
||||
/**
|
||||
* @brief Get a list of SONs by ID
|
||||
* @param son_ids IDs of the SONs to retrieve
|
||||
* @return The SONs corresponding to the provided IDs
|
||||
*
|
||||
* This function has semantics identical to @ref get_objects
|
||||
*/
|
||||
vector<optional<son_object>> get_sons(const vector<son_id_type>& son_ids)const;
|
||||
|
||||
/**
|
||||
* @brief Get the SON owned by a given account
|
||||
* @param account The ID of the account whose SON should be retrieved
|
||||
* @return The SON object, or null if the account does not have a SON
|
||||
*/
|
||||
fc::optional<son_object> get_son_by_account(account_id_type account)const;
|
||||
|
||||
/**
|
||||
* @brief Get names and IDs for registered SONs
|
||||
* @param lower_bound_name Lower bound of the first name to return
|
||||
* @param limit Maximum number of results to return -- must not exceed 1000
|
||||
* @return Map of SON names to corresponding IDs
|
||||
*/
|
||||
map<string, son_id_type> lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
|
||||
/**
|
||||
* @brief Get the total number of SONs registered with the blockchain
|
||||
*/
|
||||
uint64_t get_son_count()const;
|
||||
|
||||
/// WORKERS
|
||||
|
||||
|
|
@ -775,6 +808,12 @@ FC_API(graphene::app::database_api,
|
|||
(get_committee_member_by_account)
|
||||
(lookup_committee_member_accounts)
|
||||
|
||||
// SON members
|
||||
(get_sons)
|
||||
(get_son_by_account)
|
||||
(lookup_son_accounts)
|
||||
(get_son_count)
|
||||
|
||||
// workers
|
||||
(get_workers_by_account)
|
||||
// Votes
|
||||
|
|
|
|||
|
|
@ -113,6 +113,8 @@ add_library( graphene_chain
|
|||
|
||||
affiliate_payout.cpp
|
||||
|
||||
son_evaluator.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
||||
|
|
|
|||
|
|
@ -49,13 +49,12 @@
|
|||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
|
||||
|
||||
#include <graphene/chain/sport_object.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
#include <graphene/chain/account_evaluator.hpp>
|
||||
#include <graphene/chain/asset_evaluator.hpp>
|
||||
|
|
@ -77,6 +76,7 @@
|
|||
#include <graphene/chain/event_evaluator.hpp>
|
||||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
#include <graphene/chain/tournament_evaluator.hpp>
|
||||
#include <graphene/chain/son_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -243,6 +243,9 @@ void database::initialize_evaluators()
|
|||
register_evaluator<lottery_reward_evaluator>();
|
||||
register_evaluator<lottery_end_evaluator>();
|
||||
register_evaluator<sweeps_vesting_claim_evaluator>();
|
||||
register_evaluator<create_son_evaluator>();
|
||||
register_evaluator<update_son_evaluator>();
|
||||
register_evaluator<delete_son_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -259,6 +262,7 @@ void database::initialize_indexes()
|
|||
acnt_index->add_secondary_index<account_referrer_index>();
|
||||
|
||||
add_index< primary_index<committee_member_index, 8> >(); // 256 members per chunk
|
||||
add_index< primary_index<son_index, 8> >(); // 256 sons per chunk
|
||||
add_index< primary_index<witness_index, 10> >(); // 1024 witnesses per chunk
|
||||
add_index< primary_index<limit_order_index > >();
|
||||
add_index< primary_index<call_order_index > >();
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ void database::update_active_witnesses()
|
|||
void database::update_active_committee_members()
|
||||
{ try {
|
||||
assert( _committee_count_histogram_buffer.size() > 0 );
|
||||
share_type stake_target = (_total_voting_stake-_witness_count_histogram_buffer[0]) / 2;
|
||||
share_type stake_target = (_total_voting_stake-_committee_count_histogram_buffer[0]) / 2;
|
||||
|
||||
/// accounts that vote for 0 or 1 witness do not get to express an opinion on
|
||||
/// the number of witnesses to have (they abstain and are non-voting accounts)
|
||||
|
|
@ -326,6 +326,96 @@ void database::update_active_committee_members()
|
|||
});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::update_active_sons()
|
||||
{ try {
|
||||
assert( _son_count_histogram_buffer.size() > 0 );
|
||||
share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2;
|
||||
|
||||
/// accounts that vote for 0 or 1 son do not get to express an opinion on
|
||||
/// the number of sons to have (they abstain and are non-voting accounts)
|
||||
|
||||
share_type stake_tally = 0;
|
||||
|
||||
size_t son_count = 0;
|
||||
if( stake_target > 0 )
|
||||
{
|
||||
while( (son_count < _son_count_histogram_buffer.size() - 1)
|
||||
&& (stake_tally <= stake_target) )
|
||||
{
|
||||
stake_tally += _son_count_histogram_buffer[++son_count];
|
||||
}
|
||||
}
|
||||
|
||||
const chain_property_object& cpo = get_chain_properties();
|
||||
auto sons = sort_votable_objects<son_index>(std::max(son_count*2+1, (size_t)cpo.immutable_parameters.min_son_count));
|
||||
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
|
||||
const auto& all_sons = get_index_type<son_index>().indices();
|
||||
|
||||
for( const son_object& son : all_sons )
|
||||
{
|
||||
modify( son, [&]( son_object& obj ){
|
||||
obj.total_votes = _vote_tally_buffer[son.vote_id];
|
||||
});
|
||||
}
|
||||
|
||||
// Update SON authority
|
||||
modify( get(GRAPHENE_SON_ACCOUNT_ID), [&]( account_object& a )
|
||||
{
|
||||
if( head_block_time() < HARDFORK_533_TIME )
|
||||
{
|
||||
uint64_t total_votes = 0;
|
||||
map<account_id_type, uint64_t> weights;
|
||||
a.active.weight_threshold = 0;
|
||||
a.active.clear();
|
||||
|
||||
for( const son_object& son : sons )
|
||||
{
|
||||
weights.emplace(son.son_account, _vote_tally_buffer[son.vote_id]);
|
||||
total_votes += _vote_tally_buffer[son.vote_id];
|
||||
}
|
||||
|
||||
// total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,
|
||||
// then I want to keep the most significant 16 bits of what's left.
|
||||
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
for( const auto& weight : weights )
|
||||
{
|
||||
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
|
||||
uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) );
|
||||
a.active.account_auths[weight.first] += votes;
|
||||
a.active.weight_threshold += votes;
|
||||
}
|
||||
|
||||
a.active.weight_threshold /= 2;
|
||||
a.active.weight_threshold += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
vote_counter vc;
|
||||
for( const son_object& son : sons )
|
||||
vc.add( son.son_account, std::max(_vote_tally_buffer[son.vote_id], UINT64_C(1)) );
|
||||
vc.finish( a.active );
|
||||
}
|
||||
} );
|
||||
|
||||
modify(gpo, [&]( global_property_object& gp ){
|
||||
gp.active_sons.clear();
|
||||
gp.active_sons.reserve(sons.size());
|
||||
std::transform(sons.begin(), sons.end(),
|
||||
std::inserter(gp.active_sons, gp.active_sons.end()),
|
||||
[](const son_object& s) {
|
||||
return s.id;
|
||||
});
|
||||
});
|
||||
|
||||
//const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
//modify(wso, [&](witness_schedule_object& _wso)
|
||||
//{
|
||||
// _wso.scheduler.update(gpo.active_witnesses);
|
||||
//});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
|
|
@ -1239,6 +1329,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
d._vote_tally_buffer.resize(props.next_available_vote_id);
|
||||
d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);
|
||||
d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
|
||||
d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count / 2 + 1);
|
||||
d._total_voting_stake = 0;
|
||||
|
||||
const vesting_balance_index& vesting_index = d.get_index_type<vesting_balance_index>();
|
||||
|
|
@ -1323,6 +1414,18 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
// same rationale as for witnesses
|
||||
d._committee_count_histogram_buffer[offset] += voting_stake;
|
||||
}
|
||||
if( opinion_account.options.num_son <= props.parameters.maximum_son_count )
|
||||
{
|
||||
uint16_t offset = std::min(size_t(opinion_account.options.num_son/2),
|
||||
d._son_count_histogram_buffer.size() - 1);
|
||||
// votes for a number greater than maximum_son_count
|
||||
// are turned into votes for maximum_son_count.
|
||||
//
|
||||
// in particular, this takes care of the case where a
|
||||
// member was voting for a high number, then the
|
||||
// parameter was lowered.
|
||||
d._son_count_histogram_buffer[offset] += voting_stake;
|
||||
}
|
||||
|
||||
d._total_voting_stake += voting_stake;
|
||||
}
|
||||
|
|
@ -1353,11 +1456,13 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
};
|
||||
clear_canary a(_witness_count_histogram_buffer),
|
||||
b(_committee_count_histogram_buffer),
|
||||
d(_son_count_histogram_buffer),
|
||||
c(_vote_tally_buffer);
|
||||
|
||||
update_top_n_authorities(*this);
|
||||
update_active_witnesses();
|
||||
update_active_committee_members();
|
||||
update_active_sons();
|
||||
update_worker_votes();
|
||||
|
||||
modify(gpo, [this](global_property_object& p) {
|
||||
|
|
|
|||
|
|
@ -285,6 +285,15 @@ struct get_impacted_account_visitor
|
|||
void operator()( const sweeps_vesting_claim_operation& op ) {
|
||||
_impacted.insert( op.account );
|
||||
}
|
||||
void operator()( const son_create_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_update_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_delete_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
@ -373,6 +382,11 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
|
|||
} case balance_object_type:{
|
||||
/** these are free from any accounts */
|
||||
break;
|
||||
} case son_object_type:{
|
||||
const auto& aobj = dynamic_cast<const son_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->son_account );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
libraries/chain/hardfork.d/SON.hf
Normal file
5
libraries/chain/hardfork.d/SON.hf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// SON HARDFORK Monday, September 21, 2020 1:43:11 PM
|
||||
#ifndef HARDFORK_SON_TIME
|
||||
#include <ctime>
|
||||
#define HARDFORK_SON_TIME (fc::time_point_sec( time(NULL) - (60 * 60) ))
|
||||
#endif
|
||||
|
|
@ -90,8 +90,10 @@
|
|||
|
||||
#define GRAPHENE_DEFAULT_MIN_WITNESS_COUNT (11)
|
||||
#define GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT (11)
|
||||
#define GRAPHENE_DEFAULT_MIN_SON_COUNT (5)
|
||||
#define GRAPHENE_DEFAULT_MAX_WITNESSES (1001) // SHOULD BE ODD
|
||||
#define GRAPHENE_DEFAULT_MAX_COMMITTEE (1001) // SHOULD BE ODD
|
||||
#define GRAPHENE_DEFAULT_MAX_SONS (15)
|
||||
#define GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC (60*60*24*7*4) // Four weeks
|
||||
#define GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks
|
||||
#define GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT)
|
||||
|
|
@ -173,6 +175,8 @@
|
|||
#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::chain::account_id_type(5))
|
||||
///
|
||||
#define GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
|
||||
///
|
||||
#define GRAPHENE_SON_ACCOUNT_ID (graphene::chain::account_id_type(7))
|
||||
/// Sentinel value used in the scheduler.
|
||||
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
|
||||
///@}
|
||||
|
|
@ -227,8 +231,6 @@
|
|||
#define TOURNAMENT_MAX_WHITELIST_LENGTH 1000
|
||||
#define TOURNAMENT_MAX_START_TIME_IN_FUTURE (60*60*24*7*4) // 1 month
|
||||
#define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week
|
||||
#define GPOS_PERIOD (60*60*24*30*6) // 6 months
|
||||
#define GPOS_SUBPERIOD (60*60*24*30) // 1 month
|
||||
#define MIN_SON_MEMBER_COUNT 15
|
||||
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)
|
||||
|
|
|
|||
|
|
@ -519,6 +519,7 @@ namespace graphene { namespace chain {
|
|||
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
|
||||
void update_active_witnesses();
|
||||
void update_active_committee_members();
|
||||
void update_active_sons();
|
||||
void update_worker_votes();
|
||||
|
||||
template<class... Types>
|
||||
|
|
@ -556,6 +557,7 @@ namespace graphene { namespace chain {
|
|||
vector<uint64_t> _vote_tally_buffer;
|
||||
vector<uint64_t> _witness_count_histogram_buffer;
|
||||
vector<uint64_t> _committee_count_histogram_buffer;
|
||||
vector<uint64_t> _son_count_histogram_buffer;
|
||||
uint64_t _total_voting_stake;
|
||||
|
||||
flat_map<uint32_t,block_id_type> _checkpoints;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ namespace graphene { namespace chain {
|
|||
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
|
||||
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
|
||||
// n.b. witness scheduling is done by witness_schedule object
|
||||
|
||||
flat_set<son_id_type> active_sons; // updated once per maintenance interval
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -148,4 +150,5 @@ FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::obje
|
|||
(next_available_vote_id)
|
||||
(active_committee_members)
|
||||
(active_witnesses)
|
||||
(active_sons)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ struct immutable_chain_parameters
|
|||
{
|
||||
uint16_t min_committee_member_count = GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT;
|
||||
uint16_t min_witness_count = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;
|
||||
uint16_t min_son_count = GRAPHENE_DEFAULT_MIN_SON_COUNT;
|
||||
uint32_t num_special_accounts = 0;
|
||||
uint32_t num_special_assets = 0;
|
||||
};
|
||||
|
|
@ -44,6 +45,7 @@ struct immutable_chain_parameters
|
|||
FC_REFLECT( graphene::chain::immutable_chain_parameters,
|
||||
(min_committee_member_count)
|
||||
(min_witness_count)
|
||||
(min_son_count)
|
||||
(num_special_accounts)
|
||||
(num_special_assets)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ namespace graphene { namespace chain {
|
|||
/// The number of active committee members this account votes the blockchain should appoint
|
||||
/// Must not exceed the actual number of committee members voted for in @ref votes
|
||||
uint16_t num_committee = 0;
|
||||
/// The number of active son members this account votes the blockchain should appoint
|
||||
/// Must not exceed the actual number of son members voted for in @ref votes
|
||||
uint16_t num_son = 0;
|
||||
/// 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;
|
||||
|
|
|
|||
|
|
@ -37,10 +37,6 @@ namespace graphene { namespace chain {
|
|||
optional< uint16_t > betting_rake_fee_percentage;
|
||||
optional< flat_map<bet_multiplier_type, bet_multiplier_type> > permitted_betting_odds_increments;
|
||||
optional< uint16_t > live_betting_delay_time;
|
||||
/* gpos parameters */
|
||||
optional < uint32_t > gpos_period;
|
||||
optional < uint32_t > gpos_subperiod;
|
||||
optional < uint32_t > gpos_period_start;
|
||||
optional < uint16_t > son_count;
|
||||
optional< uint16_t > sweeps_distribution_percentage;
|
||||
optional< asset_id_type > sweeps_distribution_asset;
|
||||
|
|
@ -63,6 +59,7 @@ namespace graphene { namespace chain {
|
|||
uint8_t maximum_asset_feed_publishers = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS; ///< the maximum number of feed publishers for a given asset
|
||||
uint16_t maximum_witness_count = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses
|
||||
uint16_t maximum_committee_count = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active committee_members
|
||||
uint16_t maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS
|
||||
uint16_t maximum_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have
|
||||
uint16_t reserve_percent_of_fee = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of the network's allocation of a fee that is taken out of circulation
|
||||
uint16_t network_percent_of_fee = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
|
||||
|
|
@ -137,9 +134,6 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
|||
(betting_rake_fee_percentage)
|
||||
(permitted_betting_odds_increments)
|
||||
(live_betting_delay_time)
|
||||
(gpos_period)
|
||||
(gpos_subperiod)
|
||||
(gpos_period_start)
|
||||
(son_count)
|
||||
(sweeps_distribution_percentage)
|
||||
(sweeps_distribution_asset)
|
||||
|
|
@ -160,6 +154,7 @@ FC_REFLECT( graphene::chain::chain_parameters,
|
|||
(maximum_asset_feed_publishers)
|
||||
(maximum_witness_count)
|
||||
(maximum_committee_count)
|
||||
(maximum_son_count)
|
||||
(maximum_authority_membership)
|
||||
(reserve_percent_of_fee)
|
||||
(network_percent_of_fee)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include <graphene/chain/protocol/event.hpp>
|
||||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/protocol/son.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -135,7 +136,10 @@ namespace graphene { namespace chain {
|
|||
ticket_purchase_operation,
|
||||
lottery_reward_operation,
|
||||
lottery_end_operation,
|
||||
sweeps_vesting_claim_operation
|
||||
sweeps_vesting_claim_operation,
|
||||
son_create_operation,
|
||||
son_update_operation,
|
||||
son_delete_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
60
libraries/chain/include/graphene/chain/protocol/son.hpp
Normal file
60
libraries/chain/include/graphene/chain/protocol/son.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type owner_account;
|
||||
std::string url;
|
||||
vesting_balance_id_type deposit;
|
||||
public_key_type signing_key;
|
||||
vesting_balance_id_type pay_vb;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type owner_account;
|
||||
optional<std::string> new_url;
|
||||
optional<vesting_balance_id_type> new_deposit;
|
||||
optional<public_key_type> new_signing_key;
|
||||
optional<vesting_balance_id_type> new_pay_vb;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_delete_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type owner_account;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_create_operation, (fee)(owner_account)(url)(deposit)(signing_key)
|
||||
(pay_vb) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_update_operation, (fee)(son_id)(owner_account)(new_url)(new_deposit)
|
||||
(new_signing_key)(new_pay_vb) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_delete_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_delete_operation, (fee)(son_id)(owner_account) )
|
||||
|
|
@ -145,6 +145,7 @@ namespace graphene { namespace chain {
|
|||
betting_market_group_object_type,
|
||||
betting_market_object_type,
|
||||
bet_object_type,
|
||||
son_object_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -204,6 +205,7 @@ namespace graphene { namespace chain {
|
|||
class betting_market_group_object;
|
||||
class betting_market_object;
|
||||
class bet_object;
|
||||
class son_object;
|
||||
|
||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||
|
|
@ -230,6 +232,7 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
|
||||
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
|
||||
typedef object_id< protocol_ids, son_object_type, son_object> son_id_type;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -410,6 +413,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(betting_market_group_object_type)
|
||||
(betting_market_object_type)
|
||||
(bet_object_type)
|
||||
(son_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -479,6 +483,8 @@ FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_position_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ struct vote_id_type
|
|||
committee,
|
||||
witness,
|
||||
worker,
|
||||
son,
|
||||
VOTE_TYPE_COUNT
|
||||
};
|
||||
|
||||
|
|
@ -148,5 +149,5 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui
|
|||
|
||||
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) )
|
||||
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) )
|
||||
FC_REFLECT( graphene::chain::vote_id_type, (content) )
|
||||
|
|
|
|||
34
libraries/chain/include/graphene/chain/son_evaluator.hpp
Normal file
34
libraries/chain/include/graphene/chain/son_evaluator.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/son.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class create_son_evaluator : public evaluator<create_son_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_create_operation& o);
|
||||
object_id_type do_apply(const son_create_operation& o);
|
||||
};
|
||||
|
||||
class update_son_evaluator : public evaluator<update_son_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_update_operation& o);
|
||||
object_id_type do_apply(const son_update_operation& o);
|
||||
};
|
||||
|
||||
class delete_son_evaluator : public evaluator<delete_son_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_delete_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_delete_operation& o);
|
||||
void_result do_apply(const son_delete_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
49
libraries/chain/include/graphene/chain/son_object.hpp
Normal file
49
libraries/chain/include/graphene/chain/son_object.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_object
|
||||
* @brief tracks information about a SON account.
|
||||
* @ingroup object
|
||||
*/
|
||||
class son_object : public abstract_object<son_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_object_type;
|
||||
|
||||
account_id_type son_account;
|
||||
vote_id_type vote_id;
|
||||
uint64_t total_votes = 0;
|
||||
string url;
|
||||
vesting_balance_id_type deposit;
|
||||
public_key_type signing_key;
|
||||
vesting_balance_id_type pay_vb;
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
struct by_vote_id;
|
||||
using son_multi_index_type = multi_index_container<
|
||||
son_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_account>,
|
||||
member<son_object, account_id_type, &son_object::son_account>
|
||||
>,
|
||||
ordered_unique< tag<by_vote_id>,
|
||||
member<son_object, vote_id_type, &son_object::vote_id>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_index = generic_index<son_object, son_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object),
|
||||
(son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb) )
|
||||
|
|
@ -135,6 +135,18 @@ struct proposal_operation_hardfork_visitor
|
|||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_update_status_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_delete_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_delete_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
// loop and self visit in proposals
|
||||
void operator()(const proposal_create_operation &v) const {
|
||||
for (const op_wrapper &op : v.proposed_ops)
|
||||
|
|
|
|||
75
libraries/chain/son_evaluator.cpp
Normal file
75
libraries/chain/son_evaluator.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include <graphene/chain/son_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result create_son_evaluator::do_evaluate(const son_create_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT(db().get(op.owner_account).is_lifetime_member(), "Only Lifetime members may register a SON.");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
|
||||
{ try {
|
||||
vote_id_type vote_id;
|
||||
db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) {
|
||||
vote_id = get_next_vote_id(p, vote_id_type::son);
|
||||
});
|
||||
|
||||
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ){
|
||||
obj.son_account = op.owner_account;
|
||||
obj.vote_id = vote_id;
|
||||
obj.url = op.url;
|
||||
obj.deposit = op.deposit;
|
||||
obj.signing_key = op.signing_key;
|
||||
obj.pay_vb = op.pay_vb;
|
||||
});
|
||||
return new_son_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result update_son_evaluator::do_evaluate(const son_update_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
FC_ASSERT(db().get(op.son_id).son_account == op.owner_account);
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type update_son_evaluator::do_apply(const son_update_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](son_object &so) {
|
||||
if(op.new_url.valid()) so.url = *op.new_url;
|
||||
if(op.new_deposit.valid()) so.deposit = *op.new_deposit;
|
||||
if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key;
|
||||
if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb;
|
||||
});
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result delete_son_evaluator::do_evaluate(const son_delete_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON_HARDFORK"); // can be removed after HF date pass
|
||||
FC_ASSERT(db().get(op.son_id).son_account == op.owner_account);
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result delete_son_evaluator::do_apply(const son_delete_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
db().remove(*idx.find(op.son_id));
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -10,7 +10,7 @@ set(SOURCES node.cpp
|
|||
add_library( graphene_net ${SOURCES} ${HEADERS} )
|
||||
|
||||
target_link_libraries( graphene_net
|
||||
PUBLIC fc graphene_db )
|
||||
PUBLIC graphene_chain )
|
||||
target_include_directories( graphene_net
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../chain/include" "${CMAKE_CURRENT_BINARY_DIR}/../chain/include"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
file(GLOB HEADERS "include/graphene/peerplays_sidechain_history/*.hpp")
|
||||
file(GLOB HEADERS "include/graphene/peerplays_sidechain/*.hpp")
|
||||
|
||||
add_library( peerplays_sidechain
|
||||
peerplays_sidechain_plugin.cpp
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ struct wallet_data
|
|||
// incomplete account regs
|
||||
map<string, vector<string> > pending_account_registrations;
|
||||
map<string, string> pending_witness_registrations;
|
||||
map<string, string> pending_son_registrations;
|
||||
|
||||
key_label_index_type labeled_keys;
|
||||
blind_receipt_index_type blind_receipts;
|
||||
|
|
@ -1283,6 +1284,12 @@ class wallet_api
|
|||
*/
|
||||
map<string, committee_member_id_type> list_committee_members(const string& lowerbound, uint32_t limit);
|
||||
|
||||
/** Returns information about the given SON.
|
||||
* @param owner_account the name or id of the SON account owner, or the id of the SON
|
||||
* @returns the information about the SON stored in the block chain
|
||||
*/
|
||||
son_object get_son(string owner_account);
|
||||
|
||||
/** Returns information about the given witness.
|
||||
* @param owner_account the name or id of the witness account owner, or the id of the witness
|
||||
* @returns the information about the witness stored in the block chain
|
||||
|
|
@ -1295,6 +1302,63 @@ class wallet_api
|
|||
*/
|
||||
committee_member_object get_committee_member(string owner_account);
|
||||
|
||||
|
||||
/** Creates a SON object owned by the given account.
|
||||
*
|
||||
* An account can have at most one SON object.
|
||||
*
|
||||
* @param owner_account the name or id of the account which is creating the SON
|
||||
* @param url a URL to include in the SON record in the blockchain. Clients may
|
||||
* display this when showing a list of SONs. May be blank.
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction registering a SON
|
||||
*/
|
||||
signed_transaction create_son(string owner_account,
|
||||
string url,
|
||||
bool broadcast = false);
|
||||
|
||||
/**
|
||||
* Update a SON object owned by the given account.
|
||||
*
|
||||
* @param witness The name of the SON's owner account. Also accepts the ID of the owner account or the ID of the SON.
|
||||
* @param url Same as for create_son. The empty string makes it remain the same.
|
||||
* @param block_signing_key The new block signing public key. The empty string makes it remain the same.
|
||||
* @param broadcast true if you wish to broadcast the transaction.
|
||||
*/
|
||||
signed_transaction update_son(string owner_account,
|
||||
string url,
|
||||
string block_signing_key,
|
||||
bool broadcast = false);
|
||||
|
||||
|
||||
/** Deletes a SON object owned by the given account.
|
||||
*
|
||||
* An account can have at most one witness object.
|
||||
*
|
||||
* @param owner_account the name or id of the account which is creating the witness
|
||||
* @param url a URL to include in the witness record in the blockchain. Clients may
|
||||
* display this when showing a list of witnesses. May be blank.
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction registering a witness
|
||||
*/
|
||||
signed_transaction delete_son(string owner_account,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Lists all SONs in the blockchain.
|
||||
* This returns a list of all account names that own SON, and the associated SON id,
|
||||
* sorted by name. This lists SONs whether they are currently voted in or not.
|
||||
*
|
||||
* Use the \c lowerbound and limit parameters to page through the list. To retrieve all SONs,
|
||||
* start by setting \c lowerbound to the empty string \c "", and then each iteration, pass
|
||||
* the last SON name returned as the \c lowerbound for the next \c list_sons() call.
|
||||
*
|
||||
* @param lowerbound the name of the first SON to return. If the named SON does not exist,
|
||||
* the list will start at the SON that comes after \c lowerbound
|
||||
* @param limit the maximum number of SON to return (max: 1000)
|
||||
* @returns a list of SON mapping SON names to SON ids
|
||||
*/
|
||||
map<string, son_id_type> list_sons(const string& lowerbound, uint32_t limit);
|
||||
|
||||
/** Creates a witness object owned by the given account.
|
||||
*
|
||||
* An account can have at most one witness object.
|
||||
|
|
@ -1402,6 +1466,61 @@ class wallet_api
|
|||
bool approve,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Vote for a given SON.
|
||||
*
|
||||
* An account can publish a list of all SONs they approve of. This
|
||||
* command allows you to add or remove SONs from this list.
|
||||
* Each account's vote is weighted according to the number of shares of the
|
||||
* core asset owned by that account at the time the votes are tallied.
|
||||
*
|
||||
* @note you cannot vote against a SON, you can only vote for the SON
|
||||
* or not vote for the SON.
|
||||
*
|
||||
* @param voting_account the name or id of the account who is voting with their shares
|
||||
* @param son the name or id of the SONs' owner account
|
||||
* @param approve true if you wish to vote in favor of that SON, false to
|
||||
* remove your vote in favor of that SON
|
||||
* @param broadcast true if you wish to broadcast the transaction
|
||||
* @return the signed transaction changing your vote for the given SON
|
||||
*/
|
||||
signed_transaction vote_for_son(string voting_account,
|
||||
string son,
|
||||
bool approve,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Change your SON votes.
|
||||
*
|
||||
* An account can publish a list of all SONs they approve of.
|
||||
* Each account's vote is weighted according to the number of shares of the
|
||||
* core asset owned by that account at the time the votes are tallied.
|
||||
* This command allows you to add or remove one or more SON from this list
|
||||
* in one call. When you are changing your vote on several SONs, this
|
||||
* may be easier than multiple `vote_for_sons` and
|
||||
* `set_desired_witness_and_committee_member_count` calls.
|
||||
*
|
||||
* @note you cannot vote against a SON, you can only vote for the SON
|
||||
* or not vote for the SON.
|
||||
*
|
||||
* @param voting_account the name or id of the account who is voting with their shares
|
||||
* @param sons_to_approve the names or ids of the sons owner accounts you wish
|
||||
* to approve (these will be added to the list of sons
|
||||
* you currently approve). This list can be empty.
|
||||
* @param sons_to_reject the names or ids of the SONs owner accounts you wish
|
||||
* to reject (these will be removed from the list of SONs
|
||||
* you currently approve). This list can be empty.
|
||||
* @param desired_number_of_sons the number of SONs you believe the network
|
||||
* should have. You must vote for at least this many
|
||||
* SONs. You can set this to 0 to abstain from
|
||||
* voting on the number of SONNs.
|
||||
* @param broadcast true if you wish to broadcast the transaction
|
||||
* @return the signed transaction changing your vote for the given witnesses
|
||||
*/
|
||||
signed_transaction update_son_votes(string voting_account,
|
||||
std::vector<std::string> sons_to_approve,
|
||||
std::vector<std::string> sons_to_reject,
|
||||
uint16_t desired_number_of_son,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Vote for a given witness.
|
||||
*
|
||||
* An account can publish a list of all witnesses they approve of. This
|
||||
|
|
@ -1863,7 +1982,7 @@ FC_REFLECT( graphene::wallet::wallet_data,
|
|||
(my_accounts)
|
||||
(cipher_keys)
|
||||
(extra_keys)
|
||||
(pending_account_registrations)(pending_witness_registrations)
|
||||
(pending_account_registrations)(pending_witness_registrations)(pending_son_registrations)
|
||||
(labeled_keys)
|
||||
(blind_receipts)
|
||||
(committed_game_moves)
|
||||
|
|
@ -1969,10 +2088,15 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(settle_asset)
|
||||
(whitelist_account)
|
||||
(create_committee_member)
|
||||
(get_son)
|
||||
(get_witness)
|
||||
(get_committee_member)
|
||||
(list_witnesses)
|
||||
(list_committee_members)
|
||||
(create_son)
|
||||
(update_son)
|
||||
(delete_son)
|
||||
(list_sons)
|
||||
(create_witness)
|
||||
(update_witness)
|
||||
(create_worker)
|
||||
|
|
@ -1980,6 +2104,8 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(get_vesting_balances)
|
||||
(withdraw_vesting)
|
||||
(vote_for_committee_member)
|
||||
(vote_for_son)
|
||||
(update_son_votes)
|
||||
(vote_for_witness)
|
||||
(update_witness_votes)
|
||||
(set_voting_proxy)
|
||||
|
|
|
|||
|
|
@ -293,6 +293,23 @@ private:
|
|||
_wallet.pending_account_registrations.erase( it );
|
||||
}
|
||||
|
||||
// after a son registration succeeds, this saves the private key in the wallet permanently
|
||||
//
|
||||
void claim_registered_son(const std::string& son_name)
|
||||
{
|
||||
auto iter = _wallet.pending_son_registrations.find(son_name);
|
||||
FC_ASSERT(iter != _wallet.pending_son_registrations.end());
|
||||
std::string wif_key = iter->second;
|
||||
|
||||
// get the list key id this key is registered with in the chain
|
||||
fc::optional<fc::ecc::private_key> son_private_key = wif_to_key(wif_key);
|
||||
FC_ASSERT(son_private_key);
|
||||
|
||||
auto pub_key = son_private_key->get_public_key();
|
||||
_keys[pub_key] = wif_key;
|
||||
_wallet.pending_son_registrations.erase(iter);
|
||||
}
|
||||
|
||||
// after a witness registration succeeds, this saves the private key in the wallet permanently
|
||||
//
|
||||
void claim_registered_witness(const std::string& witness_name)
|
||||
|
|
@ -354,6 +371,24 @@ private:
|
|||
claim_registered_witness(optional_account->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_wallet.pending_son_registrations.empty())
|
||||
{
|
||||
// make a vector of the owner accounts for sons pending registration
|
||||
std::vector<string> pending_son_names = boost::copy_range<std::vector<string> >(boost::adaptors::keys(_wallet.pending_son_registrations));
|
||||
|
||||
// look up the owners on the blockchain
|
||||
std::vector<fc::optional<graphene::chain::account_object>> owner_account_objects = _remote_db->lookup_account_names(pending_son_names);
|
||||
|
||||
// if any of them have registered sons, claim them
|
||||
for( const fc::optional<graphene::chain::account_object>& optional_account : owner_account_objects )
|
||||
if (optional_account)
|
||||
{
|
||||
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(optional_account->id);
|
||||
if (son_obj)
|
||||
claim_registered_son(optional_account->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return true if any of my_accounts are players in this tournament
|
||||
|
|
@ -666,6 +701,7 @@ public:
|
|||
result["participation"] = (100*dynamic_props.recent_slots_filled.popcount()) / 128.0;
|
||||
result["active_witnesses"] = fc::variant(global_props.active_witnesses, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result["active_committee_members"] = fc::variant(global_props.active_committee_members, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result["active_sons"] = fc::variant(global_props.active_sons, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result["entropy"] = fc::variant(dynamic_props.random, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1752,6 +1788,41 @@ public:
|
|||
FC_CAPTURE_AND_RETHROW( (owner_account) )
|
||||
}
|
||||
|
||||
son_object get_son(string owner_account)
|
||||
{
|
||||
try
|
||||
{
|
||||
fc::optional<son_id_type> son_id = maybe_id<son_id_type>(owner_account);
|
||||
if (son_id)
|
||||
{
|
||||
std::vector<son_id_type> ids_to_get;
|
||||
ids_to_get.push_back(*son_id);
|
||||
std::vector<fc::optional<son_object>> son_objects = _remote_db->get_sons(ids_to_get);
|
||||
if (son_objects.front())
|
||||
return *son_objects.front();
|
||||
FC_THROW("No SON is registered for id ${id}", ("id", owner_account));
|
||||
}
|
||||
else
|
||||
{
|
||||
// then maybe it's the owner account
|
||||
try
|
||||
{
|
||||
account_id_type owner_account_id = get_account_id(owner_account);
|
||||
fc::optional<son_object> son = _remote_db->get_son_by_account(owner_account_id);
|
||||
if (son)
|
||||
return *son;
|
||||
else
|
||||
FC_THROW("No SON is registered for account ${account}", ("account", owner_account));
|
||||
}
|
||||
catch (const fc::exception&)
|
||||
{
|
||||
FC_THROW("No account or SON named ${account}", ("account", owner_account));
|
||||
}
|
||||
}
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW( (owner_account) )
|
||||
}
|
||||
|
||||
committee_member_object get_committee_member(string owner_account)
|
||||
{
|
||||
try
|
||||
|
|
@ -1787,6 +1858,82 @@ public:
|
|||
FC_CAPTURE_AND_RETHROW( (owner_account) )
|
||||
}
|
||||
|
||||
signed_transaction create_son(string owner_account,
|
||||
string url,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_object son_account = get_account(owner_account);
|
||||
fc::ecc::private_key active_private_key = get_private_key_for_account(son_account);
|
||||
int son_key_index = find_first_unused_derived_key_index(active_private_key);
|
||||
fc::ecc::private_key son_private_key = derive_private_key(key_to_wif(active_private_key), son_key_index);
|
||||
graphene::chain::public_key_type son_public_key = son_private_key.get_public_key();
|
||||
|
||||
son_create_operation son_create_op;
|
||||
son_create_op.owner_account = son_account.id;
|
||||
son_create_op.signing_key = son_public_key;
|
||||
son_create_op.url = url;
|
||||
secret_hash_type::encoder enc;
|
||||
fc::raw::pack(enc, son_private_key);
|
||||
fc::raw::pack(enc, secret_hash_type());
|
||||
|
||||
if (_remote_db->get_son_by_account(son_create_op.owner_account))
|
||||
FC_THROW("Account ${owner_account} is already a SON", ("owner_account", owner_account));
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( son_create_op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
tx.validate();
|
||||
|
||||
_wallet.pending_son_registrations[owner_account] = key_to_wif(son_private_key);
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) }
|
||||
|
||||
signed_transaction update_son(string owner_account,
|
||||
string url,
|
||||
string block_signing_key,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
son_object son = get_son(owner_account);
|
||||
account_object son_account = get_account( son.son_account );
|
||||
fc::ecc::private_key active_private_key = get_private_key_for_account(son_account);
|
||||
|
||||
son_update_operation son_update_op;
|
||||
son_update_op.son_id = son.id;
|
||||
son_update_op.owner_account = son_account.id;
|
||||
if( url != "" )
|
||||
son_update_op.new_url = url;
|
||||
if( block_signing_key != "" ) {
|
||||
son_update_op.new_signing_key = public_key_type( block_signing_key );
|
||||
}
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( son_update_op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (owner_account)(url)(block_signing_key)(broadcast) ) }
|
||||
|
||||
signed_transaction delete_son(string owner_account,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
son_object son = get_son(owner_account);
|
||||
account_object son_account = get_account( son.son_account );
|
||||
fc::ecc::private_key active_private_key = get_private_key_for_account(son_account);
|
||||
|
||||
son_delete_operation son_delete_op;
|
||||
son_delete_op.son_id = son.id;
|
||||
son_delete_op.owner_account = son_account.id;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( son_delete_op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) }
|
||||
|
||||
signed_transaction create_witness(string owner_account,
|
||||
string url,
|
||||
bool broadcast /* = false */)
|
||||
|
|
@ -2055,6 +2202,81 @@ public:
|
|||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (voting_account)(committee_member)(approve)(broadcast) ) }
|
||||
|
||||
signed_transaction vote_for_son(string voting_account,
|
||||
string son,
|
||||
bool approve,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_object voting_account_object = get_account(voting_account);
|
||||
account_id_type son_account_id = get_account_id(son);
|
||||
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(son_account_id);
|
||||
if (!son_obj)
|
||||
FC_THROW("Account ${son} is not registered as a son", ("son", son));
|
||||
if (approve)
|
||||
{
|
||||
auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id);
|
||||
if (!insert_result.second)
|
||||
FC_THROW("Account ${account} was already voting for son ${son}", ("account", voting_account)("son", son));
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id);
|
||||
if (!votes_removed)
|
||||
FC_THROW("Account ${account} is already not voting for son ${son}", ("account", voting_account)("son", son));
|
||||
}
|
||||
account_update_operation account_update_op;
|
||||
account_update_op.account = voting_account_object.id;
|
||||
account_update_op.new_options = voting_account_object.options;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( account_update_op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (voting_account)(son)(approve)(broadcast) ) }
|
||||
|
||||
signed_transaction update_son_votes(string voting_account,
|
||||
std::vector<std::string> sons_to_approve,
|
||||
std::vector<std::string> sons_to_reject,
|
||||
uint16_t desired_number_of_sons,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_object voting_account_object = get_account(voting_account);
|
||||
for (const std::string& son : sons_to_approve)
|
||||
{
|
||||
account_id_type son_owner_account_id = get_account_id(son);
|
||||
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(son_owner_account_id);
|
||||
if (!son_obj)
|
||||
FC_THROW("Account ${son} is not registered as a witness", ("son", son));
|
||||
auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id);
|
||||
if (!insert_result.second)
|
||||
FC_THROW("Account ${account} was already voting for son ${son}", ("account", voting_account)("son", son));
|
||||
}
|
||||
for (const std::string& son : sons_to_reject)
|
||||
{
|
||||
account_id_type son_owner_account_id = get_account_id(son);
|
||||
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(son_owner_account_id);
|
||||
if (!son_obj)
|
||||
FC_THROW("Account ${son} is not registered as a son", ("son", son));
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id);
|
||||
if (!votes_removed)
|
||||
FC_THROW("Account ${account} is already not voting for son ${son}", ("account", voting_account)("son", son));
|
||||
}
|
||||
voting_account_object.options.num_son = desired_number_of_sons;
|
||||
|
||||
account_update_operation account_update_op;
|
||||
account_update_op.account = voting_account_object.id;
|
||||
account_update_op.new_options = voting_account_object.options;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( account_update_op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (voting_account)(sons_to_approve)(sons_to_reject)(desired_number_of_sons)(broadcast) ) }
|
||||
|
||||
signed_transaction vote_for_witness(string voting_account,
|
||||
string witness,
|
||||
bool approve,
|
||||
|
|
@ -4004,6 +4226,11 @@ map<string,committee_member_id_type> wallet_api::list_committee_members(const st
|
|||
return my->_remote_db->lookup_committee_member_accounts(lowerbound, limit);
|
||||
}
|
||||
|
||||
son_object wallet_api::get_son(string owner_account)
|
||||
{
|
||||
return my->get_son(owner_account);
|
||||
}
|
||||
|
||||
witness_object wallet_api::get_witness(string owner_account)
|
||||
{
|
||||
return my->get_witness(owner_account);
|
||||
|
|
@ -4014,6 +4241,32 @@ committee_member_object wallet_api::get_committee_member(string owner_account)
|
|||
return my->get_committee_member(owner_account);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::create_son(string owner_account,
|
||||
string url,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->create_son(owner_account, url, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::update_son(string owner_account,
|
||||
string url,
|
||||
string block_signing_key,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->update_son(owner_account, url, block_signing_key, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::delete_son(string owner_account,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
my->delete_son(owner_account, broadcast);
|
||||
}
|
||||
|
||||
map<string, son_id_type> wallet_api::list_sons(const string& lowerbound, uint32_t limit)
|
||||
{
|
||||
my->_remote_db->lookup_son_accounts(lowerbound, limit);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::create_witness(string owner_account,
|
||||
string url,
|
||||
bool broadcast /* = false */)
|
||||
|
|
@ -4074,6 +4327,23 @@ signed_transaction wallet_api::vote_for_committee_member(string voting_account,
|
|||
return my->vote_for_committee_member(voting_account, witness, approve, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::vote_for_son(string voting_account,
|
||||
string son,
|
||||
bool approve,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->vote_for_son(voting_account, son, approve, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::update_son_votes(string voting_account,
|
||||
std::vector<std::string> sons_to_approve,
|
||||
std::vector<std::string> sons_to_reject,
|
||||
uint16_t desired_number_of_sons,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->update_son_votes(voting_account, sons_to_approve, sons_to_reject, desired_number_of_sons, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::vote_for_witness(string voting_account,
|
||||
string witness,
|
||||
bool approve,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <iostream>
|
||||
|
|
|
|||
|
|
@ -479,4 +479,4 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture )
|
|||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
186
tests/tests/son_operations_tests.cpp
Normal file
186
tests/tests/son_operations_tests.cpp
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_evaluator.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( son_operation_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_son_test ) {
|
||||
generate_blocks(HARDFORK_SON_TIME);
|
||||
while (db.head_block_time() <= HARDFORK_SON_TIME) {
|
||||
generate_block();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
ACTORS((alice)(bob));
|
||||
|
||||
upgrade_to_lifetime_member(alice);
|
||||
upgrade_to_lifetime_member(bob);
|
||||
|
||||
transfer( committee_account, alice_id, asset( 100000 ) );
|
||||
transfer( committee_account, bob_id, asset( 100000 ) );
|
||||
|
||||
set_expiration(db, trx);
|
||||
std::string test_url = "https://create_son_test";
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(10);
|
||||
//op.balance_type = vesting_balance_type::unspecified;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
// create payment vesting
|
||||
vesting_balance_id_type payment;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(10);
|
||||
//op.balance_type = vesting_balance_type::unspecified;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// alice became son
|
||||
{
|
||||
son_create_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.url = test_url;
|
||||
op.deposit = deposit;
|
||||
op.pay_vb = payment;
|
||||
op.signing_key = alice_public_key;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->url == test_url );
|
||||
BOOST_CHECK( obj->signing_key == alice_public_key );
|
||||
BOOST_CHECK( obj->deposit.instance == deposit.instance.value );
|
||||
BOOST_CHECK( obj->pay_vb.instance == payment.instance.value );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( update_son_test ) {
|
||||
|
||||
INVOKE(create_son_test);
|
||||
GET_ACTOR(alice);
|
||||
|
||||
std::string new_url = "https://anewurl.com";
|
||||
|
||||
{
|
||||
son_update_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.new_url = new_url;
|
||||
op.son_id = son_id_type(0);
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->url == new_url );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( delete_son_test ) {
|
||||
|
||||
INVOKE(create_son_test);
|
||||
GET_ACTOR(alice);
|
||||
|
||||
{
|
||||
son_delete_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( update_delete_not_own ) { // fee payer needs to be the son object owner
|
||||
try {
|
||||
|
||||
INVOKE(create_son_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
|
||||
// bob tries to update a son object he dont own
|
||||
{
|
||||
son_update_operation op;
|
||||
op.owner_account = bob_id;
|
||||
op.new_url = "whatever";
|
||||
op.son_id = son_id_type(0);
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
set_expiration(db, trx);
|
||||
trx.clear();
|
||||
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
auto obj = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
// not changing
|
||||
BOOST_CHECK( obj->url == "https://create_son_test" );
|
||||
|
||||
// bob tries to delete a son object he dont own
|
||||
{
|
||||
son_delete_operation op;
|
||||
op.owner_account = bob_id;
|
||||
op.son_id = son_id_type(0);
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);
|
||||
|
||||
}
|
||||
generate_block();
|
||||
|
||||
obj = idx.find( alice_id );
|
||||
// not deleting
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->son_account.instance == alice_id.instance);
|
||||
}
|
||||
catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
|
||||
} BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
Reference in a new issue