mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-22 07:48:26 +00:00
Restrict bloom filter propagation to tree edges, update design docs
Gate peer_inbound_filters() to only collect from tree peers (parent and children), so outgoing filter computation merges only tree-sourced information. All peers still receive FilterAnnounce messages and store filters locally for routing queries — the restriction is only on what gets merged into outgoing filters. This prevents bloom filter saturation where mesh shortcuts cause every node's filter to converge toward the full network. With tree-only merge, filters contain subtree (from children) + complement (from parent) + single-hop mesh views. Implementation: - Add is_tree_peer() helper to determine tree parent/child relationship - Gate peer_inbound_filters() to tree peers only (single control point) - Trigger bloom filter exchange on tree relationship changes - Add est_entries, set_bits, fill ratio, and tree_peer fields to FilterAnnounce send/receive debug logs - Add test_bloom_filter_split_horizon test verifying directional asymmetry: upward filters contain only the child's subtree, downward filters contain only the complement - Add print_filter_cardinality diagnostic helper for test inspection Design docs: - fips-bloom-filters.md: Add directional asymmetry and mesh peer filter subsections, update per-peer filter model, saturation mitigation, implementation status table - fips-mesh-operation.md: Update filter propagation description, add directional asymmetry, tree relationship change trigger - fips-intro.md: Rewrite bloom propagation paragraph for tree-only merge
This commit is contained in:
@@ -64,8 +64,14 @@ distance calculation makes the actual forwarding decision.
|
||||
|
||||
A bloom filter with 8,192 bits and k = 5 saturates (FPR approaches 100%)
|
||||
around 3,000–4,000 entries. Beyond this, every query returns "maybe" and the
|
||||
filter provides no candidate selection value. Hub nodes approaching this
|
||||
threshold effectively fall back to tree-distance-only routing.
|
||||
filter provides no candidate selection value.
|
||||
|
||||
Tree-only merge propagation mitigates saturation by limiting each filter's
|
||||
content to tree-relevant entries. A node's outgoing filter to its parent
|
||||
contains only its subtree; its outgoing filter to a child contains the
|
||||
complement. Neither filter contains entries from mesh shortcuts' transitive
|
||||
information, keeping filter occupancy proportional to the node's position
|
||||
in the tree rather than the total mesh connectivity.
|
||||
|
||||
## Per-Peer Filter Model
|
||||
|
||||
@@ -79,7 +85,14 @@ The outbound filter for peer Q is computed by merging:
|
||||
|
||||
1. **This node's own identity** (node_addr)
|
||||
2. **Leaf-only dependents** (if any — future direction)
|
||||
3. **All other peers' inbound filters except Q's** (split-horizon exclusion)
|
||||
3. **Tree peers' inbound filters except Q's** (tree-only merge with
|
||||
split-horizon exclusion)
|
||||
|
||||
Only filters from **tree peers** (parent and children in the spanning tree)
|
||||
are merged into outgoing filter computation. Filters from non-tree mesh
|
||||
peers are stored locally for routing queries but are not propagated
|
||||
transitively. This prevents bloom filter saturation where mesh shortcuts
|
||||
cause every node's filter to converge toward the full network.
|
||||
|
||||
### Split-Horizon Exclusion
|
||||
|
||||
@@ -89,13 +102,38 @@ creating a routing loop where Q thinks it can reach a destination through
|
||||
this node, and this node thinks it can reach the same destination through Q.
|
||||
|
||||
Split-horizon is computed per-peer: the outbound filter for peer Q merges
|
||||
all inbound filters except Q's. For a node with peers A, B, C:
|
||||
all tree peer inbound filters except Q's.
|
||||
|
||||
### Directional Asymmetry
|
||||
|
||||
Because merge is restricted to tree peers, outgoing filters exhibit
|
||||
directional asymmetry along tree edges:
|
||||
|
||||
- **Upward (child → parent)**: Contains the child's subtree — the child's
|
||||
own identity plus all entries merged from its children's filters
|
||||
- **Downward (parent → child)**: Contains the complement — the parent's
|
||||
own identity plus entries from all other tree peers (siblings' subtrees
|
||||
and the parent's own parent direction)
|
||||
|
||||
Together, the upward and downward filters for a tree edge cover the entire
|
||||
network with no overlap (excluding the node itself at the split point).
|
||||
|
||||
### Mesh Peer Filters
|
||||
|
||||
All peers — including non-tree mesh shortcuts — still **receive**
|
||||
FilterAnnounce messages and **store** received filters locally. These
|
||||
stored filters are consulted during routing (step 3 of `find_next_hop()`)
|
||||
for single-hop shortcut discovery. However, mesh peer filters contain
|
||||
only the mesh peer's own tree-propagated information, not transitive
|
||||
entries from the broader network.
|
||||
|
||||
For a node with tree peers A, B and mesh peer C:
|
||||
|
||||
| Outbound to | Includes entries from |
|
||||
| ----------- | -------------------- |
|
||||
| A | Self + B's filter + C's filter |
|
||||
| B | Self + A's filter + C's filter |
|
||||
| C | Self + A's filter + B's filter |
|
||||
| A | Self + B's filter (tree-only merge, excluding A) |
|
||||
| B | Self + A's filter (tree-only merge, excluding B) |
|
||||
| C | Self + A's filter + B's filter (tree-only merge, C excluded as non-tree) |
|
||||
|
||||
## Filter Propagation
|
||||
|
||||
@@ -121,10 +159,16 @@ cooldown period are coalesced into a single announcement.
|
||||
|
||||
### Propagation Scope
|
||||
|
||||
Filters propagate unboundedly — there is no TTL on filter propagation. At
|
||||
steady state, every reachable destination appears in at least one peer's
|
||||
filter. New nodes added to the network propagate through the entire mesh
|
||||
within O(D × 500ms) where D is the network diameter.
|
||||
Filters propagate transitively through tree edges only. Since the spanning
|
||||
tree is a connected subgraph covering all nodes, every reachable
|
||||
destination still appears in at least one tree peer's filter at steady
|
||||
state. New nodes propagate through the tree within O(depth × 500ms) where
|
||||
depth is the tree depth.
|
||||
|
||||
Mesh shortcuts provide single-hop filter visibility (the mesh peer's own
|
||||
filter) but do not contribute to transitive propagation. This bounds the
|
||||
information in each filter to tree-relevant entries rather than the full
|
||||
network.
|
||||
|
||||
## Filter Expiration
|
||||
|
||||
@@ -232,10 +276,13 @@ negotiation not present in v1.
|
||||
| 1 KB bloom filter (size_class 1) | **Implemented** |
|
||||
| 5 hash functions | **Implemented** |
|
||||
| Split-horizon filter computation | **Implemented** |
|
||||
| Tree-only merge propagation | **Implemented** |
|
||||
| Directional asymmetry (subtree/complement) | **Implemented** |
|
||||
| Per-peer filter maintenance | **Implemented** |
|
||||
| Event-driven updates | **Implemented** |
|
||||
| 500ms rate limiting | **Implemented** |
|
||||
| FilterAnnounce gossip | **Implemented** |
|
||||
| FilterAnnounce gossip (all peers) | **Implemented** |
|
||||
| Filter cardinality logging | **Implemented** |
|
||||
| Size class negotiation | Future direction |
|
||||
| Folding support | Future direction |
|
||||
| Adaptive filter sizing | Future direction |
|
||||
|
||||
@@ -350,22 +350,27 @@ routing — bloom filters narrow the set of peers worth considering, and the
|
||||
actual forwarding decision ranks those candidates by tree distance and link
|
||||
quality.
|
||||
|
||||
Filters propagate via gossip, with each node computing outbound filters by
|
||||
merging the filters received from its other peers (a
|
||||
Filters propagate transitively through tree edges, with each node computing
|
||||
outbound filters by merging the filters received from its tree peers (parent
|
||||
and children) using a
|
||||
[split-horizon](https://en.wikipedia.org/wiki/Split_horizon_route_advertisement)
|
||||
technique borrowed from distance-vector routing). At steady state, filters
|
||||
represent the entire reachable network.
|
||||
technique borrowed from distance-vector routing. All peers — including
|
||||
non-tree mesh shortcuts — receive FilterAnnounce messages, but only tree
|
||||
peers' filters are merged into outgoing computation. This prevents filter
|
||||
saturation where mesh shortcuts would cause every filter to converge toward
|
||||
the full network.
|
||||
|
||||
See [fips-bloom-filters.md](fips-bloom-filters.md) for filter parameters and
|
||||
mathematical properties.
|
||||
|
||||

|
||||
|
||||
Every filter is computed identically regardless of link type: the outbound
|
||||
filter for peer Q merges this node's identity with all inbound filters
|
||||
except Q's (split-horizon exclusion). The protocol makes no distinction
|
||||
between tree parents, tree children, or mesh peers — the asymmetry visible
|
||||
in the diagram is a consequence of tree topology, not a different operation.
|
||||
The outbound filter for peer Q merges this node's identity with tree peer
|
||||
inbound filters except Q's (split-horizon exclusion). This creates
|
||||
directional asymmetry: upward filters (child → parent) contain the child's
|
||||
subtree, while downward filters (parent → child) contain the complement.
|
||||
Mesh peers receive filters but their inbound filters are not merged
|
||||
transitively — they provide single-hop shortcut visibility only.
|
||||
|
||||
A node with multiple peers receives genuinely different filters from each.
|
||||
In the diagram, R receives {B, D, E} from B and {C, F} from C — two disjoint
|
||||
|
||||
@@ -100,25 +100,33 @@ decision uses tree coordinate distance to rank those candidates.
|
||||
|
||||
### How Filters Propagate
|
||||
|
||||
Nodes exchange **FilterAnnounce** messages with direct peers. Each
|
||||
Nodes exchange **FilterAnnounce** messages with all direct peers. Each
|
||||
FilterAnnounce replaces the previous filter for that peer — there is no
|
||||
incremental update.
|
||||
|
||||
Filter computation uses **split-horizon exclusion**: the outbound filter
|
||||
for peer Q is computed by merging the local node's own identity, its
|
||||
leaf-only dependents (if any), and the filters received from all other
|
||||
peers *except* Q. This prevents echo loops where a node advertises back
|
||||
to Q the destinations it learned from Q.
|
||||
Filter computation uses **tree-only merge with split-horizon exclusion**:
|
||||
the outbound filter for peer Q is computed by merging the local node's own
|
||||
identity, its leaf-only dependents (if any), and the inbound filters from
|
||||
tree peers (parent and children) *except* Q. Filters from non-tree mesh
|
||||
peers are stored locally for routing queries but are not merged into
|
||||
outgoing filters. This prevents saturation where mesh shortcuts cause
|
||||
filters to converge toward the full network.
|
||||
|
||||
Filters propagate unboundedly (no TTL). At steady state, every reachable
|
||||
destination appears in at least one peer's filter.
|
||||
The restriction creates **directional asymmetry**: upward filters
|
||||
(child → parent) contain the child's subtree, while downward filters
|
||||
(parent → child) contain the complement. Together they cover the entire
|
||||
network.
|
||||
|
||||
Filters propagate transitively through tree edges. At steady state, every
|
||||
reachable destination appears in at least one tree peer's filter.
|
||||
|
||||
### Update Triggers
|
||||
|
||||
Filter updates are event-driven, not periodic:
|
||||
|
||||
- Peer connects or disconnects
|
||||
- A peer's incoming filter changes
|
||||
- A peer's incoming filter changes (triggers recomputation for other peers)
|
||||
- Tree relationship changes (new parent, new child, parent switch)
|
||||
- Local state changes (new identity, leaf-only dependent changes)
|
||||
|
||||
Updates are rate-limited at 500ms to prevent storms during topology changes.
|
||||
@@ -509,8 +517,8 @@ When traffic to a destination stops:
|
||||
|
||||
1. **Session idles out** (90s) — session torn down
|
||||
2. **Coordinate caches expire** (300s) — transit nodes forget coordinates
|
||||
3. **Bloom filters remain** — they have no TTL, so reachability information
|
||||
persists
|
||||
3. **Bloom filters remain** — they have no TTL, so tree-propagated
|
||||
reachability information persists
|
||||
|
||||
When traffic resumes:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user