void responder::send_pool_tx(const code& ec, const transaction& tx, const hash_digest& tx_hash, channel::ptr node) { if (ec == error::service_stopped) return; if (ec == error::not_found) { log::debug(LOG_RESPONDER) << "Transaction for [" << node->authority() << "] not in mempool [" << encode_hash(tx_hash) << "]"; // It wasn't in the mempool, so relay the request to the blockchain. blockchain_.fetch_transaction(tx_hash, std::bind(&responder::send_chain_tx, this, _1, _2, tx_hash, node)); return; } if (ec) { log::error(LOG_RESPONDER) << "Failure fetching mempool tx data for [" << node->authority() << "] " << ec.message(); node->stop(ec); return; } send_tx(tx, tx_hash, node); }
void session::handle_is_pending(bool pending, channel::ptr channel, result_handler handle_started) { if (pending) { log::debug(LOG_NETWORK) << "Rejected connection from [" << channel->authority() << "] as loopback."; handle_started(error::accept_failed); return; } const auto version = channel->version(); if (version.value < bc::peer_minimum_version) { log::debug(LOG_NETWORK) << "Peer version (" << version.value << ") below minimum (" << bc::peer_minimum_version << ") [" << channel->authority() << "]"; handle_started(error::accept_failed); return; } network_.store(channel, handle_started); }
// protected: void session::register_channel(channel::ptr channel, result_handler handle_started, result_handler handle_stopped) { result_handler stop_handler = BIND_3(do_remove, _1, channel, handle_stopped); result_handler start_handler = BIND_4(handle_start, _1, channel, handle_started, stop_handler); if (stopped()) { start_handler(error::service_stopped); return; } if (incoming_) { handle_pend(error::success, channel, start_handler); return; } channel->set_notify(notify_); channel->set_nonce(nonzero_pseudo_random()); result_handler unpend_handler = BIND_3(do_unpend, _1, channel, start_handler); pending_.store(channel, BIND_3(handle_pend, _1, channel, unpend_handler)); }
// en.bitcoin.it/wiki/Protocol_documentation#getdata // getdata can be used to retrieve transactions, but only if they are // in the memory pool or relay set - arbitrary access to transactions // in the chain is not allowed to avoid having clients start to depend // on nodes having full transaction indexes (which modern nodes do not). void responder::send_chain_tx(const code& ec, const transaction& tx, const hash_digest& tx_hash, channel::ptr node) { if (ec == error::service_stopped) return; if (ec == error::not_found) { log::debug(LOG_RESPONDER) << "Transaction for [" << node->authority() << "] not in blockchain [" << encode_hash(tx_hash) << "]"; // It wasn't in the blockchain, so send notfound. send_tx_not_found(tx_hash, node); return; } if (ec) { log::error(LOG_RESPONDER) << "Failure fetching blockchain tx data for [" << node->authority() << "] " << ec.message(); node->stop(ec); return; } send_tx(tx, tx_hash, node); }
// We don't seem to be getting getdata requests. void responder::receive_get_data(const code& ec, const get_data& packet, channel::ptr node) { if (ec == error::channel_stopped) return; const auto peer = node->authority(); if (ec) { log::debug(LOG_RESPONDER) << "Failure in receive get data [" << peer << "] " << ec.message(); node->stop(ec); return; } // Resubscribe to serve tx and blocks. node->subscribe<message::get_data>( std::bind(&responder::receive_get_data, this, _1, _2, node)); log::debug(LOG_RESPONDER) << "Getdata BEGIN [" << peer << "] " << "txs (" << packet.count(inventory_type_id::transaction) << ") " << "blocks (" << packet.count(inventory_type_id::block) << ") " << "bloom (" << packet.count(inventory_type_id::filtered_block) << ")"; for (const auto& inventory: packet.inventories) { switch (inventory.type) { case inventory_type_id::transaction: log::debug(LOG_RESPONDER) << "Transaction getdata for [" << peer << "] " << encode_hash(inventory.hash); tx_pool_.fetch(inventory.hash, std::bind(&responder::send_pool_tx, this, _1, _2, inventory.hash, node)); break; case inventory_type_id::block: log::debug(LOG_RESPONDER) << "Block getdata for [" << peer << "] " << encode_hash(inventory.hash); block_fetcher::fetch(blockchain_, inventory.hash, std::bind(&responder::send_block, this, _1, _2, inventory.hash, node)); break; case inventory_type_id::error: case inventory_type_id::none: default: log::debug(LOG_RESPONDER) << "Ignoring invalid getdata type for [" << peer << "]"; } } log::debug(LOG_RESPONDER) << "Getdata END [" << peer << "]"; }
void session_inbound::handle_accept(const code& ec, channel::ptr channel, acceptor::ptr accept) { if (stopped()) return; start_accept(error::success, accept); if (ec) { log::debug(LOG_NETWORK) << "Failure accepting connection: " << ec.message(); return; } if (blacklisted(channel->authority())) { log::debug(LOG_NETWORK) << "Rejected inbound connection from [" << channel->authority() << "] due to blacklisted address."; return; } connection_count( dispatch_.ordered_delegate(&session_inbound::handle_connection_count, shared_from_base<session_inbound>(), _1, channel)); }
void session_inbound::handle_accept(const code& ec, channel::ptr channel, acceptor::ptr accept) { if (stopped()) { log::debug(LOG_NETWORK) << "Suspended inbound connection."; return; } start_accept(error::success, accept); if (ec) { log::debug(LOG_NETWORK) << "Failure accepting connection: " << ec.message(); return; } if (blacklisted(channel->authority())) { log::debug(LOG_NETWORK) << "Rejected inbound connection from [" << channel->authority() << "] due to blacklisted address."; return; } connection_count(BIND2(handle_connection_count, _1, channel)); }
// protected: void session::start_channel(channel::ptr channel, result_handler handle_started) { channel->set_notify(notify_on_connect_); channel->set_nonce(pseudo_random(1, max_uint64)); // The channel starts, invokes the handler, then starts the read cycle. channel->start( BIND_3(handle_starting, _1, channel, handle_started)); }
void session::handle_start(const code& ec, channel::ptr channel, result_handler handle_started, result_handler handle_stopped) { // Must either stop or subscribe the channel for stop before returning. if (ec) channel->stop(ec); else channel->subscribe_stop(handle_stopped); // This is the end of the registration sequence. handle_started(ec); }
void protocol::handle_accept(const code& ec, channel::ptr node, acceptor::ptr accept) { // Relisten for connections. start_accept(ec, accept); if (ec) { log_debug(LOG_PROTOCOL) << "Failure accepting connection: " << ec.message(); return; } if (inbound_connections_.size() >= max_inbound_) { log_debug(LOG_PROTOCOL) << "Rejected inbound connection due to connection limit"; return; } const auto address = node->address(); if (is_blacklisted(node->address())) { log_debug(LOG_PROTOCOL) << "Rejected inbound connection due to blacklisted address"; return; } if (is_loopback(node)) { log_debug(LOG_PROTOCOL) << "Rejected inbound connection from self"; return; } // Save the connection as we are now assured of getting stop event. inbound_connections_.push_back(node); // Accepted! log_info(LOG_PROTOCOL) << "Accepted connection from [" << address << "] (" << inbound_connections_.size() << " total)"; const auto stop_handler = dispatch_.ordered_delegate(&protocol::inbound_channel_stopped, this, _1, node, address.to_string()); start_talking(node, stop_handler, relay_); }
void session_manual::handle_connect(const code& ec, channel::ptr channel, const std::string& hostname, uint16_t port, uint32_t remaining, connector::ptr connector, channel_handler handler) { unpend(connector); if (ec) { LOG_WARNING(LOG_NETWORK) << "Failure connecting [" << config::endpoint(hostname, port) << "] manually: " << ec.message(); // Retry logic. // The handler invoke is the failure end of the connect sequence. if (settings_.manual_attempt_limit == 0) start_connect(hostname, port, 0, handler); else if (remaining > 0) start_connect(hostname, port, remaining - 1, handler); else handler(ec, nullptr); return; } LOG_INFO(LOG_NETWORK) << "Connected manual channel [" << config::endpoint(hostname, port) << "] as [" << channel->authority() << "]"; register_channel(channel, BIND5(handle_channel_start, _1, hostname, port, channel, handler), BIND3(handle_channel_stop, _1, hostname, port)); }
void protocol::handle_handshake(const code& ec, channel::ptr node) { if (ec) { log_debug(LOG_PROTOCOL) << "Failure in peer handshake [" << node->address() << "] " << ec.message(); node->stop(ec); return; } // Attach ping protocol to the new connection (until node stop event). std::make_shared<protocol_ping>(node, pool_, timeouts_.heartbeat)->start(); // Attach address protocol to the new connection (until node stop event). std::make_shared<protocol_address>(node, pool_, hosts_, self_)->start(); }
bool pending_channels::safe_store(channel::ptr channel) { const auto version_nonce = channel->nonce(); const auto match = [version_nonce](const channel::ptr& entry) { return entry->nonce() == version_nonce; }; // Critical Section /////////////////////////////////////////////////////////////////////////// mutex_.lock_upgrade(); const auto it = std::find_if(channels_.begin(), channels_.end(), match); const auto found = it != channels_.end(); if (!found) { //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mutex_.unlock_upgrade_and_lock(); channels_.push_back(channel); mutex_.unlock(); //--------------------------------------------------------------------- return true; } mutex_.unlock_upgrade(); /////////////////////////////////////////////////////////////////////////// return false; }
void session::do_unpend(const code& ec, channel::ptr channel, result_handler handle_started) { channel->set_nonce(0); pending_.remove(channel, BIND_1(handle_unpend, _1)); handle_started(ec); }
void session::handle_remove(const code& ec, channel::ptr channel) { if (ec) LOG_DEBUG(LOG_NETWORK) << "Failed to remove channel [" << channel->authority() << "] " << ec.message(); }
code p2p::store(channel::ptr channel) { const auto address = channel->authority(); const auto match = [&address](const channel::ptr& element) { return element->authority() == address; }; // May return error::address_in_use. const auto ec = pending_close_.store(channel, match); if (!ec && channel->notify()) channel_subscriber_->relay(error::success, channel); return ec; }
void session_manual::handle_connect(const code& ec, channel::ptr channel, const std::string& hostname, uint16_t port, channel_handler handler, uint16_t retries) { if (ec) { log::warning(LOG_NETWORK) << "Failure connecting [" << config::endpoint(hostname, port) << "] manually: " << ec.message(); // Retry logic. if (settings_.connect_attempts == 0) start_connect(hostname, port, handler, 0); else if (retries > 0) start_connect(hostname, port, handler, retries - 1); else handler(ec, nullptr); return; } log::info(LOG_NETWORK) << "Connected manual channel [" << config::endpoint(hostname, port) << "] as [" << channel->authority() << "]"; register_channel(channel, std::bind(&session_manual::handle_channel_start, shared_from_base<session_manual>(), _1, hostname, port, channel, handler), std::bind(&session_manual::handle_channel_stop, shared_from_base<session_manual>(), _1, hostname, port)); }
void responder::send_tx_not_found(const hash_digest& hash, channel::ptr node) { const auto send_handler = [hash, node](const code& ec) { if (ec) log::debug(LOG_RESPONDER) << "Failure sending tx notfound for [" << node->authority() << "]"; else log::debug(LOG_RESPONDER) << "Sent tx notfound for [" << node->authority() << "] " << encode_hash(hash); }; send_inventory_not_found(inventory_type_id::transaction, hash, node, send_handler); }
void responder::send_tx(const transaction& tx, const hash_digest& hash, channel::ptr node) { const auto send_handler = [hash, node](const code& ec) { if (ec) log::debug(LOG_RESPONDER) << "Failure sending tx for [" << node->authority() << "]"; else log::debug(LOG_RESPONDER) << "Sent tx for [" << node->authority() << "] " << encode_hash(hash); }; node->send(tx, send_handler); }
void session_manual::attach_protocols(channel::ptr channel) { if (channel->negotiated_version() >= message::version::level::bip31) attach<protocol_ping_60001>(channel)->start(); else attach<protocol_ping_31402>(channel)->start(); attach<protocol_address_31402>(channel)->start(); }
void p2p::handle_new_connection(const code& ec, channel::ptr channel, result_handler handler) { // Connection-in-use indicated here by error::address_in_use. handler(ec); if (!ec && channel->notify()) channel_subscriber_->relay(error::success, channel); }
void session_outbound::handle_channel_stop(const code& ec, connector::ptr connect, channel::ptr channel) { log::debug(LOG_NETWORK) << "Outbound channel stopped [" << channel->authority() << "] " << ec.message(); new_connection(connect); }
void responder::send_block_not_found(const hash_digest& block_hash, channel::ptr node) { const auto send_handler = [block_hash, node](const code& ec) { if (ec) log::debug(LOG_RESPONDER) << "Failure sending block notfound for [" << node->authority() << "]"; else log::debug(LOG_RESPONDER) << "Sent block notfound for [" << node->authority() << "] " << encode_hash(block_hash); }; send_inventory_not_found(inventory_type_id::block, block_hash, node, send_handler); }
// protected: void session::attach_handshake_protocols(channel::ptr channel, result_handler handle_started) { // The negotiated_version is initialized to the configured maximum. if (channel->negotiated_version() >= message::version::level::bip61) attach<protocol_version_70002>(channel)->start(handle_started); else attach<protocol_version_31402>(channel)->start(handle_started); }
void session::handle_start(const code& ec, channel::ptr channel, result_handler handle_started, result_handler handle_stopped) { // Must either stop or subscribe the channel for stop before returning. // All closures must eventually be invoked as otherwise it is a leak. // Therefore upon start failure expect start failure and stop callbacks. if (ec) { channel->stop(ec); handle_stopped(ec); } else { channel->subscribe_stop( BIND_3(do_remove, _1, channel, handle_stopped)); } // This is the end of the registration sequence. handle_started(ec); }
void session_inbound::handle_connection_count(size_t connections, channel::ptr channel) { if (connections >= settings_.inbound_connection_limit) { log::debug(LOG_NETWORK) << "Rejected inbound connection from [" << channel->authority() << "] due to connection limit."; return; } log::info(LOG_NETWORK) << "Connected inbound channel [" << channel->authority() << "]"; register_channel(channel, std::bind(&session_inbound::handle_channel_start, shared_from_base<session_inbound>(), _1, channel), std::bind(&session_inbound::handle_channel_stop, shared_from_base<session_inbound>(), _1)); }
void responder::send_inventory_not_found(inventory_type_id type_id, const hash_digest& hash, channel::ptr node, proxy::result_handler handler) { const inventory_vector block_inventory { type_id, hash }; const not_found lost{ { block_inventory } }; node->send(lost, handler); }
void session::handle_handshake(const code& ec, channel::ptr channel, result_handler handle_started) { if (ec) { log::debug(LOG_NETWORK) << "Failure in handshake with [" << channel->authority() << "] " << ec.message(); handle_started(ec); return; } truth_handler handler = BIND_3(handle_is_pending, _1, channel, handle_started); // The loopback test is for incoming channels only. if (incoming_) pending_.exists(channel->version().nonce, handler); else handler(false); }
bool protocol::is_loopback(channel::ptr node) const { const auto& outbound = outbound_connections_; const auto id = node->identifier(); const auto found = [node, id](const channel::ptr& entry) { return (entry != node) && (entry->identifier() == id); }; auto it = std::find_if(outbound.begin(), outbound.end(), found); return it != outbound.end(); return true; }
void session_inbound::handle_connection_count(size_t connections, channel::ptr channel) { const auto connection_limit = settings_.inbound_connections + settings_.outbound_connections; if (connections >= connection_limit) { log::debug(LOG_NETWORK) << "Rejected inbound connection from [" << channel->authority() << "] due to connection limit."; return; } log::info(LOG_NETWORK) << "Connected inbound channel [" << channel->authority() << "]"; register_channel(channel, BIND2(handle_channel_start, _1, channel), BIND1(handle_channel_stop, _1)); }