Compare commits

...

61 commits

Author SHA1 Message Date
Srdjan Obucina
7e596a67e6 Temoprary disable account history tests for tracking accounts 2020-02-17 14:17:43 +01:00
Srdjan Obucina
75626254fa Add cmake command line option SUPPORT_MULTIPLE_SONS 2020-02-11 16:59:10 +01:00
Srdjan Obucina
0d14b481a4 Fix merging issue 2020-02-11 15:18:04 +01:00
Srdjan Obucina
da7b161d3d Merge branch 'feature/SONs-base' into feature/SON-238 2020-02-11 14:52:05 +01:00
Srdjan Obucina
df369df421 Move transfer inside son_wallet_transfer_process_operation 2020-02-10 19:17:56 +01:00
Srdjan Obucina
95ad384f0c Fix aprovals on already approved or invalid proposals 2020-02-10 06:40:05 +01:00
Srdjan Obucina
532afc062f Fix bad param 2020-02-10 02:04:30 +01:00
Srdjan Obucina
7f56e8e661 Managing GRAPHENE_SON_ACCOUNT and issuing assets on Bitcoin deposit 2020-02-10 01:53:15 +01:00
Srdjan Obucina
69cad0f1bd Add is_active_son guards, fix sending proposals and aprovals 2020-02-07 21:38:30 +01:00
Srdjan Obucina
98ebbbd69b Merge branch 'feature/SON-231' into feature/SON-238 2020-02-07 18:30:52 +01:00
Srdjan Obucina
2cc90a8794 Add is_active_son guards for sidechain events processing 2020-02-07 18:30:11 +01:00
Srdjan Obucina
5b1dea0d39 Support multiple SON nodes per software instance 2020-02-07 00:17:19 +01:00
Srdjan Obucina
9056ba6d07 Issue tokens to the user who deposited Bitcoin, WIP... 2020-02-05 20:19:47 +01:00
Srdjan Obucina
bb8d334e6c Add son_wallet_transfer_process_operation 2020-02-05 14:07:00 +01:00
Srdjan Obucina
01f1b6137a Issue tokens to the user who deposited Bitcoin, WIP... 2020-02-04 23:40:40 +01:00
Srdjan Obucina
23458ee917 Merge branch 'feature/SONs-base' into feature/SON-231 2020-02-04 20:00:25 +01:00
Srdjan Obucina
bbd2e35014 Remove duplicated item in CMakeLists.txt 2020-02-04 19:58:59 +01:00
Srdjan Obucina
89ca9167d3 Squashed commit of the following:
commit a688bb93ed
Author: obucinac <obucinac@users.noreply.github.com>
Date:   Tue Feb 4 19:31:45 2020 +0100

    son_wallet_object operations and multisig wallet recreation by RPC (#263)

    * Extend GPO.active_sons to contain votes and all public keys

    * Introduce son_wallet_object
    * son_wallet_object operations
    * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation
    * son_wallet_object API and cli wallet commands
    * Send RPC command to bitcoin node to recreate multisig wallet
    * Updating wallet info through operation instead through database.modify() for persistance
    * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp
    * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp
    * Fix #include <graphene/chain/son_wallet_transfer_object.hpp>
    * Refactor primary wallet recreation
    * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal
    * Quickfix for checking payer in evaluator
    * Fix failing son_wallet_tests
    - Check for son_btc_account is temporarely disabled
    * Remove redundant file
    Co-authored-by: gladcow <jahr@yandex.ru>

commit 6e61d6b055
Author: satyakoneru <satyakoneru.iiith@gmail.com>
Date:   Tue Feb 4 00:14:39 2020 +1100

    SON233 - Provide correct downtime metrics to user (#278)
2020-02-04 19:56:20 +01:00
Srdjan Obucina
9db6179f79 Merge branch 'feature/SON-98' into feature/SON-231 2020-02-04 19:00:34 +01:00
Srdjan Obucina
5c57f5d9c8 Remove redundant file 2020-02-04 18:16:27 +01:00
Srdjan Obucina
703c577d90 Merge branch 'feature/SON-98' into feature/SON-231 2020-02-03 20:13:51 +01:00
Srdjan Obucina
263ba5d15b Fix failing son_wallet_tests
- Check for son_btc_account is temporarely disabled
2020-02-03 18:59:02 +01:00
Srdjan Obucina
ca112b1eb6 Merge branch 'feature/SON-98' into feature/SON-231 2020-01-31 23:55:59 +01:00
Srdjan Obucina
f62a46e789 Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-31 23:53:31 +01:00
Srdjan Obucina
5b7dd86ab9 Quickfix for checking payer in evaluator 2020-01-31 23:52:18 +01:00
Srdjan Obucina
2bb3fc79d7 Quickfix for checking payer in evaluator 2020-01-31 23:36:13 +01:00
Srdjan Obucina
c5ea418321 PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal 2020-01-31 23:28:20 +01:00
Srdjan Obucina
afcb1ace1b PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal 2020-01-31 21:53:51 +01:00
Srdjan Obucina
0c7ef96178 Refactor primary wallet recreation 2020-01-31 15:32:46 +01:00
Srdjan Obucina
95b515c09a Merge branch 'feature/SON-98' into feature/SON-231 2020-01-30 18:32:36 +01:00
Srdjan Obucina
01a1f584df Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-30 18:31:52 +01:00
Srdjan Obucina
f6c1da53df Refactor primary wallet recreation 2020-01-30 18:25:15 +01:00
Srdjan Obucina
d048873601 Merge branch 'feature/SONs-base' into feature/SON-231 2020-01-29 21:18:22 +01:00
Srdjan Obucina
42b7d25a99 SON wallet transfer object and operations, for tracking assets deposit/withdrawal 2020-01-29 17:09:37 +01:00
Srdjan Obucina
775fdf8980 SON wallet transfer object and operations, for tracking assets deposit/withdrawal 2020-01-28 22:10:35 +01:00
Srdjan Obucina
6055576aa4 Merge branch 'feature/SON-98' into feature/SON-231 2020-01-28 16:45:35 +01:00
Srdjan Obucina
1808082d59 Fix #include <graphene/chain/son_wallet_transfer_object.hpp> 2020-01-28 14:53:27 +01:00
obucinac
9c1eb63d15
Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp
Co-Authored-By: gladcow <jahr@yandex.ru>
2020-01-28 14:16:21 +01:00
obucinac
4590f919dc
Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp
Co-Authored-By: gladcow <jahr@yandex.ru>
2020-01-28 14:16:11 +01:00
Srdjan Obucina
be60f65612 Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-28 14:13:16 +01:00
Srdjan Obucina
df7ee459f9 SON wallet transfer object and operations, for tracking assets deposit/withdrawal 2020-01-28 14:04:31 +01:00
Srdjan Obucina
0142348a9c Updating wallet info through operation instead through database.modify() for persistance 2020-01-27 13:14:22 +01:00
Srdjan Obucina
8399008e7d Wallet recreation by scheduled SON only, some cosmetic refactoring 2020-01-24 16:51:45 +01:00
Srdjan Obucina
7ceed83a95 Wallet recreation by scheduled SON only, some cosmetic refactoring 2020-01-24 16:50:25 +01:00
Srdjan Obucina
a6e14af38f Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-23 13:16:45 +01:00
Srdjan Obucina
7abfa78ad6 Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-22 13:51:12 +01:00
Srdjan Obucina
0b491510c4 Send RPC command to bitcoin node to recreate multisig wallet 2020-01-22 13:44:11 +01:00
Srdjan Obucina
f0a8e8e376 Send RPC command to bitcoin node to recreate multisig wallet 2020-01-22 02:43:24 +01:00
Srdjan Obucina
c25108f228 Send RPC command to bitcoin node to recreate multisig wallet 2020-01-21 04:32:19 +01:00
Srdjan Obucina
23cb5310b0 Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-21 02:58:03 +01:00
Srdjan Obucina
ebb6662210 Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-20 13:11:23 +01:00
Srdjan Obucina
1dc0eee6b6 son_wallet_object API and cli wallet commands 2020-01-18 00:35:25 +01:00
Srdjan Obucina
b06e1d680a Create son_wallet_object on new set of SONs, to initiate primary wallet recreation 2020-01-17 21:32:44 +01:00
Srdjan Obucina
2615c41224 son_wallet_object operations completed, basic tests added 2020-01-17 14:56:44 +01:00
Srdjan Obucina
6f0c025462 son_wallet_object operations 2020-01-17 05:29:23 +01:00
Srdjan Obucina
61b8ff0cc7 son_wallet_object operations 2020-01-16 08:07:18 +01:00
Srdjan Obucina
2b8a39e332 Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-16 07:55:57 +01:00
Srdjan Obucina
ba255da679 Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-15 12:56:37 +01:00
Srdjan Obucina
0e47eeccf2 Merge branch 'feature/SONs-base' into feature/SON-98 2020-01-13 10:02:01 +01:00
Srdjan Obucina
6421d5e852 Introduce son_wallet_object 2020-01-03 03:25:46 +01:00
Srdjan Obucina
2e124b07c6 Extend GPO.active_sons to contain votes and all public keys 2019-12-26 06:31:21 +01:00
16 changed files with 453 additions and 331 deletions

View file

@ -259,6 +259,7 @@ void database::initialize_evaluators()
register_evaluator<recreate_son_wallet_evaluator>(); register_evaluator<recreate_son_wallet_evaluator>();
register_evaluator<update_son_wallet_evaluator>(); register_evaluator<update_son_wallet_evaluator>();
register_evaluator<create_son_wallet_transfer_evaluator>(); register_evaluator<create_son_wallet_transfer_evaluator>();
register_evaluator<process_son_wallet_transfer_evaluator>();
register_evaluator<add_sidechain_address_evaluator>(); register_evaluator<add_sidechain_address_evaluator>();
register_evaluator<update_sidechain_address_evaluator>(); register_evaluator<update_sidechain_address_evaluator>();
register_evaluator<delete_sidechain_address_evaluator>(); register_evaluator<delete_sidechain_address_evaluator>();
@ -446,6 +447,16 @@ void database::init_genesis(const genesis_state_type& genesis_state)
a.network_fee_percentage = 0; a.network_fee_percentage = 0;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;
}).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID); }).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "son-account";
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 0;
a.active.weight_threshold = 0;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_SON_ACCOUNT;
a.membership_expiration_date = time_point_sec::maximum();
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
}).get_id() == GRAPHENE_SON_ACCOUNT);
// Create more special accounts // Create more special accounts
while( true ) while( true )
{ {

View file

@ -432,14 +432,14 @@ void database::update_active_sons()
} }
// Update SON authority // Update SON authority
modify( get(GRAPHENE_SON_ACCOUNT_ID), [&]( account_object& a ) modify( get(GRAPHENE_SON_ACCOUNT), [&]( account_object& a )
{ {
if( head_block_time() < HARDFORK_533_TIME ) if( head_block_time() < HARDFORK_533_TIME )
{ {
uint64_t total_votes = 0; uint64_t total_votes = 0;
map<account_id_type, uint64_t> weights; map<account_id_type, uint64_t> weights;
a.active.weight_threshold = 0; a.active.weight_threshold = 0;
a.active.clear(); a.active.account_auths.clear();
for( const son_object& son : sons ) for( const son_object& son : sons )
{ {

View file

@ -417,6 +417,10 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
assert( aobj != nullptr ); assert( aobj != nullptr );
accounts.insert( aobj->son_account ); accounts.insert( aobj->son_account );
break; break;
} case son_wallet_object_type:{
break;
} case son_wallet_transfer_object_type:{
break;
} case sidechain_address_object_type:{ } case sidechain_address_object_type:{
const auto& aobj = dynamic_cast<const sidechain_address_object*>(obj); const auto& aobj = dynamic_cast<const sidechain_address_object*>(obj);
assert( aobj != nullptr ); assert( aobj != nullptr );

View file

@ -108,6 +108,12 @@ fc::variant_object get_config()
result[ "GRAPHENE_RELAXED_COMMITTEE_ACCOUNT" ] = fc::variant(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_RELAXED_COMMITTEE_ACCOUNT" ] = fc::variant(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
result[ "GRAPHENE_NULL_ACCOUNT" ] = fc::variant(GRAPHENE_NULL_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_NULL_ACCOUNT" ] = fc::variant(GRAPHENE_NULL_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
result[ "GRAPHENE_TEMP_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_TEMP_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
result[ "GRAPHENE_PROXY_TO_SELF_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
result[ "GRAPHENE_RAKE_FEE_ACCOUNT_ID" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
result[ "GRAPHENE_SON_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
result[ "GRAPHENE_NULL_WITNESS" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
result[ "GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
result[ "GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
return result; return result;
} }

View file

@ -176,7 +176,7 @@
/// ///
#define GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6)) #define GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
/// ///
#define GRAPHENE_SON_ACCOUNT_ID (graphene::chain::account_id_type(7)) #define GRAPHENE_SON_ACCOUNT (graphene::chain::account_id_type(7))
/// Sentinel value used in the scheduler. /// Sentinel value used in the scheduler.
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0)) #define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
///@} ///@}

View file

@ -19,6 +19,7 @@ namespace graphene { namespace chain {
int64_t sidechain_amount; int64_t sidechain_amount;
chain::account_id_type peerplays_from; chain::account_id_type peerplays_from;
chain::account_id_type peerplays_to; chain::account_id_type peerplays_to;
chain::asset peerplays_amount;
account_id_type fee_payer()const { return payer; } account_id_type fee_payer()const { return payer; }
share_type calculate_fee(const fee_parameters_type& k)const { return 0; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
@ -41,7 +42,7 @@ namespace graphene { namespace chain {
FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation::fee_parameters_type, (fee) )
FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation, (fee)(payer) FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation, (fee)(payer)
(timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) (peerplays_from) (peerplays_to)) (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_amount))
FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation::fee_parameters_type, (fee) )
FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation, (fee)(payer) FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation, (fee)(payer)
(son_wallet_transfer_id)) (son_wallet_transfer_id))

View file

@ -18,6 +18,7 @@ namespace graphene { namespace chain {
time_point_sec timestamp; time_point_sec timestamp;
peerplays_sidechain::sidechain_type sidechain; peerplays_sidechain::sidechain_type sidechain;
int64_t confirmations;
std::string sidechain_uid; std::string sidechain_uid;
std::string sidechain_transaction_id; std::string sidechain_transaction_id;
std::string sidechain_from; std::string sidechain_from;
@ -25,6 +26,7 @@ namespace graphene { namespace chain {
int64_t sidechain_amount; int64_t sidechain_amount;
chain::account_id_type peerplays_from; chain::account_id_type peerplays_from;
chain::account_id_type peerplays_to; chain::account_id_type peerplays_to;
chain::asset peerplays_amount;
bool processed; bool processed;
}; };
@ -60,7 +62,7 @@ namespace graphene { namespace chain {
} } // graphene::chain } } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::son_wallet_transfer_object, (graphene::db::object), FC_REFLECT_DERIVED( graphene::chain::son_wallet_transfer_object, (graphene::db::object),
(timestamp) (sidechain) (timestamp) (sidechain) (confirmations)
(sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount)
(peerplays_from) (peerplays_to) (peerplays_from) (peerplays_to) (peerplays_amount)
(processed) ) (processed) )

View file

@ -9,7 +9,7 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
//FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set.");
FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." );
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
auto itr = idx.rbegin(); auto itr = idx.rbegin();
@ -55,13 +55,13 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
//FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set.");
FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." );
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() );
auto itr = idx.find(op.son_wallet_id); //auto itr = idx.find(op.son_wallet_id);
FC_ASSERT( itr->addresses.find(peerplays_sidechain::sidechain_type::bitcoin) == itr->addresses.end() || //FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() ||
itr->addresses.at(peerplays_sidechain::sidechain_type::bitcoin).empty(), "Sidechain wallet address already set"); // itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set");
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
@ -69,11 +69,13 @@ object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_ope
{ try { { try {
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
auto itr = idx.find(op.son_wallet_id); auto itr = idx.find(op.son_wallet_id);
if(itr != idx.end()) if (itr != idx.end())
{ {
db().modify(*itr, [&op](son_wallet_object &swo) { if (itr->addresses.find(op.sidechain) == itr->addresses.end()) {
swo.addresses[op.sidechain] = op.address; db().modify(*itr, [&op](son_wallet_object &swo) {
}); swo.addresses[op.sidechain] = op.address;
});
}
} }
return op.son_wallet_id; return op.son_wallet_id;
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -1,6 +1,7 @@
#include <graphene/chain/son_wallet_transfer_evaluator.hpp> #include <graphene/chain/son_wallet_transfer_evaluator.hpp>
#include <graphene/chain/database.hpp> #include <graphene/chain/database.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <graphene/chain/son_wallet_transfer_object.hpp> #include <graphene/chain/son_wallet_transfer_object.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -9,39 +10,92 @@ void_result create_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_t
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
//FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set.");
FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." );
const auto& idx = db().get_index_type<son_wallet_transfer_index>().indices().get<by_sidechain_uid>(); //const auto& idx = db().get_index_type<son_wallet_transfer_index>().indices().get<by_sidechain_uid>();
FC_ASSERT(idx.find(op.sidechain_uid) == idx.end(), "Already registered " + op.sidechain_uid); //FC_ASSERT(idx.find(op.sidechain_uid) == idx.end(), "Already registered " + op.sidechain_uid);
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type create_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_create_operation& op) object_id_type create_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_create_operation& op)
{ try { { try {
const auto& new_son_wallet_transfer_object = db().create<son_wallet_transfer_object>( [&]( son_wallet_transfer_object& swto ){ const auto& idx = db().get_index_type<son_wallet_transfer_index>().indices().get<by_sidechain_uid>();
swto.timestamp = op.timestamp; auto itr = idx.find(op.sidechain_uid);
swto.sidechain = op.sidechain; if (itr == idx.end()) {
swto.sidechain_uid = op.sidechain_uid; const auto& new_son_wallet_transfer_object = db().create<son_wallet_transfer_object>( [&]( son_wallet_transfer_object& swto ){
swto.sidechain_transaction_id = op.sidechain_transaction_id; swto.timestamp = op.timestamp;
swto.sidechain_from = op.sidechain_from; swto.sidechain = op.sidechain;
swto.sidechain_to = op.sidechain_to; swto.confirmations = 1;
swto.sidechain_amount = op.sidechain_amount; swto.sidechain_uid = op.sidechain_uid;
swto.peerplays_from = op.peerplays_from; swto.sidechain_transaction_id = op.sidechain_transaction_id;
swto.peerplays_to = op.peerplays_to; swto.sidechain_from = op.sidechain_from;
swto.processed = false; swto.sidechain_to = op.sidechain_to;
}); swto.sidechain_amount = op.sidechain_amount;
return new_son_wallet_transfer_object.id; swto.peerplays_from = op.peerplays_from;
swto.peerplays_to = op.peerplays_to;
swto.peerplays_amount = op.peerplays_amount;
swto.processed = false;
});
return new_son_wallet_transfer_object.id;
} else {
db().modify(*itr, [&op](son_wallet_transfer_object &swto) {
swto.confirmations = swto.confirmations + 1;
});
return (*itr).id;
}
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
void_result process_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_transfer_process_operation& op) void_result process_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_transfer_process_operation& op)
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
//FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set.");
FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." );
const auto& idx = db().get_index_type<son_wallet_transfer_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_transfer_index>().indices().get<by_id>();
FC_ASSERT(idx.find(op.son_wallet_transfer_id) != idx.end(), "Son wallet transfer not found"); const auto& itr = idx.find(op.son_wallet_transfer_id);
return void_result(); FC_ASSERT(itr != idx.end(), "Son wallet transfer not found");
//FC_ASSERT(itr->processed == false, "Son wallet transfer is already processed");
const database& d = db();
const account_object& from_account = itr->peerplays_to(d); // reversed, for deposit
const account_object& to_account = itr->peerplays_from(d); // reversed, for deposit
const asset_object& asset_type = itr->peerplays_amount.asset_id(d);
try {
GRAPHENE_ASSERT(
is_authorized_asset( d, from_account, asset_type ),
transfer_from_account_not_whitelisted,
"'from' account ${from} is not whitelisted for asset ${asset}",
("from",from_account.id)
("asset",itr->peerplays_amount.asset_id)
);
GRAPHENE_ASSERT(
is_authorized_asset( d, to_account, asset_type ),
transfer_to_account_not_whitelisted,
"'to' account ${to} is not whitelisted for asset ${asset}",
("to",to_account.id)
("asset",itr->peerplays_amount.asset_id)
);
if( asset_type.is_transfer_restricted() )
{
GRAPHENE_ASSERT(
from_account.id == asset_type.issuer || to_account.id == asset_type.issuer,
transfer_restricted_transfer_asset,
"Asset {asset} has transfer_restricted flag enabled",
("asset", itr->peerplays_amount.asset_id)
);
}
bool insufficient_balance = d.get_balance( from_account, asset_type ).amount >= itr->peerplays_amount.amount;
FC_ASSERT( insufficient_balance,
"Insufficient Balance: ${balance}, unable to transfer '${total_transfer}' from account '${a}' to '${t}'",
("a",from_account.name)("t",to_account.name)("total_transfer",d.to_pretty_string(itr->peerplays_amount))("balance",d.to_pretty_string(d.get_balance(from_account, asset_type))) );
return void_result();
} FC_RETHROW_EXCEPTIONS( error, "Unable to transfer ${a} from ${f} to ${t}", ("a",d.to_pretty_string(itr->peerplays_amount))("f",from_account.name)("t",to_account.name) );
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type process_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_process_operation& op) object_id_type process_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_process_operation& op)
@ -50,9 +104,17 @@ object_id_type process_son_wallet_transfer_evaluator::do_apply(const son_wallet_
auto itr = idx.find(op.son_wallet_transfer_id); auto itr = idx.find(op.son_wallet_transfer_id);
if(itr != idx.end()) if(itr != idx.end())
{ {
db().modify(*itr, [&op](son_wallet_transfer_object &swto) { if (itr->processed == false) {
swto.processed = true; db().modify(*itr, [&op](son_wallet_transfer_object &swto) {
}); swto.processed = true;
});
const account_id_type from_account = itr->peerplays_to; // reversed, for deposit
const account_id_type to_account = itr->peerplays_from; // reversed, for deposit
db().adjust_balance( from_account, -itr->peerplays_amount );
db().adjust_balance( to_account, itr->peerplays_amount );
}
} }
return op.son_wallet_transfer_id; return op.son_wallet_transfer_id;
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -7,6 +7,13 @@ add_library( peerplays_sidechain
sidechain_net_handler_bitcoin.cpp sidechain_net_handler_bitcoin.cpp
) )
if (SUPPORT_MULTIPLE_SONS)
message ("Multiple SONs per software instance are supported")
target_compile_definitions(peerplays_sidechain PRIVATE SUPPORT_MULTIPLE_SONS)
endif()
unset(SUPPORT_MULTIPLE_SONS)
unset(SUPPORT_MULTIPLE_SONS CACHE)
target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq ) target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq )
target_include_directories( peerplays_sidechain target_include_directories( peerplays_sidechain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -27,10 +27,12 @@ class peerplays_sidechain_plugin : public graphene::app::plugin
std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my; std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my;
son_id_type get_son_id(); std::set<chain::son_id_type>& get_sons();
son_object get_son_object(); son_object get_son_object(son_id_type son_id);
bool is_active_son(); bool is_active_son(son_id_type son_id);
std::map<chain::public_key_type, fc::ecc::private_key>& get_private_keys(); std::map<chain::public_key_type, fc::ecc::private_key>& get_private_keys();
fc::ecc::private_key get_private_key(son_id_type son_id);
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
}; };
} } //graphene::peerplays_sidechain } } //graphene::peerplays_sidechain

View file

@ -2,6 +2,7 @@
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/range/algorithm_ext/insert.hpp>
#include <fc/log/logger.hpp> #include <fc/log/logger.hpp>
#include <fc/smart_ref_impl.hpp> #include <fc/smart_ref_impl.hpp>
@ -33,10 +34,12 @@ class peerplays_sidechain_plugin_impl
void plugin_initialize(const boost::program_options::variables_map& options); void plugin_initialize(const boost::program_options::variables_map& options);
void plugin_startup(); void plugin_startup();
son_id_type get_son_id(); std::set<chain::son_id_type>& get_sons();
son_object get_son_object(); son_object get_son_object(son_id_type son_id);
bool is_active_son(); bool is_active_son(son_id_type son_id);
std::map<chain::public_key_type, fc::ecc::private_key>& get_private_keys(); std::map<chain::public_key_type, fc::ecc::private_key>& get_private_keys();
fc::ecc::private_key get_private_key(son_id_type son_id);
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
void schedule_heartbeat_loop(); void schedule_heartbeat_loop();
void heartbeat_loop(); void heartbeat_loop();
@ -86,12 +89,14 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
{ {
auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan")));
string son_id_example = fc::json::to_string(chain::son_id_type(5)); string son_id_example = fc::json::to_string(chain::son_id_type(5));
string son_id_example2 = fc::json::to_string(chain::son_id_type(6));
cli.add_options() cli.add_options()
("son-id", bpo::value<vector<string>>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str()) ("son-id", bpo::value<vector<string>>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str())
("son-ids", bpo::value<string>(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str())
("peerplays-private-key", bpo::value<vector<string>>()->composing()->multitoken()-> ("peerplays-private-key", bpo::value<vector<string>>()->composing()->multitoken()->
DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))),
"Tuple of [PublicKey, WIF private key]") "Tuple of [PublicKey, WIF private key] (may specify multiple times)")
("bitcoin-node-ip", bpo::value<string>()->default_value("99.79.189.95"), "IP address of Bitcoin node") ("bitcoin-node-ip", bpo::value<string>()->default_value("99.79.189.95"), "IP address of Bitcoin node")
("bitcoin-node-zmq-port", bpo::value<uint32_t>()->default_value(11111), "ZMQ port of Bitcoin node") ("bitcoin-node-zmq-port", bpo::value<uint32_t>()->default_value(11111), "ZMQ port of Bitcoin node")
@ -107,9 +112,17 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map& options) void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map& options)
{ {
config_ready_son = options.count( "son-id" ) && options.count( "peerplays-private-key" ); config_ready_son = (options.count( "son-id" ) || options.count( "son-ids" )) && options.count( "peerplays-private-key" );
if (config_ready_son) { if (config_ready_son) {
LOAD_VALUE_SET(options, "son-id", _sons, chain::son_id_type) LOAD_VALUE_SET(options, "son-id", _sons, chain::son_id_type)
if (options.count("son-ids"))
boost::insert(_sons, fc::json::from_string(options.at("son-ids").as<string>()).as<vector<chain::son_id_type>>( 5 ));
config_ready_son = config_ready_son && !_sons.empty();
#ifndef SUPPORT_MULTIPLE_SONS
FC_ASSERT( _sons.size() == 1, "Multiple SONs not supported" );
#endif
if( options.count("peerplays-private-key") ) if( options.count("peerplays-private-key") )
{ {
const std::vector<std::string> key_id_to_wif_pair_strings = options["peerplays-private-key"].as<std::vector<std::string>>(); const std::vector<std::string> key_id_to_wif_pair_strings = options["peerplays-private-key"].as<std::vector<std::string>>();
@ -173,9 +186,8 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
void peerplays_sidechain_plugin_impl::plugin_startup() void peerplays_sidechain_plugin_impl::plugin_startup()
{ {
if (config_ready_son) { if (config_ready_son) {
ilog("SON running"); ilog("Starting ${n} SON instances", ("n", _sons.size()));
ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size()));
schedule_heartbeat_loop(); schedule_heartbeat_loop();
} else { } else {
elog("No sons configured! Please add SON IDs and private keys to configuration."); elog("No sons configured! Please add SON IDs and private keys to configuration.");
@ -190,24 +202,24 @@ void peerplays_sidechain_plugin_impl::plugin_startup()
//} //}
} }
son_id_type peerplays_sidechain_plugin_impl::get_son_id() std::set<chain::son_id_type>& peerplays_sidechain_plugin_impl::get_sons()
{ {
return *(_sons.begin()); return _sons;
} }
son_object peerplays_sidechain_plugin_impl::get_son_object() son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id)
{ {
const auto& idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>(); const auto& idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
auto son_obj = idx.find( get_son_id() ); auto son_obj = idx.find( son_id );
if (son_obj == idx.end()) if (son_obj == idx.end())
return {}; return {};
return *son_obj; return *son_obj;
} }
bool peerplays_sidechain_plugin_impl::is_active_son() bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id)
{ {
const auto& idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>(); const auto& idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
auto son_obj = idx.find( get_son_id() ); auto son_obj = idx.find( son_id );
if (son_obj == idx.end()) if (son_obj == idx.end())
return false; return false;
@ -220,7 +232,7 @@ bool peerplays_sidechain_plugin_impl::is_active_son()
return swi.son_id; return swi.son_id;
}); });
auto it = std::find(active_son_ids.begin(), active_son_ids.end(), get_son_id()); auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id);
return (it != active_son_ids.end()); return (it != active_son_ids.end());
} }
@ -230,6 +242,20 @@ std::map<chain::public_key_type, fc::ecc::private_key>& peerplays_sidechain_plug
return _private_keys; return _private_keys;
} }
fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id)
{
return get_private_key(get_son_object(son_id).signing_key);
}
fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::public_key_type public_key)
{
auto private_key_itr = _private_keys.find( public_key );
if( private_key_itr != _private_keys.end() ) {
return private_key_itr->second;
}
return {};
}
void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop()
{ {
fc::time_point now = fc::time_point::now(); fc::time_point now = fc::time_point::now();
@ -245,40 +271,29 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop()
{ {
schedule_heartbeat_loop(); schedule_heartbeat_loop();
chain::database& d = plugin.database(); chain::database& d = plugin.database();
chain::son_id_type son_id = *(_sons.begin());
const auto& idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
auto son_obj = idx.find( son_id );
if(son_obj == idx.end())
return;
const chain::global_property_object& gpo = d.get_global_properties();
vector<son_id_type> active_son_ids;
active_son_ids.reserve(gpo.active_sons.size());
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
std::inserter(active_son_ids, active_son_ids.end()),
[](const son_info& swi) {
return swi.son_id;
});
auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); for (son_id_type son_id : _sons) {
if(it != active_son_ids.end() || son_obj->status == chain::son_status::in_maintenance) { if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) {
ilog("peerplays_sidechain_plugin: sending heartbeat");
chain::son_heartbeat_operation op; ilog("peerplays_sidechain_plugin: sending heartbeat for SON ${son}", ("son", son_id));
op.owner_account = son_obj->son_account; chain::son_heartbeat_operation op;
op.son_id = son_id; op.owner_account = get_son_object(son_id).son_account;
op.ts = fc::time_point::now() + fc::seconds(0); op.son_id = son_id;
chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); op.ts = fc::time_point::now() + fc::seconds(0);
fc::future<bool> fut = fc::async( [&](){ chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_id), op);
try { fc::future<bool> fut = fc::async( [&](){
d.push_transaction(trx); try {
if(plugin.app().p2p_node()) d.push_transaction(trx, database::validation_steps::skip_block_size_check);
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); if(plugin.app().p2p_node())
return true; plugin.app().p2p_node()->broadcast(net::trx_message(trx));
} catch(fc::exception e){ return true;
ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}",("e", e.what())); } catch(fc::exception e){
return false; ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}",("e", e.what()));
} return false;
}); }
fut.wait(fc::seconds(10)); });
fut.wait(fc::seconds(10));
}
} }
} }
@ -323,10 +338,10 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals()
((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold))) { ((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold))) {
ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id)))); ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id))));
chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts);
chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_obj->signing_key), op);
fc::future<bool> fut = fc::async( [&](){ fc::future<bool> fut = fc::async( [&](){
try { try {
d.push_transaction(trx); d.push_transaction(trx, database::validation_steps::skip_block_size_check);
if(plugin.app().p2p_node()) if(plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
return true; return true;
@ -347,13 +362,6 @@ void peerplays_sidechain_plugin_impl::recreate_primary_wallet()
void peerplays_sidechain_plugin_impl::process_deposits() { void peerplays_sidechain_plugin_impl::process_deposits() {
// Account who issues tokens to the user who made deposit
account_id_type pay_from = GRAPHENE_NULL_ACCOUNT;
const auto& account_idx = plugin.database().get_index_type<account_index>().indices().get<by_name>();
const auto& account_itr = account_idx.find("nathan");
if (account_itr != account_idx.end())
pay_from = (*account_itr).id;
const auto& idx = plugin.database().get_index_type<son_wallet_transfer_index>().indices().get<by_processed>(); const auto& idx = plugin.database().get_index_type<son_wallet_transfer_index>().indices().get<by_processed>();
const auto& idx_range = idx.equal_range(false); const auto& idx_range = idx.equal_range(false);
@ -362,22 +370,31 @@ void peerplays_sidechain_plugin_impl::process_deposits() {
const chain::global_property_object& gpo = plugin.database().get_global_properties(); const chain::global_property_object& gpo = plugin.database().get_global_properties();
transfer_operation op; for (son_id_type son_id : plugin.get_sons()) {
op.from = pay_from; if (plugin.is_active_son(son_id)) {
op.to = swto.peerplays_from;
op.amount = asset(swto.sidechain_amount); // For Bitcoin, the exchange rate is 1:1, for others, get the exchange rate from market
proposal_create_operation proposal_op; son_wallet_transfer_process_operation p_op;
proposal_op.fee_paying_account = plugin.get_son_object().son_account; p_op.payer = gpo.parameters.get_son_btc_account_id();
proposal_op.proposed_ops.push_back( op_wrapper( op ) ); p_op.son_wallet_transfer_id = swto.id;
uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime );
signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); proposal_create_operation proposal_op;
try { proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account;
plugin.database().push_transaction(trx); proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) );
} catch(fc::exception e){ uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime );
ilog("sidechain_net_handler: sending proposal for transfer operation ${swto} by ${son}", ("swto", swto.id) ("son", son_id));
signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
trx.validate();
ilog("sidechain_net_handler: transaction validated ${swto} by ${son}", ("swto", swto.id) ("son", son_id));
try {
plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
if(plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
} catch(fc::exception e){
ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what()));
}
}
} }
}); });
} }
@ -388,16 +405,17 @@ void peerplays_sidechain_plugin_impl::process_deposits() {
void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b )
{ {
chain::database& d = plugin.database(); chain::database& d = plugin.database();
chain::son_id_type my_son_id = *(_sons.begin());
const chain::global_property_object& gpo = d.get_global_properties(); const chain::global_property_object& gpo = d.get_global_properties();
bool latest_block = ((fc::time_point::now() - b.timestamp) < fc::microseconds(gpo.parameters.block_interval * 1000000)); bool latest_block = ((fc::time_point::now() - b.timestamp) < fc::microseconds(gpo.parameters.block_interval * 1000000));
// Return if there are no active SONs
if(gpo.active_sons.size() <= 0 || !latest_block) { if(gpo.active_sons.size() <= 0 || !latest_block) {
return; return;
} }
chain::son_id_type next_son_id = d.get_scheduled_son(1); chain::son_id_type next_son_id = d.get_scheduled_son(1);
if(next_son_id == my_son_id) { ilog("peerplays_sidechain_plugin_impl: Scheduled SON ${son}",("son", next_son_id));
// check if we control scheduled SON
if( _sons.find( next_son_id ) != _sons.end() ) {
create_son_down_proposals(); create_son_down_proposals();
@ -412,35 +430,18 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b )
void peerplays_sidechain_plugin_impl::on_objects_new(const vector<object_id_type>& new_object_ids) void peerplays_sidechain_plugin_impl::on_objects_new(const vector<object_id_type>& new_object_ids)
{ {
chain::database& d = plugin.database();
chain::son_id_type my_son_id = *(_sons.begin());
const chain::global_property_object& gpo = d.get_global_properties();
const auto& idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
auto son_obj = idx.find( my_son_id );
vector<son_id_type> active_son_ids;
active_son_ids.reserve(gpo.active_sons.size());
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
std::inserter(active_son_ids, active_son_ids.end()),
[](const son_info& swi) {
return swi.son_id;
});
auto it = std::find(active_son_ids.begin(), active_son_ids.end(), my_son_id); auto approve_proposal = [ & ]( const chain::son_id_type& son_id, const chain::proposal_id_type& proposal_id )
if(it == active_son_ids.end()) {
return;
}
auto approve_proposal = [ & ]( const chain::proposal_id_type& id )
{ {
ilog("peerplays_sidechain_plugin: sending approval for ${t} from ${s}",("t",std::string(object_id_type(id)))("s",std::string(object_id_type(my_son_id)))); ilog("peerplays_sidechain_plugin: sending approval for ${p} from ${s}", ("p", proposal_id) ("s", son_id));
chain::proposal_update_operation puo; chain::proposal_update_operation puo;
puo.fee_paying_account = son_obj->son_account; puo.fee_paying_account = get_son_object(son_id).son_account;
puo.proposal = id; puo.proposal = proposal_id;
puo.active_approvals_to_add = { son_obj->son_account }; puo.active_approvals_to_add = { get_son_object(son_id).son_account };
chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, puo); chain::signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), puo);
fc::future<bool> fut = fc::async( [&](){ fc::future<bool> fut = fc::async( [&](){
try { try {
d.push_transaction(trx); plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
if(plugin.app().p2p_node()) if(plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
return true; return true;
@ -454,25 +455,42 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vector<object_id_type
for(auto object_id: new_object_ids) { for(auto object_id: new_object_ids) {
if( object_id.is<chain::proposal_object>() ) { if( object_id.is<chain::proposal_object>() ) {
const object* obj = d.find_object(object_id);
const chain::proposal_object* proposal = dynamic_cast<const chain::proposal_object*>(obj);
if(proposal == nullptr || (proposal->available_active_approvals.find(son_obj->son_account) != proposal->available_active_approvals.end())) {
return;
}
if(proposal->proposed_transaction.operations.size() == 1 for (son_id_type son_id : _sons) {
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_report_down_operation>::value) { if (!is_active_son(son_id)) {
approve_proposal( proposal->id ); continue;
} }
if(proposal->proposed_transaction.operations.size() == 1 const object* obj = plugin.database().find_object(object_id);
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_wallet_update_operation>::value) { const chain::proposal_object* proposal = dynamic_cast<const chain::proposal_object*>(obj);
approve_proposal( proposal->id );
}
if(proposal->proposed_transaction.operations.size() == 1 if(proposal == nullptr || (proposal->available_active_approvals.find(get_son_object(son_id).son_account) != proposal->available_active_approvals.end())) {
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::transfer_operation>::value) { continue;
approve_proposal( proposal->id ); }
if(proposal->proposed_transaction.operations.size() == 1
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_report_down_operation>::value) {
approve_proposal( son_id, proposal->id );
continue;
}
if(proposal->proposed_transaction.operations.size() == 1
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_wallet_update_operation>::value) {
approve_proposal( son_id, proposal->id );
continue;
}
if(proposal->proposed_transaction.operations.size() == 1
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_wallet_transfer_create_operation>::value) {
approve_proposal( son_id, proposal->id );
continue;
}
if(proposal->proposed_transaction.operations.size() == 1
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_wallet_transfer_process_operation>::value) {
approve_proposal( son_id, proposal->id );
continue;
}
} }
} }
} }
@ -515,19 +533,19 @@ void peerplays_sidechain_plugin::plugin_startup()
my->plugin_startup(); my->plugin_startup();
} }
son_id_type peerplays_sidechain_plugin::get_son_id() std::set<chain::son_id_type>& peerplays_sidechain_plugin::get_sons()
{ {
return my->get_son_id(); return my->get_sons();
} }
son_object peerplays_sidechain_plugin::get_son_object() son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id)
{ {
return my->get_son_object(); return my->get_son_object(son_id);
} }
bool peerplays_sidechain_plugin::is_active_son() bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id)
{ {
return my->is_active_son(); return my->is_active_son(son_id);
} }
std::map<chain::public_key_type, fc::ecc::private_key>& peerplays_sidechain_plugin::get_private_keys() std::map<chain::public_key_type, fc::ecc::private_key>& peerplays_sidechain_plugin::get_private_keys()
@ -535,5 +553,15 @@ std::map<chain::public_key_type, fc::ecc::private_key>& peerplays_sidechain_plug
return my->get_private_keys(); return my->get_private_keys();
} }
fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(son_id_type son_id)
{
return my->get_private_key(son_id);
}
fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_key_type public_key)
{
return my->get_private_key(public_key);
}
} } // graphene::peerplays_sidechain } } // graphene::peerplays_sidechain

View file

@ -42,7 +42,6 @@ std::vector<std::string> sidechain_net_handler::get_sidechain_addresses() {
} }
void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data& sed) { void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data& sed) {
ilog( __FUNCTION__ );
ilog( "sidechain_event_data:" ); ilog( "sidechain_event_data:" );
ilog( " timestamp: ${timestamp}", ( "timestamp", sed.timestamp ) ); ilog( " timestamp: ${timestamp}", ( "timestamp", sed.timestamp ) );
ilog( " sidechain: ${sidechain}", ( "sidechain", sed.sidechain ) ); ilog( " sidechain: ${sidechain}", ( "sidechain", sed.sidechain ) );
@ -54,11 +53,6 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
ilog( " peerplays_from: ${peerplays_from}", ( "peerplays_from", sed.peerplays_from ) ); ilog( " peerplays_from: ${peerplays_from}", ( "peerplays_from", sed.peerplays_from ) );
ilog( " peerplays_to: ${peerplays_to}", ( "peerplays_to", sed.peerplays_to ) ); ilog( " peerplays_to: ${peerplays_to}", ( "peerplays_to", sed.peerplays_to ) );
if (!plugin.is_active_son()) {
ilog( " !!! SON is not active and not processing sidechain events...");
return;
}
const chain::global_property_object& gpo = database.get_global_properties(); const chain::global_property_object& gpo = database.get_global_properties();
son_wallet_transfer_create_operation op; son_wallet_transfer_create_operation op;
@ -72,18 +66,26 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
op.sidechain_amount = sed.sidechain_amount; op.sidechain_amount = sed.sidechain_amount;
op.peerplays_from = sed.peerplays_from; op.peerplays_from = sed.peerplays_from;
op.peerplays_to = sed.peerplays_to; op.peerplays_to = sed.peerplays_to;
op.peerplays_amount = asset(sed.sidechain_amount / 1000); // For Bitcoin, the exchange rate is 1:1, for others, get the exchange rate from market
proposal_create_operation proposal_op; for (son_id_type son_id : plugin.get_sons()) {
proposal_op.fee_paying_account = plugin.get_son_object().son_account; if (plugin.is_active_son(son_id)) {
proposal_op.proposed_ops.push_back( op_wrapper( op ) ); proposal_create_operation proposal_op;
uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account;
proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); proposal_op.proposed_ops.emplace_back( op_wrapper( op ) );
uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime );
signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation by ${son}", ("son", son_id));
try { signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
database.push_transaction(trx); try {
} catch(fc::exception e){ database.push_transaction(trx, database::validation_steps::skip_block_size_check);
ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation failed with exception ${e}",("e", e.what())); if(plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
} catch(fc::exception e){
ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation by ${son} failed with exception ${e}", ("son", son_id) ("e", e.what()));
}
}
} }
} }

View file

@ -277,7 +277,6 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() {
boost::property_tree::ptree pt; boost::property_tree::ptree pt;
boost::property_tree::read_json( ss, pt ); boost::property_tree::read_json( ss, pt );
if( pt.count( "error" ) && pt.get_child( "error" ).empty() ) { if( pt.count( "error" ) && pt.get_child( "error" ).empty() ) {
ilog(__FUNCTION__);
std::stringstream res; std::stringstream res;
boost::property_tree::json_parser::write_json(res, pt.get_child("result")); boost::property_tree::json_parser::write_json(res, pt.get_child("result"));
@ -288,17 +287,21 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() {
op.sidechain = sidechain_type::bitcoin; op.sidechain = sidechain_type::bitcoin;
op.address = res.str(); op.address = res.str();
proposal_create_operation proposal_op; for (son_id_type son_id : plugin.get_sons()) {
proposal_op.fee_paying_account = plugin.get_son_object().son_account; proposal_create_operation proposal_op;
proposal_op.proposed_ops.push_back( op_wrapper( op ) ); proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account;
uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; proposal_op.proposed_ops.emplace_back( op_wrapper( op ) );
proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime );
signed_transaction trx = database.create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
try { try {
database.push_transaction(trx); database.push_transaction(trx, database::validation_steps::skip_block_size_check);
} catch(fc::exception e){ if(plugin.app().p2p_node())
ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what())); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
} catch(fc::exception e){
ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what()));
}
} }
} }
} }
@ -334,11 +337,6 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data
ilog("peerplays sidechain plugin: sidechain_net_handler_bitcoin::handle_event"); ilog("peerplays sidechain plugin: sidechain_net_handler_bitcoin::handle_event");
ilog(" event_data: ${event_data}", ("event_data", event_data)); ilog(" event_data: ${event_data}", ("event_data", event_data));
if (!plugin.is_active_son()) {
ilog(" !!! SON is not active and not processing sidechain events...");
return;
}
std::string block = bitcoin_client->receive_full_block( event_data ); std::string block = bitcoin_client->receive_full_block( event_data );
if( block != "" ) { if( block != "" ) {
const auto& vins = extract_info_from_block( block ); const auto& vins = extract_info_from_block( block );
@ -363,7 +361,7 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data
sed.sidechain_to = v.address; sed.sidechain_to = v.address;
sed.sidechain_amount = v.out.amount; sed.sidechain_amount = v.out.amount;
sed.peerplays_from = addr_itr->sidechain_address_account; sed.peerplays_from = addr_itr->sidechain_address_account;
sed.peerplays_to = GRAPHENE_SON_ACCOUNT_ID; sed.peerplays_to = GRAPHENE_SON_ACCOUNT;
sidechain_event_data_received(sed); sidechain_event_data_received(sed);
} }
} }

View file

@ -10,15 +10,12 @@ sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin& _plugin
plugin(_plugin), plugin(_plugin),
database(_plugin.database()) database(_plugin.database())
{ {
ilog(__FUNCTION__);
} }
sidechain_net_manager::~sidechain_net_manager() { sidechain_net_manager::~sidechain_net_manager() {
ilog(__FUNCTION__);
} }
bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options) { bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options) {
ilog(__FUNCTION__);
bool ret_val = false; bool ret_val = false;

272
tests/tests/history_api_tests.cpp Normal file → Executable file
View file

@ -407,143 +407,143 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) {
} FC_LOG_AND_RETHROW() } FC_LOG_AND_RETHROW()
} }
BOOST_AUTO_TEST_CASE(track_account) { //BOOST_AUTO_TEST_CASE(track_account) {
try { // try {
graphene::app::history_api hist_api(app); // graphene::app::history_api hist_api(app);
//
// // account_id_type() is not tracked
//
// // account_id_type() creates alice(not tracked account)
// const account_object& alice = create_account("alice");
// auto alice_id = alice.id;
//
// //account_id_type() creates some ops
// create_bitasset("CNY", account_id_type());
// create_bitasset("USD", account_id_type());
//
// // account_id_type() creates dan(account tracked)
// const account_object& dan = create_account("dan");
// auto dan_id = dan.id;
//
// // dan makes 1 op
// create_bitasset("EUR", dan_id);
//
// generate_block( ~database::skip_fork_db );
//
// // anything against account_id_type() should be {}
// vector<operation_history_object> histories =
// hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
// BOOST_CHECK_EQUAL(histories.size(), 0u);
// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0));
// BOOST_CHECK_EQUAL(histories.size(), 0u);
// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2));
// BOOST_CHECK_EQUAL(histories.size(), 0u);
//
// // anything against alice should be {}
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
// BOOST_CHECK_EQUAL(histories.size(), 0u);
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0));
// BOOST_CHECK_EQUAL(histories.size(), 0u);
// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2));
// BOOST_CHECK_EQUAL(histories.size(), 0u);
//
// // dan should have history
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
// BOOST_CHECK_EQUAL(histories.size(), 2u);
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
//
// // create more ops, starting with an untracked account
// create_bitasset( "BTC", account_id_type() );
// create_bitasset( "GBP", dan_id );
//
// generate_block( ~database::skip_fork_db );
//
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
// BOOST_CHECK_EQUAL(histories.size(), 3u);
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
//
// db.pop_block();
//
// // Try again, should result in same object IDs
// create_bitasset( "BTC", account_id_type() );
// create_bitasset( "GBP", dan_id );
//
// generate_block();
//
// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
// BOOST_CHECK_EQUAL(histories.size(), 3u);
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
// } catch (fc::exception &e) {
// edump((e.to_detail_string()));
// throw;
// }
//}
// account_id_type() is not tracked //BOOST_AUTO_TEST_CASE(track_account2) {
// try {
// account_id_type() creates alice(not tracked account) // graphene::app::history_api hist_api(app);
const account_object& alice = create_account("alice"); //
auto alice_id = alice.id; // // account_id_type() is tracked
//
//account_id_type() creates some ops // // account_id_type() creates alice(tracked account)
create_bitasset("CNY", account_id_type()); // const account_object& alice = create_account("alice");
create_bitasset("USD", account_id_type()); // auto alice_id = alice.id;
//
// account_id_type() creates dan(account tracked) // //account_id_type() creates some ops
const account_object& dan = create_account("dan"); // create_bitasset("CNY", account_id_type());
auto dan_id = dan.id; // create_bitasset("USD", account_id_type());
//
// dan makes 1 op // // alice makes 1 op
create_bitasset("EUR", dan_id); // create_bitasset("EUR", alice_id);
//
generate_block( ~database::skip_fork_db ); // // account_id_type() creates dan(account not tracked)
// const account_object& dan = create_account("dan");
// anything against account_id_type() should be {} // auto dan_id = dan.id;
vector<operation_history_object> histories = //
hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); // generate_block();
BOOST_CHECK_EQUAL(histories.size(), 0u); //
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0)); // // all account_id_type() should have 4 ops {4,2,1,0}
BOOST_CHECK_EQUAL(histories.size(), 0u); // vector<operation_history_object> histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2)); // BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories.size(), 0u); // BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);
// anything against alice should be {} // BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); // BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
BOOST_CHECK_EQUAL(histories.size(), 0u); //
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0)); // // all alice account should have 2 ops {3, 0}
BOOST_CHECK_EQUAL(histories.size(), 0u); // histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2)); // BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories.size(), 0u); // BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
// BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
// dan should have history //
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); // // alice first op should be {0}
BOOST_CHECK_EQUAL(histories.size(), 2u); // histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1));
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); // BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);
//
// create more ops, starting with an untracked account // // alice second op should be {3}
create_bitasset( "BTC", account_id_type() ); // histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0));
create_bitasset( "GBP", dan_id ); // BOOST_CHECK_EQUAL(histories.size(), 1u);
// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
generate_block( ~database::skip_fork_db ); //
// // anything against dan should be {}
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); // histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 3u); // BOOST_CHECK_EQUAL(histories.size(), 0u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); // histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); // BOOST_CHECK_EQUAL(histories.size(), 0u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); // histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2));
// BOOST_CHECK_EQUAL(histories.size(), 0u);
db.pop_block(); //
// } catch (fc::exception &e) {
// Try again, should result in same object IDs // edump((e.to_detail_string()));
create_bitasset( "BTC", account_id_type() ); // throw;
create_bitasset( "GBP", dan_id ); // }
//}
generate_block();
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
} catch (fc::exception &e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(track_account2) {
try {
graphene::app::history_api hist_api(app);
// account_id_type() is tracked
// account_id_type() creates alice(tracked account)
const account_object& alice = create_account("alice");
auto alice_id = alice.id;
//account_id_type() creates some ops
create_bitasset("CNY", account_id_type());
create_bitasset("USD", account_id_type());
// alice makes 1 op
create_bitasset("EUR", alice_id);
// account_id_type() creates dan(account not tracked)
const account_object& dan = create_account("dan");
auto dan_id = dan.id;
generate_block();
// all account_id_type() should have 4 ops {4,2,1,0}
vector<operation_history_object> histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
// all alice account should have 2 ops {3, 0}
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
// alice first op should be {0}
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);
// alice second op should be {3}
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
// anything against dan should be {}
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 0u);
} catch (fc::exception &e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(get_account_history_operations) { BOOST_AUTO_TEST_CASE(get_account_history_operations) {
try { try {