diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c
index 0688d7765d..b57f133362 100644
--- a/src/feature/hs/hs_cache.c
+++ b/src/feature/hs/hs_cache.c
@@ -175,7 +175,10 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
* old HS protocol cache subsystem for which we are tied with. */
rend_cache_increment_allocation(cache_get_dir_entry_size(desc));
- /* XXX: Update HS statistics. We should have specific stats for v3. */
+ /* Update HSv3 statistics */
+ if (get_options()->HiddenServiceStatistics) {
+ rep_hist_hsdir_stored_maybe_new_v3_onion(desc->key);
+ }
return 0;
diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c
index daf9db074c..5858f14245 100644
--- a/src/feature/stats/rephist.c
+++ b/src/feature/stats/rephist.c
@@ -1789,9 +1789,8 @@ rep_hist_seen_new_rp_cell(void)
hs_v2_stats->rp_relay_cells_seen++;
}
-/** As HSDirs, we saw another hidden service with public key
- * pubkey. Check whether we have counted it before, if not
- * count it now! */
+/** As HSDirs, we saw another v2 onion with public key pubkey. Check
+ * whether we have counted it before, if not count it now! */
void
rep_hist_hsdir_stored_maybe_new_v2_onion(const crypto_pk_t *pubkey)
{
@@ -1908,6 +1907,31 @@ should_collect_v3_stats(void)
return start_of_hs_v3_stats_interval <= approx_time();
}
+/** We just received a new descriptor with blinded_key. See if we've
+ * seen this blinded key before, and if not add it to the stats. */
+void
+rep_hist_hsdir_stored_maybe_new_v3_onion(const uint8_t *blinded_key)
+{
+ /* Return early if we don't collect HSv3 stats, or if it's not yet the time
+ * to collect them. */
+ if (!hs_v3_stats || !should_collect_v3_stats()) {
+ return;
+ }
+
+ bool seen_before = !!digestmap_get(hs_v3_stats->v3_onions_seen_this_period,
+ (char*)blinded_key);
+
+ log_info(LD_GENERAL, "Considering v3 descriptor with %s (%sseen before)",
+ safe_str(hex_str((char*)blinded_key, 32)),
+ seen_before ? "" : "not ");
+
+ /* Count it if we haven't seen it before. */
+ if (!seen_before) {
+ digestmap_set(hs_v3_stats->v3_onions_seen_this_period,
+ (char*)blinded_key, (void*)(uintptr_t)1);
+ }
+}
+
/* The number of cells that are supposed to be hidden from the adversary
* by adding noise from the Laplace distribution. This value, divided by
* EPSILON, is Laplace parameter b. It must be greather than 0. */
diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h
index e6c1509498..5873594781 100644
--- a/src/feature/stats/rephist.h
+++ b/src/feature/stats/rephist.h
@@ -71,6 +71,7 @@ void rep_hist_hsdir_stored_maybe_new_v2_onion(const crypto_pk_t *pubkey);
time_t rep_hist_hs_v3_stats_write(time_t now);
char *rep_hist_get_hs_v3_stats_string(void);
+void rep_hist_hsdir_stored_maybe_new_v3_onion(const uint8_t *blinded_key);
void rep_hist_free_all(void);