Files
pyxis/lib/tdeck_ui/UI/LXMF/UnknownSourceKeyRequest.h
T
Torlando 2eec34dfe1 test: extract unknown-source key-request policy + host tests
Move the rate-limit/cooldown/cap decision out of the UIManager.cpp
anonymous namespace into a pure, header-only policy
(UI/LXMF/UnknownSourceKeyRequest.h) so it is host-testable without the
ESP/microReticulum stack. UIManager keeps only the side effect
(Transport::request_path).

Adds tests/native/test_unknown_source_key_request.{cpp,py} (24 checks,
ASan+UBSan): new-source request, 5-min cooldown boundary, re-record
resets the window, per-source independence, 64-entry cap with oldest
eviction, and steady-state cost.
2026-09-02 16:22:56 +00:00

70 lines
2.6 KiB
C++

#pragma once
// ---------------------------------------------------------------------------
// On-demand identity acquisition policy for unknown LXMF sources.
//
// When an LXMF message is delivered to us from a source whose RNS identity
// has not been learned, the router accepts the message (microLXMF treats
// SOURCE_UNKNOWN as "will validate later if the identity is learned via
// announce") but location ingest is skipped for unauthenticated senders.
// Nothing ever triggered that learning, so an unknown peer stayed unknown
// until — and unless — a natural announce happened to arrive.
//
// This policy mirrors Sideband's "Query Network For Keys" button
// (RNS.Transport.request_path): the caller fires a broadcast RNS path
// request for the source hash, and any peer that already holds the
// source's cached announce (a phone on the same link, a hub, another node)
// answers with it. The next message from that peer then validates.
//
// The decision is pure and host-testable; the Transport::request_path call
// stays in the UI layer (see UIManager::on_message_received).
//
// Only SOURCE_UNKNOWN should ever be passed here: an invalid signature
// from a KNOWN identity is not recoverable by asking the network for the
// same key it already has.
// ---------------------------------------------------------------------------
#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
namespace UI {
namespace LXMF {
struct UnknownSourceKeyRequestPolicy {
static constexpr unsigned long long kCooldownMillis = 5U * 60U * 1000U;
static constexpr unsigned kMaxTrackedSources = 64U;
std::vector<std::pair<std::string, unsigned long long>> last_requested;
bool should_request(const std::string& source_hex,
unsigned long long now_millis) const {
for (const auto& entry : last_requested) {
if (entry.first == source_hex) {
return now_millis >=
entry.second + kCooldownMillis;
}
}
return true;
}
void record_request(const std::string& source_hex,
unsigned long long now_millis) {
for (auto& entry : last_requested) {
if (entry.first == source_hex) {
entry.second = now_millis;
return;
}
}
if (last_requested.size() >= kMaxTrackedSources) {
last_requested.erase(last_requested.begin());
}
last_requested.emplace_back(source_hex, now_millis);
}
};
} // namespace LXMF
} // namespace UI