Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ src/apps/relay/RelayWebsocket.o: build/StrfryTemplates.h
test-subid: build/subid_tests
build/subid_tests

build/subid_tests: test/SubIdTests.cpp build/golpe.h
build/subid_tests: test/tests/SubIdTests.cpp build/golpe.h
$(CXX) $(CXXFLAGS) $(INCS) $< -o $@
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ The query engine is the most complicated part of the relay, so there is a differ

To bootstrap the tests, we load in a set of [real-world nostr events](https://wiki.wellorder.net/wiki/nostr-datasets/).

There is a simple but inefficient filter implementation in `test/dumbFilter.pl` that can be used to check if an event matches a filter. In a loop, we randomly generate a complicated filter group and pipe the entire DB's worth of events through the dumb filter and record which events it matched. Next, we perform the query using strfry's query engine (using a `strfry scan`) and ensure it matches. This gives us confidence that querying for "old" records in the DB will be performed correctly.
There is a simple but inefficient filter implementation in `test/utils/dumbFilter.pl` that can be used to check if an event matches a filter. In a loop, we randomly generate a complicated filter group and pipe the entire DB's worth of events through the dumb filter and record which events it matched. Next, we perform the query using strfry's query engine (using a `strfry scan`) and ensure it matches. This gives us confidence that querying for "old" records in the DB will be performed correctly.

Next, we need to verify that monitoring for "new" records will function also. For this, in a loop we create a set of hundreds of random filters and install them in the monitoring engine. One of which is selected as a sample. The entire DB's worth of events is "posted to the relay" (actually just iterated over in the DB using `strfry monitor`), and we record which events were matched. This is then compared against a full-DB scan using the same query.

Expand Down
23 changes: 23 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "strfry",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": "^11.22.0",
"onFail": "download"
}
},
"type": "module",
"dependencies": {
"@nostr/tools": "jsr:^2.24.2"
}
}
276 changes: 276 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 18 additions & 13 deletions src/ReadRestrictor.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,26 @@ struct ReadRestrictor {

static bool isFilterAllowedToCount(const NostrFilterGroup &fg, Bytes32 pubkey) {
if (restrictedKinds().empty()) return true;

bool pubkeyIsNull = pubkey.isNull();

for (const auto &f: fg.filters) {
if (!f.kinds) continue;
bool hasSomeRestrictedKind = false;
for(size_t i = 0; i<f.kinds->size(); ++i) {
for (size_t i = 0; i < f.kinds->size(); ++i) {
uint64_t kind = f.kinds->at(i);
if (restrictedKinds().contains(kind)) {
hasSomeRestrictedKind = true;
break;
}
}
if (hasSomeRestrictedKind) {
if (pubkey.isNull()) {
if (pubkeyIsNull) {
return false;
}
if (!cfg().relay__auth__restrictReadToInvolvedPubkey) {
continue;
}
bool authorScoped = f.authors && allPubkeysMatch(*f.authors, pubkey);
bool pScoped = false;
if (auto it = f.tags.find('p'); it != f.tags.end()) {
Expand All @@ -86,30 +92,29 @@ struct ReadRestrictor {

// Returns true if the event should be sent to the subscriber
static bool shouldSendToSubscriber(const PackedEventView &packed, const Bytes32 &subscriberAuthedPubkey) {
if (!(restrictedKinds().contains(packed.kind()) && cfg().relay__auth__restrictReadToInvolvedPubkey)) {
if (!restrictedKinds().contains(packed.kind())) {
return true;
}

if (subscriberAuthedPubkey.isNull()) {
return false;
}

Bytes32 recipientPubkey;
bool foundRecipient = false;
if(!cfg().relay__auth__restrictReadToInvolvedPubkey) return true;

bool involved = subscriberAuthedPubkey == packed.pubkey();

packed.foreachTag([&](char tagName, std::string_view tagVal) {
if (tagName == 'p' && tagVal.size() == 32) {
recipientPubkey = Bytes32(tagVal);
foundRecipient = true;
return false;
if (subscriberAuthedPubkey == Bytes32(tagVal)) {
involved = true;
return false;
}
}

return true;
});

if (!foundRecipient) {
return false;
}

return subscriberAuthedPubkey == recipientPubkey || subscriberAuthedPubkey == packed.pubkey();
return involved;
}
};
Loading
Loading