From 7661865bcc14bf3c9e74b7da327c50d8d6fcd03d Mon Sep 17 00:00:00 2001 From: Doug Hoyte Date: Wed, 8 Feb 2023 06:48:38 -0500 Subject: [PATCH] NIP-40 expiration timestamp --- fbs/nostr-index.fbs | 1 + golpe.yaml | 7 +++++ src/RelayWebsocket.cpp | 1 - src/cmd_dict.cpp | 1 - src/events.cpp | 18 ++++++++++--- src/gc.h | 2 -- src/global.h | 9 ++++--- src/misc.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++ src/render.h | 44 ------------------------------- 9 files changed, 88 insertions(+), 55 deletions(-) delete mode 100644 src/render.h diff --git a/fbs/nostr-index.fbs b/fbs/nostr-index.fbs index 73680a1..580e6d8 100644 --- a/fbs/nostr-index.fbs +++ b/fbs/nostr-index.fbs @@ -21,6 +21,7 @@ table Event { kind: uint64; tagsGeneral: [TagGeneral]; tagsFixed32: [TagFixed32]; + expiration: uint64; } table Empty {} diff --git a/golpe.yaml b/golpe.yaml index 0637677..2a57d0c 100644 --- a/golpe.yaml +++ b/golpe.yaml @@ -46,6 +46,9 @@ tables: multi: true deletion: # eventId, pubkey multi: true + expiration: + integer: true + multi: true indexPrelude: | auto *flat = v.flat_nested(); @@ -70,6 +73,10 @@ tables: if (flat->kind() == 5 && tagName == 'e') deletion.push_back(std::string(tagVal) + std::string(sv(flat->pubkey()))); } + if (flat->expiration() != 0) { + expiration.push_back(flat->expiration()); + } + CompressionDictionary: fields: - name: dict diff --git a/src/RelayWebsocket.cpp b/src/RelayWebsocket.cpp index 6503b14..c0912b4 100644 --- a/src/RelayWebsocket.cpp +++ b/src/RelayWebsocket.cpp @@ -1,5 +1,4 @@ #include "RelayServer.h" -#include "render.h" #include "app_git_version.h" diff --git a/src/cmd_dict.cpp b/src/cmd_dict.cpp index 79973b7..d5120e9 100644 --- a/src/cmd_dict.cpp +++ b/src/cmd_dict.cpp @@ -9,7 +9,6 @@ #include "DBScan.h" #include "events.h" -#include "render.h" static const char USAGE[] = diff --git a/src/events.cpp b/src/events.cpp index 78455f2..49c058b 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -19,13 +19,14 @@ std::string nostrJsonToFlat(const tao::json::value &v) { std::vector> tagsGeneral; std::vector> tagsFixed32; + uint64_t expiration = 0; + if (v.at("tags").get_array().size() > cfg().events__maxNumTags) throw herr("too many tags: ", v.at("tags").get_array().size()); for (auto &tagArr : v.at("tags").get_array()) { auto &tag = tagArr.get_array(); if (tag.size() < 2) throw herr("too few fields in tag"); auto tagName = tag.at(0).get_string(); - if (tagName.size() != 1) continue; // only single-char tags need indexing auto tagVal = tag.at(1).get_string(); @@ -37,8 +38,14 @@ std::string nostrJsonToFlat(const tao::json::value &v) { (uint8_t)tagName[0], (NostrIndex::Fixed32Bytes*)tagVal.data() )); - } else { - if (tagVal.size() < 1 || tagVal.size() > cfg().events__maxTagValSize) throw herr("tag val too small/large: ", tagVal.size()); + } else if (tagName == "expiration") { + if (expiration == 0) { + expiration = parseUint64(tagVal); + if (expiration == 0) expiration = 1; // special value to indicate expiration of 0 was set + } + } else if (tagName.size() == 1) { + if (tagVal.size() == 0) throw herr("tag val empty"); + if (tagVal.size() > cfg().events__maxTagValSize) throw herr("tag val too large: ", tagVal.size()); if (tagVal.size() <= MAX_INDEXED_TAG_VAL_SIZE) { tagsGeneral.emplace_back(NostrIndex::CreateTagGeneral(builder, @@ -57,7 +64,8 @@ std::string nostrJsonToFlat(const tao::json::value &v) { created_at, kind, builder.CreateVector>(tagsGeneral), - builder.CreateVector>(tagsFixed32) + builder.CreateVector>(tagsFixed32), + expiration ); builder.Finish(eventPtr); @@ -122,6 +130,8 @@ void verifyEventTimestamp(const NostrIndex::Event *flat) { if (ts < earliest) throw herr("created_at too early"); if (ts > latest || ts > MAX_TIMESTAMP) throw herr("created_at too late"); + + if (flat->expiration() != 0 && flat->expiration() <= now) throw herr("event expired"); } void parseAndVerifyEvent(const tao::json::value &origJson, secp256k1_context *secpCtx, bool verifyMsg, bool verifyTime, std::string &flatStr, std::string &jsonStr) { diff --git a/src/gc.h b/src/gc.h index 6bfab0c..90da437 100644 --- a/src/gc.h +++ b/src/gc.h @@ -2,8 +2,6 @@ #include "golpe.h" -#include "render.h" - inline void quadrableGarbageCollect(quadrable::Quadrable &qdb, int logLevel = 0) { quadrable::Quadrable::GarbageCollector> gc(qdb); diff --git a/src/global.h b/src/global.h index ce7a282..aeb23e9 100644 --- a/src/global.h +++ b/src/global.h @@ -12,7 +12,10 @@ quadrable::Quadrable getQdbInstance(lmdb::txn &txn); quadrable::Quadrable getQdbInstance(); -std::string renderIP(std::string_view ipBytes); - - #include "constants.h" + + +std::string renderIP(std::string_view ipBytes); +std::string renderSize(uint64_t si); +std::string renderPercent(double p); +uint64_t parseUint64(const std::string &s); diff --git a/src/misc.cpp b/src/misc.cpp index 90267a8..4cfbfeb 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -1,7 +1,12 @@ #include +#include + +#include +#include #include "golpe.h" + std::string renderIP(std::string_view ipBytes) { char buf[128]; @@ -15,3 +20,58 @@ std::string renderIP(std::string_view ipBytes) { return std::string(buf); } + + +std::string renderSize(uint64_t si) { + if (si < 1024) return std::to_string(si) + "b"; + + double s = si; + char buf[128]; + char unit; + + do { + s /= 1024; + if (s < 1024) { + unit = 'K'; + break; + } + + s /= 1024; + if (s < 1024) { + unit = 'M'; + break; + } + + s /= 1024; + if (s < 1024) { + unit = 'G'; + break; + } + + s /= 1024; + unit = 'T'; + } while(0); + + ::snprintf(buf, sizeof(buf), "%.2f%c", s, unit); + return std::string(buf); +} + + + +std::string renderPercent(double p) { + char buf[128]; + ::snprintf(buf, sizeof(buf), "%.1f%%", p * 100); + return std::string(buf); +} + + + +uint64_t parseUint64(const std::string &s) { + auto digitChar = [](char c){ + return c >= '0' && c <= '9'; + }; + + if (!std::all_of(s.begin(), s.end(), digitChar)) throw herr("non-digit character"); + + return std::stoull(s); +} diff --git a/src/render.h b/src/render.h deleted file mode 100644 index 2acfb6b..0000000 --- a/src/render.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include - - -inline std::string renderSize(uint64_t si) { - if (si < 1024) return std::to_string(si) + "b"; - - double s = si; - char buf[128]; - char unit; - - do { - s /= 1024; - if (s < 1024) { - unit = 'K'; - break; - } - - s /= 1024; - if (s < 1024) { - unit = 'M'; - break; - } - - s /= 1024; - if (s < 1024) { - unit = 'G'; - break; - } - - s /= 1024; - unit = 'T'; - } while(0); - - ::snprintf(buf, sizeof(buf), "%.2f%c", s, unit); - return std::string(buf); -} - -inline std::string renderPercent(double p) { - char buf[128]; - ::snprintf(buf, sizeof(buf), "%.1f%%", p * 100); - return std::string(buf); -}