diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 423b99700e..b22c3648cb 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -5852,8 +5852,8 @@ void controller::validate_tapos( const transaction& trx )const { try { //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration EOS_ASSERT(trx.verify_reference_block(tapos_block_summary.block_id), invalid_ref_block_exception, - "Transaction's reference block did not match. Is this transaction from a different fork?", - ("tapos_summary", tapos_block_summary)); + "Transaction's reference block ${rb} did not match ${bs}. Is this transaction from a different fork?", + ("rb", trx.ref_block_num)("bs", tapos_block_summary.block_id)); } FC_CAPTURE_AND_RETHROW() } void controller::validate_db_available_size() const { diff --git a/plugins/net_plugin/include/eosio/net_plugin/net_utils.hpp b/plugins/net_plugin/include/eosio/net_plugin/net_utils.hpp index ecabacab22..deb79b73eb 100644 --- a/plugins/net_plugin/include/eosio/net_plugin/net_utils.hpp +++ b/plugins/net_plugin/include/eosio/net_plugin/net_utils.hpp @@ -40,29 +40,89 @@ namespace detail { return block_sync_rate_limit; } + /// @return host, port, remainder + inline std::tuple split_host_port_remainder(const std::string& peer_add, bool should_throw) { + using std::string; + // host:port[:trx|:blk][:] + if (peer_add.empty()) { + EOS_ASSERT(!should_throw, chain::plugin_config_exception, "Address specification is empty" ); + return {}; + } + + auto colon_count = std::count(peer_add.begin(), peer_add.end(), ':'); + string::size_type end_bracket = 0; + if (peer_add[0] == '[') { + end_bracket = peer_add.find(']'); + if (end_bracket == string::npos) { + EOS_ASSERT(!should_throw, chain::plugin_config_exception, + "Invalid address specification ${a}, IPv6 no closing square bracket", ("a", peer_add) ); + return {}; + } + } else if (colon_count >= 7) { + EOS_ASSERT(!should_throw, chain::plugin_config_exception, + "Invalid address specification ${a}; IPv6 addresses must be enclosed in square brackets.", ("a", peer_add)); + return {}; + + } else if (colon_count < 1 || colon_count > 3) { + EOS_ASSERT(!should_throw, chain::plugin_config_exception, + "Invalid address specification ${a}; unexpected number of colons.", ("a", peer_add)); + return {}; + } + string::size_type colon = peer_add.find(':', end_bracket+1); + if (colon == string::npos) { + EOS_ASSERT(!should_throw, chain::plugin_config_exception, + "Invalid address specification ${a}; missing port specification.", ("a", peer_add)); + return {}; + } + if (end_bracket != 0 && end_bracket+1 != colon) { + EOS_ASSERT(!should_throw, chain::plugin_config_exception, + "Invalid address specification ${a}; unexpected character after ']'.", ("a", peer_add)); + return {}; + } + string::size_type colon2 = peer_add.find(':', colon + 1); + string host = peer_add.substr( 0, colon ); + string port = peer_add.substr( colon + 1, colon2 == string::npos ? string::npos : colon2 - (colon + 1)); + string remainder = colon2 == string::npos ? "" : peer_add.substr( colon2 + 1 ); + return {std::move(host), std::move(port), std::move(remainder)}; + } + } // namespace detail + /// @return host, port, type. returns empty on invalid peer_add, does not throw + inline std::tuple split_host_port_type(const std::string& peer_add) { + + using std::string; + // host:port[:trx|:blk][:] // rate is discarded + if (peer_add.empty()) return {}; + + constexpr bool should_throw = false; + auto [host, port, remainder] = detail::split_host_port_remainder(peer_add, should_throw); + if (host.empty() || port.empty()) return {}; + + std::string type; + if (remainder.starts_with("blk") || remainder.starts_with("trx")) { + type = remainder.substr(0, 3); + } + + return {std::move(host), std::move(port), std::move(type)}; + } + /// @return listen address and block sync rate limit (in bytes/sec) of address string + /// @throws chain::plugin_config_exception on invalid address inline std::tuple parse_listen_address( const std::string& address ) { - auto listen_addr = address; - auto limit = std::string("0"); - auto last_colon_location = address.rfind(':'); - if( auto right_bracket_location = address.find(']'); right_bracket_location != address.npos ) { - if( std::count(address.begin()+right_bracket_location, address.end(), ':') > 1 ) { - listen_addr = std::string(address, 0, last_colon_location); - limit = std::string(address, last_colon_location+1); - } - } else { - if( auto colon_count = std::count(address.begin(), address.end(), ':'); colon_count > 1 ) { - EOS_ASSERT( colon_count <= 2, chain::plugin_config_exception, - "Invalid address specification ${addr}; IPv6 addresses must be enclosed in square brackets.", ("addr", address)); - listen_addr = std::string(address, 0, last_colon_location); - limit = std::string(address, last_colon_location+1); - } + constexpr bool should_throw = true; + auto [host, port, remainder] = detail::split_host_port_remainder(address, should_throw); + EOS_ASSERT(!host.empty() && !port.empty(), chain::plugin_config_exception, + "Invalid address specification ${a}; host or port missing.", ("a", address)); + auto listen_addr = host + ":" + port; + auto limit = remainder; + auto last_colon_location = remainder.rfind(':'); + if (last_colon_location != std::string::npos) { + limit = std::string(remainder, last_colon_location+1); } auto block_sync_rate_limit = detail::parse_connection_rate_limit(limit); - return {listen_addr, block_sync_rate_limit}; + return {std::move(listen_addr), block_sync_rate_limit}; } } // namespace eosio::net_utils \ No newline at end of file diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index 2d519f729f..a98a7a9249 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -800,6 +800,7 @@ namespace eosio { const string& peer_address() const { return peer_addr; } // thread safe, const void set_connection_type( const string& peer_addr ); + void set_peer_connection_type( const string& peer_addr ); bool is_transactions_only_connection()const { return connection_type == transactions_only; } // thread safe, atomic bool is_blocks_only_connection()const { return connection_type == blocks_only; } bool is_transactions_connection() const { return connection_type != blocks_only; } // thread safe, atomic @@ -1156,32 +1157,6 @@ namespace eosio { }; - std::tuple split_host_port_type(const std::string& peer_add, bool incoming) { - // host:port:[|] - if (peer_add.empty()) return {}; - - string::size_type p = peer_add[0] == '[' ? peer_add.find(']') : 0; - string::size_type colon = p != string::npos ? peer_add.find(':', p) : string::npos; - if (colon == std::string::npos || colon == 0) { - // if incoming then not an error this peer can do anything about - if (incoming) { - fc_dlog( logger, "Invalid peer address. must be \"host:port[:|]\": ${p}", ("p", peer_add) ); - } else { - fc_elog( logger, "Invalid peer address. must be \"host:port[:|]\": ${p}", ("p", peer_add) ); - } - return {}; - } - string::size_type colon2 = peer_add.find(':', colon + 1); - string::size_type end = colon2 == string::npos - ? string::npos : peer_add.find_first_of( " :+=.,<>!$%^&(*)|-#@\t", colon2 + 1 ); // future proof by including most symbols without using regex - string host = (p > 0) ? peer_add.substr( 1, p-1 ) : peer_add.substr( 0, colon ); - string port = peer_add.substr( colon + 1, colon2 == string::npos ? string::npos : colon2 - (colon + 1)); - string type = colon2 == string::npos ? "" : end == string::npos ? - peer_add.substr( colon2 + 1 ) : peer_add.substr( colon2 + 1, end - (colon2 + 1) ); - return {std::move(host), std::move(port), std::move(type)}; - } - - template bool connections_manager::any_of_supplied_peers( Function&& f ) const { std::shared_lock g( connections_mtx ); @@ -1253,11 +1228,10 @@ namespace eosio { return { on_fork, unknown_block }; } - connection::connection( const string& endpoint, const string& listen_address ) + connection::connection( const string& endpoint, const string& this_address ) : peer_addr( endpoint ), strand( boost::asio::make_strand(my_impl->thread_pool.get_executor()) ), socket( new tcp::socket( strand ) ), - listen_address( listen_address ), log_p2p_address( endpoint ), connection_id( ++my_impl->current_connection_id ), sync_response_expected_timer( my_impl->thread_pool.get_executor() ), @@ -1265,6 +1239,8 @@ namespace eosio { last_handshake_sent(), p2p_address( endpoint ) { + auto [host, port, type] = net_utils::split_host_port_type(this_address); + listen_address = host + ":" + port; // do not include type in listen_address to avoid peer setting type on connection set_connection_type( peer_address() ); my_impl->mark_bp_connection(this); fc_ilog( logger, "created connection - ${c} to ${n}", ("c", connection_id)("n", endpoint) ); @@ -1312,9 +1288,11 @@ namespace eosio { } // called from connection strand - void connection::set_connection_type( const std::string& peer_add ) { - auto [host, port, type] = split_host_port_type(peer_add, false); - if( type.empty() ) { + void connection::set_connection_type( const std::string& peer_add ) { + auto [host, port, type] = net_utils::split_host_port_type(peer_add); + if (host.empty()) { + fc_dlog( logger, "Invalid address: ${a}", ("a", peer_add)); + } else if( type.empty() ) { fc_dlog( logger, "Setting connection - ${c} type for: ${peer} to both transactions and blocks", ("c", connection_id)("peer", peer_add) ); connection_type = both; } else if( type == "trx" ) { @@ -1328,6 +1306,29 @@ namespace eosio { } } + // called from connection strand + void connection::set_peer_connection_type( const std::string& peer_add ) { + // peer p2p-listen-endpoint received via handshake may indicate they do not want trx or blocks + auto [host, port, type] = net_utils::split_host_port_type(peer_add); + if (host.empty()) { + fc_dlog( logger, "Invalid peer address: ${a}", ("a", peer_add)); + } else if( type.empty() ) { + // peer asked for both, continue with p2p-peer-address type + } else if( type == "trx" ) { + if (connection_type == both) { // only switch to trx if p2p-peer-address didn't specify a connection type + fc_dlog( logger, "Setting peer connection - ${c} type for: ${peer} to transactions only", ("c", connection_id)("peer", peer_add) ); + connection_type = transactions_only; + } + } else if( type == "blk" ) { + if (connection_type == both) { // only switch to blocks if p2p-peer-address didn't specify a connection type + fc_dlog( logger, "Setting peer connection - ${c} type for: ${peer} to blocks only", ("c", connection_id)("peer", peer_add) ); + connection_type = blocks_only; + } + } else { + fc_dlog( logger, "Unknown peer connection - ${c} type: ${t}, for ${peer}", ("c", connection_id)("t", type)("peer", peer_add) ); + } + } + std::string connection::state_str(connection_state s) { switch (s) { case connection_state::connecting: @@ -2930,7 +2931,7 @@ namespace eosio { fc_ilog(logger, "Accepted new connection: " + paddr_str); connections.any_of_supplied_peers([&listen_address, &paddr_str, &paddr_desc, &limit](const string& peer_addr) { - auto [host, port, type] = split_host_port_type(peer_addr, false); + auto [host, port, type] = net_utils::split_host_port_type(peer_addr); if (host == paddr_str) { if (limit > 0) { fc_dlog(logger, "Connection inbound to ${la} from ${a} is a configured p2p-peer-address and will not be throttled", ("la", listen_address)("a", paddr_desc)); @@ -3440,9 +3441,11 @@ namespace eosio { } if( incoming() ) { - auto [host, port, type] = split_host_port_type(msg.p2p_address, true); + auto [host, port, type] = net_utils::split_host_port_type(msg.p2p_address); if (host.size()) set_connection_type( msg.p2p_address); + else + peer_dlog(this, "Invalid handshake p2p_address ${p}", ("p", msg.p2p_address)); peer_dlog( this, "checking for duplicate" ); auto is_duplicate = [&](const connection_ptr& check) { @@ -3463,6 +3466,9 @@ namespace eosio { } } else { peer_dlog(this, "skipping duplicate check, addr == ${pa}, id = ${ni}", ("pa", peer_address())("ni", msg.node_id)); + + // check if peer requests no trx or no blocks + set_peer_connection_type(msg.p2p_address); } if( msg.chain_id != my_impl->chain_id ) { @@ -3710,16 +3716,19 @@ namespace eosio { switch (msg.req_blocks.mode) { case catch_up : { const block_id_type& id = msg.req_blocks.ids.empty() ? block_id_type() : msg.req_blocks.ids.back(); - peer_dlog( this, "received request_message:catch_up #${bn}:${id}", ("bn", block_header::num_from_id(id))("id",id) ); + peer_dlog( this, "${d} request_message:catch_up #${bn}:${id}", + ("d", is_blocks_connection() ? "received" : "ignoring")("bn", block_header::num_from_id(id))("id",id) ); + if (!is_blocks_connection()) + return; blk_send_branch( id ); - break; + return; } case normal : { if (protocol_version >= proto_block_nack) { if (msg.req_blocks.ids.size() == 2 && msg.req_trx.ids.empty()) { const block_id_type& req_id = msg.req_blocks.ids[0]; // 0 - req_id, 1 - peer_head_id peer_dlog( this, "${d} request_message:normal #${bn}:${id}", - ("d", is_blocks_connection() ? "received" : "ignoring")("bn", block_header::num_from_id(req_id))("id", req_id) ); + ("d", is_blocks_connection() ? "received" : "ignoring")("bn", block_header::num_from_id(req_id))("id",req_id) ); if (!is_blocks_connection()) return; const block_id_type& peer_head_id = msg.req_blocks.ids[1]; @@ -3761,6 +3770,11 @@ namespace eosio { peer_requested.reset(); flush_queues(); } else { + if (!is_blocks_connection()) { + peer_dlog(this, "received sync_request_message ${m} on transaction only connection, ignoring", ("m", msg)); + return; + } + if (peer_requested) { // This happens when peer already requested some range and sync is still in progress // It could be higher in case of peer requested head catchup and current request is lib catchup @@ -4247,11 +4261,15 @@ namespace eosio { if(hello.sig == chain::signature_type()) hello.token = sha256(); hello.p2p_address = listen_address; - if( is_transactions_only_connection() ) hello.p2p_address += ":trx"; - // if we are not accepting transactions tell peer we are blocks only - if( is_blocks_only_connection() || !my_impl->p2p_accept_transactions ) hello.p2p_address += ":blk"; - if( !is_blocks_only_connection() && !my_impl->p2p_accept_transactions ) { - peer_dlog( this, "p2p-accept-transactions=false inform peer blocks only connection ${a}", ("a", hello.p2p_address) ); + if (incoming()) { + if( is_transactions_only_connection() && hello.p2p_address.find(":trx") == std::string::npos ) hello.p2p_address += ":trx"; + // if we are not accepting transactions tell peer we are blocks only + if( is_blocks_only_connection() || !my_impl->p2p_accept_transactions ) + if (hello.p2p_address.find(":blk") == std::string::npos) + hello.p2p_address += ":blk"; + if( !is_blocks_only_connection() && !my_impl->p2p_accept_transactions ) { + peer_dlog( this, "p2p-accept-transactions=false inform peer blocks only connection ${a}", ("a", hello.p2p_address) ); + } } hello.p2p_address += " - " + hello.node_id.str().substr(0,7); #if defined( __APPLE__ ) @@ -4278,37 +4296,42 @@ namespace eosio { void net_plugin::set_program_options( options_description& /*cli*/, options_description& cfg ) { cfg.add_options() - ( "p2p-listen-endpoint", bpo::value< vector >()->default_value( vector(1, string("0.0.0.0:9876:0")) ), "The actual host:port[:] used to listen for incoming p2p connections. May be used multiple times. " - " The optional rate cap will limit per connection block sync bandwidth to the specified rate. Total " - " allowed bandwidth is the rate-cap multiplied by the connection count limit. A number alone will be " - " interpreted as bytes per second. The number may be suffixed with units. Supported units are: " - " 'B/s', 'KB/s', 'MB/s, 'GB/s', 'TB/s', 'KiB/s', 'MiB/s', 'GiB/s', 'TiB/s'." - " Transactions and blocks outside of sync mode are not throttled." - " Examples:\n" - " 192.168.0.100:9876:1MiB/s\n" - " node.eos.io:9876:1512KB/s\n" - " node.eos.io:9876:0.5GB/s\n" - " [2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876:250KB/s") - ( "p2p-server-address", bpo::value< vector >(), "An externally accessible host:port for identifying this node. Defaults to p2p-listen-endpoint. May be used as many times as p2p-listen-endpoint. If provided, the first address will be used in handshakes with other nodes. Otherwise the default is used.") + ( "p2p-listen-endpoint", bpo::value< vector >()->default_value( vector(1, string("0.0.0.0:9876:0")) ), + "The actual host:port[:trx|:blk][:] used to listen for incoming p2p connections. May be used multiple times." + " The optional rate cap will limit per connection block sync bandwidth to the specified rate. Total" + " allowed bandwidth is the rate-cap multiplied by the connection count limit. A number alone will be" + " interpreted as bytes per second. The number may be suffixed with units. Supported units are:" + " 'B/s', 'KB/s', 'MB/s, 'GB/s', 'TB/s', 'KiB/s', 'MiB/s', 'GiB/s', 'TiB/s'." + " Transactions and blocks outside sync mode are not throttled." + " The optional 'trx' and 'blk' indicates to peers that only transactions 'trx' or blocks 'blk' should be sent." + " Examples:\n" + " 192.168.0.100:9876:1MiB/s\n" + " node.eos.io:9876:trx:1512KB/s\n" + " node.eos.io:9876:0.5GB/s\n" + " [2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876:250KB/s") + ( "p2p-server-address", bpo::value< vector >(), + "An externally accessible host:port for identifying this node. Defaults to p2p-listen-endpoint." + " May be used as many times as p2p-listen-endpoint." + " If provided, the first address will be used in handshakes with other nodes; otherwise the default is used.") ( "p2p-peer-address", bpo::value< vector >()->composing(), "The public endpoint of a peer node to connect to. Use multiple p2p-peer-address options as needed to compose a network.\n" - " Syntax: host:port[:|]\n" - " The optional 'trx' and 'blk' indicates to node that only transactions 'trx' or blocks 'blk' should be sent." - " Examples:\n" - " p2p.eos.io:9876\n" - " p2p.trx.eos.io:9876:trx\n" - " p2p.blk.eos.io:9876:blk\n") + " Syntax: host:port[:trx|:blk]\n" + " The optional 'trx' and 'blk' indicates to node that only transactions 'trx' or blocks 'blk' should be sent." + " Examples:\n" + " p2p.eos.io:9876\n" + " p2p.trx.eos.io:9876:trx\n" + " p2p.blk.eos.io:9876:blk\n") ( "p2p-max-nodes-per-host", bpo::value()->default_value(def_max_nodes_per_host), "Maximum number of client nodes from any single IP address") ( "p2p-accept-transactions", bpo::value()->default_value(true), "Allow transactions received over p2p network to be evaluated and relayed if valid.") ( "p2p-disable-block-nack", bpo::value()->default_value(false), "Disable block notice and block nack. All blocks received will be broadcast to all peers unless already received.") ( "p2p-auto-bp-peer", bpo::value< vector >()->composing(), - "The account and public p2p endpoint of a block producer node to automatically connect to when the it is in producer schedule proximity\n." - " Syntax: account,host:port\n" - " Example,\n" - " eosproducer1,p2p.eos.io:9876\n" - " eosproducer2,p2p.trx.eos.io:9876:trx\n" - " eosproducer3,p2p.blk.eos.io:9876:blk\n") + "The account and public p2p endpoint of a block producer node to automatically connect to when it is in producer schedule proximity\n." + " Syntax: account,host:port\n" + " Example,\n" + " eosproducer1,p2p.eos.io:9876\n" + " eosproducer2,p2p.trx.eos.io:9876:trx\n" + " eosproducer3,p2p.blk.eos.io:9876:blk\n") ( "agent-name", bpo::value()->default_value("EOS Test Agent"), "The name supplied to identify this node amongst the peers.") ( "allowed-connection", bpo::value>()->multitoken()->default_value({"any"}, "any"), "Can be 'any' or 'producers' or 'specified' or 'none'. If 'specified', peer-key must be specified at least once. If only 'producers', peer-key is not required. 'producers' and 'specified' may be combined.") ( "peer-key", bpo::value>()->composing()->multitoken(), "Optional public key of peer allowed to connect. May be used multiple times.") @@ -4419,6 +4442,11 @@ namespace eosio { std::vector peers; if( options.count( "p2p-peer-address" )) { peers = options.at( "p2p-peer-address" ).as>(); + for (const auto& peer : peers) { + const auto& [host, port, type] = net_utils::split_host_port_type(peer); + EOS_ASSERT( !host.empty() && !port.empty(), chain::plugin_config_exception, + "Invalid p2p-peer-address ${p}, syntax host:port:[trx|blk]"); + } connections.add_supplied_peers(peers); } if( options.count( "agent-name" )) { @@ -4727,7 +4755,7 @@ namespace eosio { } string connections_manager::resolve_and_connect( const string& peer_address, const string& listen_address ) { - auto [host, port, type] = split_host_port_type(peer_address, false); + auto [host, port, type] = net_utils::split_host_port_type(peer_address); if (host.empty()) { return "invalid peer address"; } @@ -4761,7 +4789,7 @@ namespace eosio { return false; } - auto [host, port, type] = split_host_port_type(peer_address(), false); + auto [host, port, type] = net_utils::split_host_port_type(peer_address()); if (host.empty()) return false; diff --git a/plugins/net_plugin/tests/rate_limit_parse_unittest.cpp b/plugins/net_plugin/tests/rate_limit_parse_unittest.cpp index c288907908..c75fefab65 100644 --- a/plugins/net_plugin/tests/rate_limit_parse_unittest.cpp +++ b/plugins/net_plugin/tests/rate_limit_parse_unittest.cpp @@ -14,39 +14,253 @@ BOOST_AUTO_TEST_CASE(test_parse_rate_limit) { , "[::1]:9876:-250KB/s" , "0.0.0.0:9877:640Kb/s" , "0.0.0.0:9877:999999999999999999999999999TiB/s" + , "0.0.0.0:9876:trx" + , "0.0.0.0:9776:blk:0" + , "0.0.0.0:9877:trx:640KB/s" + , "192.168.0.1:9878:blk:20MiB/s" + , "localhost:9879:trx:0.5KB/s" + , "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876:trx:250KB/s" + , "[::1]:9876:trx:250KB/s" + , "2001:db8:85a3:8d3:1319:8a2e:370:7348:9876:trx:250KB/s" + , "[::1]:9876:trx:-1KB/s" + , "0.0.0.0:9877:trx:640Kb/s" + , "0.0.0.0:9877:trx:999999999999999999999999999TiB/s" + , "0.0.0.0:9876:trx - 84c470d" + , "0.0.0.0:9877:trx:640KB/s - additional info" + , "[2001:db8:85a3:8d3:1319:8a2e:370:7348]additional info:trx:640KB/s" + , "0.0.0.0" + , "0.0.0.0:" + , "0.0.0.0::" }; size_t which = 0; - auto [listen_addr, block_sync_rate_limit] = eosio::net_utils::parse_listen_address(p2p_addresses[which++]); + auto [listen_addr, block_sync_rate_limit] = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9876"); BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u); - std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9776"); BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u); - std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9877"); BOOST_CHECK_EQUAL(block_sync_rate_limit, 640000u); - std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); BOOST_CHECK_EQUAL(listen_addr, "192.168.0.1:9878"); BOOST_CHECK_EQUAL(block_sync_rate_limit, 20971520u); - std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); BOOST_CHECK_EQUAL(listen_addr, "localhost:9879"); BOOST_CHECK_EQUAL(block_sync_rate_limit, 500u); - std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); BOOST_CHECK_EQUAL(listen_addr, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876"); BOOST_CHECK_EQUAL(block_sync_rate_limit, 250000u); - std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses[which++]); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); BOOST_CHECK_EQUAL(listen_addr, "[::1]:9876"); BOOST_CHECK_EQUAL(block_sync_rate_limit, 250000u); - BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception, + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, [](const eosio::chain::plugin_config_exception& e) {return std::strstr(e.top_message().c_str(), "IPv6 addresses must be enclosed in square brackets");}); - BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception, + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, [](const eosio::chain::plugin_config_exception& e) {return std::strstr(e.top_message().c_str(), "block sync rate limit must not be negative");}); - BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception, + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, [](const eosio::chain::plugin_config_exception& e) {return std::strstr(e.top_message().c_str(), "invalid block sync rate limit specification");}); - BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses[which++]), eosio::chain::plugin_config_exception, + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, [](const eosio::chain::plugin_config_exception& e) {return std::strstr(e.top_message().c_str(), "block sync rate limit specification overflowed");}); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9876"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9776"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9877"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 640000u); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "192.168.0.1:9878"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 20971520u); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "localhost:9879"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 500u); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 250000u); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "[::1]:9876"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 250000u); + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, + [](const eosio::chain::plugin_config_exception& e) + {return std::strstr(e.top_message().c_str(), "IPv6 addresses must be enclosed in square brackets");}); + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, + [](const eosio::chain::plugin_config_exception& e) + {return std::strstr(e.top_message().c_str(), "block sync rate limit must not be negative");}); + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, + [](const eosio::chain::plugin_config_exception& e) + {return std::strstr(e.top_message().c_str(), "invalid block sync rate limit specification");}); + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, + [](const eosio::chain::plugin_config_exception& e) + {return std::strstr(e.top_message().c_str(), "block sync rate limit specification overflowed");}); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9876"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 0u); + std::tie(listen_addr, block_sync_rate_limit) = eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(listen_addr, "0.0.0.0:9877"); + BOOST_CHECK_EQUAL(block_sync_rate_limit, 640000u); + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, + [](const eosio::chain::plugin_config_exception& e) + {return std::strstr(e.top_message().c_str(), "unexpected character after ']'");}); + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, + [](const eosio::chain::plugin_config_exception& e) + {return std::strstr(e.top_message().c_str(), "unexpected number of colons");}); + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, + [](const eosio::chain::plugin_config_exception& e) + {return std::strstr(e.top_message().c_str(), "host or port missing");}); + BOOST_CHECK_EXCEPTION(eosio::net_utils::parse_listen_address(p2p_addresses.at(which++)), eosio::chain::plugin_config_exception, + [](const eosio::chain::plugin_config_exception& e) + {return std::strstr(e.top_message().c_str(), "host or port missing");}); +} + +BOOST_AUTO_TEST_CASE(test_split_host_port_type) { + std::vector p2p_addresses = { + "0.0.0.0:9876" + , "0.0.0.0:9776:0" + , "0.0.0.0:9877:640KB/s" + , "192.168.0.1:9878:20MiB/s" + , "localhost:9879:0.5KB/s" + , "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876:250KB/s" + , "[::1]:9876:250KB/s" + , "2001:db8:85a3:8d3:1319:8a2e:370:7348:9876:250KB/s" + , "[::1]:9876:-250KB/s" + , "0.0.0.0:9877:640Kb/s" + , "0.0.0.0:9877:999999999999999999999999999TiB/s" + , "0.0.0.0:9876:trx" + , "0.0.0.0:9776:blk:0" + , "0.0.0.0:9877:trx:640KB/s" + , "192.168.0.1:9878:blk:20MiB/s" + , "localhost:9879:trx:0.5KB/s" + , "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9876:trx:250KB/s" + , "[::1]:9876:trx:250KB/s" + , "2001:db8:85a3:8d3:1319:8a2e:370:7348:9876:trx:250KB/s" + , "[::1]:9876:trx:-1KB/s" + , "0.0.0.0:9877:trx:640Kb/s" + , "0.0.0.0:9877:trx:999999999999999999999999999TiB/s" + , "0.0.0.0:9876:trx - 84c470d" + , "0.0.0.0:9877:trx:640KB/s - additional info" + , "[2001:db8:85a3:8d3:1319:8a2e:370:7348]additional info:trx:640KB/s" + , "0.0.0.0" + , "0.0.0.0:" + , "0.0.0.0::" + }; + size_t which = 0; + auto [host, port, type] = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9776"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9877"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "192.168.0.1"); + BOOST_CHECK_EQUAL(port, "9878"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "localhost"); + BOOST_CHECK_EQUAL(port, "9879"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "[::1]"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, ""); + BOOST_CHECK_EQUAL(port, ""); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "[::1]"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9877"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9877"); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9776"); + BOOST_CHECK_EQUAL(type, "blk"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9877"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "192.168.0.1"); + BOOST_CHECK_EQUAL(port, "9878"); + BOOST_CHECK_EQUAL(type, "blk"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "localhost"); + BOOST_CHECK_EQUAL(port, "9879"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "[::1]"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, ""); + BOOST_CHECK_EQUAL(port, ""); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "[::1]"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9877"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9877"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9876"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, "0.0.0.0"); + BOOST_CHECK_EQUAL(port, "9877"); + BOOST_CHECK_EQUAL(type, "trx"); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, ""); + BOOST_CHECK_EQUAL(port, ""); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, ""); + BOOST_CHECK_EQUAL(port, ""); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, ""); + BOOST_CHECK_EQUAL(port, ""); + BOOST_CHECK_EQUAL(type, ""); + std::tie(host, port, type) = eosio::net_utils::split_host_port_type(p2p_addresses.at(which++)); + BOOST_CHECK_EQUAL(host, ""); + BOOST_CHECK_EQUAL(port, ""); + BOOST_CHECK_EQUAL(type, ""); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bd08c8e8c6..df2be0f309 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -66,6 +66,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_multiple_listen_test.py ${CMAKE_C configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_no_listen_test.py ${CMAKE_CURRENT_BINARY_DIR}/p2p_no_listen_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_sync_throttle_test.py ${CMAKE_CURRENT_BINARY_DIR}/p2p_sync_throttle_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_sync_throttle_test_shape.json ${CMAKE_CURRENT_BINARY_DIR}/p2p_sync_throttle_test_shape.json COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_no_blocks_test.py ${CMAKE_CURRENT_BINARY_DIR}/p2p_no_blocks_test.py COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_no_blocks_test_shape.json ${CMAKE_CURRENT_BINARY_DIR}/p2p_no_blocks_test_shape.json COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/compute_transaction_test.py ${CMAKE_CURRENT_BINARY_DIR}/compute_transaction_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/subjective_billing_test.py ${CMAKE_CURRENT_BINARY_DIR}/subjective_billing_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/get_account_test.py ${CMAKE_CURRENT_BINARY_DIR}/get_account_test.py COPYONLY) @@ -301,6 +303,8 @@ add_test(NAME p2p_sync_throttle_test COMMAND tests/p2p_sync_throttle_test.py -v set_property(TEST p2p_sync_throttle_test PROPERTY LABELS long_running_tests) add_test(NAME p2p_sync_throttle_if_test COMMAND tests/p2p_sync_throttle_test.py -v -d 2 --activate-if ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST p2p_sync_throttle_if_test PROPERTY LABELS long_running_tests) +add_test(NAME p2p_no_blocks_if_test COMMAND tests/p2p_no_blocks_test.py -v -d 2 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +set_property(TEST p2p_no_blocks_if_test PROPERTY LABELS nonparallelizable_tests) # needs iproute-tc or iproute2 depending on platform #add_test(NAME p2p_high_latency_test COMMAND tests/p2p_high_latency_test.py -v WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/tests/p2p_no_blocks_test.py b/tests/p2p_no_blocks_test.py new file mode 100755 index 0000000000..5e0200e8d2 --- /dev/null +++ b/tests/p2p_no_blocks_test.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 + +import signal +import json +import time + +from TestHarness import Account, Cluster, TestHelper, Utils, WalletMgr, CORE_SYMBOL +from TestHarness.TestHelper import AppArgs + +############################################################### +# p2p_no_blocks_test +# +# Test p2p-listen-address trx only option +# +############################################################### + +Print=Utils.Print +errorExit=Utils.errorExit + +appArgs = AppArgs() +appArgs.add(flag='--plugin',action='append',type=str,help='Run nodes with additional plugins') +appArgs.add(flag='--connection-cleanup-period',type=int,help='Interval in whole seconds to run the connection reaper and metric collection') + +args=TestHelper.parse_args({"-d","--keep-logs" + ,"--dump-error-details","-v","--leave-running" + ,"--unshared"}, + applicationSpecificArgs=appArgs) +pnodes=1 +delay=args.d +debug=args.v +prod_count = 2 +total_nodes=4 +activateIF=True +dumpErrorDetails=args.dump_error_details + +Utils.Debug=debug +testSuccessful=False + +cluster=Cluster(unshared=args.unshared, keepRunning=args.leave_running, keepLogs=args.keep_logs) +walletMgr=WalletMgr(True) + +try: + TestHelper.printSystemInfo("BEGIN") + + cluster.setWalletMgr(walletMgr) + + Print(f'producing nodes: {pnodes}, delay between nodes launch: {delay} second{"s" if delay != 1 else ""}') + + Print("Stand up cluster") + # Custom topology: + # prodNode00 <-> nonProdNode01 + # -> noBlocks02 :9902 (p2p-listen-address with trx only) (speculative mode) + # -> noBlocks03 :9903 (p2p-listen-address with trx only) + # + # 01-nonProdNode connects to 02 & 03, but 02 & 03 do not connect to 01 so they will not receive any blocks + # 02 & 03 are connected to the bios node to get blocks until bios node is killed. + # + specificExtraNodeosArgs = {} + # nonProdNode01 will connect normally but will not send blocks because 02 & 03 have specified :trx only + specificExtraNodeosArgs[1] = f'--p2p-peer-address localhost:9902 --p2p-peer-address localhost:9903 ' + # add a trx only listen endpoint to noBlocks02 & noBlocks03 + specificExtraNodeosArgs[2] = f'--p2p-peer-address localhost:9776 --read-mode speculative ' # connect to bios + specificExtraNodeosArgs[2] += f'--p2p-listen-endpoint localhost:9878 --p2p-server-address localhost:9878 ' + specificExtraNodeosArgs[2] += f'--p2p-listen-endpoint localhost:9902:trx --p2p-server-address localhost:9902:trx ' + specificExtraNodeosArgs[3] = f'--p2p-peer-address localhost:9776 ' # connect to bios + specificExtraNodeosArgs[3] += f'--p2p-listen-endpoint localhost:9879 --p2p-server-address localhost:9879 ' + specificExtraNodeosArgs[3] += f'--p2p-listen-endpoint localhost:9903:trx --p2p-server-address localhost:9903:trx ' + if cluster.launch(pnodes=pnodes, unstartedNodes=2, totalNodes=total_nodes, prodCount=prod_count, + extraNodeosArgs="--connection-cleanup-period 3", specificExtraNodeosArgs=specificExtraNodeosArgs, + topo='./tests/p2p_no_blocks_test_shape.json', delay=delay, activateIF=activateIF, biosFinalizer=False) is False: + errorExit("Failed to stand up eos cluster.") + + prodNode00 = cluster.getNode(0) + nonProdNode01 = cluster.getNode(1) + + noBlocks02 = cluster.unstartedNodes[0] + noBlocks03 = cluster.unstartedNodes[1] + + Print("Launch no block nodes 02 & 03") + cluster.launchUnstarted(2) + + assert noBlocks02.verifyAlive(), "node 02 did not launch" + assert noBlocks03.verifyAlive(), "node 03 did not launch" + + headBlockNum = nonProdNode01.getHeadBlockNum() + + Print("Sync from bios") + assert noBlocks02.waitForBlock(headBlockNum), "node02 did not sync from bios" + assert noBlocks03.waitForBlock(headBlockNum), "node03 did not sync from bios" + + # create transfer transaction now so it has a valid TAPOS + eosioBalanceBefore = nonProdNode01.getAccountEosBalance("eosio") + defprodueraBalanceBefore = nonProdNode01.getAccountEosBalance("defproducera") + transferAmount="50.0000 {0}".format(CORE_SYMBOL) + trx=nonProdNode01.transferFunds(cluster.eosioAccount, cluster.defproduceraAccount, transferAmount, dontSend=True) + + Print("Killing bios node") + cluster.biosNode.kill(signal.SIGTERM) + + # blocks could be received but not processed, so give a bit of delay for blocks to be processed + time.sleep(1) + + Print("Verify head no longer advancing after bios killed") + assert not noBlocks02.waitForHeadToAdvance(), "head advanced on node02 unexpectedly" + assert not noBlocks03.waitForHeadToAdvance(), "head advanced on node03 unexpectedly" + + Print("Send transfer trx") + cmdDesc = "push transaction --skip-sign" + cmd = "%s '%s'" % (cmdDesc, json.dumps(trx)) + trans = nonProdNode01.processCleosCmd(cmd, cmdDesc, silentErrors=False, exitOnError=True) + + # can't wait for transaction in block, so just sleep + time.sleep(0.5) + + eosioBalanceNode02 = noBlocks02.getAccountEosBalance("eosio") + defprodueraBalanceNode02 = noBlocks02.getAccountEosBalance("defproducera") + eosioBalanceNode03 = noBlocks03.getAccountEosBalance("eosio") + defprodueraBalanceNode03 = noBlocks03.getAccountEosBalance("defproducera") + + assert eosioBalanceBefore - 500000 == eosioBalanceNode02, f"{eosioBalanceBefore - 500000} != {eosioBalanceNode02}" + assert defprodueraBalanceBefore + 500000 == defprodueraBalanceNode02, f"{defprodueraBalanceBefore + 500000} != {defprodueraBalanceNode02}" + + assert eosioBalanceBefore == eosioBalanceNode03, f"{eosioBalanceBefore} != {eosioBalanceNode03}" + assert defprodueraBalanceBefore == defprodueraBalanceNode03, f"{defprodueraBalanceBefore} != {defprodueraBalanceNode03}" + + cluster.biosNode.relaunch() + + testSuccessful=True +finally: + TestHelper.shutdown(cluster, walletMgr, testSuccessful=testSuccessful, dumpErrorDetails=dumpErrorDetails) + +exitCode = 0 if testSuccessful else 1 +exit(exitCode) diff --git a/tests/p2p_no_blocks_test_shape.json b/tests/p2p_no_blocks_test_shape.json new file mode 100644 index 0000000000..cd25f42935 --- /dev/null +++ b/tests/p2p_no_blocks_test_shape.json @@ -0,0 +1,147 @@ +{ + "name": "testnet_", + "nodes": { + "bios": { + "index": -100, + "name": "bios", + "keys": [ + { + "pubkey": "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", + "privkey": "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3", + "blspubkey": "PUB_BLS_sGOyYNtpmmjfsNbQaiGJrPxeSg9sdx0nRtfhI_KnWoACXLL53FIf1HjpcN8wX0cYQyOE60NLSI9iPY8mIlT4GkiFMT3ez7j2IbBBzR0D1MthC0B_fYlgYWwjcbqCOowSaH48KA", + "blsprivkey": "PVT_BLS_QgHHJ5vprZcjG7P0xWoIdX4yKPQXoG4k3e28TpLIQicB7GL_", + "blspop": "SIG_BLS_HDzwmlF7wSJGetlSfhIGKVtjiMTeYoM4oCbNoHi1tyh0_KnZCsdLUzplexSXD80P0NAkCjlA6YFt2M5_JsZkRTqn2faFSnH6zwKIK9yr2cV3a14W4WcIC90mTP2D-HEPOBjM2gTmWCA0gYfPdV3tB3I0matrYh5R0I1FG0V6p_RVKacXMgV_M3lNUokRI84MPZlc8OVbJ0RbjoBnYylVeYtR31vSJvvk6RvykIjTktZOA0s32-TR5EcxuaFSsVQU7nSQxA" + } + ], + "peers": [ + "testnet_00" + ], + "producers": [ + "eosio" + ], + "dont_start": false, + "p2p_port": 9776, + "http_port": 8788, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", + "_dot_label": "localhost:9776\nbios\nprod=eosio" + }, + "testnet_00": { + "index": 0, + "name": "testnet_00", + "keys": [ + { + "pubkey": "EOS7D6jfN6bbJD9cYheyhnBT4bmUWc3Qf4Yphf5GBeAAy58okcwHU", + "privkey": "5KkmnyunnpCQzgFoLMEtU3j7BRBa5aWmsBNru49ke7LdnZKFhmt", + "blspubkey": "PUB_BLS_X6Wzge0CMkDLu0svywBWGBdIuMfol_hAG7zeukAddsbQsArgcuZ6tz3LLoLRurUMhz6ZpOHdYCPU0Rg8Fo8n4UDsT6pcHSmwWMKWIhyS-Ms0O_dYCRQ2Q5HLxBGMxyIWaltxlw", + "blsprivkey": "PVT_BLS_TvIkGjiwy3b5k9yc6YnwHPQp1n_9x8yP4mZQl5Ke1yvp2_vv", + "blspop": "SIG_BLS_Zzi_eRG51GhBgAFhnG048Pa3OjlenLwKtO03CBkZxQB4sdhyYWmqrJDdjpgPwvcPwbRK1jIlaUG9mJVPjJHrmocC-br8_t1EqLAHN3lyuyJ7UZWkzj2E339zNJ8aE28NmF4rmZ0UV3sUP54qZw9k75G7y0toL8djkMkPNzbz9OD0vZQDjQ-PVWQg11t-eP4MbFt8uONuk2NpEBEbT8JXPvnzh1e1-WBxId0Mra5-Pa1ca3zkrqgHdnpWKCUjBr0Kj8yZPg" + } + ], + "peers": [ + "bios" + ], + "producers": [ + "defproducera", + "defproducerb", + "defproducerc", + "defproducerd", + "defproducere", + "defproducerf", + "defproducerg", + "defproducerh", + "defproduceri", + "defproducerj", + "defproducerk", + "defproducerl", + "defproducerm", + "defproducern", + "defproducero", + "defproducerp", + "defproducerq", + "defproducerr", + "defproducers", + "defproducert", + "defproduceru" + ], + "dont_start": false, + "p2p_port": 9876, + "http_port": 8888, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", + "_dot_label": "localhost:9876\ntestnet_00\nprod=defproducera\ndefproducerb\ndefproducerc\ndefproducerd\ndefproducere\ndefproducerf\ndefproducerg\ndefproducerh\ndefproduceri\ndefproducerj\ndefproducerk\ndefproducerl\ndefproducerm\ndefproducern\ndefproducero\ndefproducerp\ndefproducerq\ndefproducerr\ndefproducers\ndefproducert\ndefproduceru" + }, + "testnet_01": { + "index": 1, + "name": "testnet_01", + "keys": [ + { + "pubkey": "EOS5tZqxLB8y9q2yHkgcXU4QFBEV6QKN3NQ54ygaFLWHJbjqYzFhw", + "privkey": "5KBs4qR7T8shJjCJUeFQXd77iKrok5TCtZiQhWJpCpc1VRxpNAs", + "blspubkey": "PUB_BLS_UmHR2Ez-gUJVkptOXXlWBCSu2aPQ3EBk69L7IzXn-pAXiWv5gP6fgQv5Js4n3VcJL6TK1M9rB9wAPhnr7b6xdKg2_zWD62qUoal9GYmBS5doxlCdKDY8ZFj6fbGS02oY_-ItrQ", + "blsprivkey": "PVT_BLS_IRjJHkfSSII-mDq7iVOsznvka_sRMsmxJSJwXQyr5mqmERAV", + "blspop": "SIG_BLS_wzTA_EfQTVoWRO4HZqoyDcQGCnlvHCkqoZXVSRbwSf7az4U4nbveWgCMRCgQZsgEJbPt6-NslwwRXJDLnFN0Hnm8F5qhmsGlWMP9tH7syPibNvldJ0RUFDH7azSZulcJ2uMxQAobCB-21c3PiUQc8JbuJFbUp9klAnXIJP60P-PT6ZUNmhNjLqHl2IlMsq8ZdFPvHVF3Z8HpfhJVKedI4yTvzWAIIOW2uSHkOmKbLP_QYc2YLRHUWV56mM-hsRwP4-hWVA" + } + ], + "peers": [ + "testnet_00" + ], + "producers": [], + "dont_start": false, + "p2p_port": 9877, + "http_port": 8889, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", + "_dot_label": "localhost:9877\ntestnet_01\nprod=" + }, + "testnet_02": { + "index": 2, + "name": "testnet_02", + "keys": [ + { + "pubkey": "EOS5FBPf5EN9bYEqmsKfPx9bxyUZ9grDiE24zqLFXtPa6UpVzMjE7", + "privkey": "5HtVDiAsD24seDm5sdswTcdZpx672XbBW9gBkyrzbsj2j9Y9JeC", + "blspubkey": "PUB_BLS_JzblSr2sf_UhxQjGxOtHbRCBkHgSB1RG4xUbKKl-fKtUjx6hyOHajnVQT4IvBF4PutlX7JTC14IqIjADlP-3_G2MXRhBlkB57r2u59OCwRQQEDqmVSADf6CoT8zFUXcSgHFw7w", + "blsprivkey": "PVT_BLS_QRxLAVbe2n7RaPWx2wHbur8erqUlAs-V_wXasGhjEA78KlBq", + "blspop": "SIG_BLS_Z5fJqFv6DIsHFhBFpkHmL_R48h80zVKQHtB5lrKGOVZTaSQNuVaXD_eHg7HBvKwY6zqgA_vryCLQo5W0Inu6HtLkGL2gYX2UHJjrZJZpfJSKG0ynqAZmyrCglxRLNm8KkFdGGR8oJXf5Yzyu7oautqTPniuKLBvNeQxGJGDOQtHSQ0uP3mD41pWzPFRoi10BUor9MbwUTQ7fO7Of4ZjhVM3IK4JrqX1RBXkDX83Wi9xFzs_fdPIyMqmgEzFgolgUa8XN4Q" + } + ], + "peers": [ + ], + "producers": [], + "dont_start": true, + "p2p_port": 9878, + "http_port": 8890, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", + "_dot_label": "localhost:9878\ntestnet_02\nprod=" + }, + "testnet_03": { + "index": 3, + "name": "testnet_03", + "keys": [ + { + "pubkey": "EOS8XH2gKxsef9zxmMHm4vaSvxQUhg7W4GC3nK2KSRxyYrNG5gZFS", + "privkey": "5JcoRRhDcgm51dkBrRTmErceTqrYhrq22UnmUjTZToMpH91B9N1", + "blspubkey": "PUB_BLS_rYRa_-bT7uLOSAfPIBy6NlXFB0YxwROeSuqHzw6s-1cuK_-GJUKqp20ktyAnsO4ZuHdx3BEPDaLronpnL22MXKWM7bvZnkCfbGCD6OzizQqxXkM9N5z5R-OUA4Ime6cF5YTSFg", + "blsprivkey": "PVT_BLS_GQjR0E8Hu8KrsTCvLKnlOCIwQijAj2-5KDizQwF-bAY6pise", + "blspop": "SIG_BLS_syFMuifUnX2zQQKr0cuHYzQQjsuPrNG75_z6y8fOyYg_twqMICZ0kT7ObbwIOUsLfXx9PVb4-QLEgUYGSRg1NSfeHGjIGkhea82wa3ayfI8elUEU1MStKbeKpys7xUAQz1PEgwcz5dClq3HyLQmMAjpoL74N_Znf0KiNEVZMte-DLF7x_6sAfp_834LthyYHjZYTmdG7belyzlYHKJb6upnZy9nR_zoKpx9jeTd3tzVhoTCuAN6aFw68D_ItY5cWiY2dhA" + } + ], + "peers": [ + ], + "producers": [], + "dont_start": true, + "p2p_port": 9879, + "http_port": 8891, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", + "_dot_label": "localhost:9879\ntestnet_03\nprod=" + } + } +}