pub struct ProviderRecord {
pub content_key: String,
pub provider_peer_id: String,
pub addresses: Vec<CandidateAddr>,
pub expires_at: u64,
pub unverified_mirror_coin_id: Option<String>,
}Expand description
The DHT’s stored value: peer provider_peer_id holds the content whose key is content_key,
reachable at addresses, until expires_at.
content_keyis the 64-hexKeythe content id hashed to — the DHT stores by key, not by the (larger, granularity-tagged) content id, so a record is compact and the store is a pure key→providers map.provider_peer_idis the 64-hexpeer_idof the holder; a finder builds aPeerTargetfrom it plusaddressesand connects via dig-nat.expires_atis absolute Unix seconds; a record past its expiry is treated as absent and GC’d. The holder republishes (a fresh record with a newexpires_at) before expiry to stay findable.
Fields§
§content_key: StringThe content key (64-hex) this record provides for — the Key a content id
hashed to.
provider_peer_id: StringThe holder’s peer_id (64-hex).
addresses: Vec<CandidateAddr>Candidate addresses to reach the holder, ordered IPv6-first then most-direct-first by
AddressKind::rank and bounded to MAX_ADDRESSES_PER_RECORD — held by BOTH
ProviderRecord::new and deserialization ([deserialize_capped_addresses]), so a record
off the wire carries the same guarantee as a constructed one.
expires_at: u64Absolute expiry (Unix seconds). A record at/after this time is stale.
unverified_mirror_coin_id: Option<String>UNTRUSTED POINTER, NOT EVIDENCE — an optional 64-hex mirror-coin id the publisher claims
bonds this (store, root) claim, carried so a verifier can fetch ONE coin instead of
searching for it.
Holding this proves nothing whatsoever. Any peer can publish any 32 bytes, and a hostile or merely stale publisher can supply a real, well-formed, fully-collateralised coin id that bonds a different store, a different root, a different epoch, or a different owner — every property checks out except the one that matters. A consumer MUST, against its own chain source:
- fetch the coin and verify it sits at
dig_mirror_coin::mirror_coin_puzzle_hash(), - verify it is $DIG with the asset id re-derived from the creating spend,
- verify it carries the full collateral, and
- confirm the coin’s DECLARED bond matches the claim —
advertises(store, root, epoch)is an exact equality on the declared triple, and the owner is checked against the four-termdig_mirror_coin::mirror_hint(store, root, owner_puzzle_hash, epoch).
Step 4 is what binds the coin to the claim; 1-3 alone prove only that a valid mirror coin exists somewhere. No verification happens in this crate — the DHT has no chain source.
Absence is normal and must never degrade discovery. Old publishers, publishers that have
not created the coin yet, and publishers mid-epoch-rollover all legitimately omit it; a
republished record can also carry a pointer that has since gone stale across an epoch
boundary. A store-granularity record can never carry one at all, since a mirror coin
bonds (store, root, owner, epoch) and a store-granularity claim names no root. Treating a
missing pointer as “uncollateralised” is a defect.
There is no equally cheap fallback, and none is required. dig_mirror_coin::discover
cannot serve as one: it takes owner_puzzle_hash as a required parameter, because the hint
is morphed from the owner, and a provider record carries no owner. An owner-less scan of the
shared mirror puzzle hash is not merely slower — it is truncated at MAX_CANDIDATES on a
list anyone may extend for the price of a dust coin, so its “not found” is not a negative a
verifier may act on, and it turns one free inbound record into an unbounded outbound read. A
consumer that cannot fetch a pointer therefore withholds credit and leaves the holder’s
ranking unchanged; it does not demote.
A wrong pointer costs the publisher, not the verifier. One chain read, no retry loop: a lookup that misses or fails the bond check yields no credit for that holder. A mismatch is not grounds for blocklisting — it is indistinguishable from an epoch rollover.
Malformed values normalize to None at the wire boundary and again at admission, so on an
ADMITTED or STORED record this is either a canonical lowercase 64-hex string or absent. That
guarantee belongs to those records only: the field is pub, so a value that has not yet
passed either normalization can hold arbitrary attacker-shaped bytes of arbitrary length. A
consumer holding a record from any other source must normalize it itself.
Implementations§
Source§impl ProviderRecord
impl ProviderRecord
Sourcepub fn new(
content_key: &Key,
provider: &PeerId,
addresses: Vec<CandidateAddr>,
expires_at: u64,
) -> Self
pub fn new( content_key: &Key, provider: &PeerId, addresses: Vec<CandidateAddr>, expires_at: u64, ) -> Self
Build a record: peer provider holds content_key, reachable at addresses, until
expires_at (absolute Unix seconds).
Sourcepub fn with_unverified_mirror_coin_id(self, coin_id: [u8; 32]) -> Self
pub fn with_unverified_mirror_coin_id(self, coin_id: [u8; 32]) -> Self
Attach the publisher’s claimed mirror-coin id — see
unverified_mirror_coin_id for why holding it
proves nothing. Stored canonically (lowercase 64-hex) so two records naming the same coin are
byte-identical.
Kept off new deliberately: the pointer is per-CONTENT rather than
per-node, because a mirror coin bonds a (store, root, owner, epoch) tuple, so only the
caller that knows which content it is announcing can supply it.
Sourcepub fn unverified_mirror_coin_id_bytes(&self) -> Option<[u8; 32]>
pub fn unverified_mirror_coin_id_bytes(&self) -> Option<[u8; 32]>
The claimed mirror-coin id as 32 bytes, or None when absent (the normal fallback case).
The bytes are a lookup key, never a fact. Returning Some means a publisher said
something, not that a collateral coin exists.
Sourcepub fn provider_peer_id(&self) -> Option<PeerId>
pub fn provider_peer_id(&self) -> Option<PeerId>
The provider’s peer_id decoded from the 64-hex field, or None if malformed.
Sourcepub fn is_expired(&self, now: u64) -> bool
pub fn is_expired(&self, now: u64) -> bool
Whether this record is expired at now (Unix seconds) — stale records are dropped on read.
Sourcepub fn best_address(&self) -> Option<&CandidateAddr>
pub fn best_address(&self) -> Option<&CandidateAddr>
The FIRST candidate only — the IPv6-preferred, most-direct dialable address, if any.
Prefer dial_candidates for dialing. This returns one address,
so a caller that dials it and stops has made a single attempt and cannot fall back: an
unusable IPv6 candidate then masks a working IPv4 one, violating the IPv4-fallback half
of §5.2 (exactly the #836 read-leg failure). Use this only where a single representative
address is genuinely what is wanted — a log line, a display string, a metric label.
Sourcepub fn dial_candidates(&self) -> Vec<&CandidateAddr>
pub fn dial_candidates(&self) -> Vec<&CandidateAddr>
This provider’s dialable candidates in §5.2 dial order — see dial_candidates for the
ordering contract. Dial these in order, falling through on failure, before concluding the
holder is unreachable.
Trait Implementations§
Source§impl Clone for ProviderRecord
impl Clone for ProviderRecord
Source§fn clone(&self) -> ProviderRecord
fn clone(&self) -> ProviderRecord
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ProviderRecord
impl Debug for ProviderRecord
Source§impl<'de> Deserialize<'de> for ProviderRecord
impl<'de> Deserialize<'de> for ProviderRecord
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for ProviderRecord
Source§impl PartialEq for ProviderRecord
impl PartialEq for ProviderRecord
Source§impl Serialize for ProviderRecord
impl Serialize for ProviderRecord
impl StructuralPartialEq for ProviderRecord
Auto Trait Implementations§
impl Freeze for ProviderRecord
impl RefUnwindSafe for ProviderRecord
impl Send for ProviderRecord
impl Sync for ProviderRecord
impl Unpin for ProviderRecord
impl UnsafeUnpin for ProviderRecord
impl UnwindSafe for ProviderRecord
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> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.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.