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>
This commit is contained in:
parent
6e61d6b055
commit
a688bb93ed
30 changed files with 895 additions and 468 deletions
|
|
@ -135,7 +135,7 @@ else( WIN32 ) # Apple AND Linux
|
|||
endif( APPLE )
|
||||
|
||||
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-invalid-offsetof" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-invalid-offsetof -Wno-terminate -Wno-sign-compare" )
|
||||
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
|
||||
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )
|
||||
|
|
|
|||
|
|
@ -152,6 +152,11 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
map<string, son_id_type> lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
uint64_t get_son_count()const;
|
||||
|
||||
// SON wallets
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
||||
|
||||
// Sidechain addresses
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const;
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_account(account_id_type account)const;
|
||||
|
|
@ -1770,6 +1775,57 @@ uint64_t database_api_impl::get_son_count()const
|
|||
return _db.get_index_type<son_index>().indices().size();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// SON Wallets //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
optional<son_wallet_object> database_api::get_active_son_wallet()
|
||||
{
|
||||
return my->get_active_son_wallet();
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api_impl::get_active_son_wallet()
|
||||
{
|
||||
const auto& idx = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx.rbegin();
|
||||
if (obj != idx.rend()) {
|
||||
return *obj;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api::get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{
|
||||
return my->get_son_wallet_by_time_point(time_point);
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api_impl::get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{
|
||||
const auto& son_wallets_by_id = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
for (const son_wallet_object& swo : son_wallets_by_id) {
|
||||
if ((time_point >= swo.valid_from) && (time_point < swo.expires))
|
||||
return swo;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<optional<son_wallet_object>> database_api::get_son_wallets(uint32_t limit)
|
||||
{
|
||||
return my->get_son_wallets(limit);
|
||||
}
|
||||
|
||||
vector<optional<son_wallet_object>> database_api_impl::get_son_wallets(uint32_t limit)
|
||||
{
|
||||
FC_ASSERT( limit <= 1000 );
|
||||
vector<optional<son_wallet_object>> result;
|
||||
const auto& son_wallets_by_id = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
for (const son_wallet_object& swo : son_wallets_by_id)
|
||||
result.push_back(swo);
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Sidechain Accounts //
|
||||
|
|
|
|||
|
|
@ -310,9 +310,18 @@ struct get_impacted_account_visitor
|
|||
void operator()( const son_heartbeat_operation& op ){
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_maintenance_operation& op ){
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_wallet_recreate_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_update_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_add_operation& op ){
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
|
|
@ -322,9 +331,6 @@ struct get_impacted_account_visitor
|
|||
void operator()( const sidechain_address_delete_operation& op ){
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
|
|
@ -603,6 +604,30 @@ class database_api
|
|||
*/
|
||||
uint64_t get_son_count()const;
|
||||
|
||||
/////////////////////////
|
||||
// SON Wallets //
|
||||
/////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Get active SON wallet
|
||||
* @return Active SON wallet object
|
||||
*/
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
|
||||
/**
|
||||
* @brief Get SON wallet that was active for a given time point
|
||||
* @param time_point Time point
|
||||
* @return SON wallet object, for the wallet that was active for a given time point
|
||||
*/
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
||||
|
||||
/**
|
||||
* @brief Get full list of SON wallets
|
||||
* @param limit Maximum number of results to return
|
||||
* @return A list of SON wallet objects
|
||||
*/
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
||||
|
||||
/////////////////////////
|
||||
// Sidechain Addresses //
|
||||
/////////////////////////
|
||||
|
|
@ -855,6 +880,11 @@ FC_API(graphene::app::database_api,
|
|||
(lookup_son_accounts)
|
||||
(get_son_count)
|
||||
|
||||
// SON wallets
|
||||
(get_active_son_wallet)
|
||||
(get_son_wallet_by_time_point)
|
||||
(get_son_wallets)
|
||||
|
||||
// Sidechain addresses
|
||||
(get_sidechain_addresses)
|
||||
(get_sidechain_addresses_by_account)
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@ add_library( graphene_chain
|
|||
son_evaluator.cpp
|
||||
son_object.cpp
|
||||
|
||||
son_wallet_evaluator.cpp
|
||||
|
||||
sidechain_address_evaluator.cpp
|
||||
|
||||
${HEADERS}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_proposal_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
|
||||
#include <graphene/chain/account_evaluator.hpp>
|
||||
|
|
@ -79,6 +80,7 @@
|
|||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
#include <graphene/chain/tournament_evaluator.hpp>
|
||||
#include <graphene/chain/son_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
|
@ -250,11 +252,13 @@ void database::initialize_evaluators()
|
|||
register_evaluator<update_son_evaluator>();
|
||||
register_evaluator<delete_son_evaluator>();
|
||||
register_evaluator<son_heartbeat_evaluator>();
|
||||
register_evaluator<son_report_down_evaluator>();
|
||||
register_evaluator<son_maintenance_evaluator>();
|
||||
register_evaluator<recreate_son_wallet_evaluator>();
|
||||
register_evaluator<update_son_wallet_evaluator>();
|
||||
register_evaluator<add_sidechain_address_evaluator>();
|
||||
register_evaluator<update_sidechain_address_evaluator>();
|
||||
register_evaluator<delete_sidechain_address_evaluator>();
|
||||
register_evaluator<son_report_down_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -299,6 +303,8 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<game_index> >();
|
||||
add_index< primary_index<son_proposal_index> >();
|
||||
|
||||
add_index< primary_index<son_wallet_index> >();
|
||||
|
||||
add_index< primary_index<sidechain_address_index> >();
|
||||
|
||||
//Implementation object indexes
|
||||
|
|
|
|||
|
|
@ -484,7 +484,41 @@ void database::update_active_sons()
|
|||
ilog( "Active SONs set NOT CHANGED" );
|
||||
} else {
|
||||
ilog( "Active SONs set CHANGED" );
|
||||
// Store new SON info, initiate wallet recreation and transfer of funds
|
||||
|
||||
bool should_recreate_pw = true;
|
||||
|
||||
// Expire for current son_wallet_object wallet, if exists
|
||||
const auto& idx_swi = get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx_swi.rbegin();
|
||||
if (obj != idx_swi.rend()) {
|
||||
// Compare current wallet SONs and to-be lists of active sons
|
||||
auto cur_wallet_sons = (*obj).sons;
|
||||
|
||||
bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size());
|
||||
if (wallet_son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) {
|
||||
wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
should_recreate_pw = !wallet_son_sets_equal;
|
||||
|
||||
if (should_recreate_pw) {
|
||||
modify(*obj, [&, obj](son_wallet_object &swo) {
|
||||
swo.expires = head_block_time();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (should_recreate_pw) {
|
||||
// Create new son_wallet_object, to initiate wallet recreation
|
||||
create<son_wallet_object>( [&]( son_wallet_object& obj ) {
|
||||
obj.valid_from = head_block_time();
|
||||
obj.expires = time_point_sec::maximum();
|
||||
obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end());
|
||||
});
|
||||
}
|
||||
|
||||
vector<son_info> sons_to_remove;
|
||||
// find all cur_active_sons members that is not in new_active_sons
|
||||
for_each(cur_active_sons.begin(), cur_active_sons.end(),
|
||||
|
|
|
|||
|
|
@ -297,9 +297,18 @@ struct get_impacted_account_visitor
|
|||
void operator()( const son_heartbeat_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_maintenance_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_wallet_recreate_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_update_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_add_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
|
|
@ -309,9 +318,6 @@ struct get_impacted_account_visitor
|
|||
void operator()( const sidechain_address_delete_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SON HARDFORK Monday, September 21, 2020 1:43:11 PM
|
||||
// SON HARDFORK Wednesday, January 1, 2020 12:00:00 AM - 1577836800
|
||||
// SON HARDFORK Monday, September 21, 2020 1:43:11 PM - 1600695791
|
||||
#ifndef HARDFORK_SON_TIME
|
||||
#include <ctime>
|
||||
#define HARDFORK_SON_TIME (fc::time_point_sec( time(NULL) - (60 * 60) ))
|
||||
#define HARDFORK_SON_TIME (fc::time_point_sec( 1577836800 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/protocol/son.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_address.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -142,11 +143,13 @@ namespace graphene { namespace chain {
|
|||
son_update_operation,
|
||||
son_delete_operation,
|
||||
son_heartbeat_operation,
|
||||
son_report_down_operation,
|
||||
son_maintenance_operation,
|
||||
son_wallet_recreate_operation,
|
||||
son_wallet_update_operation,
|
||||
sidechain_address_add_operation,
|
||||
sidechain_address_update_operation,
|
||||
sidechain_address_delete_operation,
|
||||
son_report_down_operation,
|
||||
son_maintenance_operation
|
||||
sidechain_address_delete_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_wallet_recreate_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
vector<son_info> sons;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_wallet_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_wallet_id_type son_wallet_id;
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain;
|
||||
string address;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) )
|
||||
|
|
@ -430,6 +430,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(bet_object_type)
|
||||
(son_object_type)
|
||||
(son_proposal_object_type)
|
||||
(son_wallet_object_type)
|
||||
(sidechain_address_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
|
|
@ -504,6 +505,7 @@ FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type )
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class recreate_son_wallet_evaluator : public evaluator<recreate_son_wallet_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_recreate_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_recreate_operation& o);
|
||||
object_id_type do_apply(const son_wallet_recreate_operation& o);
|
||||
};
|
||||
|
||||
class update_son_wallet_evaluator : public evaluator<update_son_wallet_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_update_operation& o);
|
||||
object_id_type do_apply(const son_wallet_update_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -16,16 +17,26 @@ namespace graphene { namespace chain {
|
|||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_wallet_object_type;
|
||||
|
||||
time_point_sec valid_from;
|
||||
time_point_sec expires;
|
||||
|
||||
flat_map<peerplays_sidechain::sidechain_type, string> addresses;
|
||||
vector<son_info> sons;
|
||||
};
|
||||
|
||||
struct by_sidechain_type;
|
||||
struct by_address;
|
||||
struct by_valid_from;
|
||||
struct by_expires;
|
||||
using son_wallet_multi_index_type = multi_index_container<
|
||||
son_wallet_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_valid_from>,
|
||||
member<son_wallet_object, time_point_sec, &son_wallet_object::valid_from>
|
||||
>,
|
||||
ordered_unique< tag<by_expires>,
|
||||
member<son_wallet_object, time_point_sec, &son_wallet_object::expires>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
|
@ -33,4 +44,4 @@ namespace graphene { namespace chain {
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_wallet_object, (graphene::db::object),
|
||||
(addresses) )
|
||||
(valid_from) (expires) (addresses) (sons) )
|
||||
|
|
|
|||
81
libraries/chain/son_wallet_evaluator.cpp
Normal file
81
libraries/chain/son_wallet_evaluator.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#include <graphene/chain/son_wallet_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op)
|
||||
{ try{
|
||||
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( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() );
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.rbegin();
|
||||
if(itr != idx.rend())
|
||||
{
|
||||
// Compare current wallet SONs and to-be lists of active sons
|
||||
auto cur_wallet_sons = (*itr).sons;
|
||||
auto new_wallet_sons = op.sons;
|
||||
|
||||
bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size());
|
||||
if (son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) {
|
||||
son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
FC_ASSERT(son_sets_equal == false, "Wallet recreation not needed, active SONs set is not changed.");
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.rbegin();
|
||||
if(itr != idx.rend())
|
||||
{
|
||||
db().modify(*itr, [&, op](son_wallet_object &swo) {
|
||||
swo.expires = db().head_block_time();
|
||||
});
|
||||
}
|
||||
|
||||
const auto& new_son_wallet_object = db().create<son_wallet_object>( [&]( son_wallet_object& obj ){
|
||||
obj.valid_from = db().head_block_time();
|
||||
obj.expires = time_point_sec::maximum();
|
||||
obj.sons = op.sons;
|
||||
});
|
||||
return new_son_wallet_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_operation& op)
|
||||
{ try{
|
||||
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( op.payer == db().get_global_properties().parameters.get_son_btc_account_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() );
|
||||
auto itr = idx.find(op.son_wallet_id);
|
||||
FC_ASSERT( itr->addresses.find(peerplays_sidechain::sidechain_type::bitcoin) == itr->addresses.end() ||
|
||||
itr->addresses.at(peerplays_sidechain::sidechain_type::bitcoin).empty(), "Sidechain wallet address already set");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_wallet_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](son_wallet_object &swo) {
|
||||
swo.addresses[op.sidechain] = op.address;
|
||||
});
|
||||
}
|
||||
return op.son_wallet_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -1,391 +0,0 @@
|
|||
2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J
|
||||
{
|
||||
"address": "2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J",
|
||||
"scriptPubKey": "a914873aad1ecf7510c80b83d8ca94d21432dc71b88787",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/2']0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd))#7s0qfnvz",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "00141d30ac0c47f7b32460265daf49c5925236f5882d",
|
||||
"pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "1d30ac0c47f7b32460265daf49c5925236f5882d",
|
||||
"pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd",
|
||||
"address": "bcrt1qr5c2crz877ejgcpxtkh5n3vj2gm0tzpdqrw3n0",
|
||||
"scriptPubKey": "00141d30ac0c47f7b32460265daf49c5925236f5882d"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/2'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2MxAnE469fhhdvUqUB7daU997VSearb2mn7
|
||||
{
|
||||
"address": "2MxAnE469fhhdvUqUB7daU997VSearb2mn7",
|
||||
"scriptPubKey": "a914360175a50918495a20573aed68d506a790420fe587",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/3']02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d))#p3st4e4e",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "00146c1c0571f3132eb0702f487123d2026495592830",
|
||||
"pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "6c1c0571f3132eb0702f487123d2026495592830",
|
||||
"pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d",
|
||||
"address": "bcrt1qdswq2u0nzvhtqup0fpcj85szvj24j2psgfwy6w",
|
||||
"scriptPubKey": "00146c1c0571f3132eb0702f487123d2026495592830"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/3'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL
|
||||
{
|
||||
"address": "2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL",
|
||||
"scriptPubKey": "a914bdce56e7f2fc04614c0f6f4d1d59fff63b0b73f187",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/4']020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462))#3r63c8fu",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "00146abd0d5f055df80b41bc6449328eda09f61a2be3",
|
||||
"pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "6abd0d5f055df80b41bc6449328eda09f61a2be3",
|
||||
"pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462",
|
||||
"address": "bcrt1qd27s6hc9thuqksduv3yn9rk6p8mp52lr2e846e",
|
||||
"scriptPubKey": "00146abd0d5f055df80b41bc6449328eda09f61a2be3"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/4'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR
|
||||
{
|
||||
"address": "2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR",
|
||||
"scriptPubKey": "a914b7abe6d957106da3a21782eea1164f4964b521ba87",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/5']03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce))#e3sfze3l",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8",
|
||||
"pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "7a4fd72ff8e192004c70d8139b4ca53e1467d8c8",
|
||||
"pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce",
|
||||
"address": "bcrt1q0f8awtlcuxfqqnrsmqfekn998c2x0kxgf4t7dm",
|
||||
"scriptPubKey": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/5'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL
|
||||
{
|
||||
"address": "2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL",
|
||||
"scriptPubKey": "a914dcb019e8330b4fffc50ba22bbf90215922a1379787",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/6']028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013))#3326m2za",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652",
|
||||
"pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "ccd3dee026a1d641352f5b8c7d72805d75fd0652",
|
||||
"pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013",
|
||||
"address": "bcrt1qenfaacpx58tyzdf0twx86u5qt46l6pjjef5y7u",
|
||||
"scriptPubKey": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/6'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG
|
||||
{
|
||||
"address": "2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG",
|
||||
"scriptPubKey": "a9144cb2b8f97d8e7ad5bfb81afd611394387f374ab887",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/7']02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b))#7gvhakzu",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1",
|
||||
"pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "e3607a0f745e2fb8b04fe1fa7f078c35009a77c1",
|
||||
"pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b",
|
||||
"address": "bcrt1quds85rm5tchm3vz0u8a87puvx5qf5a7pqw8u7l",
|
||||
"scriptPubKey": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/7'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg
|
||||
{
|
||||
"address": "2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg",
|
||||
"scriptPubKey": "a914dae51c6601ef4e05817f67d57d3ac0c8cb64948e87",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/8']03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b))#ckuy6v83",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7",
|
||||
"pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "60c1c7776b1f92cd36fe6138b99212ebe6381fe7",
|
||||
"pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b",
|
||||
"address": "bcrt1qvrquwamtr7fv6dh7vyutnysja0nrs8l8vzcnwj",
|
||||
"scriptPubKey": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/8'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX
|
||||
{
|
||||
"address": "2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX",
|
||||
"scriptPubKey": "a91413930c6d40f5f01b169f2fc7884c2b3984ff8a3087",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/9']022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac))#f8cdpnn9",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4",
|
||||
"pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "35b75b7c34a7f908955b8b93aaa677aa47fab2c4",
|
||||
"pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac",
|
||||
"address": "bcrt1qxkm4klp55lus392m3wf64fnh4frl4vkyng4ffg",
|
||||
"scriptPubKey": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/9'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM
|
||||
{
|
||||
"address": "2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM",
|
||||
"scriptPubKey": "a9145e91543069ae37d8bace2f59aade945f5916dc3287",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/10']03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af))#x7xpvc0y",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2",
|
||||
"pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2",
|
||||
"pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af",
|
||||
"address": "bcrt1qe6e4hqvckkx8dlx83fcdr5aza5w5xf0jd22xet",
|
||||
"scriptPubKey": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/10'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy
|
||||
{
|
||||
"address": "2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy",
|
||||
"scriptPubKey": "a914e132c578bd294a01a472d42b376b29e5ef6678c987",
|
||||
"ismine": true,
|
||||
"solvable": true,
|
||||
"desc": "sh(wpkh([153472fd/0'/0'/11']02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b))#2janhauz",
|
||||
"iswatchonly": false,
|
||||
"isscript": true,
|
||||
"iswitness": false,
|
||||
"script": "witness_v0_keyhash",
|
||||
"hex": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762",
|
||||
"pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b",
|
||||
"embedded": {
|
||||
"isscript": false,
|
||||
"iswitness": true,
|
||||
"witness_version": 0,
|
||||
"witness_program": "831d63f8d99be71f9b385f2ccc92ab2783da3762",
|
||||
"pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b",
|
||||
"address": "bcrt1qsvwk87xen0n3lxectukvey4ty7pa5dmz0yanuv",
|
||||
"scriptPubKey": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762"
|
||||
},
|
||||
"label": "",
|
||||
"ischange": false,
|
||||
"timestamp": 1571845292,
|
||||
"hdkeypath": "m/0'/0'/11'",
|
||||
"hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d",
|
||||
"hdmasterfingerprint": "153472fd",
|
||||
"labels": [
|
||||
{
|
||||
"name": "",
|
||||
"purpose": "receive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
#include <graphene/app/plugin.hpp>
|
||||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
using namespace chain;
|
||||
|
|
@ -30,6 +26,11 @@ class peerplays_sidechain_plugin : public graphene::app::plugin
|
|||
virtual void plugin_startup() override;
|
||||
|
||||
std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my;
|
||||
|
||||
son_id_type get_son_id();
|
||||
son_object get_son_object();
|
||||
bool is_active_son();
|
||||
std::map<chain::public_key_type, fc::ecc::private_key>& get_private_keys();
|
||||
};
|
||||
|
||||
} } //graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -1,27 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <fc/signals.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_handler {
|
||||
public:
|
||||
sidechain_net_handler(std::shared_ptr<graphene::chain::database> db, const boost::program_options::variables_map& options);
|
||||
sidechain_net_handler(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options);
|
||||
virtual ~sidechain_net_handler();
|
||||
|
||||
graphene::peerplays_sidechain::sidechain_type get_sidechain();
|
||||
std::vector<std::string> get_sidechain_addresses();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<graphene::chain::database> database;
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain;
|
||||
|
||||
void sidechain_event_data_received(const sidechain_event_data& sed);
|
||||
|
||||
virtual void recreate_primary_wallet() = 0;
|
||||
|
||||
protected:
|
||||
peerplays_sidechain_plugin& plugin;
|
||||
graphene::chain::database& database;
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain;
|
||||
|
||||
virtual std::string create_multisignature_wallet( const std::vector<std::string> public_keys ) = 0;
|
||||
virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0;
|
||||
virtual std::string sign_transaction( const std::string& transaction ) = 0;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public:
|
|||
bool receive_mempool_entry_tx( const std::string& tx_hash );
|
||||
uint64_t receive_estimated_fee();
|
||||
void send_btc_tx( const std::string& tx_hex );
|
||||
std::string add_multisig_address( const std::vector<std::string> public_keys );
|
||||
bool connection_is_not_defined() const;
|
||||
|
||||
private:
|
||||
|
|
@ -56,9 +57,11 @@ private:
|
|||
|
||||
class sidechain_net_handler_bitcoin : public sidechain_net_handler {
|
||||
public:
|
||||
sidechain_net_handler_bitcoin(std::shared_ptr<graphene::chain::database> db, const boost::program_options::variables_map& options);
|
||||
sidechain_net_handler_bitcoin(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options);
|
||||
virtual ~sidechain_net_handler_bitcoin();
|
||||
|
||||
void recreate_primary_wallet();
|
||||
|
||||
bool connection_is_not_defined() const;
|
||||
|
||||
std::string create_multisignature_wallet( const std::vector<std::string> public_keys );
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@ namespace graphene { namespace peerplays_sidechain {
|
|||
|
||||
class sidechain_net_manager {
|
||||
public:
|
||||
sidechain_net_manager(std::shared_ptr<graphene::chain::database> db);
|
||||
sidechain_net_manager(peerplays_sidechain_plugin& _plugin);
|
||||
virtual ~sidechain_net_manager();
|
||||
|
||||
bool create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options);
|
||||
void recreate_primary_wallet();
|
||||
private:
|
||||
std::shared_ptr<graphene::chain::database> database;
|
||||
peerplays_sidechain_plugin& plugin;
|
||||
graphene::chain::database& database;
|
||||
std::vector<std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
|
|
@ -25,11 +30,21 @@ class peerplays_sidechain_plugin_impl
|
|||
boost::program_options::options_description& cfg);
|
||||
void plugin_initialize(const boost::program_options::variables_map& options);
|
||||
void plugin_startup();
|
||||
|
||||
son_id_type get_son_id();
|
||||
son_object get_son_object();
|
||||
bool is_active_son();
|
||||
std::map<chain::public_key_type, fc::ecc::private_key>& get_private_keys();
|
||||
|
||||
void schedule_heartbeat_loop();
|
||||
void heartbeat_loop();
|
||||
void create_son_down_proposals();
|
||||
void recreate_primary_wallet();
|
||||
void process_deposits();
|
||||
//void process_withdrawals();
|
||||
void on_block_applied( const signed_block& b );
|
||||
void on_objects_new(const vector<object_id_type>& new_object_ids);
|
||||
void create_son_down_proposals();
|
||||
|
||||
private:
|
||||
peerplays_sidechain_plugin& plugin;
|
||||
|
||||
|
|
@ -40,6 +55,7 @@ class peerplays_sidechain_plugin_impl
|
|||
std::map<chain::public_key_type, fc::ecc::private_key> _private_keys;
|
||||
std::set<chain::son_id_type> _sons;
|
||||
fc::future<void> _heartbeat_task;
|
||||
|
||||
};
|
||||
|
||||
peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) :
|
||||
|
|
@ -121,7 +137,10 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
|
|||
throw;
|
||||
}
|
||||
|
||||
net_manager = std::unique_ptr<sidechain_net_manager>(new sidechain_net_manager(plugin.app().chain_database()));
|
||||
plugin.database().applied_block.connect( [&] (const signed_block& b) { on_block_applied(b); } );
|
||||
plugin.database().new_objects.connect( [&] (const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) { on_objects_new(ids); } );
|
||||
|
||||
net_manager = std::unique_ptr<sidechain_net_manager>(new sidechain_net_manager(plugin));
|
||||
|
||||
config_ready_bitcoin = options.count( "bitcoin-node-ip" ) &&
|
||||
options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" ) &&
|
||||
|
|
@ -153,24 +172,62 @@ void peerplays_sidechain_plugin_impl::plugin_startup()
|
|||
{
|
||||
if (config_ready_son) {
|
||||
ilog("SON running");
|
||||
|
||||
ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size()));
|
||||
schedule_heartbeat_loop();
|
||||
} else {
|
||||
elog("No sons configured! Please add SON IDs and private keys to configuration.");
|
||||
}
|
||||
|
||||
if (config_ready_bitcoin) {
|
||||
ilog("Bitcoin sidechain handler running");
|
||||
}
|
||||
|
||||
if( !_sons.empty() && !_private_keys.empty() )
|
||||
{
|
||||
ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size()));
|
||||
heartbeat_loop();
|
||||
} else
|
||||
elog("No sons configured! Please add SON IDs and private keys to configuration.");
|
||||
|
||||
//if (config_ready_ethereum) {
|
||||
// ilog("Ethereum sidechain handler running");
|
||||
//}
|
||||
}
|
||||
|
||||
son_id_type peerplays_sidechain_plugin_impl::get_son_id()
|
||||
{
|
||||
return *(_sons.begin());
|
||||
}
|
||||
|
||||
son_object peerplays_sidechain_plugin_impl::get_son_object()
|
||||
{
|
||||
const auto& idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( get_son_id() );
|
||||
if (son_obj == idx.end())
|
||||
return {};
|
||||
return *son_obj;
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_active_son()
|
||||
{
|
||||
const auto& idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( get_son_id() );
|
||||
if (son_obj == idx.end())
|
||||
return false;
|
||||
|
||||
const chain::global_property_object& gpo = plugin.database().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(), get_son_id());
|
||||
|
||||
return (it != active_son_ids.end());
|
||||
}
|
||||
|
||||
std::map<chain::public_key_type, fc::ecc::private_key>& peerplays_sidechain_plugin_impl::get_private_keys()
|
||||
{
|
||||
return _private_keys;
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop()
|
||||
{
|
||||
fc::time_point now = fc::time_point::now();
|
||||
|
|
@ -280,6 +337,17 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals()
|
|||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::recreate_primary_wallet()
|
||||
{
|
||||
net_manager->recreate_primary_wallet();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_deposits() {
|
||||
}
|
||||
|
||||
//void peerplays_sidechain_plugin_impl::process_withdrawals() {
|
||||
//}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b )
|
||||
{
|
||||
chain::database& d = plugin.database();
|
||||
|
|
@ -293,7 +361,15 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b )
|
|||
|
||||
chain::son_id_type next_son_id = d.get_scheduled_son(1);
|
||||
if(next_son_id == my_son_id) {
|
||||
|
||||
create_son_down_proposals();
|
||||
|
||||
recreate_primary_wallet();
|
||||
|
||||
process_deposits();
|
||||
|
||||
//process_withdrawals();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -351,6 +427,11 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vector<object_id_type
|
|||
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_report_down_operation>::value) {
|
||||
approve_proposal( proposal->id );
|
||||
}
|
||||
|
||||
if(proposal->proposed_transaction.operations.size() == 1
|
||||
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::son_wallet_update_operation>::value) {
|
||||
approve_proposal( proposal->id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -384,8 +465,6 @@ void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options:
|
|||
{
|
||||
ilog("peerplays sidechain plugin: plugin_initialize()");
|
||||
my->plugin_initialize(options);
|
||||
database().applied_block.connect( [&]( const signed_block& b){ my->on_block_applied(b); } );
|
||||
database().new_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) { my->on_objects_new(ids); });
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin::plugin_startup()
|
||||
|
|
@ -394,5 +473,25 @@ void peerplays_sidechain_plugin::plugin_startup()
|
|||
my->plugin_startup();
|
||||
}
|
||||
|
||||
son_id_type peerplays_sidechain_plugin::get_son_id()
|
||||
{
|
||||
return my->get_son_id();
|
||||
}
|
||||
|
||||
son_object peerplays_sidechain_plugin::get_son_object()
|
||||
{
|
||||
return my->get_son_object();
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin::is_active_son()
|
||||
{
|
||||
return my->is_active_son();
|
||||
}
|
||||
|
||||
std::map<chain::public_key_type, fc::ecc::private_key>& peerplays_sidechain_plugin::get_private_keys()
|
||||
{
|
||||
return my->get_private_keys();
|
||||
}
|
||||
|
||||
} } // graphene::peerplays_sidechain
|
||||
|
||||
|
|
|
|||
|
|
@ -6,21 +6,26 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_handler::sidechain_net_handler(std::shared_ptr<graphene::chain::database> db, const boost::program_options::variables_map& options) :
|
||||
database(db)
|
||||
sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) :
|
||||
plugin(_plugin),
|
||||
database(_plugin.database())
|
||||
{
|
||||
}
|
||||
|
||||
sidechain_net_handler::~sidechain_net_handler() {
|
||||
}
|
||||
|
||||
graphene::peerplays_sidechain::sidechain_type sidechain_net_handler::get_sidechain() {
|
||||
return sidechain;
|
||||
}
|
||||
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_addresses() {
|
||||
std::vector<std::string> result;
|
||||
|
||||
switch (sidechain) {
|
||||
case sidechain_type::bitcoin:
|
||||
{
|
||||
const auto& sidechain_addresses_idx = database->get_index_type<sidechain_address_index>();
|
||||
const auto& sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get<by_sidechain>();
|
||||
const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
#include <fc/network/ip.hpp>
|
||||
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
|
|
@ -129,6 +132,41 @@ void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex )
|
|||
}
|
||||
}
|
||||
|
||||
std::string bitcoin_rpc_client::add_multisig_address( const std::vector<std::string> public_keys )
|
||||
{
|
||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", \"method\": \"addmultisigaddress\", \"params\": [");
|
||||
std::string params = "2, [";
|
||||
std::string pubkeys = "";
|
||||
for (std::string pubkey : public_keys) {
|
||||
if (!pubkeys.empty()) {
|
||||
pubkeys = pubkeys + ",";
|
||||
}
|
||||
pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\"");
|
||||
}
|
||||
params = params + pubkeys + std::string("]");
|
||||
body = body + params + std::string("] }");
|
||||
|
||||
const auto reply = send_post_request( body );
|
||||
|
||||
if( reply.body.empty() )
|
||||
return "";
|
||||
|
||||
std::string reply_str( reply.body.begin(), reply.body.end() );
|
||||
|
||||
std::stringstream ss(reply_str);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json( ss, json );
|
||||
|
||||
if( reply.status == 200 ) {
|
||||
return reply_str;
|
||||
}
|
||||
|
||||
if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
|
||||
wlog( "BTC multisig address creation failed! Reply: ${msg}", ("msg", reply_str) );
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool bitcoin_rpc_client::connection_is_not_defined() const
|
||||
{
|
||||
return ip.empty() || rpc_port == 0 || user.empty() || password.empty();
|
||||
|
|
@ -186,8 +224,8 @@ void zmq_listener::handle_zmq() {
|
|||
|
||||
// =============================================================================
|
||||
|
||||
sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(std::shared_ptr<graphene::chain::database> db, const boost::program_options::variables_map& options) :
|
||||
sidechain_net_handler(db, options) {
|
||||
sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) :
|
||||
sidechain_net_handler(_plugin, options) {
|
||||
sidechain = sidechain_type::bitcoin;
|
||||
|
||||
ip = options.at("bitcoin-node-ip").as<std::string>();
|
||||
|
|
@ -215,6 +253,57 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(std::shared_ptr<gra
|
|||
sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() {
|
||||
}
|
||||
|
||||
void sidechain_net_handler_bitcoin::recreate_primary_wallet() {
|
||||
const auto& idx_swi = database.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx_swi.rbegin();
|
||||
if (obj != idx_swi.rend()) {
|
||||
|
||||
if ((obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) ||
|
||||
(obj->addresses.at(sidechain_type::bitcoin).empty())) {
|
||||
|
||||
const chain::global_property_object& gpo = database.get_global_properties();
|
||||
|
||||
auto active_sons = gpo.active_sons;
|
||||
vector<string> son_pubkeys_bitcoin;
|
||||
for ( const son_info& si : active_sons ) {
|
||||
son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin));
|
||||
}
|
||||
string reply_str = create_multisignature_wallet(son_pubkeys_bitcoin);
|
||||
|
||||
ilog(reply_str);
|
||||
|
||||
std::stringstream ss(reply_str);
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json( ss, pt );
|
||||
if( pt.count( "error" ) && pt.get_child( "error" ).empty() ) {
|
||||
ilog(__FUNCTION__);
|
||||
|
||||
std::stringstream res;
|
||||
boost::property_tree::json_parser::write_json(res, pt.get_child("result"));
|
||||
|
||||
son_wallet_update_operation op;
|
||||
op.payer = gpo.parameters.get_son_btc_account_id();
|
||||
op.son_wallet_id = (*obj).id;
|
||||
op.sidechain = sidechain_type::bitcoin;
|
||||
op.address = res.str();
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_son_object().son_account;
|
||||
proposal_op.proposed_ops.push_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 = database.create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op);
|
||||
try {
|
||||
database.push_transaction(trx);
|
||||
} catch(fc::exception e){
|
||||
ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_bitcoin::connection_is_not_defined() const
|
||||
{
|
||||
return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined();
|
||||
|
|
@ -222,7 +311,7 @@ bool sidechain_net_handler_bitcoin::connection_is_not_defined() const
|
|||
|
||||
std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector<std::string> public_keys )
|
||||
{
|
||||
return "";
|
||||
return bitcoin_client->add_multisig_address(public_keys);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount )
|
||||
|
|
@ -248,7 +337,7 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data
|
|||
if( block != "" ) {
|
||||
const auto& vins = extract_info_from_block( block );
|
||||
|
||||
const auto& sidechain_addresses_idx = database->get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_address>();
|
||||
const auto& sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_address>();
|
||||
|
||||
for( const auto& v : vins ) {
|
||||
const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain_type::bitcoin, v.address));
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_manager::sidechain_net_manager(std::shared_ptr<graphene::chain::database> db) :
|
||||
database(db)
|
||||
sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin& _plugin) :
|
||||
plugin(_plugin),
|
||||
database(_plugin.database())
|
||||
{
|
||||
ilog(__FUNCTION__);
|
||||
}
|
||||
|
|
@ -22,10 +24,10 @@ bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type s
|
|||
|
||||
switch (sidechain) {
|
||||
case sidechain_type::bitcoin: {
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_bitcoin(database, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_bitcoin(plugin, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
|
|
@ -34,5 +36,11 @@ bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type s
|
|||
return ret_val;
|
||||
}
|
||||
|
||||
void sidechain_net_manager::recreate_primary_wallet() {
|
||||
for ( size_t i = 0; i < net_handlers.size(); i++ ) {
|
||||
net_handlers.at(i)->recreate_primary_wallet();
|
||||
}
|
||||
}
|
||||
|
||||
} } // graphene::peerplays_sidechain
|
||||
|
||||
|
|
|
|||
|
|
@ -1390,6 +1390,26 @@ class wallet_api
|
|||
*/
|
||||
map<string, son_id_type> list_active_sons();
|
||||
|
||||
/**
|
||||
* @brief Get active SON wallet
|
||||
* @return Active SON wallet object
|
||||
*/
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
|
||||
/**
|
||||
* @brief Get SON wallet that was active for a given time point
|
||||
* @param time_point Time point
|
||||
* @return SON wallet object, for the wallet that was active for a given time point
|
||||
*/
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
||||
|
||||
/**
|
||||
* @brief Get full list of SON wallets
|
||||
* @param limit Maximum number of results to return
|
||||
* @return A list of SON wallet objects
|
||||
*/
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
||||
|
||||
/** Adds sidechain address owned by the given account for a given sidechain.
|
||||
*
|
||||
* An account can have at most one sidechain address for one sidechain.
|
||||
|
|
@ -2221,6 +2241,9 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(delete_son)
|
||||
(list_sons)
|
||||
(list_active_sons)
|
||||
(get_active_son_wallet)
|
||||
(get_son_wallet_by_time_point)
|
||||
(get_son_wallets)
|
||||
(add_sidechain_address)
|
||||
(update_sidechain_address)
|
||||
(delete_sidechain_address)
|
||||
|
|
|
|||
|
|
@ -2006,6 +2006,21 @@ public:
|
|||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
optional<son_wallet_object> get_active_son_wallet()
|
||||
{ try {
|
||||
return _remote_db->get_active_son_wallet();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{ try {
|
||||
return _remote_db->get_son_wallet_by_time_point(time_point);
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit)
|
||||
{ try {
|
||||
return _remote_db->get_son_wallets(limit);
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
signed_transaction add_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
string address,
|
||||
|
|
@ -4470,6 +4485,21 @@ map<string, son_id_type> wallet_api::list_active_sons()
|
|||
return my->list_active_sons();
|
||||
}
|
||||
|
||||
optional<son_wallet_object> wallet_api::get_active_son_wallet()
|
||||
{
|
||||
return my->get_active_son_wallet();
|
||||
}
|
||||
|
||||
optional<son_wallet_object> wallet_api::get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{
|
||||
return my->get_son_wallet_by_time_point(time_point);
|
||||
}
|
||||
|
||||
vector<optional<son_wallet_object>> wallet_api::get_son_wallets(uint32_t limit)
|
||||
{
|
||||
return my->get_son_wallets(limit);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::add_sidechain_address(string account,
|
||||
peerplays_sidechain::sidechain_type sidechain,
|
||||
string address,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include <fc/crypto/digest.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
|
@ -265,11 +266,11 @@ void database_fixture::verify_asset_supplies( const database& db )
|
|||
total_balances[betting_market_group.asset_id] += o.fees_collected;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint64_t sweeps_vestings = 0;
|
||||
for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() )
|
||||
sweeps_vestings += svbo.balance;
|
||||
|
||||
|
||||
total_balances[db.get_global_properties().parameters.sweeps_distribution_asset()] += sweeps_vestings / SWEEPS_VESTING_BALANCE_MULTIPLIER;
|
||||
total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget;
|
||||
total_balances[asset_id_type()] += db.get_dynamic_global_properties().son_budget;
|
||||
|
|
@ -413,6 +414,23 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i
|
|||
generate_block(skip);
|
||||
}
|
||||
|
||||
bool database_fixture::generate_maintenance_block() {
|
||||
try {
|
||||
fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
|
||||
uint32_t skip = ~database::skip_fork_db;
|
||||
auto maint_time = db.get_dynamic_global_properties().next_maintenance_time;
|
||||
auto slots_to_miss = db.get_slot_at_time(maint_time);
|
||||
db.generate_block(db.get_slot_time(slots_to_miss),
|
||||
db.get_scheduled_witness(slots_to_miss),
|
||||
committee_key,
|
||||
skip);
|
||||
return true;
|
||||
} catch (std::exception& e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
account_create_operation database_fixture::make_account(
|
||||
const std::string& name /* = "nathan" */,
|
||||
public_key_type key /* = key_id_type() */
|
||||
|
|
@ -731,7 +749,7 @@ const witness_object& database_fixture::create_witness( const account_object& ow
|
|||
witness_create_operation op;
|
||||
op.witness_account = owner.id;
|
||||
op.block_signing_key = signing_private_key.get_public_key();
|
||||
|
||||
|
||||
secret_hash_type::encoder enc;
|
||||
fc::raw::pack(enc, signing_private_key);
|
||||
fc::raw::pack(enc, secret_hash_type());
|
||||
|
|
@ -1116,12 +1134,12 @@ int64_t database_fixture::get_balance( const account_object& account, const asse
|
|||
}
|
||||
|
||||
int64_t database_fixture::get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type,
|
||||
account_id_type dividend_holder_account_id,
|
||||
asset_id_type dividend_payout_asset_type) const
|
||||
account_id_type dividend_holder_account_id,
|
||||
asset_id_type dividend_payout_asset_type) const
|
||||
{
|
||||
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index =
|
||||
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index =
|
||||
db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>();
|
||||
auto pending_payout_iter =
|
||||
auto pending_payout_iter =
|
||||
pending_payout_balance_index.indices().get<by_dividend_payout_account>().find(boost::make_tuple(dividend_holder_asset_type, dividend_payout_asset_type, dividend_holder_account_id));
|
||||
if (pending_payout_iter == pending_payout_balance_index.indices().get<by_dividend_payout_account>().end())
|
||||
return 0;
|
||||
|
|
@ -1342,7 +1360,7 @@ void database_fixture::delete_sport(sport_id_type sport_id)
|
|||
sport_delete_op.sport_id = sport_id;
|
||||
process_operation_by_witnesses(sport_delete_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (sport_id) ) }
|
||||
|
||||
|
||||
const event_group_object& database_fixture::create_event_group(internationalized_string_type name, sport_id_type sport_id)
|
||||
{ try {
|
||||
event_group_create_operation event_group_create_op;
|
||||
|
|
@ -1372,7 +1390,7 @@ void database_fixture::delete_event_group(event_group_id_type event_group_id)
|
|||
process_operation_by_witnesses(event_group_delete_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (event_group_id) )
|
||||
}
|
||||
|
||||
|
||||
void database_fixture::try_update_event_group(event_group_id_type event_group_id,
|
||||
fc::optional<object_id_type> sport_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
|
|
@ -1412,7 +1430,7 @@ void database_fixture::update_event_impl(event_id_type event_id,
|
|||
fc::optional<object_id_type> event_group_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<event_status> status,
|
||||
fc::optional<event_status> status,
|
||||
bool force)
|
||||
{ try {
|
||||
event_update_operation event_update_op;
|
||||
|
|
@ -1449,9 +1467,9 @@ void database_fixture::update_betting_market_rules(betting_market_rules_id_type
|
|||
process_operation_by_witnesses(betting_market_rules_update_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (name)(description) ) }
|
||||
|
||||
const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
asset_id_type asset_id,
|
||||
bool never_in_play,
|
||||
uint32_t delay_before_settling)
|
||||
|
|
@ -1521,7 +1539,7 @@ void database_fixture::update_betting_market(betting_market_id_type betting_mark
|
|||
bet_place_op.amount_to_bet = amount_to_bet;
|
||||
bet_place_op.backer_multiplier = backer_multiplier;
|
||||
bet_place_op.back_or_lay = back_or_lay;
|
||||
|
||||
|
||||
trx.operations.push_back(bet_place_op);
|
||||
trx.validate();
|
||||
processed_transaction ptx = db.push_transaction(trx, ~0);
|
||||
|
|
|
|||
|
|
@ -199,6 +199,12 @@ struct database_fixture {
|
|||
*/
|
||||
void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0);
|
||||
|
||||
///////////
|
||||
/// @brief Skip intermediate blocks, and generate a maintenance block
|
||||
/// @returns true on success
|
||||
///////////
|
||||
bool generate_maintenance_block();
|
||||
|
||||
account_create_operation make_account(
|
||||
const std::string& name = "nathan",
|
||||
public_key_type = public_key_type()
|
||||
|
|
@ -295,7 +301,7 @@ struct database_fixture {
|
|||
int64_t get_balance( account_id_type account, asset_id_type a )const;
|
||||
int64_t get_balance( const account_object& account, const asset_object& a )const;
|
||||
int64_t get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type,
|
||||
account_id_type dividend_holder_account_id,
|
||||
account_id_type dividend_holder_account_id,
|
||||
asset_id_type dividend_payout_asset_type) const;
|
||||
vector< operation_history_object > get_operation_history( account_id_type account_id )const;
|
||||
void process_operation_by_witnesses(operation op);
|
||||
|
|
@ -321,7 +327,7 @@ struct database_fixture {
|
|||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<event_status> status,
|
||||
bool force);
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag,
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag,
|
||||
(required (event_id, (event_id_type)))
|
||||
(optional (event_group_id, (fc::optional<object_id_type>), fc::optional<object_id_type>())
|
||||
(name, (fc::optional<internationalized_string_type>), fc::optional<internationalized_string_type>())
|
||||
|
|
@ -336,9 +342,9 @@ struct database_fixture {
|
|||
void update_betting_market_rules(betting_market_rules_id_type rules_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> description);
|
||||
const betting_market_group_object& create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
const betting_market_group_object& create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
asset_id_type asset_id,
|
||||
bool never_in_play,
|
||||
uint32_t delay_before_settling);
|
||||
|
|
@ -347,7 +353,7 @@ struct database_fixture {
|
|||
fc::optional<object_id_type> rules_id,
|
||||
fc::optional<betting_market_group_status> status,
|
||||
bool force);
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag,
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag,
|
||||
(required (betting_market_group_id, (betting_market_group_id_type)))
|
||||
(optional (description, (fc::optional<internationalized_string_type>), fc::optional<internationalized_string_type>())
|
||||
(rules_id, (fc::optional<object_id_type>), fc::optional<object_id_type>())
|
||||
|
|
|
|||
225
tests/tests/son_wallet_tests.cpp
Normal file
225
tests/tests/son_wallet_tests.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
using namespace graphene;
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( son_wallet_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("son_wallet_recreate_test");
|
||||
|
||||
generate_blocks(HARDFORK_SON_TIME);
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
ACTORS((alice)(bob));
|
||||
|
||||
upgrade_to_lifetime_member(alice);
|
||||
upgrade_to_lifetime_member(bob);
|
||||
|
||||
transfer( committee_account, alice_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) );
|
||||
transfer( committee_account, bob_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) );
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
std::string test_url = "https://create_son_test";
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit_alice;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer {};
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit_alice = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create payment normal vesting
|
||||
vesting_balance_id_type payment_alice;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment_alice = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// alice becomes son
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address";
|
||||
|
||||
son_create_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.url = test_url;
|
||||
op.deposit = deposit_alice;
|
||||
op.pay_vb = payment_alice;
|
||||
op.signing_key = alice_public_key;
|
||||
op.sidechain_public_keys = sidechain_public_keys;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit_bob;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer {};
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit_bob = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create payment normal vesting
|
||||
vesting_balance_id_type payment_bob ;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment_bob = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// bob becomes son
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address";
|
||||
|
||||
son_create_operation op;
|
||||
op.owner_account = bob_id;
|
||||
op.url = test_url;
|
||||
op.deposit = deposit_bob;
|
||||
op.pay_vb = payment_bob;
|
||||
op.signing_key = bob_public_key;
|
||||
op.sidechain_public_keys = sidechain_public_keys;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
generate_blocks(60);
|
||||
set_expiration(db, trx);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send son_wallet_recreate_operation");
|
||||
|
||||
son_wallet_recreate_operation op;
|
||||
|
||||
op.payer = db.get_global_properties().parameters.get_son_btc_account_id();
|
||||
|
||||
{
|
||||
son_info si;
|
||||
si.son_id = son_id_type(0);
|
||||
si.total_votes = 1000;
|
||||
si.signing_key = alice_public_key;
|
||||
si.sidechain_public_keys[peerplays_sidechain::sidechain_type::bitcoin] = "";
|
||||
op.sons.push_back(si);
|
||||
}
|
||||
|
||||
{
|
||||
son_info si;
|
||||
si.son_id = son_id_type(1);
|
||||
si.total_votes = 1000;
|
||||
si.signing_key = bob_public_key;
|
||||
si.sidechain_public_keys[peerplays_sidechain::sidechain_type::bitcoin] = "";
|
||||
op.sons.push_back(si);
|
||||
}
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check son_wallet_recreate_operation results");
|
||||
|
||||
const auto& idx = db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find(son_wallet_id_type(0));
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_REQUIRE( obj->expires == time_point_sec::maximum() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( son_wallet_update_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("son_wallet_update_test");
|
||||
|
||||
INVOKE(son_wallet_recreate_test);
|
||||
GET_ACTOR(alice);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send son_wallet_update_operation");
|
||||
|
||||
son_wallet_update_operation op;
|
||||
|
||||
op.payer = db.get_global_properties().parameters.get_son_btc_account_id();
|
||||
op.son_wallet_id = son_wallet_id_type(0);
|
||||
op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin;
|
||||
op.address = "bitcoin address";
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
generate_block();
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Check son_wallet_update_operation results");
|
||||
|
||||
const auto& idx = db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find(son_wallet_id_type(0));
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_REQUIRE( obj->addresses.at(graphene::peerplays_sidechain::sidechain_type::bitcoin) == "bitcoin address" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
Reference in a new issue