Compare commits

...

37 commits

Author SHA1 Message Date
Srdjan Obucina
d8e4a9a160 Fix build error, remove undeclared field 2019-10-09 19:32:28 +02:00
Srdjan Obucina
af0e0e4d30 Add missing #include 2019-10-09 18:33:07 +02:00
Srdjan Obucina
388d00b310 Fix build error, add missing parameter 2019-10-09 17:56:51 +02:00
Srdjan Obucina
bdfbf5c87b Minor code cleanup 2019-10-09 15:46:49 +02:00
Srdjan Obucina
54599cc22b Add missing parenthesis in ../../libraries/app/impacted.cpp 2019-10-08 21:52:08 +02:00
Srdjan Obucina
896b4df417 Add missing parenthesis in libraries/chain/db_notify.cpp 2019-10-08 21:46:31 +02:00
Srdjan Obucina
659bfcb7cf Add missing comma in operations.hpp 2019-10-08 21:38:04 +02:00
Srdjan Obucina
d06b2361ba Add newline at the end of SON.hf 2019-10-08 21:35:04 +02:00
Srdjan Obucina
7b6562ec85 Some renaming to follow existing naming convention, code cleanup 2019-10-08 21:14:26 +02:00
Srdjan Obucina
3b167ffd03 Merge branch 'feature/SONs-base' into feature/SON-operations 2019-10-08 20:58:07 +02:00
Srdjan Obucina
3e4322b08d WIP, SON operations, cli_wallet commands and RPC
- CLI Wallet tests imported from develop branch
2019-10-06 14:08:15 +02:00
Srdjan Obucina
8bc204c69b WIP, SON operations, cli_wallet commands and RPC
- rename vote_for_son_member to vote_for_son
2019-10-04 02:43:16 +02:00
Srdjan Obucina
e4bf00e349 WIP, SON operations, cli_wallet commands and RPC
- update_son_votes
2019-10-04 01:24:30 +02:00
Srdjan Obucina
5ecbba8717 WIP, SON operations, cli_wallet commands and RPC
- get_son in cli_wallet
2019-10-03 19:57:34 +02:00
Srdjan Obucina
4d1bec05bc WIP, SON operations, cli_wallet commands and RPC
- get_son in cli_wallet
2019-10-03 19:50:49 +02:00
Srdjan Obucina
ecfcd8eda1 WIP, SON operations, cli_wallet commands and RPC
- Decrease SON hardfork time for test purposes
2019-10-03 19:02:55 +02:00
Srdjan Obucina
203ee89218 WIP, SON operations, cli_wallet commands and RPC
- Updating global_property_object
2019-10-03 01:26:35 +02:00
Srdjan Obucina
e0ff813879 WIP, SON operations, cli_wallet commands and RPC
- Updating global_property_object
2019-10-03 01:24:31 +02:00
Srdjan Obucina
53666a9431 Merge branch 'feature/SON-operations' of https://github.com/peerplays-network/peerplays into feature/SON-operations 2019-10-03 01:24:19 +02:00
Srdjan Obucina
49564af60a WIP, SON related operations, cli_wallet commands and RPC
- Updating global_property_object
2019-10-03 01:22:21 +02:00
Srdjan Obucina
457c8b9d9f WIP, SON related operations, cli_wallet commands and RPC
- Updating global_property_object
2019-10-03 01:17:29 +02:00
Srdjan Obucina
44f185e0c2 WIP, SON operations, cli_wallet commands and RPC
- claim_registered_son
- some renaming, to follow existing naming convention
2019-10-02 18:28:07 +02:00
Srdjan Obucina
3a65102e56 WIP, SON operations, cli_wallet commands and RPC
- create_son, update_son, delete_son, list_sons
- get_sons, get_son_by_account, lookup_son_accounts, get_son_count
2019-10-02 01:47:24 +02:00
Alfredo Garcia
e2c579bb02 add additional fields to son object and operations 2019-09-28 18:35:38 -03:00
Alfredo Garcia
4d0a5b683f add son vote type to lookup_vote_ids 2019-09-27 16:03:33 -03:00
Alfredo Garcia
17dfc6af64 add protection to delete and modify son object only if owner 2019-09-25 18:17:42 -03:00
Alfredo Garcia
3a4445b20c add missing SON hardfork file 2019-09-24 17:10:34 -03:00
Alfredo Garcia
723c802006 minor clean 2019-09-21 18:45:40 -03:00
Alfredo Garcia
0a5d694568 add hardfork guards 2019-09-21 12:19:57 -03:00
Alfredo Garcia
6efbf84339 change indentation of the son tests 2019-09-21 09:56:23 -03:00
Alfredo Garcia
5dcb55c992 add modify logic, test case, modify delete 2019-09-21 09:23:32 -03:00
Alfredo Garcia
ba77a33711 rename object, add update operation skeleton 2019-09-20 08:38:22 -03:00
Alfredo Garcia
52e9c2a5a6 remove not needed library 2019-09-19 13:34:59 -03:00
Alfredo Garcia
38ec553773 remove member from operation names 2019-09-19 13:22:46 -03:00
Alfredo Garcia
7d6b2156e4 rename and move stuff 2019-09-19 11:52:23 -03:00
Alfredo Garcia
d36168b71e Merge commit 'refs/pull/48/head' of https://github.com/peerplays-network/peerplays into feature/SONs-base 2019-09-18 17:30:03 -03:00
PixelPlex
988a1771ed Added SON object 2019-07-29 11:44:06 +03:00
32 changed files with 1131 additions and 25 deletions

View file

@ -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
View file

@ -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

View file

@ -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:

View file

@ -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) );

View file

@ -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 )

View file

@ -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

View file

@ -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"

View file

@ -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 > >();

View file

@ -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) {

View file

@ -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;
}
}
}

View 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

View file

@ -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)

View file

@ -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;

View file

@ -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)
)

View file

@ -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)
)

View file

@ -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;

View file

@ -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)

View file

@ -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

View 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) )

View file

@ -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, )

View file

@ -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) )

View 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

View 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) )

View file

@ -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)

View 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

View file

@ -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"

View file

@ -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

View file

@ -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)

View file

@ -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,

View file

@ -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>

View file

@ -479,4 +479,4 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture )
edump((e.to_detail_string()));
throw;
}
}
}

View 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()