diff --git a/binary_port/src/error_code.rs b/binary_port/src/error_code.rs index 56cc370e37..34efcdb31f 100644 --- a/binary_port/src/error_code.rs +++ b/binary_port/src/error_code.rs @@ -307,6 +307,9 @@ pub enum ErrorCode { /// Malformed binary request #[error("malformed binary request")] MalformedBinaryRequest = 96, + /// Peer count unavailable + #[error("peer count unavailable")] + PeerCountUnavailable = 97, } impl TryFrom for ErrorCode { @@ -411,6 +414,7 @@ impl TryFrom for ErrorCode { 94 => Ok(ErrorCode::MalformedProtocolVersion), 95 => Ok(ErrorCode::MalformedBinaryRequestHeader), 96 => Ok(ErrorCode::MalformedBinaryRequest), + 97 => Ok(ErrorCode::PeerCountUnavailable), _ => Err(UnknownErrorCode), } } diff --git a/binary_port/src/information_request.rs b/binary_port/src/information_request.rs index 39b823bbc4..2b42f945e3 100644 --- a/binary_port/src/information_request.rs +++ b/binary_port/src/information_request.rs @@ -83,6 +83,8 @@ pub enum InformationRequest { /// Whether to return the bytecode with the entity. include_bytecode: bool, }, + /// Returns the count of connected peers. + PeerCount, } impl InformationRequest { @@ -115,6 +117,7 @@ impl InformationRequest { InformationRequest::ProtocolVersion => InformationRequestTag::ProtocolVersion, InformationRequest::Package { .. } => InformationRequestTag::Package, InformationRequest::Entity { .. } => InformationRequestTag::Entity, + InformationRequest::PeerCount => InformationRequestTag::PeerCount, } } @@ -132,6 +135,7 @@ impl InformationRequest { with_finalized_approvals: rng.gen(), }, InformationRequestTag::Peers => InformationRequest::Peers, + InformationRequestTag::PeerCount => InformationRequest::PeerCount, InformationRequestTag::Uptime => InformationRequest::Uptime, InformationRequestTag::LastProgress => InformationRequest::LastProgress, InformationRequestTag::ReactorState => InformationRequest::ReactorState, @@ -198,6 +202,7 @@ impl ToBytes for InformationRequest { with_finalized_approvals.write_bytes(writer) } InformationRequest::Peers + | InformationRequest::PeerCount | InformationRequest::Uptime | InformationRequest::LastProgress | InformationRequest::ReactorState @@ -253,6 +258,7 @@ impl ToBytes for InformationRequest { with_finalized_approvals, } => hash.serialized_length() + with_finalized_approvals.serialized_length(), InformationRequest::Peers + | InformationRequest::PeerCount | InformationRequest::Uptime | InformationRequest::LastProgress | InformationRequest::ReactorState @@ -317,6 +323,7 @@ impl TryFrom<(InformationRequestTag, &[u8])> for InformationRequest { ) } InformationRequestTag::Peers => (InformationRequest::Peers, key_bytes), + InformationRequestTag::PeerCount => (InformationRequest::PeerCount, key_bytes), InformationRequestTag::Uptime => (InformationRequest::Uptime, key_bytes), InformationRequestTag::LastProgress => (InformationRequest::LastProgress, key_bytes), InformationRequestTag::ReactorState => (InformationRequest::ReactorState, key_bytes), @@ -444,6 +451,8 @@ pub enum InformationRequestTag { Package = 18, /// Addressable entity request. Entity = 19, + /// Peer count. + PeerCount = 20, } impl InformationRequestTag { @@ -470,6 +479,7 @@ impl InformationRequestTag { 17 => InformationRequestTag::ProtocolVersion, 18 => InformationRequestTag::Package, 19 => InformationRequestTag::Entity, + 20 => InformationRequestTag::PeerCount, _ => unreachable!(), } } @@ -500,6 +510,7 @@ impl TryFrom for InformationRequestTag { 17 => Ok(InformationRequestTag::ProtocolVersion), 18 => Ok(InformationRequestTag::Package), 19 => Ok(InformationRequestTag::Entity), + 20 => Ok(InformationRequestTag::PeerCount), _ => Err(UnknownInformationRequestTag(value)), } } diff --git a/binary_port/src/response_type.rs b/binary_port/src/response_type.rs index e74bc9cf14..dacc3ce2ce 100644 --- a/binary_port/src/response_type.rs +++ b/binary_port/src/response_type.rs @@ -121,6 +121,8 @@ pub enum ResponseType { AddressableEntityInformation, /// Response to KeepAliveRequest query KeepAliveInformation, + /// Peer count. + PeerCount, } impl ResponseType { @@ -231,6 +233,7 @@ impl TryFrom for ResponseType { x if x == ResponseType::KeepAliveInformation as u8 => { Ok(ResponseType::KeepAliveInformation) } + x if x == ResponseType::PeerCount as u8 => Ok(ResponseType::PeerCount), _ => Err(()), } } @@ -296,6 +299,7 @@ impl fmt::Display for ResponseType { ResponseType::KeepAliveInformation => { write!(f, "KeepAliveInformation") } + ResponseType::PeerCount => write!(f, "PeerCount"), } } } diff --git a/node/src/components/binary_port.rs b/node/src/components/binary_port.rs index 1ab69c10f9..760cf49b0a 100644 --- a/node/src/components/binary_port.rs +++ b/node/src/components/binary_port.rs @@ -1097,10 +1097,24 @@ where protocol_version, ) } - InformationRequest::Peers => BinaryResponse::from_value( - Peers::from(effect_builder.network_peers().await), - protocol_version, - ), + InformationRequest::Peers => { + let map = effect_builder.network_peers().await; + if map.is_empty() { + BinaryResponse::new_empty(protocol_version) + } else { + let peers = Peers::from(map); + BinaryResponse::from_value(peers, protocol_version) + } + } + InformationRequest::PeerCount => { + let map = effect_builder.network_peers().await; + let count = map.len() as u32; + if let Ok(bytes) = u32::to_bytes(&count) { + BinaryResponse::from_raw_bytes(ResponseType::PeerCount, bytes, protocol_version) + } else { + BinaryResponse::new_error(ErrorCode::PeerCountUnavailable, protocol_version) + } + } InformationRequest::Uptime => { BinaryResponse::from_value(effect_builder.get_uptime().await, protocol_version) } @@ -1499,6 +1513,7 @@ where let (response, id) = handle_payload(effect_builder, payload, version, Arc::clone(&rate_limiter)).await; + framed .send(BinaryMessage::new( BinaryResponseAndRequest::new(response, payload, id).to_bytes()?, diff --git a/node/src/reactor/main_reactor/tests.rs b/node/src/reactor/main_reactor/tests.rs index 3bfc43adf7..c398eb7b2b 100644 --- a/node/src/reactor/main_reactor/tests.rs +++ b/node/src/reactor/main_reactor/tests.rs @@ -48,9 +48,9 @@ use casper_types::{ AccountConfig, AccountsConfig, ActivationPoint, AddressableEntityHash, AvailableBlockRange, Block, BlockHash, BlockHeader, BlockV2, CLValue, Chainspec, ChainspecRawBytes, ConsensusProtocolName, Deploy, EraId, FeeHandling, Gas, HoldBalanceHandling, Key, Motes, - NextUpgrade, Peers, PricingHandling, PricingMode, ProtocolVersion, PublicKey, RefundHandling, - Rewards, SecretKey, StoredValue, SystemHashRegistry, TimeDiff, Timestamp, Transaction, - TransactionHash, TransactionV1Config, ValidatorConfig, U512, + NextUpgrade, PricingHandling, PricingMode, ProtocolVersion, PublicKey, RefundHandling, Rewards, + SecretKey, StoredValue, SystemHashRegistry, TimeDiff, Timestamp, Transaction, TransactionHash, + TransactionV1Config, ValidatorConfig, U512, }; use crate::{ @@ -1490,9 +1490,9 @@ async fn should_be_peerless_in_isolation() { let (mut client, finish_cranking) = setup_network_and_get_binary_port_handle(initial_stakes, spec_override).await; - let peers_request_bytes = { + let peer_count_request_bytes = { let request = BinaryRequest::Get( - InformationRequest::Peers + InformationRequest::PeerCount .try_into() .expect("should convert"), ); @@ -1510,7 +1510,7 @@ async fn should_be_peerless_in_isolation() { .collect::>() }; client - .send(BinaryMessage::new(peers_request_bytes)) + .send(BinaryMessage::new(peer_count_request_bytes)) .await .expect("should send message"); let response = timeout(Duration::from_secs(20), client.next()) @@ -1519,13 +1519,10 @@ async fn should_be_peerless_in_isolation() { .unwrap_or_else(|| panic!("should have bytes")) .unwrap_or_else(|err| panic!("should have ok response: {}", err)); - let peers: Peers = FromBytes::from_bytes(response.payload()) - .expect("Peers should be deserializable") - .0; - assert!( - peers.into_inner().len() == 0, - "should not have peers in isolated mode" - ); + let (peer_count, _) = + ::from_bytes(response.payload()).expect("Peers should be deserializable"); + + assert_eq!(peer_count, 0, "should not have peers in isolated mode"); let (_net, _rng) = timeout(Duration::from_secs(20), finish_cranking) .await diff --git a/types/src/bytesrepr.rs b/types/src/bytesrepr.rs index 2ca8547a0b..fef64fdda6 100644 --- a/types/src/bytesrepr.rs +++ b/types/src/bytesrepr.rs @@ -159,10 +159,6 @@ impl Display for Error { } impl ToBytes for Error { - fn write_bytes(&self, writer: &mut Vec) -> Result<(), Error> { - (*self as u8).write_bytes(writer) - } - fn to_bytes(&self) -> Result, Error> { (*self as u8).to_bytes() } @@ -170,6 +166,10 @@ impl ToBytes for Error { fn serialized_length(&self) -> usize { U8_SERIALIZED_LENGTH } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), Error> { + (*self as u8).write_bytes(writer) + } } impl FromBytes for Error { @@ -541,7 +541,7 @@ fn vec_from_vec(bytes: Vec) -> Result<(Vec, Vec), Error /// 4096 bytes. This function will never return less than 1. #[inline] fn cautious(hint: usize) -> usize { - let el_size = core::mem::size_of::(); + let el_size = mem::size_of::(); core::cmp::max(core::cmp::min(hint, 4096 / el_size), 1) } diff --git a/types/src/peers_map.rs b/types/src/peers_map.rs index 1ac5da20a1..caaaf1c4c5 100644 --- a/types/src/peers_map.rs +++ b/types/src/peers_map.rs @@ -134,4 +134,18 @@ mod tests { let val = Peers::random(rng); bytesrepr::test_serialization_roundtrip(&val); } + + #[test] + fn bytesrepr_empty_roundtrip() { + let val = Peers(vec![]); + bytesrepr::test_serialization_roundtrip(&val); + } + + #[test] + fn bytesrepr_empty_vec_should_have_count_0() { + let val = Peers(vec![]); + let x = Peers::to_bytes(&val).expect("should have vec"); + let (count, _) = u32::from_bytes(&x).expect("should have count"); + assert!(count == 0, "count should be 0"); + } }