Compare commits
17 commits
master
...
son_object
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
964d7e0dc0 | ||
|
|
fcdbce84ea | ||
|
|
c6ef1870cb | ||
|
|
e2c579bb02 | ||
|
|
4d0a5b683f | ||
|
|
17dfc6af64 | ||
|
|
3a4445b20c | ||
|
|
723c802006 | ||
|
|
0a5d694568 | ||
|
|
6efbf84339 | ||
|
|
5dcb55c992 | ||
|
|
ba77a33711 | ||
|
|
52e9c2a5a6 | ||
|
|
38ec553773 | ||
|
|
7d6b2156e4 | ||
|
|
d36168b71e | ||
|
|
988a1771ed |
29 changed files with 786 additions and 12 deletions
|
|
@ -432,7 +432,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_member_account );
|
||||
break;
|
||||
}
|
||||
case sport_object_type:
|
||||
case event_group_object_type:
|
||||
case event_object_type:
|
||||
|
|
|
|||
|
|
@ -136,6 +136,9 @@ 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
|
||||
fc::optional<son_object> get_son_member_by_account(account_id_type account)const;
|
||||
|
||||
// Votes
|
||||
vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;
|
||||
|
||||
|
|
@ -1577,6 +1580,26 @@ map<string, committee_member_id_type> database_api_impl::lookup_committee_member
|
|||
return committee_members_by_account_name;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// SON members //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
fc::optional<son_object> database_api::get_son_member_by_account(account_id_type account)const
|
||||
{
|
||||
return my->get_son_member_by_account( account );
|
||||
}
|
||||
|
||||
fc::optional<son_object> database_api_impl::get_son_member_by_account(account_id_type account) const
|
||||
{
|
||||
const auto& idx = _db.get_index_type<son_member_index>().indices().get<by_account>();
|
||||
auto itr = idx.find(account);
|
||||
if( itr != idx.end() )
|
||||
return *itr;
|
||||
return {};
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Votes //
|
||||
|
|
@ -1596,6 +1619,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_member_index>().indices().get<by_vote_id>();
|
||||
|
||||
vector<variant> result;
|
||||
result.reserve( votes.size() );
|
||||
|
|
@ -1638,6 +1662,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 ) );
|
||||
else
|
||||
result.emplace_back( variant() );
|
||||
break;
|
||||
}
|
||||
|
||||
case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,6 +282,15 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.affiliate );
|
||||
}
|
||||
void operator()( const affiliate_referral_payout_operation& op ) { }
|
||||
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>
|
||||
|
|
@ -546,6 +547,17 @@ 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 the son_member owned by a given account
|
||||
* @param account The ID of the account whose son_member should be retrieved
|
||||
* @return The son_member object, or null if the account does not have a son_member
|
||||
*/
|
||||
fc::optional<son_object> get_son_member_by_account(account_id_type account)const;
|
||||
|
||||
|
||||
/// WORKERS
|
||||
|
||||
|
|
@ -757,6 +769,9 @@ FC_API(graphene::app::database_api,
|
|||
(get_committee_member_by_account)
|
||||
(lookup_committee_member_accounts)
|
||||
|
||||
// SON members
|
||||
(get_son_member_by_account)
|
||||
|
||||
// workers
|
||||
(get_workers_by_account)
|
||||
// Votes
|
||||
|
|
|
|||
|
|
@ -111,6 +111,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>
|
||||
|
|
@ -76,6 +75,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>
|
||||
|
||||
|
|
@ -237,6 +237,9 @@ void database::initialize_evaluators()
|
|||
register_evaluator<tournament_join_evaluator>();
|
||||
register_evaluator<game_move_evaluator>();
|
||||
register_evaluator<tournament_leave_evaluator>();
|
||||
register_evaluator<create_son_evaluator>();
|
||||
register_evaluator<update_son_evaluator>();
|
||||
register_evaluator<delete_son_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -301,6 +304,8 @@ void database::initialize_indexes()
|
|||
//add_index< primary_index<distributed_dividend_balance_object_index > >();
|
||||
add_index< primary_index<pending_dividend_payout_balance_for_holder_object_index > >();
|
||||
add_index< primary_index<total_distributed_dividend_balance_object_index > >();
|
||||
|
||||
add_index< primary_index<son_member_index> >();
|
||||
}
|
||||
|
||||
void database::init_genesis(const genesis_state_type& genesis_state)
|
||||
|
|
|
|||
|
|
@ -269,6 +269,15 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.affiliate );
|
||||
}
|
||||
void operator()( const affiliate_referral_payout_operation& op ) { }
|
||||
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 )
|
||||
|
|
@ -357,6 +366,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_member_account );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
libraries/chain/hardfork.d/SON.hf
Normal file
4
libraries/chain/hardfork.d/SON.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// SON HARDFORK Monday, September 21, 2020 1:43:11 PM
|
||||
#ifndef HARDFORK_SON_TIME
|
||||
#define HARDFORK_SON_TIME (fc::time_point_sec( 1600695791 ))
|
||||
#endif
|
||||
|
|
@ -228,3 +228,6 @@
|
|||
#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 SON_VESTING_AMOUNT 50 // 50 PPY
|
||||
#define SON_VESTING_PERIOD (60*60*24*30) // 2 days
|
||||
|
||||
|
|
|
|||
|
|
@ -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_son_members; // updated once per maintenance interval
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ namespace graphene { namespace chain {
|
|||
optional < uint32_t > gpos_period;
|
||||
optional < uint32_t > gpos_subperiod;
|
||||
optional < uint32_t > gpos_period_start;
|
||||
optional < uint32_t > son_vesting_amount;
|
||||
optional < uint32_t > son_vesting_period;
|
||||
};
|
||||
|
||||
struct chain_parameters
|
||||
|
|
@ -121,6 +123,12 @@ namespace graphene { namespace chain {
|
|||
inline uint32_t gpos_period_start()const {
|
||||
return extensions.value.gpos_period_start.valid() ? *extensions.value.gpos_period_start : HARDFORK_GPOS_TIME.sec_since_epoch(); /// current period start date
|
||||
}
|
||||
inline uint32_t son_vesting_amount()const {
|
||||
return extensions.value.son_vesting_amount.valid() ? *extensions.value.son_vesting_amount : SON_VESTING_AMOUNT; /// current period start date
|
||||
}
|
||||
inline uint32_t son_vesting_period()const {
|
||||
return extensions.value.son_vesting_period.valid() ? *extensions.value.son_vesting_period : SON_VESTING_PERIOD; /// current period start date
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -134,6 +142,8 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
|||
(gpos_period)
|
||||
(gpos_subperiod)
|
||||
(gpos_period_start)
|
||||
(son_vesting_amount)
|
||||
(son_vesting_period)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::chain_parameters,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,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 {
|
||||
|
||||
|
|
@ -129,7 +130,10 @@ namespace graphene { namespace chain {
|
|||
sport_delete_operation,
|
||||
event_group_delete_operation,
|
||||
affiliate_payout_operation, // VIRTUAL
|
||||
affiliate_referral_payout_operation // VIRTUAL
|
||||
affiliate_referral_payout_operation, // VIRTUAL
|
||||
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
|
||||
};
|
||||
|
||||
|
|
@ -202,6 +203,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;
|
||||
|
|
@ -228,6 +230,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;
|
||||
|
|
@ -402,6 +405,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,
|
||||
|
|
@ -469,6 +473,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, )
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
enum class vesting_balance_type { unspecified, gpos };
|
||||
enum class vesting_balance_type { unspecified, gpos, son };
|
||||
|
||||
struct linear_vesting_policy_initializer
|
||||
{
|
||||
|
|
@ -44,9 +44,16 @@ namespace graphene { namespace chain {
|
|||
cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){}
|
||||
};
|
||||
|
||||
typedef fc::static_variant<linear_vesting_policy_initializer, cdd_vesting_policy_initializer> vesting_policy_initializer;
|
||||
|
||||
struct dormant_vesting_policy_initializer
|
||||
{
|
||||
/** none may be claimed if dormant is true, otherwise this is a linear policy */
|
||||
bool dormant = true;
|
||||
fc::time_point_sec begin_timestamp;
|
||||
uint32_t vesting_cliff_seconds = 0;
|
||||
uint32_t vesting_duration_seconds = 0;
|
||||
};
|
||||
|
||||
typedef fc::static_variant<linear_vesting_policy_initializer, cdd_vesting_policy_initializer, dormant_vesting_policy_initializer> vesting_policy_initializer;
|
||||
|
||||
/**
|
||||
* @brief Create a vesting balance.
|
||||
|
|
@ -120,6 +127,7 @@ FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_b
|
|||
|
||||
FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) )
|
||||
FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) )
|
||||
FC_REFLECT(graphene::chain::dormant_vesting_policy_initializer, (dormant)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (unspecified)(gpos) )
|
||||
FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (unspecified)(gpos)(son) )
|
||||
|
|
|
|||
|
|
@ -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 );
|
|||
|
||||
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_member 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_member_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_member_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_member_account>
|
||||
>,
|
||||
ordered_unique< tag<by_vote_id>,
|
||||
member<son_object, vote_id_type, &son_object::vote_id>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_member_index = generic_index<son_object, son_member_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object),
|
||||
(son_member_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb) )
|
||||
|
|
@ -123,10 +123,44 @@ namespace graphene { namespace chain {
|
|||
void on_withdraw(const vesting_policy_context& ctx);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Cant withdraw anything while dormant mode is true, linear policy after that changes.
|
||||
*
|
||||
* This vesting balance type is needed to register SON users where balance may be claimable only after
|
||||
* the SON object is deleted(plus a linear policy).
|
||||
* When deleting a SON member the dormant_mode will change and the linear policy will became active.
|
||||
*
|
||||
* @note New funds may not be added to a dormant vesting balance.
|
||||
*/
|
||||
struct dormant_vesting_policy
|
||||
{
|
||||
/// dormant mode flag indicates if we are in dormant mode or linear policy.
|
||||
bool dormant_mode = true;
|
||||
|
||||
/// This is the time at which funds begin vesting.
|
||||
fc::time_point_sec begin_timestamp;
|
||||
/// No amount may be withdrawn before this many seconds of the vesting period have elapsed.
|
||||
uint32_t vesting_cliff_seconds = 0;
|
||||
/// Duration of the vesting period, in seconds. Must be greater than 0 and greater than vesting_cliff_seconds.
|
||||
uint32_t vesting_duration_seconds = 0;
|
||||
/// The total amount of asset to vest.
|
||||
share_type begin_balance;
|
||||
|
||||
asset get_allowed_withdraw(const vesting_policy_context& ctx)const;
|
||||
bool is_deposit_allowed(const vesting_policy_context& ctx)const;
|
||||
bool is_deposit_vested_allowed(const vesting_policy_context&)const { return false; }
|
||||
bool is_withdraw_allowed(const vesting_policy_context& ctx)const;
|
||||
void on_deposit(const vesting_policy_context& ctx);
|
||||
void on_deposit_vested(const vesting_policy_context&)
|
||||
{ FC_THROW( "May not deposit vested into a linear vesting balance." ); }
|
||||
void on_withdraw(const vesting_policy_context& ctx);
|
||||
};
|
||||
|
||||
typedef fc::static_variant<
|
||||
linear_vesting_policy,
|
||||
cdd_vesting_policy
|
||||
> vesting_policy;
|
||||
cdd_vesting_policy,
|
||||
dormant_vesting_policy
|
||||
> vesting_policy;
|
||||
|
||||
/**
|
||||
* Vesting balance object is a balance that is locked by the blockchain for a period of time.
|
||||
|
|
@ -225,6 +259,14 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy,
|
|||
(coin_seconds_earned_last_update)
|
||||
)
|
||||
|
||||
FC_REFLECT(graphene::chain::dormant_vesting_policy,
|
||||
(dormant_mode)
|
||||
(begin_timestamp)
|
||||
(vesting_cliff_seconds)
|
||||
(vesting_duration_seconds)
|
||||
(begin_balance)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::vesting_policy )
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object),
|
||||
|
|
|
|||
|
|
@ -140,6 +140,18 @@ struct proposal_operation_hardfork_visitor
|
|||
FC_ASSERT( vbco.balance_type == vesting_balance_type::unspecified, "balance_type in vesting create 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)
|
||||
|
|
|
|||
89
libraries/chain/son_evaluator.cpp
Normal file
89
libraries/chain/son_evaluator.cpp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#include <graphene/chain/son_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.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_member_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_member_account == op.owner_account);
|
||||
const auto& idx = db().get_index_type<son_member_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_member_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_member_account == op.owner_account);
|
||||
const auto& idx = db().get_index_type<son_member_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_member_index>().indices().get<by_id>();
|
||||
auto son = idx.find(op.son_id);
|
||||
if(son != idx.end()) {
|
||||
vesting_balance_object deposit = son->deposit(db());
|
||||
dormant_vesting_policy new_vesting_policy;
|
||||
new_vesting_policy.dormant_mode = false;
|
||||
new_vesting_policy.begin_timestamp = db().head_block_time();
|
||||
new_vesting_policy.vesting_cliff_seconds = db().get_global_properties().parameters.son_vesting_period();
|
||||
|
||||
db().modify(son->deposit(db()), [&new_vesting_policy](vesting_balance_object &vbo) {
|
||||
vbo.policy = new_vesting_policy;
|
||||
});
|
||||
|
||||
db().remove(*son);
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -45,7 +45,11 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance
|
|||
if(d.head_block_time() < HARDFORK_GPOS_TIME) // Todo: can be removed after gpos hf time pass
|
||||
FC_ASSERT( op.balance_type == vesting_balance_type::unspecified);
|
||||
|
||||
if(d.head_block_time() >= HARDFORK_SON_TIME && op.balance_type == vesting_balance_type::son) // Todo: hf check can be removed after pass
|
||||
FC_ASSERT( op.amount.amount >= d.get_global_properties().parameters.son_vesting_amount() );
|
||||
|
||||
return void_result();
|
||||
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
struct init_policy_visitor
|
||||
|
|
@ -79,6 +83,17 @@ struct init_policy_visitor
|
|||
policy.coin_seconds_earned_last_update = now;
|
||||
p = policy;
|
||||
}
|
||||
|
||||
void operator()( const dormant_vesting_policy_initializer& i )const
|
||||
{
|
||||
dormant_vesting_policy policy;
|
||||
policy.dormant_mode = i.dormant;
|
||||
policy.begin_timestamp = i.begin_timestamp;
|
||||
policy.vesting_cliff_seconds = i.vesting_cliff_seconds;
|
||||
policy.vesting_duration_seconds = i.vesting_duration_seconds;
|
||||
policy.begin_balance = init_balance;
|
||||
p = policy;
|
||||
}
|
||||
};
|
||||
|
||||
object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance_create_operation& op )
|
||||
|
|
@ -105,6 +120,14 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance
|
|||
p.vesting_duration_seconds = gpo.parameters.gpos_subperiod();
|
||||
obj.policy = p;
|
||||
}
|
||||
if(op.balance_type == vesting_balance_type::son)
|
||||
{
|
||||
const auto &gpo = d.get_global_properties();
|
||||
// forcing son dormant policy
|
||||
dormant_vesting_policy p;
|
||||
p.dormant_mode = true;
|
||||
obj.policy = p;
|
||||
}
|
||||
else {
|
||||
op.policy.visit(init_policy_visitor(obj.policy, op.amount.amount, now));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,6 +157,59 @@ bool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)c
|
|||
return (ctx.amount <= get_allowed_withdraw(ctx));
|
||||
}
|
||||
|
||||
asset dormant_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const
|
||||
{
|
||||
share_type allowed_withdraw = 0;
|
||||
|
||||
if( !dormant_mode && ctx.now > begin_timestamp)
|
||||
{
|
||||
const auto elapsed_seconds = (ctx.now - begin_timestamp).to_seconds();
|
||||
assert( elapsed_seconds > 0 );
|
||||
|
||||
if( elapsed_seconds >= vesting_cliff_seconds )
|
||||
{
|
||||
share_type total_vested = 0;
|
||||
if( elapsed_seconds < vesting_duration_seconds )
|
||||
{
|
||||
total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64();
|
||||
}
|
||||
else
|
||||
{
|
||||
total_vested = begin_balance;
|
||||
}
|
||||
assert( total_vested >= 0 );
|
||||
|
||||
const share_type withdrawn_already = begin_balance - ctx.balance.amount;
|
||||
assert( withdrawn_already >= 0 );
|
||||
|
||||
allowed_withdraw = total_vested - withdrawn_already;
|
||||
assert( allowed_withdraw >= 0 );
|
||||
}
|
||||
}
|
||||
|
||||
return asset( allowed_withdraw, ctx.balance.asset_id );
|
||||
}
|
||||
|
||||
void dormant_vesting_policy::on_deposit(const vesting_policy_context& ctx)
|
||||
{
|
||||
}
|
||||
|
||||
bool dormant_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const
|
||||
{
|
||||
return (ctx.amount.asset_id == ctx.balance.asset_id)
|
||||
&& sum_below_max_shares(ctx.amount, ctx.balance);
|
||||
}
|
||||
|
||||
void dormant_vesting_policy::on_withdraw(const vesting_policy_context& ctx)
|
||||
{
|
||||
}
|
||||
|
||||
bool dormant_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const
|
||||
{
|
||||
return (ctx.amount.asset_id == ctx.balance.asset_id)
|
||||
&& (ctx.amount <= get_allowed_withdraw(ctx));
|
||||
}
|
||||
|
||||
#define VESTING_VISITOR(NAME, MAYBE_CONST) \
|
||||
struct NAME ## _visitor \
|
||||
{ \
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -1378,6 +1378,28 @@ class wallet_api
|
|||
bool approve,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Vote for a given son_member.
|
||||
*
|
||||
* An account can publish a list of all son_memberes they approve of. This
|
||||
* command allows you to add or remove son_memberes 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_member, you can only vote for the son_member
|
||||
* or not vote for the son_member.
|
||||
*
|
||||
* @param voting_account the name or id of the account who is voting with their shares
|
||||
* @param son_member the name or id of the son_member' owner account
|
||||
* @param approve true if you wish to vote in favor of that son_member, false to
|
||||
* remove your vote in favor of that son_member
|
||||
* @param broadcast true if you wish to broadcast the transaction
|
||||
* @return the signed transaction changing your vote for the given son_member
|
||||
*/
|
||||
signed_transaction vote_for_son_member(string voting_account,
|
||||
string son_member,
|
||||
bool approve,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Vote for a given witness.
|
||||
*
|
||||
* An account can publish a list of all witnesses they approve of. This
|
||||
|
|
@ -1965,6 +1987,7 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(get_vesting_balances)
|
||||
(withdraw_vesting)
|
||||
(vote_for_committee_member)
|
||||
(vote_for_son_member)
|
||||
(vote_for_witness)
|
||||
(update_witness_votes)
|
||||
(set_voting_proxy)
|
||||
|
|
|
|||
|
|
@ -2024,6 +2024,40 @@ public:
|
|||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (voting_account)(committee_member)(approve)(broadcast) ) }
|
||||
|
||||
signed_transaction vote_for_son_member(string voting_account,
|
||||
string son_member,
|
||||
bool approve,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_object voting_account_object = get_account(voting_account);
|
||||
account_id_type son_member_owner_account_id = get_account_id(son_member);
|
||||
fc::optional<son_object> son_member_obj = _remote_db->get_son_member_by_account(son_member_owner_account_id);
|
||||
if (!son_member_obj)
|
||||
FC_THROW("Account ${son_member} is not registered as a son_member", ("son_member", son_member));
|
||||
if (approve)
|
||||
{
|
||||
auto insert_result = voting_account_object.options.votes.insert(son_member_obj->vote_id);
|
||||
if (!insert_result.second)
|
||||
FC_THROW("Account ${account} was already voting for son_member ${son_member}", ("account", voting_account)("son_member", son_member));
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(son_member_obj->vote_id);
|
||||
if (!votes_removed)
|
||||
FC_THROW("Account ${account} is already not voting for son_member ${son_member}", ("account", voting_account)("son_member", son_member));
|
||||
}
|
||||
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_member)(approve)(broadcast) ) }
|
||||
|
||||
signed_transaction vote_for_witness(string voting_account,
|
||||
string witness,
|
||||
bool approve,
|
||||
|
|
@ -4044,6 +4078,14 @@ 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_member(string voting_account,
|
||||
string son_member,
|
||||
bool approve,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->vote_for_son_member(voting_account, son_member, approve, 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>
|
||||
|
|
|
|||
221
tests/tests/son_operations_tests.cpp
Normal file
221
tests/tests/son_operations_tests.cpp
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_evaluator.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.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);
|
||||
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::son;
|
||||
trx.operations.push_back(op);
|
||||
|
||||
// amount in the son balance need to be at least 50
|
||||
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception );
|
||||
|
||||
op.amount = asset(50);
|
||||
trx.clear();
|
||||
trx.operations.push_back(op);
|
||||
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
|
||||
deposit = ptx.operation_results[0].get<object_id_type>();
|
||||
|
||||
auto deposit_vesting = db.get<vesting_balance_object>(ptx.operation_results[0].get<object_id_type>());
|
||||
|
||||
BOOST_CHECK_EQUAL(deposit(db).balance.amount.value, 50);
|
||||
auto now = db.head_block_time();
|
||||
BOOST_CHECK_EQUAL(deposit(db).is_withdraw_allowed(now, asset(50)), false); // cant withdraw
|
||||
}
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create payment normal vesting
|
||||
vesting_balance_id_type payment ;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(1);
|
||||
op.balance_type = vesting_balance_type::unspecified;
|
||||
|
||||
op.validate();
|
||||
|
||||
trx.operations.push_back(op);
|
||||
trx.validate();
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// 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_member_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_member_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);
|
||||
|
||||
auto deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||
BOOST_CHECK_EQUAL(deposit_vesting.policy.get<dormant_vesting_policy>().dormant_mode, true); // dormant while active
|
||||
|
||||
{
|
||||
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_member_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.empty() );
|
||||
|
||||
deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||
BOOST_CHECK_EQUAL(deposit_vesting.policy.get<dormant_vesting_policy>().dormant_mode, false); // not sleeping anymore
|
||||
|
||||
auto now = db.head_block_time();
|
||||
|
||||
BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // but still cant withdraw
|
||||
|
||||
generate_blocks(now + fc::seconds(db.get_global_properties().parameters.son_vesting_period()));
|
||||
generate_block();
|
||||
|
||||
deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||
now = db.head_block_time();
|
||||
BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), true); // after 2 days withdraw is allowed
|
||||
}
|
||||
|
||||
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_member_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_member_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