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
1,052 changes: 526 additions & 526 deletions Cargo.lock

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,18 @@ debug = true
[workspace.dependencies]
# Internal crates. Version-plus-path keeps publishing metadata while making this
# monorepo's sources authoritative for all local builds.
librtbit = { version = "0.1.6", path = "crates/librtbit", default-features = false }
bencode = { version = "0.1.5", path = "crates/librtbit-bencode", package = "librtbit-bencode", default-features = false }
buffers = { version = "0.1.2", path = "crates/librtbit-buffers", package = "librtbit-buffers" }
clone_to_owned = { version = "0.1.1", path = "crates/librtbit-clone-to-owned", package = "librtbit-clone-to-owned" }
librtbit-core = { version = "0.1.5", path = "crates/librtbit-core", default-features = false }
dht = { version = "0.1.4", path = "crates/librtbit-dht", package = "librtbit-dht", default-features = false }
librtbit-lsd = { version = "0.1.2", path = "crates/librtbit-lsd", default-features = false }
peer_binary_protocol = { version = "0.1.4", path = "crates/librtbit-peer-protocol", package = "librtbit-peer-protocol", default-features = false }
sha1w = { version = "0.1.2", path = "crates/librtbit-sha1-wrapper", package = "librtbit-sha1-wrapper", default-features = false }
tracker_comms = { version = "0.1.6", path = "crates/librtbit-tracker-comms", package = "librtbit-tracker-comms", default-features = false }
librtbit-upnp = { version = "0.1.2", path = "crates/librtbit-upnp" }
upnp-serve = { version = "0.1.2", path = "crates/librtbit-upnp-serve", package = "librtbit-upnp-serve", default-features = false }
librtbit = { version = "0.1.0", path = "crates/librtbit", package = "swarmforge", default-features = false }
bencode = { version = "0.1.0", path = "crates/librtbit-bencode", package = "swarmforge-bencode", default-features = false }
buffers = { version = "0.1.0", path = "crates/librtbit-buffers", package = "swarmforge-buffers" }
clone_to_owned = { version = "0.1.0", path = "crates/librtbit-clone-to-owned", package = "swarmforge-clone-to-owned" }
librtbit-core = { version = "0.1.0", path = "crates/librtbit-core", package = "swarmforge-core", default-features = false }
dht = { version = "0.1.0", path = "crates/librtbit-dht", package = "swarmforge-dht", default-features = false }
librtbit-lsd = { version = "0.1.0", path = "crates/librtbit-lsd", package = "swarmforge-lsd", default-features = false }
peer_binary_protocol = { version = "0.1.0", path = "crates/librtbit-peer-protocol", package = "swarmforge-peer-protocol", default-features = false }
sha1w = { version = "0.1.0", path = "crates/librtbit-sha1-wrapper", package = "swarmforge-sha1-wrapper", default-features = false }
tracker_comms = { version = "0.1.0", path = "crates/librtbit-tracker-comms", package = "swarmforge-tracker-comms", default-features = false }
librtbit-upnp = { version = "0.1.0", path = "crates/librtbit-upnp", package = "swarmforge-upnp" }
upnp-serve = { version = "0.1.0", path = "crates/librtbit-upnp-serve", package = "swarmforge-upnp-serve", default-features = false }

# External crates
anyhow = "1"
Expand Down
32 changes: 32 additions & 0 deletions crates/SWARMFORGE-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# SwarmForge

SwarmForge is the reusable BitTorrent engine and protocol-crate family that
powers [rustTorrent](https://github.com/TheDancingDeveloper-org/rustTorrent).
The public packages are released together from the rustTorrent monorepo.

The first coordinated release changes Cargo package identities from the
historical `librtbit-*` names to `swarmforge-*`. Rust library names remain
compatible, so existing imports such as `librtbit::Session`,
`librtbit_core::Id20`, and `librtbit_bencode` continue to work when dependencies
use an alias:

```toml
librtbit = { package = "swarmforge", version = "0.1" }
librtbit-core = { package = "swarmforge-core", version = "0.1" }
bencode = { package = "swarmforge-bencode", version = "0.1" }
```

Consumers must migrate every SwarmForge family member in one dependency graph
atomically. Mixing old and new package identities can duplicate shared types and
break trait coherence. The historical repositories and packages remain available
as rollback sources while downstream consumers migrate.

SwarmForge is derived from [rqbit](https://github.com/ikatson/rqbit) by Igor
Katson. Copyright 2021 Igor Katson. The original Apache-2.0 license applies; see
the rustTorrent repository's [license](https://github.com/TheDancingDeveloper-org/rustTorrent/blob/main/LICENSE)
and
[fork provenance](https://github.com/TheDancingDeveloper-org/rustTorrent/blob/main/docs/FORK_PROVENANCE.md)
for details.

Source, issues, and release history are maintained in the
[rustTorrent repository](https://github.com/TheDancingDeveloper-org/rustTorrent).
12 changes: 9 additions & 3 deletions crates/librtbit-bencode/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
[package]
name = "librtbit-bencode"
version = "0.1.5"
name = "swarmforge-bencode"
version = "0.1.0"
edition = "2024"
description = "Bencode serialization and deserialization using Serde"
license = "MIT"
license = "Apache-2.0"
repository = "https://github.com/TheDancingDeveloper-org/rustTorrent"
readme = "../SWARMFORGE-README.md"
keywords = ["bencode", "bittorrent", "swarmforge"]
categories = ["encoding", "network-programming"]

[lib]
name = "librtbit_bencode"

[dependencies]
serde = { version = "1", features = ["derive"] }
Expand Down
12 changes: 9 additions & 3 deletions crates/librtbit-buffers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
[package]
name = "librtbit-buffers"
version = "0.1.2"
name = "swarmforge-buffers"
version = "0.1.0"
edition = "2024"
description = "Utils to work with byte buffers in librtbit source code"
license = "MIT"
license = "Apache-2.0"
repository = "https://github.com/TheDancingDeveloper-org/rustTorrent"
readme = "../SWARMFORGE-README.md"
keywords = ["buffers", "bittorrent", "swarmforge"]
categories = ["data-structures", "network-programming"]

[lib]
name = "librtbit_buffers"

[dependencies]
serde = "1"
Expand Down
12 changes: 9 additions & 3 deletions crates/librtbit-clone-to-owned/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
[package]
name = "librtbit-clone-to-owned"
version = "0.1.1"
name = "swarmforge-clone-to-owned"
version = "0.1.0"
edition = "2024"
description = "Util traits to represent something that can be made owned and change type at the same time"
license = "MIT"
license = "Apache-2.0"
repository = "https://github.com/TheDancingDeveloper-org/rustTorrent"
readme = "../SWARMFORGE-README.md"
keywords = ["ownership", "bittorrent", "swarmforge"]
categories = ["data-structures"]

[lib]
name = "librtbit_clone_to_owned"

[dependencies]
bytes = "1"
Expand Down
12 changes: 9 additions & 3 deletions crates/librtbit-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
[package]
name = "librtbit-core"
version = "0.1.5"
name = "swarmforge-core"
version = "0.1.0"
edition = "2024"
description = "Core types and utilities for the rtbit BitTorrent client"
license = "MIT"
license = "Apache-2.0"
repository = "https://github.com/TheDancingDeveloper-org/rustTorrent"
readme = "../SWARMFORGE-README.md"
keywords = ["bittorrent", "torrent", "swarmforge"]
categories = ["network-programming", "data-structures"]

[lib]
name = "librtbit_core"

[features]
default = ["sha1-crypto-hash"]
Expand Down
75 changes: 75 additions & 0 deletions crates/librtbit-core/src/announce_port.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::{
num::NonZeroU16,
sync::{
Arc,
atomic::{AtomicU16, Ordering},
},
};

/// Runtime-mutable port advertised to BitTorrent peer-discovery services.
///
/// A zero atomic value represents a disabled announcement. Clones share the
/// same value, allowing tracker, DHT, and LSD workers to observe lease changes
/// without being restarted.
#[derive(Clone, Debug, Default)]
pub struct AnnouncePort(Arc<AtomicU16>);

impl AnnouncePort {
pub fn new(port: Option<NonZeroU16>) -> Self {
Self(Arc::new(AtomicU16::new(
port.map(NonZeroU16::get).unwrap_or_default(),
)))
}

pub fn get(&self) -> Option<NonZeroU16> {
NonZeroU16::new(self.0.load(Ordering::Acquire))
}

pub fn set(&self, port: NonZeroU16) {
self.0.store(port.get(), Ordering::Release);
}
}

#[cfg(test)]
mod tests {
use super::AnnouncePort;
use std::num::NonZeroU16;
use std::thread;

#[test]
fn clones_observe_runtime_updates() {
let port = AnnouncePort::new(NonZeroU16::new(4241));
let worker_port = port.clone();

port.set(NonZeroU16::new(51_234).unwrap());

assert_eq!(worker_port.get().map(NonZeroU16::get), Some(51_234));
}

#[test]
fn concurrent_readers_observe_only_complete_updates() {
let port = AnnouncePort::new(NonZeroU16::new(4241));
let readers = (0..4)
.map(|_| {
let port = port.clone();
thread::spawn(move || {
for _ in 0..10_000 {
let observed = port.get().map(NonZeroU16::get).unwrap();
assert!(matches!(observed, 4241 | 51_234));
}
})
})
.collect::<Vec<_>>();

for _ in 0..10_000 {
port.set(NonZeroU16::new(51_234).unwrap());
port.set(NonZeroU16::new(4241).unwrap());
}
port.set(NonZeroU16::new(51_234).unwrap());

for reader in readers {
reader.join().unwrap();
}
assert_eq!(port.get().map(NonZeroU16::get), Some(51_234));
}
}
2 changes: 2 additions & 0 deletions crates/librtbit-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod announce_port;
pub mod compact_ip;
pub mod constants;
pub mod directories;
Expand All @@ -9,6 +10,7 @@ pub mod peer_id;
pub mod spawn_utils;
pub mod speed_estimator;
pub mod torrent_metainfo;
pub use announce_port::AnnouncePort;
pub use hash_id::{Id20, Id32};

pub use error::Error;
Expand Down
12 changes: 9 additions & 3 deletions crates/librtbit-dht/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
[package]
name = "librtbit-dht"
version = "0.1.4"
name = "swarmforge-dht"
version = "0.1.0"
edition = "2024"
description = "Kademlia DHT implementation for the rtbit BitTorrent client"
license = "MIT"
license = "Apache-2.0"
repository = "https://github.com/TheDancingDeveloper-org/rustTorrent"
readme = "../SWARMFORGE-README.md"
keywords = ["bittorrent", "dht", "swarmforge"]
categories = ["network-programming"]

[lib]
name = "librtbit_dht"

[features]
default = ["sha1-crypto-hash"]
Expand Down
52 changes: 47 additions & 5 deletions crates/librtbit-dht/src/dht.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use futures::{
use leaky_bucket::RateLimiter;
use librqbit_dualstack_sockets::{BindDevice, UdpSocket};
use librtbit_core::{
AnnouncePort,
compact_ip::{CompactSerialize, CompactSerializeFixedLen},
crate_version,
hash_id::Id20,
Expand Down Expand Up @@ -108,7 +109,16 @@ trait RecursiveRequestCallbacks: Sized + Send + Sync + 'static {
struct RecursiveRequestCallbacksGetPeers {
// Id20::from_str("00000fffffffffffffffffffffffffffffffffff").unwrap()
min_distance_to_announce: Id20,
announce_port: Option<u16>,
announce_port: Option<AnnouncePort>,
}

impl RecursiveRequestCallbacksGetPeers {
fn current_announce_port(&self) -> Option<u16> {
self.announce_port
.as_ref()
.and_then(AnnouncePort::get)
.map(std::num::NonZeroU16::get)
}
}

impl RecursiveRequestCallbacks for RecursiveRequestCallbacksGetPeers {
Expand All @@ -121,8 +131,8 @@ impl RecursiveRequestCallbacks for RecursiveRequestCallbacksGetPeers {
addr: SocketAddr,
resp: &crate::Result<ResponseOrError>,
) {
let announce_port = match self.announce_port {
Some(a) => a,
let announce_port = match self.current_announce_port() {
Some(port) => port,
None => return,
};
let resp = match resp {
Expand Down Expand Up @@ -157,6 +167,27 @@ impl RecursiveRequestCallbacks for RecursiveRequestCallbacksGetPeers {
}
}

#[cfg(test)]
mod announce_port_tests {
use super::RecursiveRequestCallbacksGetPeers;
use librtbit_core::{AnnouncePort, Id20};
use std::{num::NonZeroU16, str::FromStr};

#[test]
fn announce_peer_decision_reads_runtime_port() {
let shared = AnnouncePort::new(NonZeroU16::new(4241));
let callbacks = RecursiveRequestCallbacksGetPeers {
min_distance_to_announce: Id20::from_str("0000ffffffffffffffffffffffffffffffffffff")
.unwrap(),
announce_port: Some(shared.clone()),
};

assert_eq!(callbacks.current_announce_port(), Some(4241));
shared.set(NonZeroU16::new(51_234).unwrap());
assert_eq!(callbacks.current_announce_port(), Some(51_234));
}
}

struct RecursiveRequestCallbacksFindNodes {}
impl RecursiveRequestCallbacks for RecursiveRequestCallbacksFindNodes {
fn on_request_start(&self, req: &RecursiveRequest<Self>, target_node: Id20, addr: SocketAddr) {
Expand Down Expand Up @@ -204,7 +235,7 @@ pub struct RequestPeersStream {
}

impl RequestPeersStream {
fn new(dht: Arc<DhtState>, info_hash: Id20, announce_port: Option<u16>) -> Self {
fn new(dht: Arc<DhtState>, info_hash: Id20, announce_port: Option<AnnouncePort>) -> Self {
let (peer_tx, peer_rx) = unbounded_channel();
let make = |is_v4: bool, dht: Arc<DhtState>, peer_tx: UnboundedSender<SocketAddr>| {
let (node_tx, node_rx) = unbounded_channel();
Expand All @@ -222,7 +253,7 @@ impl RequestPeersStream {
"0000ffffffffffffffffffffffffffffffffffff",
)
.unwrap(),
announce_port,
announce_port: announce_port.clone(),
},
});
rp.request_peers_forever(node_rx, is_v4)
Expand Down Expand Up @@ -1438,6 +1469,17 @@ impl DhtState {
self: &Arc<Self>,
info_hash: Id20,
announce_port: Option<u16>,
) -> RequestPeersStream {
self.get_peers_with_shared_announce_port(
info_hash,
announce_port.map(|port| AnnouncePort::new(std::num::NonZeroU16::new(port))),
)
}

pub fn get_peers_with_shared_announce_port(
self: &Arc<Self>,
info_hash: Id20,
announce_port: Option<AnnouncePort>,
) -> RequestPeersStream {
RequestPeersStream::new(self.clone(), info_hash, announce_port)
}
Expand Down
12 changes: 9 additions & 3 deletions crates/librtbit-lsd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
[package]
name = "librtbit-lsd"
version = "0.1.2"
name = "swarmforge-lsd"
version = "0.1.0"
edition = "2024"
description = "BEP 14 Local Service Discovery for the rtbit BitTorrent client"
license = "MIT"
license = "Apache-2.0"
repository = "https://github.com/TheDancingDeveloper-org/rustTorrent"
readme = "../SWARMFORGE-README.md"
keywords = ["bittorrent", "lsd", "swarmforge"]
categories = ["network-programming"]

[lib]
name = "librtbit_lsd"

[dependencies]
anyhow = "1"
Expand Down
Loading
Loading