rate limiting for son deposit, withdrawal, pw change, tx settle operations

This commit is contained in:
sierra19XX 2021-05-03 09:26:43 +00:00
parent ae712122d4
commit 668f677867
4 changed files with 75 additions and 7 deletions

View file

@ -12,6 +12,20 @@ namespace detail {
class peerplays_sidechain_plugin_impl; class peerplays_sidechain_plugin_impl;
} }
struct son_proposal_type {
son_proposal_type(int op, son_id_type son, object_id_type object) :
op_type(op),
son_id(son),
object_id(object) {
}
int op_type;
son_id_type son_id;
object_id_type object_id;
bool operator<(const son_proposal_type &other) const {
return std::tie(op_type, son_id, object_id) < std::tie(other.op_type, other.son_id, other.object_id);
}
};
class peerplays_sidechain_plugin : public graphene::app::plugin { class peerplays_sidechain_plugin : public graphene::app::plugin {
public: public:
peerplays_sidechain_plugin(); peerplays_sidechain_plugin();
@ -34,6 +48,8 @@ public:
bool is_son_deregistered(son_id_type son_id); bool is_son_deregistered(son_id_type son_id);
fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(son_id_type son_id);
fc::ecc::private_key get_private_key(chain::public_key_type public_key); fc::ecc::private_key get_private_key(chain::public_key_type public_key);
void log_son_proposal_retry(int op_type, object_id_type object_id);
bool can_son_participate(int op_type, object_id_type object_id);
}; };
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -41,6 +41,8 @@ public:
bool is_valid_son_proposal(const chain::proposal_object &proposal); bool is_valid_son_proposal(const chain::proposal_object &proposal);
fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(son_id_type son_id);
fc::ecc::private_key get_private_key(chain::public_key_type public_key); fc::ecc::private_key get_private_key(chain::public_key_type public_key);
void log_son_proposal_retry(int op_type, object_id_type object_id);
bool can_son_participate(int op_type, object_id_type object_id);
void schedule_heartbeat_loop(); void schedule_heartbeat_loop();
void heartbeat_loop(); void heartbeat_loop();
@ -75,6 +77,8 @@ private:
std::map<chain::public_key_type, fc::ecc::private_key> private_keys; std::map<chain::public_key_type, fc::ecc::private_key> private_keys;
fc::future<void> _heartbeat_task; fc::future<void> _heartbeat_task;
fc::future<void> _son_processing_task; fc::future<void> _son_processing_task;
std::map<son_proposal_type, uint16_t> son_retry_count;
uint16_t retries_threshold;
bool first_block_skipped; bool first_block_skipped;
void on_applied_block(const signed_block &b); void on_applied_block(const signed_block &b);
@ -130,6 +134,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
cli.add_options()("bitcoin-wallet-password", bpo::value<string>(), "Bitcoin wallet password"); cli.add_options()("bitcoin-wallet-password", bpo::value<string>(), "Bitcoin wallet password");
cli.add_options()("bitcoin-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), cli.add_options()("bitcoin-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")),
"Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)");
cli.add_options()("sidechain-retry-threshold", bpo::value<uint16_t>()->default_value(15), "Sidechain retry throttling threshold");
cfg.add(cli); cfg.add(cli);
} }
@ -169,6 +174,8 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
} }
config_ready_son = config_ready_son && !private_keys.empty(); config_ready_son = config_ready_son && !private_keys.empty();
} }
retries_threshold = options.at("sidechain-retry-threshold").as<uint16_t>();
ilog("sidechain-retry-threshold: ${sidechain-retry-threshold}", ("sidechain-retry-threshold", retries_threshold));
} }
if (!config_ready_son) { if (!config_ready_son) {
wlog("Haven't set up SON parameters"); wlog("Haven't set up SON parameters");
@ -279,7 +286,7 @@ bool peerplays_sidechain_plugin_impl::is_son_deregistered(son_id_type son_id) {
if (son_obj == idx.end()) if (son_obj == idx.end())
return true; return true;
if(son_obj->status == chain::son_status::deregistered) { if (son_obj->status == chain::son_status::deregistered) {
return true; return true;
} }
@ -449,6 +456,22 @@ bool peerplays_sidechain_plugin_impl::is_valid_son_proposal(const chain::proposa
return false; return false;
} }
void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object_id_type object_id) {
son_proposal_type prop_type(op_type, get_current_son_id(), object_id);
auto itr = son_retry_count.find(prop_type);
if (itr != son_retry_count.end()) {
itr->second++;
} else {
son_retry_count[prop_type] = 1;
}
}
bool peerplays_sidechain_plugin_impl::can_son_participate(int op_type, object_id_type object_id) {
son_proposal_type prop_type(op_type, get_current_son_id(), object_id);
auto itr = son_retry_count.find(prop_type);
return (itr == son_retry_count.end() || itr->second < retries_threshold);
}
void peerplays_sidechain_plugin_impl::approve_proposals() { void peerplays_sidechain_plugin_impl::approve_proposals() {
auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) { auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) {
@ -695,4 +718,12 @@ fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_k
return my->get_private_key(public_key); return my->get_private_key(public_key);
} }
void peerplays_sidechain_plugin::log_son_proposal_retry(int op_type, object_id_type object_id) {
my->log_son_proposal_retry(op_type, object_id);
}
bool peerplays_sidechain_plugin::can_son_participate(int op_type, object_id_type object_id) {
return my->can_son_participate(op_type, object_id);
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -275,6 +275,7 @@ void sidechain_net_handler::process_proposals() {
int32_t op_idx_0 = -1; int32_t op_idx_0 = -1;
chain::operation op_obj_idx_0; chain::operation op_obj_idx_0;
object_id_type object_id;
if (po->proposed_transaction.operations.size() >= 1) { if (po->proposed_transaction.operations.size() >= 1) {
op_idx_0 = po->proposed_transaction.operations[0].which(); op_idx_0 = po->proposed_transaction.operations[0].which();
@ -283,7 +284,7 @@ void sidechain_net_handler::process_proposals() {
int32_t op_idx_1 = -1; int32_t op_idx_1 = -1;
chain::operation op_obj_idx_1; chain::operation op_obj_idx_1;
(void) op_idx_1; (void)op_idx_1;
if (po->proposed_transaction.operations.size() >= 2) { if (po->proposed_transaction.operations.size() >= 2) {
op_idx_1 = po->proposed_transaction.operations[1].which(); op_idx_1 = po->proposed_transaction.operations[1].which();
@ -293,11 +294,13 @@ void sidechain_net_handler::process_proposals() {
switch (op_idx_0) { switch (op_idx_0) {
case chain::operation::tag<chain::son_wallet_update_operation>::value: { case chain::operation::tag<chain::son_wallet_update_operation>::value: {
should_process = (op_obj_idx_0.get<son_wallet_update_operation>().sidechain == sidechain); should_process = (op_obj_idx_0.get<son_wallet_update_operation>().sidechain == sidechain);
object_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id;
break; break;
} }
case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: { case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: {
son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id; son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id;
object_id = swdo_id;
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>(); const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
const auto swdo = idx.find(swdo_id); const auto swdo = idx.find(swdo_id);
if (swdo != idx.end()) { if (swdo != idx.end()) {
@ -308,6 +311,7 @@ void sidechain_net_handler::process_proposals() {
case chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value: { 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; son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get<son_wallet_withdraw_process_operation>().son_wallet_withdraw_id;
object_id = swwo_id;
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>(); const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto swwo = idx.find(swwo_id); const auto swwo = idx.find(swwo_id);
if (swwo != idx.end()) { if (swwo != idx.end()) {
@ -323,6 +327,7 @@ void sidechain_net_handler::process_proposals() {
const auto sto = idx.find(st_id); const auto sto = idx.find(st_id);
if (sto != idx.end()) { if (sto != idx.end()) {
should_process = ((sto->sidechain == sidechain) && (sto->status == sidechain_transaction_status::valid) && signer_expected(*sto, signer)); should_process = ((sto->sidechain == sidechain) && (sto->status == sidechain_transaction_status::valid) && signer_expected(*sto, signer));
object_id = sto->object_id;
} }
break; break;
} }
@ -333,6 +338,7 @@ void sidechain_net_handler::process_proposals() {
const auto sto = idx.find(st_id); const auto sto = idx.find(st_id);
if (sto != idx.end()) { if (sto != idx.end()) {
should_process = (sto->sidechain == sidechain); should_process = (sto->sidechain == sidechain);
object_id = sto->object_id;
} }
break; break;
} }
@ -344,10 +350,14 @@ void sidechain_net_handler::process_proposals() {
elog("=================================================="); elog("==================================================");
} }
if (should_process) { if (should_process && (op_idx_0 == chain::operation::tag<chain::sidechain_transaction_sign_operation>::value || plugin.can_son_participate(op_idx_0, object_id))) {
bool should_approve = process_proposal(*po); bool should_approve = process_proposal(*po);
if (should_approve) { if (should_approve) {
approve_proposal(po->id, plugin.get_current_son_id()); if (approve_proposal(po->id, plugin.get_current_son_id())) {
if (op_idx_0 != chain::operation::tag<chain::sidechain_transaction_sign_operation>::value) {
plugin.log_son_proposal_retry(op_idx_0, object_id);
}
}
} }
} }
} }
@ -374,7 +384,7 @@ void sidechain_net_handler::process_deposits() {
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); 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) { 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) || !plugin.can_son_participate(chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id)) {
return; return;
} }
//Ignore the deposits which are not valid anymore, considered refunds. //Ignore the deposits which are not valid anymore, considered refunds.
@ -392,6 +402,7 @@ void sidechain_net_handler::process_deposits() {
wlog("Deposit not processed: ${swdo}", ("swdo", swdo)); wlog("Deposit not processed: ${swdo}", ("swdo", swdo));
return; return;
} }
plugin.log_son_proposal_retry(chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id);
}); });
} }
@ -404,7 +415,7 @@ void sidechain_net_handler::process_withdrawals() {
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); 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) { 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) || !plugin.can_son_participate(chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id)) {
return; return;
} }
@ -416,6 +427,7 @@ void sidechain_net_handler::process_withdrawals() {
wlog("Withdraw not processed: ${swwo}", ("swwo", swwo)); wlog("Withdraw not processed: ${swwo}", ("swwo", swwo));
return; return;
} }
plugin.log_son_proposal_retry(chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id);
}); });
} }
@ -514,6 +526,10 @@ void sidechain_net_handler::settle_sidechain_transactions() {
return; return;
} }
if (!plugin.can_son_participate(chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id)) {
return;
}
ilog("Sidechain transaction to settle: ${sto}", ("sto", sto.id)); ilog("Sidechain transaction to settle: ${sto}", ("sto", sto.id));
int64_t settle_amount = settle_sidechain_transaction(sto); int64_t settle_amount = settle_sidechain_transaction(sto);
@ -560,6 +576,7 @@ void sidechain_net_handler::settle_sidechain_transactions() {
database.push_transaction(trx, database::validation_steps::skip_block_size_check); database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node()) if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
plugin.log_son_proposal_retry(chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id);
} catch (fc::exception &e) { } catch (fc::exception &e) {
elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what())); elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what()));
} }

View file

@ -1005,7 +1005,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
int32_t op_idx_1 = -1; int32_t op_idx_1 = -1;
chain::operation op_obj_idx_1; chain::operation op_obj_idx_1;
(void) op_idx_1; (void)op_idx_1;
if (po.proposed_transaction.operations.size() >= 2) { if (po.proposed_transaction.operations.size() >= 2) {
op_idx_1 = po.proposed_transaction.operations[1].which(); op_idx_1 = po.proposed_transaction.operations[1].which();
@ -1287,6 +1287,9 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
boost::property_tree::ptree active_pw_pt; boost::property_tree::ptree active_pw_pt;
boost::property_tree::read_json(active_pw_ss, active_pw_pt); boost::property_tree::read_json(active_pw_ss, active_pw_pt);
if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) {
if (!plugin.can_son_participate(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) {
return;
}
proposal_create_operation proposal_op; proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
@ -1325,6 +1328,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
database.push_transaction(trx, database::validation_steps::skip_block_size_check); database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node()) if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
plugin.log_son_proposal_retry(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id);
} catch (fc::exception &e) { } catch (fc::exception &e) {
elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what()));
return; return;