BitShares-Core  7.0.2
BitShares blockchain node software and command-line wallet software
witness.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
25 
28 
30 
31 #include <fc/thread/thread.hpp>
32 #include <fc/io/fstream.hpp>
33 
34 #include <boost/filesystem/path.hpp>
35 
36 #include <iostream>
37 
38 using namespace graphene::witness_plugin;
39 using std::string;
40 using std::vector;
41 
42 namespace bpo = boost::program_options;
43 
45 {
46  ilog("\n"
47  "********************************\n"
48  "* *\n"
49  "* ------- NEW CHAIN ------ *\n"
50  "* - Welcome to BitShares! - *\n"
51  "* ------------------------ *\n"
52  "* *\n"
53  "********************************\n"
54  "\n");
55  if( db.get_slot_at_time( fc::time_point::now() ) > 200 )
56  {
57  wlog("Your genesis seems to have an old timestamp");
58  wlog("Please consider using the --genesis-timestamp option to give your genesis a recent timestamp");
59  }
60 }
61 
62 void witness_plugin::plugin_set_program_options(
63  boost::program_options::options_description& command_line_options,
64  boost::program_options::options_description& config_file_options)
65 {
66  auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan")));
67  string witness_id_example = fc::json::to_string(chain::witness_id_type(5));
68  command_line_options.add_options()
69  ("enable-stale-production", bpo::bool_switch()->notifier([this](bool e){_production_enabled = e;}),
70  "Enable block production, even if the chain is stale.")
71  ("required-participation", bpo::value<uint32_t>()->default_value(33),
72  "Percent of witnesses (0-100) that must be participating in order to produce blocks")
73  ("witness-id,w", bpo::value<vector<string>>()->composing()->multitoken(),
74  ("ID of witness controlled by this node (e.g. " + witness_id_example +
75  ", quotes are required, may specify multiple times)").c_str())
76  ("private-key", bpo::value<vector<string>>()->composing()->multitoken()->
77  DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()),
78  graphene::utilities::key_to_wif(default_priv_key))),
79  "Tuple of [PublicKey, WIF private key] (may specify multiple times)")
80  ("private-key-file", bpo::value<vector<boost::filesystem::path>>()->composing()->multitoken(),
81  "Path to a file containing tuples of [PublicKey, WIF private key]."
82  " The file has to contain exactly one tuple (i.e. private - public key pair) per line."
83  " This option may be specified multiple times, thus multiple files can be provided.")
84  ;
85  config_file_options.add(command_line_options);
86 }
87 
88 std::string witness_plugin::plugin_name()const
89 {
90  return "witness";
91 }
92 
93 void witness_plugin::add_private_key(const std::string& key_id_to_wif_pair_string)
94 {
95  auto key_id_to_wif_pair = graphene::app::dejsonify<std::pair<chain::public_key_type, std::string>>
96  (key_id_to_wif_pair_string, 5);
97  fc::optional<fc::ecc::private_key> private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second);
98  if (!private_key)
99  {
100  // the key isn't in WIF format; see if they are still passing the old native private key format. This is
101  // just here to ease the transition, can be removed soon
102  try
103  {
104  private_key = fc::variant(key_id_to_wif_pair.second, 2).as<fc::ecc::private_key>(1);
105  }
106  catch (const fc::exception&)
107  {
108  FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second));
109  }
110  }
111 
112  if (_private_keys.find(key_id_to_wif_pair.first) == _private_keys.end())
113  {
114  ilog("Public Key: ${public}", ("public", key_id_to_wif_pair.first));
115  _private_keys[key_id_to_wif_pair.first] = *private_key;
116  }
117 }
118 
119 void witness_plugin::plugin_initialize(const boost::program_options::variables_map& options)
120 { try {
121  ilog("witness plugin: plugin_initialize() begin");
122  _options = &options;
123  LOAD_VALUE_SET(options, "witness-id", _witnesses, chain::witness_id_type);
124 
125  if( options.count("private-key") > 0 )
126  {
127  const std::vector<std::string> key_id_to_wif_pair_strings = options["private-key"].as<std::vector<std::string>>();
128  for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)
129  {
130  add_private_key(key_id_to_wif_pair_string);
131  }
132  }
133  if (options.count("private-key-file") > 0)
134  {
135  const std::vector<boost::filesystem::path> key_id_to_wif_pair_files =
136  options["private-key-file"].as<std::vector<boost::filesystem::path>>();
137  for (const boost::filesystem::path& key_id_to_wif_pair_file : key_id_to_wif_pair_files)
138  {
139  if (fc::exists(key_id_to_wif_pair_file))
140  {
141  std::string file_content;
142  fc::read_file_contents(key_id_to_wif_pair_file, file_content);
143  std::istringstream file_content_as_stream(file_content);
144 
145  std::string line; // key_id_to_wif_pair_string
146  while (std::getline(file_content_as_stream, line))
147  {
148  add_private_key(line);
149  }
150  }
151  else
152  {
153  FC_THROW("Failed to load private key file from ${path}", ("path", key_id_to_wif_pair_file.string()));
154  }
155  }
156  }
157  if(options.count("required-participation") > 0)
158  {
159  auto required_participation = options["required-participation"].as<uint32_t>();
160  FC_ASSERT(required_participation <= 100);
161  _required_witness_participation = options["required-participation"].as<uint32_t>()*GRAPHENE_1_PERCENT;
162  if(required_participation < 10)
163  wlog("witness plugin: Warning - Low required participation of ${rp}% found", ("rp", required_participation));
164  else if(required_participation > 90)
165  wlog("witness plugin: Warning - High required participation of ${rp}% found", ("rp", required_participation));
166  }
167  ilog("witness plugin: plugin_initialize() end");
168 } FC_LOG_AND_RETHROW() }
169 
170 void witness_plugin::plugin_startup()
171 { try {
172  ilog("witness plugin: plugin_startup() begin");
173  chain::database& d = database();
174  if( !_witnesses.empty() )
175  {
176  ilog("Launching block production for ${n} witnesses.", ("n", _witnesses.size()));
177  app().set_block_production(true);
178  if( _production_enabled )
179  {
180  if( d.head_block_num() == 0 )
181  new_chain_banner(d);
182  _production_skip_flags |= graphene::chain::database::skip_undo_history_check;
183  }
184  refresh_witness_key_cache();
185  d.applied_block.connect( [this]( const chain::signed_block& b )
186  {
187  refresh_witness_key_cache();
188  });
189  schedule_production_loop();
190  }
191  else
192  {
193  ilog("No witness configured.");
194  }
195  ilog("witness plugin: plugin_startup() end");
197 
198 void witness_plugin::stop_block_production()
199 {
200  _shutting_down = true;
201 
202  try {
203  if( _block_production_task.valid() )
204  _block_production_task.cancel_and_wait(__FUNCTION__);
205  } catch(fc::canceled_exception&) {
206  //Expected exception. Move along.
207  } catch(fc::exception& e) {
208  edump((e.to_detail_string()));
209  }
210 }
211 
212 void witness_plugin::refresh_witness_key_cache()
213 {
214  const auto& db = database();
215  for( const chain::witness_id_type& wit_id : _witnesses )
216  {
217  const chain::witness_object* wit_obj = db.find( wit_id );
218  if( wit_obj )
219  _witness_key_cache[wit_id] = wit_obj->signing_key;
220  else
221  _witness_key_cache[wit_id] = fc::optional<chain::public_key_type>();
222  }
223 }
224 
225 void witness_plugin::schedule_production_loop()
226 {
227  if (_shutting_down) return;
228 
229  //Schedule for the next second's tick regardless of chain state
230  // If we would wait less than 50ms, wait for the whole second.
232  int64_t time_to_next_second = 1000000 - (now.time_since_epoch().count() % 1000000);
233  if( time_to_next_second < 50000 ) // we must sleep for at least 50ms
234  time_to_next_second += 1000000;
235 
236  fc::time_point next_wakeup( now + fc::microseconds( time_to_next_second ) );
237 
238  _block_production_task = fc::schedule([this]{block_production_loop();},
239  next_wakeup, "Witness Block Production");
240 }
241 
242 block_production_condition::block_production_condition_enum witness_plugin::block_production_loop()
243 {
246 
247  if (_shutting_down)
248  {
250  }
251  else
252  {
253  try
254  {
255  result = maybe_produce_block(capture);
256  }
257  catch( const fc::canceled_exception& )
258  {
259  //We're trying to exit. Go ahead and let this one out.
260  throw;
261  }
262  catch( const fc::exception& e )
263  {
264  elog("Got exception while generating block:\n${e}", ("e", e.to_detail_string()));
266  }
267  }
268 
269  switch( result )
270  {
272  ilog("Generated block #${n} with ${x} transaction(s) and timestamp ${t} at time ${c}", (capture));
273  break;
275  ilog("Not producing block because production is disabled until we receive a recent block "
276  "(see: --enable-stale-production)");
277  break;
279  break;
281  break;
283  ilog("Not producing block because I don't have the private key for ${scheduled_key}", (capture) );
284  break;
286  elog("Not producing block because node appears to be on a minority fork with only ${pct}% witness participation",
287  (capture) );
288  break;
290  elog("Not producing block because node didn't wake up within 2500ms of the slot time.");
291  break;
293  elog( "exception producing block" );
294  break;
296  ilog( "shutdown producing block" );
297  return result;
299  ilog( "not connected to P2P network" );
300  return result;
301  default:
302  elog( "unknown condition ${result} while producing block", ("result", (unsigned char)result) );
303  break;
304  }
305 
306  schedule_production_loop();
307  return result;
308 }
309 
310 block_production_condition::block_production_condition_enum witness_plugin::maybe_produce_block(
312 {
313  chain::database& db = database();
314  fc::time_point now_fine = fc::time_point::now();
315  fc::time_point_sec now = now_fine + fc::microseconds( 500000 );
316 
317  // If the next block production opportunity is in the present or future, we're synced.
318  if( !_production_enabled )
319  {
320  if( db.get_slot_time(1) >= now )
321  _production_enabled = true;
322  else
324  }
325 
326  // is anyone scheduled to produce now or one second in the future?
327  uint32_t slot = db.get_slot_at_time( now );
328  if( slot == 0 )
329  {
330  capture("next_time", db.get_slot_time(1));
332  }
333 
334  //
335  // this assert should not fail, because now <= db.head_block_time()
336  // should have resulted in slot == 0.
337  //
338  // if this assert triggers, there is a serious bug in get_slot_at_time()
339  // which would result in allowing a later block to have a timestamp
340  // less than or equal to the previous block
341  //
342  assert( now > db.head_block_time() );
343 
344  graphene::chain::witness_id_type scheduled_witness = db.get_scheduled_witness( slot );
345  // we must control the witness scheduled to produce the next block.
346  if( _witnesses.find( scheduled_witness ) == _witnesses.end() )
347  {
348  capture("scheduled_witness", scheduled_witness);
350  }
351 
352  fc::time_point_sec scheduled_time = db.get_slot_time( slot );
353  graphene::chain::public_key_type scheduled_key = *_witness_key_cache[scheduled_witness]; // should be valid
354  auto private_key_itr = _private_keys.find( scheduled_key );
355 
356  if( private_key_itr == _private_keys.end() )
357  {
358  capture("scheduled_key", scheduled_key);
360  }
361 
362  uint32_t prate = db.witness_participation_rate();
363  if( prate < _required_witness_participation )
364  {
365  capture("pct", uint32_t(100*uint64_t(prate) / GRAPHENE_1_PERCENT));
367  }
368 
369  if( llabs((scheduled_time - now).count()) > fc::milliseconds( 2500 ).count() )
370  {
371  capture("scheduled_time", scheduled_time)("now", now);
373  }
374 
375  if( p2p_node() == nullptr )
377 
378  auto block = db.generate_block(
379  scheduled_time,
380  scheduled_witness,
381  private_key_itr->second,
382  _production_skip_flags
383  );
384  capture("n", block.block_num())("t", block.timestamp)("c", now)("x", block.transactions.size());
385  fc::async( [this,block](){ p2p_node()->broadcast(net::block_message(block)); } );
386 
388 }
GRAPHENE_1_PERCENT
#define GRAPHENE_1_PERCENT
Definition: config.hpp:103
FC_CAPTURE_AND_RETHROW
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:479
fc::milliseconds
microseconds milliseconds(int64_t s)
Definition: time.hpp:35
graphene::chain::database
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
wlog
#define wlog(FORMAT,...)
Definition: logger.hpp:123
fc::exception
Used to generate a useful error report when an exception is thrown.
Definition: exception.hpp:56
graphene::witness_plugin::block_production_condition::exception_producing_block
@ exception_producing_block
Definition: witness.hpp:44
database.hpp
graphene::app::plugin::database
chain::database & database()
Definition: plugin.hpp:115
graphene::utilities::key_to_wif
std::string key_to_wif(const fc::sha256 &private_secret)
Definition: key_conversion.cpp:30
graphene::utilities::wif_to_key
fc::optional< fc::ecc::private_key > wif_to_key(const std::string &wif_key)
Definition: key_conversion.cpp:47
FC_THROW
#define FC_THROW( ...)
Definition: exception.hpp:366
FC_LOG_AND_RETHROW
#define FC_LOG_AND_RETHROW()
Definition: exception.hpp:395
graphene::witness_plugin::block_production_condition::not_synced
@ not_synced
Definition: witness.hpp:38
fc::schedule
auto schedule(Functor &&f, const fc::time_point &t, const char *desc FC_TASK_NAME_DEFAULT_ARG, priority prio=priority()) -> fc::future< decltype(f())>
Definition: thread.hpp:231
graphene::witness_plugin::block_production_condition::block_production_condition_enum
block_production_condition_enum
Definition: witness.hpp:35
graphene::witness_plugin::block_production_condition::not_time_yet
@ not_time_yet
Definition: witness.hpp:40
graphene::chain::database::applied_block
fc::signal< void(const signed_block &)> applied_block
Definition: database.hpp:624
graphene::protocol::public_key_type
Definition: types.hpp:312
graphene::witness_plugin::block_production_condition::not_my_turn
@ not_my_turn
Definition: witness.hpp:39
fc::json::to_string
static string to_string(const variant &v, output_formatting format=stringify_large_ints_and_doubles, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition: json.cpp:650
fc::time_point::time_since_epoch
const microseconds & time_since_epoch() const
Definition: time.hpp:54
fc::ecc::private_key
an elliptic curve private key.
Definition: elliptic.hpp:89
graphene::witness_plugin::block_production_condition::no_network
@ no_network
Definition: witness.hpp:46
graphene::witness_plugin::block_production_condition::shutdown
@ shutdown
Definition: witness.hpp:45
graphene::witness_plugin::block_production_condition::no_private_key
@ no_private_key
Definition: witness.hpp:41
new_chain_banner
void new_chain_banner(const graphene::chain::database &db)
Definition: witness.cpp:44
graphene::witness_plugin
Definition: witness.hpp:31
ilog
#define ilog(FORMAT,...)
Definition: logger.hpp:117
edump
#define edump(SEQ)
Definition: logger.hpp:182
graphene::witness_plugin::block_production_condition::low_participation
@ low_participation
Definition: witness.hpp:42
graphene::witness_plugin::block_production_condition::produced
@ produced
Definition: witness.hpp:37
witness.hpp
fc::time_point_sec
Definition: time.hpp:74
thread.hpp
fc::async
auto async(Functor &&f, const char *desc FC_TASK_NAME_DEFAULT_ARG, priority prio=priority()) -> fc::future< decltype(f())>
Definition: thread.hpp:227
fc::variant::as
T as(uint32_t max_depth) const
Definition: variant.hpp:337
fc::exception::to_detail_string
std::string to_detail_string(log_level ll=log_level::all) const
Definition: exception.cpp:183
fc::future< void >::valid
bool valid() const
Definition: future.hpp:311
fc::future< void >::cancel_and_wait
void cancel_and_wait(const char *reason FC_CANCELATION_REASON_DEFAULT_ARG)
Definition: future.hpp:314
graphene::chain::database::head_block_num
uint32_t head_block_num() const
Definition: db_getter.cpp:72
graphene::chain::database::get_slot_at_time
uint32_t get_slot_at_time(fc::time_point_sec when) const
Definition: db_witness_schedule.cpp:74
fc::limited_mutable_variant_object
Definition: variant_object.hpp:220
fc::microseconds
Definition: time.hpp:12
graphene::chain::witness_object::signing_key
public_key_type signing_key
Definition: witness_object.hpp:37
fc::time_point::now
static time_point now()
Definition: time.cpp:13
graphene::app::application::set_block_production
void set_block_production(bool producing_blocks)
Definition: application.cpp:1409
FC_ASSERT
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
DEFAULT_VALUE_VECTOR
#define DEFAULT_VALUE_VECTOR(value)
Definition: plugin.hpp:136
fc::variant
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object's.
Definition: variant.hpp:198
fc::time_point
Definition: time.hpp:44
fc::read_file_contents
void read_file_contents(const fc::path &filename, std::string &result)
Definition: fstream.cpp:107
fc::microseconds::count
int64_t count() const
Definition: time.hpp:28
fstream.hpp
GRAPHENE_MAX_NESTED_OBJECTS
#define GRAPHENE_MAX_NESTED_OBJECTS
Definition: config.hpp:33
graphene::chain::witness_object
Definition: witness_object.hpp:32
graphene::witness_plugin::block_production_condition::lag
@ lag
Definition: witness.hpp:43
fc::optional
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
graphene::app::plugin::p2p_node
net::node_ptr p2p_node() const
Definition: plugin.hpp:117
key_conversion.hpp
LOAD_VALUE_SET
#define LOAD_VALUE_SET(options, name, container, type)
Definition: plugin.hpp:137
graphene::protocol::signed_block
Definition: block.hpp:64
witness_object.hpp
graphene::app::abstract_plugin::app
application & app() const
Get a reference of the application bound to the plugin.
Definition: plugin.hpp:46
graphene::chain::database::skip_undo_history_check
@ skip_undo_history_check
used while reindexing
Definition: database.hpp:88
fc::ecc::private_key::regenerate
static private_key regenerate(const fc::sha256 &secret)
Definition: elliptic_impl_priv.cpp:52
fc::getline
fc::istream & getline(fc::istream &, std::string &, char delim='\n')
Definition: iostream.cpp:78
fc::sha256::hash
static sha256 hash(const char *d, uint32_t dlen)
Definition: sha256.cpp:41
fc::exists
bool exists(const path &p)
Definition: filesystem.cpp:209
elog
#define elog(FORMAT,...)
Definition: logger.hpp:129