int main(int argc, char** argv) { std::queue<fc::ip::endpoint> nodes_to_visit; std::set<fc::ip::endpoint> nodes_to_visit_set; std::set<fc::ip::endpoint> nodes_already_visited; if ( argc < 3 ) { std::cerr << "Usage: " << argv[0] << " <chain-id> <seed-addr> [<seed-addr> ...]\n"; exit(1); } const graphene::chain::chain_id_type chain_id( argv[1] ); for ( int i = 2; i < argc; i++ ) { std::string ep(argv[i]); uint16_t port; auto pos = ep.find(':'); if (pos > 0) port = boost::lexical_cast<uint16_t>( ep.substr( pos+1, ep.size() ) ); else port = 1776; for (const auto& addr : fc::resolve( ep.substr( 0, pos > 0 ? pos : ep.size() ), port )) nodes_to_visit.push( addr ); } fc::path data_dir = fc::temp_directory_path() / ("network_map_" + (fc::string) chain_id); fc::create_directories(data_dir); fc::ip::endpoint seed_node1 = nodes_to_visit.front(); fc::ecc::private_key my_node_id = fc::ecc::private_key::generate(); std::map<graphene::net::node_id_t, graphene::net::address_info> address_info_by_node_id; std::map<graphene::net::node_id_t, std::vector<graphene::net::address_info> > connections_by_node_id; std::vector<std::shared_ptr<peer_probe>> probes; while (!nodes_to_visit.empty() || !probes.empty()) { while (!nodes_to_visit.empty()) { fc::ip::endpoint remote = nodes_to_visit.front(); nodes_to_visit.pop(); nodes_to_visit_set.erase( remote ); nodes_already_visited.insert( remote ); try { std::shared_ptr<peer_probe> probe(new peer_probe()); probe->start(remote, my_node_id, chain_id); probes.emplace_back( std::move( probe ) ); } catch (const fc::exception&) { std::cerr << "Failed to connect " << fc::string(remote) << " - skipping!" << std::endl; } } if (!probes.empty()) { fc::yield(); std::vector<std::shared_ptr<peer_probe>> running; for ( auto& probe : probes ) { if (probe->_probe_complete_promise->error()) { std::cerr << fc::string(probe->_remote) << " ran into an error!\n"; continue; } if (!probe->_probe_complete_promise->ready()) { running.push_back( probe ); continue; } if( probe->_node_id.valid() ) { graphene::net::address_info this_node_info; this_node_info.direction = graphene::net::peer_connection_direction::outbound; this_node_info.firewalled = graphene::net::firewalled_state::not_firewalled; this_node_info.remote_endpoint = probe->_remote; this_node_info.node_id = probe->_node_id; connections_by_node_id[this_node_info.node_id] = probe->_peers; if (address_info_by_node_id.find(this_node_info.node_id) == address_info_by_node_id.end()) address_info_by_node_id[this_node_info.node_id] = this_node_info; } for (const graphene::net::address_info& info : probe->_peers) { if (nodes_already_visited.find(info.remote_endpoint) == nodes_already_visited.end() && info.firewalled == graphene::net::firewalled_state::not_firewalled && nodes_to_visit_set.find(info.remote_endpoint) == nodes_to_visit_set.end()) { nodes_to_visit.push(info.remote_endpoint); nodes_to_visit_set.insert(info.remote_endpoint); } if (address_info_by_node_id.find(info.node_id) == address_info_by_node_id.end()) address_info_by_node_id[info.node_id] = info; } } probes = std::move( running ); std::cout << address_info_by_node_id.size() << " checked, " << probes.size() << " active, " << nodes_to_visit.size() << " to do\n"; } } graphene::net::node_id_t seed_node_id; std::set<graphene::net::node_id_t> non_firewalled_nodes_set; for (const auto& address_info_for_node : address_info_by_node_id) { if (address_info_for_node.second.remote_endpoint == seed_node1) seed_node_id = address_info_for_node.first; if (address_info_for_node.second.firewalled == graphene::net::firewalled_state::not_firewalled) non_firewalled_nodes_set.insert(address_info_for_node.first); } std::set<graphene::net::node_id_t> seed_node_connections; for (const graphene::net::address_info& info : connections_by_node_id[seed_node_id]) seed_node_connections.insert(info.node_id); std::set<graphene::net::node_id_t> seed_node_missing_connections; std::set_difference(non_firewalled_nodes_set.begin(), non_firewalled_nodes_set.end(), seed_node_connections.begin(), seed_node_connections.end(), std::inserter(seed_node_missing_connections, seed_node_missing_connections.end())); seed_node_missing_connections.erase(seed_node_id); std::ofstream dot_stream((data_dir / "network_graph.dot").string().c_str()); dot_stream << "graph G {\n"; dot_stream << " // Total " << address_info_by_node_id.size() << " nodes, firewalled: " << (address_info_by_node_id.size() - non_firewalled_nodes_set.size()) << ", non-firewalled: " << non_firewalled_nodes_set.size() << "\n"; dot_stream << " // Seed node is " << (std::string)address_info_by_node_id[seed_node_id].remote_endpoint << " id: " << fc::variant( seed_node_id, 1 ).as_string() << "\n"; dot_stream << " // Seed node is connected to " << connections_by_node_id[seed_node_id].size() << " nodes\n"; dot_stream << " // Seed node is missing connections to " << seed_node_missing_connections.size() << " non-firewalled nodes:\n"; for (const graphene::net::node_id_t& id : seed_node_missing_connections) dot_stream << " // " << (std::string)address_info_by_node_id[id].remote_endpoint << "\n"; dot_stream << " layout=\"circo\";\n"; for (const auto& address_info_for_node : address_info_by_node_id) { dot_stream << " \"" << fc::variant( address_info_for_node.first, 1 ).as_string() << "\"[label=\"" << (std::string)address_info_for_node.second.remote_endpoint << "\""; if (address_info_for_node.second.firewalled != graphene::net::firewalled_state::not_firewalled) dot_stream << ",shape=rectangle"; dot_stream << "];\n"; } for (auto& node_and_connections : connections_by_node_id) for (const graphene::net::address_info& this_connection : node_and_connections.second) dot_stream << " \"" << fc::variant( node_and_connections.first, 2 ).as_string() << "\" -- \"" << fc::variant( this_connection.node_id, 1 ).as_string() << "\";\n"; dot_stream << "}\n"; return 0; }
int main(int argc, char** argv) { std::queue<fc::ip::endpoint> nodes_to_visit; std::set<fc::ip::endpoint> nodes_to_visit_set; std::set<fc::ip::endpoint> nodes_already_visited; fc::path data_dir = fc::temp_directory_path() / "map_bts_network"; fc::create_directories(data_dir); bts::client::config default_client_config; for (const std::string default_peer : default_client_config.default_peers) nodes_to_visit.push(fc::ip::endpoint::from_string(default_peer)); fc::ip::endpoint seed_node1 = nodes_to_visit.front(); fc::ecc::private_key my_node_id = fc::ecc::private_key::generate(); bts::blockchain::chain_database_ptr chain_db = std::make_shared<bts::blockchain::chain_database>(); chain_db->open(data_dir / "chain", fc::optional<fc::path>("C:/Users/Administrator/AppData/Local/Temp/map_bts_network/genesis.json")); std::map<bts::net::node_id_t, bts::net::address_info> address_info_by_node_id; std::map<bts::net::node_id_t, std::vector<bts::net::address_info> > connections_by_node_id; //std::map<bts::net::node_id_t, fc::ip::endpoint> all_known_nodes; while (!nodes_to_visit.empty()) { bts::net::address_info this_node_info; this_node_info.direction = bts::net::peer_connection_direction::outbound; this_node_info.firewalled = bts::net::firewalled_state::not_firewalled; this_node_info.remote_endpoint = nodes_to_visit.front();; nodes_to_visit.pop(); nodes_to_visit_set.erase(this_node_info.remote_endpoint); nodes_already_visited.insert(this_node_info.remote_endpoint); peer_probe probe; try { probe.start(this_node_info.remote_endpoint, my_node_id, chain_db->chain_id()); probe.wait(); this_node_info.node_id = probe._node_id; connections_by_node_id[this_node_info.node_id] = probe._peers; if (address_info_by_node_id.find(probe._node_id) == address_info_by_node_id.end()) address_info_by_node_id[probe._node_id] = this_node_info; for (const bts::net::address_info& info : probe._peers) { if (nodes_already_visited.find(info.remote_endpoint) == nodes_already_visited.end() && info.firewalled == bts::net::firewalled_state::not_firewalled && nodes_to_visit_set.find(info.remote_endpoint) == nodes_to_visit_set.end()) { nodes_to_visit.push(info.remote_endpoint); nodes_to_visit_set.insert(info.remote_endpoint); } if (address_info_by_node_id.find(info.node_id) == address_info_by_node_id.end()) address_info_by_node_id[info.node_id] = info; } } catch (const fc::exception&) { } std::cout << "Traversed " << nodes_already_visited.size() << " of " << (nodes_already_visited.size() + nodes_to_visit.size()) << " known nodes\n"; } bts::net::node_id_t seed_node_id; std::set<bts::net::node_id_t> non_firewalled_nodes_set; for (const auto& address_info_for_node : address_info_by_node_id) { if (address_info_for_node.second.remote_endpoint == seed_node1) seed_node_id = address_info_for_node.first; if (address_info_for_node.second.firewalled == bts::net::firewalled_state::not_firewalled) non_firewalled_nodes_set.insert(address_info_for_node.first); } std::set<bts::net::node_id_t> seed_node_connections; for (const bts::net::address_info& info : connections_by_node_id[seed_node_id]) seed_node_connections.insert(info.node_id); std::set<bts::net::node_id_t> seed_node_missing_connections; std::set_difference(non_firewalled_nodes_set.begin(), non_firewalled_nodes_set.end(), seed_node_connections.begin(), seed_node_connections.end(), std::inserter(seed_node_missing_connections, seed_node_missing_connections.end())); seed_node_missing_connections.erase(seed_node_id); std::ofstream dot_stream((data_dir / "network_graph.dot").string().c_str()); std::map<bts::net::node_id_t, fc::ip::endpoint> all_known_nodes; dot_stream << "graph G {\n"; dot_stream << " // Total " << address_info_by_node_id.size() << " nodes, firewalled: " << (address_info_by_node_id.size() - non_firewalled_nodes_set.size()) << ", non-firewalled: " << non_firewalled_nodes_set.size() << "\n"; dot_stream << " // Seed node is " << (std::string)address_info_by_node_id[seed_node_id].remote_endpoint << " id: " << fc::variant(seed_node_id).as_string() << "\n"; dot_stream << " // Seed node is connected to " << connections_by_node_id[seed_node_id].size() << " nodes\n"; dot_stream << " // Seed node is missing connections to " << seed_node_missing_connections.size() << " non-firewalled nodes:\n"; for (const bts::net::node_id_t& id : seed_node_missing_connections) dot_stream << " // " << (std::string)address_info_by_node_id[id].remote_endpoint << "\n"; dot_stream << " layout=\"circo\";\n"; //for (const auto& node_and_connections : connections_by_node_id) // all_known_nodes[node_and_connections.first] = address_info_by_node_id[node_and_connections.first].remote_endpoint; for (const auto& address_info_for_node : address_info_by_node_id) { dot_stream << " \"" << fc::variant(address_info_for_node.first).as_string() << "\"[label=\"" << (std::string)address_info_for_node.second.remote_endpoint << "\""; if (address_info_for_node.second.firewalled != bts::net::firewalled_state::not_firewalled) dot_stream << ",shape=rectangle"; dot_stream << "];\n"; } for (auto& node_and_connections : connections_by_node_id) for (const bts::net::address_info& this_connection : node_and_connections.second) dot_stream << " \"" << fc::variant(node_and_connections.first).as_string() << "\" -- \"" << fc::variant(this_connection.node_id).as_string() << "\";\n"; dot_stream << "}\n"; #if 0 for (auto& node_and_connections : connections_by_node_id) { out << " " << (std::string)node_and_connections.first.node_id.data << "[label=\"" << (std::string)node_and_connections.first.remote_endpoint << "\"];\n"; //node_and_connections.first.node_id } #endif return 0; }