Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e2c24f6b5 | ||
|
|
5fc9e92db6 | ||
|
|
c282ac4d0d | ||
|
|
1d8b6ed845 |
8 changed files with 147 additions and 275 deletions
|
|
@ -427,34 +427,6 @@ signed_block database::_generate_block(
|
||||||
_pending_tx_session.reset();
|
_pending_tx_session.reset();
|
||||||
_pending_tx_session = _undo_db.start_undo_session();
|
_pending_tx_session = _undo_db.start_undo_session();
|
||||||
|
|
||||||
if( head_block_time() > HARDFORK_SON_TIME )
|
|
||||||
{
|
|
||||||
// Approve proposals raised by me in previous schedule or before
|
|
||||||
process_son_proposals( witness_obj, block_signing_private_key );
|
|
||||||
// Check for new SON Deregistration Proposals to be raised
|
|
||||||
std::set<son_id_type> sons_to_be_dereg = get_sons_to_be_deregistered();
|
|
||||||
if(sons_to_be_dereg.size() > 0)
|
|
||||||
{
|
|
||||||
// We shouldn't raise proposals for the SONs for which a de-reg
|
|
||||||
// proposal is already raised.
|
|
||||||
std::set<son_id_type> sons_being_dereg = get_sons_being_deregistered();
|
|
||||||
for( auto& son : sons_to_be_dereg)
|
|
||||||
{
|
|
||||||
// New SON to be deregistered
|
|
||||||
if(sons_being_dereg.find(son) == sons_being_dereg.end())
|
|
||||||
{
|
|
||||||
// Creating the de-reg proposal
|
|
||||||
auto op = create_son_deregister_proposal(son, witness_obj);
|
|
||||||
if(op.valid())
|
|
||||||
{
|
|
||||||
// Signing and pushing into the txs to be included in the block
|
|
||||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *op ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t postponed_tx_count = 0;
|
uint64_t postponed_tx_count = 0;
|
||||||
// pop pending state (reset to head block state)
|
// pop pending state (reset to head block state)
|
||||||
for( const processed_transaction& tx : _pending_tx )
|
for( const processed_transaction& tx : _pending_tx )
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ std::set<son_id_type> database::get_sons_to_be_deregistered()
|
||||||
// TODO : We need to add a function that returns if we can deregister SON
|
// TODO : We need to add a function that returns if we can deregister SON
|
||||||
// i.e. with introduction of PW code, we have to make a decision if the SON
|
// i.e. with introduction of PW code, we have to make a decision if the SON
|
||||||
// is needed for release of funds from the PW
|
// is needed for release of funds from the PW
|
||||||
if(head_block_time() - stats.last_down_timestamp >= fc::hours(SON_DEREGISTER_TIME))
|
if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))
|
||||||
{
|
{
|
||||||
ret.insert(son.id);
|
ret.insert(son.id);
|
||||||
}
|
}
|
||||||
|
|
@ -194,14 +194,14 @@ std::set<son_id_type> database::get_sons_being_reported_down()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
fc::optional<operation> database::create_son_deregister_proposal(const son_id_type& son_id, const witness_object& current_witness )
|
fc::optional<operation> database::create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son )
|
||||||
{
|
{
|
||||||
son_delete_operation son_dereg_op;
|
son_delete_operation son_dereg_op;
|
||||||
son_dereg_op.payer = current_witness.witness_account;
|
son_dereg_op.payer = GRAPHENE_SON_ACCOUNT;
|
||||||
son_dereg_op.son_id = son_id;
|
son_dereg_op.son_id = son_id;
|
||||||
|
|
||||||
proposal_create_operation proposal_op;
|
proposal_create_operation proposal_op;
|
||||||
proposal_op.fee_paying_account = current_witness.witness_account;
|
proposal_op.fee_paying_account = paying_son;
|
||||||
proposal_op.proposed_ops.push_back( op_wrapper( son_dereg_op ) );
|
proposal_op.proposed_ops.push_back( op_wrapper( son_dereg_op ) );
|
||||||
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
|
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
|
||||||
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
|
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
|
||||||
|
|
@ -222,31 +222,6 @@ signed_transaction database::create_signed_transaction( const fc::ecc::private_k
|
||||||
return processed_trx;
|
return processed_trx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void database::process_son_proposals( const witness_object& current_witness, const fc::ecc::private_key& private_key )
|
|
||||||
{
|
|
||||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get< by_id >();
|
|
||||||
const auto& proposal_idx = get_index_type<proposal_index>().indices().get< by_id >();
|
|
||||||
|
|
||||||
auto approve_proposal = [ & ]( const proposal_id_type& id )
|
|
||||||
{
|
|
||||||
proposal_update_operation puo;
|
|
||||||
puo.fee_paying_account = current_witness.witness_account;
|
|
||||||
puo.proposal = id;
|
|
||||||
puo.active_approvals_to_add = { current_witness.witness_account };
|
|
||||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( private_key, puo ) );
|
|
||||||
};
|
|
||||||
|
|
||||||
for( auto& son_proposal : son_proposal_idx )
|
|
||||||
{
|
|
||||||
const auto& proposal = proposal_idx.find( son_proposal.proposal_id );
|
|
||||||
FC_ASSERT( proposal != proposal_idx.end() );
|
|
||||||
if( proposal->proposer == current_witness.witness_account)
|
|
||||||
{
|
|
||||||
approve_proposal( proposal->id );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void database::remove_son_proposal( const proposal_object& proposal )
|
void database::remove_son_proposal( const proposal_object& proposal )
|
||||||
{ try {
|
{ try {
|
||||||
if( proposal.proposed_transaction.operations.size() == 1 &&
|
if( proposal.proposed_transaction.operations.size() == 1 &&
|
||||||
|
|
@ -262,13 +237,13 @@ void database::remove_son_proposal( const proposal_object& proposal )
|
||||||
}
|
}
|
||||||
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
||||||
|
|
||||||
bool database::is_son_dereg_valid( const son_id_type& son_id )
|
bool database::is_son_dereg_valid( son_id_type son_id )
|
||||||
{
|
{
|
||||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||||
auto son = son_idx.find( son_id );
|
auto son = son_idx.find( son_id );
|
||||||
FC_ASSERT( son != son_idx.end() );
|
FC_ASSERT( son != son_idx.end() );
|
||||||
bool ret = ( son->status == son_status::in_maintenance &&
|
bool ret = ( son->status == son_status::in_maintenance &&
|
||||||
(head_block_time() - son->statistics(*this).last_down_timestamp >= fc::hours(SON_DEREGISTER_TIME)));
|
(head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,9 @@
|
||||||
#define MIN_SON_MEMBER_COUNT 15
|
#define MIN_SON_MEMBER_COUNT 15
|
||||||
#define SON_VESTING_AMOUNT (50*GRAPHENE_BLOCKCHAIN_PRECISION) // 50 PPY
|
#define SON_VESTING_AMOUNT (50*GRAPHENE_BLOCKCHAIN_PRECISION) // 50 PPY
|
||||||
#define SON_VESTING_PERIOD (60*60*24*2) // 2 days
|
#define SON_VESTING_PERIOD (60*60*24*2) // 2 days
|
||||||
#define SON_DEREGISTER_TIME (12) // 12 Hours
|
#define SON_DEREGISTER_TIME (60*60*12) // 12 Hours in seconds
|
||||||
|
#define SON_HEARTBEAT_FREQUENCY (60*3) // 3 minutes in seconds
|
||||||
|
#define SON_DOWN_TIME (60*3*2) // 2 Heartbeats in seconds
|
||||||
#define MIN_SON_PAY_DAILY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200))
|
#define MIN_SON_PAY_DAILY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200))
|
||||||
#define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)
|
#define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)
|
||||||
#define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0))
|
#define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0))
|
||||||
|
|
|
||||||
|
|
@ -302,11 +302,10 @@ namespace graphene { namespace chain {
|
||||||
std::set<son_id_type> get_sons_being_deregistered();
|
std::set<son_id_type> get_sons_being_deregistered();
|
||||||
std::set<son_id_type> get_sons_being_reported_down();
|
std::set<son_id_type> get_sons_being_reported_down();
|
||||||
std::set<son_id_type> get_sons_to_be_deregistered();
|
std::set<son_id_type> get_sons_to_be_deregistered();
|
||||||
fc::optional<operation> create_son_deregister_proposal(const son_id_type& son_id, const witness_object& current_witness );
|
fc::optional<operation> create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son );
|
||||||
signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op );
|
signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op );
|
||||||
void process_son_proposals( const witness_object& current_witness, const fc::ecc::private_key& private_key );
|
|
||||||
void remove_son_proposal( const proposal_object& proposal );
|
void remove_son_proposal( const proposal_object& proposal );
|
||||||
bool is_son_dereg_valid( const son_id_type& son_id );
|
bool is_son_dereg_valid( son_id_type son_id );
|
||||||
|
|
||||||
time_point_sec head_block_time()const;
|
time_point_sec head_block_time()const;
|
||||||
uint32_t head_block_num()const;
|
uint32_t head_block_num()const;
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@ namespace graphene { namespace chain {
|
||||||
optional < uint32_t > son_vesting_amount;
|
optional < uint32_t > son_vesting_amount;
|
||||||
optional < uint32_t > son_vesting_period;
|
optional < uint32_t > son_vesting_period;
|
||||||
optional < uint32_t > son_pay_daily_max;
|
optional < uint32_t > son_pay_daily_max;
|
||||||
|
optional < uint32_t > son_deregister_time;
|
||||||
|
optional < uint32_t > son_heartbeat_frequency;
|
||||||
|
optional < uint32_t > son_down_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct chain_parameters
|
struct chain_parameters
|
||||||
|
|
@ -138,6 +141,15 @@ namespace graphene { namespace chain {
|
||||||
inline uint16_t son_pay_daily_max()const {
|
inline uint16_t son_pay_daily_max()const {
|
||||||
return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX;
|
return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX;
|
||||||
}
|
}
|
||||||
|
inline uint16_t son_deregister_time()const {
|
||||||
|
return extensions.value.son_deregister_time.valid() ? *extensions.value.son_deregister_time : SON_DEREGISTER_TIME;
|
||||||
|
}
|
||||||
|
inline uint16_t son_heartbeat_frequency()const {
|
||||||
|
return extensions.value.son_heartbeat_frequency.valid() ? *extensions.value.son_heartbeat_frequency : SON_HEARTBEAT_FREQUENCY;
|
||||||
|
}
|
||||||
|
inline uint16_t son_down_time()const {
|
||||||
|
return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} } // graphene::chain
|
} } // graphene::chain
|
||||||
|
|
@ -155,6 +167,9 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
||||||
(son_vesting_amount)
|
(son_vesting_amount)
|
||||||
(son_vesting_period)
|
(son_vesting_period)
|
||||||
(son_pay_daily_max)
|
(son_pay_daily_max)
|
||||||
|
(son_deregister_time)
|
||||||
|
(son_heartbeat_frequency)
|
||||||
|
(son_down_time)
|
||||||
)
|
)
|
||||||
|
|
||||||
FC_REFLECT( graphene::chain::chain_parameters,
|
FC_REFLECT( graphene::chain::chain_parameters,
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ void_result delete_son_evaluator::do_evaluate(const son_delete_operation& op)
|
||||||
// Get the current block witness signatory
|
// Get the current block witness signatory
|
||||||
witness_id_type wit_id = db().get_scheduled_witness(1);
|
witness_id_type wit_id = db().get_scheduled_witness(1);
|
||||||
const witness_object& current_witness = wit_id(db());
|
const witness_object& current_witness = wit_id(db());
|
||||||
// Either owner can remove or witness
|
// Either owner can remove or consensus son account
|
||||||
FC_ASSERT(db().get(op.son_id).son_account == op.owner_account || (db().is_son_dereg_valid(op.son_id) && op.payer == current_witness.witness_account));
|
FC_ASSERT(op.payer == db().get(op.son_id).son_account || (db().is_son_dereg_valid(op.son_id) && op.payer == GRAPHENE_SON_ACCOUNT));
|
||||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||||
return void_result();
|
return void_result();
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ class peerplays_sidechain_plugin_impl
|
||||||
void schedule_heartbeat_loop();
|
void schedule_heartbeat_loop();
|
||||||
void heartbeat_loop();
|
void heartbeat_loop();
|
||||||
void create_son_down_proposals();
|
void create_son_down_proposals();
|
||||||
|
void create_son_deregister_proposals();
|
||||||
void recreate_primary_wallet();
|
void recreate_primary_wallet();
|
||||||
void process_deposits();
|
void process_deposits();
|
||||||
void process_withdrawals();
|
void process_withdrawals();
|
||||||
|
|
@ -285,9 +286,9 @@ fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::pub
|
||||||
void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop()
|
void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop()
|
||||||
{
|
{
|
||||||
fc::time_point now = fc::time_point::now();
|
fc::time_point now = fc::time_point::now();
|
||||||
int64_t time_to_next_heartbeat = 180000000;
|
int64_t time_to_next_heartbeat = plugin.database().get_global_properties().parameters.son_heartbeat_frequency();
|
||||||
|
|
||||||
fc::time_point next_wakeup( now + fc::microseconds( time_to_next_heartbeat ) );
|
fc::time_point next_wakeup( now + fc::seconds( time_to_next_heartbeat ) );
|
||||||
|
|
||||||
_heartbeat_task = fc::schedule([this]{heartbeat_loop();},
|
_heartbeat_task = fc::schedule([this]{heartbeat_loop();},
|
||||||
next_wakeup, "SON Heartbeat Production");
|
next_wakeup, "SON Heartbeat Production");
|
||||||
|
|
@ -323,6 +324,47 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void peerplays_sidechain_plugin_impl::create_son_deregister_proposals()
|
||||||
|
{
|
||||||
|
chain::database& d = plugin.database();
|
||||||
|
std::set<son_id_type> sons_to_be_dereg = d.get_sons_to_be_deregistered();
|
||||||
|
chain::son_id_type my_son_id = get_current_son_id();
|
||||||
|
|
||||||
|
if(sons_to_be_dereg.size() > 0)
|
||||||
|
{
|
||||||
|
// We shouldn't raise proposals for the SONs for which a de-reg
|
||||||
|
// proposal is already raised.
|
||||||
|
std::set<son_id_type> sons_being_dereg = d.get_sons_being_deregistered();
|
||||||
|
for( auto& son : sons_to_be_dereg)
|
||||||
|
{
|
||||||
|
// New SON to be deregistered
|
||||||
|
if(sons_being_dereg.find(son) == sons_being_dereg.end() && my_son_id != son)
|
||||||
|
{
|
||||||
|
// Creating the de-reg proposal
|
||||||
|
auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account);
|
||||||
|
if(op.valid())
|
||||||
|
{
|
||||||
|
// Signing and pushing into the txs to be included in the block
|
||||||
|
ilog("peerplays_sidechain_plugin: sending son deregister proposal for ${p} from ${s}", ("p", son) ("s", my_son_id));
|
||||||
|
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op);
|
||||||
|
fc::future<bool> fut = fc::async( [&](){
|
||||||
|
try {
|
||||||
|
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||||
|
if(plugin.app().p2p_node())
|
||||||
|
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||||
|
return true;
|
||||||
|
} catch(fc::exception e){
|
||||||
|
ilog("peerplays_sidechain_plugin_impl: sending son dereg proposal failed with exception ${e}",("e", e.what()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fut.wait(fc::seconds(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::create_son_down_proposals()
|
void peerplays_sidechain_plugin_impl::create_son_down_proposals()
|
||||||
{
|
{
|
||||||
auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) {
|
auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) {
|
||||||
|
|
@ -356,9 +398,9 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals()
|
||||||
auto stats = son_obj->statistics(d);
|
auto stats = son_obj->statistics(d);
|
||||||
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time);
|
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time);
|
||||||
int64_t down_threshold = 2*180000000;
|
int64_t down_threshold = gpo.parameters.son_down_time();
|
||||||
if(((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) &&
|
if(((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) &&
|
||||||
((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold))) {
|
((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) {
|
||||||
ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id))));
|
ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id))));
|
||||||
chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts);
|
chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts);
|
||||||
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op);
|
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op);
|
||||||
|
|
@ -412,6 +454,8 @@ void peerplays_sidechain_plugin_impl::on_applied_block( const signed_block& b )
|
||||||
|
|
||||||
create_son_down_proposals();
|
create_son_down_proposals();
|
||||||
|
|
||||||
|
create_son_deregister_proposals();
|
||||||
|
|
||||||
recreate_primary_wallet();
|
recreate_primary_wallet();
|
||||||
|
|
||||||
process_deposits();
|
process_deposits();
|
||||||
|
|
@ -467,6 +511,12 @@ void peerplays_sidechain_plugin_impl::on_new_objects(const vector<object_id_type
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(proposal->proposed_transaction.operations.size() == 1
|
||||||
|
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_delete_operation>::value) {
|
||||||
|
approve_proposal( son_id, proposal->id );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(proposal->proposed_transaction.operations.size() == 1
|
if(proposal->proposed_transaction.operations.size() == 1
|
||||||
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_wallet_update_operation>::value) {
|
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_wallet_update_operation>::value) {
|
||||||
approve_proposal( son_id, proposal->id );
|
approve_proposal( son_id, proposal->id );
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,71 @@ catch (fc::exception &e) {
|
||||||
throw;
|
throw;
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( delete_son_test_with_consensus_account ) {
|
||||||
|
try {
|
||||||
|
INVOKE(create_son_test);
|
||||||
|
GET_ACTOR(alice);
|
||||||
|
GET_ACTOR(bob);
|
||||||
|
|
||||||
|
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() );
|
||||||
|
|
||||||
|
const auto& sidx = db.get_index_type<son_stats_index>().indices().get<by_id>();
|
||||||
|
BOOST_REQUIRE( sidx.size() == 1 );
|
||||||
|
auto son_stats_obj = sidx.find( obj->statistics );
|
||||||
|
BOOST_REQUIRE( son_stats_obj != sidx.end() );
|
||||||
|
|
||||||
|
// Modify SON's status to active
|
||||||
|
db.modify( *obj, [&]( son_object& _s)
|
||||||
|
{
|
||||||
|
_s.status = son_status::in_maintenance;
|
||||||
|
});
|
||||||
|
|
||||||
|
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||||
|
{
|
||||||
|
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
||||||
|
});
|
||||||
|
|
||||||
|
auto deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||||
|
auto now = db.head_block_time();
|
||||||
|
BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // cant withdraw
|
||||||
|
|
||||||
|
{
|
||||||
|
trx.clear();
|
||||||
|
son_delete_operation op;
|
||||||
|
op.owner_account = alice_id;
|
||||||
|
op.son_id = son_id_type(0);
|
||||||
|
op.payer = GRAPHENE_SON_ACCOUNT;
|
||||||
|
|
||||||
|
trx.operations.push_back(op);
|
||||||
|
sign(trx, bob_private_key);
|
||||||
|
PUSH_TX(db, trx, ~0);
|
||||||
|
}
|
||||||
|
generate_block();
|
||||||
|
|
||||||
|
BOOST_REQUIRE( idx.size() == 0 );
|
||||||
|
|
||||||
|
deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||||
|
BOOST_CHECK_EQUAL(deposit_vesting.policy.get<linear_vesting_policy>().vesting_cliff_seconds,
|
||||||
|
db.get_global_properties().parameters.son_vesting_period()); // in linear policy
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
catch (fc::exception &e) {
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
} }
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE( update_delete_not_own ) { // fee payer needs to be the son object owner
|
BOOST_AUTO_TEST_CASE( update_delete_not_own ) { // fee payer needs to be the son object owner
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
@ -458,212 +523,6 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE( son_witness_proposal_test )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const dynamic_global_property_object& dpo = db.get_dynamic_global_properties();
|
|
||||||
generate_blocks(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( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) );
|
|
||||||
transfer( committee_account, bob_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) );
|
|
||||||
|
|
||||||
set_expiration(db, trx);
|
|
||||||
generate_block();
|
|
||||||
// Now create SONs
|
|
||||||
std::string test_url1 = "https://create_son_test1";
|
|
||||||
std::string test_url2 = "https://create_son_test2";
|
|
||||||
|
|
||||||
// create deposit vesting
|
|
||||||
vesting_balance_id_type deposit1;
|
|
||||||
{
|
|
||||||
vesting_balance_create_operation op;
|
|
||||||
op.creator = alice_id;
|
|
||||||
op.owner = alice_id;
|
|
||||||
op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION);
|
|
||||||
op.balance_type = vesting_balance_type::son;
|
|
||||||
op.policy = dormant_vesting_policy_initializer {};
|
|
||||||
|
|
||||||
trx.operations.push_back(op);
|
|
||||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
|
||||||
set_expiration(db, trx);
|
|
||||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
|
||||||
trx.clear();
|
|
||||||
deposit1 = ptx.operation_results[0].get<object_id_type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// create payment vesting
|
|
||||||
vesting_balance_id_type payment1;
|
|
||||||
{
|
|
||||||
vesting_balance_create_operation op;
|
|
||||||
op.creator = alice_id;
|
|
||||||
op.owner = alice_id;
|
|
||||||
op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION);
|
|
||||||
op.balance_type = vesting_balance_type::normal;
|
|
||||||
|
|
||||||
trx.operations.push_back(op);
|
|
||||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
|
||||||
set_expiration(db, trx);
|
|
||||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
|
||||||
trx.clear();
|
|
||||||
payment1 = ptx.operation_results[0].get<object_id_type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// create deposit vesting
|
|
||||||
vesting_balance_id_type deposit2;
|
|
||||||
{
|
|
||||||
vesting_balance_create_operation op;
|
|
||||||
op.creator = bob_id;
|
|
||||||
op.owner = bob_id;
|
|
||||||
op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION);
|
|
||||||
op.balance_type = vesting_balance_type::son;
|
|
||||||
op.policy = dormant_vesting_policy_initializer {};
|
|
||||||
|
|
||||||
trx.operations.push_back(op);
|
|
||||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
|
||||||
set_expiration(db, trx);
|
|
||||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
|
||||||
trx.clear();
|
|
||||||
deposit2 = ptx.operation_results[0].get<object_id_type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// create payment vesting
|
|
||||||
vesting_balance_id_type payment2;
|
|
||||||
{
|
|
||||||
vesting_balance_create_operation op;
|
|
||||||
op.creator = bob_id;
|
|
||||||
op.owner = bob_id;
|
|
||||||
op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION);
|
|
||||||
op.balance_type = vesting_balance_type::normal;
|
|
||||||
|
|
||||||
trx.operations.push_back(op);
|
|
||||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
|
||||||
set_expiration(db, trx);
|
|
||||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
|
||||||
trx.clear();
|
|
||||||
payment2 = ptx.operation_results[0].get<object_id_type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// alice becomes son
|
|
||||||
{
|
|
||||||
son_create_operation op;
|
|
||||||
op.owner_account = alice_id;
|
|
||||||
op.url = test_url1;
|
|
||||||
op.deposit = deposit1;
|
|
||||||
op.pay_vb = payment1;
|
|
||||||
op.fee = asset(0);
|
|
||||||
op.signing_key = alice_public_key;
|
|
||||||
trx.operations.push_back(op);
|
|
||||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
|
||||||
sign(trx, alice_private_key);
|
|
||||||
PUSH_TX(db, trx, ~0);
|
|
||||||
trx.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// bob becomes son
|
|
||||||
{
|
|
||||||
son_create_operation op;
|
|
||||||
op.owner_account = bob_id;
|
|
||||||
op.url = test_url2;
|
|
||||||
op.deposit = deposit2;
|
|
||||||
op.pay_vb = payment2;
|
|
||||||
op.fee = asset(0);
|
|
||||||
op.signing_key = bob_public_key;
|
|
||||||
trx.operations.push_back(op);
|
|
||||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
|
||||||
sign(trx, bob_private_key);
|
|
||||||
PUSH_TX(db, trx, ~0);
|
|
||||||
trx.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_block();
|
|
||||||
// Check if SONs are created properly
|
|
||||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
|
||||||
BOOST_REQUIRE( idx.size() == 2 );
|
|
||||||
// Alice's SON
|
|
||||||
auto obj1 = idx.find( alice_id );
|
|
||||||
BOOST_REQUIRE( obj1 != idx.end() );
|
|
||||||
BOOST_CHECK( obj1->url == test_url1 );
|
|
||||||
BOOST_CHECK( obj1->signing_key == alice_public_key );
|
|
||||||
BOOST_CHECK( obj1->deposit.instance == deposit1.instance.value );
|
|
||||||
BOOST_CHECK( obj1->pay_vb.instance == payment1.instance.value );
|
|
||||||
// Bob's SON
|
|
||||||
auto obj2 = idx.find( bob_id );
|
|
||||||
BOOST_REQUIRE( obj2 != idx.end() );
|
|
||||||
BOOST_CHECK( obj2->url == test_url2 );
|
|
||||||
BOOST_CHECK( obj2->signing_key == bob_public_key );
|
|
||||||
BOOST_CHECK( obj2->deposit.instance == deposit2.instance.value );
|
|
||||||
BOOST_CHECK( obj2->pay_vb.instance == payment2.instance.value );
|
|
||||||
// Get the statistics object for the SONs
|
|
||||||
const auto& sidx = db.get_index_type<son_stats_index>().indices().get<by_id>();
|
|
||||||
BOOST_REQUIRE( sidx.size() == 2 );
|
|
||||||
auto son_stats_obj1 = sidx.find( obj1->statistics );
|
|
||||||
auto son_stats_obj2 = sidx.find( obj2->statistics );
|
|
||||||
BOOST_REQUIRE( son_stats_obj1 != sidx.end() );
|
|
||||||
BOOST_REQUIRE( son_stats_obj2 != sidx.end() );
|
|
||||||
|
|
||||||
|
|
||||||
// Modify SON's status to in_maintenance
|
|
||||||
db.modify( *obj1, [&]( son_object& _s)
|
|
||||||
{
|
|
||||||
_s.status = son_status::in_maintenance;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Modify the Alice's SON down timestamp to now-12 hours
|
|
||||||
db.modify( *son_stats_obj1, [&]( son_statistics_object& _s)
|
|
||||||
{
|
|
||||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - fc::hours(12));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Modify SON's status to in_maintenance
|
|
||||||
db.modify( *obj2, [&]( son_object& _s)
|
|
||||||
{
|
|
||||||
_s.status = son_status::in_maintenance;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Modify the Bob's SON down timestamp to now-12 hours
|
|
||||||
db.modify( *son_stats_obj2, [&]( son_statistics_object& _s)
|
|
||||||
{
|
|
||||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - fc::hours(12));
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto& son_proposal_idx = db.get_index_type<son_proposal_index>().indices().get<by_id>();
|
|
||||||
const auto& proposal_idx = db.get_index_type<proposal_index>().indices().get<by_id>();
|
|
||||||
|
|
||||||
BOOST_CHECK( son_proposal_idx.size() == 0 && proposal_idx.size() == 0 );
|
|
||||||
|
|
||||||
generate_block();
|
|
||||||
witness_id_type proposal_initiator = dpo.current_witness;
|
|
||||||
|
|
||||||
BOOST_CHECK( son_proposal_idx.size() == 2 && proposal_idx.size() == 2 );
|
|
||||||
|
|
||||||
for(size_t i = 0 ; i < 3 * db.get_global_properties().active_witnesses.size() ; i++ )
|
|
||||||
{
|
|
||||||
generate_block();
|
|
||||||
if( dpo.current_witness != proposal_initiator)
|
|
||||||
{
|
|
||||||
BOOST_CHECK( son_proposal_idx.size() == 2 && proposal_idx.size() == 2 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BOOST_CHECK( son_proposal_idx.size() == 0 && proposal_idx.size() == 0 );
|
|
||||||
BOOST_REQUIRE( idx.size() == 0 );
|
|
||||||
generate_block();
|
|
||||||
} FC_LOG_AND_RETHROW()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue