From 8613bab257d392139febb022cba2f8c8e53562f4 Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Thu, 29 Aug 2019 10:34:15 -0300
Subject: [PATCH 001/151] issue - 154: Don't allow to vote when vesting balance
is 0
---
libraries/wallet/wallet.cpp | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index 812740e6..46acf25e 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -1995,6 +1995,13 @@ public:
bool approve,
bool broadcast /* = false */)
{ try {
+ std::vector vbo_info = get_vesting_balances(voting_account);
+ std::vector::iterator vbo_iter;
+
+ vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;});
+ if( vbo_info.size() == 0 || vbo_iter == vbo_info.end())
+ FC_THROW("Account *** ${account} *** have insufficient or 0 vested balance(GPOS) to vote", ("account", voting_account));
+
account_object voting_account_object = get_account(voting_account);
account_id_type committee_member_owner_account_id = get_account_id(committee_member);
fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member_owner_account_id);
@@ -2029,6 +2036,13 @@ public:
bool approve,
bool broadcast /* = false */)
{ try {
+ std::vector vbo_info = get_vesting_balances(voting_account);
+ std::vector::iterator vbo_iter;
+
+ vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;});
+ if( vbo_info.size() == 0 || vbo_iter == vbo_info.end())
+ FC_THROW("Account *** ${account} *** have insufficient or 0 vested balance(GPOS) to vote", ("account", voting_account));
+
account_object voting_account_object = get_account(voting_account);
account_id_type witness_owner_account_id = get_account_id(witness);
fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
From 2a3d8a4c66456c18a7d7bd0790477a2f59295799 Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Fri, 20 Sep 2019 11:32:07 -0300
Subject: [PATCH 002/151] changes to withdraw_vesting feature(for both cdd and
GPOS)
---
libraries/chain/db_maint.cpp | 4 +-
.../chain/include/graphene/chain/config.hpp | 1 +
.../chain/protocol/chain_parameters.hpp | 5 ++
.../graphene/chain/protocol/vesting.hpp | 4 +-
.../graphene/chain/vesting_balance_object.hpp | 2 +-
libraries/chain/proposal_evaluator.cpp | 2 +-
libraries/chain/vesting_balance_evaluator.cpp | 4 +-
.../wallet/include/graphene/wallet/wallet.hpp | 4 +-
libraries/wallet/wallet.cpp | 74 +++++++++++++++----
9 files changed, 75 insertions(+), 25 deletions(-)
diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp
index 06e15a19..81fce8f9 100644
--- a/libraries/chain/db_maint.cpp
+++ b/libraries/chain/db_maint.cpp
@@ -873,7 +873,7 @@ void schedule_pending_dividend_balances(database& db,
std::map vesting_amounts;
- auto balance_type = vesting_balance_type::unspecified;
+ auto balance_type = vesting_balance_type::normal;
if(db.head_block_time() >= HARDFORK_GPOS_TIME)
balance_type = vesting_balance_type::gpos;
@@ -1403,7 +1403,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
d._total_voting_stake = 0;
- auto balance_type = vesting_balance_type::unspecified;
+ auto balance_type = vesting_balance_type::normal;
if(d.head_block_time() >= HARDFORK_GPOS_TIME)
balance_type = vesting_balance_type::gpos;
diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp
index 7b3e8743..fd080b09 100644
--- a/libraries/chain/include/graphene/chain/config.hpp
+++ b/libraries/chain/include/graphene/chain/config.hpp
@@ -228,3 +228,4 @@
#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 GPOS_VESTING_LOCKIN_PERIOD (60*60*24*30) // 1 month
diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
index 87c2e3fe..a66e4ba8 100644
--- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
+++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
@@ -43,6 +43,7 @@ namespace graphene { namespace chain {
optional < uint32_t > gpos_period;
optional < uint32_t > gpos_subperiod;
optional < uint32_t > gpos_period_start;
+ optional < uint32_t > gpos_vesting_lockin_period;
};
struct chain_parameters
@@ -121,6 +122,9 @@ 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 gpos_vesting_lockin_period()const {
+ return extensions.value.gpos_vesting_lockin_period.valid() ? *extensions.value.gpos_vesting_lockin_period : GPOS_VESTING_LOCKIN_PERIOD; /// GPOS vesting lockin period
+ }
};
} } // graphene::chain
@@ -134,6 +138,7 @@ FC_REFLECT( graphene::chain::parameter_extension,
(gpos_period)
(gpos_subperiod)
(gpos_period_start)
+ (gpos_vesting_lockin_period)
)
FC_REFLECT( graphene::chain::chain_parameters,
diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp
index 5a78fd65..ac995aaf 100644
--- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp
+++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp
@@ -26,7 +26,7 @@
namespace graphene { namespace chain {
- enum class vesting_balance_type { unspecified, gpos };
+ enum class vesting_balance_type { normal, gpos };
struct linear_vesting_policy_initializer
{
@@ -122,4 +122,4 @@ FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)
FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_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, (normal)(gpos) )
diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp
index 6e0bd689..a94e7015 100644
--- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp
+++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp
@@ -146,7 +146,7 @@ namespace graphene { namespace chain {
vesting_policy policy;
/// We can have 2 types of vesting, gpos and all the rest
- vesting_balance_type balance_type = vesting_balance_type::unspecified;
+ vesting_balance_type balance_type = vesting_balance_type::normal;
vesting_balance_object() {}
diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp
index 8306128d..a690ab33 100644
--- a/libraries/chain/proposal_evaluator.cpp
+++ b/libraries/chain/proposal_evaluator.cpp
@@ -137,7 +137,7 @@ struct proposal_operation_hardfork_visitor
void operator()(const vesting_balance_create_operation &vbco) const {
if(block_time < HARDFORK_GPOS_TIME)
- FC_ASSERT( vbco.balance_type == vesting_balance_type::unspecified, "balance_type in vesting create not allowed yet!" );
+ FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" );
}
// loop and self visit in proposals
diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp
index 0b6e192e..bd44b934 100644
--- a/libraries/chain/vesting_balance_evaluator.cpp
+++ b/libraries/chain/vesting_balance_evaluator.cpp
@@ -43,7 +43,7 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance
FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() );
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);
+ FC_ASSERT( op.balance_type == vesting_balance_type::normal);
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@@ -101,7 +101,7 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance
// forcing gpos policy
linear_vesting_policy p;
p.begin_timestamp = now;
- p.vesting_cliff_seconds = gpo.parameters.gpos_subperiod();
+ p.vesting_cliff_seconds = gpo.parameters.gpos_vesting_lockin_period();
p.vesting_duration_seconds = gpo.parameters.gpos_subperiod();
obj.policy = p;
}
diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp
index a7189138..2b8012b4 100644
--- a/libraries/wallet/include/graphene/wallet/wallet.hpp
+++ b/libraries/wallet/include/graphene/wallet/wallet.hpp
@@ -1349,12 +1349,14 @@ class wallet_api
* @param amount The amount to withdraw.
* @param asset_symbol The symbol of the asset to withdraw.
* @param broadcast true if you wish to broadcast the transaction
+ * @param vb_type vestig balance type to withdraw 0-OLD, 1-GPOS, 2-SONS(if required)
*/
signed_transaction withdraw_vesting(
string witness_name,
string amount,
string asset_symbol,
- bool broadcast = false);
+ bool broadcast = false,
+ uint8_t vb_type = 0);
/** Vote for a given committee_member.
*
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index 46acf25e..75a90f82 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -1963,26 +1963,64 @@ public:
string witness_name,
string amount,
string asset_symbol,
- bool broadcast = false )
+ bool broadcast = false,
+ uint8_t vb_type = 0 )
{ try {
asset_object asset_obj = get_asset( asset_symbol );
+ vector< vesting_balance_object > vbos;
fc::optional vbid = maybe_id(witness_name);
if( !vbid )
{
- witness_object wit = get_witness( witness_name );
- FC_ASSERT( wit.pay_vb );
- vbid = wit.pay_vb;
+ //Changes done to retrive user accounts along with witnesses accounts based on account name
+ fc::optional acct_id = maybe_id( witness_name );
+ if( !acct_id )
+ acct_id = get_account( witness_name ).id;
+
+ vbos = _remote_db->get_vesting_balances( *acct_id );
+ if( vbos.size() == 0 )
+ {
+ witness_object wit = get_witness( witness_name );
+ FC_ASSERT( wit.pay_vb );
+ vbid = wit.pay_vb;
+ }
}
- vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid );
- vesting_balance_withdraw_operation vesting_balance_withdraw_op;
-
- vesting_balance_withdraw_op.vesting_balance = *vbid;
- vesting_balance_withdraw_op.owner = vbo.owner;
- vesting_balance_withdraw_op.amount = asset_obj.amount_from_string(amount);
-
+ //whether it is a witness or user, keep in container and iterate over it process all vesting balances and types
+ if(!vbos.size())
+ vbos.emplace_back( get_object(*vbid) );
+
signed_transaction tx;
- tx.operations.push_back( vesting_balance_withdraw_op );
+ asset withdraw_amount = asset_obj.amount_from_string(amount);
+
+ for(const vesting_balance_object& vbo: vbos )
+ {
+ if((vb_type == (uint8_t)vbo.balance_type) && vbo.balance.amount > 0)
+ {
+ fc::optional vest_id = vbo.id;
+ vesting_balance_withdraw_operation vesting_balance_withdraw_op;
+
+ vesting_balance_withdraw_op.vesting_balance = *vest_id;
+ vesting_balance_withdraw_op.owner = vbo.owner;
+ if(withdraw_amount.amount >= vbo.balance.amount)
+ {
+ vesting_balance_withdraw_op.amount = vbo.balance.amount;
+ withdraw_amount.amount -= vbo.balance.amount;
+ }
+ else
+ {
+ vesting_balance_withdraw_op.amount = withdraw_amount.amount;
+ tx.operations.push_back( vesting_balance_withdraw_op );
+ withdraw_amount.amount -= vbo.balance.amount;
+ break;
+ }
+
+ tx.operations.push_back( vesting_balance_withdraw_op );
+ }
+ }
+
+ if( withdraw_amount.amount > 0)
+ FC_THROW("Account has insufficient balance to withdraw");
+
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees );
tx.validate();
@@ -4045,9 +4083,10 @@ signed_transaction wallet_api::withdraw_vesting(
string witness_name,
string amount,
string asset_symbol,
- bool broadcast /* = false */)
+ bool broadcast,
+ uint8_t vb_type)
{
- return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast );
+ return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast, vb_type );
}
signed_transaction wallet_api::vote_for_committee_member(string voting_account,
@@ -5783,7 +5822,7 @@ signed_transaction wallet_api::create_vesting_balance(string owner,
fc::optional asset_obj = get_asset(asset_symbol);
- auto type = vesting_balance_type::unspecified;
+ auto type = vesting_balance_type::normal;
if(is_gpos)
type = vesting_balance_type::gpos;
@@ -5856,7 +5895,10 @@ vesting_balance_object_with_info::vesting_balance_object_with_info( const vestin
: vesting_balance_object( vbo )
{
allowed_withdraw = get_allowed_withdraw( now );
- allowed_withdraw_time = now;
+ if(vbo.balance_type == vesting_balance_type::gpos)
+ allowed_withdraw_time = vbo.policy.get().begin_timestamp + vbo.policy.get().vesting_cliff_seconds;
+ else
+ allowed_withdraw_time = now;
}
} } // graphene::wallet
From b358241e43fbaca0669c9ab34d4116e96822bb9d Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Fri, 20 Sep 2019 14:03:59 -0300
Subject: [PATCH 003/151] Comments update
---
libraries/wallet/wallet.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index 75a90f82..185fc7d1 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -1971,7 +1971,7 @@ public:
fc::optional vbid = maybe_id(witness_name);
if( !vbid )
{
- //Changes done to retrive user accounts along with witnesses accounts based on account name
+ //Changes done to retrive user account/witness account based on account name
fc::optional acct_id = maybe_id( witness_name );
if( !acct_id )
acct_id = get_account( witness_name ).id;
@@ -1985,7 +1985,7 @@ public:
}
}
- //whether it is a witness or user, keep in container and iterate over it process all vesting balances and types
+ //whether it is a witness or user, keep it in a container and iterate over to process all vesting balances and types
if(!vbos.size())
vbos.emplace_back( get_object(*vbid) );
From 8e1c0385589e68ce879f07bc8b131690ba9b6090 Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Fri, 20 Sep 2019 16:58:06 -0300
Subject: [PATCH 004/151] update to GPOS hardfork ref
---
.../chain/include/graphene/chain/protocol/chain_parameters.hpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
index a66e4ba8..b020c4b4 100644
--- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
+++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
@@ -27,7 +27,7 @@
#include
#include
-#include
+#include <../hardfork.d/GPOS.hf>
namespace graphene { namespace chain { struct fee_schedule; } }
From 4a72f943e8347fd1125d258015632c9fc9361de0 Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Sat, 21 Sep 2019 13:04:43 -0300
Subject: [PATCH 005/151] fix for get_vesting_balance API call
---
libraries/chain/vesting_balance_object.cpp | 35 ++++++++++++++--------
1 file changed, 23 insertions(+), 12 deletions(-)
diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp
index 73448e04..794413d1 100644
--- a/libraries/chain/vesting_balance_object.cpp
+++ b/libraries/chain/vesting_balance_object.cpp
@@ -35,6 +35,7 @@ inline bool sum_below_max_shares(const asset& a, const asset& b)
}
asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const
+{
{
share_type allowed_withdraw = 0;
@@ -45,23 +46,33 @@ asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context&
if( elapsed_seconds >= vesting_cliff_seconds )
{
- share_type total_vested = 0;
- if( elapsed_seconds < vesting_duration_seconds )
+ // BLOCKBACK-154 fix, Begin balance for linear vesting applies only to initial account balance from genesis
+ // So, for any GPOS vesting, the begin balance would be 0 and should be able to withdraw balance amount based on lockin period
+ if(begin_balance == 0)
{
- total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64();
+ allowed_withdraw = ctx.balance.amount;
+ return asset( allowed_withdraw, ctx.balance.asset_id );
}
else
{
- total_vested = begin_balance;
+ 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 );
}
- 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 );
From a7df686ebe92e6ac25c310a714bd34d26dc6c612 Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Sat, 21 Sep 2019 13:08:33 -0300
Subject: [PATCH 006/151] braces update
---
libraries/chain/vesting_balance_object.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp
index 794413d1..afba2557 100644
--- a/libraries/chain/vesting_balance_object.cpp
+++ b/libraries/chain/vesting_balance_object.cpp
@@ -35,7 +35,6 @@ inline bool sum_below_max_shares(const asset& a, const asset& b)
}
asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const
-{
{
share_type allowed_withdraw = 0;
From 83b19d0b8487fe048a65410cd3f99cf78d4e39b1 Mon Sep 17 00:00:00 2001
From: Wei Yang
Date: Wed, 30 May 2018 16:30:03 +0800
Subject: [PATCH 007/151] node.cpp: Check the attacker/buggy client before
updating items ids
The peer is an attacker or buggy, which means the item_hashes_received is
not correct.
Move the check before updating items ids to save some time in this case.
---
libraries/net/node.cpp | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp
index a38199fd..7a978017 100644
--- a/libraries/net/node.cpp
+++ b/libraries/net/node.cpp
@@ -2649,11 +2649,6 @@ namespace graphene { namespace net { namespace detail {
if (!item_hashes_received.empty() && !originating_peer->ids_of_items_to_get.empty())
assert(item_hashes_received.front() != originating_peer->ids_of_items_to_get.back());
- // append the remaining items to the peer's list
- boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received);
-
- originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count;
-
// at any given time, there's a maximum number of blocks that can possibly be out there
// [(now - genesis time) / block interval]. If they offer us more blocks than that,
// they must be an attacker or have a buggy client.
@@ -2676,6 +2671,12 @@ namespace graphene { namespace net { namespace detail {
return;
}
+
+ // append the remaining items to the peer's list
+ boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received);
+
+ originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count;
+
uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers();
if (new_number_of_unfetched_items != _total_number_of_unfetched_items)
_delegate->sync_status(blockchain_item_ids_inventory_message_received.item_type,
From db01f313e5d0b80f1dd75d50e8e7173bc9e50c02 Mon Sep 17 00:00:00 2001
From: Roshan Syed
Date: Wed, 25 Sep 2019 10:30:15 -0300
Subject: [PATCH 008/151] Create .gitlab-ci.yml
---
.gitlab-ci.yml | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 .gitlab-ci.yml
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..620c6673
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,29 @@
+stages:
+ - build
+ - test
+
+build:
+ stage: build
+ script:
+ - git submodule update --init --recursive
+ - cmake .
+ - make -j$(nproc)
+ artifacts:
+ untracked: true
+ paths:
+ - libraries/
+ - programs/
+ - tests/
+ tags:
+ - builder
+
+test:
+ stage: test
+ dependencies:
+ - build
+ script:
+ - ./tests/betting_test
+ - ./tests/chain_test
+ - ./tests/cli_test
+ tags:
+ - builder
From 7fae375e0f03f61901c43b66276f3f5d46b78c86 Mon Sep 17 00:00:00 2001
From: Bobinson K B
Date: Thu, 26 Sep 2019 11:41:28 -0400
Subject: [PATCH 009/151] fixing build errors (#150)
* fixing build errors
vest type correction
* fixing build errors
vest type correction
* fixes
new Dockerfile
* vesting_balance_type correction
vesting_balance_type changed to normal
* gcc5 support to Dockerfile
gcc5 support to Dockerfile
---
Dockerfile | 70 +++++++++++++++++++++-----------
tests/tests/operation_tests.cpp | 6 +--
tests/tests/operation_tests2.cpp | 4 +-
3 files changed, 51 insertions(+), 29 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index a3cc326a..fa7cb87a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,44 +1,66 @@
-FROM phusion/baseimage:0.9.19
+FROM ubuntu:18.04
MAINTAINER PeerPlays Blockchain Standards Association
-ENV LANG=en_US.UTF-8
+ENV LANG en_US.UTF-8
+ENV LANGUAGE en_US.UTF-8
+ENV LC_ALL en_US.UTF-8
+
RUN \
apt-get update -y && \
- apt-get install -y \
- g++ \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y \
autoconf \
- cmake \
- git \
- libbz2-dev \
- libreadline-dev \
- libboost-all-dev \
- libcurl4-openssl-dev \
- libssl-dev \
- libncurses-dev \
- doxygen \
+ gcc-5 \
+ g++-5 \
+ bash \
+ build-essential \
ca-certificates \
+ cmake \
+ doxygen \
+ git \
+ graphviz \
+ libbz2-dev \
+ libcurl4-openssl-dev \
+ libncurses-dev \
+ libreadline-dev \
+ libssl-dev \
+ libtool \
+ locales \
+ ntp \
+ pkg-config \
+ wget \
&& \
- apt-get update -y && \
- apt-get install -y fish && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+RUN \
+ sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
+ locale-gen
+
+# Compile Boost
+RUN \
+ BOOST_ROOT=$HOME/boost_1_67_0 && \
+ wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.gz/download' -O boost_1_67_0.tar.gz &&\
+ tar -zxvf boost_1_67_0.tar.gz && \
+ cd boost_1_67_0/ && \
+ ./bootstrap.sh "--prefix=$BOOST_ROOT" && \
+ ./b2 install && \
+ cd ..
+
ADD . /peerplays-core
WORKDIR /peerplays-core
-# Compile
+# Compile Peerplays
RUN \
- ( git submodule sync --recursive || \
- find `pwd` -type f -name .git | \
- while read f; do \
- rel="$(echo "${f#$PWD/}" | sed 's=[^/]*/=../=g')"; \
- sed -i "s=: .*/.git/=: $rel/=" "$f"; \
- done && \
- git submodule sync --recursive ) && \
+ BOOST_ROOT=$HOME/boost_1_67_0 && \
+ export CC=gcc-5 ; export CXX=g++-5\
git submodule update --init --recursive && \
+ mkdir build && \
+ mkdir build/release && \
+ cd build/release && \
cmake \
+ -DBOOST_ROOT="$BOOST_ROOT" \
-DCMAKE_BUILD_TYPE=Release \
- . && \
+ ../.. && \
make witness_node cli_wallet && \
install -s programs/witness_node/witness_node programs/cli_wallet/cli_wallet /usr/local/bin && \
#
diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp
index c1278021..e04db96c 100644
--- a/tests/tests/operation_tests.cpp
+++ b/tests/tests/operation_tests.cpp
@@ -1560,7 +1560,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test )
op.amount = test_asset.amount( 100 );
//op.vesting_seconds = 60*60*24;
op.policy = cdd_vesting_policy_initializer{ 60*60*24 };
- op.balance_type == vesting_balance_type::unspecified;
+ op.balance_type == vesting_balance_type::normal;
// Fee must be non-negative
REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(1) );
@@ -1580,7 +1580,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test )
op.creator = alice_account.get_id();
op.owner = alice_account.get_id();
- op.balance_type = vesting_balance_type::unspecified;
+ op.balance_type = vesting_balance_type::normal;
account_id_type nobody = account_id_type(1234);
@@ -1651,7 +1651,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test )
create_op.owner = owner;
create_op.amount = amount;
create_op.policy = cdd_vesting_policy_initializer(vesting_seconds);
- create_op.balance_type = vesting_balance_type::unspecified;
+ create_op.balance_type = vesting_balance_type::normal;
tx.operations.push_back( create_op );
set_expiration( db, tx );
diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp
index 9b6bb5ee..834d2d42 100644
--- a/tests/tests/operation_tests2.cpp
+++ b/tests/tests/operation_tests2.cpp
@@ -1312,7 +1312,7 @@ BOOST_AUTO_TEST_CASE(zero_second_vbo)
create_op.owner = alice_id;
create_op.amount = asset(500);
create_op.policy = pinit;
- create_op.balance_type = vesting_balance_type::unspecified;
+ create_op.balance_type = vesting_balance_type::normal;
signed_transaction create_tx;
create_tx.operations.push_back( create_op );
@@ -1396,7 +1396,7 @@ BOOST_AUTO_TEST_CASE( vbo_withdraw_different )
create_op.owner = alice_id;
create_op.amount = asset(100, stuff_id);
create_op.policy = pinit;
- create_op.balance_type = vesting_balance_type::unspecified;
+ create_op.balance_type = vesting_balance_type::normal;
signed_transaction create_tx;
create_tx.operations.push_back( create_op );
From f1eb625df8a23d6fa5498e093cf4fe25a0c41699 Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Mon, 30 Sep 2019 00:27:21 -0300
Subject: [PATCH 010/151] Changes to compiple with GCC 7(Ubuntu 18.04)
---
CMakeLists.txt | 4 ++++
.../chain/include/graphene/chain/vesting_balance_object.hpp | 4 ++--
libraries/net/CMakeLists.txt | 2 +-
libraries/wallet/CMakeLists.txt | 2 +-
4 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 20d96a9a..e939f113 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -120,6 +120,10 @@ else( WIN32 ) # Apple AND Linux
set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -Wall" )
set( rt_library rt )
set( pthread_library pthread)
+ set(CMAKE_LINKER_FLAGS "-pthread" CACHE STRING "Linker Flags" FORCE)
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_LINKER_FLAGS}" CACHE STRING "" FORCE)
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS}" CACHE STRING "" FORCE)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS}" CACHE STRING "" FORCE)
if ( NOT DEFINED crypto_library )
# I'm not sure why this is here, I guess someone has openssl and can't detect it with find_package()?
# if you have a normal install, you can define crypto_library to the empty string to avoid a build error
diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp
index a94e7015..ec789f30 100644
--- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp
+++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp
@@ -189,9 +189,9 @@ namespace graphene { namespace chain {
ordered_non_unique< tag,
composite_key<
vesting_balance_object,
- member_offset,
+ member_offset,
member,
- member_offset
+ member_offset
//member
//member_offset
>,
diff --git a/libraries/net/CMakeLists.txt b/libraries/net/CMakeLists.txt
index 39f9cd05..7aa617d7 100644
--- a/libraries/net/CMakeLists.txt
+++ b/libraries/net/CMakeLists.txt
@@ -13,7 +13,7 @@ target_link_libraries( graphene_net
PUBLIC fc graphene_db )
target_include_directories( graphene_net
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
- PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../chain/include"
+ PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../chain/include" "${CMAKE_CURRENT_BINARY_DIR}/../chain/include"
)
if(MSVC)
diff --git a/libraries/wallet/CMakeLists.txt b/libraries/wallet/CMakeLists.txt
index 74b9f7c5..8c9f8790 100644
--- a/libraries/wallet/CMakeLists.txt
+++ b/libraries/wallet/CMakeLists.txt
@@ -10,7 +10,7 @@ if( PERL_FOUND AND DOXYGEN_FOUND AND NOT "${CMAKE_GENERATOR}" STREQUAL "Ninja" )
COMMAND ${DOXYGEN_EXECUTABLE}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile include/graphene/wallet/wallet.hpp )
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp
- COMMAND PERLLIB=${CMAKE_CURRENT_SOURCE_DIR} ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate_api_documentation.pl ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new
+ COMMAND PERLLIB=${CMAKE_CURRENT_BINARY_DIR} ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate_api_documentation.pl ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new
From d65f20a89fc941d6b06643c8a78846a1aa1d104f Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Thu, 3 Oct 2019 16:38:40 -0300
Subject: [PATCH 011/151] changes to have separate methods and single withdrawl
fee for multiple vest objects
---
.../chain/vesting_balance_evaluator.hpp | 1 +
libraries/chain/vesting_balance_evaluator.cpp | 26 ++++++-
.../wallet/include/graphene/wallet/wallet.hpp | 21 ++++-
libraries/wallet/wallet.cpp | 78 +++++++++++++++----
4 files changed, 107 insertions(+), 19 deletions(-)
diff --git a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp
index fccfbb75..9bb7520e 100644
--- a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp
+++ b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp
@@ -46,6 +46,7 @@ class vesting_balance_withdraw_evaluator : public evaluator();
+
+ const time_point_sec now = d.head_block_time();
+
+ if(now >= (fc::time_point_sec(1570114100)) )
+ {
+ if(oper.fee.amount == 0)
+ {
+ trx_state->skip_fee_schedule_check = true;
+ trx_state->skip_fee = true;
+ }
+ }
+ //check_required_authorities(op);
+ auto result = evaluate( oper );
+
+ if( apply ) result = this->apply( oper );
+ return result;
+} FC_CAPTURE_AND_RETHROW() }
+
void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balance_withdraw_operation& op )
{ try {
const database& d = db();
@@ -125,7 +148,7 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan
FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "", ("now", now)("op", op)("vbo", vbo) );
assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached
- /* const account_object& owner_account = */ op.owner( d );
+ /* const account_object& owner_account = op.owner( d ); */
// TODO: Check asset authorizations and withdrawals
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@@ -133,6 +156,7 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan
void_result vesting_balance_withdraw_evaluator::do_apply( const vesting_balance_withdraw_operation& op )
{ try {
database& d = db();
+
const time_point_sec now = d.head_block_time();
const vesting_balance_object& vbo = op.vesting_balance( d );
diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp
index 2b8012b4..8a15fec0 100644
--- a/libraries/wallet/include/graphene/wallet/wallet.hpp
+++ b/libraries/wallet/include/graphene/wallet/wallet.hpp
@@ -1343,20 +1343,32 @@ class wallet_api
vector< vesting_balance_object_with_info > get_vesting_balances( string account_name );
/**
- * Withdraw a vesting balance.
+ * Withdraw a normal(old) vesting balance.
*
* @param witness_name The account name of the witness, also accepts account ID or vesting balance ID type.
* @param amount The amount to withdraw.
* @param asset_symbol The symbol of the asset to withdraw.
* @param broadcast true if you wish to broadcast the transaction
- * @param vb_type vestig balance type to withdraw 0-OLD, 1-GPOS, 2-SONS(if required)
*/
signed_transaction withdraw_vesting(
string witness_name,
string amount,
string asset_symbol,
- bool broadcast = false,
- uint8_t vb_type = 0);
+ bool broadcast = false);
+
+ /**
+ * Withdraw a GPOS vesting balance.
+ *
+ * @param account_name The account name of the witness/user, also accepts account ID or vesting balance ID type.
+ * @param amount The amount to withdraw.
+ * @param asset_symbol The symbol of the asset to withdraw.
+ * @param broadcast true if you wish to broadcast the transaction
+ */
+ signed_transaction withdraw_GPOS_vesting_balance(
+ string account_name,
+ string amount,
+ string asset_symbol,
+ bool broadcast = false);
/** Vote for a given committee_member.
*
@@ -1966,6 +1978,7 @@ FC_API( graphene::wallet::wallet_api,
(update_worker_votes)
(get_vesting_balances)
(withdraw_vesting)
+ (withdraw_GPOS_vesting_balance)
(vote_for_committee_member)
(vote_for_witness)
(update_witness_votes)
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index 185fc7d1..b6aa2cbf 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -1963,23 +1963,57 @@ public:
string witness_name,
string amount,
string asset_symbol,
- bool broadcast = false,
- uint8_t vb_type = 0 )
+ bool broadcast = false )
{ try {
asset_object asset_obj = get_asset( asset_symbol );
- vector< vesting_balance_object > vbos;
fc::optional vbid = maybe_id(witness_name);
if( !vbid )
+ {
+ witness_object wit = get_witness( witness_name );
+ FC_ASSERT( wit.pay_vb );
+ vbid = wit.pay_vb;
+ }
+
+ vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid );
+
+ if(vbo.balance_type != vesting_balance_type::normal)
+ FC_THROW("Allowed to withdraw only Normal type vest balances with this method");
+
+ vesting_balance_withdraw_operation vesting_balance_withdraw_op;
+
+ vesting_balance_withdraw_op.vesting_balance = *vbid;
+ vesting_balance_withdraw_op.owner = vbo.owner;
+ vesting_balance_withdraw_op.amount = asset_obj.amount_from_string(amount);
+
+ signed_transaction tx;
+ tx.operations.push_back( vesting_balance_withdraw_op );
+ set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees );
+ tx.validate();
+
+ return sign_transaction( tx, broadcast );
+ } FC_CAPTURE_AND_RETHROW( (witness_name)(amount) )
+ }
+
+ signed_transaction withdraw_GPOS_vesting_balance(
+ string account_name,
+ string amount,
+ string asset_symbol,
+ bool broadcast = false)
+ { try {
+ asset_object asset_obj = get_asset( asset_symbol );
+ vector< vesting_balance_object > vbos;
+ fc::optional vbid = maybe_id(account_name);
+ if( !vbid )
{
//Changes done to retrive user account/witness account based on account name
- fc::optional acct_id = maybe_id( witness_name );
+ fc::optional acct_id = maybe_id( account_name );
if( !acct_id )
- acct_id = get_account( witness_name ).id;
+ acct_id = get_account( account_name ).id;
vbos = _remote_db->get_vesting_balances( *acct_id );
if( vbos.size() == 0 )
{
- witness_object wit = get_witness( witness_name );
+ witness_object wit = get_witness( account_name );
FC_ASSERT( wit.pay_vb );
vbid = wit.pay_vb;
}
@@ -1991,14 +2025,22 @@ public:
signed_transaction tx;
asset withdraw_amount = asset_obj.amount_from_string(amount);
-
+ bool onetime_fee_paid = false;
+
for(const vesting_balance_object& vbo: vbos )
{
- if((vb_type == (uint8_t)vbo.balance_type) && vbo.balance.amount > 0)
+ if((vbo.balance_type == vesting_balance_type::gpos) && vbo.balance.amount > 0)
{
fc::optional vest_id = vbo.id;
vesting_balance_withdraw_operation vesting_balance_withdraw_op;
+ // Since there are multiple vesting objects, below logic with vesting_balance_evaluator.cpp changes will
+ // deduct fee from single object and set withdrawl fee to 0 for rest of objects based on requested amount.
+ if(onetime_fee_paid)
+ vesting_balance_withdraw_op.fee = asset( 0, asset_id_type() );
+ else
+ vesting_balance_withdraw_op.fee = _remote_db->get_global_properties().parameters.current_fees->calculate_fee(vesting_balance_withdraw_op);
+
vesting_balance_withdraw_op.vesting_balance = *vest_id;
vesting_balance_withdraw_op.owner = vbo.owner;
if(withdraw_amount.amount >= vbo.balance.amount)
@@ -2015,17 +2057,17 @@ public:
}
tx.operations.push_back( vesting_balance_withdraw_op );
+ onetime_fee_paid = true;
}
}
if( withdraw_amount.amount > 0)
- FC_THROW("Account has insufficient balance to withdraw");
+ FC_THROW("Account has NO or Insufficient balance to withdraw");
- set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees );
tx.validate();
return sign_transaction( tx, broadcast );
- } FC_CAPTURE_AND_RETHROW( (witness_name)(amount) )
+ } FC_CAPTURE_AND_RETHROW( (account_name)(amount) )
}
signed_transaction vote_for_committee_member(string voting_account,
@@ -4083,10 +4125,18 @@ signed_transaction wallet_api::withdraw_vesting(
string witness_name,
string amount,
string asset_symbol,
- bool broadcast,
- uint8_t vb_type)
+ bool broadcast)
{
- return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast, vb_type );
+ return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast );
+}
+
+signed_transaction wallet_api::withdraw_GPOS_vesting_balance(
+ string account_name,
+ string amount,
+ string asset_symbol,
+ bool broadcast)
+{
+ return my->withdraw_GPOS_vesting_balance( account_name, amount, asset_symbol, broadcast );
}
signed_transaction wallet_api::vote_for_committee_member(string voting_account,
From c73d0a338a1ded621a28064e30131ff4de650c68 Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Thu, 3 Oct 2019 22:22:21 -0300
Subject: [PATCH 012/151] 163-fix, Return only non-zero vesting balances
---
libraries/app/database_api.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp
index 3f95a8c1..e3e82790 100644
--- a/libraries/app/database_api.cpp
+++ b/libraries/app/database_api.cpp
@@ -935,7 +935,8 @@ vector database_api_impl::get_vesting_balances( account_
auto vesting_range = _db.get_index_type().indices().get().equal_range(account_id);
std::for_each(vesting_range.first, vesting_range.second,
[&result](const vesting_balance_object& balance) {
- result.emplace_back(balance);
+ if(balance.balance.amount > 0)
+ result.emplace_back(balance);
});
return result;
}
From ec33f0cc07d78825bf0cb0116b93be6522fd9717 Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Thu, 10 Oct 2019 21:24:12 +0530
Subject: [PATCH 013/151] [GRPH-3] Additional cli tests (#155)
* Additional cli tests
* Compatible with latest fc changes
* Fixed Spacing issues
---
.../wallet/include/graphene/wallet/wallet.hpp | 46 +++
libraries/wallet/wallet.cpp | 131 +++++++
tests/cli/main.cpp | 363 +++++++++++++++++-
3 files changed, 529 insertions(+), 11 deletions(-)
diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp
index 3059f179..3890a2b4 100644
--- a/libraries/wallet/include/graphene/wallet/wallet.hpp
+++ b/libraries/wallet/include/graphene/wallet/wallet.hpp
@@ -497,6 +497,11 @@ class wallet_api
* @ingroup Transaction Builder API
*/
signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true);
+ /** Broadcast signed transaction
+ * @param tx signed transaction
+ * @returns the transaction ID along with the signed transaction.
+ */
+ pair broadcast_transaction(signed_transaction tx);
/**
* @ingroup Transaction Builder API
*/
@@ -596,6 +601,12 @@ class wallet_api
*/
bool load_wallet_file(string wallet_filename = "");
+ /** Quitting from Peerplays wallet.
+ *
+ * The current wallet will be closed.
+ */
+ void quit();
+
/** Saves the current wallet to the given filename.
*
* @warning This does not change the wallet filename that will be used for future
@@ -1513,6 +1524,37 @@ class wallet_api
*/
signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false);
+ /** Get transaction signers.
+ *
+ * Returns information about who signed the transaction, specifically,
+ * the corresponding public keys of the private keys used to sign the transaction.
+ * @param tx the signed transaction
+ * @return the set of public_keys
+ */
+ flat_set get_transaction_signers(const signed_transaction &tx) const;
+
+ /** Get key references.
+ *
+ * Returns accounts related to given public keys.
+ * @param keys public keys to search for related accounts
+ * @return the set of related accounts
+ */
+ vector> get_key_references(const vector &keys) const;
+
+ /** Signs a transaction.
+ *
+ * Given a fully-formed transaction with or without signatures, signs
+ * the transaction with the owned keys and optionally broadcasts the
+ * transaction.
+ *
+ * @param tx the unsigned transaction
+ * @param broadcast true if you wish to broadcast the transaction
+ *
+ * @return the signed transaction
+ */
+ signed_transaction add_transaction_signature( signed_transaction tx,
+ bool broadcast = false );
+
/** Returns an uninitialized object representing a given blockchain operation.
*
* This returns a default-initialized object of the given type; it can be used
@@ -1920,6 +1962,7 @@ FC_API( graphene::wallet::wallet_api,
(set_fees_on_builder_transaction)
(preview_builder_transaction)
(sign_builder_transaction)
+ (broadcast_transaction)
(propose_builder_transaction)
(propose_builder_transaction2)
(remove_builder_transaction)
@@ -2005,6 +2048,9 @@ FC_API( graphene::wallet::wallet_api,
(save_wallet_file)
(serialize_transaction)
(sign_transaction)
+ (get_transaction_signers)
+ (get_key_references)
+ (add_transaction_signature)
(get_prototype_operation)
(propose_parameter_change)
(propose_fee_change)
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index 5d534536..47f2460a 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -274,6 +274,7 @@ public:
private:
void claim_registered_account(const account_object& account)
{
+ bool import_keys = false;
auto it = _wallet.pending_account_registrations.find( account.name );
FC_ASSERT( it != _wallet.pending_account_registrations.end() );
for (const std::string& wif_key : it->second)
@@ -289,8 +290,13 @@ private:
// possibility of migrating to a fork where the
// name is available, the user can always
// manually re-register)
+ } else {
+ import_keys = true;
}
_wallet.pending_account_registrations.erase( it );
+
+ if (import_keys)
+ save_wallet_file();
}
// after a witness registration succeeds, this saves the private key in the wallet permanently
@@ -599,6 +605,13 @@ public:
fc::async([this, object]{subscribed_object_changed(object);}, "Object changed");
}
+ void quit()
+ {
+ ilog( "Quitting Cli Wallet ..." );
+
+ throw fc::canceled_exception();
+ }
+
bool copy_wallet_file( string destination_filename )
{
fc::path src_path = get_wallet_filename();
@@ -1147,6 +1160,20 @@ public:
return _builder_transactions[transaction_handle] = sign_transaction(_builder_transactions[transaction_handle], broadcast);
}
+
+ pair broadcast_transaction(signed_transaction tx)
+ {
+ try {
+ _remote_net_broadcast->broadcast_transaction(tx);
+ }
+ catch (const fc::exception& e) {
+ elog("Caught exception while broadcasting tx ${id}: ${e}",
+ ("id", tx.id().str())("e", e.to_detail_string()));
+ throw;
+ }
+ return std::make_pair(tx.id(),tx);
+ }
+
signed_transaction propose_builder_transaction(
transaction_handle_type handle,
time_point_sec expiration = time_point::now() + fc::minutes(1),
@@ -2250,6 +2277,84 @@ public:
return tx;
}
+ flat_set get_transaction_signers(const signed_transaction &tx) const
+ {
+ return tx.get_signature_keys(_chain_id);
+ }
+
+ vector> get_key_references(const vector &keys) const
+ {
+ return _remote_db->get_key_references(keys);
+ }
+
+ /**
+ * Get the required public keys to sign the transaction which had been
+ * owned by us
+ *
+ * NOTE, if `erase_existing_sigs` set to true, the original trasaction's
+ * signatures will be erased
+ *
+ * @param tx The transaction to be signed
+ * @param erase_existing_sigs
+ * The transaction could have been partially signed already,
+ * if set to false, the corresponding public key of existing
+ * signatures won't be returned.
+ * If set to true, the existing signatures will be erased and
+ * all required keys returned.
+ */
+ set get_owned_required_keys( signed_transaction &tx,
+ bool erase_existing_sigs = true)
+ {
+ set pks = _remote_db->get_potential_signatures( tx );
+ flat_set owned_keys;
+ owned_keys.reserve( pks.size() );
+ std::copy_if( pks.begin(), pks.end(),
+ std::inserter( owned_keys, owned_keys.end() ),
+ [this]( const public_key_type &pk ) {
+ return _keys.find( pk ) != _keys.end();
+ } );
+
+ if ( erase_existing_sigs )
+ tx.signatures.clear();
+
+ return _remote_db->get_required_signatures( tx, owned_keys );
+ }
+
+ signed_transaction add_transaction_signature( signed_transaction tx,
+ bool broadcast )
+ {
+ set approving_key_set = get_owned_required_keys(tx, false);
+
+ if ( ( ( tx.ref_block_num == 0 && tx.ref_block_prefix == 0 ) ||
+ tx.expiration == fc::time_point_sec() ) &&
+ tx.signatures.empty() )
+ {
+ auto dyn_props = get_dynamic_global_properties();
+ auto parameters = get_global_properties().parameters;
+ fc::time_point_sec now = dyn_props.time;
+ tx.set_reference_block( dyn_props.head_block_id );
+ tx.set_expiration( now + parameters.maximum_time_until_expiration );
+ }
+ for ( const public_key_type &key : approving_key_set )
+ tx.sign( get_private_key( key ), _chain_id );
+
+ if ( broadcast )
+ {
+ try
+ {
+ _remote_net_broadcast->broadcast_transaction( tx );
+ }
+ catch ( const fc::exception &e )
+ {
+ elog( "Caught exception while broadcasting tx ${id}: ${e}",
+ ( "id", tx.id().str() )( "e", e.to_detail_string() ) );
+ FC_THROW( "Caught exception while broadcasting tx" );
+ }
+ }
+
+ return tx;
+ }
+
signed_transaction sell_asset(string seller_account,
string amount_to_sell,
string symbol_to_sell,
@@ -3645,6 +3750,11 @@ signed_transaction wallet_api::sign_builder_transaction(transaction_handle_type
return my->sign_builder_transaction(transaction_handle, broadcast);
}
+pair wallet_api::broadcast_transaction(signed_transaction tx)
+{
+ return my->broadcast_transaction(tx);
+}
+
signed_transaction wallet_api::propose_builder_transaction(
transaction_handle_type handle,
time_point_sec expiration,
@@ -4117,6 +4227,22 @@ signed_transaction wallet_api::sign_transaction(signed_transaction tx, bool broa
return my->sign_transaction( tx, broadcast);
} FC_CAPTURE_AND_RETHROW( (tx) ) }
+signed_transaction wallet_api::add_transaction_signature( signed_transaction tx,
+ bool broadcast )
+{
+ return my->add_transaction_signature( tx, broadcast );
+}
+
+flat_set wallet_api::get_transaction_signers(const signed_transaction &tx) const
+{ try {
+ return my->get_transaction_signers(tx);
+} FC_CAPTURE_AND_RETHROW( (tx) ) }
+
+vector> wallet_api::get_key_references(const vector &keys) const
+{ try {
+ return my->get_key_references(keys);
+} FC_CAPTURE_AND_RETHROW( (keys) ) }
+
operation wallet_api::get_prototype_operation(string operation_name)
{
return my->get_prototype_operation( operation_name );
@@ -4304,6 +4430,11 @@ bool wallet_api::load_wallet_file( string wallet_filename )
return my->load_wallet_file( wallet_filename );
}
+void wallet_api::quit()
+{
+ my->quit();
+}
+
void wallet_api::save_wallet_file( string wallet_filename )
{
my->save_wallet_file( wallet_filename );
diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp
index ee59f40f..aceae279 100644
--- a/tests/cli/main.cpp
+++ b/tests/cli/main.cpp
@@ -28,6 +28,9 @@
#include
#include
#include
+#include
+#include
+
#include
#include
#include
@@ -81,6 +84,9 @@ int sockQuit(void)
#include "../common/genesis_file_util.hpp"
+using std::exception;
+using std::cerr;
+
#define INVOKE(test) ((struct test*)this)->test_method();
//////
@@ -120,6 +126,8 @@ std::shared_ptr start_application(fc::temp_directory
app1->register_plugin< graphene::bookie::bookie_plugin>();
app1->register_plugin();
+ app1->register_plugin< graphene::market_history::market_history_plugin >();
+ app1->register_plugin< graphene::witness_plugin::witness_plugin >();
app1->startup_plugins();
boost::program_options::variables_map cfg;
#ifdef _WIN32
@@ -329,6 +337,16 @@ BOOST_FIXTURE_TEST_CASE( cli_connect, cli_fixture )
BOOST_TEST_MESSAGE("Testing wallet connection.");
}
+////////////////
+// Start a server and connect using the same calls as the CLI
+// Quit wallet and be sure that file was saved correctly
+////////////////
+BOOST_FIXTURE_TEST_CASE( cli_quit, cli_fixture )
+{
+ BOOST_TEST_MESSAGE("Testing wallet connection and quit command.");
+ BOOST_CHECK_THROW( con.wallet_api_ptr->quit(), fc::canceled_exception );
+}
+
BOOST_FIXTURE_TEST_CASE( upgrade_nathan_account, cli_fixture )
{
try
@@ -380,8 +398,11 @@ BOOST_FIXTURE_TEST_CASE( create_new_account, cli_fixture )
BOOST_CHECK(con.wallet_api_ptr->import_key("jmjatlanta", bki.wif_priv_key));
con.wallet_api_ptr->save_wallet_file(con.wallet_filename);
- // attempt to give jmjatlanta some CORE
- BOOST_TEST_MESSAGE("Transferring CORE from Nathan to jmjatlanta");
+ BOOST_CHECK(generate_block(app1));
+ fc::usleep( fc::seconds(1) );
+
+ // attempt to give jmjatlanta some peerplays
+ BOOST_TEST_MESSAGE("Transferring peerplays from Nathan to jmjatlanta");
signed_transaction transfer_tx = con.wallet_api_ptr->transfer(
"nathan", "jmjatlanta", "10000", "1.3.0", "Here are some CORE token for your new account", true
);
@@ -401,14 +422,14 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )
try
{
BOOST_TEST_MESSAGE("Cli Vote Test for 2 Witnesses");
-
- INVOKE(upgrade_nathan_account); // just to fund nathan
+
+ INVOKE(create_new_account);
// get the details for init1
witness_object init1_obj = con.wallet_api_ptr->get_witness("init1");
int init1_start_votes = init1_obj.total_votes;
// Vote for a witness
- signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init1", true, true);
+ signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init1", true, true);
// generate a block to get things started
BOOST_CHECK(generate_block(app1));
@@ -423,7 +444,7 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )
// Vote for a 2nd witness
int init2_start_votes = init2_obj.total_votes;
- signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init2", true, true);
+ signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init2", true, true);
// send another block to trigger maintenance interval
BOOST_CHECK(generate_maintenance_block(app1));
@@ -442,6 +463,42 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )
}
}
+BOOST_FIXTURE_TEST_CASE( cli_get_signed_transaction_signers, cli_fixture )
+{
+ try
+ {
+ INVOKE(upgrade_nathan_account);
+
+ // register account and transfer funds
+ const auto test_bki = con.wallet_api_ptr->suggest_brain_key();
+ con.wallet_api_ptr->register_account(
+ "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true
+ );
+ con.wallet_api_ptr->transfer("nathan", "test", "1000", "1.3.0", "", true);
+
+ // import key and save wallet
+ BOOST_CHECK(con.wallet_api_ptr->import_key("test", test_bki.wif_priv_key));
+ con.wallet_api_ptr->save_wallet_file(con.wallet_filename);
+
+ // create transaction and check expected result
+ auto signed_trx = con.wallet_api_ptr->transfer("test", "nathan", "10", "1.3.0", "", true);
+
+ const auto &test_acc = con.wallet_api_ptr->get_account("test");
+ flat_set expected_signers = {test_bki.pub_key};
+ vector > expected_key_refs{{test_acc.id, test_acc.id}};
+
+ auto signers = con.wallet_api_ptr->get_transaction_signers(signed_trx);
+ BOOST_CHECK(signers == expected_signers);
+
+ auto key_refs = con.wallet_api_ptr->get_key_references({test_bki.pub_key});
+ BOOST_CHECK(key_refs == expected_key_refs);
+
+ } catch( fc::exception& e ) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
///////////////////////
// Check account history pagination
///////////////////////
@@ -451,7 +508,7 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture )
{
INVOKE(create_new_account);
- // attempt to give jmjatlanta some peerplay
+ // attempt to give jmjatlanta some peerplay
BOOST_TEST_MESSAGE("Transferring peerplay from Nathan to jmjatlanta");
for(int i = 1; i <= 199; i++)
{
@@ -461,13 +518,13 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture )
BOOST_CHECK(generate_block(app1));
- // now get account history and make sure everything is there (and no duplicates)
+ // now get account history and make sure everything is there (and no duplicates)
std::vector history = con.wallet_api_ptr->get_account_history("jmjatlanta", 300);
BOOST_CHECK_EQUAL(201u, history.size() );
- std::set operation_ids;
+ std::set operation_ids;
- for(auto& op : history)
+ for(auto& op : history)
{
if( operation_ids.find(op.op.id) != operation_ids.end() )
{
@@ -479,4 +536,288 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture )
edump((e.to_detail_string()));
throw;
}
-}
\ No newline at end of file
+}
+
+BOOST_FIXTURE_TEST_CASE( cli_get_available_transaction_signers, cli_fixture )
+{
+ try
+ {
+ INVOKE(upgrade_nathan_account);
+
+ // register account
+ const auto test_bki = con.wallet_api_ptr->suggest_brain_key();
+ con.wallet_api_ptr->register_account(
+ "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true
+ );
+ const auto &test_acc = con.wallet_api_ptr->get_account("test");
+
+ // create and sign transaction
+ signed_transaction trx;
+ trx.operations = {transfer_operation()};
+
+ // sign with test key
+ const auto test_privkey = wif_to_key( test_bki.wif_priv_key );
+ BOOST_REQUIRE( test_privkey );
+ trx.sign( *test_privkey, con.wallet_data.chain_id );
+
+ // sign with other keys
+ const auto privkey_1 = fc::ecc::private_key::generate();
+ trx.sign( privkey_1, con.wallet_data.chain_id );
+
+ const auto privkey_2 = fc::ecc::private_key::generate();
+ trx.sign( privkey_2, con.wallet_data.chain_id );
+
+ // verify expected result
+ flat_set expected_signers = {test_bki.pub_key,
+ privkey_1.get_public_key(),
+ privkey_2.get_public_key()};
+
+ auto signers = con.wallet_api_ptr->get_transaction_signers(trx);
+ BOOST_CHECK(signers == expected_signers);
+
+ // blockchain has no references to unknown accounts (privkey_1, privkey_2)
+ // only test account available
+ vector > expected_key_refs;
+ expected_key_refs.push_back(vector());
+ expected_key_refs.push_back(vector());
+ expected_key_refs.push_back({test_acc.id, test_acc.id});
+
+ auto key_refs = con.wallet_api_ptr->get_key_references({expected_signers.begin(), expected_signers.end()});
+ std::sort(key_refs.begin(), key_refs.end());
+
+ BOOST_CHECK(key_refs == expected_key_refs);
+
+ } catch( fc::exception& e ) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE( cli_cant_get_signers_from_modified_transaction, cli_fixture )
+{
+ try
+ {
+ INVOKE(upgrade_nathan_account);
+
+ // register account
+ const auto test_bki = con.wallet_api_ptr->suggest_brain_key();
+ con.wallet_api_ptr->register_account(
+ "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true
+ );
+
+ // create and sign transaction
+ signed_transaction trx;
+ trx.operations = {transfer_operation()};
+
+ // sign with test key
+ const auto test_privkey = wif_to_key( test_bki.wif_priv_key );
+ BOOST_REQUIRE( test_privkey );
+ trx.sign( *test_privkey, con.wallet_data.chain_id );
+
+ // modify transaction (MITM-attack)
+ trx.operations.clear();
+
+ // verify if transaction has no valid signature of test account
+ flat_set expected_signers_of_valid_transaction = {test_bki.pub_key};
+ auto signers = con.wallet_api_ptr->get_transaction_signers(trx);
+ BOOST_CHECK(signers != expected_signers_of_valid_transaction);
+
+ } catch( fc::exception& e ) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+///////////////////
+// Start a server and connect using the same calls as the CLI
+// Set a voting proxy and be assured that it sticks
+///////////////////
+BOOST_FIXTURE_TEST_CASE( cli_set_voting_proxy, cli_fixture )
+{
+ try {
+ INVOKE(create_new_account);
+
+ // grab account for comparison
+ account_object prior_voting_account = con.wallet_api_ptr->get_account("jmjatlanta");
+ // set the voting proxy to nathan
+ BOOST_TEST_MESSAGE("About to set voting proxy.");
+ signed_transaction voting_tx = con.wallet_api_ptr->set_voting_proxy("jmjatlanta", "nathan", true);
+ account_object after_voting_account = con.wallet_api_ptr->get_account("jmjatlanta");
+ // see if it changed
+ BOOST_CHECK(prior_voting_account.options.voting_account != after_voting_account.options.voting_account);
+ } catch( fc::exception& e ) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+
+///////////////////////
+// Create a multi-sig account and verify that only when all signatures are
+// signed, the transaction could be broadcast
+///////////////////////
+BOOST_AUTO_TEST_CASE( cli_multisig_transaction )
+{
+ using namespace graphene::chain;
+ using namespace graphene::app;
+ std::shared_ptr app1;
+ try {
+ fc::temp_directory app_dir( graphene::utilities::temp_directory_path() );
+
+ int server_port_number = 0;
+ app1 = start_application(app_dir, server_port_number);
+
+ // connect to the server
+ client_connection con(app1, app_dir, server_port_number);
+
+ BOOST_TEST_MESSAGE("Setting wallet password");
+ con.wallet_api_ptr->set_password("supersecret");
+ con.wallet_api_ptr->unlock("supersecret");
+
+ // import Nathan account
+ BOOST_TEST_MESSAGE("Importing nathan key");
+ std::vector nathan_keys{"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"};
+ BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3");
+ BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0]));
+
+ BOOST_TEST_MESSAGE("Importing nathan's balance");
+ std::vector import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true);
+ account_object nathan_acct_before_upgrade = con.wallet_api_ptr->get_account("nathan");
+
+ // upgrade nathan
+ BOOST_TEST_MESSAGE("Upgrading Nathan to LTM");
+ signed_transaction upgrade_tx = con.wallet_api_ptr->upgrade_account("nathan", true);
+ account_object nathan_acct_after_upgrade = con.wallet_api_ptr->get_account("nathan");
+
+ // verify that the upgrade was successful
+ BOOST_CHECK_PREDICATE( std::not_equal_to(), (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch())(nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) );
+ BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member());
+
+ // create a new multisig account
+ graphene::wallet::brain_key_info bki1 = con.wallet_api_ptr->suggest_brain_key();
+ graphene::wallet::brain_key_info bki2 = con.wallet_api_ptr->suggest_brain_key();
+ graphene::wallet::brain_key_info bki3 = con.wallet_api_ptr->suggest_brain_key();
+ graphene::wallet::brain_key_info bki4 = con.wallet_api_ptr->suggest_brain_key();
+ BOOST_CHECK(!bki1.brain_priv_key.empty());
+ BOOST_CHECK(!bki2.brain_priv_key.empty());
+ BOOST_CHECK(!bki3.brain_priv_key.empty());
+ BOOST_CHECK(!bki4.brain_priv_key.empty());
+
+ signed_transaction create_multisig_acct_tx;
+ account_create_operation account_create_op;
+
+ account_create_op.referrer = nathan_acct_after_upgrade.id;
+ account_create_op.referrer_percent = nathan_acct_after_upgrade.referrer_rewards_percentage;
+ account_create_op.registrar = nathan_acct_after_upgrade.id;
+ account_create_op.name = "cifer.test";
+ account_create_op.owner = authority(1, bki1.pub_key, 1);
+ account_create_op.active = authority(2, bki2.pub_key, 1, bki3.pub_key, 1);
+ account_create_op.options.memo_key = bki4.pub_key;
+ account_create_op.fee = asset(1000000); // should be enough for creating account
+
+ create_multisig_acct_tx.operations.push_back(account_create_op);
+ con.wallet_api_ptr->sign_transaction(create_multisig_acct_tx, true);
+
+ // attempt to give cifer.test some peerplays
+ BOOST_TEST_MESSAGE("Transferring peerplays from Nathan to cifer.test");
+ signed_transaction transfer_tx1 = con.wallet_api_ptr->transfer("nathan", "cifer.test", "10000", "1.3.0", "Here are some BTS for your new account", true);
+
+ // transfer bts from cifer.test to nathan
+ BOOST_TEST_MESSAGE("Transferring peerplays from cifer.test to nathan");
+ auto dyn_props = app1->chain_database()->get_dynamic_global_properties();
+ account_object cifer_test = con.wallet_api_ptr->get_account("cifer.test");
+
+ // construct a transfer transaction
+ signed_transaction transfer_tx2;
+ transfer_operation xfer_op;
+ xfer_op.from = cifer_test.id;
+ xfer_op.to = nathan_acct_after_upgrade.id;
+ xfer_op.amount = asset(100000000);
+ xfer_op.fee = asset(3000000); // should be enough for transfer
+ transfer_tx2.operations.push_back(xfer_op);
+
+ // case1: sign a transaction without TaPoS and expiration fields
+ // expect: return a transaction with TaPoS and expiration filled
+ transfer_tx2 =
+ con.wallet_api_ptr->add_transaction_signature( transfer_tx2, false );
+ BOOST_CHECK( ( transfer_tx2.ref_block_num != 0 &&
+ transfer_tx2.ref_block_prefix != 0 ) ||
+ ( transfer_tx2.expiration != fc::time_point_sec() ) );
+
+ // case2: broadcast without signature
+ // expect: exception with missing active authority
+ BOOST_CHECK_THROW(con.wallet_api_ptr->broadcast_transaction(transfer_tx2), fc::exception);
+
+ // case3:
+ // import one of the private keys for this new account in the wallet file,
+ // sign and broadcast with partial signatures
+ //
+ // expect: exception with missing active authority
+ BOOST_CHECK(con.wallet_api_ptr->import_key("cifer.test", bki2.wif_priv_key));
+ BOOST_CHECK_THROW(con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true), fc::exception);
+
+ // case4: sign again as signature exists
+ // expect: num of signatures not increase
+ // transfer_tx2 = con.wallet_api_ptr->add_transaction_signature(transfer_tx2, false);
+ // BOOST_CHECK_EQUAL(transfer_tx2.signatures.size(), 1);
+
+ // case5:
+ // import another private key, sign and broadcast without full signatures
+ //
+ // expect: transaction broadcast successfully
+ BOOST_CHECK(con.wallet_api_ptr->import_key("cifer.test", bki3.wif_priv_key));
+ con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true);
+ auto balances = con.wallet_api_ptr->list_account_balances( "cifer.test" );
+ for (auto b : balances) {
+ if (b.asset_id == asset_id_type()) {
+ BOOST_ASSERT(b == asset(900000000 - 3000000));
+ }
+ }
+
+ // wait for everything to finish up
+ fc::usleep(fc::seconds(1));
+ } catch( fc::exception& e ) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+ app1->shutdown();
+}
+
+graphene::wallet::plain_keys decrypt_keys( const std::string& password, const vector& cipher_keys )
+{
+ auto pw = fc::sha512::hash( password.c_str(), password.size() );
+ vector decrypted = fc::aes_decrypt( pw, cipher_keys );
+ return fc::raw::unpack( decrypted );
+}
+
+BOOST_AUTO_TEST_CASE( saving_keys_wallet_test )
+{
+ cli_fixture cli;
+
+ cli.con.wallet_api_ptr->import_balance( "nathan", cli.nathan_keys, true );
+ cli.con.wallet_api_ptr->upgrade_account( "nathan", true );
+ std::string brain_key( "FICTIVE WEARY MINIBUS LENS HAWKIE MAIDISH MINTY GLYPH GYTE KNOT COCKSHY LENTIGO PROPS BIFORM KHUTBAH BRAZIL" );
+ cli.con.wallet_api_ptr->create_account_with_brain_key( brain_key, "account1", "nathan", "nathan", true );
+
+ BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "nathan", "account1", "9000", "1.3.0", "", true ) );
+
+ std::string path( cli.app_dir.path().generic_string() + "/wallet.json" );
+ graphene::wallet::wallet_data wallet = fc::json::from_file( path ).as( 2 * GRAPHENE_MAX_NESTED_OBJECTS );
+ BOOST_CHECK( wallet.extra_keys.size() == 1 ); // nathan
+ BOOST_CHECK( wallet.pending_account_registrations.size() == 1 ); // account1
+ BOOST_CHECK( wallet.pending_account_registrations["account1"].size() == 2 ); // account1 active key + account1 memo key
+
+ graphene::wallet::plain_keys pk = decrypt_keys( "supersecret", wallet.cipher_keys );
+ BOOST_CHECK( pk.keys.size() == 1 ); // nathan key
+
+ BOOST_CHECK( generate_block( cli.app1 ) );
+ fc::usleep( fc::seconds(1) );
+
+ wallet = fc::json::from_file( path ).as( 2 * GRAPHENE_MAX_NESTED_OBJECTS );
+ BOOST_CHECK( wallet.extra_keys.size() == 2 ); // nathan + account1
+ BOOST_CHECK( wallet.pending_account_registrations.empty() );
+ BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "account1", "nathan", "1000", "1.3.0", "", true ) );
+
+ pk = decrypt_keys( "supersecret", wallet.cipher_keys );
+ BOOST_CHECK( pk.keys.size() == 3 ); // nathan key + account1 active key + account1 memo key
+}
From 40534446dae8dba5b682b6b8a72fc7319e4347d8 Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Thu, 10 Oct 2019 21:29:01 +0530
Subject: [PATCH 014/151] [GRPH-106] Added voting tests (#136)
* Added more voting tests
* Added additional option
---
libraries/app/application.cpp | 8 +
libraries/chain/db_maint.cpp | 52 ++-
.../chain/include/graphene/chain/database.hpp | 7 +
tests/common/database_fixture.cpp | 6 +
tests/tests/voting_tests.cpp | 358 +++++++++++++++++-
5 files changed, 419 insertions(+), 12 deletions(-)
diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp
index 29cefcfc..c652a798 100644
--- a/libraries/app/application.cpp
+++ b/libraries/app/application.cpp
@@ -375,6 +375,11 @@ namespace detail {
}
_chain_db->add_checkpoints( loaded_checkpoints );
+ if( _options->count("enable-standby-votes-tracking") )
+ {
+ _chain_db->enable_standby_votes_tracking( _options->at("enable-standby-votes-tracking").as() );
+ }
+
bool replay = false;
std::string replay_reason = "reason not provided";
@@ -925,6 +930,9 @@ void application::set_program_options(boost::program_options::options_descriptio
("genesis-json", bpo::value(), "File to read Genesis State from")
("dbg-init-key", bpo::value(), "Block signing key to use for init witnesses, overrides genesis file")
("api-access", bpo::value(), "JSON file specifying API permissions")
+ ("enable-standby-votes-tracking", bpo::value()->implicit_value(true),
+ "Whether to enable tracking of votes of standby witnesses and committee members. "
+ "Set it to true to provide accurate data to API clients, set to false for slightly better performance.")
;
command_line_options.add(configuration_file_options);
command_line_options.add_options()
diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp
index 3ec84d14..58288945 100644
--- a/libraries/chain/db_maint.cpp
+++ b/libraries/chain/db_maint.cpp
@@ -185,13 +185,27 @@ void database::update_active_witnesses()
const global_property_object& gpo = get_global_properties();
- const auto& all_witnesses = get_index_type().indices();
+ auto update_witness_total_votes = [this]( const witness_object& wit ) {
+ modify( wit, [this]( witness_object& obj )
+ {
+ obj.total_votes = _vote_tally_buffer[obj.vote_id];
+ });
+ };
- for( const witness_object& wit : all_witnesses )
+ if( _track_standby_votes )
{
- modify( wit, [&]( witness_object& obj ){
- obj.total_votes = _vote_tally_buffer[wit.vote_id];
- });
+ const auto& all_witnesses = get_index_type().indices();
+ for( const witness_object& wit : all_witnesses )
+ {
+ update_witness_total_votes( wit );
+ }
+ }
+ else
+ {
+ for( const witness_object& wit : wits )
+ {
+ update_witness_total_votes( wit );
+ }
}
// Update witness authority
@@ -267,13 +281,29 @@ void database::update_active_committee_members()
const chain_property_object& cpo = get_chain_properties();
auto committee_members = sort_votable_objects(std::max(committee_member_count*2+1, (size_t)cpo.immutable_parameters.min_committee_member_count));
- for( const committee_member_object& del : committee_members )
- {
- modify( del, [&]( committee_member_object& obj ){
- obj.total_votes = _vote_tally_buffer[del.vote_id];
- });
- }
+ auto update_committee_member_total_votes = [this]( const committee_member_object& cm ) {
+ modify( cm, [this]( committee_member_object& obj )
+ {
+ obj.total_votes = _vote_tally_buffer[obj.vote_id];
+ });
+ };
+ if( _track_standby_votes )
+ {
+ const auto& all_committee_members = get_index_type().indices();
+ for( const committee_member_object& cm : all_committee_members )
+ {
+ update_committee_member_total_votes( cm );
+ }
+ }
+ else
+ {
+ for( const committee_member_object& cm : committee_members )
+ {
+ update_committee_member_total_votes( cm );
+ }
+ }
+
// Update committee authorities
if( !committee_members.empty() )
{
diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp
index 483bfc8a..cd0a9766 100644
--- a/libraries/chain/include/graphene/chain/database.hpp
+++ b/libraries/chain/include/graphene/chain/database.hpp
@@ -460,6 +460,8 @@ namespace graphene { namespace chain {
/**
* @}
*/
+ /// Enable or disable tracking of votes of standby witnesses and committee members
+ inline void enable_standby_votes_tracking(bool enable) { _track_standby_votes = enable; }
protected:
//Mark pop_undo() as protected -- we do not want outside calling pop_undo(); it should call pop_block() instead
void pop_undo() { object_database::pop_undo(); }
@@ -561,6 +563,11 @@ namespace graphene { namespace chain {
flat_map _checkpoints;
node_property_object _node_property_object;
+
+ /// Whether to update votes of standby witnesses and committee members when performing chain maintenance.
+ /// Set it to true to provide accurate data to API clients, set to false to have better performance.
+ bool _track_standby_votes = true;
+
fc::hash_ctr_rng _random_number_generator;
bool _slow_replays = false;
diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp
index f31ec00a..d613bfba 100644
--- a/tests/common/database_fixture.cpp
+++ b/tests/common/database_fixture.cpp
@@ -127,6 +127,12 @@ database_fixture::database_fixture()
options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false)));
}
+ // standby votes tracking
+ if( boost::unit_test::framework::current_test_case().p_name.value == "track_votes_witnesses_disabled" ||
+ boost::unit_test::framework::current_test_case().p_name.value == "track_votes_committee_disabled") {
+ app.chain_database()->enable_standby_votes_tracking( false );
+ }
+
// app.initialize();
ahplugin->plugin_set_app(&app);
ahplugin->plugin_initialize(options);
diff --git a/tests/tests/voting_tests.cpp b/tests/tests/voting_tests.cpp
index b88f485a..870fd359 100644
--- a/tests/tests/voting_tests.cpp
+++ b/tests/tests/voting_tests.cpp
@@ -163,4 +163,360 @@ BOOST_AUTO_TEST_CASE(last_voting_date_proxy)
} FC_LOG_AND_RETHROW()
}
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_CASE(put_my_witnesses)
+{
+ try
+ {
+ graphene::app::database_api db_api1(db);
+
+ ACTORS( (witness0)
+ (witness1)
+ (witness2)
+ (witness3)
+ (witness4)
+ (witness5)
+ (witness6)
+ (witness7)
+ (witness8)
+ (witness9)
+ (witness10)
+ (witness11)
+ (witness12)
+ (witness13) );
+
+ // Upgrade all accounts to LTM
+ upgrade_to_lifetime_member(witness0_id);
+ upgrade_to_lifetime_member(witness1_id);
+ upgrade_to_lifetime_member(witness2_id);
+ upgrade_to_lifetime_member(witness3_id);
+ upgrade_to_lifetime_member(witness4_id);
+ upgrade_to_lifetime_member(witness5_id);
+ upgrade_to_lifetime_member(witness6_id);
+ upgrade_to_lifetime_member(witness7_id);
+ upgrade_to_lifetime_member(witness8_id);
+ upgrade_to_lifetime_member(witness9_id);
+ upgrade_to_lifetime_member(witness10_id);
+ upgrade_to_lifetime_member(witness11_id);
+ upgrade_to_lifetime_member(witness12_id);
+ upgrade_to_lifetime_member(witness13_id);
+
+ // Create all the witnesses
+ const witness_id_type witness0_witness_id = create_witness(witness0_id, witness0_private_key).id;
+ const witness_id_type witness1_witness_id = create_witness(witness1_id, witness1_private_key).id;
+ const witness_id_type witness2_witness_id = create_witness(witness2_id, witness2_private_key).id;
+ const witness_id_type witness3_witness_id = create_witness(witness3_id, witness3_private_key).id;
+ const witness_id_type witness4_witness_id = create_witness(witness4_id, witness4_private_key).id;
+ const witness_id_type witness5_witness_id = create_witness(witness5_id, witness5_private_key).id;
+ const witness_id_type witness6_witness_id = create_witness(witness6_id, witness6_private_key).id;
+ const witness_id_type witness7_witness_id = create_witness(witness7_id, witness7_private_key).id;
+ const witness_id_type witness8_witness_id = create_witness(witness8_id, witness8_private_key).id;
+ const witness_id_type witness9_witness_id = create_witness(witness9_id, witness9_private_key).id;
+ const witness_id_type witness10_witness_id = create_witness(witness10_id, witness10_private_key).id;
+ const witness_id_type witness11_witness_id = create_witness(witness11_id, witness11_private_key).id;
+ const witness_id_type witness12_witness_id = create_witness(witness12_id, witness12_private_key).id;
+ const witness_id_type witness13_witness_id = create_witness(witness13_id, witness13_private_key).id;
+
+ // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time
+ const vector private_keys = {
+ witness0_private_key,
+ witness1_private_key,
+ witness2_private_key,
+ witness3_private_key,
+ witness4_private_key,
+ witness5_private_key,
+ witness6_private_key,
+ witness7_private_key,
+ witness8_private_key,
+ witness9_private_key,
+ witness10_private_key,
+ witness11_private_key,
+ witness12_private_key,
+ witness13_private_key
+
+ };
+
+ // create a map with account id and witness id of the first 11 witnesses
+ const flat_map witness_map = {
+ {witness0_id, witness0_witness_id},
+ {witness1_id, witness1_witness_id},
+ {witness2_id, witness2_witness_id},
+ {witness3_id, witness3_witness_id},
+ {witness4_id, witness4_witness_id},
+ {witness5_id, witness5_witness_id},
+ {witness6_id, witness6_witness_id},
+ {witness7_id, witness7_witness_id},
+ {witness8_id, witness8_witness_id},
+ {witness9_id, witness9_witness_id},
+ {witness10_id, witness10_witness_id},
+ {witness11_id, witness11_witness_id},
+ {witness12_id, witness12_witness_id},
+ {witness13_id, witness13_witness_id}
+ };
+
+ // Check current default witnesses, default chain is configured with 10 witnesses
+ auto witnesses = db.get_global_properties().active_witnesses;
+ BOOST_CHECK_EQUAL(witnesses.size(), 10);
+ BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 1);
+ BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 2);
+ BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 3);
+ BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 4);
+ BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 5);
+ BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 6);
+ BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 7);
+ BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 8);
+ BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 9);
+ BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 10);
+
+ // Activate all witnesses
+ // Each witness is voted with incremental stake so last witness created will be the ones with more votes
+ int c = 0;
+ for (auto l : witness_map) {
+ int stake = 100 + c + 10;
+ transfer(committee_account, l.first, asset(stake));
+ {
+ set_expiration(db, trx);
+ account_update_operation op;
+ op.account = l.first;
+ op.new_options = l.first(db).options;
+ op.new_options->votes.insert(l.second(db).vote_id);
+
+ trx.operations.push_back(op);
+ sign(trx, private_keys.at(c));
+ PUSH_TX(db, trx);
+ trx.clear();
+ }
+ ++c;
+ }
+
+ // Trigger the new witnesses
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ // Check my witnesses are now in control of the system
+ witnesses = db.get_global_properties().active_witnesses;
+ BOOST_CHECK_EQUAL(witnesses.size(), 11);
+ BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 14);
+ BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 15);
+ BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 16);
+ BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 17);
+ BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 18);
+ BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 19);
+ BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 20);
+ BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 21);
+ BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 22);
+ BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 23);
+ BOOST_CHECK_EQUAL(witnesses.begin()[10].instance.value, 24);
+
+ } FC_LOG_AND_RETHROW()
+}
+
+BOOST_AUTO_TEST_CASE(track_votes_witnesses_enabled)
+{
+ try
+ {
+ graphene::app::database_api db_api1(db);
+
+ INVOKE(put_my_witnesses);
+
+ const account_id_type witness1_id= get_account("witness1").id;
+ auto witness1_object = db_api1.get_witness_by_account(witness1_id);
+ BOOST_CHECK_EQUAL(witness1_object->total_votes, 111);
+
+ } FC_LOG_AND_RETHROW()
+}
+
+BOOST_AUTO_TEST_CASE(track_votes_witnesses_disabled)
+{
+ try
+ {
+ graphene::app::database_api db_api1(db);
+
+ INVOKE(put_my_witnesses);
+
+ const account_id_type witness1_id= get_account("witness1").id;
+ auto witness1_object = db_api1.get_witness_by_account(witness1_id);
+ BOOST_CHECK_EQUAL(witness1_object->total_votes, 0);
+
+ } FC_LOG_AND_RETHROW()
+}
+
+BOOST_AUTO_TEST_CASE(put_my_committee_members)
+{
+ try
+ {
+ graphene::app::database_api db_api1(db);
+
+ ACTORS( (committee0)
+ (committee1)
+ (committee2)
+ (committee3)
+ (committee4)
+ (committee5)
+ (committee6)
+ (committee7)
+ (committee8)
+ (committee9)
+ (committee10)
+ (committee11)
+ (committee12)
+ (committee13) );
+
+ // Upgrade all accounts to LTM
+ upgrade_to_lifetime_member(committee0_id);
+ upgrade_to_lifetime_member(committee1_id);
+ upgrade_to_lifetime_member(committee2_id);
+ upgrade_to_lifetime_member(committee3_id);
+ upgrade_to_lifetime_member(committee4_id);
+ upgrade_to_lifetime_member(committee5_id);
+ upgrade_to_lifetime_member(committee6_id);
+ upgrade_to_lifetime_member(committee7_id);
+ upgrade_to_lifetime_member(committee8_id);
+ upgrade_to_lifetime_member(committee9_id);
+ upgrade_to_lifetime_member(committee10_id);
+ upgrade_to_lifetime_member(committee11_id);
+ upgrade_to_lifetime_member(committee12_id);
+ upgrade_to_lifetime_member(committee13_id);
+
+ // Create all the committee
+ const committee_member_id_type committee0_committee_id = create_committee_member(committee0_id(db)).id;
+ const committee_member_id_type committee1_committee_id = create_committee_member(committee1_id(db)).id;
+ const committee_member_id_type committee2_committee_id = create_committee_member(committee2_id(db)).id;
+ const committee_member_id_type committee3_committee_id = create_committee_member(committee3_id(db)).id;
+ const committee_member_id_type committee4_committee_id = create_committee_member(committee4_id(db)).id;
+ const committee_member_id_type committee5_committee_id = create_committee_member(committee5_id(db)).id;
+ const committee_member_id_type committee6_committee_id = create_committee_member(committee6_id(db)).id;
+ const committee_member_id_type committee7_committee_id = create_committee_member(committee7_id(db)).id;
+ const committee_member_id_type committee8_committee_id = create_committee_member(committee8_id(db)).id;
+ const committee_member_id_type committee9_committee_id = create_committee_member(committee9_id(db)).id;
+ const committee_member_id_type committee10_committee_id = create_committee_member(committee10_id(db)).id;
+ const committee_member_id_type committee11_committee_id = create_committee_member(committee11_id(db)).id;
+ const committee_member_id_type committee12_committee_id = create_committee_member(committee12_id(db)).id;
+ const committee_member_id_type committee13_committee_id = create_committee_member(committee13_id(db)).id;
+
+ // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time
+ const vector private_keys = {
+ committee0_private_key,
+ committee1_private_key,
+ committee2_private_key,
+ committee3_private_key,
+ committee4_private_key,
+ committee5_private_key,
+ committee6_private_key,
+ committee7_private_key,
+ committee8_private_key,
+ committee9_private_key,
+ committee10_private_key,
+ committee11_private_key,
+ committee12_private_key,
+ committee13_private_key
+ };
+
+ // create a map with account id and committee id of the first 11 witnesses
+ const flat_map committee_map = {
+ {committee0_id, committee0_committee_id},
+ {committee1_id, committee1_committee_id},
+ {committee2_id, committee2_committee_id},
+ {committee3_id, committee3_committee_id},
+ {committee4_id, committee4_committee_id},
+ {committee5_id, committee5_committee_id},
+ {committee6_id, committee6_committee_id},
+ {committee7_id, committee7_committee_id},
+ {committee8_id, committee8_committee_id},
+ {committee9_id, committee9_committee_id},
+ {committee10_id, committee10_committee_id},
+ {committee11_id, committee11_committee_id},
+ {committee12_id, committee12_committee_id},
+ {committee13_id, committee13_committee_id}
+ };
+
+ // Check current default witnesses, default chain is configured with 10 witnesses
+ auto committee_members = db.get_global_properties().active_committee_members;
+
+ BOOST_CHECK_EQUAL(committee_members.size(), 10);
+ BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 0);
+ BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 1);
+ BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 2);
+ BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 3);
+ BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 4);
+ BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 5);
+ BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 6);
+ BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 7);
+ BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 8);
+ BOOST_CHECK_EQUAL(committee_members.begin()[9].instance.value, 9);
+
+ // Activate all committee
+ // Each witness is voted with incremental stake so last witness created will be the ones with more votes
+ int c = 0;
+ for (auto committee : committee_map) {
+ int stake = 100 + c + 10;
+ transfer(committee_account, committee.first, asset(stake));
+ {
+ set_expiration(db, trx);
+ account_update_operation op;
+ op.account = committee.first;
+ op.new_options = committee.first(db).options;
+ op.new_options->votes.insert(committee.second(db).vote_id);
+
+ trx.operations.push_back(op);
+ sign(trx, private_keys.at(c));
+ PUSH_TX(db, trx);
+ trx.clear();
+ }
+ ++c;
+ }
+
+ // Trigger the new committee
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ // Check my witnesses are now in control of the system
+ committee_members = db.get_global_properties().active_committee_members;
+ BOOST_CHECK_EQUAL(committee_members.size(), 11);
+
+ /* TODO we are not in full control, seems to committee members have votes by default
+ BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 14);
+ BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 15);
+ BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 16);
+ BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 17);
+ BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 18);
+ BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 19);
+ BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 20);
+ BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 21);
+ BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 22);
+ BOOST_CHECK_EQUAL(committee_members.begin()[9].instance.value, 23);
+ BOOST_CHECK_EQUAL(committee_members.begin()[10].instance.value, 24);
+ */
+ } FC_LOG_AND_RETHROW()
+}
+
+BOOST_AUTO_TEST_CASE(track_votes_committee_enabled)
+{
+ try
+ {
+ graphene::app::database_api db_api1(db);
+
+ INVOKE(put_my_committee_members);
+
+ const account_id_type committee1_id= get_account("committee1").id;
+ auto committee1_object = db_api1.get_committee_member_by_account(committee1_id);
+ BOOST_CHECK_EQUAL(committee1_object->total_votes, 111);
+
+ } FC_LOG_AND_RETHROW()
+}
+
+BOOST_AUTO_TEST_CASE(track_votes_committee_disabled)
+{
+ try
+ {
+ graphene::app::database_api db_api1(db);
+
+ INVOKE(put_my_committee_members);
+
+ const account_id_type committee1_id= get_account("committee1").id;
+ auto committee1_object = db_api1.get_committee_member_by_account(committee1_id);
+ BOOST_CHECK_EQUAL(committee1_object->total_votes, 0);
+
+ } FC_LOG_AND_RETHROW()
+}
+
+BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file
From e3b2459de4d0a98b1818df4039eb22031b1e6685 Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Wed, 16 Oct 2019 20:57:39 +0530
Subject: [PATCH 015/151] Adjust p2p log level (#180)
---
libraries/net/node.cpp | 12 ++++++------
libraries/net/peer_connection.cpp | 12 ++++++------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp
index a38199fd..7df98294 100644
--- a/libraries/net/node.cpp
+++ b/libraries/net/node.cpp
@@ -1249,7 +1249,7 @@ namespace graphene { namespace net { namespace detail {
for (const peer_connection_ptr& peer : _active_connections)
{
// only advertise to peers who are in sync with us
- wdump((peer->peer_needs_sync_items_from_us));
+ idump((peer->peer_needs_sync_items_from_us));
if( !peer->peer_needs_sync_items_from_us )
{
std::map > items_to_advertise_by_type;
@@ -1257,7 +1257,7 @@ namespace graphene { namespace net { namespace detail {
// or anything it has advertised to us
// group the items we need to send by type, because we'll need to send one inventory message per type
unsigned total_items_to_send_to_this_peer = 0;
- wdump((inventory_to_advertise));
+ idump((inventory_to_advertise));
for (const item_id& item_to_advertise : inventory_to_advertise)
{
auto adv_to_peer = peer->inventory_advertised_to_peer.find(item_to_advertise);
@@ -1276,9 +1276,9 @@ namespace graphene { namespace net { namespace detail {
else
{
if (adv_to_peer != peer->inventory_advertised_to_peer.end() )
- wdump( (*adv_to_peer) );
+ idump( (*adv_to_peer) );
if (adv_to_us != peer->inventory_peer_advertised_to_us.end() )
- wdump( (*adv_to_us) );
+ idump( (*adv_to_us) );
}
}
dlog("advertising ${count} new item(s) of ${types} type(s) to peer ${endpoint}",
@@ -2278,7 +2278,7 @@ namespace graphene { namespace net { namespace detail {
bool disconnect_from_inhibited_peer = false;
// if our client doesn't have any items after the item the peer requested, it will send back
// a list containing the last item the peer requested
- wdump((reply_message)(fetch_blockchain_item_ids_message_received.blockchain_synopsis));
+ idump((reply_message)(fetch_blockchain_item_ids_message_received.blockchain_synopsis));
if( reply_message.item_hashes_available.empty() )
originating_peer->peer_needs_sync_items_from_us = false; /* I have no items in my blockchain */
else if( !fetch_blockchain_item_ids_message_received.blockchain_synopsis.empty() &&
@@ -2935,7 +2935,7 @@ namespace graphene { namespace net { namespace detail {
if( closing_connection_message_received.closing_due_to_error )
{
- elog( "Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}",
+ wlog( "Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}",
( "peer", originating_peer->get_remote_endpoint() )
( "msg", closing_connection_message_received.reason_for_closing )
( "error", closing_connection_message_received.error ) );
diff --git a/libraries/net/peer_connection.cpp b/libraries/net/peer_connection.cpp
index f1f20d3f..4dd151b5 100644
--- a/libraries/net/peer_connection.cpp
+++ b/libraries/net/peer_connection.cpp
@@ -260,7 +260,7 @@ namespace graphene { namespace net
}
catch ( fc::exception& e )
{
- elog( "fatal: error connecting to peer ${remote_endpoint}: ${e}", ("remote_endpoint", remote_endpoint )("e", e.to_detail_string() ) );
+ wlog( "fatal: error connecting to peer ${remote_endpoint}: ${e}", ("remote_endpoint", remote_endpoint )("e", e.to_detail_string() ) );
throw;
}
} // connect_to()
@@ -312,24 +312,24 @@ namespace graphene { namespace net
}
catch (const fc::exception& send_error)
{
- elog("Error sending message: ${exception}. Closing connection.", ("exception", send_error));
+ wlog("Error sending message: ${exception}. Closing connection.", ("exception", send_error));
try
{
close_connection();
}
catch (const fc::exception& close_error)
{
- elog("Caught error while closing connection: ${exception}", ("exception", close_error));
+ wlog("Caught error while closing connection: ${exception}", ("exception", close_error));
}
return;
}
catch (const std::exception& e)
{
- elog("message_oriented_exception::send_message() threw a std::exception(): ${what}", ("what", e.what()));
+ wlog("message_oriented_exception::send_message() threw a std::exception(): ${what}", ("what", e.what()));
}
catch (...)
{
- elog("message_oriented_exception::send_message() threw an unhandled exception");
+ wlog("message_oriented_exception::send_message() threw an unhandled exception");
}
_queued_messages.front()->transmission_finish_time = fc::time_point::now();
_total_queued_messages_size -= _queued_messages.front()->get_size_in_queue();
@@ -345,7 +345,7 @@ namespace graphene { namespace net
_queued_messages.emplace(std::move(message_to_send));
if (_total_queued_messages_size > GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES)
{
- elog("send queue exceeded maximum size of ${max} bytes (current size ${current} bytes)",
+ wlog("send queue exceeded maximum size of ${max} bytes (current size ${current} bytes)",
("max", GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES)("current", _total_queued_messages_size));
try
{
From 8c188bd53fe1c21fd42ee80f26b3b0709d9e8e33 Mon Sep 17 00:00:00 2001
From: Alfredo Garcia
Date: Thu, 17 Oct 2019 13:39:44 -0300
Subject: [PATCH 016/151] merge gpos to develop (#186)
* issue - 154: Don't allow to vote when vesting balance is 0
* changes to withdraw_vesting feature(for both cdd and GPOS)
* Comments update
* update to GPOS hardfork ref
* fix for get_vesting_balance API call
* braces update
* Create .gitlab-ci.yml
* fixing build errors (#150)
* fixing build errors
vest type correction
* fixing build errors
vest type correction
* fixes
new Dockerfile
* vesting_balance_type correction
vesting_balance_type changed to normal
* gcc5 support to Dockerfile
gcc5 support to Dockerfile
* Changes to compiple with GCC 7(Ubuntu 18.04)
* changes to have separate methods and single withdrawl fee for multiple vest objects
* 163-fix, Return only non-zero vesting balances
* Revert "Revert "GPOS protocol""
This reverts commit 67616417b7f0b5d087b9862de0e48b2d8ccc1bca.
* add new line needed to gpos hardfork file
* comment temporally cli_vote_for_2_witnesses until refactor or delete
* fix gpos tests
* fix gitlab-ci conflict
---
.gitlab-ci.yml | 2 +-
libraries/app/database_api.cpp | 54 +-
.../app/include/graphene/app/database_api.hpp | 23 +-
libraries/chain/db_maint.cpp | 295 ++++--
libraries/chain/hardfork.d/GPOS.hf | 4 +
.../chain/include/graphene/chain/config.hpp | 4 +-
.../chain/include/graphene/chain/database.hpp | 5 +-
.../chain/protocol/chain_parameters.hpp | 23 +
.../graphene/chain/protocol/vesting.hpp | 10 +-
.../chain/vesting_balance_evaluator.hpp | 1 +
.../graphene/chain/vesting_balance_object.hpp | 8 +
libraries/chain/proposal_evaluator.cpp | 5 +
libraries/chain/vesting_balance_evaluator.cpp | 45 +-
libraries/chain/vesting_balance_object.cpp | 34 +-
.../wallet/include/graphene/wallet/wallet.hpp | 32 +-
libraries/wallet/wallet.cpp | 141 ++-
tests/cli/main.cpp | 4 +
tests/tests/gpos_tests.cpp | 954 ++++++++++++++++++
tests/tests/operation_tests.cpp | 7 +-
tests/tests/operation_tests2.cpp | 4 +-
20 files changed, 1568 insertions(+), 87 deletions(-)
create mode 100644 libraries/chain/hardfork.d/GPOS.hf
create mode 100644 tests/tests/gpos_tests.cpp
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8355d795..8747be6f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -30,7 +30,7 @@ test:
- ./tests/cli_test
tags:
- builder
-
+
code_quality:
stage: test
image: docker:stable
diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp
index e692f137..abdc0269 100644
--- a/libraries/app/database_api.cpp
+++ b/libraries/app/database_api.cpp
@@ -171,6 +171,8 @@ class database_api_impl : public std::enable_shared_from_this
vector get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state);
vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
+ // gpos
+ gpos_info get_gpos_info(const account_id_type account) const;
//private:
template
@@ -934,7 +936,8 @@ vector database_api_impl::get_vesting_balances( account_
auto vesting_range = _db.get_index_type().indices().get().equal_range(account_id);
std::for_each(vesting_range.first, vesting_range.second,
[&result](const vesting_balance_object& balance) {
- result.emplace_back(balance);
+ if(balance.balance.amount > 0)
+ result.emplace_back(balance);
});
return result;
}
@@ -2130,6 +2133,55 @@ vector database_api_impl::get_registered_tournaments(account
return tournament_ids;
}
+//////////////////////////////////////////////////////////////////////
+// //
+// GPOS methods //
+// //
+//////////////////////////////////////////////////////////////////////
+
+graphene::app::gpos_info database_api::get_gpos_info(const account_id_type account) const
+{
+ return my->get_gpos_info(account);
+
+}
+graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type account) const
+{
+ gpos_info result;
+ result.vesting_factor = _db.calculate_vesting_factor(account(_db));
+
+ const auto& dividend_data = asset_id_type()(_db).dividend_data(_db);
+ const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(_db);
+ result.award = _db.get_balance(dividend_distribution_account, asset_id_type()(_db));
+
+ share_type total_amount;
+ auto balance_type = vesting_balance_type::gpos;
+#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX
+ // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
+ auto vesting_balances_begin =
+ vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type(), balance_type));
+ auto vesting_balances_end =
+ vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), balance_type, share_type()));
+
+ for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
+ {
+ total_amount += vesting_balance_obj.balance.amount;
+ }
+#else
+ const vesting_balance_index& vesting_index = _db.get_index_type();
+ const auto& vesting_balances = vesting_index.indices().get();
+ for (const vesting_balance_object& vesting_balance_obj : vesting_balances)
+ {
+ if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance_type == balance_type)
+ {
+ total_amount += vesting_balance_obj.balance.amount;
+ }
+ }
+#endif
+
+ result.total_amount = total_amount;
+ return result;
+}
+
//////////////////////////////////////////////////////////////////////
// //
// Private methods //
diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp
index 78a9ca1f..b455546d 100644
--- a/libraries/app/include/graphene/app/database_api.hpp
+++ b/libraries/app/include/graphene/app/database_api.hpp
@@ -114,6 +114,12 @@ struct market_trade
double value;
};
+struct gpos_info {
+ double vesting_factor;
+ asset award;
+ share_type total_amount;
+};
+
/**
* @brief The database_api class implements the RPC API for the chain database.
*
@@ -673,7 +679,17 @@ class database_api
*/
vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
- private:
+ //////////
+ // GPOS //
+ //////////
+ /**
+ * @return account and network GPOS information
+ */
+ gpos_info get_gpos_info(const account_id_type account) const;
+
+
+
+private:
std::shared_ptr< database_api_impl > my;
};
@@ -684,6 +700,8 @@ FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) );
FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_trade, (date)(price)(amount)(value) );
+FC_REFLECT( graphene::app::gpos_info, (vesting_factor)(award)(total_amount) );
+
FC_API(graphene::app::database_api,
// Objects
@@ -801,4 +819,7 @@ FC_API(graphene::app::database_api,
(get_tournaments_by_state)
(get_tournaments )
(get_registered_tournaments)
+
+ // gpos
+ (get_gpos_info)
)
diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp
index 58288945..931f6c63 100644
--- a/libraries/chain/db_maint.cpp
+++ b/libraries/chain/db_maint.cpp
@@ -749,6 +749,120 @@ void deprecate_annual_members( database& db )
return;
}
+double database::calculate_vesting_factor(const account_object& stake_account)
+{
+ // get last time voted form stats
+ const auto &stats = stake_account.statistics(*this);
+ fc::time_point_sec last_date_voted = stats.last_vote_time;
+
+ // get global data related to gpos
+ const auto &gpo = this->get_global_properties();
+ const auto vesting_period = gpo.parameters.gpos_period();
+ const auto vesting_subperiod = gpo.parameters.gpos_subperiod();
+ const auto period_start = fc::time_point_sec(gpo.parameters.gpos_period_start());
+
+ // variables needed
+ const fc::time_point_sec period_end = period_start + vesting_period;
+ const auto number_of_subperiods = vesting_period / vesting_subperiod;
+ const auto now = this->head_block_time();
+ double vesting_factor;
+ auto seconds_since_period_start = now.sec_since_epoch() - period_start.sec_since_epoch();
+
+ FC_ASSERT(period_start <= now && now <= period_end);
+
+ // get in what sub period we are
+ uint32_t current_subperiod = 0;
+ std::list period_list(number_of_subperiods);
+ std::iota(period_list.begin(), period_list.end(), 1);
+
+ std::for_each(period_list.begin(), period_list.end(),[&](uint32_t period) {
+ if(seconds_since_period_start >= vesting_subperiod * (period - 1) &&
+ seconds_since_period_start < vesting_subperiod * period)
+ current_subperiod = period;
+ });
+
+ if(current_subperiod == 0 || current_subperiod > number_of_subperiods) return 0;
+ if(last_date_voted < period_start) return 0;
+
+ double numerator = number_of_subperiods;
+
+ if(current_subperiod > 1) {
+ std::list subperiod_list(current_subperiod - 1);
+ std::iota(subperiod_list.begin(), subperiod_list.end(), 2);
+ subperiod_list.reverse();
+
+ for(auto subperiod: subperiod_list)
+ {
+ numerator--;
+
+ auto last_period_start = period_start + fc::seconds(vesting_subperiod * (subperiod - 1));
+ auto last_period_end = period_start + fc::seconds(vesting_subperiod * (subperiod));
+
+ if (last_date_voted > last_period_start && last_date_voted <= last_period_end) {
+ numerator++;
+ break;
+ }
+ }
+ }
+ vesting_factor = numerator / number_of_subperiods;
+ return vesting_factor;
+}
+
+share_type credit_account(database& db, const account_id_type owner_id, const std::string owner_name,
+ share_type remaining_amount_to_distribute,
+ const share_type shares_to_credit, const asset_id_type payout_asset_type,
+ const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index,
+ const asset_id_type dividend_id) {
+
+ //wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset));
+ if (shares_to_credit.value) {
+
+ remaining_amount_to_distribute -= shares_to_credit;
+
+ dlog("Crediting account ${account} with ${amount}",
+ ("account", owner_name)
+ ("amount", asset(shares_to_credit, payout_asset_type)));
+ auto pending_payout_iter =
+ pending_payout_balance_index.indices().get().find(
+ boost::make_tuple(dividend_id, payout_asset_type,
+ owner_id));
+ if (pending_payout_iter ==
+ pending_payout_balance_index.indices().get().end())
+ db.create(
+ [&](pending_dividend_payout_balance_for_holder_object &obj) {
+ obj.owner = owner_id;
+ obj.dividend_holder_asset_type = dividend_id;
+ obj.dividend_payout_asset_type = payout_asset_type;
+ obj.pending_balance = shares_to_credit;
+ });
+ else
+ db.modify(*pending_payout_iter,
+ [&](pending_dividend_payout_balance_for_holder_object &pending_balance) {
+ pending_balance.pending_balance += shares_to_credit;
+ });
+ }
+ return remaining_amount_to_distribute;
+}
+
+void rolling_period_start(database& db)
+{
+ if(db.head_block_time() >= HARDFORK_GPOS_TIME)
+ {
+ auto gpo = db.get_global_properties();
+ auto period_start = db.get_global_properties().parameters.gpos_period_start();
+ auto vesting_period = db.get_global_properties().parameters.gpos_period();
+
+ auto now = db.head_block_time();
+ if(now.sec_since_epoch() > (period_start + vesting_period))
+ {
+ // roll
+ db.modify(db.get_global_properties(), [now](global_property_object& p) {
+ p.parameters.extensions.value.gpos_period_start = now.sec_since_epoch();
+ });
+ }
+ }
+}
+
// Schedules payouts from a dividend distribution account to the current holders of the
// dividend-paying asset. This takes any deposits made to the dividend distribution account
// since the last time it was called, and distributes them to the current owners of the
@@ -780,34 +894,42 @@ void schedule_pending_dividend_balances(database& db,
balance_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id));
auto holder_balances_end =
balance_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type()));
- uint32_t holder_account_count = std::distance(holder_balances_begin, holder_balances_end);
uint64_t distribution_base_fee = gpo.parameters.current_fees->get().distribution_base_fee;
uint32_t distribution_fee_per_holder = gpo.parameters.current_fees->get().distribution_fee_per_holder;
- // the fee, in BTS, for distributing each asset in the account
- uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder;
std::map vesting_amounts;
+
+ auto balance_type = vesting_balance_type::normal;
+ if(db.head_block_time() >= HARDFORK_GPOS_TIME)
+ balance_type = vesting_balance_type::gpos;
+
+ uint32_t holder_account_count = 0;
+
#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX
// get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
auto vesting_balances_begin =
- vesting_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id));
+ vesting_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id, balance_type));
auto vesting_balances_end =
- vesting_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type()));
+ vesting_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, balance_type, share_type()));
+
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
{
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
- //dlog("Vesting balance for account: ${owner}, amount: ${amount}",
- // ("owner", vesting_balance_obj.owner(db).name)
- // ("amount", vesting_balance_obj.balance.amount));
+ ++holder_account_count;
+ dlog("Vesting balance for account: ${owner}, amount: ${amount}",
+ ("owner", vesting_balance_obj.owner(db).name)
+ ("amount", vesting_balance_obj.balance.amount));
}
#else
// get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
const auto& vesting_balances = vesting_index.indices().get();
for (const vesting_balance_object& vesting_balance_obj : vesting_balances)
{
- if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount)
+ if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount &&
+ vesting_balance_object.balance_type == balance_type)
{
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
+ ++gpos_holder_account_count;
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
("owner", vesting_balance_obj.owner(db).name)
("amount", vesting_balance_obj.balance.amount));
@@ -816,6 +938,12 @@ void schedule_pending_dividend_balances(database& db,
#endif
auto current_distribution_account_balance_iter = current_distribution_account_balance_range.begin();
+ if(db.head_block_time() < HARDFORK_GPOS_TIME)
+ holder_account_count = std::distance(holder_balances_begin, holder_balances_end);
+ // the fee, in BTS, for distributing each asset in the account
+ uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder;
+
+ //auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first;
auto previous_distribution_account_balance_iter = previous_distribution_account_balance_range.first;
dlog("Current balances in distribution account: ${current}, Previous balances: ${previous}",
("current", (int64_t)std::distance(current_distribution_account_balance_range.begin(), current_distribution_account_balance_range.end()))
@@ -825,14 +953,23 @@ void schedule_pending_dividend_balances(database& db,
// accounts other than the distribution account (it would be silly to distribute dividends back to
// the distribution account)
share_type total_balance_of_dividend_asset;
- for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end))
- if (holder_balance_object.owner != dividend_data.dividend_distribution_account)
- {
- total_balance_of_dividend_asset += holder_balance_object.balance;
- auto itr = vesting_amounts.find(holder_balance_object.owner);
- if (itr != vesting_amounts.end())
- total_balance_of_dividend_asset += itr->second;
- }
+ if(db.head_block_time() >= HARDFORK_GPOS_TIME && dividend_holder_asset_obj.symbol == GRAPHENE_SYMBOL) { // only core
+ for (const vesting_balance_object &holder_balance_object : boost::make_iterator_range(vesting_balances_begin,
+ vesting_balances_end))
+ if (holder_balance_object.owner != dividend_data.dividend_distribution_account) {
+ total_balance_of_dividend_asset += holder_balance_object.balance.amount;
+ }
+ }
+ else {
+ for (const account_balance_object &holder_balance_object : boost::make_iterator_range(holder_balances_begin,
+ holder_balances_end))
+ if (holder_balance_object.owner != dividend_data.dividend_distribution_account) {
+ total_balance_of_dividend_asset += holder_balance_object.balance;
+ auto itr = vesting_amounts.find(holder_balance_object.owner);
+ if (itr != vesting_amounts.end())
+ total_balance_of_dividend_asset += itr->second;
+ }
+ }
// loop through all of the assets currently or previously held in the distribution account
while (current_distribution_account_balance_iter != current_distribution_account_balance_range.end() ||
previous_distribution_account_balance_iter != previous_distribution_account_balance_range.second)
@@ -956,46 +1093,68 @@ void schedule_pending_dividend_balances(database& db,
("total", total_balance_of_dividend_asset));
share_type remaining_amount_to_distribute = delta_balance;
- // credit each account with their portion, don't send any back to the dividend distribution account
- for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end))
- {
- if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue;
+ if(db.head_block_time() >= HARDFORK_GPOS_TIME && dividend_holder_asset_obj.symbol == GRAPHENE_SYMBOL) { // core only
+ // credit each account with their portion, don't send any back to the dividend distribution account
+ for (const vesting_balance_object &holder_balance_object : boost::make_iterator_range(
+ vesting_balances_begin, vesting_balances_end)) {
+ if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue;
- auto holder_balance = holder_balance_object.balance;
+ auto vesting_factor = db.calculate_vesting_factor(holder_balance_object.owner(db));
- auto itr = vesting_amounts.find(holder_balance_object.owner);
- if (itr != vesting_amounts.end())
- holder_balance += itr->second;
+ auto holder_balance = holder_balance_object.balance;
- fc::uint128_t amount_to_credit(delta_balance.value);
- amount_to_credit *= holder_balance.value;
- amount_to_credit /= total_balance_of_dividend_asset.value;
- share_type shares_to_credit((int64_t)amount_to_credit.to_uint64());
- if (shares_to_credit.value)
- {
- wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset));
+ fc::uint128_t amount_to_credit(delta_balance.value);
+ amount_to_credit *= holder_balance.amount.value;
+ amount_to_credit /= total_balance_of_dividend_asset.value;
+ share_type full_shares_to_credit((int64_t) amount_to_credit.to_uint64());
+ share_type shares_to_credit = (uint64_t) floor(full_shares_to_credit.value * vesting_factor);
- remaining_amount_to_distribute -= shares_to_credit;
+ if (shares_to_credit < full_shares_to_credit) {
+ // Todo: sending results of decay to committee account, need to change to specified account
+ dlog("Crediting committee_account with ${amount}",
+ ("amount", asset(full_shares_to_credit - shares_to_credit, payout_asset_type)));
+ db.adjust_balance(dividend_data.dividend_distribution_account,
+ -(full_shares_to_credit - shares_to_credit));
+ db.adjust_balance(account_id_type(0), full_shares_to_credit - shares_to_credit);
+ }
- dlog("Crediting account ${account} with ${amount}",
- ("account", holder_balance_object.owner(db).name)
- ("amount", asset(shares_to_credit, payout_asset_type)));
- auto pending_payout_iter =
- pending_payout_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_obj.id, payout_asset_type, holder_balance_object.owner));
- if (pending_payout_iter == pending_payout_balance_index.indices().get().end())
- db.create( [&]( pending_dividend_payout_balance_for_holder_object& obj ){
- obj.owner = holder_balance_object.owner;
- obj.dividend_holder_asset_type = dividend_holder_asset_obj.id;
- obj.dividend_payout_asset_type = payout_asset_type;
- obj.pending_balance = shares_to_credit;
- });
- else
- db.modify(*pending_payout_iter, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){
- pending_balance.pending_balance += shares_to_credit;
- });
+ remaining_amount_to_distribute = credit_account(db,
+ holder_balance_object.owner,
+ holder_balance_object.owner(db).name,
+ remaining_amount_to_distribute,
+ shares_to_credit,
+ payout_asset_type,
+ pending_payout_balance_index,
+ dividend_holder_asset_obj.id);
}
}
+ else {
+ // credit each account with their portion, don't send any back to the dividend distribution account
+ for (const account_balance_object &holder_balance_object : boost::make_iterator_range(
+ holder_balances_begin, holder_balances_end)) {
+ if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue;
+ auto holder_balance = holder_balance_object.balance;
+
+ auto itr = vesting_amounts.find(holder_balance_object.owner);
+ if (itr != vesting_amounts.end())
+ holder_balance += itr->second;
+
+ fc::uint128_t amount_to_credit(delta_balance.value);
+ amount_to_credit *= holder_balance.value;
+ amount_to_credit /= total_balance_of_dividend_asset.value;
+ share_type shares_to_credit((int64_t) amount_to_credit.to_uint64());
+
+ remaining_amount_to_distribute = credit_account(db,
+ holder_balance_object.owner,
+ holder_balance_object.owner(db).name,
+ remaining_amount_to_distribute,
+ shares_to_credit,
+ payout_asset_type,
+ pending_payout_balance_index,
+ dividend_holder_asset_obj.id);
+ }
+ }
for (const auto& pending_payout : pending_payout_balance_index.indices())
if (pending_payout.pending_balance.value)
dlog("Pending payout: ${account_name} -> ${amount}",
@@ -1256,6 +1415,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
distribute_fba_balances(*this);
create_buyback_orders(*this);
+ rolling_period_start(*this);
+
process_dividend_assets(*this);
struct vote_tally_helper {
@@ -1271,24 +1432,28 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
d._total_voting_stake = 0;
+ auto balance_type = vesting_balance_type::normal;
+ if(d.head_block_time() >= HARDFORK_GPOS_TIME)
+ balance_type = vesting_balance_type::gpos;
+
const vesting_balance_index& vesting_index = d.get_index_type();
#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX
auto vesting_balances_begin =
- vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type()));
+ vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type(), balance_type));
auto vesting_balances_end =
- vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), share_type()));
+ vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), balance_type, share_type()));
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
{
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
- //dlog("Vesting balance for account: ${owner}, amount: ${amount}",
- // ("owner", vesting_balance_obj.owner(d).name)
- // ("amount", vesting_balance_obj.balance.amount));
+ dlog("Vesting balance for account: ${owner}, amount: ${amount}",
+ ("owner", vesting_balance_obj.owner(d).name)
+ ("amount", vesting_balance_obj.balance.amount));
}
#else
const auto& vesting_balances = vesting_index.indices().get();
for (const vesting_balance_object& vesting_balance_obj : vesting_balances)
{
- if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount)
+ if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount && vesting_balance_obj.balance_type == balance_type)
{
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
@@ -1316,13 +1481,27 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
const account_object& opinion_account = *opinion_account_ptr;
const auto& stats = stake_account.statistics(d);
- uint64_t voting_stake = stats.total_core_in_orders.value
- + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0)
- + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value;
+ uint64_t voting_stake = 0;
auto itr = vesting_amounts.find(stake_account.id);
if (itr != vesting_amounts.end())
voting_stake += itr->second.value;
+
+ if(d.head_block_time() >= HARDFORK_GPOS_TIME)
+ {
+ if (itr == vesting_amounts.end())
+ return;
+
+ auto vesting_factor = d.calculate_vesting_factor(stake_account);
+ voting_stake = (uint64_t)floor(voting_stake * vesting_factor);
+ }
+ else
+ {
+ voting_stake += stats.total_core_in_orders.value
+ + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value : 0)
+ + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value;
+ }
+
for( vote_id_type id : opinion_account.options.votes )
{
uint32_t offset = id.instance();
diff --git a/libraries/chain/hardfork.d/GPOS.hf b/libraries/chain/hardfork.d/GPOS.hf
new file mode 100644
index 00000000..e109a8ad
--- /dev/null
+++ b/libraries/chain/hardfork.d/GPOS.hf
@@ -0,0 +1,4 @@
+// GPOS HARDFORK Friday, March 15, 2019 11:57:28 PM
+#ifndef HARDFORK_GPOS_TIME
+#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1552694248 ))
+#endif
diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp
index a5354f85..933f5997 100644
--- a/libraries/chain/include/graphene/chain/config.hpp
+++ b/libraries/chain/include/graphene/chain/config.hpp
@@ -227,8 +227,10 @@
#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 SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)
#define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0))
#define SWEEPS_VESTING_BALANCE_MULTIPLIER 100000000
#define SWEEPS_ACCUMULATOR_ACCOUNT (graphene::chain::account_id_type(0))
+#define GPOS_PERIOD (60*60*24*30*6) // 6 months
+#define GPOS_SUBPERIOD (60*60*24*30) // 1 month
+#define GPOS_VESTING_LOCKIN_PERIOD (60*60*24*30) // 1 month
diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp
index cd0a9766..dee3d006 100644
--- a/libraries/chain/include/graphene/chain/database.hpp
+++ b/libraries/chain/include/graphene/chain/database.hpp
@@ -522,7 +522,10 @@ namespace graphene { namespace chain {
void update_active_witnesses();
void update_active_committee_members();
void update_worker_votes();
-
+
+ public:
+ double calculate_vesting_factor(const account_object& stake_account);
+
template
void perform_account_maintenance(std::tuple helpers);
///@}
diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
index 647d3f99..20ed68e1 100644
--- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
+++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp
@@ -27,6 +27,8 @@
#include
#include
+#include <../hardfork.d/GPOS.hf>
+
namespace graphene { namespace chain { struct fee_schedule; } }
namespace graphene { namespace chain {
@@ -40,6 +42,11 @@ namespace graphene { namespace chain {
optional< uint16_t > sweeps_distribution_percentage;
optional< asset_id_type > sweeps_distribution_asset;
optional< account_id_type > sweeps_vesting_accumulator_account;
+ /* gpos parameters */
+ optional < uint32_t > gpos_period;
+ optional < uint32_t > gpos_subperiod;
+ optional < uint32_t > gpos_period_start;
+ optional < uint32_t > gpos_vesting_lockin_period;
};
struct chain_parameters
@@ -119,6 +126,18 @@ namespace graphene { namespace chain {
inline account_id_type sweeps_vesting_accumulator_account()const {
return extensions.value.sweeps_vesting_accumulator_account.valid() ? *extensions.value.sweeps_vesting_accumulator_account : SWEEPS_ACCUMULATOR_ACCOUNT;
}
+ inline uint32_t gpos_period()const {
+ return extensions.value.gpos_period.valid() ? *extensions.value.gpos_period : GPOS_PERIOD; /// total seconds of current gpos period
+ }
+ inline uint32_t gpos_subperiod()const {
+ return extensions.value.gpos_subperiod.valid() ? *extensions.value.gpos_subperiod : GPOS_SUBPERIOD; /// gpos_period % gpos_subperiod = 0
+ }
+ 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 gpos_vesting_lockin_period()const {
+ return extensions.value.gpos_vesting_lockin_period.valid() ? *extensions.value.gpos_vesting_lockin_period : GPOS_VESTING_LOCKIN_PERIOD; /// GPOS vesting lockin period
+ }
};
} } // graphene::chain
@@ -132,6 +151,10 @@ FC_REFLECT( graphene::chain::parameter_extension,
(sweeps_distribution_percentage)
(sweeps_distribution_asset)
(sweeps_vesting_accumulator_account)
+ (gpos_period)
+ (gpos_subperiod)
+ (gpos_period_start)
+ (gpos_vesting_lockin_period)
)
FC_REFLECT( graphene::chain::chain_parameters,
diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp
index 4915b62e..abe380a7 100644
--- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp
+++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp
@@ -24,7 +24,9 @@
#pragma once
#include
-namespace graphene { namespace chain {
+namespace graphene { namespace chain {
+
+ enum class vesting_balance_type { normal, gpos };
struct linear_vesting_policy_initializer
{
@@ -72,6 +74,7 @@ namespace graphene { namespace chain {
account_id_type owner; ///< Who is able to withdraw the balance
asset amount;
vesting_policy_initializer policy;
+ vesting_balance_type balance_type;
account_id_type fee_payer()const { return creator; }
void validate()const
@@ -112,9 +115,12 @@ namespace graphene { namespace chain {
FC_REFLECT( graphene::chain::vesting_balance_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type, (fee) )
-FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy) )
+FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy)(balance_type) )
FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) )
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_TYPENAME( graphene::chain::vesting_policy_initializer )
+
+FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos) )
+
diff --git a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp
index fccfbb75..9bb7520e 100644
--- a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp
+++ b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp
@@ -46,6 +46,7 @@ class vesting_balance_withdraw_evaluator : public evaluator
+#include
+
#include
#include
@@ -140,6 +142,9 @@ namespace graphene { namespace chain {
/// The vesting policy stores details on when funds vest, and controls when they may be withdrawn
vesting_policy policy;
+ /// We can have 2 types of vesting, gpos and all the rest
+ vesting_balance_type balance_type = vesting_balance_type::normal;
+
vesting_balance_object() {}
asset_id_type get_asset_id() const { return balance.asset_id; }
@@ -186,12 +191,14 @@ namespace graphene { namespace chain {
composite_key<
vesting_balance_object,
member_offset,
+ member,
member_offset
//member
//member_offset
>,
composite_key_compare<
std::less< asset_id_type >,
+ std::less< vesting_balance_type >,
std::greater< share_type >
//std::less< account_id_type >
>
@@ -225,4 +232,5 @@ FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::objec
(owner)
(balance)
(policy)
+ (balance_type)
)
diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp
index 3a44ca5c..0b42f371 100644
--- a/libraries/chain/proposal_evaluator.cpp
+++ b/libraries/chain/proposal_evaluator.cpp
@@ -135,6 +135,11 @@ struct proposal_operation_hardfork_visitor
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_update_status_operation not allowed yet!" );
}
+ void operator()(const vesting_balance_create_operation &vbco) const {
+ if(block_time < HARDFORK_GPOS_TIME)
+ FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create 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)
diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp
index ee918fd1..d0f3c345 100644
--- a/libraries/chain/vesting_balance_evaluator.cpp
+++ b/libraries/chain/vesting_balance_evaluator.cpp
@@ -42,6 +42,10 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance
FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount );
FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() );
+ 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::normal);
+
+
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@@ -92,13 +96,49 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance
// If making changes to this logic, check if those changes should also be made there as well.
obj.owner = op.owner;
obj.balance = op.amount;
- op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) );
+ if(op.balance_type == vesting_balance_type::gpos)
+ {
+ const auto &gpo = d.get_global_properties();
+ // forcing gpos policy
+ linear_vesting_policy p;
+ p.begin_timestamp = now;
+ p.vesting_cliff_seconds = gpo.parameters.gpos_vesting_lockin_period();
+ p.vesting_duration_seconds = gpo.parameters.gpos_subperiod();
+ obj.policy = p;
+ }
+ else {
+ op.policy.visit(init_policy_visitor(obj.policy, op.amount.amount, now));
+ }
+ obj.balance_type = op.balance_type;
} );
return vbo.id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
+operation_result vesting_balance_withdraw_evaluator::start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply )
+{ try {
+ trx_state = &eval_state;
+ database& d = db();
+ const auto& oper = op.get();
+
+ const time_point_sec now = d.head_block_time();
+
+ if(now >= (fc::time_point_sec(1570114100)) )
+ {
+ if(oper.fee.amount == 0)
+ {
+ trx_state->skip_fee_schedule_check = true;
+ trx_state->skip_fee = true;
+ }
+ }
+ //check_required_authorities(op);
+ auto result = evaluate( oper );
+
+ if( apply ) result = this->apply( oper );
+ return result;
+} FC_CAPTURE_AND_RETHROW() }
+
void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balance_withdraw_operation& op )
{ try {
const database& d = db();
@@ -109,7 +149,7 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan
FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "", ("now", now)("op", op)("vbo", vbo) );
assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached
- /* const account_object& owner_account = */ op.owner( d );
+ /* const account_object& owner_account = op.owner( d ); */
// TODO: Check asset authorizations and withdrawals
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@@ -117,6 +157,7 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan
void_result vesting_balance_withdraw_evaluator::do_apply( const vesting_balance_withdraw_operation& op )
{ try {
database& d = db();
+
const time_point_sec now = d.head_block_time();
const vesting_balance_object& vbo = op.vesting_balance( d );
diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp
index 73448e04..afba2557 100644
--- a/libraries/chain/vesting_balance_object.cpp
+++ b/libraries/chain/vesting_balance_object.cpp
@@ -45,23 +45,33 @@ asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context&
if( elapsed_seconds >= vesting_cliff_seconds )
{
- share_type total_vested = 0;
- if( elapsed_seconds < vesting_duration_seconds )
+ // BLOCKBACK-154 fix, Begin balance for linear vesting applies only to initial account balance from genesis
+ // So, for any GPOS vesting, the begin balance would be 0 and should be able to withdraw balance amount based on lockin period
+ if(begin_balance == 0)
{
- total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64();
+ allowed_withdraw = ctx.balance.amount;
+ return asset( allowed_withdraw, ctx.balance.asset_id );
}
else
{
- total_vested = begin_balance;
+ 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 );
}
- 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 );
diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp
index 3890a2b4..ac54f258 100644
--- a/libraries/wallet/include/graphene/wallet/wallet.hpp
+++ b/libraries/wallet/include/graphene/wallet/wallet.hpp
@@ -1378,7 +1378,7 @@ class wallet_api
vector< vesting_balance_object_with_info > get_vesting_balances( string account_name );
/**
- * Withdraw a vesting balance.
+ * Withdraw a normal(old) vesting balance.
*
* @param witness_name The account name of the witness, also accepts account ID or vesting balance ID type.
* @param amount The amount to withdraw.
@@ -1391,6 +1391,20 @@ class wallet_api
string asset_symbol,
bool broadcast = false);
+ /**
+ * Withdraw a GPOS vesting balance.
+ *
+ * @param account_name The account name of the witness/user, also accepts account ID or vesting balance ID type.
+ * @param amount The amount to withdraw.
+ * @param asset_symbol The symbol of the asset to withdraw.
+ * @param broadcast true if you wish to broadcast the transaction
+ */
+ signed_transaction withdraw_GPOS_vesting_balance(
+ string account_name,
+ string amount,
+ string asset_symbol,
+ bool broadcast = false);
+
/** Vote for a given committee_member.
*
* An account can publish a list of all committee_memberes they approve of. This
@@ -1861,6 +1875,20 @@ class wallet_api
rock_paper_scissors_gesture gesture,
bool broadcast);
+ /** Create a vesting balance including gpos vesting balance after HARDFORK_GPOS_TIME
+ * @param owner vesting balance owner and creator
+ * @param amount amount to vest
+ * @param asset_symbol the symbol of the asset to vest
+ * @param is_gpos True if the balance is of gpos type
+ * @param broadcast true if you wish to broadcast the transaction
+ * @return the signed version of the transaction
+ */
+ signed_transaction create_vesting_balance(string owner,
+ string amount,
+ string asset_symbol,
+ bool is_gpos,
+ bool broadcast);
+
void dbg_make_uia(string creator, string symbol);
void dbg_make_mia(string creator, string symbol);
void dbg_push_blocks( std::string src_filename, uint32_t count );
@@ -2022,6 +2050,7 @@ FC_API( graphene::wallet::wallet_api,
(update_worker_votes)
(get_vesting_balances)
(withdraw_vesting)
+ (withdraw_GPOS_vesting_balance)
(vote_for_committee_member)
(vote_for_witness)
(update_witness_votes)
@@ -2106,6 +2135,7 @@ FC_API( graphene::wallet::wallet_api,
(tournament_join)
(tournament_leave)
(rps_throw)
+ (create_vesting_balance)
(get_upcoming_tournaments)
(get_tournaments)
(get_tournaments_by_state)
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index 47f2460a..4469e598 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -2033,6 +2033,10 @@ public:
}
vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid );
+
+ if(vbo.balance_type != vesting_balance_type::normal)
+ FC_THROW("Allowed to withdraw only Normal type vest balances with this method");
+
vesting_balance_withdraw_operation vesting_balance_withdraw_op;
vesting_balance_withdraw_op.vesting_balance = *vbid;
@@ -2048,11 +2052,94 @@ public:
} FC_CAPTURE_AND_RETHROW( (witness_name)(amount) )
}
+ signed_transaction withdraw_GPOS_vesting_balance(
+ string account_name,
+ string amount,
+ string asset_symbol,
+ bool broadcast = false)
+ { try {
+ asset_object asset_obj = get_asset( asset_symbol );
+ vector< vesting_balance_object > vbos;
+ fc::optional vbid = maybe_id(account_name);
+ if( !vbid )
+ {
+ //Changes done to retrive user account/witness account based on account name
+ fc::optional acct_id = maybe_id( account_name );
+ if( !acct_id )
+ acct_id = get_account( account_name ).id;
+
+ vbos = _remote_db->get_vesting_balances( *acct_id );
+ if( vbos.size() == 0 )
+ {
+ witness_object wit = get_witness( account_name );
+ FC_ASSERT( wit.pay_vb );
+ vbid = wit.pay_vb;
+ }
+ }
+
+ //whether it is a witness or user, keep it in a container and iterate over to process all vesting balances and types
+ if(!vbos.size())
+ vbos.emplace_back( get_object(*vbid) );
+
+ signed_transaction tx;
+ asset withdraw_amount = asset_obj.amount_from_string(amount);
+ bool onetime_fee_paid = false;
+
+ for(const vesting_balance_object& vbo: vbos )
+ {
+ if((vbo.balance_type == vesting_balance_type::gpos) && vbo.balance.amount > 0)
+ {
+ fc::optional vest_id = vbo.id;
+ vesting_balance_withdraw_operation vesting_balance_withdraw_op;
+
+ // Since there are multiple vesting objects, below logic with vesting_balance_evaluator.cpp changes will
+ // deduct fee from single object and set withdrawl fee to 0 for rest of objects based on requested amount.
+ if(onetime_fee_paid)
+ vesting_balance_withdraw_op.fee = asset( 0, asset_id_type() );
+ else
+ vesting_balance_withdraw_op.fee = _remote_db->get_global_properties().parameters.current_fees->calculate_fee(vesting_balance_withdraw_op);
+
+ vesting_balance_withdraw_op.vesting_balance = *vest_id;
+ vesting_balance_withdraw_op.owner = vbo.owner;
+ if(withdraw_amount.amount >= vbo.balance.amount)
+ {
+ vesting_balance_withdraw_op.amount = vbo.balance.amount;
+ withdraw_amount.amount -= vbo.balance.amount;
+ }
+ else
+ {
+ vesting_balance_withdraw_op.amount = withdraw_amount.amount;
+ tx.operations.push_back( vesting_balance_withdraw_op );
+ withdraw_amount.amount -= vbo.balance.amount;
+ break;
+ }
+
+ tx.operations.push_back( vesting_balance_withdraw_op );
+ onetime_fee_paid = true;
+ }
+ }
+
+ if( withdraw_amount.amount > 0)
+ FC_THROW("Account has NO or Insufficient balance to withdraw");
+
+ tx.validate();
+
+ return sign_transaction( tx, broadcast );
+ } FC_CAPTURE_AND_RETHROW( (account_name)(amount) )
+ }
+
signed_transaction vote_for_committee_member(string voting_account,
string committee_member,
bool approve,
bool broadcast /* = false */)
{ try {
+ std::vector vbo_info = get_vesting_balances(voting_account);
+ std::vector::iterator vbo_iter;
+
+ vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;});
+ if( vbo_info.size() == 0 || vbo_iter == vbo_info.end())
+ FC_THROW("Account *** ${account} *** have insufficient or 0 vested balance(GPOS) to vote", ("account", voting_account));
+
account_object voting_account_object = get_account(voting_account);
account_id_type committee_member_owner_account_id = get_account_id(committee_member);
fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member_owner_account_id);
@@ -2087,6 +2174,13 @@ public:
bool approve,
bool broadcast /* = false */)
{ try {
+ std::vector vbo_info = get_vesting_balances(voting_account);
+ std::vector::iterator vbo_iter;
+
+ vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;});
+ if( vbo_info.size() == 0 || vbo_iter == vbo_info.end())
+ FC_THROW("Account *** ${account} *** have insufficient or 0 vested balance(GPOS) to vote", ("account", voting_account));
+
account_object voting_account_object = get_account(voting_account);
account_id_type witness_owner_account_id = get_account_id(witness);
fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
@@ -4171,11 +4265,20 @@ signed_transaction wallet_api::withdraw_vesting(
string witness_name,
string amount,
string asset_symbol,
- bool broadcast /* = false */)
+ bool broadcast)
{
return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast );
}
+signed_transaction wallet_api::withdraw_GPOS_vesting_balance(
+ string account_name,
+ string amount,
+ string asset_symbol,
+ bool broadcast)
+{
+ return my->withdraw_GPOS_vesting_balance( account_name, amount, asset_symbol, broadcast );
+}
+
signed_transaction wallet_api::vote_for_committee_member(string voting_account,
string witness,
bool approve,
@@ -5917,6 +6020,37 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id,
return my->sign_transaction( tx, broadcast );
}
+signed_transaction wallet_api::create_vesting_balance(string owner,
+ string amount,
+ string asset_symbol,
+ bool is_gpos,
+ bool broadcast)
+{
+ FC_ASSERT( !is_locked() );
+
+ account_object owner_account = get_account(owner);
+ account_id_type owner_id = owner_account.id;
+
+ fc::optional asset_obj = get_asset(asset_symbol);
+
+ auto type = vesting_balance_type::normal;
+ if(is_gpos)
+ type = vesting_balance_type::gpos;
+
+ vesting_balance_create_operation op;
+ op.creator = owner_id;
+ op.owner = owner_id;
+ op.amount = asset_obj->amount_from_string(amount);
+ op.balance_type = type;
+
+ signed_transaction trx;
+ trx.operations.push_back(op);
+ my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees );
+ trx.validate();
+
+ return my->sign_transaction( trx, broadcast );
+}
+
// default ctor necessary for FC_REFLECT
signed_block_with_info::signed_block_with_info()
{
@@ -5972,7 +6106,10 @@ vesting_balance_object_with_info::vesting_balance_object_with_info( const vestin
: vesting_balance_object( vbo )
{
allowed_withdraw = get_allowed_withdraw( now );
- allowed_withdraw_time = now;
+ if(vbo.balance_type == vesting_balance_type::gpos)
+ allowed_withdraw_time = vbo.policy.get().begin_timestamp + vbo.policy.get().vesting_cliff_seconds;
+ else
+ allowed_withdraw_time = now;
}
} } // graphene::wallet
diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp
index aceae279..8fd5b5f4 100644
--- a/tests/cli/main.cpp
+++ b/tests/cli/main.cpp
@@ -417,6 +417,9 @@ BOOST_FIXTURE_TEST_CASE( create_new_account, cli_fixture )
// Vote for two witnesses, and make sure they both stay there
// after a maintenance block
///////////////////////
+
+// Todo: Removed by GPOS, refactor test.
+/*
BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )
{
try
@@ -462,6 +465,7 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )
throw;
}
}
+*/
BOOST_FIXTURE_TEST_CASE( cli_get_signed_transaction_signers, cli_fixture )
{
diff --git a/tests/tests/gpos_tests.cpp b/tests/tests/gpos_tests.cpp
new file mode 100644
index 00000000..2f8f7014
--- /dev/null
+++ b/tests/tests/gpos_tests.cpp
@@ -0,0 +1,954 @@
+/*
+ * Copyright (c) 2018 oxarbitrage and contributors.
+ *
+ * The MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "../common/database_fixture.hpp"
+
+#include
+
+using namespace graphene::chain;
+using namespace graphene::chain::test;
+
+struct gpos_fixture: database_fixture
+{
+ const worker_object& create_worker( const account_id_type owner, const share_type daily_pay,
+ const fc::microseconds& duration ) {
+ worker_create_operation op;
+ op.owner = owner;
+ op.daily_pay = daily_pay;
+ op.initializer = vesting_balance_worker_initializer(1);
+ op.work_begin_date = db.head_block_time();
+ op.work_end_date = op.work_begin_date + duration;
+ trx.operations.push_back(op);
+ set_expiration(db, trx);
+ trx.validate();
+ processed_transaction ptx = db.push_transaction(trx, ~0);
+ trx.clear();
+ return db.get(ptx.operation_results[0].get());
+ }
+ const vesting_balance_object& create_vesting(const account_id_type owner, const asset amount,
+ const vesting_balance_type type)
+ {
+ vesting_balance_create_operation op;
+ op.creator = owner;
+ op.owner = owner;
+ op.amount = amount;
+ op.balance_type = type;
+
+ trx.operations.push_back(op);
+ set_expiration(db, trx);
+ processed_transaction ptx = PUSH_TX(db, trx, ~0);
+ trx.clear();
+ return db.get(ptx.operation_results[0].get());
+ }
+
+ void update_payout_interval(std::string asset_name, fc::time_point start, uint32_t interval)
+ {
+ auto dividend_holder_asset_object = get_asset(asset_name);
+ asset_update_dividend_operation op;
+ op.issuer = dividend_holder_asset_object.issuer;
+ op.asset_to_update = dividend_holder_asset_object.id;
+ op.new_options.next_payout_time = start;
+ op.new_options.payout_interval = interval;
+ trx.operations.push_back(op);
+ set_expiration(db, trx);
+ PUSH_TX(db, trx, ~0);
+ trx.operations.clear();
+ }
+
+ void update_gpos_global(uint32_t vesting_period, uint32_t vesting_subperiod, fc::time_point_sec period_start)
+ {
+ db.modify(db.get_global_properties(), [vesting_period, vesting_subperiod, period_start](global_property_object& p) {
+ p.parameters.extensions.value.gpos_period = vesting_period;
+ p.parameters.extensions.value.gpos_subperiod = vesting_subperiod;
+ p.parameters.extensions.value.gpos_period_start = period_start.sec_since_epoch();
+ });
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), vesting_period);
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), vesting_subperiod);
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), period_start.sec_since_epoch());
+ }
+ void vote_for(const account_id_type account_id, const vote_id_type vote_for, const fc::ecc::private_key& key)
+ {
+ account_update_operation op;
+ op.account = account_id;
+ op.new_options = account_id(db).options;
+ op.new_options->votes.insert(vote_for);
+ trx.operations.push_back(op);
+ set_expiration(db, trx);
+ trx.validate();
+ sign(trx, key);
+ PUSH_TX(db, trx);
+ trx.clear();
+ }
+ void fill_reserve_pool(const account_id_type account_id, asset amount)
+ {
+ asset_reserve_operation op;
+ op.payer = account_id;
+ op.amount_to_reserve = amount;
+ trx.operations.push_back(op);
+ trx.validate();
+ set_expiration(db, trx);
+ PUSH_TX( db, trx, ~0 );
+ trx.clear();
+ }
+
+ void advance_x_maint(int periods)
+ {
+ for(int i=0; i(ptx.operation_results[0].get());
+
+ // check created vesting amount and policy
+ BOOST_CHECK_EQUAL(alice_vesting.balance.amount.value, 100);
+ BOOST_CHECK_EQUAL(alice_vesting.policy.get().vesting_duration_seconds,
+ db.get_global_properties().parameters.gpos_subperiod());
+ BOOST_CHECK_EQUAL(alice_vesting.policy.get().vesting_cliff_seconds,
+ db.get_global_properties().parameters.gpos_subperiod());
+
+ // bob creates a gpos vesting with his custom policy
+ {
+ vesting_balance_create_operation op;
+ op.creator = bob_id;
+ op.owner = bob_id;
+ op.amount = core.amount(200);
+ op.balance_type = vesting_balance_type::gpos;
+ op.policy = cdd_vesting_policy_initializer{ 60*60*24 };
+
+ trx.operations.push_back(op);
+ set_expiration(db, trx);
+ ptx = PUSH_TX(db, trx, ~0);
+ trx.clear();
+ }
+ auto bob_vesting = db.get(ptx.operation_results[0].get());
+
+ generate_block();
+
+ // policy is not the one defined by the user but default
+ BOOST_CHECK_EQUAL(bob_vesting.balance.amount.value, 200);
+ BOOST_CHECK_EQUAL(bob_vesting.policy.get().vesting_duration_seconds,
+ db.get_global_properties().parameters.gpos_subperiod());
+ BOOST_CHECK_EQUAL(bob_vesting.policy.get().vesting_cliff_seconds,
+ db.get_global_properties().parameters.gpos_subperiod());
+
+ }
+ catch (fc::exception& e)
+ {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+BOOST_AUTO_TEST_CASE( dividends )
+{
+ ACTORS((alice)(bob));
+ try
+ {
+ // move to 1 week before hardfork
+ generate_blocks( HARDFORK_GPOS_TIME - fc::days(7) );
+ generate_block();
+
+ const auto& core = asset_id_type()(db);
+
+ // all core coins are in the committee_account
+ BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 1000000000000000);
+
+ // transfer half of the total stake to alice so not all the dividends will go to the committee_account
+ transfer( committee_account, alice_id, core.amount( 500000000000000 ) );
+ generate_block();
+
+ // send some to bob
+ transfer( committee_account, bob_id, core.amount( 1000 ) );
+ generate_block();
+
+ // committee balance
+ BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999999000);
+
+ // alice balance
+ BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000000);
+
+ // bob balance
+ BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000);
+
+ // get core asset object
+ const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL);
+
+ // by default core token pays dividends once per month
+ const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
+ BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 2592000); // 30 days
+
+ // update the payout interval for speed purposes of the test
+ update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day
+
+ generate_block();
+
+ BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 86400); // 1 day now
+
+ // get the dividend distribution account
+ const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
+
+ // transfering some coins to distribution account.
+ // simulating the blockchain haves some dividends to pay.
+ transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) );
+ generate_block();
+
+ // committee balance
+ BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998900 );
+
+ // distribution account balance
+ BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100);
+
+ // get when is the next payout time as we need to advance there
+ auto next_payout_time = dividend_data.options.next_payout_time;
+
+ // advance to next payout
+ generate_blocks(*next_payout_time);
+ wdump((*next_payout_time));
+
+ // advance to next maint after payout time arrives
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // check balances now, dividends are paid "normally"
+ BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998949 );
+ BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000050 );
+ BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 );
+ BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 1);
+
+ // advance to hardfork
+ generate_blocks( HARDFORK_GPOS_TIME );
+
+ // advance to next maint
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // send 99 to the distribution account so it will have 100 PPY again to share
+ transfer( committee_account, dividend_distribution_account.id, core.amount( 99 ) );
+ generate_block();
+
+ // get when is the next payout time as we need to advance there
+ next_payout_time = dividend_data.options.next_payout_time;
+
+ // advance to next payout
+ generate_blocks(*next_payout_time);
+
+ // advance to next maint
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // make sure no dividends were paid "normally"
+ BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998850 );
+ BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000050 );
+ BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 );
+ BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100);
+
+ // create vesting balance
+ create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos);
+
+ // need to vote to get paid
+ auto witness1 = witness_id_type(1)(db);
+ vote_for(bob_id, witness1.vote_id, bob_private_key);
+
+ generate_block();
+
+ // check balances
+ BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 900 );
+ BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100);
+
+ // advance to next payout
+ generate_blocks(*next_payout_time);
+
+ // advance to next maint
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // check balances, dividends paid to bob
+ BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 );
+ BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 0);
+ }
+ catch (fc::exception& e)
+ {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+BOOST_AUTO_TEST_CASE( voting )
+{
+ ACTORS((alice)(bob));
+ try {
+
+ // move to hardfork
+ generate_blocks( HARDFORK_GPOS_TIME );
+ generate_block();
+
+ const auto& core = asset_id_type()(db);
+
+ // send some asset to alice and bob
+ transfer( committee_account, alice_id, core.amount( 1000 ) );
+ transfer( committee_account, bob_id, core.amount( 1000 ) );
+ generate_block();
+
+ // default maintenance_interval is 1 day
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, 86400);
+
+ // add some vesting to alice and bob
+ create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos);
+ create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos);
+ generate_block();
+
+ // default gpos values
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 15552000);
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 2592000);
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), HARDFORK_GPOS_TIME.sec_since_epoch());
+
+ // update default gpos for test speed
+ auto now = db.head_block_time();
+ // 5184000 = 60x60x24x60 = 60 days
+ // 864000 = 60x60x24x10 = 10 days
+ update_gpos_global(5184000, 864000, now);
+
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 5184000);
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 864000);
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
+ // end global changes
+
+ generate_block();
+
+ // no votes for witness 1
+ auto witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 0);
+
+ // no votes for witness 2
+ auto witness2 = witness_id_type(2)(db);
+ BOOST_CHECK_EQUAL(witness2.total_votes, 0);
+
+ // vote for witness1
+ vote_for(alice_id, witness1.vote_id, alice_private_key);
+
+ // go to maint
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // vote is the same as amount in the first subperiod since voting
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 100);
+
+ advance_x_maint(10);
+
+ // vote decay as time pass
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 83);
+
+ advance_x_maint(10);
+
+ // decay more
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 66);
+
+ advance_x_maint(10);
+
+ // more
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 50);
+
+ advance_x_maint(10);
+
+ // more
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 33);
+
+ advance_x_maint(10);
+
+ // more
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 16);
+
+ // we are still in gpos period 1
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
+
+ advance_x_maint(10);
+
+ // until 0
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 0);
+
+ // a new GPOS period is in but vote from user is before the start so his voting power is 0
+ now = db.head_block_time();
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
+
+ generate_block();
+
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 0);
+
+ // we are in the second GPOS period, at subperiod 2, lets vote here
+ vote_for(bob_id, witness2.vote_id, bob_private_key);
+ generate_block();
+
+ // go to maint
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ witness1 = witness_id_type(1)(db);
+ witness2 = witness_id_type(2)(db);
+
+ BOOST_CHECK_EQUAL(witness1.total_votes, 0);
+ BOOST_CHECK_EQUAL(witness2.total_votes, 100);
+
+ advance_x_maint(10);
+
+ witness1 = witness_id_type(1)(db);
+ witness2 = witness_id_type(2)(db);
+
+ BOOST_CHECK_EQUAL(witness1.total_votes, 0);
+ BOOST_CHECK_EQUAL(witness2.total_votes, 83);
+
+ advance_x_maint(10);
+
+ witness1 = witness_id_type(1)(db);
+ witness2 = witness_id_type(2)(db);
+
+ BOOST_CHECK_EQUAL(witness1.total_votes, 0);
+ BOOST_CHECK_EQUAL(witness2.total_votes, 66);
+
+ // alice votes again, now for witness 2, her vote worth 100 now
+ vote_for(alice_id, witness2.vote_id, alice_private_key);
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ witness1 = witness_id_type(1)(db);
+ witness2 = witness_id_type(2)(db);
+
+ BOOST_CHECK_EQUAL(witness1.total_votes, 100);
+ BOOST_CHECK_EQUAL(witness2.total_votes, 166);
+
+ }
+ catch (fc::exception &e) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+BOOST_AUTO_TEST_CASE( rolling_period_start )
+{
+ // period start rolls automatically after HF
+ try {
+ // advance to HF
+ generate_blocks(HARDFORK_GPOS_TIME);
+ generate_block();
+
+ // update default gpos global parameters to make this thing faster
+ auto now = db.head_block_time();
+ update_gpos_global(518400, 86400, now);
+
+ // moving outside period:
+ while( db.head_block_time() <= now + fc::days(6) )
+ {
+ generate_block();
+ }
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // rolling is here so getting the new now
+ now = db.head_block_time();
+ generate_block();
+
+ // period start rolled
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
+ }
+ catch (fc::exception &e) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+BOOST_AUTO_TEST_CASE( worker_dividends_voting )
+{
+ try {
+ // advance to HF
+ generate_blocks(HARDFORK_GPOS_TIME);
+ generate_block();
+
+ // update default gpos global parameters to 4 days
+ auto now = db.head_block_time();
+ update_gpos_global(345600, 86400, now);
+
+ generate_block();
+ set_expiration(db, trx);
+ const auto& core = asset_id_type()(db);
+
+ // get core asset object
+ const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL);
+
+ // by default core token pays dividends once per month
+ const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
+ BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 2592000); // 30 days
+
+ // update the payout interval to 1 day for speed purposes of the test
+ update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day
+
+ generate_block();
+
+ // get the dividend distribution account
+ const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
+
+ // transfering some coins to distribution account.
+ transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) );
+ generate_block();
+
+ ACTORS((nathan)(voter1)(voter2)(voter3));
+
+ transfer( committee_account, nathan_id, core.amount( 1000 ) );
+ transfer( committee_account, voter1_id, core.amount( 1000 ) );
+ transfer( committee_account, voter2_id, core.amount( 1000 ) );
+
+ generate_block();
+
+ upgrade_to_lifetime_member(nathan_id);
+
+ auto worker = create_worker(nathan_id, 10, fc::days(6));
+
+ // add some vesting to voter1
+ create_vesting(voter1_id, core.amount(100), vesting_balance_type::gpos);
+
+ // add some vesting to voter2
+ create_vesting(voter2_id, core.amount(100), vesting_balance_type::gpos);
+
+ generate_block();
+
+ // vote for worker
+ vote_for(voter1_id, worker.vote_for, voter1_private_key);
+
+ // first maint pass, coefficient will be 1
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ worker = worker_id_type()(db);
+ BOOST_CHECK_EQUAL(worker.total_votes_for, 100);
+
+ // here dividends are paid to voter1 and voter2
+ // voter1 get paid full dividend share as coefficent is at 1 here
+ BOOST_CHECK_EQUAL(get_balance(voter1_id(db), core), 950);
+
+ // voter2 didnt voted so he dont get paid
+ BOOST_CHECK_EQUAL(get_balance(voter2_id(db), core), 900);
+
+ // send some asset to the reserve pool so the worker can get paid
+ fill_reserve_pool(account_id_type(), asset(GRAPHENE_MAX_SHARE_SUPPLY/2));
+
+ BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().balance(db).balance.amount.value, 0);
+ BOOST_CHECK_EQUAL(worker.worker.get().balance(db).balance.amount.value, 0);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // worker is getting paid
+ BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().balance(db).balance.amount.value, 10);
+ BOOST_CHECK_EQUAL(worker.worker.get().balance(db).balance.amount.value, 10);
+
+ // second maint pass, coefficient will be 0.75
+ worker = worker_id_type()(db);
+ BOOST_CHECK_EQUAL(worker.total_votes_for, 75);
+
+ // more decay
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ worker = worker_id_type()(db);
+ BOOST_CHECK_EQUAL(worker.total_votes_for, 50);
+
+ transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) );
+ generate_block();
+
+ BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999996850);
+
+ // more decay
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ worker = worker_id_type()(db);
+ BOOST_CHECK_EQUAL(worker.total_votes_for, 25);
+
+ // here voter1 get paid again but less money by vesting coefficient
+ BOOST_CHECK_EQUAL(get_balance(voter1_id(db), core), 962);
+ BOOST_CHECK_EQUAL(get_balance(voter2_id(db), core), 900);
+
+ // remaining dividends not paid by coeffcient are sent to committee account
+ BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999996938);
+ }
+ catch (fc::exception &e) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+BOOST_AUTO_TEST_CASE( account_multiple_vesting )
+{
+ try {
+ // advance to HF
+ generate_blocks(HARDFORK_GPOS_TIME);
+ generate_block();
+ set_expiration(db, trx);
+
+ // update default gpos global parameters to 4 days
+ auto now = db.head_block_time();
+ update_gpos_global(345600, 86400, now);
+
+ ACTORS((sam)(patty));
+
+ const auto& core = asset_id_type()(db);
+
+ transfer( committee_account, sam_id, core.amount( 300 ) );
+ transfer( committee_account, patty_id, core.amount( 100 ) );
+
+ // add some vesting to sam
+ create_vesting(sam_id, core.amount(100), vesting_balance_type::gpos);
+
+ // have another balance with 200 more
+ create_vesting(sam_id, core.amount(200), vesting_balance_type::gpos);
+
+ // patty also have vesting balance
+ create_vesting(patty_id, core.amount(100), vesting_balance_type::gpos);
+
+ // get core asset object
+ const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL);
+ const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
+
+ // update the payout interval
+ update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day
+
+ // get the dividend distribution account
+ const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
+
+ // transfering some coins to distribution account.
+ transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) );
+ generate_block();
+
+ // vote for a votable object
+ auto witness1 = witness_id_type(1)(db);
+ vote_for(sam_id, witness1.vote_id, sam_private_key);
+ vote_for(patty_id, witness1.vote_id, patty_private_key);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // amount in vested balanced will sum up as voting power
+ witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 400);
+
+ // sam get paid dividends
+ BOOST_CHECK_EQUAL(get_balance(sam_id(db), core), 75);
+
+ // patty also
+ BOOST_CHECK_EQUAL(get_balance(patty_id(db), core), 25);
+
+ // total vote not decaying
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ witness1 = witness_id_type(1)(db);
+
+ BOOST_CHECK_EQUAL(witness1.total_votes, 300);
+ }
+ catch (fc::exception &e) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+/*
+BOOST_AUTO_TEST_CASE( competing_proposals )
+{
+ try {
+ // advance to HF
+ generate_blocks(HARDFORK_GPOS_TIME);
+ generate_block();
+ set_expiration(db, trx);
+
+ ACTORS((voter1)(voter2)(worker1)(worker2));
+
+ const auto& core = asset_id_type()(db);
+
+ transfer( committee_account, worker1_id, core.amount( 1000 ) );
+ transfer( committee_account, worker2_id, core.amount( 1000 ) );
+ transfer( committee_account, voter1_id, core.amount( 1000 ) );
+ transfer( committee_account, voter2_id, core.amount( 1000 ) );
+
+ create_vesting(voter1_id, core.amount(200), vesting_balance_type::gpos);
+ create_vesting(voter2_id, core.amount(300), vesting_balance_type::gpos);
+
+ generate_block();
+
+ auto now = db.head_block_time();
+ update_gpos_global(518400, 86400, now);
+
+ update_payout_interval(core.symbol, fc::time_point::now() + fc::minutes(1), 60 * 60 * 24); // 1 day
+
+ upgrade_to_lifetime_member(worker1_id);
+ upgrade_to_lifetime_member(worker2_id);
+
+ // create 2 competing proposals asking a lot of token
+ // todo: maybe a refund worker here so we can test with smaller numbers
+ auto w1 = create_worker(worker1_id, 100000000000, fc::days(10));
+ auto w1_id_instance = w1.id.instance();
+ auto w2 = create_worker(worker2_id, 100000000000, fc::days(10));
+ auto w2_id_instance = w2.id.instance();
+
+ fill_reserve_pool(account_id_type(), asset(GRAPHENE_MAX_SHARE_SUPPLY/2));
+
+ // vote for the 2 workers
+ vote_for(voter1_id, w1.vote_for, voter1_private_key);
+ vote_for(voter2_id, w2.vote_for, voter2_private_key);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ w1 = worker_id_type(w1_id_instance)(db);
+ w2 = worker_id_type(w2_id_instance)(db);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ // only w2 is getting paid as it haves more votes and money is only enough for 1
+ BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0);
+ BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 100000000000);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0);
+ BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 150000000000);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ w1 = worker_id_type(w1_id_instance)(db);
+ w2 = worker_id_type(w2_id_instance)(db);
+
+ // as votes decay w1 is still getting paid as it always have more votes than w1
+ BOOST_CHECK_EQUAL(w1.total_votes_for, 100);
+ BOOST_CHECK_EQUAL(w2.total_votes_for, 150);
+
+ BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0);
+ BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 200000000000);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ w1 = worker_id_type(w1_id_instance)(db);
+ w2 = worker_id_type(w2_id_instance)(db);
+
+ BOOST_CHECK_EQUAL(w1.total_votes_for, 66);
+ BOOST_CHECK_EQUAL(w2.total_votes_for, 100);
+
+ // worker is sil getting paid as days pass
+ BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0);
+ BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 250000000000);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ w1 = worker_id_type(w1_id_instance)(db);
+ w2 = worker_id_type(w2_id_instance)(db);
+
+ BOOST_CHECK_EQUAL(w1.total_votes_for, 33);
+ BOOST_CHECK_EQUAL(w2.total_votes_for, 50);
+
+ BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0);
+ BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 300000000000);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ w1 = worker_id_type(w1_id_instance)(db);
+ w2 = worker_id_type(w2_id_instance)(db);
+
+ // worker2 will not get paid anymore as it haves 0 votes
+ BOOST_CHECK_EQUAL(w1.total_votes_for, 0);
+ BOOST_CHECK_EQUAL(w2.total_votes_for, 0);
+
+ BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0);
+ BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 300000000000);
+ }
+ catch (fc::exception &e) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+*/
+BOOST_AUTO_TEST_CASE( proxy_voting )
+{
+ try {
+
+ }
+ catch (fc::exception &e) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+BOOST_AUTO_TEST_CASE( no_proposal )
+{
+ try {
+
+ }
+ catch (fc::exception &e) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+BOOST_AUTO_TEST_CASE( database_api )
+{
+ ACTORS((alice)(bob));
+ try {
+
+ // move to hardfork
+ generate_blocks( HARDFORK_GPOS_TIME );
+ generate_block();
+
+ // database api
+ graphene::app::database_api db_api(db);
+
+ const auto& core = asset_id_type()(db);
+
+ // send some asset to alice and bob
+ transfer( committee_account, alice_id, core.amount( 1000 ) );
+ transfer( committee_account, bob_id, core.amount( 1000 ) );
+ generate_block();
+
+ // add some vesting to alice and bob
+ create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos);
+ generate_block();
+
+ // total balance is 100 rest of data at 0
+ auto gpos_info = db_api.get_gpos_info(alice_id);
+ BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0);
+ BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0);
+ BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 100);
+
+ create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos);
+ generate_block();
+
+ // total gpos balance is now 200
+ gpos_info = db_api.get_gpos_info(alice_id);
+ BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200);
+
+ // update default gpos and dividend interval to 10 days
+ auto now = db.head_block_time();
+ update_gpos_global(5184000, 864000, now); // 10 days subperiods
+ update_payout_interval(core.symbol, now + fc::minutes(1), 60 * 60 * 24 * 10); // 10 days
+
+ generate_block();
+
+ // no votes for witness 1
+ auto witness1 = witness_id_type(1)(db);
+ BOOST_CHECK_EQUAL(witness1.total_votes, 0);
+
+ // no votes for witness 2
+ auto witness2 = witness_id_type(2)(db);
+ BOOST_CHECK_EQUAL(witness2.total_votes, 0);
+
+ // transfering some coins to distribution account.
+ const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL);
+ const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
+ const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
+ transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) );
+ generate_block();
+
+ // award balance is now 100
+ gpos_info = db_api.get_gpos_info(alice_id);
+ BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0);
+ BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 100);
+ BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200);
+
+ // vote for witness1
+ vote_for(alice_id, witness1.vote_id, alice_private_key);
+ vote_for(bob_id, witness1.vote_id, bob_private_key);
+
+ // go to maint
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // payment for alice and bob is done, distribution account is back in 0
+ gpos_info = db_api.get_gpos_info(alice_id);
+ BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 1);
+ BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0);
+ BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200);
+
+ advance_x_maint(10);
+
+ // alice vesting coeffcient decay
+ gpos_info = db_api.get_gpos_info(alice_id);
+ BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0.83333333333333337);
+ BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0);
+ BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200);
+
+ advance_x_maint(10);
+
+ // vesting factor for alice decaying more
+ gpos_info = db_api.get_gpos_info(alice_id);
+ BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0.66666666666666663);
+ BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0);
+ BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200);
+ }
+ catch (fc::exception &e) {
+ edump((e.to_detail_string()));
+ throw;
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp
index 443cd011..3093f0b0 100644
--- a/tests/tests/operation_tests.cpp
+++ b/tests/tests/operation_tests.cpp
@@ -1561,7 +1561,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test )
op.amount = test_asset.amount( 100 );
//op.vesting_seconds = 60*60*24;
op.policy = cdd_vesting_policy_initializer{ 60*60*24 };
- //op.balance_type == vesting_balance_type::unspecified;
+ op.balance_type == vesting_balance_type::normal;
// Fee must be non-negative
REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(1) );
@@ -1581,7 +1581,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test )
op.creator = alice_account.get_id();
op.owner = alice_account.get_id();
- //op.balance_type = vesting_balance_type::unspecified;
+ op.balance_type = vesting_balance_type::normal;
account_id_type nobody = account_id_type(1234);
@@ -1652,7 +1652,8 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test )
create_op.owner = owner;
create_op.amount = amount;
create_op.policy = cdd_vesting_policy_initializer(vesting_seconds);
- //create_op.balance_type = vesting_balance_type::unspecified;
+ create_op.balance_type = vesting_balance_type::normal;
+
tx.operations.push_back( create_op );
set_expiration( db, tx );
diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp
index b68b34a7..2e175c9d 100644
--- a/tests/tests/operation_tests2.cpp
+++ b/tests/tests/operation_tests2.cpp
@@ -1316,7 +1316,7 @@ BOOST_AUTO_TEST_CASE(zero_second_vbo)
create_op.owner = alice_id;
create_op.amount = asset(500);
create_op.policy = pinit;
- //create_op.balance_type = vesting_balance_type::unspecified;
+ create_op.balance_type = vesting_balance_type::normal;
signed_transaction create_tx;
create_tx.operations.push_back( create_op );
@@ -1400,7 +1400,7 @@ BOOST_AUTO_TEST_CASE( vbo_withdraw_different )
create_op.owner = alice_id;
create_op.amount = asset(100, stuff_id);
create_op.policy = pinit;
- //create_op.balance_type = vesting_balance_type::unspecified;
+ create_op.balance_type = vesting_balance_type::normal;
signed_transaction create_tx;
create_tx.operations.push_back( create_op );
From c025f639d7e9f3479438dde8be217f762ec6ac67 Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Sat, 19 Oct 2019 10:58:56 +0530
Subject: [PATCH 017/151] Fixed few error messages
---
libraries/chain/vesting_balance_evaluator.cpp | 2 +-
.../wallet/include/graphene/wallet/wallet.hpp | 7 +++
libraries/wallet/wallet.cpp | 60 ++++++++++++++++---
3 files changed, 61 insertions(+), 8 deletions(-)
diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp
index 22cf544f..9630d011 100644
--- a/libraries/chain/vesting_balance_evaluator.cpp
+++ b/libraries/chain/vesting_balance_evaluator.cpp
@@ -145,7 +145,7 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan
const vesting_balance_object& vbo = op.vesting_balance( d );
FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) );
- FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "", ("now", now)("op", op)("vbo", vbo) );
+ FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "GPOS Vested Balance cannont be withdrwan during the locking period", ("now", now)("op", op)("vbo", vbo) );
assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached
/* const account_object& owner_account = op.owner( d ); */
diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp
index 8a15fec0..fed7d55d 100644
--- a/libraries/wallet/include/graphene/wallet/wallet.hpp
+++ b/libraries/wallet/include/graphene/wallet/wallet.hpp
@@ -1265,6 +1265,12 @@ class wallet_api
*/
witness_object get_witness(string owner_account);
+ /** Returns true if the account is witness, false otherwise
+ * @param owner_account the name or id of the witness account owner, or the id of the witness
+ * @returns true if account is witness, false otherwise
+ */
+ bool is_witness(string owner_account);
+
/** Returns information about the given committee_member.
* @param owner_account the name or id of the committee_member account owner, or the id of the committee_member
* @returns the information about the committee_member stored in the block chain
@@ -1969,6 +1975,7 @@ FC_API( graphene::wallet::wallet_api,
(whitelist_account)
(create_committee_member)
(get_witness)
+ (is_witness)
(get_committee_member)
(list_witnesses)
(list_committee_members)
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index b6aa2cbf..75c47d8a 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -1720,6 +1720,42 @@ public:
FC_CAPTURE_AND_RETHROW( (owner_account) )
}
+ bool is_witness(string owner_account)
+ {
+ try
+ {
+ fc::optional witness_id = maybe_id(owner_account);
+ if (witness_id)
+ {
+ std::vector ids_to_get;
+ ids_to_get.push_back(*witness_id);
+ std::vector> witness_objects = _remote_db->get_witnesses(ids_to_get);
+ if (witness_objects.front())
+ return true;
+ else
+ return false;
+ }
+ else
+ {
+ // then maybe it's the owner account
+ try
+ {
+ account_id_type owner_account_id = get_account_id(owner_account);
+ fc::optional witness = _remote_db->get_witness_by_account(owner_account_id);
+ if (witness)
+ return true;
+ else
+ return false;
+ }
+ catch (const fc::exception&)
+ {
+ return false;
+ }
+ }
+ }
+ FC_CAPTURE_AND_RETHROW( (owner_account) )
+ }
+
committee_member_object get_committee_member(string owner_account)
{
try
@@ -2013,9 +2049,14 @@ public:
vbos = _remote_db->get_vesting_balances( *acct_id );
if( vbos.size() == 0 )
{
- witness_object wit = get_witness( account_name );
- FC_ASSERT( wit.pay_vb );
- vbid = wit.pay_vb;
+ if (is_witness(account_name))
+ {
+ witness_object wit = get_witness( account_name );
+ FC_ASSERT( wit.pay_vb );
+ vbid = wit.pay_vb;
+ }
+ else
+ FC_THROW("Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", account_name));
}
}
@@ -2080,7 +2121,7 @@ public:
vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;});
if( vbo_info.size() == 0 || vbo_iter == vbo_info.end())
- FC_THROW("Account *** ${account} *** have insufficient or 0 vested balance(GPOS) to vote", ("account", voting_account));
+ FC_THROW("Account ${account} has no core Token vested and thus she will not be allowed to vote for the committee member", ("account", voting_account));
account_object voting_account_object = get_account(voting_account);
account_id_type committee_member_owner_account_id = get_account_id(committee_member);
@@ -2121,7 +2162,7 @@ public:
vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;});
if( vbo_info.size() == 0 || vbo_iter == vbo_info.end())
- FC_THROW("Account *** ${account} *** have insufficient or 0 vested balance(GPOS) to vote", ("account", voting_account));
+ FC_THROW("Account ${account} has no core Token vested and thus she will not be allowed to vote for the witness", ("account", voting_account));
account_object voting_account_object = get_account(voting_account);
account_id_type witness_owner_account_id = get_account_id(witness);
@@ -2132,13 +2173,13 @@ public:
{
auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id);
if (!insert_result.second)
- FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness));
+ FC_THROW("Account ${account} has already voted for witness ${witness}", ("account", voting_account)("witness", witness));
}
else
{
unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id);
if (!votes_removed)
- FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness));
+ FC_THROW("Account ${account} has not voted for witness ${witness}", ("account", voting_account)("witness", witness));
}
account_update_operation account_update_op;
account_update_op.account = voting_account_object.id;
@@ -4073,6 +4114,11 @@ witness_object wallet_api::get_witness(string owner_account)
return my->get_witness(owner_account);
}
+bool wallet_api::is_witness(string owner_account)
+{
+ return my->is_witness(owner_account);
+}
+
committee_member_object wallet_api::get_committee_member(string owner_account)
{
return my->get_committee_member(owner_account);
From 0800e2bc6710cd67394943812fbddf29e88cee91 Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Sat, 19 Oct 2019 16:38:12 +0530
Subject: [PATCH 018/151] error message corrections at other places
---
.../include/graphene/chain/protocol/vesting.hpp | 10 ++++++++++
libraries/chain/vesting_balance_evaluator.cpp | 3 ++-
libraries/wallet/wallet.cpp | 13 +++++++++----
3 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp
index ac995aaf..2a861b2a 100644
--- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp
+++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp
@@ -28,6 +28,16 @@ namespace graphene { namespace chain {
enum class vesting_balance_type { normal, gpos };
+ inline std::string get_vesting_balance_type(vesting_balance_type type) {
+ switch (type) {
+ case vesting_balance_type::normal:
+ return "NORMAL";
+ case vesting_balance_type::gpos:
+ default:
+ return "GPOS";
+ }
+ }
+
struct linear_vesting_policy_initializer
{
/** while vesting begins on begin_timestamp, none may be claimed before vesting_cliff_seconds have passed */
diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp
index 9630d011..9f42d4ff 100644
--- a/libraries/chain/vesting_balance_evaluator.cpp
+++ b/libraries/chain/vesting_balance_evaluator.cpp
@@ -145,7 +145,8 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan
const vesting_balance_object& vbo = op.vesting_balance( d );
FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) );
- FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "GPOS Vested Balance cannont be withdrwan during the locking period", ("now", now)("op", op)("vbo", vbo) );
+ FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "${balance_type} Vested Balance cannont be withdrwan during the locking period",
+ ("balance_type", get_vesting_balance_type(vbo.balance_type))("now", now)("op", op)("vbo", vbo) );
assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached
/* const account_object& owner_account = op.owner( d ); */
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index 75c47d8a..c7e605cc 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -2005,9 +2005,14 @@ public:
fc::optional vbid = maybe_id(witness_name);
if( !vbid )
{
- witness_object wit = get_witness( witness_name );
- FC_ASSERT( wit.pay_vb );
- vbid = wit.pay_vb;
+ if (is_witness(witness_name))
+ {
+ witness_object wit = get_witness( witness_name );
+ FC_ASSERT( wit.pay_vb, "Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", witness_name));
+ vbid = wit.pay_vb;
+ }
+ else
+ FC_THROW("Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", witness_name));
}
vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid );
@@ -2052,7 +2057,7 @@ public:
if (is_witness(account_name))
{
witness_object wit = get_witness( account_name );
- FC_ASSERT( wit.pay_vb );
+ FC_ASSERT( wit.pay_vb, "Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", account_name));
vbid = wit.pay_vb;
}
else
From f7c592dd0ed00692b9d48624e980ef389aa7faac Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Mon, 21 Oct 2019 23:24:20 +0530
Subject: [PATCH 019/151] Updated FC repository to
peerplays-network/peerplays-fc (#189)
Point to fc commit hash 6096e94 [latest-fc branch]
---
.gitmodules | 2 +-
libraries/fc | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.gitmodules b/.gitmodules
index 4a2c72e0..b3b4d866 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,5 +4,5 @@
ignore = dirty
[submodule "libraries/fc"]
path = libraries/fc
- url = https://github.com/PBSA/peerplays-fc.git
+ url = https://github.com/peerplays-network/peerplays-fc.git
ignore = dirty
diff --git a/libraries/fc b/libraries/fc
index f13d0632..6096e94e 160000
--- a/libraries/fc
+++ b/libraries/fc
@@ -1 +1 @@
-Subproject commit f13d0632b08b9983a275304317a033914938e339
+Subproject commit 6096e94e1b4c48a393c9335580365df144f2758f
From d2a6f6d319e844bc6c3b09d7e8bda971eeae8d2a Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Tue, 22 Oct 2019 18:22:23 +0530
Subject: [PATCH 020/151] Project name update in Doxyfile (#146)
---
Doxyfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doxyfile b/Doxyfile
index 75931ef9..18bb33e2 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
-PROJECT_NAME = "Graphene"
+PROJECT_NAME = "Peerplays"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
From 0d1c41557d54029ef2d49862d9a6a441cdd4ddec Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Tue, 22 Oct 2019 10:39:45 -0300
Subject: [PATCH 021/151] changes to allow user to vote in each sub-period
---
libraries/app/database_api.cpp | 2 +
.../app/include/graphene/app/database_api.hpp | 5 ++-
libraries/chain/account_evaluator.cpp | 4 +-
libraries/chain/db_maint.cpp | 38 +++++++++++++++----
.../chain/include/graphene/chain/database.hpp | 1 +
libraries/wallet/wallet.cpp | 9 ++++-
6 files changed, 47 insertions(+), 12 deletions(-)
diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp
index e3e82790..e27da19b 100644
--- a/libraries/app/database_api.cpp
+++ b/libraries/app/database_api.cpp
@@ -2039,6 +2039,8 @@ graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type
{
gpos_info result;
result.vesting_factor = _db.calculate_vesting_factor(account(_db));
+ result.current_subperiod = _db.get_gpos_current_subperiod();
+ result.last_voted_time = account(_db).statistics(_db).last_vote_time;
const auto& dividend_data = asset_id_type()(_db).dividend_data(_db);
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(_db);
diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp
index 3fac4b5f..7d9ffde8 100644
--- a/libraries/app/include/graphene/app/database_api.hpp
+++ b/libraries/app/include/graphene/app/database_api.hpp
@@ -118,6 +118,9 @@ struct gpos_info {
double vesting_factor;
asset award;
share_type total_amount;
+ uint32_t current_subperiod;
+ fc::time_point_sec last_voted_time;
+
};
/**
@@ -672,7 +675,7 @@ FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) );
FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_trade, (date)(price)(amount)(value) );
-FC_REFLECT( graphene::app::gpos_info, (vesting_factor)(award)(total_amount) );
+FC_REFLECT( graphene::app::gpos_info, (vesting_factor)(award)(total_amount)(current_subperiod)(last_voted_time) );
FC_API(graphene::app::database_api,
diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp
index 2d117f52..b29c169c 100644
--- a/libraries/chain/account_evaluator.cpp
+++ b/libraries/chain/account_evaluator.cpp
@@ -284,8 +284,8 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
{
d.modify( acnt->statistics( d ), [&]( account_statistics_object& aso )
{
- if((o.new_options->votes != acnt->options.votes ||
- o.new_options->voting_account != acnt->options.voting_account))
+ //if((o.new_options->votes != acnt->options.votes ||
+ // o.new_options->voting_account != acnt->options.voting_account))
aso.last_vote_time = d.head_block_time();
} );
}
diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp
index 81fce8f9..ade8c160 100644
--- a/libraries/chain/db_maint.cpp
+++ b/libraries/chain/db_maint.cpp
@@ -725,13 +725,8 @@ void deprecate_annual_members( database& db )
return;
}
-double database::calculate_vesting_factor(const account_object& stake_account)
+uint32_t database::get_gpos_current_subperiod()
{
- // get last time voted form stats
- const auto &stats = stake_account.statistics(*this);
- fc::time_point_sec last_date_voted = stats.last_vote_time;
-
- // get global data related to gpos
const auto &gpo = this->get_global_properties();
const auto vesting_period = gpo.parameters.gpos_period();
const auto vesting_subperiod = gpo.parameters.gpos_subperiod();
@@ -741,7 +736,6 @@ double database::calculate_vesting_factor(const account_object& stake_account)
const fc::time_point_sec period_end = period_start + vesting_period;
const auto number_of_subperiods = vesting_period / vesting_subperiod;
const auto now = this->head_block_time();
- double vesting_factor;
auto seconds_since_period_start = now.sec_since_epoch() - period_start.sec_since_epoch();
FC_ASSERT(period_start <= now && now <= period_end);
@@ -757,6 +751,28 @@ double database::calculate_vesting_factor(const account_object& stake_account)
current_subperiod = period;
});
+ return current_subperiod;
+}
+
+double database::calculate_vesting_factor(const account_object& stake_account)
+{
+ // get last time voted form stats
+ const auto &stats = stake_account.statistics(*this);
+ fc::time_point_sec last_date_voted = stats.last_vote_time;
+
+ // get global data related to gpos
+ const auto &gpo = this->get_global_properties();
+ const auto vesting_period = gpo.parameters.gpos_period();
+ const auto vesting_subperiod = gpo.parameters.gpos_subperiod();
+ const auto period_start = fc::time_point_sec(gpo.parameters.gpos_period_start());
+
+ // variables needed
+ const auto number_of_subperiods = vesting_period / vesting_subperiod;
+ double vesting_factor;
+
+ // get in what sub period we are
+ uint32_t current_subperiod = get_gpos_current_subperiod();
+
if(current_subperiod == 0 || current_subperiod > number_of_subperiods) return 0;
if(last_date_voted < period_start) return 0;
@@ -1389,7 +1405,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
rolling_period_start(*this);
process_dividend_assets(*this);
-
+
struct vote_tally_helper {
database& d;
const global_property_object& props;
@@ -1558,6 +1574,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
p.pending_parameters->extensions.value.permitted_betting_odds_increments = p.parameters.extensions.value.permitted_betting_odds_increments;
if( !p.pending_parameters->extensions.value.live_betting_delay_time.valid() )
p.pending_parameters->extensions.value.live_betting_delay_time = p.parameters.extensions.value.live_betting_delay_time;
+ if( !p.pending_parameters->extensions.value.gpos_period.valid() )
+ p.pending_parameters->extensions.value.gpos_period = p.parameters.extensions.value.gpos_period;
+ if( !p.pending_parameters->extensions.value.gpos_subperiod.valid() )
+ p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod;
+ if( !p.pending_parameters->extensions.value.gpos_vesting_lockin_period.valid() )
+ p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period;
p.parameters = std::move(*p.pending_parameters);
p.pending_parameters.reset();
}
diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp
index 179fb2df..a181fe58 100644
--- a/libraries/chain/include/graphene/chain/database.hpp
+++ b/libraries/chain/include/graphene/chain/database.hpp
@@ -500,6 +500,7 @@ namespace graphene { namespace chain {
void update_worker_votes();
public:
double calculate_vesting_factor(const account_object& stake_account);
+ uint32_t get_gpos_current_subperiod();
template
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index b6aa2cbf..5b867c47 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -2125,13 +2125,20 @@ public:
account_object voting_account_object = get_account(voting_account);
account_id_type witness_owner_account_id = get_account_id(witness);
+
fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
if (!witness_obj)
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
if (approve)
{
+ account_id_type stake_account = get_account_id(voting_account);
+ const auto gpos_info = _remote_db->get_gpos_info(stake_account);
+ const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod();
+ const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start());
+ const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod;
+
auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id);
- if (!insert_result.second)
+ if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time))
FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness));
}
else
From 73829bd97f070f8264ab9a98a8a68a3a21be1ec4 Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Wed, 23 Oct 2019 11:56:38 +0530
Subject: [PATCH 022/151] Fixed GPOS vesting factor issue when proxy is set
---
libraries/chain/db_maint.cpp | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp
index 81fce8f9..182c04fc 100644
--- a/libraries/chain/db_maint.cpp
+++ b/libraries/chain/db_maint.cpp
@@ -727,9 +727,13 @@ void deprecate_annual_members( database& db )
double database::calculate_vesting_factor(const account_object& stake_account)
{
- // get last time voted form stats
- const auto &stats = stake_account.statistics(*this);
- fc::time_point_sec last_date_voted = stats.last_vote_time;
+ fc::time_point_sec last_date_voted;
+ // get last time voted form account stats
+ // check last_vote_time of proxy voting account if proxy is set
+ if (stake_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT)
+ last_date_voted = stake_account.statistics(*this).last_vote_time;
+ else
+ last_date_voted = stake_account.options.voting_account(*this).statistics(*this).last_vote_time;
// get global data related to gpos
const auto &gpo = this->get_global_properties();
From ccdea033f398b7de760e08d9b220a54fb50f9456 Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Wed, 23 Oct 2019 17:41:16 +0530
Subject: [PATCH 023/151] Added unit test for proxy voting
---
tests/tests/gpos_tests.cpp | 87 +++++++++++++++++++++++++++++++++++++-
1 file changed, 86 insertions(+), 1 deletion(-)
diff --git a/tests/tests/gpos_tests.cpp b/tests/tests/gpos_tests.cpp
index 11104409..81f56500 100644
--- a/tests/tests/gpos_tests.cpp
+++ b/tests/tests/gpos_tests.cpp
@@ -832,8 +832,94 @@ BOOST_AUTO_TEST_CASE( competing_proposals )
*/
BOOST_AUTO_TEST_CASE( proxy_voting )
{
+ ACTORS((alice)(bob));
try {
+ // move to hardfork
+ generate_blocks( HARDFORK_GPOS_TIME );
+ generate_block();
+
+ // database api
+ graphene::app::database_api db_api(db);
+
+ const auto& core = asset_id_type()(db);
+
+ // send some asset to alice and bob
+ transfer( committee_account, alice_id, core.amount( 1000 ) );
+ transfer( committee_account, bob_id, core.amount( 1000 ) );
+ generate_block();
+
+ // add some vesting to alice and bob
+ create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos);
+ generate_block();
+
+ // total balance is 100 rest of data at 0
+ auto gpos_info = db_api.get_gpos_info(alice_id);
+ BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0);
+ BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0);
+ BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 100);
+
+ create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos);
+ generate_block();
+
+ gpos_info = db_api.get_gpos_info(bob_id);
+ BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0);
+ BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0);
+ BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200);
+
+ auto now = db.head_block_time();
+ update_gpos_global(518400, 86400, now);
+
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 518400);
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 86400);
+ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
+
+ // alice assign bob as voting account
+ graphene::chain::account_update_operation op;
+ op.account = alice_id;
+ op.new_options = alice_id(db).options;
+ op.new_options->voting_account = bob_id;
+ trx.operations.push_back(op);
+ set_expiration(db, trx);
+ trx.validate();
+ sign(trx, alice_private_key);
+ PUSH_TX( db, trx, ~0 );
+ trx.clear();
+
+ generate_block();
+
+ // vote for witness1
+ auto witness1 = witness_id_type(1)(db);
+ vote_for(bob_id, witness1.vote_id, bob_private_key);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+
+ // check vesting factor of current subperiod
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 1);
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 1);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ // GPOS 2nd subperiod started.
+ // vesting factor decay
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337);
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337);
+
+ // vote for witness2
+ auto witness2 = witness_id_type(2)(db);
+ vote_for(bob_id, witness2.vote_id, bob_private_key);
+
+ // vesting factor should be 1 for both alice and bob for the current subperiod
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 1);
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 1);
+
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ // vesting factor decay
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337);
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337);
}
catch (fc::exception &e) {
edump((e.to_detail_string()));
@@ -949,5 +1035,4 @@ BOOST_AUTO_TEST_CASE( database_api )
throw;
}
}
-
BOOST_AUTO_TEST_SUITE_END()
From 8bbab4c113aa77164a8382bd025eb4b0de748acd Mon Sep 17 00:00:00 2001
From: Sandip Patel
Date: Wed, 23 Oct 2019 18:25:33 +0530
Subject: [PATCH 024/151] Review changes
---
tests/tests/gpos_tests.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tests/tests/gpos_tests.cpp b/tests/tests/gpos_tests.cpp
index 81f56500..5b089685 100644
--- a/tests/tests/gpos_tests.cpp
+++ b/tests/tests/gpos_tests.cpp
@@ -906,6 +906,14 @@ BOOST_AUTO_TEST_CASE( proxy_voting )
BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337);
BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337);
+ generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
+ generate_block();
+
+ // GPOS 3rd subperiod started
+ // vesting factor decay
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.66666666666666663);
+ BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.66666666666666663);
+
// vote for witness2
auto witness2 = witness_id_type(2)(db);
vote_for(bob_id, witness2.vote_id, bob_private_key);
From 48d0d88ff048e203a82bb9e2f6e736fe785e4ca0 Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Wed, 23 Oct 2019 18:33:17 -0300
Subject: [PATCH 025/151] changes to update last voting time
---
.../app/include/graphene/app/database_api.hpp | 3 +--
libraries/chain/account_evaluator.cpp | 6 +++--
.../graphene/chain/protocol/account.hpp | 1 +
libraries/wallet/wallet.cpp | 24 ++++++++++++++++---
4 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp
index 7d9ffde8..843d6af5 100644
--- a/libraries/app/include/graphene/app/database_api.hpp
+++ b/libraries/app/include/graphene/app/database_api.hpp
@@ -119,8 +119,7 @@ struct gpos_info {
asset award;
share_type total_amount;
uint32_t current_subperiod;
- fc::time_point_sec last_voted_time;
-
+ fc::time_point_sec last_voted_time;
};
/**
diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp
index b29c169c..3185c456 100644
--- a/libraries/chain/account_evaluator.cpp
+++ b/libraries/chain/account_evaluator.cpp
@@ -284,8 +284,10 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
{
d.modify( acnt->statistics( d ), [&]( account_statistics_object& aso )
{
- //if((o.new_options->votes != acnt->options.votes ||
- // o.new_options->voting_account != acnt->options.voting_account))
+ fc::optional< bool > flag = o.extensions.value.update_last_voting_time;
+ if((o.new_options->votes != acnt->options.votes ||
+ o.new_options->voting_account != acnt->options.voting_account) ||
+ flag)
aso.last_vote_time = d.head_block_time();
} );
}
diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp
index 6d13a4d3..a0e43ad0 100644
--- a/libraries/chain/include/graphene/chain/protocol/account.hpp
+++ b/libraries/chain/include/graphene/chain/protocol/account.hpp
@@ -140,6 +140,7 @@ namespace graphene { namespace chain {
optional< void_t > null_ext;
optional< special_authority > owner_special_authority;
optional< special_authority > active_special_authority;
+ optional< bool > update_last_voting_time = false;
};
struct fee_parameters_type
diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp
index 5b867c47..5bc53bdc 100644
--- a/libraries/wallet/wallet.cpp
+++ b/libraries/wallet/wallet.cpp
@@ -2087,11 +2087,22 @@ public:
fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member_owner_account_id);
if (!committee_member_obj)
FC_THROW("Account ${committee_member} is not registered as a committee_member", ("committee_member", committee_member));
+
+ bool update_vote_time = false;
+
if (approve)
{
+ account_id_type stake_account = get_account_id(voting_account);
+ const auto gpos_info = _remote_db->get_gpos_info(stake_account);
+ const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod();
+ const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start());
+ const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod;
+
auto insert_result = voting_account_object.options.votes.insert(committee_member_obj->vote_id);
- if (!insert_result.second)
- FC_THROW("Account ${account} was already voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member));
+ if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time))
+ FC_THROW("Account ${account} was already voting for committee_member ${committee_member} in the current GPOS sub-period", ("account", voting_account)("committee_member", committee_member));
+ else
+ update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF)
}
else
{
@@ -2102,6 +2113,7 @@ public:
account_update_operation account_update_op;
account_update_op.account = voting_account_object.id;
account_update_op.new_options = voting_account_object.options;
+ account_update_op.extensions.value.update_last_voting_time = update_vote_time;
signed_transaction tx;
tx.operations.push_back( account_update_op );
@@ -2129,6 +2141,8 @@ public:
fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
if (!witness_obj)
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
+
+ bool update_vote_time = false;
if (approve)
{
account_id_type stake_account = get_account_id(voting_account);
@@ -2139,7 +2153,9 @@ public:
auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id);
if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time))
- FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness));
+ FC_THROW("Account ${account} was already voting for witness ${witness} in the current GPOS sub-period", ("account", voting_account)("witness", witness));
+ else
+ update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF)
}
else
{
@@ -2147,9 +2163,11 @@ public:
if (!votes_removed)
FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness));
}
+
account_update_operation account_update_op;
account_update_op.account = voting_account_object.id;
account_update_op.new_options = voting_account_object.options;
+ account_update_op.extensions.value.update_last_voting_time = update_vote_time;
signed_transaction tx;
tx.operations.push_back( account_update_op );
From d6da2963dcb2267ce7a924a6a876f56180b1b71f Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Wed, 23 Oct 2019 22:15:26 -0300
Subject: [PATCH 026/151] resolve merge conflict
---
libraries/chain/db_maint.cpp | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp
index 7eab5cf8..aee9d451 100644
--- a/libraries/chain/db_maint.cpp
+++ b/libraries/chain/db_maint.cpp
@@ -728,12 +728,6 @@ void deprecate_annual_members( database& db )
uint32_t database::get_gpos_current_subperiod()
{
fc::time_point_sec last_date_voted;
- // get last time voted form account stats
- // check last_vote_time of proxy voting account if proxy is set
- if (stake_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT)
- last_date_voted = stake_account.statistics(*this).last_vote_time;
- else
- last_date_voted = stake_account.options.voting_account(*this).statistics(*this).last_vote_time;
const auto &gpo = this->get_global_properties();
const auto vesting_period = gpo.parameters.gpos_period();
@@ -764,9 +758,13 @@ uint32_t database::get_gpos_current_subperiod()
double database::calculate_vesting_factor(const account_object& stake_account)
{
- // get last time voted form stats
- const auto &stats = stake_account.statistics(*this);
- fc::time_point_sec last_date_voted = stats.last_vote_time;
+ fc::time_point_sec last_date_voted;
+ // get last time voted form account stats
+ // check last_vote_time of proxy voting account if proxy is set
+ if (stake_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT)
+ last_date_voted = stake_account.statistics(*this).last_vote_time;
+ else
+ last_date_voted = stake_account.options.voting_account(*this).statistics(*this).last_vote_time;
// get global data related to gpos
const auto &gpo = this->get_global_properties();
From cf3b54ece47773c4daa407b9ed51128fb2800a3a Mon Sep 17 00:00:00 2001
From: pbattu123
Date: Wed, 23 Oct 2019 23:13:23 -0300
Subject: [PATCH 027/151] unit test changes and also separated GPOS test suite
---
tests/CMakeLists.txt | 4 +
tests/gpos/gpos_tests.cpp | 1093 +++++++++++++++++++++++++++++++++++++
2 files changed, 1097 insertions(+)
create mode 100644 tests/gpos/gpos_tests.cpp
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 57a451aa..55f369f4 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -13,6 +13,10 @@ if(MSVC)
set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
+file(GLOB GPOS_TESTS "gpos/*.cpp")
+add_executable( gpos_test ${GPOS_TESTS} ${COMMON_SOURCES} )
+target_link_libraries( gpos_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
+
file(GLOB PERFORMANCE_TESTS "performance/*.cpp")
add_executable( performance_test ${PERFORMANCE_TESTS} ${COMMON_SOURCES} )
target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
diff --git a/tests/gpos/gpos_tests.cpp b/tests/gpos/gpos_tests.cpp
new file mode 100644
index 00000000..335230d7
--- /dev/null
+++ b/tests/gpos/gpos_tests.cpp
@@ -0,0 +1,1093 @@
+/*
+ * Copyright (c) 2018 oxarbitrage and contributors.
+ *
+ * The MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include