Refactor and enhance event handling, database logic, and replaceable event processing

- **main.go**: Replace `ratel` storage backend with `database.New` for enhanced reliability and error handling.
- **database/query-events.go**: Rewrite event querying to handle replaceable and parameterized replaceable events; introduce deletion-based filtering, and improve sorting logic.
- **realy/addEvent.go**: Remove unnecessary logging for ephemeral events to optimize processing.
- **database/query-events_test.go**: Add comprehensive tests for replaceable events, parameterized replaceable events, and deletion handling to validate new logic.
This commit is contained in:
2025-07-13 09:41:11 +01:00
parent 9b868cea17
commit 96b852d6f1
8 changed files with 657 additions and 609 deletions

495
.idea/workspace.xml generated
View File

@@ -19,401 +19,14 @@
</component>
<component name="ChangeListManager">
<list default="true" id="848aca24-a3ec-4e50-a3d5-7b132d168000" name="Changes" comment="Update error handling in `socketapi/handleReq.go`&#10;&#10;- **socketapi/handleReq.go**: Replace `return` statements with `continue` in error conditions to ensure subsequent events are processed without interruption.">
<change afterPath="$PROJECT_DIR$/addresstag/addresstag.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/main.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/resources.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/apputil/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/.codecov.yml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/CHANGELOG.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/LICENSE" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/Makefile" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/README.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/assert_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/bool.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/bool_ext.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/bool_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/duration.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/duration_ext.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/duration_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/error.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/error_ext.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/error_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/example_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/float32.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/float32_ext.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/float32_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/float64.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/float64_ext.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/float64_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/gen.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/int32.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/int32_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/int64.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/int64_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/internal/gen-atomicint/main.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/internal/gen-atomicint/wrapper.tmpl" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/internal/gen-atomicwrapper/main.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/internal/gen-atomicwrapper/wrapper.tmpl" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/nocmp.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/nocmp_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/pointer_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/stress_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/string.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/string_ext.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/string_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/time.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/time_ext.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/time_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/tools/tools.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/uint32.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/uint32_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/uint64.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/uint64_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/uintptr.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/uintptr_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/unsafe_pointer.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/unsafe_pointer_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/value.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/atomic/value_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/bin/binary.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/LICENSE" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/README.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/buf/bufpool.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/hsts/proxy.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/main.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/reverse/proxy.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/tcpkeepalive/listener.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/timeout/conn.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/lerproxy/util/u.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/nauth/main.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/nurl/main.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/vainstr/LICENSE" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/vainstr/README.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/cmd/vainstr/main.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/codec/codec.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/dns/nip05.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/dns/nip05_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ec/base58/util_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ec/ecdsa/util_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ec/secp256k1/util_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ec/util_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/encryption/README.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/encryption/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/encryption/nip4.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/encryption/nip44.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/encryption/nip44_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/keys/keys.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/layer2/badgerbadger/badgerbadger.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/layer2/badgerbadger/tester/badgerbadger.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/layer2/layer2.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/error.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/get_balance.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/get_info.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/lightning.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/list_transactions.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/lookup_invoice.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/make_invoice_response.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/multi_pay_invoice.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/multi_pay_keysend.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/names.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/payKeysend.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/pay_invoice.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/pay_invoice_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/pay_keysend.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nwc/protocols.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/common.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-configuration.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-disconnect.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-event.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-events.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-export.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-filter.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-import.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-nuke.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-relay.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-rescan.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-shutdown.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/http-subscribe.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/huma.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/publisher-openapi.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/openapi/serveMux.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/p256k/btcec/util_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/p256k/util_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/qu/README.adoc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/qu/qu.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/close.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/compact.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/configuration.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/countevents.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/create-a-tag.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/del/del.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/deleteevent.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/export.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/fetch-ids.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/garbagecollector.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/gccount.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/gcmark.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/gcsweep.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/getecounterkey.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/getindexkeysforevent.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/gettagkeyprefix.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/import.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/init.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/arb/arb.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/arb/arb_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/count/count.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/createdat/createdat.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/createdat/createdat_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/fullid/fullid.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/fullid/fullid_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/fullpubkey/fullpubkey.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/fullpubkey/fullpubkey_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/id/id.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/id/id_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/index/index.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/index/prefixes.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/keys.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/keys_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/kinder/kind.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/kinder/kind_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/pubkey/pubkey.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/pubkey/pubkey_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/serial/serial.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/serial/serial_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/tombstone/tombstone.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/tombstone/tombstone_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/keys/util_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/log.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/main.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/nuke.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/prefixes/index_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/prefixes/prefixes.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/preparequeries.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/queryevents.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/queryforids.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/rescan.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ratel/saveevent.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/readme.adoc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy.service" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/addEvent.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/auth.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/config/config.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/disconnect.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/handleRelayinfo.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/handleWebsocket.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/helpers/helpers.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/interfaces/interfaces.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/options/options.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/publish/publisher/interface.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/server-impl.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/server-publish.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/server.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/server_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/realy/testrelay.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/relay/interface.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/reload.sh" afterDir="false" />
<change afterPath="$PROJECT_DIR$/scripts/runtests.sh" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/LICENSE" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/README.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/cpuid_other.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256blockAvx512_amd64.asm" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256blockAvx512_amd64.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256blockAvx512_amd64.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256blockAvx512_amd64_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256block_amd64.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256block_amd64.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256block_amd64_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256block_arm64.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256block_arm64.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/sha256block_other.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/sha256/test-architectures.sh" afterDir="false" />
<change afterPath="$PROJECT_DIR$/socketapi/challenge.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/socketapi/handleAuth.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/socketapi/pinger.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/socketapi/upgrader.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/generate.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/version/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ws/client.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ws/client_test.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ws/doc.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ws/pool.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ws/subscription.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ws/subscription_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/LICENSE" beforeDir="false" afterPath="$PROJECT_DIR$/LICENSE" afterDir="false" />
<change beforePath="$PROJECT_DIR$/auth/nip42.go" beforeDir="false" afterPath="$PROJECT_DIR$/auth/nip42.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/auth/nip42_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/auth/nip42_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/bech32encoding/keys.go" beforeDir="false" afterPath="$PROJECT_DIR$/bech32encoding/keys.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/bech32encoding/keys_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/bech32encoding/keys_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/bech32encoding/nip19.go" beforeDir="false" afterPath="$PROJECT_DIR$/bech32encoding/nip19.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/bech32encoding/nip19_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/bech32encoding/nip19_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/bech32encoding/tlv/tlv.go" beforeDir="false" afterPath="$PROJECT_DIR$/bech32encoding/tlv/tlv.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/fetch-event-by-serial_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/fetch-event-by-serial_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/get-serial-by-id_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/get-serial-by-id_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/indexes/keys_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/indexes/keys_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/indexes/types/identhash_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/indexes/types/identhash_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/database.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/database.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/query-events.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/query-events.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/query-for-ids.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/query-for-ids.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/save-event.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/save-event.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/base58/base58_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/base58/base58_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/base58/base58check.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/base58/base58check.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/base58/base58check_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/base58/base58check_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/base58/example_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/base58/example_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/base58/genalphabet.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/base58/genalphabet.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/bech32/bech32.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/bech32/bech32.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/bech32/bech32_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/bech32/bech32_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/bech32/example_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/bech32/example_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/bench_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/bench_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/btcec_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/btcec_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/chaincfg/deployment_time_frame.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/chaincfg/deployment_time_frame.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/chaincfg/params.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/chaincfg/params.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/chainhash/hash.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/chainhash/hash.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/chainhash/hash_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/chainhash/hash_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/chainhash/hashfuncs.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/chainhash/hashfuncs.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/ciphering_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/ciphering_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/curve.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/curve.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/ecdsa/bench_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/ecdsa/bench_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/ecdsa/example_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/ecdsa/example_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/ecdsa/signature_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/ecdsa/signature_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/field_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/field_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/fuzz_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/fuzz_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/bench_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/bench_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/context.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/context.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/keys.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/keys.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/keys_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/keys_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/musig2_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/musig2_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/nonces.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/nonces.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/nonces_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/nonces_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/sign.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/sign.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/musig2/sign_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/musig2/sign_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/pubkey_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/pubkey_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/schnorr/bench_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/schnorr/bench_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/schnorr/signature.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/schnorr/signature.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/schnorr/signature_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/schnorr/signature_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/bench_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/bench_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/curve.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/curve.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/curve_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/curve_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/ecdh_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/ecdh_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/ellipticadaptor_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/ellipticadaptor_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/example_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/example_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/field_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/field_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/loadprecomputed.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/loadprecomputed.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/modnscalar_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/modnscalar_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/nonce.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/nonce.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/nonce_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/nonce_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/precomps/genprecomps.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/precomps/genprecomps.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/pubkey_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/pubkey_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/seckey.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/seckey.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/seckey_bench_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/seckey_bench_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/secp256k1/seckey_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/secp256k1/seckey_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ec/taproot/taproot.go" beforeDir="false" afterPath="$PROJECT_DIR$/ec/taproot/taproot.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/env/config.go" beforeDir="false" afterPath="$PROJECT_DIR$/env/config.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/authenvelope/authenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/authenvelope/authenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/authenvelope/authenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/authenvelope/authenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/closedenvelope/closedenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/closedenvelope/closedenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/closedenvelope/closedenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/closedenvelope/closedenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/closeenvelope/closeenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/closeenvelope/closeenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/closeenvelope/closeenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/closeenvelope/closeenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/countenvelope/countenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/countenvelope/countenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/countenvelope/countenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/countenvelope/countenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/eid/eid.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/eoseenvelope/eoseenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/eoseenvelope/eoseenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/eoseenvelope/eoseenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/eoseenvelope/eoseenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/eventenvelope/eventenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/eventenvelope/eventenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/eventenvelope/eventenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/eventenvelope/eventenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/identify.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/identify.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/noticeenvelope/noticeenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/noticeenvelope/noticeenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/noticeenvelope/noticeenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/noticeenvelope/noticeenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/okenvelope/okenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/okenvelope/okenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/okenvelope/okenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/okenvelope/okenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/reqenvelope/reqenvelope.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/reqenvelope/reqenvelope.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/envelopes/reqenvelope/reqenvelope_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/envelopes/reqenvelope/reqenvelope_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/event/binary.go" beforeDir="false" afterPath="$PROJECT_DIR$/event/binary.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/event/json.go" beforeDir="false" afterPath="$PROJECT_DIR$/event/json.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/eventid/eventid.go" beforeDir="false" afterPath="$PROJECT_DIR$/eventid/eventid.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/filter/filter.go" beforeDir="false" afterPath="$PROJECT_DIR$/filter/filter.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/filter/filter_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/filter/filter_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/filter/simple.go" beforeDir="false" afterPath="$PROJECT_DIR$/filter/simple.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/filters/filters.go" beforeDir="false" afterPath="$PROJECT_DIR$/filters/filters.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/filters/filters_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/filters/filters_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
<change beforePath="$PROJECT_DIR$/helpers/helpers.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/hex/aliases.go" beforeDir="false" afterPath="$PROJECT_DIR$/hex/aliases.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/httpauth/nip98auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/httpauth/nip98auth.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/httpauth/validate.go" beforeDir="false" afterPath="$PROJECT_DIR$/httpauth/validate.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/interfaces/store/store_interface.go" beforeDir="false" afterPath="$PROJECT_DIR$/interfaces/store/store_interface.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/interrupt/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/interrupt/main.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/interrupt/restart.go" beforeDir="false" afterPath="$PROJECT_DIR$/interrupt/restart.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ints/gen/pregen.go" beforeDir="false" afterPath="$PROJECT_DIR$/ints/gen/pregen.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ints/ints.go" beforeDir="false" afterPath="$PROJECT_DIR$/ints/ints.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ints/ints_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/ints/ints_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/json/base64.go" beforeDir="false" afterPath="$PROJECT_DIR$/json/base64.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/json/bech32.go" beforeDir="false" afterPath="$PROJECT_DIR$/json/bech32.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/json/examples_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/json/examples_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/json/keyvalue.go" beforeDir="false" afterPath="$PROJECT_DIR$/json/keyvalue.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/json/signed.go" beforeDir="false" afterPath="$PROJECT_DIR$/json/signed.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/json/unsigned.go" beforeDir="false" afterPath="$PROJECT_DIR$/json/unsigned.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kind/kind.go" beforeDir="false" afterPath="$PROJECT_DIR$/kind/kind.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kind/kind_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/kind/kind_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kinds/kinds_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/kinds/kinds_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/list/list.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/lol/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/lol/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lol/log.go" beforeDir="false" afterPath="$PROJECT_DIR$/lol/log.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lol/log_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/lol/log_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database/query-events_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/query-events_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/main.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/normalize/normalize.go" beforeDir="false" afterPath="$PROJECT_DIR$/normalize/normalize.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orly.png" beforeDir="false" afterPath="$PROJECT_DIR$/realy.png" afterDir="false" />
<change beforePath="$PROJECT_DIR$/p256k/btcec.go" beforeDir="false" afterPath="$PROJECT_DIR$/p256k/btcec.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/p256k/btcec/btcec.go" beforeDir="false" afterPath="$PROJECT_DIR$/p256k/btcec/btcec.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/p256k/btcec/btcec_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/p256k/btcec/btcec_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/p256k/doc.go" beforeDir="false" afterPath="$PROJECT_DIR$/p256k/doc.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/p256k/p256k.go" beforeDir="false" afterPath="$PROJECT_DIR$/p256k/p256k.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/p256k/p256k_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/p256k/p256k_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/p256k/secp256k1.go" beforeDir="false" afterPath="$PROJECT_DIR$/p256k/secp256k1.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/p256k/secp256k1_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/p256k/secp256k1_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pointers/pointers.go" beforeDir="false" afterPath="$PROJECT_DIR$/realy/pointers/pointers.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/publish/publisher.go" beforeDir="false" afterPath="$PROJECT_DIR$/realy/publish/publisher.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/readme.md" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/reason/reason.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/relayinfo/fetch.go" beforeDir="false" afterPath="$PROJECT_DIR$/relayinfo/fetch.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/relayinfo/types.go" beforeDir="false" afterPath="$PROJECT_DIR$/relayinfo/types.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/servemux/serveMux.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/server/add-event.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/server/handle-relayinfo.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/server/server.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/signer/signer.go" beforeDir="false" afterPath="$PROJECT_DIR$/signer/signer.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/socketapi/handleClose.go" beforeDir="false" afterPath="$PROJECT_DIR$/socketapi/handleClose.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/openapi/http-event.go" beforeDir="false" afterPath="$PROJECT_DIR$/openapi/http-event.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/realy/addEvent.go" beforeDir="false" afterPath="$PROJECT_DIR$/realy/addEvent.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/socketapi/handleEvent.go" beforeDir="false" afterPath="$PROJECT_DIR$/socketapi/handleEvent.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/socketapi/handleMessage.go" beforeDir="false" afterPath="$PROJECT_DIR$/socketapi/handleMessage.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/socketapi/handleReq.go" beforeDir="false" afterPath="$PROJECT_DIR$/socketapi/handleReq.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/socketapi/ok.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/socketapi/publisher.go" beforeDir="false" afterPath="$PROJECT_DIR$/socketapi/publisher.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/socketapi/socketapi.go" beforeDir="false" afterPath="$PROJECT_DIR$/socketapi/socketapi.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/subscription/subscriptionid.go" beforeDir="false" afterPath="$PROJECT_DIR$/subscription/subscriptionid.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/subscription/subscriptionid_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/subscription/subscriptionid_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tag/atag/atag.go" beforeDir="false" afterPath="$PROJECT_DIR$/tag/atag/atag.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tag/atag/atag_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/tag/atag/atag_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tag/tag.go" beforeDir="false" afterPath="$PROJECT_DIR$/tag/tag.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tag/tag_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/tag/tag_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tags/tags.go" beforeDir="false" afterPath="$PROJECT_DIR$/tags/tags.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tags/tags_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/tags/tags_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/text/escape_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/text/escape_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/text/helpers.go" beforeDir="false" afterPath="$PROJECT_DIR$/text/helpers.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/text/helpers_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/text/helpers_test.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/timestamp/timestamp.go" beforeDir="false" afterPath="$PROJECT_DIR$/timestamp/timestamp.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ubuntu_install_libsecp256k1.sh" beforeDir="false" afterPath="$PROJECT_DIR$/ubuntu_install_libsecp256k1.sh" afterDir="false" />
<change beforePath="$PROJECT_DIR$/version/version" beforeDir="false" afterPath="$PROJECT_DIR$/version/version" afterDir="false" />
<change beforePath="$PROJECT_DIR$/version/version.go" beforeDir="false" afterPath="$PROJECT_DIR$/version/version.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ws/connection.go" beforeDir="false" afterPath="$PROJECT_DIR$/ws/connection.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ws/listener.go" beforeDir="false" afterPath="$PROJECT_DIR$/ws/listener.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -461,57 +74,57 @@
<option name="openDirectoriesWithSingleClick" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"DefaultGoTemplateProperty": "Go File",
"Go Build.go build github.com/mleku/realy.lol.executor": "Run",
"Go Build.go build not.realy.lol.executor": "Run",
"Go Build.go build orly.dev/event/examples/filter.executor": "Run",
"Go Build.go build sample.go.executor": "Run",
"Go Test.TestFromCanonical in orly.dev/event.executor": "Run",
"Go Test.TestGenerateIndexes in not.realy.lol/database.executor": "Run",
"Go Test.TestGetIndexesForEvent in orly.dev/database.executor": "Run",
"Go Test.TestGetIndexesForEvent/BasicEvent in orly.dev/database.executor": "Run",
"Go Test.TestGetIndexesFromFilter in orly.dev/database.executor": "Run",
"Go Test.TestQueryEventsIntersection in orly.dev/database.executor": "Run",
"Go Test.TestQueryForAuthorsTags in orly.dev/database.executor": "Run",
"Go Test.TestQueryForCreatedAt in orly.dev/database.executor": "Run",
"Go Test.TestQueryForIds in orly.dev/database.executor": "Run",
"Go Test.TestQueryForKinds in orly.dev/database.executor": "Run",
"Go Test.TestQueryForKindsAuthors in orly.dev/database.executor": "Run",
"Go Test.TestQueryForKindsAuthorsTags in orly.dev/database.executor": "Run",
"Go Test.TestQueryForKindsTags in orly.dev/database.executor": "Run",
"Go Test.TestQueryForTags in orly.dev/database.executor": "Run",
"Go Test.TestSaveEvents in orly.dev/database.executor": "Run",
"Go Test.TestT in not.realy.lol/database/indexes/types.executor": "Run",
"Go Test.TestTMarshalBinary_UnmarshalBinary in orly.dev/event.executor": "Run",
"Go Test.go test not.realy.lol/database/indexes.executor": "Run",
"Go Test.go test not.realy.lol/database/indexes/types.executor": "Run",
"Go Test.go test orly.dev/database.executor": "Run",
"Go Test.go test orly.dev/database/indexes.executor": "Run",
"Go Test.go test orly.dev/event.executor": "Run",
"Go Test.gobench not.realy.lol/codecbuf.executor": "Run",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.GoLinterPluginOnboarding": "true",
"RunOnceActivity.GoLinterPluginStorageMigration": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.go.formatter.settings.were.checked": "true",
"RunOnceActivity.go.migrated.go.modules.settings": "true",
"RunOnceActivity.go.modules.automatic.dependencies.download": "true",
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
"SHELLCHECK.PATH": "/home/david/.local/share/JetBrains/GoLand2025.1/Shell Script/shellcheck",
"git-widget-placeholder": "test1",
"go.import.settings.migrated": "true",
"go.sdk.automatically.set": "true",
"junie.onboarding.icon.badge.shown": "true",
"last_opened_file_path": "/home/david/src/orly.dev/interfaces/store",
"node.js.detected.package.eslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "junie.project.settings"
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;DefaultGoTemplateProperty&quot;: &quot;Go File&quot;,
&quot;Go Build.go build github.com/mleku/realy.lol.executor&quot;: &quot;Run&quot;,
&quot;Go Build.go build not.realy.lol.executor&quot;: &quot;Run&quot;,
&quot;Go Build.go build orly.dev/event/examples/filter.executor&quot;: &quot;Run&quot;,
&quot;Go Build.go build sample.go.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestFromCanonical in orly.dev/event.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestGenerateIndexes in not.realy.lol/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestGetIndexesForEvent in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestGetIndexesForEvent/BasicEvent in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestGetIndexesFromFilter in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryEventsIntersection in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryForAuthorsTags in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryForCreatedAt in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryForIds in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryForKinds in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryForKindsAuthors in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryForKindsAuthorsTags in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryForKindsTags in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestQueryForTags in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestSaveEvents in orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestT in not.realy.lol/database/indexes/types.executor&quot;: &quot;Run&quot;,
&quot;Go Test.TestTMarshalBinary_UnmarshalBinary in orly.dev/event.executor&quot;: &quot;Run&quot;,
&quot;Go Test.go test not.realy.lol/database/indexes.executor&quot;: &quot;Run&quot;,
&quot;Go Test.go test not.realy.lol/database/indexes/types.executor&quot;: &quot;Run&quot;,
&quot;Go Test.go test orly.dev/database.executor&quot;: &quot;Run&quot;,
&quot;Go Test.go test orly.dev/database/indexes.executor&quot;: &quot;Run&quot;,
&quot;Go Test.go test orly.dev/event.executor&quot;: &quot;Run&quot;,
&quot;Go Test.gobench not.realy.lol/codecbuf.executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.GoLinterPluginOnboarding&quot;: &quot;true&quot;,
&quot;RunOnceActivity.GoLinterPluginStorageMigration&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.automatic.dependencies.download&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;,
&quot;SHELLCHECK.PATH&quot;: &quot;/home/david/.local/share/JetBrains/GoLand2025.1/Shell Script/shellcheck&quot;,
&quot;git-widget-placeholder&quot;: &quot;test1&quot;,
&quot;go.import.settings.migrated&quot;: &quot;true&quot;,
&quot;go.sdk.automatically.set&quot;: &quot;true&quot;,
&quot;junie.onboarding.icon.badge.shown&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/david/src/orly.dev/interfaces/store&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;junie.project.settings&quot;
}
}]]></component>
}</component>
<component name="RecentsManager">
<key name="GoMoveDeclarationDialog.RECENT_KEY">
<recent name="$PROJECT_DIR$/database" />

View File

@@ -1,11 +1,14 @@
package database
import (
"bytes"
"encoding/binary"
"github.com/dgraph-io/badger/v4"
"io"
"orly.dev/apputil"
"orly.dev/chk"
"orly.dev/context"
"orly.dev/database/indexes"
"orly.dev/eventid"
"orly.dev/eventidserial"
"orly.dev/log"
@@ -13,6 +16,7 @@ import (
"orly.dev/units"
"os"
"path/filepath"
"time"
)
type D struct {
@@ -77,10 +81,84 @@ func (d *D) Wipe() (err error) {
}
func (d *D) DeleteEvent(
c context.T, ev *eventid.T, noTombstone ...bool,
c context.T, eid *eventid.T, noTombstone ...bool,
) (err error) {
// TODO implement me
panic("implement me")
d.Logger.Warningf("deleting event %0x", eid.Bytes())
// Get the serial number for the event ID
ser, err := d.GetSerialById(eid.Bytes())
if err != nil {
return
}
if ser == nil {
// Event not found, nothing to delete
return
}
// Fetch the event to get its data
ev, err := d.FetchEventBySerial(ser)
if err != nil {
return
}
if ev == nil {
// Event not found, nothing to delete
return
}
// Get all indexes for the event
idxs, err := GetIndexesForEvent(ev, ser.Get())
if err != nil {
return
}
// Create a tombstone key if requested
var tombstoneKey []byte
if len(noTombstone) == 0 || !noTombstone[0] {
log.I.F("making tombstone for event %0x", eid.Bytes())
// Create a tombstone key using the event ID and current timestamp
// Since we don't have a dedicated tombstone prefix in the database package,
// we'll use a custom prefix "tmb" for tombstones
buf := new(bytes.Buffer)
// Write the tombstone prefix
buf.Write([]byte("tmb"))
// Write the event ID
buf.Write(eid.Bytes())
// Write the current timestamp
ts := uint64(time.Now().Unix())
binary.BigEndian.PutUint64(make([]byte, 8), ts)
buf.Write(make([]byte, 8))
tombstoneKey = buf.Bytes()
}
// Get the event key
eventKey := new(bytes.Buffer)
if err = indexes.EventEnc(ser).MarshalWrite(eventKey); err != nil {
return
}
// Delete the event and all its indexes in a transaction
err = d.Update(
func(txn *badger.Txn) (err error) {
// Delete the event
if err = txn.Delete(eventKey.Bytes()); err != nil {
return
}
// Delete all indexes
for _, key := range idxs {
if err = txn.Delete(key); err != nil {
return
}
}
// Write the tombstone if requested
if len(tombstoneKey) > 0 {
log.D.F("writing tombstone %0x", tombstoneKey)
log.W.F(
"writing tombstone %0x for event %0x", tombstoneKey,
eid.Bytes(),
)
if err = txn.Set(tombstoneKey, nil); err != nil {
return
}
}
return
},
)
return
}
func (d *D) Import(r io.Reader) {
@@ -103,3 +181,31 @@ func (d *D) EventIdsBySerial(start uint64, count int) (
// TODO implement me
panic("implement me")
}
// Init initializes the database with the given path.
func (d *D) Init(path string) (err error) {
// The database is already initialized in the New function,
// so we just need to ensure the path is set correctly.
d.dataDir = path
return nil
}
// Sync flushes the database buffers to disk.
func (d *D) Sync() (err error) {
return d.DB.Sync()
}
// Close releases resources and closes the database.
func (d *D) Close() (err error) {
if d.seq != nil {
if err = d.seq.Release(); chk.E(err) {
return
}
}
if d.DB != nil {
if err = d.DB.Close(); chk.E(err) {
return
}
}
return
}

View File

@@ -7,15 +7,21 @@ import (
"orly.dev/database/indexes/types"
"orly.dev/event"
"orly.dev/filter"
"orly.dev/hex"
"orly.dev/interfaces/store"
"orly.dev/ints"
"orly.dev/kind"
"orly.dev/sha256"
"orly.dev/tag"
"sort"
"strconv"
)
// QueryEvents retrieves events based on the provided filter.
// If the filter contains Ids, it fetches events by those Ids directly,
// overriding other filter criteria. Otherwise, it queries by other filter
// criteria and fetches matching events. Results are returned in reverse
// chronological order of their creation timestamps.
// QueryEvents retrieves events based on the provided filter. If the filter
// contains Ids, it fetches events by those Ids directly, overriding other
// filter criteria. Otherwise, it queries by other filter criteria and fetches
// matching events. Results are returned in reverse chronological order of their
// creation timestamps.
func (d *D) QueryEvents(c context.T, f *filter.F) (evs event.S, err error) {
// if there is Ids in the query, this overrides anything else
if f.Ids != nil && f.Ids.Len() > 0 {
@@ -47,7 +53,23 @@ func (d *D) QueryEvents(c context.T, f *filter.F) (evs event.S, err error) {
if idPkTs, err = d.QueryForIds(c, f); chk.E(err) {
return
}
// fetch the events
// Create a map to store the latest version of replaceable events
replaceableEvents := make(map[string]*event.E)
// Create a map to store the latest version of parameterized replaceable
// events
paramReplaceableEvents := make(map[string]map[string]*event.E)
// Regular events that are not replaceable
var regularEvents event.S
// Map to track deletion events by kind and pubkey (for replaceable events)
deletionsByKindPubkey := make(map[string]bool)
// Map to track deletion events by kind, pubkey, and d-tag (for
// parameterized replaceable events)
deletionsByKindPubkeyDTag := make(map[string]map[string]bool)
// First pass: collect all deletion events
for _, idpk := range idPkTs {
var ev *event.E
ser := new(types.Uint40)
@@ -57,21 +79,206 @@ func (d *D) QueryEvents(c context.T, f *filter.F) (evs event.S, err error) {
if ev, err = d.FetchEventBySerial(ser); err != nil {
continue
}
if ev.Kind.IsReplaceable() {
for _, e := range evs {
if bytes.Equal(
ev.Pubkey, e.Pubkey,
) && ev.Kind.K == e.Kind.K {
// Process deletion events to build our deletion maps
if ev.Kind.Equal(kind.Deletion) {
// Check for 'e' tags that directly reference event IDs
eTags := ev.Tags.GetAll(tag.New([]byte{'e'}))
for _, eTag := range eTags.ToSliceOfTags() {
if eTag.Len() < 2 {
continue
}
// We don't need to do anything with direct event ID references
// as we'll filter those out in the second pass
}
// Check for 'a' tags that reference parameterized replaceable events
aTags := ev.Tags.GetAll(tag.New([]byte{'a'}))
for _, aTag := range aTags.ToSliceOfTags() {
if aTag.Len() < 2 {
continue
}
// Parse the 'a' tag value: kind:pubkey:d-tag
split := bytes.Split(aTag.Value(), []byte{':'})
if len(split) != 3 {
continue
}
// Parse the kind
kin := ints.New(uint16(0))
if _, err = kin.Unmarshal(split[0]); err != nil {
continue
}
kk := kind.New(kin.Uint16())
// Only process parameterized replaceable events
if !kk.IsParameterizedReplaceable() {
continue
}
// Parse the pubkey
var pk []byte
if pk, err = hex.DecAppend(nil, split[1]); err != nil {
continue
}
// Only allow users to delete their own events
if !bytes.Equal(pk, ev.Pubkey) {
continue
}
// Create the key for the deletion map
key := string(pk) + ":" + strconv.Itoa(int(kk.K))
// Initialize the inner map if it doesn't exist
if _, exists := deletionsByKindPubkeyDTag[key]; !exists {
deletionsByKindPubkeyDTag[key] = make(map[string]bool)
}
// Mark this d-tag as deleted
dValue := string(split[2])
deletionsByKindPubkeyDTag[key][dValue] = true
}
// For replaceable events, we need to check if there are any
// e-tags that reference events with the same kind and pubkey
for _, eTag := range eTags.ToSliceOfTags() {
if eTag.Len() < 2 {
continue
}
// Get the event ID from the e-tag
evId := make([]byte, sha256.Size)
if _, err = hex.DecBytes(evId, eTag.Value()); err != nil {
continue
}
// Query for the event
var targetEvs event.S
targetEvs, err = d.QueryEvents(
c, &filter.F{Ids: tag.New(evId)},
)
if err != nil || len(targetEvs) == 0 {
continue
}
targetEv := targetEvs[0]
// Only allow users to delete their own events
if !bytes.Equal(targetEv.Pubkey, ev.Pubkey) {
continue
}
// If the event is replaceable, mark it as deleted
if targetEv.Kind.IsReplaceable() {
key := string(targetEv.Pubkey) + ":" + strconv.Itoa(int(targetEv.Kind.K))
deletionsByKindPubkey[key] = true
}
}
// } else if ev.Kind.IsParameterizedReplaceable(){
} else {
}
}
// Second pass: process all events, filtering out deleted ones
for _, idpk := range idPkTs {
var ev *event.E
ser := new(types.Uint40)
if err = ser.Set(idpk.Ser); chk.E(err) {
continue
}
if ev, err = d.FetchEventBySerial(ser); err != nil {
continue
}
// Skip events with kind 5 (Deletion)
if ev.Kind.Equal(kind.Deletion) {
continue
}
// Check if this event's ID is in the filter
isIdInFilter := false
if f.Ids != nil && f.Ids.Len() > 0 {
for i := 0; i < f.Ids.Len(); i++ {
if bytes.Equal(ev.Id, f.Ids.B(i)) {
isIdInFilter = true
break
}
}
}
if ev.Kind.IsReplaceable() {
// For replaceable events, we only keep the latest version for
// each pubkey and kind, and only if it hasn't been deleted
key := string(ev.Pubkey) + ":" + strconv.Itoa(int(ev.Kind.K))
// Skip this event if it has been deleted and its ID is not in
// the filter
if deletionsByKindPubkey[key] && !isIdInFilter {
continue
}
existing, exists := replaceableEvents[key]
if !exists || ev.CreatedAt.I64() > existing.CreatedAt.I64() {
replaceableEvents[key] = ev
}
} else if ev.Kind.IsParameterizedReplaceable() {
// For parameterized replaceable events, we need to consider the
// 'd' tag
key := string(ev.Pubkey) + ":" + strconv.Itoa(int(ev.Kind.K))
// Get the 'd' tag value
dTag := ev.Tags.GetFirst(tag.New([]byte{'d'}))
var dValue string
if dTag != nil && dTag.Len() > 1 {
dValue = string(dTag.Value())
} else {
// If no 'd' tag, use empty string
dValue = ""
}
if deletionMap, exists := deletionsByKindPubkeyDTag[key]; exists {
if deletionMap[dValue] && !isIdInFilter {
continue
}
}
// Initialize the inner map if it doesn't exist
if _, exists := paramReplaceableEvents[key]; !exists {
paramReplaceableEvents[key] = make(map[string]*event.E)
}
// Check if we already have an event with this 'd' tag value
existing, exists := paramReplaceableEvents[key][dValue]
if !exists || ev.CreatedAt.I64() > existing.CreatedAt.I64() {
paramReplaceableEvents[key][dValue] = ev
}
} else {
// Regular events
regularEvents = append(regularEvents, ev)
}
}
// Add all the latest replaceable events to the result
for _, ev := range replaceableEvents {
evs = append(evs, ev)
}
// Add all the latest parameterized replaceable events to the result
for _, innerMap := range paramReplaceableEvents {
for _, ev := range innerMap {
evs = append(evs, ev)
}
}
// Add all regular events to the result
evs = append(evs, regularEvents...)
// Sort all events by timestamp (newest first)
sort.Slice(
evs, func(i, j int) bool {
return evs[i].CreatedAt.I64() > evs[j].CreatedAt.I64()
},
)
}
return
}

View File

@@ -3,11 +3,13 @@ package database
import (
"bufio"
"bytes"
"fmt"
"orly.dev/chk"
"orly.dev/context"
"orly.dev/event"
"orly.dev/event/examples"
"orly.dev/filter"
"orly.dev/hex"
"orly.dev/kind"
"orly.dev/kinds"
"orly.dev/tag"
@@ -145,6 +147,228 @@ func TestQueryEvents(t *testing.T) {
}
}
// Test querying for replaced events by ID
// Create a replaceable event
replaceableEvent := event.New()
replaceableEvent.Kind = kind.ProfileMetadata // Kind 0 is replaceable
replaceableEvent.Pubkey = events[0].Pubkey // Use the same pubkey as an existing event
replaceableEvent.CreatedAt = new(timestamp.T)
replaceableEvent.CreatedAt.V = timestamp.Now().V - 7200 // 2 hours ago
replaceableEvent.Content = []byte("Original profile")
replaceableEvent.Tags = tags.New()
// Save the replaceable event
if _, _, err = db.SaveEvent(ctx, replaceableEvent); err != nil {
t.Fatalf("Failed to save replaceable event: %v", err)
}
// Create a newer version of the replaceable event
newerEvent := event.New()
newerEvent.Kind = kind.ProfileMetadata // Same kind
newerEvent.Pubkey = replaceableEvent.Pubkey // Same pubkey
newerEvent.CreatedAt = new(timestamp.T)
newerEvent.CreatedAt.V = timestamp.Now().V - 3600 // 1 hour ago (newer than the original)
newerEvent.Content = []byte("Updated profile")
newerEvent.Tags = tags.New()
// Save the newer event
if _, _, err = db.SaveEvent(ctx, newerEvent); err != nil {
t.Fatalf("Failed to save newer event: %v", err)
}
// Query for the original event by ID
evs, err = db.QueryEvents(
ctx, &filter.F{
Ids: tag.New(replaceableEvent.Id),
},
)
if err != nil {
t.Fatalf("Failed to query for replaced event by ID: %v", err)
}
// Verify we got exactly one event
if len(evs) != 1 {
t.Fatalf("Expected 1 event when querying for replaced event by ID, got %d", len(evs))
}
// Verify it's the original event
if !bytes.Equal(evs[0].Id, replaceableEvent.Id) {
t.Fatalf("Event ID doesn't match when querying for replaced event. Got %x, expected %x",
evs[0].Id, replaceableEvent.Id)
}
// Query for all events of this kind and pubkey
kindFilter = kinds.New(kind.ProfileMetadata)
authorFilter = tag.New(replaceableEvent.Pubkey)
evs, err = db.QueryEvents(
ctx, &filter.F{
Kinds: kindFilter,
Authors: authorFilter,
},
)
if err != nil {
t.Fatalf("Failed to query for replaceable events: %v", err)
}
// Verify we got only one event (the latest one)
if len(evs) != 1 {
t.Fatalf("Expected 1 event when querying for replaceable events, got %d", len(evs))
}
// Verify it's the newer event
if !bytes.Equal(evs[0].Id, newerEvent.Id) {
t.Fatalf("Event ID doesn't match when querying for replaceable events. Got %x, expected %x",
evs[0].Id, newerEvent.Id)
}
// Test deletion events
// Create a deletion event that references the replaceable event
deletionEvent := event.New()
deletionEvent.Kind = kind.Deletion // Kind 5 is deletion
deletionEvent.Pubkey = replaceableEvent.Pubkey // Same pubkey as the event being deleted
deletionEvent.CreatedAt = new(timestamp.T)
deletionEvent.CreatedAt.V = timestamp.Now().V // Current time
deletionEvent.Content = []byte("Deleting the replaceable event")
deletionEvent.Tags = tags.New()
// Add an e-tag referencing the replaceable event
deletionEvent.Tags = deletionEvent.Tags.AppendTags(
tag.New([]byte{'e'}, []byte(hex.Enc(replaceableEvent.Id))),
)
// Save the deletion event
if _, _, err = db.SaveEvent(ctx, deletionEvent); err != nil {
t.Fatalf("Failed to save deletion event: %v", err)
}
// Query for all events of this kind and pubkey again
evs, err = db.QueryEvents(
ctx, &filter.F{
Kinds: kindFilter,
Authors: authorFilter,
},
)
if err != nil {
t.Fatalf("Failed to query for replaceable events after deletion: %v", err)
}
// Verify we still get the newer event (deletion should only affect the original event)
if len(evs) != 1 {
t.Fatalf("Expected 1 event when querying for replaceable events after deletion, got %d", len(evs))
}
// Verify it's still the newer event
if !bytes.Equal(evs[0].Id, newerEvent.Id) {
t.Fatalf("Event ID doesn't match after deletion. Got %x, expected %x",
evs[0].Id, newerEvent.Id)
}
// Query for the original event by ID
evs, err = db.QueryEvents(
ctx, &filter.F{
Ids: tag.New(replaceableEvent.Id),
},
)
if err != nil {
t.Fatalf("Failed to query for deleted event by ID: %v", err)
}
// Verify we still get the original event when querying by ID
if len(evs) != 1 {
t.Fatalf("Expected 1 event when querying for deleted event by ID, got %d", len(evs))
}
// Verify it's the original event
if !bytes.Equal(evs[0].Id, replaceableEvent.Id) {
t.Fatalf("Event ID doesn't match when querying for deleted event by ID. Got %x, expected %x",
evs[0].Id, replaceableEvent.Id)
}
// Create a parameterized replaceable event
paramEvent := event.New()
paramEvent.Kind = kind.New(30000) // Kind 30000+ is parameterized replaceable
paramEvent.Pubkey = events[0].Pubkey // Use the same pubkey as an existing event
paramEvent.CreatedAt = new(timestamp.T)
paramEvent.CreatedAt.V = timestamp.Now().V - 7200 // 2 hours ago
paramEvent.Content = []byte("Original parameterized event")
paramEvent.Tags = tags.New()
// Add a d-tag
paramEvent.Tags = paramEvent.Tags.AppendTags(
tag.New([]byte{'d'}, []byte("test-d-tag")),
)
// Save the parameterized replaceable event
if _, _, err = db.SaveEvent(ctx, paramEvent); err != nil {
t.Fatalf("Failed to save parameterized replaceable event: %v", err)
}
// Create a deletion event that references the parameterized replaceable event using an a-tag
paramDeletionEvent := event.New()
paramDeletionEvent.Kind = kind.Deletion // Kind 5 is deletion
paramDeletionEvent.Pubkey = paramEvent.Pubkey // Same pubkey as the event being deleted
paramDeletionEvent.CreatedAt = new(timestamp.T)
paramDeletionEvent.CreatedAt.V = timestamp.Now().V // Current time
paramDeletionEvent.Content = []byte("Deleting the parameterized replaceable event")
paramDeletionEvent.Tags = tags.New()
// Add an a-tag referencing the parameterized replaceable event
// Format: kind:pubkey:d-tag
aTagValue := fmt.Sprintf("%d:%s:%s",
paramEvent.Kind.K,
hex.Enc(paramEvent.Pubkey),
"test-d-tag")
paramDeletionEvent.Tags = paramDeletionEvent.Tags.AppendTags(
tag.New([]byte{'a'}, []byte(aTagValue)),
)
// Save the parameterized deletion event
if _, _, err = db.SaveEvent(ctx, paramDeletionEvent); err != nil {
t.Fatalf("Failed to save parameterized deletion event: %v", err)
}
// Query for all events of this kind and pubkey
paramKindFilter := kinds.New(paramEvent.Kind)
paramAuthorFilter := tag.New(paramEvent.Pubkey)
evs, err = db.QueryEvents(
ctx, &filter.F{
Kinds: paramKindFilter,
Authors: paramAuthorFilter,
},
)
if err != nil {
t.Fatalf("Failed to query for parameterized replaceable events after deletion: %v", err)
}
// Verify we get no events (since the only one was deleted)
if len(evs) != 0 {
t.Fatalf("Expected 0 events when querying for deleted parameterized replaceable events, got %d", len(evs))
}
// Query for the parameterized event by ID
evs, err = db.QueryEvents(
ctx, &filter.F{
Ids: tag.New(paramEvent.Id),
},
)
if err != nil {
t.Fatalf("Failed to query for deleted parameterized event by ID: %v", err)
}
// Verify we still get the event when querying by ID
if len(evs) != 1 {
t.Fatalf("Expected 1 event when querying for deleted parameterized event by ID, got %d", len(evs))
}
// Verify it's the correct event
if !bytes.Equal(evs[0].Id, paramEvent.Id) {
t.Fatalf("Event ID doesn't match when querying for deleted parameterized event by ID. Got %x, expected %x",
evs[0].Id, paramEvent.Id)
}
// Test querying by time range
// Use the timestamp from the middle event as a reference
middleIndex := len(events) / 2

20
main.go
View File

@@ -12,17 +12,15 @@ import (
"orly.dev/log"
realy_lol "orly.dev/version"
"os"
"sync"
"orly.dev/app"
"orly.dev/context"
"orly.dev/database"
"orly.dev/interrupt"
"orly.dev/lol"
"orly.dev/ratel"
"orly.dev/realy"
"orly.dev/realy/config"
"orly.dev/realy/options"
"orly.dev/units"
)
func main() {
@@ -52,17 +50,11 @@ func main() {
chk.E(http.ListenAndServe("127.0.0.1:6060", nil))
}()
}
var wg sync.WaitGroup
c, cancel := context.Cancel(context.Bg())
storage := ratel.New(
ratel.BackendParams{
Ctx: c,
WG: &wg,
BlockCacheSize: units.Gb,
LogLevel: lol.GetLogLevel(cfg.DbLogLevel),
MaxLimit: ratel.DefaultMaxLimit,
},
)
storage, err := database.New(c, cancel, cfg.DataDir, cfg.DbLogLevel)
if chk.E(err) {
os.Exit(1)
}
r := &app.Relay{C: cfg, Store: storage}
go app.MonitorResources(c)
var server *realy.Server
@@ -71,7 +63,7 @@ func main() {
Cancel: cancel,
Rl: r,
DbPath: cfg.DataDir,
MaxLimit: ratel.DefaultMaxLimit,
MaxLimit: 512, // Default max limit for events
}
var opts []options.O
if server, err = realy.NewServer(serverParams, opts...); chk.E(err) {

View File

@@ -217,12 +217,8 @@ func (x *Operations) RegisterEvent(api huma.API) {
// if advancedDeleter != nil {
// advancedDeleter.BeforeDelete(ctx, t.Value(), ev.Pubkey)
// }
if err = sto.DeleteEvent(
ctx, target.EventId(),
); chk.T(err) {
err = huma.Error500InternalServerError(err.Error())
return
}
// Instead of deleting the event, we'll just add the deletion event
// The query logic will filter out deleted events
// if advancedDeleter != nil {
// advancedDeleter.AfterDelete(t.Value(), ev.Pubkey)
// }

View File

@@ -3,7 +3,6 @@ package realy
import (
"errors"
"net/http"
"orly.dev/log"
"strings"
"orly.dev/context"
@@ -56,9 +55,6 @@ func (s *Server) addEvent(
return false, normalize.Error.F("failed to save (%s)", errmsg)
}
}
log.I.F(
"event id %0x stored ephemeral: %s", ev.Id, ev.Kind.IsEphemeral(),
)
// if advancedSaver != nil {
// advancedSaver.AfterSave(ev)
// }
@@ -70,6 +66,5 @@ func (s *Server) addEvent(
// notify subscribers
s.listeners.Deliver(ev)
accepted = true
log.I.S(ev)
return
}

View File

@@ -14,7 +14,6 @@ import (
"orly.dev/log"
"orly.dev/normalize"
"orly.dev/realy/interfaces"
"orly.dev/sha256"
"orly.dev/tag"
)
@@ -30,11 +29,7 @@ func (a *A) HandleEvent(
if sto == nil {
panic("no event store has been set to store event")
}
// var auther relay.Authenticator
// if auther, ok = srv.Relay().(relay.Authenticator); ok {
// }
rl := srv.Relay()
// advancedDeleter, _ := sto.(relay.AdvancedDeleter)
env := eventenvelope.NewSubmission()
if rem, err = env.Unmarshal(req); chk.E(err) {
return
@@ -42,46 +37,6 @@ func (a *A) HandleEvent(
if len(rem) > 0 {
log.I.F("extra '%s'", rem)
}
// accept, notice, after := rl.AcceptEvent(c, env.F, a.Req(),
// a.RealRemote(), nil,
// //a.AuthedBytes(),
// )
// if !accept {
// if strings.Contains(notice, "mute") {
// if err = okenvelope.NewFrom(env.Id, false,
// normalize.Blocked.F(notice)).Write(a.Listener); chk.F(err) {
// }
// } else {
// //if auther != nil && auther.AuthRequired() {
// // if !a.AuthRequested() {
// // a.RequestAuth()
// // log.I.F("requesting auth from client %s", a.RealRemote())
// // if err = authenvelope.NewChallengeWith(a.Challenge()).Write(a.Listener); chk.F(err) {
// // return
// // }
// // if err = okenvelope.NewFrom(env.Id, false,
// // normalize.AuthRequired.F("auth required for storing events")).Write(a.Listener); chk.F(err) {
// // }
// // return
// // } else {
// // log.I.F("requesting auth again from client %s", a.RealRemote())
// // if err = authenvelope.NewChallengeWith(a.Challenge()).Write(a.Listener); chk.F(err) {
// // return
// // }
// // if err = okenvelope.NewFrom(env.Id, false,
// // normalize.AuthRequired.F("auth required for storing events")).Write(a.Listener); chk.F(err) {
// // }
// // return
// // }
// //} else {
// // log.W.F("didn't find authentication method")
// //}
// }
// if err = okenvelope.NewFrom(env.Id, false,
// normalize.Invalid.F(notice)).Write(a.Listener); chk.F(err) {
// }
// return
// }
if !bytes.Equal(env.GetIDBytes(), env.Id) {
if err = okenvelope.NewFrom(
env.Id, false,
@@ -113,46 +68,21 @@ func (a *A) HandleEvent(
var res []*event.E
if t.Len() >= 2 {
switch {
case bytes.Equal(t.Key(), []byte("e")):
evId := make([]byte, sha256.Size)
if _, err = hex.DecBytes(evId, t.Value()); chk.E(err) {
continue
}
res, err = sto.QueryEvents(c, &filter.F{Ids: tag.New(evId)})
if err != nil {
if err = okenvelope.NewFrom(
env.Id, false,
normalize.Error.F("failed to query for target event"),
).Write(a.Listener); chk.E(err) {
return
}
return
}
for i := range res {
if res[i].Kind.Equal(kind.Deletion) {
if err = okenvelope.NewFrom(
env.Id, false,
normalize.Blocked.F("not processing or storing delete event containing delete event references"),
).Write(a.Listener); chk.E(err) {
return
}
return
}
if !bytes.Equal(res[i].Pubkey, env.E.Pubkey) {
if err = okenvelope.NewFrom(
env.Id, false,
normalize.Blocked.F("cannot delete other users' events (delete by e tag)"),
).Write(a.Listener); chk.E(err) {
return
}
return
}
}
case bytes.Equal(t.Key(), []byte("a")):
split := bytes.Split(t.Value(), []byte{':'})
if len(split) != 3 {
continue
}
// Check if the deletion event is trying to delete itself
if bytes.Equal(split[2], env.Id) {
if err = okenvelope.NewFrom(
env.Id, false,
normalize.Blocked.F("deletion event cannot reference its own ID"),
).Write(a.Listener); chk.E(err) {
return
}
return
}
var pk []byte
if pk, err = hex.DecAppend(nil, split[1]); chk.E(err) {
if err = okenvelope.NewFrom(
@@ -210,10 +140,6 @@ func (a *A) HandleEvent(
}
f := filter.New()
f.Kinds.K = []*kind.T{kk}
// aut := make(by, 0, len(pk)/2)
// if aut, err = hex.DecAppend(aut, pk); chk.E(err) {
// return
// }
f.Authors.Append(pk)
f.Tags.AppendTags(tag.New([]byte{'#', 'd'}, split[2]))
res, err = sto.QueryEvents(c, f)
@@ -266,21 +192,13 @@ func (a *A) HandleEvent(
}
return
}
// if advancedDeleter != nil {
// advancedDeleter.BeforeDelete(c, t.Value(), env.Pubkey)
// }
if err = sto.DeleteEvent(c, target.EventId()); chk.T(err) {
if err = okenvelope.NewFrom(
env.Id, false,
normalize.Error.F(err.Error()),
).Write(a.Listener); chk.E(err) {
return
}
// Instead of deleting the event, we'll just add the deletion
// event The query logic will filter out deleted events
if err = okenvelope.NewFrom(
env.Id, true,
).Write(a.Listener); chk.E(err) {
return
}
// if advancedDeleter != nil {
// advancedDeleter.AfterDelete(t.Value(), env.Pubkey)
// }
}
res = nil
}
@@ -293,7 +211,7 @@ func (a *A) HandleEvent(
var reason []byte
ok, reason = srv.AddEvent(
c, rl, env.E, a.Req(), a.RealRemote(), nil,
) // a.AuthedBytes(),
)
log.I.F("event added %v, %s", ok, reason)
if err = okenvelope.NewFrom(
@@ -301,8 +219,5 @@ func (a *A) HandleEvent(
).Write(a.Listener); chk.E(err) {
return
}
// if after != nil {
// after()
// }
return
}