35 #include <fc/time.hpp>
37 #include <graphene/utilities/key_conversion.hpp>
39 #include <fc/smart_ref_impl.hpp>
40 #include <fc/thread/thread.hpp>
45 #define DISTANCE_CALC_PRECISION (10000)
50 namespace bpo = boost::program_options;
52 using protocol::signed_transaction;
53 using chain::account_object;
66 void plugin_initialize();
72 void update_account_bandwidth(
const account_object& a, uint32_t trx_size,
const bandwidth_type type);
81 void check_memo(
const std::string& memo,
const account_object& account,
const account_authority_object& auth)
83 std::vector<public_key_type> keys;
88 keys.push_back(fc::ecc::extended_private_key::from_base58(memo).get_public_key());
90 catch (fc::parse_error_exception&)
93 catch (fc::assert_exception&)
98 std::string owner_seed = account.name +
"owner" + memo;
99 auto owner_secret = fc::sha256::hash(owner_seed.c_str(), owner_seed.size());
100 keys.push_back(fc::ecc::private_key::regenerate(owner_secret).get_public_key());
102 std::string active_seed = account.name +
"active" + memo;
103 auto active_secret = fc::sha256::hash(active_seed.c_str(), active_seed.size());
104 keys.push_back(fc::ecc::private_key::regenerate(active_secret).get_public_key());
106 std::string posting_seed = account.name +
"posting" + memo;
107 auto posting_secret = fc::sha256::hash(posting_seed.c_str(), posting_seed.size());
108 keys.push_back(fc::ecc::private_key::regenerate(posting_secret).get_public_key());
111 for (
auto& key_weight_pair : auth.owner.key_auths)
113 for (
auto& key : keys)
114 SCORUM_ASSERT(key_weight_pair.first != key, chain::plugin_exception,
115 "Detected private owner key in memo field. You should change your owner keys.");
118 for (
auto& key_weight_pair : auth.active.key_auths)
120 for (
auto& key : keys)
121 SCORUM_ASSERT(key_weight_pair.first != key, chain::plugin_exception,
122 "Detected private active key in memo field. You should change your active keys.");
125 for (
auto& key_weight_pair : auth.posting.key_auths)
127 for (
auto& key : keys)
128 SCORUM_ASSERT(key_weight_pair.first != key, chain::plugin_exception,
129 "Detected private posting key in memo field. You should change your posting keys.");
132 const auto& memo_key = account.memo_key;
133 for (
auto& key : keys)
135 "Detected private memo key in memo field. You should change your memo key.");
157 "Cannot specify more than 8 beneficiaries.");
172 "Comment is nested ${x} posts deep, maximum depth is ${y}.",
181 if (comment.cashout_time == fc::time_point_sec::maximum())
183 FC_THROW_EXCEPTION(chain::plugin_exception,
"The comment is archived");
190 if (o.
memo.length() > 0)
192 _db.get<account_authority_object, chain::by_account>(o.
from));
198 const auto& _db = _self.database();
199 flat_set<account_name_type> required;
200 std::vector<authority> other;
203 auto trx_size = fc::raw::pack_size(trx);
205 auto& account_svc = _db.account_service();
207 for (
const auto& auth : required)
209 const auto& acnt = account_svc.get_account(auth);
226 const auto& _db = _self.database();
227 if (_db.is_producing())
235 auto& db = _self.database();
236 int64_t max_block_size
241 if (BOOST_UNLIKELY(reserve_ratio_ptr ==
nullptr))
244 r.average_block_size = 0;
269 if (db.head_block_num() % 20 == 0)
301 ilog(
"Reserve ratio updated from ${old} to ${new}. Block: ${blocknum}",
320 bool has_bandwidth =
true;
322 if (props.total_scorumpower.amount > 0)
336 auto delta_time = (_db.
head_block_time() - band->last_bandwidth_update).to_seconds();
348 new_bandwidth += trx_bandwidth;
356 fc::uint128 account_vshares(a.effective_scorumpower().amount.value);
357 fc::uint128 total_vshares(props.total_scorumpower.amount.value);
358 fc::uint128 account_average_bandwidth(band->average_bandwidth.value);
361 has_bandwidth = (account_vshares * max_virtual_bandwidth) > (account_average_bandwidth * total_vshares);
365 "Account: ${account} bandwidth limit exceeded. Please wait to transact or power up SCR.",
366 (
"account", a.name)(
"account_vshares", account_vshares)(
"account_average_bandwidth",
367 account_average_bandwidth)(
368 "max_virtual_bandwidth", max_virtual_bandwidth)(
"total_scorumpower", total_vshares));
375 , _my(new detail::witness_plugin_impl(*this))
383 if (_block_production_task.valid())
385 _block_production_task.cancel_and_wait(__FUNCTION__);
388 catch (fc::canceled_exception&)
392 catch (fc::exception& e)
394 edump((e.to_detail_string()));
399 boost::program_options::options_description& config_file_options)
401 std::string witness_id_example =
"initwitness";
403 command_line_options.add_options()
404 (
"enable-stale-production",
405 bpo::bool_switch()->notifier([
this](
bool e) { _production_enabled = e; }),
406 "Enable block production, even if the chain is stale.")
407 (
"required-participation",
408 bpo::bool_switch()->notifier([
this](
int e) { _required_witness_participation = uint32_t(e *
SCORUM_1_PERCENT); }),
409 "Percent of witnesses (0-99) that must be participating in order to produce blocks")
411 bpo::value<std::vector<std::string>>()->composing()->multitoken(),
412 (
"name of witness controlled by this node (e.g. " + witness_id_example +
" )").c_str())
414 bpo::value<std::vector<std::string>>()->composing()->multitoken(),
415 "WIF PRIVATE KEY to be used by one or more witnesses or miners");
418 config_file_options.add(command_line_options);
433 if (options.count(
"private-key"))
435 const std::vector<std::string> keys = options[
"private-key"].as<std::vector<std::string>>();
436 for (
const std::string& wif_key : keys)
438 fc::optional<fc::ecc::private_key> private_key = graphene::utilities::wif_to_key(wif_key);
439 FC_ASSERT(private_key.valid(),
"unable to parse private key");
440 _private_keys[private_key->get_public_key()] = *private_key;
462 ilog(
"witness plugin: plugin_startup() begin");
464 if (!_witnesses.empty())
466 ilog(
"Launching block production for ${n} witnesses.", (
"n", _witnesses.size()));
469 if (_production_enabled)
473 schedule_production_loop();
477 elog(
"No witnesses configured! Please add witness names and private keys to configuration.");
479 ilog(
"witness plugin: plugin_startup() end");
481 FC_CAPTURE_AND_RETHROW()
489 void witness_plugin::schedule_production_loop()
491 static const int64_t ONE_SECOND_MS = 1000000;
494 fc::time_point fc_now = fc::time_point::now();
495 int64_t time_to_next_second = ONE_SECOND_MS - (fc_now.time_since_epoch().count() % ONE_SECOND_MS);
496 if (time_to_next_second < 50000)
498 time_to_next_second += ONE_SECOND_MS;
501 fc::time_point next_wakeup(fc_now + fc::microseconds(time_to_next_second));
504 _block_production_task = fc::schedule([
this] { block_production_loop(); }, next_wakeup,
"Witness Block Production");
507 void witness_plugin::block_production_loop()
511 if (fc::time_point::now() < genesis_time)
513 wlog(
"waiting until genesis time to produce block: ${t}", (
"t", genesis_time));
514 schedule_production_loop();
519 fc::mutable_variant_object capture;
522 result = maybe_produce_block(capture);
524 catch (
const fc::canceled_exception&)
529 catch (
const scorum::chain::unknown_hardfork_exception& e)
532 elog(
"${e}\nNode may be out of date...", (
"e", e.to_detail_string()));
535 catch (
const fc::exception& e)
537 elog(
"Got exception while generating block:\n${e}", (
"e", e.to_detail_string()));
544 ilog(
"Generated block #${n} with timestamp ${t} at time ${c} by ${w}", (capture));
557 ilog(
"Not producing block for ${scheduled_witness} because I don't have the private key for ${scheduled_key}",
562 "Not producing block because node appears to be on a minority fork with only ${pct}% witness participation",
566 elog(
"Not producing block because node didn't wake up within 500ms of the slot time.");
569 elog(
"Not producing block because the last block was generated by the same witness.\nThis node is probably "
570 "disconnected from the network so block production has been disabled.\nDisable this check with "
571 "--allow-consecutive option.");
574 elog(
"Failure when producing block with no transactions");
580 dlog(
"result = ${r}", (
"r", result));
581 schedule_production_loop();
585 witness_plugin::maybe_produce_block(fc::mutable_variant_object& capture)
588 fc::time_point now_fine = fc::time_point::now();
589 fc::time_point_sec now = now_fine + fc::microseconds(500000);
592 if (!_production_enabled)
594 if (db.get_slot_time(1) >= now)
596 _production_enabled =
true;
605 uint32_t slot = db.get_slot_at_time(now);
608 capture(
"next_time", db.get_slot_time(1));
620 assert(now > db.head_block_time());
622 std::string scheduled_witness = db.get_scheduled_witness(slot);
624 if (_witnesses.find(scheduled_witness) == _witnesses.end())
626 capture(
"scheduled_witness", scheduled_witness);
630 const auto& witness_by_name = db.get_index<
chain::witness_index>().indices().get<chain::by_name>();
631 auto itr = witness_by_name.find(scheduled_witness);
633 fc::time_point_sec scheduled_time = db.get_slot_time(slot);
635 auto private_key_itr = _private_keys.find(scheduled_key);
637 if (private_key_itr == _private_keys.end())
639 capture(
"scheduled_witness", scheduled_witness);
640 capture(
"scheduled_key", scheduled_key);
644 uint32_t prate = db.witness_participation_rate();
645 if (prate < _required_witness_participation)
651 fc::microseconds dlt = scheduled_time - now;
652 dlog(
"scheduled_time (${s} s) - now (${n} s) = ${d} ms",
653 (
"s", scheduled_time.sec_since_epoch())(
"n", now.sec_since_epoch())(
"d", dlt.count()));
655 if (llabs(dlt.count()) > fc::milliseconds(500).count())
657 capture(
"scheduled_time", scheduled_time)(
"now", now);
667 = db.generate_block(scheduled_time, scheduled_witness, private_key_itr->second, _production_skip_flags);
668 capture(
"n", block.block_num())(
"t", block.timestamp)(
"c", now)(
"w", scheduled_witness);
669 fc::async([
this, block]() {
p2p_node().broadcast(graphene::net::block_message(block)); });
673 catch (fc::exception& e)
675 elog(
"${e}", (
"e", e.to_detail_string()));
676 elog(
"Clearing pending transactions and attempting again");
void set_block_production(bool producing_blocks)
application & app() const
chain::database & database()
graphene::net::node & p2p_node()
tracks the blockchain state in an extensible manner
time_point_sec head_block_time() const
time_point_sec get_genesis_time() const
fc::signal< void(const signed_transaction &)> on_pre_apply_transaction
fc::signal< void(const signed_block &)> applied_block
@ skip_undo_history_check
used while reindexing
bool is_producing() const
fc::signal< void(const operation_notification &)> pre_apply_operation
virtual const object_type & create(const modifier_type &modifier) override
ConcreteService & obtain_service() const
share_type lifetime_bandwidth
share_type average_bandwidth
time_point_sec last_bandwidth_update
void pre_operation(const operation_notification ¬e)
witness_plugin_impl(witness_plugin &plugin)
void update_account_bandwidth(const account_object &a, uint32_t trx_size, const bandwidth_type type)
void pre_transaction(const signed_transaction &trx)
void on_block(const signed_block &b)
int32_t average_block_size
uint128_t max_virtual_bandwidth
int64_t current_reserve_ratio
virtual void plugin_initialize(const boost::program_options::variables_map &options) override
Perform early startup routines and register plugin indexes, callbacks, etc.
virtual void plugin_startup() override
Begin normal runtime operations.
witness_plugin(application *app)
virtual void plugin_set_program_options(boost::program_options::options_description &command_line_options, boost::program_options::options_description &config_file_options) override
Fill in command line parameters used by the plugin.
virtual ~witness_plugin()
virtual void plugin_shutdown() override
Cleanly shut down the plugin.
std::string plugin_name() const override
#define SCORUM_MAX_RESERVE_RATIO
#define SCORUM_BANDWIDTH_AVERAGE_WINDOW_SECONDS
#define SCORUM_SOFT_MAX_COMMENT_DEPTH
#define SCORUM_BANDWIDTH_PRECISION
1 million
#define SCORUM_BLOCK_INTERVAL
#define SCORUM_ASSERT(expr, exc_type, FORMAT,...)
shared_multi_index_container< witness_object, indexed_by< ordered_unique< tag< by_id >, member< witness_object, witness_id_type, &witness_object::id > >, ordered_unique< tag< by_name >, member< witness_object, account_name_type, &witness_object::owner > >, ordered_unique< tag< by_vote_name >, composite_key< witness_object, member< witness_object, share_type, &witness_object::votes >, member< witness_object, account_name_type, &witness_object::owner > >, composite_key_compare< std::greater< share_type >, std::less< account_name_type > > >, ordered_unique< tag< by_schedule_time >, composite_key< witness_object, member< witness_object, fc::uint128, &witness_object::virtual_scheduled_time >, member< witness_object, witness_id_type, &witness_object::id > > > > > witness_index
@ market
Rate limiting for all other actions.
@ forum
Rate limiting for all forum related actions.
fc::safe< share_value_type > share_type
bool is_market_operation(const operation &op)
block_production_condition_enum
@ exception_producing_block
void check_memo(const std::string &memo, const account_object &account, const account_authority_object &auth)
shared_multi_index_container< account_bandwidth_object, indexed_by< ordered_unique< tag< by_id >, member< account_bandwidth_object, account_bandwidth_id_type, &account_bandwidth_object::id > >, ordered_unique< tag< by_account_bandwidth_type >, composite_key< account_bandwidth_object, member< account_bandwidth_object, account_name_type, &account_bandwidth_object::account >, member< account_bandwidth_object, bandwidth_type, &account_bandwidth_object::type > > > > > account_bandwidth_index
oid< reserve_ratio_object > reserve_ratio_id_type
shared_multi_index_container< reserve_ratio_object, indexed_by< ordered_unique< tag< by_id >, member< reserve_ratio_object, reserve_ratio_id_type, &reserve_ratio_object::id > > > > reserve_ratio_index
#define LOAD_VALUE_SET(options, name, container, type)
#define SCORUM_DEFINE_PLUGIN(plugin_name, plugin_class)
const protocol::operation & op
std::vector< operation > operations
void get_required_authorities(flat_set< account_name_type > &active, flat_set< account_name_type > &owner, flat_set< account_name_type > &posting, std::vector< authority > &other) const
Transfers SCR from one account to another.
void operator()(const comment_options_operation &o) const
void operator()(const T &) const
const chain::database & _db
operation_visitor(const chain::database &db)
void operator()(const transfer_operation &o) const
void operator()(const comment_operation &o) const
#define DISTANCE_CALC_PRECISION
#define RESERVE_RATIO_PRECISION
#define RESERVE_RATIO_MIN_INCREMENT