Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions OPERATOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,8 @@ Clean smoke:
| `--listen ADDR` | `listen=` | bind later default port |
| `--no-listen` / `--listen=0` | `listen=0` / `no_listen=` | bind a loopback default; **off** = no P2P socket (outbound-only) |
| `--no-discover` | `no_discover=` | discover **on**; flag off = no self-announce / `localaddresses` |
| `--connect ADDR` | `connect=` (repeatable) | seeds |
| `--only-net NET` | `only_net=` | all nets; repeatable `ipv4` / `ipv6` / `onion` (`i2p`/`cjdns` later) |
| `--connect ADDR` | `connect=` (repeatable) | seeds; `IP:port` or Tor v3 `.onion:port` |
| `--proxy HOST:PORT` | `proxy=` | unset — SOCKS5 for all P2P outbound |
| `--onion HOST:PORT` | `onion=` | unset — SOCKS5 for onion destinations |
| `--proxy-randomize[=0\|1]` | `proxy_randomize=` | **on** — fresh SOCKS username per peer (Tor circuit isolation) |
Expand Down Expand Up @@ -437,6 +438,10 @@ mempool_size_mb=100
pass `--connect ADDR` (or reuse a `peers` file). `--proxy-randomize` (default
on) uses a fresh SOCKS username per peer so Tor isolates circuits.
`--onion HOST:PORT` stores a separate SOCKS endpoint for onion destinations.
`--only-net onion` (repeatable with `ipv4`/`ipv6`) filters dial and learn;
onion requires `--proxy` or `--onion`. `--connect foo.onion:8333` is a start
error when the v3 checksum is invalid. The peers file is `rbitcoin-peers-v2`
(v1 IPv4/IPv6 still loads).

`--listen=0` / `--no-listen` starts without a P2P TCP bind (no ISP port
forward). `--max-inbound 0` refuses inbound slots. `--no-discover` does not
Expand Down Expand Up @@ -609,7 +614,9 @@ warning and continues without inhibit.
**Peers file:** `{datadir}/peers` stores discovered addresses and **PeerFlags**
(connected / fast / slow / incompatible / last-fail) between runs. Loaded at
start (before seeds), updated after IBD and on shutdown. Seeds are merged in
without clearing known flags.
without clearing known flags. New writes are `rbitcoin-peers-v2` (IPv4, IPv6,
and Tor v3 `.onion:port` tokens). `rbitcoin-peers-v1` IPv4/IPv6 files still
load.

**Index modes:** Direct vs Tip: [`docs/concurrency.md`](docs/concurrency.md).
IBD finishes Class A + `tx.head` + spend annotations **before** tip; tip entry
Expand Down
1 change: 1 addition & 0 deletions crates/rbitcoin-net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ bip324 = { workspace = true }
tokio = { workspace = true }
arc-swap = { workspace = true }
getrandom = "0.4"
sha3 = "0.10"

[target.'cfg(target_os = "linux")'.dependencies]
# POLLRDHUP: peer FIN with unread bytes still in the receive buffer.
Expand Down
25 changes: 20 additions & 5 deletions crates/rbitcoin-net/src/ibd/events/confirm_reject_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1961,14 +1961,23 @@ fn apply_peer_event_body_and_control_surface() {
inject_learned_addrs(
&mut book,
&[
addr(2),
local,
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 1),
crate::NetAddr::Ip(addr(2)),
crate::NetAddr::Ip(local),
crate::NetAddr::Ip(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 1)),
],
local,
1,
);
assert!(book.entry(&addr(2)).is_some());
let onion: crate::NetAddr =
"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
.parse()
.unwrap();
inject_learned_addrs(&mut book, &[onion], local, 1);
assert!(
book.entries().iter().any(|e| e.addr == onion),
"IBD addrv2 onion must enter the dial book"
);

// Dead releases work.
st.slots[0].in_flight.insert(h(4));
Expand Down Expand Up @@ -2379,7 +2388,10 @@ fn apply_peer_event_block_framed_bq_horizon_and_headers_done() {
let n0 = book.len();
inject_learned_addrs(
&mut book,
&[SocketAddr::new(IpAddr::V4(Ipv4Addr::new(9, 9, 9, 9)), 8333)],
&[crate::NetAddr::Ip(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(9, 9, 9, 9)),
8333,
))],
local,
1,
);
Expand All @@ -2396,7 +2408,10 @@ fn apply_peer_event_block_framed_bq_horizon_and_headers_done() {
ctrl_tx
.send(PeerEvent::Addrs {
peer: 1,
addrs: vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), 8333)],
addrs: vec![crate::NetAddr::Ip(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)),
8333,
))],
})
.unwrap();
let stats = super::super::status::LoopStats::default();
Expand Down
10 changes: 7 additions & 3 deletions crates/rbitcoin-net/src/ibd/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ fn apply_peer_dead(st: &mut IbdWorkState, peer_book: &mut AddrMan, peer: usize,
/// Grow the IBD dial book from peer-advertised addresses (getaddr responses).
pub(crate) fn inject_learned_addrs(
book: &mut AddrMan,
addrs: &[SocketAddr],
addrs: &[crate::NetAddr],
local_addr: SocketAddr,
from_peer: usize,
) {
Expand All @@ -521,10 +521,14 @@ pub(crate) fn inject_learned_addrs(
}
let mut added = 0usize;
for &a in addrs {
if a == local_addr || a.ip().is_unspecified() || a.port() == 0 {
if a.port() == 0 {
continue;
}
if book.add_learned(a, MAX_PEER_POOL) {
match a {
crate::NetAddr::Ip(s) if s == local_addr || s.ip().is_unspecified() => continue,
_ => {}
}
if book.add_learned_addr(a, MAX_PEER_POOL) {
added += 1;
}
}
Expand Down
60 changes: 43 additions & 17 deletions crates/rbitcoin-net/src/ibd/peer_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub(crate) enum PeerEvent {
/// Addresses learned from `addr` / `addrv2` (for IBD redial pool growth).
Addrs {
peer: usize,
addrs: Vec<SocketAddr>,
addrs: Vec<crate::NetAddr>,
},
/// Peer failed or closed.
Dead {
Expand Down Expand Up @@ -160,7 +160,7 @@ pub(crate) async fn spawn_peer(
sinks: PeerEventSinks,
dialer: crate::socks::Dialer,
) -> Result<PeerSlot, NetError> {
let stream = dialer.connect(addr).await?;
let stream = dialer.connect_net(crate::NetAddr::Ip(addr)).await?;
let ua = rbitcoin_primitives::rbitcoin_subversion(env!("CARGO_PKG_VERSION"), &[] as &[&str])
.unwrap_or_else(|_| format!("/rbitcoin:{}/", env!("CARGO_PKG_VERSION")));
let (ver, reader, writer, _wire, _tcp_shutdown) = connect_and_handshake_timed(
Expand Down Expand Up @@ -277,13 +277,13 @@ pub(crate) async fn spawn_peer(
}
}
NetworkMessage::Addr(list) => {
let addrs = socket_addrs_from_addr(&list);
let addrs = net_addrs_from_addr(&list);
if !addrs.is_empty() {
sinks_d.send_ctrl(PeerEvent::Addrs { peer: id, addrs });
}
}
NetworkMessage::AddrV2(list) => {
let addrs = socket_addrs_from_addrv2(&list);
let addrs = net_addrs_from_addrv2(&list);
if !addrs.is_empty() {
sinks_d.send_ctrl(PeerEvent::Addrs { peer: id, addrs });
}
Expand Down Expand Up @@ -436,25 +436,35 @@ pub(crate) async fn spawn_peer(
}

/// IPv4/IPv6 sockets that advertise full/limited network **and** `P2P_V2`.
fn socket_addrs_from_addr(list: &[(u32, bitcoin::p2p::address::Address)]) -> Vec<SocketAddr> {
fn net_addrs_from_addr(list: &[(u32, bitcoin::p2p::address::Address)]) -> Vec<crate::NetAddr> {
socket_addrs_from_addr(list)
.into_iter()
.map(crate::NetAddr::Ip)
.collect()
}

/// BIP155 rows that advertise full/limited network **and** `P2P_V2`, including onion.
fn net_addrs_from_addrv2(list: &[bitcoin::p2p::address::AddrV2Message]) -> Vec<crate::NetAddr> {
let mut out = Vec::with_capacity(list.len().min(32));
for (_ts, a) in list {
for a in list {
if !services_useful_for_ibd(a.services) {
continue;
}
if let Ok(sa) = a.socket_addr() {
if usable_dial_addr(&sa) {
out.push(sa);
}
let Some(addr) = crate::NetAddr::from_addrv2(a) else {
continue;
};
match addr {
crate::NetAddr::Ip(sa) if !usable_dial_addr(&sa) => {}
ok => out.push(ok),
}
}
out
}

/// IPv4/IPv6 sockets that advertise full/limited network **and** `P2P_V2`.
fn socket_addrs_from_addrv2(list: &[bitcoin::p2p::address::AddrV2Message]) -> Vec<SocketAddr> {
fn socket_addrs_from_addr(list: &[(u32, bitcoin::p2p::address::Address)]) -> Vec<SocketAddr> {
let mut out = Vec::with_capacity(list.len().min(32));
for a in list {
for (_ts, a) in list {
if !services_useful_for_ibd(a.services) {
continue;
}
Expand Down Expand Up @@ -650,13 +660,13 @@ mod tests {
addr: AddrV2::Ipv4(Ipv4Addr::new(9, 9, 9, 11)),
port: 0,
};
let out2 = socket_addrs_from_addrv2(&[v2_good, v2_net_only, v2_bad_svc, v2_zero_port]);
let out2 = net_addrs_from_addrv2(&[v2_good, v2_net_only, v2_bad_svc, v2_zero_port]);
assert_eq!(
out2,
vec![SocketAddr::new(
vec![crate::NetAddr::Ip(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(9, 9, 9, 9)),
18444
)]
))]
);

let v6_multi =
Expand All @@ -669,7 +679,23 @@ mod tests {
addr: AddrV2::Ipv6(Ipv6Addr::LOCALHOST),
port: 8333,
};
let out3 = socket_addrs_from_addrv2(&[v2_v6]);
assert_eq!(out3, vec![v6_net]);
let out3 = net_addrs_from_addrv2(&[v2_v6]);
assert_eq!(out3, vec![crate::NetAddr::Ip(v6_net)]);

let onion: crate::NetAddr =
"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
.parse()
.unwrap();
let crate::NetAddr::Onion { pk, port } = onion else {
panic!("fixture");
};
let v2_onion = AddrV2Message {
time: 1,
services: v2_net,
addr: AddrV2::TorV3(pk),
port,
};
let nets = net_addrs_from_addrv2(&[v2_onion]);
assert_eq!(nets, vec![onion]);
}
}
6 changes: 4 additions & 2 deletions crates/rbitcoin-net/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod ibd;
mod most_work;
mod msg_decode;
mod net_permissions;
mod netaddr;
mod netgroup;
mod peer;
mod peer_dos;
Expand Down Expand Up @@ -43,15 +44,16 @@ pub use net_permissions::{
apply_implicit, parse_whitebind, parse_whitelist, NetPermTable, NetPermissionFlags,
WhitebindGrant, WhitelistGrant, DEFAULT_WHITELISTFORCERELAY, DEFAULT_WHITELISTRELAY,
};
pub use netaddr::{addr_allowed, NetAddr, OnlyNet};
pub use netgroup::netgroup;
pub use peer::{
drain_pending_now, flush_tx_invs, force_announce_txid, local_service_flags, run_feeler_timed,
PendingBlocks, V2PlainSession, MAX_SERVE_BLOCKS,
};
pub use peer_dos::DEFAULT_MAX_INBOUND;
pub use peers::{
parse_peer_addr, pick_stale_follow_evict, DialRequest, DialTarget, LivePeer, PeerConnType,
PeerHub, PeerInfo, PeerOut, PingAction,
parse_peer_addr, parse_peer_net, pick_stale_follow_evict, DialRequest, DialTarget, LivePeer,
PeerConnType, PeerHub, PeerInfo, PeerOut, PingAction,
};
pub use rbitcoin_mempool::AcceptError;
pub(crate) use rbitcoin_mempool::MempoolGraphStats;
Expand Down
Loading
Loading