BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
peer_database.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  */
24 #include <boost/multi_index_container.hpp>
25 #include <boost/multi_index/ordered_index.hpp>
26 #include <boost/multi_index/hashed_index.hpp>
27 #include <boost/multi_index/member.hpp>
28 #include <boost/multi_index/mem_fun.hpp>
29 #include <boost/multi_index/tag.hpp>
30 
31 #include <fc/io/raw.hpp>
32 #include <fc/io/raw_variant.hpp>
33 #include <fc/log/logger.hpp>
34 #include <fc/io/json.hpp>
35 
37 #include <graphene/net/config.hpp>
38 
39 namespace graphene { namespace net {
40  namespace detail
41  {
42  using namespace boost::multi_index;
43 
45  {
46  public:
48  struct endpoint_index {};
49  typedef boost::multi_index_container<potential_peer_record,
50  indexed_by<ordered_non_unique<tag<last_seen_time_index>,
51  member<potential_peer_record,
54  std::greater<fc::time_point_sec> >,
55  hashed_unique<tag<endpoint_index>,
56  member<potential_peer_record,
60 
61  private:
62  potential_peer_set _potential_peer_set;
63  fc::path _peer_database_filename;
64 
65  public:
66  void open(const fc::path& databaseFilename);
67  void close();
68  void clear();
69  void erase(const fc::ip::endpoint& endpointToErase);
70  void update_entry(const potential_peer_record& updatedRecord);
71  potential_peer_record lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);
72  fc::optional<potential_peer_record> lookup_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);
73 
74  peer_database::iterator begin() const;
75  peer_database::iterator end() const;
76  size_t size() const;
77  };
78 
80  {
81  public:
82  typedef peer_database_impl::potential_peer_set::index<peer_database_impl::last_seen_time_index>::type::iterator last_seen_time_index_iterator;
83  last_seen_time_index_iterator _iterator;
84  explicit peer_database_iterator_impl(const last_seen_time_index_iterator& iterator) :
85  _iterator(iterator)
86  {}
87  };
89  boost::iterator_facade<peer_database_iterator, const potential_peer_record, boost::forward_traversal_tag>(c){}
90 
91  void peer_database_impl::open(const fc::path& peer_database_filename)
92  {
93  _peer_database_filename = peer_database_filename;
94  if (fc::exists(_peer_database_filename))
95  {
96  try
97  {
98  std::vector<potential_peer_record> peer_records = fc::json::from_file(_peer_database_filename).as<std::vector<potential_peer_record> >( GRAPHENE_NET_MAX_NESTED_OBJECTS );
99  std::copy(peer_records.begin(), peer_records.end(), std::inserter(_potential_peer_set, _potential_peer_set.end()));
100  if (_potential_peer_set.size() > MAXIMUM_PEERDB_SIZE)
101  {
102  // prune database to a reasonable size
103  auto iter = _potential_peer_set.begin();
104  std::advance(iter, MAXIMUM_PEERDB_SIZE);
105  _potential_peer_set.erase(iter, _potential_peer_set.end());
106  }
107  }
108  catch (const fc::exception& e)
109  {
110  elog("error opening peer database file ${peer_database_filename}, starting with a clean database",
111  ("peer_database_filename", _peer_database_filename));
112  }
113  }
114  }
115 
117  {
118  std::vector<potential_peer_record> peer_records;
119  peer_records.reserve(_potential_peer_set.size());
120  std::copy(_potential_peer_set.begin(), _potential_peer_set.end(), std::back_inserter(peer_records));
121 
122  try
123  {
124  fc::path peer_database_filename_dir = _peer_database_filename.parent_path();
125  if (!fc::exists(peer_database_filename_dir))
126  fc::create_directories(peer_database_filename_dir);
127  fc::json::save_to_file( peer_records, _peer_database_filename, GRAPHENE_NET_MAX_NESTED_OBJECTS );
128  }
129  catch (const fc::exception& e)
130  {
131  elog("error saving peer database to file ${peer_database_filename}",
132  ("peer_database_filename", _peer_database_filename));
133  }
134  _potential_peer_set.clear();
135  }
136 
138  {
139  _potential_peer_set.clear();
140  }
141 
142  void peer_database_impl::erase(const fc::ip::endpoint& endpointToErase)
143  {
144  auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToErase);
145  if (iter != _potential_peer_set.get<endpoint_index>().end())
146  _potential_peer_set.get<endpoint_index>().erase(iter);
147  }
148 
150  {
151  auto iter = _potential_peer_set.get<endpoint_index>().find(updatedRecord.endpoint);
152  if (iter != _potential_peer_set.get<endpoint_index>().end())
153  _potential_peer_set.get<endpoint_index>().modify(iter, [&updatedRecord](potential_peer_record& record) { record = updatedRecord; });
154  else
155  _potential_peer_set.get<endpoint_index>().insert(updatedRecord);
156  }
157 
159  {
160  auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToLookup);
161  if (iter != _potential_peer_set.get<endpoint_index>().end())
162  return *iter;
163  return potential_peer_record(endpointToLookup);
164  }
165 
167  {
168  auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToLookup);
169  if (iter != _potential_peer_set.get<endpoint_index>().end())
170  return *iter;
172  }
173 
175  {
176  return peer_database::iterator(new peer_database_iterator_impl(_potential_peer_set.get<last_seen_time_index>().begin()));
177  }
178 
180  {
181  return peer_database::iterator(new peer_database_iterator_impl(_potential_peer_set.get<last_seen_time_index>().end()));
182  }
183 
185  {
186  return _potential_peer_set.size();
187  }
188 
190  {
191  }
192 
194  {
195  }
196 
198  my(impl)
199  {
200  }
201 
202  void peer_database_iterator::increment()
203  {
204  ++my->_iterator;
205  }
206 
207  bool peer_database_iterator::equal(const peer_database_iterator& other) const
208  {
209  return my->_iterator == other.my->_iterator;
210  }
211 
212  const potential_peer_record& peer_database_iterator::dereference() const
213  {
214  return *my->_iterator;
215  }
216 
217  } // end namespace detail
218 
220  my(new detail::peer_database_impl)
221  {
222  }
223 
225  {}
226 
227  void peer_database::open(const fc::path& databaseFilename)
228  {
229  my->open(databaseFilename);
230  }
231 
233  {
234  my->close();
235  }
236 
238  {
239  my->clear();
240  }
241 
242  void peer_database::erase(const fc::ip::endpoint& endpointToErase)
243  {
244  my->erase(endpointToErase);
245  }
246 
248  {
249  my->update_entry(updatedRecord);
250  }
251 
253  {
254  return my->lookup_or_create_entry_for_endpoint(endpointToLookup);
255  }
256 
258  {
259  return my->lookup_entry_for_endpoint(endpoint_to_lookup);
260  }
261 
263  {
264  return my->begin();
265  }
266 
268  {
269  return my->end();
270  }
271 
272  size_t peer_database::size() const
273  {
274  return my->size();
275  }
276 
277 } } // end namespace graphene::net
278 
284  (endpoint)(last_seen_time)(last_connection_disposition)
285  (last_connection_attempt_time)(number_of_successful_connection_attempts)
286  (number_of_failed_connection_attempts)(last_error) )
287 
boost::multi_index_container< potential_peer_record, indexed_by< ordered_non_unique< tag< last_seen_time_index >, member< potential_peer_record, fc::time_point_sec,&potential_peer_record::last_seen_time >, std::greater< fc::time_point_sec > >, hashed_unique< tag< endpoint_index >, member< potential_peer_record, fc::ip::endpoint,&potential_peer_record::endpoint >, std::hash< fc::ip::endpoint > > > > potential_peer_set
void update_entry(const potential_peer_record &updatedRecord)
bool exists(const path &p)
Definition: filesystem.cpp:209
T as(uint32_t max_depth) const
Definition: variant.hpp:336
fc::optional< potential_peer_record > lookup_entry_for_endpoint(const fc::ip::endpoint &endpointToLookup)
potential_peer_record lookup_or_create_entry_for_endpoint(const fc::ip::endpoint &endpointToLookup)
#define GRAPHENE_NET_MAX_NESTED_OBJECTS
Definition: config.hpp:110
Definition: api.cpp:56
#define elog(FORMAT,...)
Definition: logger.hpp:129
fc::optional< potential_peer_record > lookup_entry_for_endpoint(const fc::ip::endpoint &endpointToLookup)
peer_database_iterator_impl(const last_seen_time_index_iterator &iterator)
#define FC_REFLECT_DERIVED_NO_TYPENAME(TYPE, INHERITS, MEMBERS)
Definition: reflect.hpp:356
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
void create_directories(const path &p)
Definition: filesystem.cpp:210
static variant from_file(const fc::path &p, parse_type ptype=legacy_parser, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition: json.cpp:755
peer_database::iterator begin() const
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
FC_REFLECT_ENUM(graphene::net::core_message_type_enum,(trx_message_type)(block_message_type)(core_message_type_first)(item_ids_inventory_message_type)(blockchain_item_ids_inventory_message_type)(fetch_blockchain_item_ids_message_type)(fetch_items_message_type)(item_not_available_message_type)(hello_message_type)(connection_accepted_message_type)(connection_rejected_message_type)(address_request_message_type)(address_message_type)(closing_connection_message_type)(current_time_request_message_type)(current_time_reply_message_type)(check_firewall_message_type)(check_firewall_reply_message_type)(get_current_connections_request_message_type)(get_current_connections_reply_message_type)(core_message_type_last))(different_chain)(already_connected)(connected_to_self)(not_accepting_connections)(blocked)(invalid_hello_message)(client_too_old))(inbound)(outbound))(firewalled)(not_firewalled))(unable_to_connect)(connection_successful)) namespace std
peer_database_impl::potential_peer_set::index< peer_database_impl::last_seen_time_index >::type::iterator last_seen_time_index_iterator
void erase(const fc::ip::endpoint &endpointToErase)
detail::peer_database_iterator iterator
peer_database::iterator end() const
void update_entry(const potential_peer_record &updatedRecord)
boost::asio::ip::tcp::endpoint endpoint
Definition: asio.hpp:240
#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type)
Definition: types.hpp:74
potential_peer_record lookup_or_create_entry_for_endpoint(const fc::ip::endpoint &endpointToLookup)
potential_peer_last_connection_disposition
fc::path parent_path() const
Definition: filesystem.cpp:163
void erase(const fc::ip::endpoint &endpointToErase)
void copy(const path &from, const path &to)
Definition: filesystem.cpp:241
#define MAXIMUM_PEERDB_SIZE
Definition: config.hpp:112
wraps boost::filesystem::path to provide platform independent path manipulation.
Definition: filesystem.hpp:28
void open(const fc::path &databaseFilename)
void open(const fc::path &databaseFilename)
static void save_to_file(const T &v, const fc::path &fi, bool pretty=true, output_formatting format=stringify_large_ints_and_doubles, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition: json.hpp:51