BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
peer_connection.hpp
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 #pragma once
25 
26 #include <graphene/net/node.hpp>
29 #include <graphene/net/config.hpp>
30 
31 #include <boost/tuple/tuple.hpp>
32 
33 #include <boost/multi_index_container.hpp>
34 #include <boost/multi_index/ordered_index.hpp>
35 #include <boost/multi_index/mem_fun.hpp>
36 #include <boost/multi_index/member.hpp>
37 #include <boost/multi_index/tag.hpp>
38 #include <boost/multi_index/hashed_index.hpp>
39 
40 #include <queue>
41 #include <boost/container/deque.hpp>
42 #include <fc/thread/future.hpp>
43 
44 namespace graphene { namespace net
45  {
47  {
50 
51  // if we're coordinating a firewall check for another node, these are the helper
52  // nodes we've already had do the test (if this structure is still relevant, that
53  // that means they have all had indeterminate results
54  std::set<node_id_t> nodes_already_tested;
55 
56  // If we're a just a helper node, this is the node we report back to
57  // when we have a result
59  };
60 
61  class peer_connection;
63  {
64  public:
65  virtual ~peer_connection_delegate() = default;
66  virtual void on_message(peer_connection* originating_peer,
67  const message& received_message) = 0;
68  virtual void on_connection_closed(peer_connection* originating_peer) = 0;
69  virtual message get_message_for_item(const item_id& item) = 0;
70  };
71 
73  typedef std::shared_ptr<peer_connection> peer_connection_ptr;
75  public std::enable_shared_from_this<peer_connection>
76  {
77  public:
79  {
80  disconnected,
81  just_connected, // if in this state, we have sent a hello_message
82  connection_accepted, // remote side has sent us a connection_accepted, we're operating normally with them
83  connection_rejected // remote side has sent us a connection_rejected, we may be exchanging address with them or may just be waiting for them to close
84  };
86  {
87  disconnected,
88  just_connected, // we have not yet received a hello_message
89  connection_accepted, // we have sent them a connection_accepted
90  connection_rejected // we have sent them a connection_rejected
91  };
93  {
94  disconnected,
95  connecting,
96  connected,
97  accepting,
98  accepted,
99  hello_sent,
100  peer_connection_accepted,
101  peer_connection_rejected,
102  negotiation_complete,
103  closing,
104  closed
105  };
106  private:
108  fc::optional<fc::ip::endpoint> _remote_endpoint;
109  message_oriented_connection _message_connection;
110 
111  /* a base class for messages on the queue, to hide the fact that some
112  * messages are complete messages and some are only hashes of messages.
113  */
114  struct queued_message
115  {
116  fc::time_point enqueue_time;
117  fc::time_point transmission_start_time;
118  fc::time_point transmission_finish_time;
119 
120  queued_message(fc::time_point enqueue_time = fc::time_point::now()) :
121  enqueue_time(enqueue_time)
122  {}
123 
124  virtual message get_message(peer_connection_delegate* node) = 0;
128  virtual size_t get_size_in_queue() = 0;
129  virtual ~queued_message() {}
130  };
131 
132  /* when you queue up a 'real_queued_message', a full copy of the message is
133  * stored on the heap until it is sent
134  */
135  struct real_queued_message : queued_message
136  {
137  message message_to_send;
138  size_t message_send_time_field_offset;
139 
140  real_queued_message(message message_to_send,
141  size_t message_send_time_field_offset = (size_t)-1) :
142  message_to_send(std::move(message_to_send)),
143  message_send_time_field_offset(message_send_time_field_offset)
144  {}
145 
146  message get_message(peer_connection_delegate* node) override;
147  size_t get_size_in_queue() override;
148  };
149 
150  /* when you queue up a 'virtual_queued_message', we just queue up the hash of the
151  * item we want to send. When it reaches the top of the queue, we make a callback
152  * to the node to generate the message.
153  */
154  struct virtual_queued_message : queued_message
155  {
156  item_id item_to_send;
157 
158  virtual_queued_message(item_id item_to_send) :
159  item_to_send(std::move(item_to_send))
160  {}
161 
162  message get_message(peer_connection_delegate* node) override;
163  size_t get_size_in_queue() override;
164  };
165 
166 
167  size_t _total_queued_messages_size = 0;
168  std::queue<std::unique_ptr<queued_message>, std::list<std::unique_ptr<queued_message> > > _queued_messages;
169  fc::future<void> _send_queued_messages_done;
170  public:
175  //connection_state state;
179 
180  our_connection_state our_state = our_connection_state::disconnected;
181  bool they_have_requested_close = false;
182  their_connection_state their_state = their_connection_state::disconnected;
183  bool we_have_requested_close = false;
184 
185  connection_negotiation_status negotiation_status = connection_negotiation_status::disconnected;
187 
188  fc::time_point get_connection_time()const { return _message_connection.get_connection_time(); }
189  fc::time_point get_connection_terminated_time()const { return connection_terminated_time; }
190 
193 
200  uint32_t core_protocol_version = 0;
201  std::string user_agent;
208 
209  // for inbound connections, these fields record what the peer sent us in
210  // its hello message. For outbound, they record what we sent the peer
211  // in our hello message
213  uint16_t inbound_port = 0;
214  uint16_t outbound_port = 0;
216 
217  typedef std::unordered_map<item_id, fc::time_point> item_to_time_map_type;
218 
221  boost::container::deque<item_hash_t> ids_of_items_to_get;
222  std::set<item_hash_t> ids_of_items_being_processed;
223  uint32_t number_of_unfetched_item_ids = 0;
224  bool peer_needs_sync_items_from_us = false;
225  bool we_need_sync_items_from_peer = false;
228  std::set<item_hash_t> sync_items_requested_from_peer;
231  bool inhibit_fetching_sync_blocks = false;
233 
237  {
240  timestamped_item_id(const item_id& item, const fc::time_point_sec timestamp) :
241  item(item),
242  timestamp(timestamp)
243  {}
244  };
245  struct timestamp_index{};
246  typedef boost::multi_index_container<timestamped_item_id,
247  boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::member<timestamped_item_id, item_id, &timestamped_item_id::item>,
248  std::hash<item_id> >,
249  boost::multi_index::ordered_non_unique<boost::multi_index::tag<timestamp_index>,
250  boost::multi_index::member<timestamped_item_id, fc::time_point_sec, &timestamped_item_id::timestamp> > > > timestamped_items_set_type;
251  timestamped_items_set_type inventory_peer_advertised_to_us;
252  timestamped_items_set_type inventory_advertised_to_peer;
253 
254  item_to_time_map_type items_requested_from_peer;
255 
257  // if they're flooding us with transactions, we set this to avoid fetching for a few seconds to let the
258  // blockchain catch up
260 
261  uint32_t last_known_fork_block_number = 0;
262 
264 
265  firewall_check_state_data *firewall_check_state = nullptr;
266  private:
267 #ifndef NDEBUG
268  fc::thread* _thread = nullptr;
269  unsigned _send_message_queue_tasks_running = 0; // temporary debugging
270 #endif
271  bool _currently_handling_message = false; // true while we're in the middle of handling a message from the remote system
273  void destroy();
274  public:
275  static peer_connection_ptr make_shared(peer_connection_delegate* delegate); // use this instead of the constructor
276  virtual ~peer_connection();
277 
278  fc::tcp_socket& get_socket();
279  void accept_connection();
280  void connect_to(const fc::ip::endpoint& remote_endpoint, fc::optional<fc::ip::endpoint> local_endpoint = fc::optional<fc::ip::endpoint>());
281 
282  void on_message(message_oriented_connection* originating_connection, const message& received_message) override;
283  void on_connection_closed(message_oriented_connection* originating_connection) override;
284 
285  void send_queueable_message(std::unique_ptr<queued_message>&& message_to_send);
286  void send_message(const message& message_to_send, size_t message_send_time_field_offset = (size_t)-1);
287  void send_item(const item_id& item_to_send);
288  void close_connection();
289  void destroy_connection();
290 
291  uint64_t get_total_bytes_sent() const;
292  uint64_t get_total_bytes_received() const;
293 
294  fc::time_point get_last_message_sent_time() const;
295  fc::time_point get_last_message_received_time() const;
296 
297  fc::optional<fc::ip::endpoint> get_remote_endpoint();
298  fc::ip::endpoint get_local_endpoint();
299  void set_remote_endpoint(fc::optional<fc::ip::endpoint> new_remote_endpoint);
300 
301  bool busy() const;
302  bool idle() const;
303  bool is_currently_handling_message() const;
304 
305  bool is_transaction_fetching_inhibited() const;
306  fc::sha512 get_shared_secret() const;
307  void clear_old_inventory();
308  bool is_inventory_advertised_to_us_list_full_for_transactions() const;
309  bool is_inventory_advertised_to_us_list_full() const;
310  bool performing_firewall_check() const;
311  fc::optional<fc::ip::endpoint> get_endpoint_for_connecting() const;
312  private:
313  void send_queued_messages_task();
314  void accept_connection_task();
315  void connect_to_task(const fc::ip::endpoint& remote_endpoint);
316  };
317  typedef std::shared_ptr<peer_connection> peer_connection_ptr;
318 
319  } } // end namespace graphene::net
320 
321 // not sent over the wire, just reflected for logging
323  (just_connected)
324  (connection_accepted)
325  (connection_rejected))
327  (just_connected)
328  (connection_accepted)
329  (connection_rejected))
331  (connecting)
332  (connected)
333  (accepting)
334  (accepted)
335  (hello_sent)
336  (peer_connection_accepted)
337  (peer_connection_rejected)
338  (negotiation_complete)
339  (closing)
340  (closed) )
341 
342 FC_REFLECT( graphene::net::peer_connection::timestamped_item_id, (item)(timestamp));
item_to_time_map_type items_requested_from_peer
fc::future< void > accept_or_connect_task_done
item_hash_t last_block_delegate_has_seen
ids of blocks we&#39;ve requested from this peer during sync. fetch from another peer if this peer discon...
timestamped_items_set_type inventory_peer_advertised_to_us
const uint32_t core_protocol_version
#define FC_REFLECT(TYPE, MEMBERS)
Specializes fc::reflector for TYPE.
Definition: reflect.hpp:386
std::unordered_map< item_id, fc::time_point > item_to_time_map_type
Definition: api.cpp:56
std::set< item_hash_t > sync_items_requested_from_peer
the time we received the last sync item or the time we sent the last batch of sync item requests to t...
timestamped_items_set_type inventory_advertised_to_peer
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
provides application independent P2P broadcast and data synchronization
Definition: node.hpp:192
fc::time_point last_sync_item_received_time
we check this to detect a timed-out request and in busy()
fc::time_point transaction_fetching_inhibited_until
timestamped_item_id(const item_id &item, const fc::time_point_sec timestamp)
fc::optional< std::string > platform
fc::optional< std::string > fc_git_revision_sha
std::shared_ptr< peer_connection > peer_connection_ptr
fc::optional< uint32_t > bitness
fc::optional< boost::tuple< std::vector< item_hash_t >, fc::time_point > > item_ids_requested_from_peer
fc::optional< std::string > graphene_git_revision_sha
fc::time_point get_connection_terminated_time() const
fc::optional< fc::time_point_sec > fc_git_revision_unix_timestamp
fc::optional< fc::time_point_sec > graphene_git_revision_unix_timestamp
std::set< item_hash_t > ids_of_items_being_processed
id of items in the blockchain that this peer has told us about
static time_point now()
Definition: time.cpp:13
boost::multi_index_container< timestamped_item_id, boost::multi_index::indexed_by< boost::multi_index::hashed_unique< boost::multi_index::member< timestamped_item_id, item_id,&timestamped_item_id::item >, std::hash< item_id > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< timestamp_index >, boost::multi_index::member< timestamped_item_id, fc::time_point_sec,&timestamped_item_id::timestamp > > > > timestamped_items_set_type
boost::container::deque< item_hash_t > ids_of_items_to_get
fc::time_point_sec last_block_time_delegate_has_seen
the hash of the last block this peer has told us about that the peer knows
fc::time_point get_connection_time() const