Compare commits

...

25 commits

Author SHA1 Message Date
Srdjan Obucina
1048fb5fb4 Fix PW transfer settling 2020-04-19 05:32:28 +02:00
Srdjan Obucina
c6c862d482 Add check for settle proposal duplicate 2020-04-19 05:00:59 +02:00
Srdjan Obucina
9004325597 Remove logs 2020-04-19 04:40:25 +02:00
Srdjan Obucina
edbf8fea1d Remove logs 2020-04-19 04:37:35 +02:00
Srdjan Obucina
9af7bfc58f Fix withdraw settling 2020-04-19 04:34:46 +02:00
Srdjan Obucina
9c550956dc Deposit/withdraw process and sidechain transaction creation in single proposal 2020-04-19 04:04:37 +02:00
Srdjan Obucina
1caaab2cef Merge branch 'feature/SONs-base' into feature/SON-349 2020-04-18 23:35:30 +02:00
Srdjan Obucina
56ec93e6ce All dev features enabled/disabled with single flag 2020-04-15 19:48:57 +02:00
Srdjan Obucina
b6514577a7 Remove logs 2020-04-15 03:13:17 +02:00
Srdjan Obucina
d937863949 Align with SONs-base 2020-04-15 01:15:34 +02:00
Srdjan Obucina
40fb681162 Merge branch 'feature/SONs-base' into feature/SON-349_SON-350 2020-04-15 00:41:58 +02:00
Srdjan Obucina
0a5ad52ee1 Merge branch 'feature/SON-353' into feature/SON-349_SON-350 2020-04-14 23:26:46 +02:00
Srdjan Obucina
5ec353f46b Remove logs 2020-04-14 20:42:48 +02:00
Srdjan Obucina
5284a1487c Fix transaction validation 2020-04-14 19:04:17 +02:00
Srdjan Obucina
3f71b7de1f Fix segfault 2020-04-14 07:48:59 +02:00
Srdjan Obucina
703a13dab4 Fix transaction verification 2020-04-14 07:27:18 +02:00
Srdjan Obucina
bc25f6d20d Fix transaction verification 2020-04-14 06:59:20 +02:00
Srdjan Obucina
2d079cacf6 Fix build error 2020-04-14 06:05:53 +02:00
Srdjan Obucina
16a090fc5e Merge branch 'feature/SON-353' into feature/SON-349_SON-350 2020-04-14 05:46:49 +02:00
Srdjan Obucina
4507601992 Refactor proposal approvement 2020-04-14 05:27:14 +02:00
Srdjan Obucina
88c98db1f4 Add proposal checks for deposit and withdrawal 2020-04-14 05:15:17 +02:00
Srdjan Obucina
ba6fd8e690 [SON-353] Refactor PW processing, PW transfer fixed 2020-04-14 04:50:29 +02:00
Srdjan Obucina
7dd0afe257 Handle peerplays deposits with transaction settling 2020-04-13 07:07:42 +02:00
Srdjan Obucina
66713f0b48 Separate transaction settling from deposit/withdrawal processing 2020-04-13 01:43:09 +02:00
Srdjan Obucina
c77c19563c [SON-349, SON-350] Delay BTC asset issue/reserve until tx confirmed on sidchain 2020-04-11 03:18:41 +02:00
18 changed files with 524 additions and 224 deletions

2
docs

@ -1 +1 @@
Subproject commit 8d8b69d82482101279460fa02f814d0e4030966f
Subproject commit 740407c22154634aa0881e6b85e19ad0191e8325

View file

@ -272,6 +272,7 @@ void database::initialize_evaluators()
register_evaluator<sidechain_transaction_create_evaluator>();
register_evaluator<sidechain_transaction_sign_evaluator>();
register_evaluator<sidechain_transaction_send_evaluator>();
register_evaluator<sidechain_transaction_settle_evaluator>();
}
void database::initialize_indexes()

View file

@ -347,6 +347,9 @@ struct get_impacted_account_visitor
void operator()( const sidechain_transaction_send_operation& op ) {
_impacted.insert( op.payer );
}
void operator()( const sidechain_transaction_settle_operation& op ) {
_impacted.insert( op.payer );
}
};
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )

View file

@ -159,7 +159,8 @@ namespace graphene { namespace chain {
sidechain_address_delete_operation,
sidechain_transaction_create_operation,
sidechain_transaction_sign_operation,
sidechain_transaction_send_operation
sidechain_transaction_send_operation,
sidechain_transaction_settle_operation
> operation;
/// @} // operations group

View file

@ -50,6 +50,19 @@ namespace graphene { namespace chain {
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
};
struct sidechain_transaction_settle_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 0; };
asset fee;
account_id_type payer;
sidechain_transaction_id_type sidechain_transaction_id;
account_id_type fee_payer()const { return payer; }
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
};
} } // graphene::chain
FC_REFLECT( graphene::chain::sidechain_transaction_create_operation::fee_parameters_type, (fee) )
@ -68,3 +81,7 @@ FC_REFLECT( graphene::chain::sidechain_transaction_send_operation::fee_parameter
FC_REFLECT( graphene::chain::sidechain_transaction_send_operation, (fee)(payer)
(sidechain_transaction_id)
(sidechain_transaction) )
FC_REFLECT( graphene::chain::sidechain_transaction_settle_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::sidechain_transaction_settle_operation, (fee)(payer)
(sidechain_transaction_id) )

View file

@ -31,4 +31,13 @@ public:
object_id_type do_apply(const sidechain_transaction_send_operation& o);
};
class sidechain_transaction_settle_evaluator : public evaluator<sidechain_transaction_settle_evaluator>
{
public:
typedef sidechain_transaction_settle_operation operation_type;
void_result do_evaluate(const sidechain_transaction_settle_operation& o);
object_id_type do_apply(const sidechain_transaction_settle_operation& o);
};
} } // namespace graphene::chain

View file

@ -7,6 +7,14 @@
namespace graphene { namespace chain {
using namespace graphene::db;
enum class sidechain_transaction_status {
invalid,
valid,
complete,
sent,
settled
};
/**
* @class sidechain_transaction_object
* @brief tracks state of sidechain transaction during signing process.
@ -28,14 +36,12 @@ namespace graphene { namespace chain {
uint32_t total_weight = 0;
uint32_t current_weight = 0;
uint32_t threshold = 0;
bool valid = false;
bool complete = false;
bool sent = false;
sidechain_transaction_status status;
};
struct by_object_id;
struct by_sidechain_and_complete;
struct by_sidechain_and_complete_and_sent;
struct by_sidechain_and_status;
using sidechain_transaction_multi_index_type = multi_index_container<
sidechain_transaction_object,
indexed_by<
@ -45,17 +51,10 @@ namespace graphene { namespace chain {
ordered_unique< tag<by_object_id>,
member<sidechain_transaction_object, object_id_type, &sidechain_transaction_object::object_id>
>,
ordered_non_unique< tag<by_sidechain_and_complete>,
ordered_non_unique< tag<by_sidechain_and_status>,
composite_key<sidechain_transaction_object,
member<sidechain_transaction_object, sidechain_type, &sidechain_transaction_object::sidechain>,
member<sidechain_transaction_object, bool, &sidechain_transaction_object::complete>
>
>,
ordered_non_unique< tag<by_sidechain_and_complete_and_sent>,
composite_key<sidechain_transaction_object,
member<sidechain_transaction_object, sidechain_type, &sidechain_transaction_object::sidechain>,
member<sidechain_transaction_object, bool, &sidechain_transaction_object::complete>,
member<sidechain_transaction_object, bool, &sidechain_transaction_object::sent>
member<sidechain_transaction_object, sidechain_transaction_status, &sidechain_transaction_object::status>
>
>
>
@ -63,6 +62,13 @@ namespace graphene { namespace chain {
using sidechain_transaction_index = generic_index<sidechain_transaction_object, sidechain_transaction_multi_index_type>;
} } // graphene::chain
FC_REFLECT_ENUM( graphene::chain::sidechain_transaction_status,
(invalid)
(valid)
(complete)
(sent)
(settled) )
FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db::object ),
(sidechain)
(object_id)
@ -73,6 +79,4 @@ FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db
(total_weight)
(current_weight)
(threshold)
(valid)
(complete)
(sent) )
(status) )

View file

@ -41,9 +41,7 @@ object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_
sto.sidechain_transaction = "";
sto.current_weight = 0;
sto.threshold = sto.total_weight * 2 / 3 + 1;
sto.valid = true;
sto.complete = false;
sto.sent = false;
sto.status = sidechain_transaction_status::valid;
});
return new_sidechain_transaction_object.id;
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
@ -68,9 +66,7 @@ void_result sidechain_transaction_sign_evaluator::do_evaluate(const sidechain_tr
}
FC_ASSERT(expected, "Signer not expected");
FC_ASSERT(sto_obj->valid, "Transaction not valid");
FC_ASSERT(!sto_obj->complete, "Transaction signing completed");
FC_ASSERT(!sto_obj->sent, "Transaction already sent");
FC_ASSERT(sto_obj->status == sidechain_transaction_status::valid, "Invalid transaction status");
return void_result();
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
@ -94,7 +90,9 @@ object_id_type sidechain_transaction_sign_evaluator::do_apply(const sidechain_tr
sto.current_weight = sto.current_weight + sto.signers.at(i).weight;
}
}
sto.complete = (sto.current_weight >= sto.threshold);
if (sto.current_weight >= sto.threshold) {
sto.status = sidechain_transaction_status::complete;
}
});
db().modify(son_obj->statistics(db()), [&](son_statistics_object& sso) {
@ -112,9 +110,7 @@ void_result sidechain_transaction_send_evaluator::do_evaluate(const sidechain_tr
const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id);
FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found");
FC_ASSERT(sto_obj->valid, "Transaction not valid");
FC_ASSERT(sto_obj->complete, "Transaction signing not complete");
FC_ASSERT(!sto_obj->sent, "Transaction already sent");
FC_ASSERT(sto_obj->status == sidechain_transaction_status::complete, "Invalid transaction status");
return void_result();
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
@ -126,7 +122,33 @@ object_id_type sidechain_transaction_send_evaluator::do_apply(const sidechain_tr
db().modify(*sto_obj, [&](sidechain_transaction_object &sto) {
sto.sidechain_transaction = op.sidechain_transaction;
sto.sent = true;
sto.status = sidechain_transaction_status::sent;
});
return op.sidechain_transaction_id;
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
void_result sidechain_transaction_settle_evaluator::do_evaluate(const sidechain_transaction_settle_operation &op)
{ try {
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id);
FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found");
FC_ASSERT(sto_obj->status == sidechain_transaction_status::sent, "Invalid transaction status");
return void_result();
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
object_id_type sidechain_transaction_settle_evaluator::do_apply(const sidechain_transaction_settle_operation &op)
{ try {
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
auto sto_obj = sto_idx.find(op.sidechain_transaction_id);
db().modify(*sto_obj, [&](sidechain_transaction_object &sto) {
sto.status = sidechain_transaction_status::settled;
});
return op.sidechain_transaction_id;

View file

@ -15,12 +15,26 @@ add_library( peerplays_sidechain
bitcoin/sign_bitcoin_transaction.cpp
)
if (SUPPORT_MULTIPLE_SONS)
message ("Multiple SONs per software instance are supported")
target_compile_definitions(peerplays_sidechain PRIVATE SUPPORT_MULTIPLE_SONS)
if (ENABLE_DEV_FEATURES)
set(ENABLE_MULTIPLE_SONS 1)
set(ENABLE_PEERPLAYS_ASSET_DEPOSITS 1)
endif()
unset(SUPPORT_MULTIPLE_SONS)
unset(SUPPORT_MULTIPLE_SONS CACHE)
unset(ENABLE_DEV_FEATURES)
unset(ENABLE_DEV_FEATURES CACHE)
if (ENABLE_MULTIPLE_SONS)
message ("Multiple SONs per software instance are supported")
target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_MULTIPLE_SONS)
endif()
unset(ENABLE_MULTIPLE_SONS)
unset(ENABLE_MULTIPLE_SONS CACHE)
if (ENABLE_PEERPLAYS_ASSET_DEPOSITS)
message ("Depositing Peerplays assets enabled")
target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_PEERPLAYS_ASSET_DEPOSITS)
endif()
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS)
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE)
target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq )
target_include_directories( peerplays_sidechain

View file

@ -35,6 +35,7 @@ public:
void process_withdrawals();
void process_sidechain_transactions();
void send_sidechain_transactions();
void settle_sidechain_transactions();
virtual bool process_proposal(const proposal_object &po) = 0;
virtual void process_primary_wallet() = 0;
@ -43,6 +44,7 @@ public:
virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0;
virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
virtual std::string send_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
virtual int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
protected:
peerplays_sidechain_plugin &plugin;

View file

@ -92,6 +92,7 @@ public:
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto);
private:
std::string ip;

View file

@ -20,6 +20,7 @@ public:
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto);
private:
void on_applied_block(const signed_block &b);

View file

@ -22,6 +22,7 @@ public:
void process_withdrawals();
void process_sidechain_transactions();
void send_sidechain_transactions();
void settle_sidechain_transactions();
private:
peerplays_sidechain_plugin &plugin;

View file

@ -58,6 +58,7 @@ public:
void process_withdrawals();
void process_sidechain_transactions();
void send_sidechain_transactions();
void settle_sidechain_transactions();
private:
peerplays_sidechain_plugin &plugin;
@ -142,8 +143,10 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
boost::insert(sons, fc::json::from_string(options.at("son-ids").as<string>()).as<vector<chain::son_id_type>>(5));
config_ready_son = config_ready_son && !sons.empty();
#ifndef SUPPORT_MULTIPLE_SONS
FC_ASSERT(sons.size() == 1, "Multiple SONs not supported");
#ifndef ENABLE_MULTIPLE_SONS
if (sons.size() > 1) {
FC_THROW("Invalid configuration, multiple SON IDs provided");
}
#endif
if (options.count("peerplays-private-key")) {
@ -343,6 +346,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() {
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_id), op);
fc::future<bool> fut = fc::async([&]() {
try {
trx.validate();
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -414,6 +418,8 @@ void peerplays_sidechain_plugin_impl::son_processing() {
process_sidechain_transactions();
send_sidechain_transactions();
settle_sidechain_transactions();
}
}
}
@ -450,6 +456,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() {
chain::signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), puo);
fc::future<bool> fut = fc::async([&]() {
try {
trx.validate();
plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -525,6 +532,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op);
fc::future<bool> fut = fc::async([&]() {
try {
trx.validate();
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -559,6 +567,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op);
fc::future<bool> fut = fc::async([&]() {
try {
trx.validate();
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -599,6 +608,10 @@ void peerplays_sidechain_plugin_impl::send_sidechain_transactions() {
net_manager->send_sidechain_transactions();
}
void peerplays_sidechain_plugin_impl::settle_sidechain_transactions() {
net_manager->settle_sidechain_transactions();
}
void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) {
if (first_block_skipped) {
schedule_son_processing();

View file

@ -92,11 +92,6 @@ bool sidechain_net_handler::proposal_exists(int32_t operation_tag, const object_
break;
}
case chain::operation::tag<chain::sidechain_transaction_create_operation>::value: {
result = (op_obj_idx_0.get<sidechain_transaction_create_operation>().object_id == object_id);
break;
}
default:
return false;
}
@ -116,11 +111,12 @@ bool sidechain_net_handler::approve_proposal(const proposal_id_type &proposal_id
op.proposal = proposal_id;
op.active_approvals_to_add = {plugin.get_son_object(son_id).son_account};
signed_transaction tx = database.create_signed_transaction(plugin.get_private_key(son_id), op);
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op);
try {
database.push_transaction(tx, database::validation_steps::skip_block_size_check);
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(tx));
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
return true;
} catch (fc::exception e) {
elog("Sending approval from ${son_id} for proposal ${proposal_id} failed with exception ${e}",
@ -151,8 +147,18 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
fc::to_string(btc_asset_id.type_id) + "." +
fc::to_string((uint64_t)btc_asset_id.instance);
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
// Accepts BTC and peerplays asset deposits
bool deposit_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) != 0));
bool withdraw_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0));
#else
// Accepts BTC deposits only
bool deposit_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare("BTC") == 0));
bool withdraw_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0));
#endif
// Deposit request
if ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) != 0)) {
if (deposit_condition) {
for (son_id_type son_id : plugin.get_sons()) {
if (plugin.is_active_son(son_id)) {
@ -175,6 +181,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op);
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -187,7 +194,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
}
// Withdrawal request
if ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0)) {
if (withdraw_condition) {
// BTC Payout only (for now)
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain>();
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin));
@ -216,6 +223,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op);
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -226,8 +234,6 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
}
return;
}
FC_ASSERT(false, "Invalid sidechain event");
}
void sidechain_net_handler::process_proposals() {
@ -242,8 +248,6 @@ void sidechain_net_handler::process_proposals() {
const auto po = idx.find(proposal_id);
if (po != idx.end()) {
ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", (*po).id)("son_id", plugin.get_current_son_id()));
if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) {
continue;
}
@ -258,6 +262,14 @@ void sidechain_net_handler::process_proposals() {
op_obj_idx_0 = po->proposed_transaction.operations[0];
}
int32_t op_idx_1 = -1;
chain::operation op_obj_idx_1;
if (po->proposed_transaction.operations.size() >= 2) {
op_idx_1 = po->proposed_transaction.operations[1].which();
op_obj_idx_1 = po->proposed_transaction.operations[1];
}
switch (op_idx_0) {
case chain::operation::tag<chain::son_wallet_update_operation>::value: {
should_process = (op_obj_idx_0.get<son_wallet_update_operation>().sidechain == sidechain);
@ -279,14 +291,18 @@ void sidechain_net_handler::process_proposals() {
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto swwo = idx.find(swwo_id);
if (swwo != idx.end()) {
should_process = (swwo->sidechain == sidechain);
should_process = (swwo->withdraw_sidechain == sidechain);
}
break;
}
case chain::operation::tag<chain::sidechain_transaction_create_operation>::value: {
sidechain_type sc = op_obj_idx_0.get<sidechain_transaction_create_operation>().sidechain;
should_process = (sc == sidechain);
case chain::operation::tag<chain::sidechain_transaction_settle_operation>::value: {
sidechain_transaction_id_type st_id = op_obj_idx_0.get<sidechain_transaction_settle_operation>().sidechain_transaction_id;
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_id>();
const auto sto = idx.find(st_id);
if (sto != idx.end()) {
should_process = (sto->sidechain == sidechain);
}
break;
}
@ -298,16 +314,10 @@ void sidechain_net_handler::process_proposals() {
}
if (should_process) {
ilog("Proposal ${po} will be processed by sidechain handler ${sidechain}", ("po", (*po).id)("sidechain", sidechain));
bool should_approve = process_proposal(*po);
if (should_approve) {
ilog("Proposal ${po} will be approved", ("po", *po));
approve_proposal(po->id, plugin.get_current_son_id());
} else {
ilog("Proposal ${po} is not approved", ("po", (*po).id));
}
} else {
ilog("Proposal ${po} will not be processed by sidechain handler ${sidechain}", ("po", (*po).id)("sidechain", sidechain));
}
}
}
@ -327,8 +337,9 @@ void sidechain_net_handler::process_deposits() {
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) {
if (swdo.id == object_id_type(0, 0, 0))
if (swdo.id == object_id_type(0, 0, 0)) {
return;
}
ilog("Deposit to process: ${swdo}", ("swdo", swdo));
@ -338,36 +349,6 @@ void sidechain_net_handler::process_deposits() {
wlog("Deposit not processed: ${swdo}", ("swdo", swdo));
return;
}
const chain::global_property_object &gpo = database.get_global_properties();
son_wallet_deposit_process_operation swdp_op;
swdp_op.payer = gpo.parameters.son_account();
swdp_op.son_wallet_deposit_id = swdo.id;
asset_issue_operation ai_op;
ai_op.fee = asset(2001000);
ai_op.issuer = gpo.parameters.son_account();
price btc_price = database.get<asset_object>(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate;
ai_op.asset_to_issue = asset(swdo.peerplays_asset.amount * btc_price.quote.amount / btc_price.base.amount, database.get_global_properties().parameters.btc_asset());
ai_op.issue_to_account = swdo.peerplays_from;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
proposal_op.proposed_ops.emplace_back(swdp_op);
proposal_op.proposed_ops.emplace_back(ai_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_key(plugin.get_current_son_id()), proposal_op);
trx.validate();
try {
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
} catch (fc::exception e) {
elog("Sending proposal for son wallet deposit process operation failed with exception ${e}", ("e", e.what()));
}
});
}
@ -380,8 +361,9 @@ void sidechain_net_handler::process_withdrawals() {
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) {
if (swwo.id == object_id_type(0, 0, 0))
if (swwo.id == object_id_type(0, 0, 0)) {
return;
}
ilog("Withdraw to process: ${swwo}", ("swwo", swwo));
@ -391,48 +373,20 @@ void sidechain_net_handler::process_withdrawals() {
wlog("Withdraw not processed: ${swwo}", ("swwo", swwo));
return;
}
const chain::global_property_object &gpo = database.get_global_properties();
son_wallet_withdraw_process_operation swwp_op;
swwp_op.payer = gpo.parameters.son_account();
swwp_op.son_wallet_withdraw_id = swwo.id;
asset_reserve_operation ar_op;
ar_op.fee = asset(2001000);
ar_op.payer = gpo.parameters.son_account();
ar_op.amount_to_reserve = asset(swwo.withdraw_amount, database.get_global_properties().parameters.btc_asset());
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
proposal_op.proposed_ops.emplace_back(swwp_op);
proposal_op.proposed_ops.emplace_back(ar_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_key(plugin.get_current_son_id()), proposal_op);
trx.validate();
try {
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
} catch (fc::exception e) {
elog("Sending proposal for son wallet withdraw process operation failed with exception ${e}", ("e", e.what()));
}
});
}
void sidechain_net_handler::process_sidechain_transactions() {
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_complete>();
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false));
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid));
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
if (sto.id == object_id_type(0, 0, 0))
if (sto.id == object_id_type(0, 0, 0)) {
return;
}
ilog("Sidechain transaction to process: ${sto}", ("sto", sto.id));
bool complete = false;
std::string processed_sidechain_tx = process_sidechain_transaction(sto);
if (processed_sidechain_tx.empty()) {
@ -446,8 +400,8 @@ void sidechain_net_handler::process_sidechain_transactions() {
sts_op.signature = processed_sidechain_tx;
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op);
trx.validate();
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -458,12 +412,13 @@ void sidechain_net_handler::process_sidechain_transactions() {
}
void sidechain_net_handler::send_sidechain_transactions() {
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_complete_and_sent>();
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::complete));
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
if (sto.id == object_id_type(0, 0, 0))
if (sto.id == object_id_type(0, 0, 0)) {
return;
}
ilog("Sidechain transaction to send: ${sto}", ("sto", sto.id));
@ -480,8 +435,8 @@ void sidechain_net_handler::send_sidechain_transactions() {
sts_op.sidechain_transaction = sidechain_transaction;
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op);
trx.validate();
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -491,4 +446,69 @@ void sidechain_net_handler::send_sidechain_transactions() {
});
}
void sidechain_net_handler::settle_sidechain_transactions() {
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::sent));
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
if (sto.id == object_id_type(0, 0, 0)) {
return;
}
if (proposal_exists(chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.id)) {
return;
}
ilog("Sidechain transaction to settle: ${sto}", ("sto", sto.id));
int64_t settle_amount = settle_sidechain_transaction(sto);
if (settle_amount < 0) {
wlog("Sidechain transaction not settled: ${sto}", ("sto", sto.id));
return;
}
const chain::global_property_object &gpo = database.get_global_properties();
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
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);
sidechain_transaction_settle_operation sts_op;
sts_op.payer = gpo.parameters.son_account();
sts_op.sidechain_transaction_id = sto.id;
proposal_op.proposed_ops.emplace_back(sts_op);
if (settle_amount != 0) {
if (sto.object_id.is<son_wallet_deposit_id_type>()) {
asset_issue_operation ai_op;
ai_op.fee = asset(2001000);
ai_op.issuer = gpo.parameters.son_account();
ai_op.asset_to_issue = asset(settle_amount, database.get_global_properties().parameters.btc_asset());
ai_op.issue_to_account = database.get<son_wallet_deposit_object>(sto.object_id).peerplays_from;
proposal_op.proposed_ops.emplace_back(ai_op);
}
if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
asset_reserve_operation ar_op;
ar_op.fee = asset(2001000);
ar_op.payer = gpo.parameters.son_account();
ar_op.amount_to_reserve = asset(settle_amount, database.get_global_properties().parameters.btc_asset());
proposal_op.proposed_ops.emplace_back(ar_op);
}
}
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
} catch (fc::exception e) {
elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what()));
}
});
}
}} // namespace graphene::peerplays_sidechain

View file

@ -1015,6 +1015,8 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
}
case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: {
bool process_ok = false;
bool transaction_ok = false;
son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id;
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
const auto swdo = idx.find(swdo_id);
@ -1035,8 +1037,8 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
std::string tx_txid = tx_json.get<std::string>("result.txid");
uint32_t tx_confirmations = tx_json.get<uint32_t>("result.confirmations");
std::string tx_address = "";
uint64_t tx_amount = 0;
uint64_t tx_vout = 0;
int64_t tx_amount = -1;
int64_t tx_vout = -1;
for (auto &input : tx_json.get_child("result.details")) {
tx_address = input.second.get<std::string>("address");
@ -1050,49 +1052,104 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
}
}
should_approve = (swdo_txid == tx_txid) &&
(swdo_address == tx_address) &&
(swdo_amount == tx_amount) &&
(swdo_vout == tx_vout) &&
(gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations);
process_ok = (swdo_txid == tx_txid) &&
(swdo_address == tx_address) &&
(swdo_amount == tx_amount) &&
(swdo_vout == tx_vout) &&
(gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations);
}
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
const auto st = st_idx.find(object_id);
if (st == st_idx.end()) {
std::string tx_str = "";
if (object_id.is<son_wallet_deposit_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
const auto swdo = idx.find(object_id);
if (swdo != idx.end()) {
tx_str = create_deposit_transaction(*swdo);
}
}
if (object_id.is<son_wallet_withdraw_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto swwo = idx.find(object_id);
if (swwo != idx.end()) {
tx_str = create_withdrawal_transaction(*swwo);
}
}
transaction_ok = (op_tx_str == tx_str);
}
}
should_approve = process_ok &&
transaction_ok;
break;
}
case chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value: {
should_approve = false;
bool process_ok = false;
bool transaction_ok = false;
son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get<son_wallet_withdraw_process_operation>().son_wallet_withdraw_id;
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto swwo = idx.find(swwo_id);
if (swwo != idx.end()) {
uint32_t swwo_block_num = swwo->block_num;
std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id;
uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1));
const auto &block = database.fetch_block_by_number(swwo_block_num);
for (const auto &tx : block->transactions) {
if (tx.id().str() == swwo_peerplays_transaction_id) {
operation op = tx.operations[swwo_op_idx];
transfer_operation t_op = op.get<transfer_operation>();
price asset_price = database.get<asset_object>(t_op.amount.asset_id).options.core_exchange_rate;
asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount);
process_ok = (t_op.to == gpo.parameters.son_account()) &&
(swwo->peerplays_from == t_op.from) &&
(swwo->peerplays_asset == peerplays_asset);
break;
}
}
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
const auto st = st_idx.find(object_id);
if (st == st_idx.end()) {
std::string tx_str = "";
if (object_id.is<son_wallet_withdraw_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto swwo = idx.find(object_id);
if (swwo != idx.end()) {
tx_str = create_withdrawal_transaction(*swwo);
}
}
transaction_ok = (op_tx_str == tx_str);
}
}
should_approve = process_ok &&
transaction_ok;
break;
}
case chain::operation::tag<chain::sidechain_transaction_create_operation>::value: {
object_id_type object_id = op_obj_idx_0.get<sidechain_transaction_create_operation>().object_id;
std::string op_tx_str = op_obj_idx_0.get<sidechain_transaction_create_operation>().transaction;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
const auto st = st_idx.find(object_id);
if (st == st_idx.end()) {
std::string tx_str = "";
if (object_id.is<son_wallet_deposit_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
const auto swdo = idx.find(object_id);
if (swdo != idx.end()) {
tx_str = create_deposit_transaction(*swdo);
}
}
if (object_id.is<son_wallet_withdraw_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto swwo = idx.find(object_id);
if (swwo != idx.end()) {
tx_str = create_withdrawal_transaction(*swwo);
}
}
should_approve = (op_tx_str == tx_str);
}
case chain::operation::tag<chain::sidechain_transaction_settle_operation>::value: {
should_approve = true;
break;
}
@ -1161,6 +1218,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -1204,8 +1262,8 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() {
op.withdraw_address = sao.withdraw_address;
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op);
trx.validate();
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -1228,22 +1286,27 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj
if (!tx_str.empty()) {
const chain::global_property_object &gpo = database.get_global_properties();
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
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);
son_wallet_deposit_process_operation swdp_op;
swdp_op.payer = gpo.parameters.son_account();
swdp_op.son_wallet_deposit_id = swdo.id;
proposal_op.proposed_ops.emplace_back(swdp_op);
sidechain_transaction_create_operation stc_op;
stc_op.payer = gpo.parameters.son_account();
stc_op.object_id = swdo.id;
stc_op.sidechain = sidechain;
stc_op.transaction = tx_str;
stc_op.signers = gpo.active_sons;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
proposal_op.proposed_ops.emplace_back(stc_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_key(plugin.get_current_son_id()), proposal_op);
trx.validate();
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -1267,22 +1330,27 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw
if (!tx_str.empty()) {
const chain::global_property_object &gpo = database.get_global_properties();
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
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);
son_wallet_withdraw_process_operation swwp_op;
swwp_op.payer = gpo.parameters.son_account();
swwp_op.son_wallet_withdraw_id = swwo.id;
proposal_op.proposed_ops.emplace_back(swwp_op);
sidechain_transaction_create_operation stc_op;
stc_op.payer = gpo.parameters.son_account();
stc_op.object_id = swwo.id;
stc_op.sidechain = sidechain;
stc_op.transaction = tx_str;
stc_op.signers = gpo.active_sons;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
proposal_op.proposed_ops.emplace_back(stc_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_key(plugin.get_current_son_id()), proposal_op);
trx.validate();
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -1303,6 +1371,55 @@ std::string sidechain_net_handler_bitcoin::send_sidechain_transaction(const side
return send_transaction(sto);
}
int64_t sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain_transaction_object &sto) {
if (sto.object_id.is<son_wallet_id_type>()) {
return 0;
}
int64_t settle_amount = -1;
if (sto.sidechain_transaction.empty()) {
return settle_amount;
}
std::string tx_str = bitcoin_client->gettransaction(sto.sidechain_transaction, true);
std::stringstream tx_ss(tx_str);
boost::property_tree::ptree tx_json;
boost::property_tree::read_json(tx_ss, tx_json);
if ((tx_json.count("error")) && (!tx_json.get_child("error").empty())) {
return settle_amount;
}
std::string tx_txid = tx_json.get<std::string>("result.txid");
uint32_t tx_confirmations = tx_json.get<uint32_t>("result.confirmations");
std::string tx_address = "";
int64_t tx_amount = -1;
const chain::global_property_object &gpo = database.get_global_properties();
if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) {
if (sto.object_id.is<son_wallet_deposit_id_type>()) {
for (auto &input : tx_json.get_child("result.details")) {
if (input.second.get<std::string>("category") == "receive") {
std::string tx_amount_s = input.second.get<std::string>("amount");
tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end());
tx_amount = std::stoll(tx_amount_s);
break;
}
}
settle_amount = tx_amount;
}
if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
auto swwo = database.get<son_wallet_withdraw_object>(sto.object_id);
settle_amount = swwo.withdraw_amount.value;
}
}
return settle_amount;
}
std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector<son_info> &son_pubkeys) {
using namespace bitcoin;
@ -1321,7 +1438,6 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s
<< "}, \"error\":null}";
std::string res = ss.str();
ilog("Weighted Multisig Address = ${a}", ("a", res));
return res;
}
@ -1452,7 +1568,6 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
return create_transaction(inputs, outputs, redeem_script);
}
// Function to actually create transaction should return transaction string, or empty string in case of failure
std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs, std::string &redeem_script) {
using namespace bitcoin;
@ -1474,12 +1589,9 @@ std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector<
const auto tx = tb.get_transaction();
std::string hex_tx = fc::to_hex(pack(tx));
std::string tx_raw = write_transaction_data(hex_tx, in_amounts, redeem_script);
ilog("Raw transaction ${tx}", ("tx", tx_raw));
return tx_raw;
}
// Adds signature to transaction
// Function to actually add signature should return transaction with added signature string, or empty string in case of failure
std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_transaction_object &sto) {
using namespace bitcoin;
std::string pubkey = plugin.get_current_son_object().sidechain_public_keys.at(sidechain);
@ -1498,15 +1610,11 @@ std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_tran
read_transaction_data(sto.transaction, tx_hex, in_amounts, redeem_script);
ilog("Sign transaction retreived: ${s}", ("s", tx_hex));
bitcoin_transaction tx = unpack(parse_hex(tx_hex));
std::vector<bitcoin::bytes> redeem_scripts(tx.vin.size(), parse_hex(redeem_script));
auto sigs = sign_witness_transaction_part(tx, redeem_scripts, in_amounts, privkey_signing, btc_context(), 1);
std::string tx_signature = write_transaction_signatures(sigs);
ilog("Signatures: son-id = ${son}, pkey = ${prvkey}, tx_signature = ${s}", ("son", plugin.get_current_son_id())("prvkey", prvkey)("s", tx_signature));
return tx_signature;
}
@ -1518,8 +1626,6 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran
read_transaction_data(sto.transaction, tx_hex, in_amounts, redeem_script);
ilog("Send transaction retreived: ${s}", ("s", tx_hex));
bitcoin_transaction tx = unpack(parse_hex(tx_hex));
std::vector<bitcoin::bytes> redeem_scripts(tx.vin.size(), parse_hex(redeem_script));
@ -1547,8 +1653,6 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran
std::string final_tx_hex = fc::to_hex(pack(tx));
std::string res = bitcoin_client->sendrawtransaction(final_tx_hex);
ilog("Send transaction: ${tx}, [${res}]", ("tx", final_tx_hex)("res", res));
return res;
}
@ -1692,5 +1796,4 @@ void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector<object_id
}
// =============================================================================
}} // namespace graphene::peerplays_sidechain

View file

@ -8,6 +8,7 @@
#include <boost/property_tree/ptree.hpp>
#include <fc/crypto/base64.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/log/logger.hpp>
#include <fc/network/ip.hpp>
@ -15,12 +16,26 @@
#include <graphene/chain/protocol/son_wallet.hpp>
#include <graphene/chain/son_info.hpp>
#include <graphene/chain/son_wallet_object.hpp>
#include <graphene/utilities/key_conversion.hpp>
namespace graphene { namespace peerplays_sidechain {
sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
sidechain_net_handler(_plugin, options) {
sidechain = sidechain_type::peerplays;
if (options.count("peerplays-private-key")) {
const std::vector<std::string> pub_priv_keys = options["peerplays-private-key"].as<std::vector<std::string>>();
for (const std::string &itr_key_pair : pub_priv_keys) {
auto key_pair = graphene::app::dejsonify<std::pair<std::string, std::string>>(itr_key_pair, 5);
ilog("Peerplays Public Key: ${public}", ("public", key_pair.first));
if (!key_pair.first.length() || !key_pair.second.length()) {
FC_THROW("Invalid public private key pair.");
}
private_keys[key_pair.first] = key_pair.second;
}
}
database.applied_block.connect([&](const signed_block &b) {
on_applied_block(b);
});
@ -85,37 +100,12 @@ bool sidechain_net_handler_peerplays::process_proposal(const proposal_object &po
}
case chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value: {
son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get<son_wallet_withdraw_process_operation>().son_wallet_withdraw_id;
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto swwo = idx.find(swwo_id);
if (swwo != idx.end()) {
uint32_t swwo_block_num = swwo->block_num;
std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id;
uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1));
const auto &block = database.fetch_block_by_number(swwo_block_num);
for (const auto &tx : block->transactions) {
if (tx.id().str() == swwo_peerplays_transaction_id) {
operation op = tx.operations[swwo_op_idx];
transfer_operation t_op = op.get<transfer_operation>();
price asset_price = database.get<asset_object>(t_op.amount.asset_id).options.core_exchange_rate;
asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount);
should_approve = (t_op.to == gpo.parameters.son_account()) &&
(swwo->peerplays_from == t_op.from) &&
(swwo->peerplays_asset == peerplays_asset);
break;
}
}
}
should_approve = false;
break;
}
case chain::operation::tag<chain::sidechain_transaction_create_operation>::value: {
should_approve = false;
case chain::operation::tag<chain::sidechain_transaction_settle_operation>::value: {
should_approve = true;
break;
}
@ -138,7 +128,55 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() {
}
bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) {
return true;
const chain::global_property_object &gpo = database.get_global_properties();
asset_issue_operation ai_op;
ai_op.issuer = gpo.parameters.son_account();
price btc_price = database.get<asset_object>(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate;
ai_op.asset_to_issue = asset(swdo.peerplays_asset.amount * btc_price.quote.amount / btc_price.base.amount, database.get_global_properties().parameters.btc_asset());
ai_op.issue_to_account = swdo.peerplays_from;
signed_transaction tx;
auto dyn_props = database.get_dynamic_global_properties();
tx.set_reference_block(dyn_props.head_block_id);
tx.set_expiration(database.head_block_time() + gpo.parameters.maximum_time_until_expiration);
tx.operations.push_back(ai_op);
database.current_fee_schedule().set_fee(tx.operations.back());
std::stringstream ss;
fc::raw::pack(ss, tx, 1000);
std::string tx_str = boost::algorithm::hex(ss.str());
if (!tx_str.empty()) {
const chain::global_property_object &gpo = database.get_global_properties();
sidechain_transaction_create_operation stc_op;
stc_op.payer = gpo.parameters.son_account();
stc_op.object_id = swdo.id;
stc_op.sidechain = sidechain;
stc_op.transaction = tx_str;
stc_op.signers = gpo.active_sons;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
proposal_op.proposed_ops.emplace_back(stc_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_key(plugin.get_current_son_id()), proposal_op);
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
return true;
} catch (fc::exception e) {
elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what()));
return false;
}
}
return false;
}
bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) {
@ -146,11 +184,55 @@ bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdr
}
std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const sidechain_transaction_object &sto) {
return sto.transaction;
std::stringstream ss_trx(boost::algorithm::unhex(sto.transaction));
signed_transaction trx;
fc::raw::unpack(ss_trx, trx, 1000);
fc::optional<fc::ecc::private_key> privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain)));
signature_type st = trx.sign(*privkey, database.get_chain_id());
std::stringstream ss_st;
fc::raw::pack(ss_st, st, 1000);
std::string st_str = boost::algorithm::hex(ss_st.str());
return st_str;
}
std::string sidechain_net_handler_peerplays::send_sidechain_transaction(const sidechain_transaction_object &sto) {
return sto.transaction;
std::stringstream ss_trx(boost::algorithm::unhex(sto.transaction));
signed_transaction trx;
fc::raw::unpack(ss_trx, trx, 1000);
for (auto signature : sto.signatures) {
if (!signature.second.empty()) {
std::stringstream ss_st(boost::algorithm::unhex(signature.second));
signature_type st;
fc::raw::unpack(ss_st, st, 1000);
trx.signatures.push_back(st);
trx.signees.clear();
}
}
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
return trx.id().str();
} catch (fc::exception e) {
elog("Sidechain transaction failed with exception ${e}", ("e", e.what()));
return "";
}
return "";
}
int64_t sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidechain_transaction_object &sto) {
int64_t settle_amount = 0;
return settle_amount;
}
void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) {

View file

@ -75,4 +75,10 @@ void sidechain_net_manager::send_sidechain_transactions() {
}
}
void sidechain_net_manager::settle_sidechain_transactions() {
for (size_t i = 0; i < net_handlers.size(); i++) {
net_handlers.at(i)->settle_sidechain_transactions();
}
}
}} // namespace graphene::peerplays_sidechain