-
Notifications
You must be signed in to change notification settings - Fork 10
support nip_22 #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
9fcb5eb
88cab83
1716711
ea24ddd
8ece436
473c19b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| import time | ||
|
|
||
| from pyrelay.nostr.event import EventKind, NostrEvent | ||
| from pyrelay.nostr.msgs import NostrCommandResults | ||
| from pyrelay.relay.client_session import ClientSession | ||
|
|
@@ -31,6 +33,11 @@ async def _save_event(repo: EventsRepository, event: NostrEvent) -> NostrCommand | |
| if nips_config.nip_9 and event.kind == EventKind.EventDeletion: # type: ignore | ||
| await _handle_delete_event(repo, event) | ||
|
|
||
| if not is_creation_time_valid(event.created_at, time.time(), nips_config.nip_22): | ||
| return NostrCommandResults( | ||
| event_id=event.id, saved=False, message="invalid: timestamp is not in required time frame" | ||
| ) | ||
|
|
||
| try: | ||
| # todo: add duplicate validation | ||
| await repo.add(event) | ||
|
|
@@ -45,3 +52,10 @@ async def _save_event(repo: EventsRepository, event: NostrEvent) -> NostrCommand | |
| async def _handle_delete_event(repo: EventsRepository, event: NostrEvent): | ||
| keys_to_delete = event.e_tags | ||
| await repo.delete(keys_to_delete) | ||
|
|
||
|
|
||
| def is_creation_time_valid(event_creation_time: int, current_time: float, nip_22_config: tuple[int, int]) -> bool: | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add a docstring? |
||
| if not nip_22_config: | ||
| return True | ||
| creation_time_offset = event_creation_time - current_time | ||
| return int(creation_time_offset) in range(*nip_22_config) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import time | ||
| import uuid | ||
| from collections import defaultdict | ||
|
|
||
|
|
@@ -10,6 +11,8 @@ | |
| from pyrelay.relay.bootstrap import get_uow_factory | ||
| from pyrelay.relay.client_session import BaseClientSession | ||
| from pyrelay.relay.dispatcher import RelayDispatcher | ||
| from pyrelay.relay.nip_config import nips_config | ||
| from pyrelay.relay.handlers.send_event_handler import is_creation_time_valid | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
|
|
@@ -33,6 +36,18 @@ def get_dispatcher(): | |
| return dispatcher | ||
|
|
||
|
|
||
| async def _attempt_timestamp(event_builder, timestamps: list[int], should_save: bool): | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't really understand what the function is doing with the |
||
| dispatcher = get_dispatcher() | ||
| client_session = MockClientSession() | ||
| for timestamp in timestamps: | ||
| event = event_builder.create_event( | ||
| "", created_at=timestamp | ||
| ) | ||
| await dispatcher.handle(client_session, event) | ||
| for msg in client_session.calls["send_event"]: | ||
| assert msg.saved == should_save | ||
|
|
||
|
|
||
| class TestRelayDispatcher: | ||
| @pytest.mark.asyncio | ||
| async def test_report_and_save_events(self, event_builder): | ||
|
|
@@ -123,7 +138,7 @@ async def test_subscribe_and_broadcast(self): | |
|
|
||
| ] | ||
| assert calls == expected | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_wrong_msg(self): | ||
| dispatcher = get_dispatcher() | ||
|
|
@@ -144,3 +159,56 @@ async def test_event_3(self, event_builder): | |
| ] | ||
| ) | ||
| await dispatcher.handle(client_session, event) | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_legal_timestamp(self, event_builder): | ||
| nip_22_config = nips_config.nip_22 | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this test work based on configuration? |
||
| if not nip_22_config: | ||
| return | ||
| current_time = time.time() | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the fun part about not working with a method that uses the actual time it to test it specifically without having the whole dispatcher |
||
| legal_timeframe_size = nip_22_config[1] - nip_22_config[0] | ||
| lower_bound_timestamp = current_time + nip_22_config[0] | ||
| amount = 5 | ||
| # do not check lower bound as it can fail due to runtime delay | ||
| legal_timestamps = [lower_bound_timestamp + legal_timeframe_size / amount * i for i in range(1, amount + 1)] | ||
| await _attempt_timestamp(event_builder=event_builder, timestamps=legal_timestamps, should_save=True) | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_illegal_timestamp(self, event_builder): | ||
| nip_22_config = nips_config.nip_22 | ||
| if not nip_22_config: | ||
| return | ||
| current_time = time.time() | ||
| illegal_timestamps = [ | ||
| int(current_time + nip_22_config[0] - 60 * 60 * 24 * 365), # a year before lower limit | ||
| int(current_time + nip_22_config[0] - 60), # a minute before lower limit | ||
| int(current_time + nip_22_config[1] + 60), # a minute after upper limit | ||
| int(current_time + nip_22_config[1] + 60 * 60 * 24 * 365) # a year after upper limit | ||
| ] | ||
| await _attempt_timestamp(event_builder=event_builder, timestamps=illegal_timestamps, should_save=False) | ||
|
|
||
| def test_timestamp_validation(self): | ||
| good_timestamps_params = [ | ||
| (1500, 2000, (-600, 0)), | ||
| (1700, 2000, (-600, -200)), | ||
| (190000, 100000, (80000, 100000)), | ||
| (250000, 100000, (-600, 500000)), | ||
| (100000, 100000, (0, 1)), | ||
| (101, 100, (-600, 500000)), | ||
| (8000, 100000, None), | ||
| ] | ||
| bad_timestamps_params = [ | ||
| (1300, 2000, (-600, 0)), | ||
| (1900, 2000, (-600, -200)), | ||
| (150000, 100000, (80000, 100000)), | ||
| (9999999, 100000, (-600, 500000)), | ||
| (100001, 100000, (0, 1)), | ||
| (151, 100, (-600, 50)), | ||
| ] | ||
| for param_list, expected in [ | ||
| (good_timestamps_params, True), | ||
| (bad_timestamps_params, False) | ||
| ]: | ||
| for param_instance in param_list: | ||
| result = is_creation_time_valid(*param_instance) | ||
| assert result == expected | ||
|
Comment on lines
+190
to
+214
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. first - you probably want to use parameterize here :) |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the validation should happen before the delete