Refactor proposal processing

This commit is contained in:
Srdjan Obucina 2020-04-02 04:29:15 +02:00
parent 27f401d24e
commit 63e3b3a420
12 changed files with 256 additions and 67 deletions

View file

@ -234,6 +234,7 @@
#define SON_DEREGISTER_TIME (60*60*12) // 12 Hours in seconds
#define SON_HEARTBEAT_FREQUENCY (60*3) // 3 minutes in seconds
#define SON_DOWN_TIME (60*3*2) // 2 Heartbeats in seconds
#define SON_BITCOIN_MIN_TX_CONFIRMATIONS (1)
#define SON_PAY_TIME (60*60*24) // 1 day
#define SON_PAY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200))
#define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)

View file

@ -58,6 +58,8 @@ namespace graphene { namespace chain {
optional < uint32_t > son_heartbeat_frequency;
optional < uint32_t > son_down_time;
optional < account_id_type > son_account;
optional < uint16_t > son_bitcoin_min_tx_confirmations;
};
struct chain_parameters
@ -159,6 +161,9 @@ namespace graphene { namespace chain {
inline uint16_t son_down_time()const {
return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME;
}
inline uint16_t son_bitcoin_min_tx_confirmations()const {
return extensions.value.son_bitcoin_min_tx_confirmations.valid() ? *extensions.value.son_bitcoin_min_tx_confirmations : SON_BITCOIN_MIN_TX_CONFIRMATIONS;
}
inline uint32_t gpos_period()const {
return extensions.value.gpos_period.valid() ? *extensions.value.gpos_period : GPOS_PERIOD; /// total seconds of current gpos period
}
@ -199,6 +204,7 @@ FC_REFLECT( graphene::chain::parameter_extension,
(son_heartbeat_frequency)
(son_down_time)
(son_account)
(son_bitcoin_min_tx_confirmations)
)
FC_REFLECT( graphene::chain::chain_parameters,

View file

@ -2,6 +2,7 @@
#include <boost/multi_index/composite_key.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/sidechain_defs.hpp>
#include <graphene/chain/son_info.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;

View file

@ -5,8 +5,7 @@
#include <boost/program_options.hpp>
#include <fc/signals.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/sidechain_address_object.hpp>
#include <graphene/chain/sidechain_transaction_object.hpp>
#include <graphene/chain/son_wallet_deposit_object.hpp>
@ -26,13 +25,18 @@ public:
std::vector<std::string> get_sidechain_withdraw_addresses();
std::string get_private_key(std::string public_key);
bool approve_proposal(const proposal_id_type &proposal_id, const son_id_type &son_id);
void sidechain_event_data_received(const sidechain_event_data &sed);
void process_proposals();
void process_active_sons_change();
void process_deposits();
void process_withdrawals();
void process_sidechain_transactions();
void send_sidechain_transactions();
virtual void recreate_primary_wallet() = 0;
virtual bool process_proposal(const proposal_object &po) = 0;
virtual void process_primary_wallet() = 0;
virtual bool process_deposit(const son_wallet_deposit_object &swdo) = 0;
virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0;
virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) = 0;

View file

@ -33,9 +33,10 @@ public:
std::string finalizepsbt(std::string const &tx_psbt);
std::string getaddressinfo(const std::string &address);
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
std::string gettransaction(const std::string &txid, const bool include_watch_only = false);
void importaddress(const std::string &address_or_script);
std::vector<btc_txout> listunspent();
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount);
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
std::string loadwallet(const std::string &filename);
bool sendrawtransaction(const std::string &tx_hex);
std::string signrawtransactionwithwallet(const std::string &tx_hash);
@ -83,7 +84,8 @@ public:
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 process_proposal(const proposal_object &po);
void process_primary_wallet();
bool process_deposit(const son_wallet_deposit_object &swdo);
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete);

View file

@ -13,7 +13,8 @@ public:
sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
virtual ~sidechain_net_handler_peerplays();
void recreate_primary_wallet();
bool process_proposal(const proposal_object &po);
void process_primary_wallet();
bool process_deposit(const son_wallet_deposit_object &swdo);
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete);

View file

@ -16,7 +16,8 @@ public:
virtual ~sidechain_net_manager();
bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options);
void recreate_primary_wallet();
void process_proposals();
void process_active_sons_change();
void process_deposits();
void process_withdrawals();
void process_sidechain_transactions();

View file

@ -50,7 +50,9 @@ public:
void approve_proposals();
void create_son_down_proposals();
void create_son_deregister_proposals();
void recreate_primary_wallet();
void process_proposals();
void process_active_sons_change();
void process_deposits();
void process_withdrawals();
void process_sidechain_transactions();
@ -373,16 +375,16 @@ void peerplays_sidechain_plugin_impl::son_processing() {
("scheduled_son_id", scheduled_son_id)("now", now));
for (son_id_type son_id : plugin.get_sons()) {
current_son_id = son_id;
// These tasks are executed by
// - All active SONs, no matter if scheduled
// - All previously active SONs
approve_proposals();
process_proposals();
process_sidechain_transactions();
if (plugin.is_active_son(son_id)) {
current_son_id = son_id;
// Tasks that are executed by all active SONs, no matter if scheduled
// E.g. sending approvals and signing (only signing that can be done in parallel)
approve_proposals();
process_sidechain_transactions();
// Tasks that are executed by scheduled and active SON only
if (current_son_id == scheduled_son_id) {
@ -390,7 +392,7 @@ void peerplays_sidechain_plugin_impl::son_processing() {
create_son_deregister_proposals();
recreate_primary_wallet();
process_active_sons_change();
process_deposits();
@ -400,13 +402,6 @@ void peerplays_sidechain_plugin_impl::son_processing() {
send_sidechain_transactions();
}
} else {
// Tasks that are executed by previously active SONs
// E.g. sending approvals and signing that SON was required to do while it was active
//approve_leftover_proposals(); ???
//process_leftover_sidechain_transactions(); ???
approve_proposals();
process_sidechain_transactions();
}
}
}
@ -423,26 +418,6 @@ bool peerplays_sidechain_plugin_impl::is_valid_son_proposal(const chain::proposa
if (op_idx_0 == chain::operation::tag<chain::son_delete_operation>::value) {
return is_son_delete_op_valid(op);
}
if (op_idx_0 == chain::operation::tag<chain::son_wallet_update_operation>::value) {
return true;
}
if (op_idx_0 == chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value) {
return true;
}
if (op_idx_0 == chain::operation::tag<chain::sidechain_transaction_create_operation>::value) {
return true;
}
} else if (proposal.proposed_transaction.operations.size() == 2) {
int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which();
int32_t op_idx_1 = proposal.proposed_transaction.operations[1].which();
if ((op_idx_0 == chain::operation::tag<chain::son_wallet_deposit_process_operation>::value) &&
(op_idx_1 == chain::operation::tag<chain::transfer_operation>::value)) {
return true;
}
}
return false;
@ -587,8 +562,12 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
}
}
void peerplays_sidechain_plugin_impl::recreate_primary_wallet() {
net_manager->recreate_primary_wallet();
void peerplays_sidechain_plugin_impl::process_proposals() {
net_manager->process_proposals();
}
void peerplays_sidechain_plugin_impl::process_active_sons_change() {
net_manager->process_active_sons_change();
}
void peerplays_sidechain_plugin_impl::process_deposits() {

View file

@ -2,6 +2,8 @@
#include <fc/log/logger.hpp>
#include <fc/smart_ref_fwd.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/proposal_object.hpp>
namespace graphene { namespace peerplays_sidechain {
@ -51,6 +53,26 @@ std::string sidechain_net_handler::get_private_key(std::string public_key) {
return std::string();
}
bool sidechain_net_handler::approve_proposal(const proposal_id_type &proposal_id, const son_id_type &son_id) {
proposal_update_operation op;
op.fee_paying_account = plugin.get_son_object(son_id).son_account;
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);
try {
database.push_transaction(tx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(tx));
return true;
} catch (fc::exception e) {
elog("Sending approval from ${son_id} for proposal ${proposal_id} failed with exception ${e}",
("son_id", son_id)("proposal_id", proposal_id)("e", e.what()));
return false;
}
}
void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data &sed) {
ilog("sidechain_event_data:");
ilog(" timestamp: ${timestamp}", ("timestamp", sed.timestamp));
@ -142,6 +164,86 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
FC_ASSERT(false, "Invalid sidechain event");
}
void sidechain_net_handler::process_proposals() {
const auto &idx = database.get_index_type<proposal_index>().indices().get<by_id>();
vector<proposal_id_type> proposals;
for (const auto &proposal : idx) {
proposals.push_back(proposal.id);
}
for (const auto proposal_id : proposals) {
const auto &idx = database.get_index_type<proposal_index>().indices().get<by_id>();
const auto po = idx.find(proposal_id);
if (po != idx.end()) {
if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) {
continue;
}
bool should_process = false;
if (po->proposed_transaction.operations.size() == 1) {
int32_t op_idx_0 = po->proposed_transaction.operations[0].which();
chain::operation op = po->proposed_transaction.operations[0];
switch (op_idx_0) {
case chain::operation::tag<chain::son_wallet_update_operation>::value: {
should_process = true;
//son_wallet_id_type swo_id = op.get<son_wallet_update_operation>().son_wallet_id;
//const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
//const auto swo = idx.find(swo_id);
//if (swo != idx.end()) {
// should_process = (swo->sidechain == sidechain);
//}
break;
}
case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: {
son_wallet_deposit_id_type swdo_id = op.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);
if (swdo != idx.end()) {
should_process = (swdo->sidechain == sidechain);
}
break;
}
case chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value: {
son_wallet_withdraw_id_type swwo_id = op.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()) {
should_process = (swwo->sidechain == sidechain);
}
break;
}
case chain::operation::tag<chain::sidechain_transaction_create_operation>::value: {
sidechain_type sc = op.get<sidechain_transaction_create_operation>().sidechain;
should_process = (sc == sidechain);
break;
}
default:
should_process = false;
}
}
if (should_process) {
bool should_approve = process_proposal(*po);
if (should_approve) {
approve_proposal(po->id, plugin.get_current_son_id());
}
}
}
}
}
void sidechain_net_handler::process_active_sons_change() {
process_primary_wallet();
}
void sidechain_net_handler::process_deposits() {
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) {
return;
@ -166,16 +268,16 @@ void sidechain_net_handler::process_deposits() {
swdp_op.payer = gpo.parameters.son_account();
swdp_op.son_wallet_deposit_id = swdo.id;
transfer_operation t_op;
t_op.fee = asset(2000000);
t_op.from = swdo.peerplays_to; // gpo.parameters.son_account()
t_op.to = swdo.peerplays_from;
t_op.amount = swdo.peerplays_asset;
//transfer_operation t_op;
//t_op.fee = asset(2000000);
//t_op.from = swdo.peerplays_to; // gpo.parameters.son_account()
//t_op.to = swdo.peerplays_from;
//t_op.amount = swdo.peerplays_asset;
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(t_op);
//proposal_op.proposed_ops.emplace_back(t_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);
@ -298,8 +400,12 @@ void sidechain_net_handler::send_sidechain_transactions() {
});
}
void sidechain_net_handler::recreate_primary_wallet() {
FC_ASSERT(false, "recreate_primary_wallet not implemented");
bool sidechain_net_handler::process_proposal(const proposal_object &po) {
FC_ASSERT(false, "process_proposal not implemented");
}
void sidechain_net_handler::process_primary_wallet() {
FC_ASSERT(false, "process_primary_wallet not implemented");
}
bool sidechain_net_handler::process_deposit(const son_wallet_deposit_object &swdo) {

View file

@ -421,10 +421,36 @@ std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t
return "";
}
std::string bitcoin_rpc_client::gettransaction(const std::string &txid, const bool include_watch_only) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"gettransaction\", \"method\": "
"\"gettransaction\", \"params\": [\"" +
txid + "\"] }");
const auto reply = send_post_request(body, true);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return "";
}
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
if (reply.status == 200) {
return ss.str();
}
if (json.count("error") && !json.get_child("error").empty()) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
}
void bitcoin_rpc_client::importaddress(const std::string &address_or_script) {
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", "
"\"method\": \"importaddress\", \"params\": [") +
std::string("\"") + address_or_script + std::string("\"") + std::string("] }");
"\"method\": \"importaddress\", \"params\": [" +
std::string("\"") + address_or_script + std::string("\"") + std::string("] }"));
const auto reply = send_post_request(body);
@ -444,9 +470,10 @@ void bitcoin_rpc_client::importaddress(const std::string &address_or_script) {
}
}
std::vector<btc_txout> bitcoin_rpc_client::listunspent() {
std::vector<btc_txout> bitcoin_rpc_client::listunspent(const uint32_t minconf, const uint32_t maxconf) {
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": "
"\"listunspent\", \"params\": [] }");
"\"listunspent\", \"params\": [" +
std::to_string(minconf) + "," + std::to_string(maxconf) + "] }");
const auto reply = send_post_request(body);
@ -477,14 +504,15 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent() {
return result;
}
std::vector<btc_txout> bitcoin_rpc_client::listunspent_by_address_and_amount(const std::string &address, double minimum_amount) {
std::vector<btc_txout> bitcoin_rpc_client::listunspent_by_address_and_amount(const std::string &address, double minimum_amount, const uint32_t minconf, const uint32_t maxconf) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": "
"\"listunspent\", \"params\": [");
body += std::string("1,999999,[\"");
"\"listunspent\", \"params\": [" +
std::to_string(minconf) + "," + std::to_string(maxconf) + ",");
body += std::string("[\"");
body += address;
body += std::string("\"],true,{\"minimumAmount\":");
body += std::to_string(minimum_amount);
body += std::string("}] }");
body += std::string("} ] }");
const auto reply = send_post_request(body);
@ -840,7 +868,57 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() {
}
}
void sidechain_net_handler_bitcoin::recreate_primary_wallet() {
bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) {
bool should_approve = false;
std::string txid = "";
std::string cmp_str_object = "";
std::string cmp_str_tx = "";
if (po.proposed_transaction.operations.size() == 1) {
int32_t op_idx_0 = po.proposed_transaction.operations[0].which();
chain::operation op = po.proposed_transaction.operations[0];
switch (op_idx_0) {
case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: {
son_wallet_deposit_id_type swdo_id = op.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);
if (swdo != idx.end()) {
std::string txid = swdo->sidechain_transaction_id;
std::string suid = swdo->sidechain_uid;
std::string nvout = suid.substr(suid.find_last_of("-") + 1);
uint64_t deposit_amount = swdo->sidechain_amount.value;
std::string tx_str = bitcoin_client->gettransaction(txid);
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()) {
should_approve = true;
}
}
break;
}
case chain::operation::tag<chain::sidechain_transaction_create_operation>::value: {
should_approve = true;
break;
}
default:
should_approve = false;
}
}
return should_approve;
}
void sidechain_net_handler_bitcoin::process_primary_wallet() {
const auto &swi = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto &active_sw = swi.rbegin();
if (active_sw != swi.rend()) {

View file

@ -29,7 +29,11 @@ sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidec
sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() {
}
void sidechain_net_handler_peerplays::recreate_primary_wallet() {
bool sidechain_net_handler_peerplays::process_proposal(const proposal_object &po) {
return true;
}
void sidechain_net_handler_peerplays::process_primary_wallet() {
return;
}

View file

@ -39,9 +39,15 @@ bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost
return ret_val;
}
void sidechain_net_manager::recreate_primary_wallet() {
void sidechain_net_manager::process_proposals() {
for (size_t i = 0; i < net_handlers.size(); i++) {
net_handlers.at(i)->recreate_primary_wallet();
net_handlers.at(i)->process_proposals();
}
}
void sidechain_net_manager::process_active_sons_change() {
for (size_t i = 0; i < net_handlers.size(); i++) {
net_handlers.at(i)->process_active_sons_change();
}
}