diff --git a/CHANGELOG.md b/CHANGELOG.md index a768570..22278db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,4 +31,10 @@ ## 1.3.1 -- fix: Inconsitency in events is breaking tags \ No newline at end of file +- fix: Inconsitency in events is breaking tags + +## 1.3.2 + +- refactor: Event with optional verification +- remove tests with encoding problem +- improve coverage diff --git a/lib/src/event.dart b/lib/src/event.dart index 97fd630..6cd7c87 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -53,24 +53,32 @@ class Event { this.kind, this.tags, this.content, - this.sig, - ) { + this.sig, { + this.subscriptionId, + bool verify = true, + }) { assert(createdAt.toString().length == 10); - assert(createdAt <= currentUnixTimestampSeconds()); pubkey = pubkey.toLowerCase(); - String id = getEventId(); - assert(this.id == id); - assert(bip340.verify(pubkey, id, sig)); + + /// This option adds event checks such as id, signature, non-futuristic event: default=True + /// Performances could be a reason to disable event checks + if (verify == true) { + assert(createdAt <= currentUnixTimestampSeconds()); + String verifyId = getEventId(); + assert(id == verifyId); + assert(bip340.verify(pubkey, id, sig)); + } } /// Instanciate Event object from the minimum available data - Event.from( - {this.createdAt = 0, - required this.kind, - required this.tags, - required this.content, - required String privkey, - this.subscriptionId}) { + Event.from({ + this.createdAt = 0, + required this.kind, + required this.tags, + required this.content, + required String privkey, + this.subscriptionId, + }) { if (createdAt == 0) { createdAt = currentUnixTimestampSeconds(); } @@ -82,16 +90,20 @@ class Event { } /// Deserialize an event from a JSON - Event.fromJson(Map json, {this.subscriptionId}) { - id = json['id']; - pubkey = json['pubkey']; - createdAt = json['created_at']; - kind = json['kind']; - tags = (json['tags'] as List) + factory Event.fromJson(Map json, {bool verify = true}) { + var tags = (json['tags'] as List) .map((e) => (e as List).map((e) => e as String).toList()) .toList(); - content = json['content']; - sig = json['sig']; + return Event( + json['id'], + json['pubkey'], + json['created_at'], + json['kind'], + tags, + json['content'], + json['sig'], + verify: verify, + ); } /// Serialize an event in JSON @@ -119,25 +131,33 @@ class Event { /// Deserialize a nostr event message /// - ["EVENT", event JSON as defined above] /// - ["EVENT", subscription_id, event JSON as defined above] - Event.deserialize(input) { + factory Event.deserialize(input, {bool verify = true}) { Map json = {}; + String? subscriptionId; if (input.length == 2) { json = input[1] as Map; } else if (input.length == 3) { json = input[2] as Map; - subscriptionId = input[1]; + subscriptionId = input[1] as String; } else { throw Exception('invalid input'); } - id = json['id']; - pubkey = json['pubkey']; - createdAt = json['created_at']; - kind = json['kind']; - tags = (json['tags'] as List) + + var tags = (json['tags'] as List) .map((e) => (e as List).map((e) => e as String).toList()) .toList(); - content = json['content']; - sig = json['sig']; + + return Event( + json['id'], + json['pubkey'], + json['created_at'], + json['kind'], + tags, + json['content'], + json['sig'], + subscriptionId: subscriptionId, + verify: verify, + ); } /// To obtain the event.id, we sha256 the serialized event. diff --git a/pubspec.yaml b/pubspec.yaml index 7619ab9..3b8779c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: nostr description: A library for nostr protocol implemented in dart for flutter -version: 1.3.1 +version: 1.3.2 homepage: https://github.com/ethicnology/dart-nostr environment: diff --git a/test/event_test.dart b/test/event_test.dart index 50586cc..b611a6b 100644 --- a/test/event_test.dart +++ b/test/event_test.dart @@ -103,31 +103,8 @@ void main() { }); test('Constructor.toJson', () { - Map json = { - "pubkey": - "9be7376ef6c0d493235ddf9018ff675a04bfcaf34dc1f97a1d270470f28c0ae0", - "content": "How does this work? 👀", - "id": - "883334badc17315fc61f0a13eec84c8c87df9d504ce0788f5eeab4a3527ddc97", - "created_at": 1672477967, - "sig": - "3ce34915e90505f9760a463eb8f9e8b1748fd4c10c4cfddc09a2930ecce249ce8dd499eeffd6e24a215bb2f8265b68085f7104eb7d506f8d9b76a7c5312b09fd", - "kind": 1, - "tags": [ - [ - "e", - "68ae015bf4833a6ff0ed86564c5afaa65c31791d35e8432755535d02eafc4375" - ], - [ - "e", - "de2d85a00a52ceb25f3cfc41e22d927f6166250f210f928e2552b97c0bd66dcf" - ], - [ - "p", - "052acd328f1c1d48e86fff3e34ada4bfc60578116f4f68f296602530529656a2" - ] - ] - }; + Map json = jsonDecode( + '{"id":"f9b8c5b7a8692b0f5b8ca9f2c29ff84d6baa5a60d14cbf1c54bd2bb77ee8b41f","kind":1,"pubkey":"891b945271cd3c65dc22cb9e77ba08f5cd165ad8d9fba370b740f7db95f98b10","created_at":1675015139,"content":"Authorize just this time...I have commitment issues.","tags":[["e","343ed9c6ca7a0a8f33f8cfed04b6cea4a4dda50a649daffaf85d6410507c5c7c","wss://relay.damus.io","reply"],["p","7b6461d02c6f0be1cacdcf968c4246105a2db51c7770993bf8bb25e59cedffa7"]],"sig":"bd63b762379bd06e536ccb943f909f075bd512315fbf2407be19f03ee9d3ef5b4a70205aa7a8e68cb8c2d250f56ef8f5074339abd741d32dbd18d16641a339ef"}'); Event event = Event.fromJson(json); Map toJson = event.toJson(); expect(toJson, json); @@ -180,31 +157,29 @@ void main() { test('Constructor.deserialize', () { var serialized = [ "EVENT", - "7971516031312706", + "0954524188078879", { "id": - "883334badc17315fc61f0a13eec84c8c87df9d504ce0788f5eeab4a3527ddc97", - "pubkey": - "9be7376ef6c0d493235ddf9018ff675a04bfcaf34dc1f97a1d270470f28c0ae0", - "created_at": 1672477967, + "f9b8c5b7a8692b0f5b8ca9f2c29ff84d6baa5a60d14cbf1c54bd2bb77ee8b41f", "kind": 1, + "pubkey": + "891b945271cd3c65dc22cb9e77ba08f5cd165ad8d9fba370b740f7db95f98b10", + "created_at": 1675015139, + "content": "Authorize just this time...I have commitment issues.", "tags": [ [ "e", - "68ae015bf4833a6ff0ed86564c5afaa65c31791d35e8432755535d02eafc4375" - ], - [ - "e", - "de2d85a00a52ceb25f3cfc41e22d927f6166250f210f928e2552b97c0bd66dcf" + "343ed9c6ca7a0a8f33f8cfed04b6cea4a4dda50a649daffaf85d6410507c5c7c", + "wss://relay.damus.io", + "reply" ], [ "p", - "052acd328f1c1d48e86fff3e34ada4bfc60578116f4f68f296602530529656a2" + "7b6461d02c6f0be1cacdcf968c4246105a2db51c7770993bf8bb25e59cedffa7" ] ], - "content": "How does this work? 👀", "sig": - "3ce34915e90505f9760a463eb8f9e8b1748fd4c10c4cfddc09a2930ecce249ce8dd499eeffd6e24a215bb2f8265b68085f7104eb7d506f8d9b76a7c5312b09fd", + "bd63b762379bd06e536ccb943f909f075bd512315fbf2407be19f03ee9d3ef5b4a70205aa7a8e68cb8c2d250f56ef8f5074339abd741d32dbd18d16641a339ef" } ]; Event event = Event.deserialize(serialized); @@ -239,9 +214,15 @@ void main() { expect(eventWithoutSubscriptionId.subscriptionId, null); }); - test('Generated from decoded json', () { - Event event = Event.fromJson(jsonDecode( - '{"kind": 1, "pubkey":"0ba0206887bd61579bf65ec09d7806bea32c64be1cf2c978cf031a811cd238db","content": "dart-nostr","tags": [["p","052acd328f1c1d48e86fff3e34ada4bfc60578116f4f68f296602530529656a2",""]],"created_at": 1672477962,"sig":"246970954e7b74e7fe381a4c818fed739ee59444cb536dadf45fbbce33bd7455ae7cd678c347c4a0c6e0a4483d18c7e26b7abe76f4cc73234f774e0e0d65204b","id": "047663d895d56aefa3f528935c7ce7dc8939eb721a0ec76ef2e558a8257955d2"}')); + test('Fake event (verify=false) with empty tag', () { + var json = jsonDecode( + '{"kind": 1, "pubkey":"0ba0206887bd61579bf65ec09d7806bea32c64be1cf2c978cf031a811cd238db","content": "dart-nostr","tags": [["p","052acd328f1c1d48e86fff3e34ada4bfc60578116f4f68f296602530529656a2",""]],"created_at": 1672477962,"sig":"246970954e7b74e7fe381a4c818fed739ee59444cb536dadf45fbbce33bd7455ae7cd678c347c4a0c6e0a4483d18c7e26b7abe76f4cc73234f774e0e0d65204b","id": "047663d895d56aefa3f528935c7ce7dc8939eb721a0ec76ef2e558a8257955d2"}', + ); + Event event = Event.fromJson(json, verify: false); expect(event.tags[0][2], equals("")); }); + + test('Event.deserialize throw', () { + expect(() => Event.deserialize([]), throwsException); + }); } diff --git a/test/message_test.dart b/test/message_test.dart index b8f32fd..6c2929f 100644 --- a/test/message_test.dart +++ b/test/message_test.dart @@ -5,11 +5,11 @@ void main() { group('Message', () { test('EVENT', () { String payload = - '["EVENT","3979053091133091",{"id":"a60679692533b308f1d862c2a5ca5c08a304e5157b1df5cde0ff0454b9920605","pubkey":"7c579328cf9028a4548d5117afa4f8448fb510ca9023f576b7bc90fc5be6ce7e","created_at":1674405882,"kind":1,"tags":[],"content":"GM gm gm! Currently bathing my brain in coffee ☕️ hahaha. How many other nostrinos love coffee? 🤪🤙","sig":"10262aa6a83e0b744cda2097f06f7354357512b82846f6ef23ef7d997136b64815c343b613a0635a27da7e628c96ac2475f66dd72513c1fb8ce6560824eb25b8"}]'; + '["EVENT","0954524188078879",{"id":"da5107e5aa00978e0dd9bb3c09b2303af414fe4c10600b3c61bb938675d191af","kind":1,"pubkey":"da0cc82154bdf4ce8bf417eaa2d2fa99aa65c96c77867d6656fccdbf8e781b18","created_at":1675015235,"content":"#[0] test","tags":[["p","939ddb0c77d18ccd1ebb44c7a32b9cdc29b489e710c54db7cf1383ee86674a24"]],"sig":"f2b507e1039084d07477ccd2876ee0eb9e80f214be80e4e2ab28ad60dc5858297bbd9bf177be230b571c0015deb05c12eb02b659586fd3573cfa84c9292400b5"}]'; var msg = Message.deserialize(payload); expect(msg.type, "EVENT"); expect(msg.message.id, - "a60679692533b308f1d862c2a5ca5c08a304e5157b1df5cde0ff0454b9920605"); + "da5107e5aa00978e0dd9bb3c09b2303af414fe4c10600b3c61bb938675d191af"); }); test('REQ', () { String payload =