| … | … | @@ -929,3 +929,60 @@ measures the NETWORK; letting it gate our build would be a category error. |
| 929 | 929 | **Why this belongs in a parity audit:** it is the only measurement here that |
| 930 | 930 | could have said "stop looking". Every other slice found something. This one |
| 931 | 931 | found that the thing I was about to chase was the DHT behaving normally. |
| 932 | |
| 933 | ## 15. Slice 11 — ⛔ we do not know which nodes hold our record |
| 934 | |
| 935 | Investigating B's liveness ping (§12.1) turned up its prerequisite, and it is a |
| 936 | defect in its own right. |
| 937 | |
| 938 | `_store_announced_nodes` (`hyperdht.cpp:890`) does NOT record the nodes that |
| 939 | took our announce. It records the closest nodes in OUR OWN ROUTING TABLE, and |
| 940 | it explicitly discards the token: |
| 941 | |
| 942 | // Store the closest nodes from the routing table for later unannounce. |
| 943 | KademliaNode nodes[20]; |
| 944 | int n = _rpc->table().closest(topic, nodes, 20); |
| 945 | ... |
| 946 | memset(at.nodes[i].token, 0, 32); // Token not available here. |
| 947 | |
| 948 | JS records the ACTUAL committing node, from the reply |
| 949 | (`announcer.js:265-267`): |
| 950 | |
| 951 | relays.push({ relayAddress: msg.from, peerAddress: msg.to }) |
| 952 | this._serverRelays[2].set(msg.from.host + ':' + msg.from.port, msg.from) |
| 953 | |
| 954 | Three consequences, and they run through several open items: |
| 955 | |
| 956 | 1. **The liveness ping cannot be built on this.** Pinging routing-table-closest |
| 957 | nodes says nothing about whether the nodes HOLDING the record are alive — |
| 958 | which is the entire question. JS pings `_serverRelays[2]`, the real holders. |
| 959 | 2. **The stored tokens are zeros**, so anything that needs a per-node token from |
| 960 | this table (the unannounce path once A2 is addressed) cannot use it. |
| 961 | 3. **The three-generation cycling (B remainder) has nothing to cycle.** The |
| 962 | generations ARE sets of committing nodes. |
| 963 | |
| 964 | ⛔ **And the information is not currently available where it is needed.** The |
| 965 | per-node commit callback is |
| 966 | |
| 967 | std::function<std::vector<uint8_t>(const uint8_t *target, |
| 968 | const uint8_t *token, |
| 969 | const uint8_t *node_id)> |
| 970 | |
| 971 | — target, token, node id. **No address.** So recording the true holders means |
| 972 | widening that signature in `dht_rpc` first (`dht_rpc.cpp:239`, called at |
| 973 | `:1046`), then recording per announce, and only then can the ping round be |
| 974 | written. |
| 975 | |
| 976 | ### Revised plan for the rest of B |
| 977 | |
| 978 | 1. widen the commit callback to carry the responding node's ADDRESS |
| 979 | 2. record the true holder set (address + token) per announced target |
| 980 | 3. ping round on a 3s schedule with tag->response accounting |
| 981 | 4. re-announce when answered < min(held, MIN_ACTIVE = 3) |
| 982 | |
| 983 | Steps 1-2 are also what A2 needs, and what the generation cycling would cycle. |
| 984 | Doing them first makes three open items cheaper; doing the ping first is not |
| 985 | possible. |
| 986 | |
| 987 | ⚠ NOT STARTED. Recorded rather than begun because it is a four-part change |
| 988 | across two files' public signatures, and half of it landing is worse than none. |