Skip to content

Commit 249a32c

Browse files
Merge #904: fix: another bundle of small fixes for how we select peers
857a80b fix: use AddrMan::enough_addresses to decide whether to use hardcoded peers (Davidson Souza) b108023 chore: increase timeout to two minutes (Davidson Souza) ed87428 fix: don't drop inflight requests before completion (JoseSK999) 9caa04f fix: fix service bits for some hardcoded addresses (Davidson Souza) 497a59c fix: update excess peer's data (Davidson Souza) af42edc fix(sync_ctx): make it more aggressive at oppening utreexo connections (Davidson Souza) 04ba097 test: add a new test for adding fixed peers on addr_man (Davidson Souza) 2efbbec fix: update update_set_service_flag to require NETWORK_LIMITED (Davidson Souza) 43b5574 fix(addr_man): don't return connected addresses in get_address_by_service (Davidson Souza) Pull request description: ### Description and Notes This PR bundles a couple of small changes intended to make our node more effective at finding Utreexo peers. See each commit message for more details about what they are changing and why. Changelog: ``` fix(addr_man: don't return connected addresses in get_address_by_service fix: update update_set_service_flag to require NETWORK_LIMITED test: add a new test for adding fixed peers on addr_man fix(sync_ctx): make it more aggressive at oppening utreexo connections fix: update excess peer's data fix: fix service bits for some hardcoded addresses ``` ACKs for top commit: JoseSK999: ACK 857a80b; works well on signet luisschwab: ACK 857a80b Tree-SHA512: ec18bb8ef87cf6df8856292666a34c61b6fbde2583ff536749e84958263d557779cf5cfe0c6fd6e488628c9b29b02a85b7555263ea44407d5bbbf6d1d24b9886
2 parents 6626f5c + 857a80b commit 249a32c

7 files changed

Lines changed: 108 additions & 65 deletions

File tree

crates/floresta-wire/src/p2p_wire/address_man.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use bitcoin::p2p::ServiceFlags;
2121
use bitcoin::Network;
2222
use floresta_chain::DnsSeed;
2323
use floresta_common::service_flags;
24+
use rand::seq::IteratorRandom;
2425
use serde::Deserialize;
2526
use serde::Serialize;
2627
use tracing::debug;
@@ -742,15 +743,16 @@ impl AddressMan {
742743
}
743744

744745
fn get_address_by_service(&self, service: ServiceFlags) -> Option<(usize, LocalAddress)> {
745-
let peers = self.good_peers_by_service.get(&service)?;
746-
if peers.is_empty() {
747-
return None;
748-
}
749-
750-
let idx = rand::random::<usize>() % peers.len();
751-
let utreexo_peer = peers.get(idx)?;
746+
let candidates = self.good_peers_by_service.get(&service)?;
752747

753-
Some((*utreexo_peer, self.addresses.get(utreexo_peer)?.to_owned()))
748+
candidates
749+
.iter()
750+
.filter_map(|id| {
751+
let addr = self.addresses.get(id)?;
752+
(addr.state != AddressState::Connected).then_some((id, addr))
753+
})
754+
.choose(&mut rand::thread_rng())
755+
.map(|(id, addr)| (*id, addr.to_owned()))
754756
}
755757

756758
pub fn start_addr_man(&mut self, datadir: String) -> Vec<LocalAddress> {
@@ -969,7 +971,7 @@ impl AddressMan {
969971
/// Updates the service flags after we receive a version message
970972
pub fn update_set_service_flag(&mut self, idx: usize, flags: ServiceFlags) -> &mut Self {
971973
// if this peer turns out to not have the minimum required services, we remove it
972-
if !flags.has(ServiceFlags::NETWORK) || !flags.has(ServiceFlags::WITNESS) {
974+
if !flags.has(ServiceFlags::NETWORK_LIMITED) || !flags.has(ServiceFlags::WITNESS) {
973975
self.addresses.remove(&idx);
974976
for peers in self.peers_by_service.values_mut() {
975977
peers.retain(|&x| x != idx);
@@ -1321,6 +1323,41 @@ mod test {
13211323
}
13221324
}
13231325

1326+
#[test]
1327+
fn test_adding_fixed_peer() {
1328+
let signet_addresses =
1329+
load_addresses_from_json("./src/p2p_wire/seeds/signet_seeds.json").unwrap();
1330+
1331+
let mut addr_man =
1332+
AddressMan::new(None, &[ReachableNetworks::IPv4, ReachableNetworks::IPv6]);
1333+
addr_man.add_fixed_addresses(Network::Signet);
1334+
1335+
assert_eq!(addr_man.good_addresses.len(), signet_addresses.len());
1336+
1337+
let utreexo_addresses = signet_addresses
1338+
.iter()
1339+
.filter(|address| address.services.has(service_flags::UTREEXO.into()))
1340+
.collect::<Vec<_>>();
1341+
1342+
assert_eq!(
1343+
addr_man
1344+
.good_peers_by_service
1345+
.get(&service_flags::UTREEXO.into())
1346+
.unwrap()
1347+
.len(),
1348+
utreexo_addresses.len()
1349+
);
1350+
1351+
assert_eq!(
1352+
addr_man
1353+
.peers_by_service
1354+
.get(&service_flags::UTREEXO.into())
1355+
.unwrap()
1356+
.len(),
1357+
utreexo_addresses.len()
1358+
);
1359+
}
1360+
13241361
#[test]
13251362
fn test_parse() {
13261363
let signet_address =

crates/floresta-wire/src/p2p_wire/node/chain_selector_ctx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,7 @@ where
10051005
}
10061006

10071007
PeerMessages::Ready(version) => {
1008-
self.handle_peer_ready(peer, &version)?;
1008+
self.handle_peer_ready(peer, version)?;
10091009
if matches!(self.context.state, ChainSelectorState::LookingForForks(_)) {
10101010
let locator = self.chain.get_block_locator().unwrap();
10111011
self.send_to_peer(peer, NodeRequest::GetHeaders(locator))?;

crates/floresta-wire/src/p2p_wire/node/conn.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ where
549549
/// This is only done if we don't have any peers for a long time, or we
550550
/// can't find a Utreexo peer in a context we need them. This function
551551
/// won't do anything if `--connect` was used
552-
fn maybe_use_hardcoded_addresses(&mut self, needs_utreexo: bool) {
552+
fn maybe_use_hardcoded_addresses(&mut self) {
553553
if self.fixed_peer.is_some() {
554554
return;
555555
}
@@ -558,18 +558,11 @@ where
558558
return;
559559
}
560560

561-
let has_peers = !self.peers.is_empty();
562-
// Return if we have peers and utreexo isn't needed OR we have utreexo peers
563-
if has_peers && (!needs_utreexo || self.has_utreexo_peers()) {
561+
if self.address_man.enough_addresses() {
564562
return;
565563
}
566564

567-
let mut wait = HARDCODED_ADDRESSES_GRACE_PERIOD;
568-
if needs_utreexo {
569-
// This gives some extra time for the node to try connections after chain selection
570-
wait += Duration::from_secs(60);
571-
}
572-
565+
let wait = HARDCODED_ADDRESSES_GRACE_PERIOD;
573566
if self.startup_time.elapsed() < wait {
574567
return;
575568
}
@@ -629,8 +622,7 @@ where
629622
// If we've tried getting some connections, but the addresses we have are not
630623
// working. Try getting some more addresses from DNS
631624
self.maybe_ask_dns_seed_for_addresses();
632-
let needs_utreexo = required_service.has(service_flags::UTREEXO.into());
633-
self.maybe_use_hardcoded_addresses(needs_utreexo);
625+
self.maybe_use_hardcoded_addresses();
634626

635627
for _ in 0..T::NEW_CONNECTIONS_BATCH_SIZE {
636628
// Ignore the error so we don't break out of the loop

crates/floresta-wire/src/p2p_wire/node/peer_man.rs

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::address_man::AddressState;
3131
use crate::address_man::LocalAddress;
3232
use crate::block_proof::Bitmap;
3333
use crate::node::running_ctx::RunningNode;
34+
use crate::node::try_and_log;
3435
use crate::node_context::NodeContext;
3536
use crate::node_context::PeerId;
3637
use crate::node_interface::NodeResponse;
@@ -212,7 +213,7 @@ where
212213
pub(crate) fn handle_peer_ready(
213214
&mut self,
214215
peer: u32,
215-
version: &Version,
216+
mut version: Version,
216217
) -> Result<(), WireError> {
217218
self.inflight.remove(&InflightRequests::Connect(peer));
218219

@@ -226,6 +227,30 @@ where
226227
self.inflight
227228
.insert(InflightRequests::GetAddresses, (peer, Instant::now()));
228229

230+
let good_peers_count = self.connected_peers();
231+
if good_peers_count > T::MAX_OUTGOING_PEERS {
232+
// We allow utreexo, extra and manual peers to bypass our connection limits
233+
let is_utreexo_peer = matches!(version.kind, ConnectionKind::Regular(services) if services.has(service_flags::UTREEXO.into()));
234+
let is_manual_peer = version.kind == ConnectionKind::Manual;
235+
let is_extra = version.kind == ConnectionKind::Extra;
236+
237+
if !(is_utreexo_peer || is_manual_peer || is_extra) {
238+
debug!(
239+
"Already have {} peers, disconnecting peer to avoid blowing up our max of {}",
240+
good_peers_count,
241+
T::MAX_OUTGOING_PEERS
242+
);
243+
244+
// If a peer exceeds our max, just turn them into a feeler so we can receive their
245+
// AddrV2 message and then disconnect.
246+
self.peers.entry(peer).and_modify(|p| {
247+
p.kind = ConnectionKind::Feeler;
248+
});
249+
250+
version.kind = ConnectionKind::Feeler;
251+
}
252+
}
253+
229254
if version.kind == ConnectionKind::Feeler {
230255
let now = SystemTime::now()
231256
.duration_since(UNIX_EPOCH)
@@ -249,27 +274,6 @@ where
249274
return Ok(());
250275
}
251276

252-
let good_peers_count = self.connected_peers();
253-
if good_peers_count > T::MAX_OUTGOING_PEERS {
254-
// Don't allow our node to have more than T::MAX_OUTGOING_PEERS, unless this is a
255-
// manual peer, those can exceed our quota.
256-
if version.kind != ConnectionKind::Manual {
257-
debug!(
258-
"Already have {} peers, disconnecting peer to avoid blowing up our max of {}",
259-
good_peers_count,
260-
T::MAX_OUTGOING_PEERS
261-
);
262-
263-
// If a peer exceeds our max, just turn them into a feeler so we can receive their
264-
// AddrV2 message and then disconnect.
265-
self.peers.entry(peer).and_modify(|p| {
266-
p.kind = ConnectionKind::Feeler;
267-
});
268-
269-
return Ok(());
270-
}
271-
}
272-
273277
info!(
274278
"New peer id={} version={} blocks={} services={}",
275279
version.id, version.user_agent, version.blocks, version.services
@@ -479,7 +483,12 @@ where
479483

480484
for req in inflight {
481485
self.inflight.remove(&req.0);
482-
self.redo_inflight_request(req.0.clone())?;
486+
487+
if let Err(e) = self.redo_inflight_request(&req.0) {
488+
// CRITICAL: never drop the request, so we retry it later
489+
self.inflight.insert(req.0, req.1);
490+
return Err(e);
491+
}
483492
}
484493

485494
#[cfg(feature = "metrics")]
@@ -565,7 +574,7 @@ where
565574
.collect::<Vec<_>>();
566575

567576
for req in timed_out {
568-
let Some((peer, _)) = self.inflight.remove(&req) else {
577+
let Some((peer, time)) = self.inflight.remove(&req) else {
569578
continue;
570579
};
571580

@@ -587,8 +596,14 @@ where
587596
}
588597

589598
debug!("Request timed out: {req:?}");
590-
self.increase_banscore(peer, 1)?;
591-
self.redo_inflight_request(req)?;
599+
// Increase the banscore and try banning the peer if needed, then re-request
600+
try_and_log!(self.increase_banscore(peer, 1));
601+
602+
if let Err(e) = self.redo_inflight_request(&req) {
603+
// CRITICAL: never drop the request, so we retry it later
604+
self.inflight.insert(req, (peer, time));
605+
return Err(e);
606+
}
592607
}
593608

594609
Ok(())
@@ -617,39 +632,39 @@ where
617632
Ok(())
618633
}
619634

620-
pub(crate) fn redo_inflight_request(&mut self, req: InflightRequests) -> Result<(), WireError> {
635+
pub(crate) fn redo_inflight_request(
636+
&mut self,
637+
req: &InflightRequests,
638+
) -> Result<(), WireError> {
621639
match req {
622640
InflightRequests::UtreexoProof(block_hash) => {
623641
if !self.has_utreexo_peers() {
624642
return Ok(());
625643
}
626644

627-
if !self.blocks.contains_key(&block_hash) {
645+
if !self.blocks.contains_key(block_hash) {
628646
// If we don't have the block anymore, we can't ask for the proof
629647
return Ok(());
630648
}
631649

632-
if self
633-
.inflight
634-
.contains_key(&InflightRequests::UtreexoProof(block_hash))
635-
{
650+
if self.inflight.contains_key(req) {
636651
// If we already have an inflight request for this block, we don't need to redo it
637652
return Ok(());
638653
}
639654

640655
let peer = self.send_to_fast_peer(
641-
NodeRequest::GetBlockProof((block_hash, Bitmap::new(), Bitmap::new())),
656+
NodeRequest::GetBlockProof((*block_hash, Bitmap::new(), Bitmap::new())),
642657
service_flags::UTREEXO.into(),
643658
)?;
644659

645660
self.inflight.insert(
646-
InflightRequests::UtreexoProof(block_hash),
661+
InflightRequests::UtreexoProof(*block_hash),
647662
(peer, Instant::now()),
648663
);
649664
}
650665

651666
InflightRequests::Blocks(block) => {
652-
self.request_blocks(vec![block])?;
667+
self.request_blocks(vec![*block])?;
653668
}
654669

655670
InflightRequests::Headers => {

crates/floresta-wire/src/p2p_wire/node/running_ctx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ where
732732
"handshake with peer={peer} succeeded feeler={:?}",
733733
version.kind
734734
);
735-
self.handle_peer_ready(peer, &version)?;
735+
self.handle_peer_ready(peer, version)?;
736736
}
737737

738738
PeerMessages::Disconnected(idx) => {

crates/floresta-wire/src/p2p_wire/node/sync_ctx.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ impl NodeContext for SyncNode {
4848
ServiceFlags::NETWORK | ServiceFlags::WITNESS | service_flags::UTREEXO_ARCHIVE.into()
4949
}
5050

51-
const TRY_NEW_CONNECTION: u64 = 30; // 30 seconds
52-
const REQUEST_TIMEOUT: u64 = 60; // 1 minute
51+
const REQUEST_TIMEOUT: u64 = 60 * 2; // 2 minutes
5352
const MAX_INFLIGHT_REQUESTS: usize = 100; // double the default
5453

5554
// A more conservative value than the default of 1 second, since we'll have many peer messages
@@ -321,7 +320,7 @@ where
321320
}
322321

323322
PeerMessages::Ready(version) => {
324-
try_and_log!(self.handle_peer_ready(peer, &version));
323+
try_and_log!(self.handle_peer_ready(peer, version));
325324
}
326325

327326
PeerMessages::Disconnected(idx) => {

crates/floresta-wire/src/p2p_wire/seeds/signet_seeds.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
"state": {
107107
"Tried": 0
108108
},
109-
"services": 1037,
109+
"services": 1033,
110110
"port": 38333
111111
},
112112
{
@@ -139,7 +139,7 @@
139139
"state": {
140140
"Tried": 0
141141
},
142-
"services": 0,
142+
"services": 1033,
143143
"port": 38333
144144
},
145145
{
@@ -150,7 +150,7 @@
150150
"state": {
151151
"Tried": 0
152152
},
153-
"services": 0,
153+
"services": 1033,
154154
"port": 38333
155155
},
156156
{
@@ -161,7 +161,7 @@
161161
"state": {
162162
"Tried": 0
163163
},
164-
"services": 0,
164+
"services": 1033,
165165
"port": 38333
166166
}
167167
]

0 commit comments

Comments
 (0)