Skip to main content

DhtService

Struct DhtService 

Source
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

Source

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.

Source

pub fn local_id(&self) -> &PeerId

This node’s id.

Source

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.

Source

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.

Source

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).

Source

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).

Source

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.

Source

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.

Source

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 (contentcontent.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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

pub async fn gc(&self) -> usize

Drop expired provider records. Call periodically (piggy-backs on republish/refresh). Returns the number of records removed.

Source

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.

Source

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).

Source

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.

Source

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).

Source

pub async fn routing_len(&self) -> usize

The number of peers currently in this node’s routing table (diagnostic / metrics).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows 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
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows 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
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .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
where Self: BorrowMut<B>, B: ?Sized,

Calls .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
where Self: AsRef<R>, R: ?Sized,

Calls .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
where Self: AsMut<R>, R: ?Sized,

Calls .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
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more