Skip to content

Commit de5cb7f

Browse files
authored
Merge pull request #12 from 2140-dev/feeler-7-14
p2p: Add "feeler" connection
2 parents e9e7c75 + e15577b commit de5cb7f

File tree

4 files changed

+81
-15
lines changed

4 files changed

+81
-15
lines changed

p2p/examples/feeler.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,15 @@ fn main() {
2222
.set_user_agent("/bitcoin-feeler:0.1.0".to_string())
2323
.connection_timeout(Duration::from_millis(3500))
2424
.change_network(NETWORK)
25-
.open_connection(socket_addr);
25+
.open_feeler(socket_addr);
2626
match connection {
27-
Ok(_) => tracing::info!("Connection successful!"),
27+
Ok(f) => {
28+
tracing::info!(
29+
"Connection successful: Advertised protocol version {}, Adveristed services {}",
30+
f.protocol_version.0,
31+
f.services
32+
);
33+
}
2834
Err(e) => tracing::warn!("Connection failed {e:?}"),
2935
}
3036
}

p2p/src/lib.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impl ConnectionContext {
7070
read_half: ReadHalf,
7171
negotiation: Negotiation,
7272
their_services: ServiceFlags,
73+
their_version: ProtocolVerison,
7374
) -> Self {
7475
let read_ctx = ReadContext {
7576
read_half,
@@ -83,6 +84,7 @@ impl ConnectionContext {
8384
write_half,
8485
negotiation,
8586
their_services,
87+
their_protocol_verison: their_version,
8688
};
8789
Self {
8890
read_ctx,
@@ -210,6 +212,7 @@ pub struct WriteContext {
210212
write_half: WriteHalf,
211213
negotiation: Negotiation,
212214
their_services: ServiceFlags,
215+
their_protocol_verison: ProtocolVerison,
213216
}
214217

215218
impl WriteContext {
@@ -432,6 +435,12 @@ impl Default for Offered {
432435
}
433436
}
434437

438+
#[derive(Debug, Clone, Copy)]
439+
pub struct Feeler {
440+
pub services: ServiceFlags,
441+
pub protocol_version: ProtocolVerison,
442+
}
443+
435444
pub(crate) struct MessageHeader {
436445
magic: Magic,
437446
_command: CommandString,
@@ -490,7 +499,7 @@ fn interpret_first_message(
490499
nonce: u64,
491500
their_expected_version: ProtocolVerison,
492501
their_expected_services: ServiceFlags,
493-
) -> Result<(), HandshakeError> {
502+
) -> Result<(ProtocolVerison, ServiceFlags), HandshakeError> {
494503
if let NetworkMessage::Version(version) = message {
495504
if version.nonce.eq(&nonce) {
496505
return Err(HandshakeError::ConnectedToSelf);
@@ -503,10 +512,10 @@ fn interpret_first_message(
503512
if !version.services.has(their_expected_services) {
504513
return Err(HandshakeError::UnsupportedFeature);
505514
}
515+
Ok((ProtocolVerison(version.version), version.services))
506516
} else {
507-
return Err(HandshakeError::IrrelevantMessage(message));
517+
Err(HandshakeError::IrrelevantMessage(message))
508518
}
509-
Ok(())
510519
}
511520

512521
/// Errors when parsing a peer-to-peer message.
@@ -656,8 +665,15 @@ macro_rules! define_version_message_logic {
656665
let version = $awaiter!(read_half.read_message(&mut $reader))?;
657666
match version {
658667
Some(version) => {
659-
interpret_first_message(version, nonce, $conn.their_version, $conn.their_services)
660-
.map_err(ConnectionError::Protocol)?;
668+
let (protocol, services) = interpret_first_message(
669+
version,
670+
nonce,
671+
$conn.their_version,
672+
$conn.their_services,
673+
)
674+
.map_err(ConnectionError::Protocol)?;
675+
$conn.their_services = services;
676+
$conn.their_version = protocol;
661677
}
662678
None => {
663679
return Err(ConnectionError::Protocol(HandshakeError::BadDecoy));
@@ -717,8 +733,13 @@ macro_rules! define_version_message_logic {
717733
&mut write_half,
718734
))?;
719735
}
720-
let context =
721-
ConnectionContext::new(write_half, read_half, negotiation, $conn.their_services);
736+
let context = ConnectionContext::new(
737+
write_half,
738+
read_half,
739+
negotiation,
740+
$conn.their_services,
741+
$conn.their_version,
742+
);
722743
Ok(($reader, context))
723744
}};
724745
}

p2p/src/net.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use bitcoin::{consensus, p2p::message_compact_blocks::SendCmpct};
1111

1212
use crate::{
1313
blocking_awaiter, interpret_first_message, make_version, version_handshake_blocking,
14-
ConnectionBuilder, ConnectionContext, HandshakeError, Negotiation, ParseMessageError,
14+
ConnectionBuilder, ConnectionContext, Feeler, HandshakeError, Negotiation, ParseMessageError,
1515
ReadContext, ReadHalf, WriteContext, WriteHalf,
1616
};
1717

@@ -30,11 +30,15 @@ pub trait ConnectionExt {
3030
self,
3131
tcp_stream: TcpStream,
3232
) -> Result<(TcpStream, ConnectionContext), ConnectionError>;
33+
34+
/// Open a "feeler" connection to test if the peer is online and update their services and
35+
/// protocol version.
36+
fn open_feeler(self, to: impl Into<SocketAddr>) -> Result<Feeler, ConnectionError>;
3337
}
3438

3539
impl ConnectionExt for ConnectionBuilder {
3640
fn open_connection(
37-
self,
41+
mut self,
3842
to: impl Into<SocketAddr>,
3943
) -> Result<(TcpStream, ConnectionContext), ConnectionError> {
4044
let socket_addr = to.into();
@@ -44,11 +48,26 @@ impl ConnectionExt for ConnectionBuilder {
4448
}
4549

4650
fn start_handshake(
47-
self,
51+
mut self,
4852
mut tcp_stream: TcpStream,
4953
) -> Result<(TcpStream, ConnectionContext), ConnectionError> {
5054
version_handshake_blocking!(tcp_stream, self)
5155
}
56+
57+
fn open_feeler(mut self, to: impl Into<SocketAddr>) -> Result<Feeler, ConnectionError> {
58+
let socket_addr = to.into();
59+
let mut tcp_stream = TcpStream::connect_timeout(&socket_addr, self.tcp_timeout)?;
60+
let res: Result<(TcpStream, ConnectionContext), ConnectionError> =
61+
version_handshake_blocking!(tcp_stream, self);
62+
let (_, ctx) = res?;
63+
let (_, wtx) = ctx.into_split();
64+
let services = wtx.their_services;
65+
let protocol_version = wtx.their_protocol_verison;
66+
Ok(Feeler {
67+
services,
68+
protocol_version,
69+
})
70+
}
5271
}
5372

5473
#[allow(clippy::result_large_err)]

p2p/src/tokio_ext.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use tokio::{
1414

1515
use crate::{
1616
async_awaiter, interpret_first_message, make_version, version_handshake_async,
17-
ConnectionBuilder, ConnectionContext, HandshakeError, Negotiation, ParseMessageError,
17+
ConnectionBuilder, ConnectionContext, Feeler, HandshakeError, Negotiation, ParseMessageError,
1818
ReadContext, ReadHalf, WriteContext, WriteHalf,
1919
};
2020

@@ -36,13 +36,17 @@ pub trait TokioConnectionExt {
3636
self,
3737
tcp_stream: TcpStream,
3838
) -> Result<(TcpStream, ConnectionContext), Self::Error>;
39+
40+
///
41+
#[allow(async_fn_in_trait)]
42+
async fn open_feeler(self, to: impl Into<SocketAddr>) -> Result<Feeler, Self::Error>;
3943
}
4044

4145
impl TokioConnectionExt for ConnectionBuilder {
4246
type Error = ConnectionError;
4347

4448
async fn open_connection(
45-
self,
49+
mut self,
4650
to: impl Into<SocketAddr>,
4751
) -> Result<(TcpStream, ConnectionContext), Self::Error> {
4852
let socket_addr = to.into();
@@ -53,11 +57,27 @@ impl TokioConnectionExt for ConnectionBuilder {
5357
}
5458

5559
async fn start_handshake(
56-
self,
60+
mut self,
5761
mut tcp_stream: TcpStream,
5862
) -> Result<(TcpStream, ConnectionContext), Self::Error> {
5963
version_handshake_async!(tcp_stream, self)
6064
}
65+
66+
async fn open_feeler(mut self, to: impl Into<SocketAddr>) -> Result<Feeler, Self::Error> {
67+
let socket_addr = to.into();
68+
let timeout = tokio::time::timeout(self.tcp_timeout, TcpStream::connect(socket_addr)).await;
69+
let mut tcp_stream =
70+
timeout.map_err(|_| ConnectionError::Protocol(HandshakeError::Timeout))??;
71+
let res: Result<(TcpStream, ConnectionContext), Self::Error> =
72+
version_handshake_async!(tcp_stream, self);
73+
let (_, ctx) = res?;
74+
let services = ctx.write_ctx.their_services;
75+
let protocol_version = ctx.write_ctx.their_protocol_verison;
76+
Ok(Feeler {
77+
services,
78+
protocol_version,
79+
})
80+
}
6181
}
6282

6383
async fn write_message<W: AsyncWriteExt + Send + Sync + Unpin>(

0 commit comments

Comments
 (0)