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:
obucinac 2020-02-04 19:31:45 +01:00 committed by GitHub
parent 6e61d6b055
commit a688bb93ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 895 additions and 468 deletions

View file

@ -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" )

View file

@ -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 //

View file

@ -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 )

View file

@ -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)

View file

@ -117,6 +117,8 @@ add_library( graphene_chain
son_evaluator.cpp
son_object.cpp
son_wallet_evaluator.cpp
sidechain_address_evaluator.cpp
${HEADERS}

View file

@ -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

View file

@ -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(),

View file

@ -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 )

View file

@ -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

View file

@ -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

View file

@ -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) )

View file

@ -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 )

View file

@ -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

View file

@ -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) )

View 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

View file

@ -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"
}
]
}

View file

@ -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

View file

@ -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;

View file

@ -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 );

View file

@ -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;
};

View file

@ -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

View file

@ -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,

View file

@ -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));

View file

@ -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,7 +24,7 @@ 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));
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;
@ -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

View file

@ -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)

View file

@ -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,

View file

@ -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>

View file

@ -51,6 +51,7 @@
#include <fc/crypto/digest.hpp>
#include <fc/smart_ref_impl.hpp>
#include <exception>
#include <iostream>
#include <iomanip>
#include <sstream>
@ -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() */

View file

@ -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()

View 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()