Skip to content

Commit 190303d

Browse files
authored
Merge pull request #332 from relaystr/fix-websocket-performance-issues-2-refactor-nip-01
refactor: nip01 immutable
2 parents f368849 + 22f7e13 commit 190303d

File tree

78 files changed

+1269
-659
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+1269
-659
lines changed

packages/amber/lib/data_layer/repositories/signers/amber_event_signer.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'dart:convert';
22

33
import 'package:ndk/ndk.dart';
4-
import 'package:ndk/shared/nips/nip19/nip19.dart';
54

65
import '../../data_sources/amber_flutter.dart';
76

@@ -18,13 +17,15 @@ class AmberEventSigner implements EventSigner {
1817
});
1918

2019
@override
21-
Future<void> sign(Nip01Event event) async {
20+
Future<Nip01Event> sign(Nip01Event event) async {
2221
final npub = publicKey.startsWith('npub')
2322
? publicKey
2423
: Nip19.encodePubKey(publicKey);
2524
Map<dynamic, dynamic> map = await amberFlutterDS.amber.signEvent(
26-
currentUser: npub, eventJson: jsonEncode(event.toJson()), id: event.id);
27-
event.sig = map['signature'];
25+
currentUser: npub,
26+
eventJson: Nip01EventModel.fromEntity(event).toJsonString(),
27+
id: event.id);
28+
return event.copyWith(sig: map['signature']);
2829
}
2930

3031
@override

packages/isar/lib/data_layer/models/db/db_event.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class DbEvent extends Nip01Event {
4242
String get content => super.content;
4343

4444
@override
45-
String get sig => super.sig;
45+
String? get sig => super.sig;
4646

4747
@override
4848
bool? get validSig => super.validSig;
@@ -56,12 +56,9 @@ class DbEvent extends Nip01Event {
5656
required super.tags,
5757
required super.content,
5858
super.createdAt,
59-
required String sig,
59+
String? sig,
6060
bool? validSig,
6161
required List<String> sources}) {
62-
super.sig = sig;
63-
super.validSig = validSig;
64-
super.sources = sources;
6562
}
6663

6764
static DbEvent fromNip01Event(Nip01Event event) {

packages/isar/lib/data_layer/models/db/db_event.g.dart

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/isar/pubspec.lock

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,9 @@ packages:
356356
ndk:
357357
dependency: "direct main"
358358
description:
359-
name: ndk
360-
sha256: "88c371ca73014fa41b8f711036fb64d01c99045e7ce7e9560fa49a0976c36e18"
361-
url: "https://pub.dev"
362-
source: hosted
359+
path: "../ndk"
360+
relative: true
361+
source: path
363362
version: "0.6.1-dev.1"
364363
node_preamble:
365364
dependency: transitive

packages/ndk/example/bunkers_example.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ Future<void> main() async {
2222
tags: [],
2323
);
2424

25-
await ndk.accounts.sign(event);
25+
final signedEvent = await ndk.accounts.sign(event);
2626
log('Event signed successfully!');
27-
log('Event ID: ${event.id}');
27+
log('Event ID: ${signedEvent.id}');
2828
} catch (e) {
2929
log('Error: $e');
3030
}
Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
import 'dart:developer';
22

3-
import 'package:ndk/domain_layer/entities/nip_01_event.dart';
3+
import 'package:ndk/ndk.dart';
44
import 'package:ndk/shared/nips/nip01/bip340.dart';
55

66
void main() async {
77
final keyPair = Bip340.generatePrivateKey();
88

9-
final minedEvent = Nip01Event(
9+
final event = Nip01Event(
1010
pubKey: keyPair.publicKey,
1111
kind: 1,
1212
tags: [],
1313
content: 'message',
14-
).minePoW(12);
14+
);
1515

16-
log(minedEvent.id); // the id will start with "000"
16+
/// your global NDK instance
17+
final ndk = Ndk.emptyBootstrapRelaysConfig();
1718

18-
if (minedEvent.checkPoWDifficulty(10)) {
19+
/// pass your event to the proof of work usecase
20+
final minedEvent =
21+
await ndk.proofOfWork.minePoW(event: event, targetDifficulty: 10);
22+
23+
/// the id will start with "000"
24+
log(minedEvent.id);
25+
26+
/// check the difficulty
27+
if (ndk.proofOfWork.checkPoWDifficulty(event: event, targetDifficulty: 10)) {
1928
log('Event has difficulty >= 10');
2029
}
2130
}

packages/ndk/example/nostrconnect_example.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ Future<void> main() async {
2828
tags: [],
2929
);
3030

31-
await ndk.accounts.sign(event);
31+
final signedEvent = await ndk.accounts.sign(event);
3232
log('Event signed successfully!');
33-
log('Event ID: ${event.id}');
33+
log('Event ID: ${signedEvent.id}');
3434
} catch (e) {
3535
log('Error: $e');
3636
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import 'dart:convert';
2+
3+
import '../../domain_layer/entities/nip_01_event.dart';
4+
import '../../shared/helpers/list_casting.dart';
5+
import '../../shared/nips/nip19/nip19.dart';
6+
7+
/// Data model for NIP-01 Event
8+
/// Extends [Nip01Event] entity with serialization methods to/from JSON and other formats
9+
class Nip01EventModel extends Nip01Event {
10+
/// creates a new [Nip01EventModel] instance
11+
Nip01EventModel({
12+
required super.id,
13+
required super.pubKey,
14+
required super.createdAt,
15+
required super.kind,
16+
required super.tags,
17+
required super.content,
18+
required super.sig,
19+
super.validSig,
20+
super.sources = const [],
21+
});
22+
23+
/// creates a copy of this event with the given fields replaced by the new values \
24+
/// needed so other packages depending on the extened nip01_event.dart can use copyWith
25+
@override
26+
Nip01EventModel copyWith({
27+
String? id,
28+
String? pubKey,
29+
int? createdAt,
30+
int? kind,
31+
List<List<String>>? tags,
32+
String? content,
33+
String? sig,
34+
bool? validSig,
35+
List<String>? sources,
36+
}) {
37+
return Nip01EventModel(
38+
id: id ?? this.id,
39+
pubKey: pubKey ?? this.pubKey,
40+
createdAt: createdAt ?? this.createdAt,
41+
kind: kind ?? this.kind,
42+
tags: tags ?? this.tags,
43+
content: content ?? this.content,
44+
sig: sig ?? this.sig,
45+
validSig: validSig ?? this.validSig,
46+
sources: sources ?? this.sources);
47+
}
48+
49+
/**
50+
* encoding/decoding methods
51+
*/
52+
53+
/// creates a new [Nip01EventModel] instance from a [Nip01Event] entity
54+
factory Nip01EventModel.fromEntity(Nip01Event event) {
55+
return Nip01EventModel(
56+
id: event.id,
57+
pubKey: event.pubKey,
58+
createdAt: event.createdAt,
59+
kind: event.kind,
60+
tags: event.tags,
61+
content: event.content,
62+
sig: event.sig,
63+
sources: event.sources,
64+
validSig: event.validSig,
65+
);
66+
}
67+
68+
/// creates a new [Nip01EventModel] instance from a JSON object
69+
factory Nip01EventModel.fromJson(Map<dynamic, dynamic> data) {
70+
final id = data['id'] as String? ?? '';
71+
final pubKey = data['pubkey'] as String? ?? '';
72+
final createdAt = data['created_at'] as int;
73+
final kind = data['kind'] as int;
74+
final tags = castToListOfListOfString(data['tags']);
75+
final content = data['content'] as String? ?? '';
76+
77+
/// '' to support rumor events
78+
final sig = (data['sig'] as String?);
79+
80+
return Nip01EventModel(
81+
id: id,
82+
pubKey: pubKey,
83+
createdAt: createdAt,
84+
kind: kind,
85+
tags: tags,
86+
content: content,
87+
sig: sig,
88+
);
89+
}
90+
91+
/// Returns the Event object as a JSON object
92+
Map<String, dynamic> toJson() {
93+
return {
94+
'id': id,
95+
'pubkey': pubKey,
96+
'created_at': createdAt,
97+
'kind': kind,
98+
'tags': tags,
99+
'content': content,
100+
'sig': sig
101+
};
102+
}
103+
104+
/// Returns the Event object as a JSON string
105+
String toJsonString() {
106+
final jsonMap = toJson();
107+
return json.encode(jsonMap);
108+
}
109+
110+
/// Encode this event as a nevent (NIP-19 event reference)
111+
///
112+
/// Returns a bech32-encoded nevent string that includes:
113+
/// - Event ID (required)
114+
/// - Author pubkey (included)
115+
/// - Kind (included)
116+
/// - Relay hints from event.sources (if available)
117+
///
118+
/// Usage: `final nevent = event.nevent;`
119+
String get nevent {
120+
return Nip19.encodeNevent(
121+
eventId: id,
122+
author: pubKey,
123+
kind: kind,
124+
relays: sources.isEmpty ? null : sources,
125+
);
126+
}
127+
128+
/// Encode this event as an naddr (NIP-19 addressable event coordinate)
129+
///
130+
/// Only works for addressable/replaceable events (kind >= 10000 or kind 0, 3, 41)
131+
/// Requires a "d" tag to identify the event.
132+
///
133+
/// Returns a bech32-encoded naddr string or null if:
134+
/// - Event is not addressable/replaceable
135+
/// - Event doesn't have a "d" tag
136+
///
137+
/// Usage: `final naddr = event.naddr;`
138+
String? get naddr {
139+
// Check if this is an addressable event
140+
if (!_isAddressableKind(kind)) {
141+
return null;
142+
}
143+
144+
// Get the "d" tag identifier
145+
final identifier = getDtag();
146+
if (identifier == null) {
147+
return null;
148+
}
149+
150+
return Nip19.encodeNaddr(
151+
identifier: identifier,
152+
pubkey: pubKey,
153+
kind: kind,
154+
relays: sources.isEmpty ? null : sources,
155+
);
156+
}
157+
158+
/// Returns the Event object as a base64-encoded JSON string
159+
String toBase64() {
160+
return base64Encode(utf8.encode(json.encode(toJson())));
161+
}
162+
163+
/// Check if an event kind is addressable/replaceable
164+
///
165+
/// According to NIP-01:
166+
/// - Replaceable events: 0, 3, 41
167+
/// - Parameterized replaceable events: 10000-19999, 30000-39999
168+
bool _isAddressableKind(int kind) {
169+
// Replaceable events
170+
if (kind == 0 || kind == 3 || kind == 41) {
171+
return true;
172+
}
173+
174+
// Parameterized replaceable events
175+
if ((kind >= 10000 && kind <= 19999) || (kind >= 30000 && kind <= 39999)) {
176+
return true;
177+
}
178+
179+
return false;
180+
}
181+
}

packages/ndk/lib/data_layer/repositories/blossom/blossom_impl.dart

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import '../../../domain_layer/entities/nip_01_event.dart';
77
import '../../../domain_layer/entities/tuple.dart';
88
import '../../../domain_layer/repositories/blossom.dart';
99
import '../../data_sources/http_request.dart';
10+
import '../../models/nip_01_event_model.dart';
1011

1112
class BlossomRepositoryImpl implements BlossomRepository {
1213
final HttpRequestDS client;
@@ -169,7 +170,8 @@ class BlossomRepositoryImpl implements BlossomRepository {
169170
headers: {
170171
if (contentType != null) 'Content-Type': contentType,
171172
if (authorization != null)
172-
'Authorization': "Nostr ${authorization.toBase64()}",
173+
'Authorization':
174+
"Nostr ${Nip01EventModel.fromEntity(authorization).toBase64()}",
173175
'Content-Length': '${data.length}',
174176
},
175177
);
@@ -212,7 +214,8 @@ class BlossomRepositoryImpl implements BlossomRepository {
212214
url: Uri.parse('$serverUrl/mirror'),
213215
body: myBody,
214216
headers: {
215-
'Authorization': "Nostr ${authorization.toBase64()}",
217+
'Authorization':
218+
"Nostr ${Nip01EventModel.fromEntity(authorization).toBase64()}",
216219
'Content-Type': 'application/json',
217220
},
218221
);
@@ -259,7 +262,8 @@ class BlossomRepositoryImpl implements BlossomRepository {
259262
}
260263

261264
if (authorization != null) {
262-
headers['Authorization'] = "Nostr ${authorization.toBase64()}";
265+
headers['Authorization'] =
266+
"Nostr ${Nip01EventModel.fromEntity(authorization).toBase64()}";
263267
}
264268

265269
final response = await client.get(
@@ -298,7 +302,8 @@ class BlossomRepositoryImpl implements BlossomRepository {
298302
final headers = <String, String>{};
299303

300304
if (authorization != null) {
301-
headers['Authorization'] = "Nostr ${authorization.toBase64()}";
305+
headers['Authorization'] =
306+
"Nostr ${Nip01EventModel.fromEntity(authorization).toBase64()}";
302307
}
303308

304309
for (final url in serverUrls) {
@@ -420,7 +425,8 @@ class BlossomRepositoryImpl implements BlossomRepository {
420425

421426
final headers = <String, String>{};
422427
if (authorization != null) {
423-
headers['Authorization'] = "Nostr ${authorization.toBase64()}";
428+
headers['Authorization'] =
429+
"Nostr ${Nip01EventModel.fromEntity(authorization).toBase64()}";
424430
}
425431

426432
final response = await client.get(
@@ -466,7 +472,8 @@ class BlossomRepositoryImpl implements BlossomRepository {
466472
final response = await client.delete(
467473
url: Uri.parse('$serverUrl/$sha256'),
468474
headers: {
469-
'Authorization': "Nostr ${authorization.toBase64()}",
475+
'Authorization':
476+
"Nostr ${Nip01EventModel.fromEntity(authorization).toBase64()}",
470477
},
471478
);
472479

@@ -504,7 +511,8 @@ class BlossomRepositoryImpl implements BlossomRepository {
504511
required String sha256,
505512
required Nip01Event reportEvent,
506513
}) async {
507-
final String myBody = jsonEncode(reportEvent.toJson());
514+
final String myBody =
515+
jsonEncode(Nip01EventModel.fromEntity(reportEvent).toJson());
508516

509517
final response = await client.put(
510518
url: Uri.parse('$serverUrl/report'),

0 commit comments

Comments
 (0)