Replies: 2 comments 3 replies
-
|
What is a token budget? It suddenly appears halfway down, without explanation. What problem is this solving? Next-hop is much lower broadcast budget, even if the whole nodeID is included. I can't see how this is stored on a per-destination basis, and even if it were stored in a similar manner to next-hop (which is currently one byte per node in nodeDB), with a hop-limited mesh, the routing to the destination isn't usually the constraint. Edit to add: the favouring of shorter response times only works in the router CW. For the client CW, that should be reversed. |
Beta Was this translation helpful? Give feedback.
-
|
Ah, token buckets was a concept separate from this, but the idea is that clients can act as routers sometimes if the bloom filters show they are the best path. Basically, become a router for very specific packets, but don't make it a habit. (use tokens to keep track of this habit) So something like this could replace CLIENT_BASE if the route back has been determined that the CLIENT is the best path back. There is a lot of work that can be done here. For example, if the bloom filter for receiving a packet is X and bloom filter for the destination is Y, and it's very significantly different, tell client can take the initiative and "please route this packet, because apparently there are only a few paths between the source and destination, and you're it" |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Reverse-Path Bloom Routing (RBF)
A single‑file, human‑sized guide to adding compact, multi‑path reply routing via Bloom filters.
TL;DR
ELI5
Protocol at a glance
request_id.Wire format (TLV)
size_idx→ m bits: 0→64, 1→96, 2→128, 3→192, 4→256k_hint: 0=profile default; else clamp to [3..7]salt16: randomized per request to avoid trivial steering/forgeryHashing and IDs
idx_i = (h1 + i*h2) mod mfrom two 64‑bit bases.Why a salt
(m, salt), which prevents accidentally OR‑ing stale or unrelated flows together.Salt doesn’t anonymize NodeIDs by itself; it just makes membership mapping flow‑local. For stronger metadata protection, combine salted RBF with the anonymous variants described later.
Algorithms (pseudocode)
Request receive (with RBF):
Reply forward:
Two‑tier fallback (optional):
Keeping n small in a flood
n ≈hops on the used path.f_union ≈ 1 − ∏(1 − f_i)andp_union ≈ f_union^k. Union only iff_union ≤ f_max(e.g., 0.75) andp_union ≤ p_cap_union(e.g., 10%).p_insto target an expectedn_target. Ifn_estis frommax_hops × density, setp_ins = clamp(n_target / max(1, n_est), p_min, 1.0).Sizing
What's Actually Important
When you're building a Bloom filter for RBF, here’s what matters to get good reverse-path coverage and keep false-positives low:
False positive probability ("p"):
pis better, but costs more bits.Deciding on filter size ("m") and hash count ("k"):
Quick Rules (Best Practices):
For reliable RBF paths, you want at least 8 bits per hop:
Keep
m/n >= 8(bits per expected participant, e.g. 128 bits for ~16 hops).~2%or better.Recommended hash count:
k ≈ (m/n) * 0.693(0.693 is log2; just set k based on m and estimated n, then clamp to [3,7]).
Shortcut Table
Use at least 8 bits per expected hop (node) on the path if you want p under 2%.
Fast cheat sheet (realistic configs):
Below, left column is total bits, top is expected path length (number of hops or nodes).
Maths...
p ≈ (1 - e^(-k·n/m))^k, but just use the table above or keep m/n ≥ 8.Runtime/On-the-fly estimation
When receiving or building a filter, you can:
f = popcount/m(fill fraction)n_hat ≈ -(m/k) * ln(1 - f)p_hat ≈ f^k(if p_hat > 0.10, filter is probably "full"/useless)Pro tip: If fill fraction f ≥ 0.75 or p_hat > 10%, stop unioning—just use the "best" filter.
"Sweet Spot" Sizing Algorithm (for the origin node)
How to choose m and k when you build an RBF at the origin:
n_est = round(alpha * max_hops * d_eff)m_req = ceil(1.44 * n_est * log2(1/p_target))k = clamp(round((m / n_est) * ln2), 3, 7)unless the user specifically asks for something.About "K-winners" and survivor bias
f) and estimated p (p_hat) might look better than the total flood population.Roles: ROUTER vs CLIENT (and “routerified” clients)
Roles stay. RBF doesn’t cancel physics or battery chemistry.
f ≤ 0.25;p_hat ≤ 2%;max_hops ≤ 7(0‑based, 4‑bit) or at a junction; and token budget available. Use router jitter; keep standard “saw request” and membership gates for replies.Sweeping simplification for want_response traffic
When a packet explicitly requests a reply (
want_response = true), treat CLIENT and ROUTER similarly for the parts that matter:Security and abuse cases
Integration notes (MeshPacketSerializer.cpp and friends)
f=b/m; skip unions that exceedf_maxorp_cap_union.Future ideas and variants
Anonymous reverse path (no return address):
Anonymous destination DMs:
Channel delivery via Bloom sets:
Layered filters (region → node):
Multi‑salt epochs:
Counting/Stable Bloom variants:
If any of these spark joy, we can sketch wire impacts and safety checks next.
Test plan (short)
Diagrams
Sequence (forward then reply):
sequenceDiagram participant S as Source participant A as Node A participant B as Node B participant C as Node C participant D as Destination S->>A: Request + RBF{S} A->>B: Request + RBF{S,A} A->>C: (dup) union if local not in RBF B->>D: Request + RBF{S,A,B,C} D-->>B: Reply + RBF B-->>A: Member + saw request → forward A-->>S: Member + saw request → forwardFlow (request handling):
flowchart TD R[Recv Request] --> S{Seen request_id?} S -- yes --> U{Has RBF?} U -- no --> X[Drop/dup policy] U -- yes --> G{Local in incoming?} G -- no --> O[best_rbf = best_rbf OR incoming] G -- yes --> X S -- no --> H{Has RBF?} H -- no --> F[Forward normally] H -- yes --> M{Member?} M -- no --> I[Insert self] M -- yes --> C[Cache] I --> C C --> FBloom Filters Explained
This is a short, friendly primer for engineers on how Bloom filters differ from regular hash tables, and why “collisions” are a feature, not a bug, when you want tiny, fast membership tests.
Hash Tables: collisions are undesirable
Example (abbreviated, mod 8 table)
Bloom Filters: collisions are intentional (and cheap)
Key differences vs hash tables
A tiny worked example (m = 16 bits, k = 3)
Start with all zeros (bit 0 is the least significant bit of byte 0):
Bits: 0000 0000 0000 0000
Let’s say our k=3 indices come from good 64-bit hashes reduced mod 16.
Insert A (indices: 1, 7, 12)
Insert B (indices: 3, 7, 9)
Insert C (indices: 0, 12, 15)
Now query D (indices: 1, 9, 12)
Table (easier to scan)
Takeaway: collisions let us compress many items into a tiny bitset, at the cost of some “friendly lies” (false positives) but zero false negatives.
When “too many” match: additional nodes enter the mix
Back-of-envelope for m = 16, k = 3
p ≈ (1 − e^(−k·n/m))^k
Why this is useful for reverse-path routing
That’s the magic: hash tables avoid collisions to guarantee precision, while Bloom filters embrace controlled collisions to trade a few “maybe” answers for massive space savings and speed.
Final notes
f_max=0.75,p_cap_union=0.10. Tighten/loosen after field telemetry.Beta Was this translation helpful? Give feedback.
All reactions