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
28 changes: 14 additions & 14 deletions Cargo.lock

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

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ edition = "2021"
# the ROOT manifest (`[workspace.package].version`), so it MUST be set here for a
# release to fire (§3.6). The library crates (dig-node-core/dig-runtime/dig-wallet)
# keep their own independent versions — only the released binary tracks the workspace version.
version = "0.68.0"
version = "0.68.1"

# Release hardening, matching digstore: keep integer-overflow checks ON in release.
# The node parses untrusted serialized input and does offset/length arithmetic over
Expand All @@ -54,8 +54,8 @@ overflow-checks = true
# standalone. `native-tls` is NOT patched: dig-gossip is pulled with
# `default-features = false, features = ["rustls", "relay"]`, so the OpenSSL/native-tls path is off.
[patch.crates-io]
chia-protocol = { git = "https://github.com/DIG-Network/dig-gossip", rev = "3e352d91fbda830e517c914cf4f6afa6a9a7e121" }
chia-sdk-client = { git = "https://github.com/DIG-Network/dig-gossip", rev = "3e352d91fbda830e517c914cf4f6afa6a9a7e121" }
chia-protocol = { git = "https://github.com/DIG-Network/dig-gossip", rev = "dd29ce8df275d60b3568079f3739bbe55f7bbf17" }
chia-sdk-client = { git = "https://github.com/DIG-Network/dig-gossip", rev = "dd29ce8df275d60b3568079f3739bbe55f7bbf17" }

# The dig-nat unification patch is RETIRED (#1280 crates.io cascade): dig-nat 0.7 is on crates.io and
# the ENTIRE peer stack (dig-gossip 0.7.1, dig-dht 0.2.2, dig-download 0.2.1, dig-peer-selector 0.2.1,
Expand Down
8 changes: 8 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -3247,6 +3247,14 @@ connects to, with a restart re-entering the same state. Re-stating the whole inv
single chokepoint. dig-dht is crypto-free by design, and `ingest_verified_provider` is the sole sanctioned
bypass of its mTLS self-announce check, so these are the whole of the authentication:

0. **DECODE refuses a frame whose declared counts exceed the protocol maxima, BEFORE reserving for them**
(#1723). The wire states its batch size and each `Add`'s address count as `u16`, so both are the
sender's to choose; decode MUST check them against `HOLDINGS_MAX_CHANGES` (256) and
`MAX_ADDRS_PER_CHANGE` (32) before any allocation is sized from them. This gate is stated separately
from gate 1 because it necessarily runs BEFORE it: decode precedes the signature check, so an
allocation sized here is one an UNAUTHENTICATED peer commissioned. A ~200-byte frame declaring
65,535 addresses would otherwise reserve ~2 MiB, and no later gate can refund it. The general rule
this instantiates: **never size an allocation from a number a peer supplied.**
1. `verify_holdings_announce` passes (batch cap, `SHA-256(provider_spki) == provider_peer_id`, P-256 SPKI,
valid signature over the `dig:holdings:v1` domain-separated message).
2. `provider_peer_id` is CANONICALIZED (decoded to 32 bytes, re-encoded lowercase) and every subsequent
Expand Down
4 changes: 2 additions & 2 deletions crates/dig-node-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ name = "dig-node-core"
# compatibility-relevant change even though the crate is consumed in-workspace and is not published to
# crates.io. Holding it at 0.23.0 would have overstated stability to anyone reading the manifest for
# what the exported types are (dig_ecosystem#1785c).
version = "0.25.0"
version = "0.25.1"
edition = "2021"
license = "GPL-2.0-only"
description = "The canonical DIG node ENGINE library (crate `dig_node_core`): the JSON-RPC dispatch (`handle_rpc`, the same contract as rpc.dig.net), local-first content serve/fetch/redirect from LOCAL .dig store modules (via digstore_host::serve_blind), chain-anchored-root resolution, chain-watch + subscriptions + generation gap-fill, the LRU cache, and the full P2P stack. Shared UNCHANGED by both host shells: the `dig-node` OS-service binary (dig-node-service) and the DIG Browser's in-process cdylib (dig-runtime). Native Rust so the compiled-module serve path works."
Expand Down Expand Up @@ -177,7 +177,7 @@ dig-nat = "0.14"
# graph still unifies to ONE of each. The vendored chia-protocol/chia-sdk-client forks it path-patches
# are re-declared at this SAME rev in the workspace `[patch.crates-io]` (a git dep's own patches do not
# apply transitively — the #1529 3-rev lockstep: this dep rev + the two patch revs move together).
dig-gossip = { git = "https://github.com/DIG-Network/dig-gossip", rev = "3e352d91fbda830e517c914cf4f6afa6a9a7e121", default-features = false, features = ["rustls", "relay"] }
dig-gossip = { git = "https://github.com/DIG-Network/dig-gossip", rev = "dd29ce8df275d60b3568079f3739bbe55f7bbf17", default-features = false, features = ["rustls", "relay"] }
# The canonical DIG network id (genesis challenge) for the gossip handshake / introducer scope. From
# crates.io: dig-nat 0.7 requires `>=0.4, <0.6` and dig-gossip 0.7.1 requires `>=0.2, <0.5`, so `0.4`
# sits in both windows and the whole graph resolves to ONE crates.io dig-constants 0.4.x.
Expand Down
32 changes: 26 additions & 6 deletions crates/dig-node-core/src/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,11 +338,22 @@ pub(crate) fn pool_event_to_selector(peer_id: [u8; 32], event: PoolEventKind) ->
}
}

/// The 1:1 field map of `dig_gossip::PoolRemovalReason` → the selector's local [`PoolRemovalReason`]
/// (identical variants; `Banned` makes the peer ineligible until re-added, SPEC §9.4).
/// Maps `dig_gossip::PoolRemovalReason` → the selector's local [`PoolRemovalReason`]
/// (`Banned` makes the peer ineligible until re-added, SPEC §9.4).
///
/// `Reaped` has no selector counterpart, so it must fold into one of the three the selector knows.
/// It folds to `Disconnected` rather than `Dead` because a reaped peer's transport was *provably
/// closed* — a departure, which is what `Disconnected` names — whereas `Dead` names a keepalive
/// finding a peer unresponsive. What makes the choice safe rather than merely tidy: the selector
/// distinguishes only `Banned` behaviourally (`engine.rs` matches on it alone to mark a peer
/// ineligible) and treats `Disconnected`/`Dead` identically, so this fold is observability-only and
/// cannot change eligibility. Folding it to `Banned` would be the real error — it would make an
/// honestly-departed peer ineligible and bias the node toward unremembered peers, which is a sybil.
pub(crate) fn pool_removal_reason(reason: GossipRemovalReason) -> PoolRemovalReason {
match reason {
GossipRemovalReason::Disconnected => PoolRemovalReason::Disconnected,
GossipRemovalReason::Disconnected | GossipRemovalReason::Reaped => {
PoolRemovalReason::Disconnected
}
GossipRemovalReason::Dead => PoolRemovalReason::Dead,
GossipRemovalReason::Banned => PoolRemovalReason::Banned,
}
Expand Down Expand Up @@ -415,6 +426,9 @@ pub(crate) enum GossipRemovalReason {
Dead,
/// Banned for misbehaviour.
Banned,
/// Swept up by dig-gossip's departed-peer reaper: the transport was provably closed, but the
/// slot is keepalive-less so nothing else observed the departure.
Reaped,
}

// -- Selector-driven DIAL ordering (#384) ------------------------------------------------------------
Expand Down Expand Up @@ -2649,10 +2663,12 @@ pub(crate) mod tests {

// -- the peer selector (#178): the discovery → select → download → record_outcome loop ----------

/// The gossip → selector `PoolEvent` map is a byte-identical 1:1 (SPEC §5.4): the peer id is the
/// same 32 bytes and the removal reasons map variant-for-variant.
/// The gossip → selector `PoolEvent` map preserves identity and removal semantics (SPEC §5.4):
/// the peer id is the same 32 bytes, the three reasons the selector shares map
/// variant-for-variant, and `Reaped` — which the selector has no variant for — folds to the
/// non-punitive `Disconnected`.
#[test]
fn pool_event_map_is_1_to_1() {
fn pool_event_map_preserves_identity_and_removal_semantics() {
let addr: std::net::SocketAddr = "203.0.113.7:9444".parse().unwrap();
let added = pool_event_to_selector([9u8; 32], PoolEventKind::Added { addr });
assert_eq!(
Expand All @@ -2669,6 +2685,10 @@ pub(crate) mod tests {
),
(GossipRemovalReason::Dead, PoolRemovalReason::Dead),
(GossipRemovalReason::Banned, PoolRemovalReason::Banned),
// `Reaped` has no selector counterpart and folds to `Disconnected`. Pinned here so the
// fold is a decision the suite states, not an accident a later edit can quietly change
// into `Banned` — which would make an honestly-departed peer ineligible.
(GossipRemovalReason::Reaped, PoolRemovalReason::Disconnected),
] {
assert_eq!(pool_removal_reason(g), s);
assert_eq!(
Expand Down
3 changes: 3 additions & 0 deletions crates/dig-node-core/src/peer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1929,6 +1929,9 @@ pub(crate) fn map_gossip_pool_event(ev: &dig_gossip::PoolEvent) -> dig_peer_sele
dig_gossip::PoolRemovalReason::Banned => {
crate::download::GossipRemovalReason::Banned
}
dig_gossip::PoolRemovalReason::Reaped => {
crate::download::GossipRemovalReason::Reaped
}
};
crate::download::pool_event_to_selector(
bytes,
Expand Down
Loading
Loading