pub struct DhtService { /* private fields */ }Expand description
The DHT service for one node. Cloneable-by-Arc internally; wrap in Arc to share between the
serving task (inbound RPC) and querying callers.
Implementations§
Source§impl DhtService
impl DhtService
Sourcepub fn new(
local_id: PeerId,
local_addresses: Vec<CandidateAddr>,
config: DhtConfig,
transport: Arc<dyn DhtTransport>,
) -> Self
pub fn new( local_id: PeerId, local_addresses: Vec<CandidateAddr>, config: DhtConfig, transport: Arc<dyn DhtTransport>, ) -> Self
Create a service for the node identified by local_id, advertising local_addresses in the
provider records it announces, driving RPC over transport.
Sourcepub async fn bootstrap(
&self,
peers: &[BootstrapPeer],
) -> Result<usize, DhtError>
pub async fn bootstrap( &self, peers: &[BootstrapPeer], ) -> Result<usize, DhtError>
Seed the routing table from peers and populate it by looking up this node’s own id (the
canonical Kademlia bootstrap: a self-lookup fills the buckets around us). Returns the number
of distinct peers now known.
Safe to call repeatedly (on reconnect / when new bootstrap peers arrive) — it merges, never resets.
Sourcepub async fn find_node(
&self,
peer_id: &PeerId,
) -> Result<Vec<Contact>, DhtError>
pub async fn find_node( &self, peer_id: &PeerId, ) -> Result<Vec<Contact>, DhtError>
Find the k peers closest to peer_id (the routing primitive). Runs an iterative
find_node lookup and returns the converged closest contacts.
Sourcepub async fn find_providers(
&self,
content: &ContentId,
) -> Result<Vec<ProviderRecord>, DhtError>
pub async fn find_providers( &self, content: &ContentId, ) -> Result<Vec<ProviderRecord>, DhtError>
Find the providers of content — the peers holding it. Runs an iterative find_providers
lookup toward the content key, returning every live provider record collected (deduped by
provider). The node then connects to those providers over dig-nat and fetches via the L7 peer
RPC.
Returns an empty vec (not an error) when the content simply has no known providers; returns
DhtError::NoPeers only when there is no one to ask (empty routing table + no bootstrap).
Sourcepub async fn announce_provider(
&self,
content: &ContentId,
) -> Result<usize, DhtError>
pub async fn announce_provider( &self, content: &ContentId, ) -> Result<usize, DhtError>
Announce that THIS node holds content: build a provider record (this node’s peer_id +
addresses, expiring at now + provider_ttl), store it locally, remember to republish it, and
PUT it at the k nodes closest to the content key. Returns how many peers accepted the PUT.
Called when the node’s inventory gains content (a new capsule/root/resource it now serves).
Sourcepub async fn withdraw_provider(&self, content: &ContentId) -> bool
pub async fn withdraw_provider(&self, content: &ContentId) -> bool
Stop announcing content (the node no longer holds it). The record ages out of the DHT via
TTL; we just stop republishing it. Returns whether it was being announced.
This is the passive withdraw: it leaves this node’s own local provider record in place
(it only expires with TTL) and merely stops re-publishing it, so a find_providers on this
node may still return self until the local record’s TTL elapses. For an immediate
own-retract — the local-state half of the #1423 evict+retract step — use
retract_own_provider.
Sourcepub async fn ingest_verified_provider(
&self,
record: ProviderRecord,
) -> PutOutcome
pub async fn ingest_verified_provider( &self, record: ProviderRecord, ) -> PutOutcome
Ingest a provider record for a THIRD-PARTY holder that the caller has ALREADY verified was
signed by record.provider_peer_id — the inbound-add half of the real-time holdings map
(SPEC §6.5). Returns the store admission outcome.
This is the authenticated push path a node’s announce receiver calls after verifying a
signed HoldingsAnnounce (dig-gossip opcode 222): the holder’s signature has replaced mTLS
attribution as the proof of who provides the content, so — unlike the serving-side
add_provider (§6.4) — this method bypasses the mTLS self-announce identity check (the
caller, not the DHT, established authenticity). dig-dht itself stays crypto-free (SPEC §15):
it NEVER verifies a signature; passing an unverified record here is a caller bug that
poisons the local provider set.
Every other admission guard still applies exactly as for add_provider: the address list is
capped (MAX_ADDRESSES_PER_RECORD), expires_at is
clamped to min(record.expires_at, now + provider_ttl) (§6.2), and the per-key / global
admission caps (§6.3) are enforced — an over-capacity ingest returns
PutOutcome::RejectedOverCapacity and stores nothing. On acceptance the holder is folded
into the routing table so this node can reach it.
Sourcepub async fn remove_provider_record(
&self,
content_key: &str,
provider_peer_id: &str,
) -> bool
pub async fn remove_provider_record( &self, content_key: &str, provider_peer_id: &str, ) -> bool
Remove exactly the local provider record for (content_key, provider_peer_id) — the
inbound-retract half of the real-time holdings map (SPEC §6.6). Returns whether a record
was removed.
content_key and provider_peer_id are the 64-hex forms as they appear on a
ProviderRecord (content → content.to_key().to_hex(); the holder’s peer_id hex).
The caller MUST have verified the retract was signed by that same provider_peer_id
(authenticated retract): a retract signed by one holder removes ONLY that holder’s record and
can never evict another provider of the same key (censorship-resistance, §6.6). dig-dht does
not verify the signature (SPEC §15) — that is the caller’s responsibility.
Sourcepub async fn retract_own_provider(&self, content: &ContentId) -> bool
pub async fn retract_own_provider(&self, content: &ContentId) -> bool
Actively retract THIS node’s own provider record for content: remove the local record AND
stop republishing it, so find_providers on this node stops returning self as a holder
immediately (SPEC §6.6). Returns whether this node was providing the content (a local record
existed or the key was being announced).
This is the local-state half of the #1423 atomic evict + retract step (on an LRU cache
eviction the node no longer serves the content). Unlike the passive
withdraw_provider (which leaves the local record to expire via
TTL), this deletes it now. The copies previously PUT at the k closest peers are NOT deleted
by this call — they age out via TTL, or are removed sooner when dig-node floods the signed
retract announce and each recipient calls
remove_provider_record.
Sourcepub async fn holders_of(
&self,
content: &ContentId,
) -> Result<Vec<PeerId>, DhtError>
pub async fn holders_of( &self, content: &ContentId, ) -> Result<Vec<PeerId>, DhtError>
The peer_ids of the peers that hold content — a thin, address-free convenience over
find_providers for callers that only need “which peers hold X”
(e.g. an RPC holder-set query) and do not dial the holders themselves.
find_providers remains the PRIMARY API: it returns full ProviderRecords with candidate
addresses, which dig-download needs to actually connect and fetch. This method runs the same
distributed iterative lookup and simply projects each record to its holder peer_id
(records with a malformed peer id are skipped; the set is already deduped by provider).
Sourcepub async fn republish(&self) -> usize
pub async fn republish(&self) -> usize
Republish every content key this node still announces — re-runs the announce PUT so provider
records never expire while the node is online. Call on the DhtConfig::republish_interval.
Returns the number of content keys republished.
Sourcepub async fn refresh_buckets(&self) -> usize
pub async fn refresh_buckets(&self) -> usize
Refresh populated buckets by looking up a random key in each — keeps the routing table fresh
as peers churn. Call on the DhtConfig::refresh_interval. Returns the number of buckets
refreshed.
Sourcepub async fn gc(&self) -> usize
pub async fn gc(&self) -> usize
Drop expired provider records. Call periodically (piggy-backs on republish/refresh). Returns the number of records removed.
Sourcepub async fn ping(&self, peer: &Contact) -> bool
pub async fn ping(&self, peer: &Contact) -> bool
Ping a peer for liveness; on failure, evict it from the routing table. Used by the ping-and-replace maintenance when a bucket is full. Returns whether the peer is alive.
Sourcepub async fn handle_request(&self, request: DhtRequest) -> DhtResponse
pub async fn handle_request(&self, request: DhtRequest) -> DhtResponse
Answer an inbound DHT request from another node, without a known caller identity. Prefer
handle_request_from on an authenticated transport (it lets the
responder learn the caller and populate its routing table bidirectionally, the way Kademlia
tables fill).
Sourcepub async fn handle_request_from(
&self,
caller: Option<Contact>,
request: DhtRequest,
) -> DhtResponse
pub async fn handle_request_from( &self, caller: Option<Contact>, request: DhtRequest, ) -> DhtResponse
Answer an inbound DHT request, folding the authenticated caller into the routing table.
This is the server half — a dig-node wires it to inbound DHT streams, passing the caller’s
mTLS-verified Contact as caller. Learning the caller from every inbound RPC is how a
Kademlia node discovers peers without an explicit announce: a node that talks to you becomes
a candidate in your table. The caller MUST come from the authenticated transport (the mTLS
peer_id), never from the request body — identity is not self-asserted.
It reads/writes only local state (routing table + provider store) and never makes outbound RPCs, so it cannot recurse or block on the network.
Sourcepub async fn known_closest(&self, target: &Key) -> Vec<Contact>
pub async fn known_closest(&self, target: &Key) -> Vec<Contact>
The contacts currently in this node’s routing table closest to target (diagnostic /
introspection — the peers this node knows without any network round-trip).
Sourcepub async fn routing_len(&self) -> usize
pub async fn routing_len(&self) -> usize
The number of peers currently in this node’s routing table (diagnostic / metrics).
Auto Trait Implementations§
impl !RefUnwindSafe for DhtService
impl !UnwindSafe for DhtService
impl Freeze for DhtService
impl Send for DhtService
impl Sync for DhtService
impl Unpin for DhtService
impl UnsafeUnpin for DhtService
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.