import 'dart:convert'; import 'package:bip340/bip340.dart' as bip340; import 'package:crypto/crypto.dart'; import 'package:swarm/swarm.dart'; import 'package:test/test.dart'; const _privKey = '0000000000000000000000000000000000000000000000000000000000000001'; void main() { late NostrSignerActor signer; setUp(() { signer = NostrSignerActor(_privKey); }); test('GetPublicKey returns correct pubkey', () async { final result = await signer.handle(const GetPublicKey()); expect(result, isA()); expect( (result as PublicKeyResult).publicKey, equals(bip340.getPublicKey(_privKey)), ); }); test('SignEvent returns EventSigned', () async { final result = await signer.handle( const SignEvent(kind: 1, content: 'hello'), ); expect(result, isA()); }); test('signed event has all required fields', () async { final result = await signer.handle( const SignEvent(kind: 1, tags: [], content: 'test content'), ); final event = jsonDecode((result as EventSigned).event) as Map; expect(event.containsKey('id'), isTrue); expect(event.containsKey('pubkey'), isTrue); expect(event.containsKey('created_at'), isTrue); expect(event.containsKey('kind'), isTrue); expect(event.containsKey('tags'), isTrue); expect(event.containsKey('content'), isTrue); expect(event.containsKey('sig'), isTrue); }); test('event id is correct SHA-256', () async { const kind = 1; const content = 'sha256 test'; const tags = >[]; const ts = 1700000000; final result = await signer.handle( const SignEvent(kind: kind, tags: tags, content: content, createdAt: ts), ); final event = jsonDecode((result as EventSigned).event) as Map; final pubkey = bip340.getPublicKey(_privKey); final ser = jsonEncode([0, pubkey, ts, kind, tags, content]); final expectedId = sha256 .convert(utf8.encode(ser)) .bytes .map((b) => b.toRadixString(16).padLeft(2, '0')) .join(); expect(event['id'], equals(expectedId)); }); test('signature verifies', () async { final result = await signer.handle( const SignEvent(kind: 1, content: 'verify me', createdAt: 1700000000), ); final event = jsonDecode((result as EventSigned).event) as Map; final pubkey = event['pubkey'] as String; final id = event['id'] as String; final sig = event['sig'] as String; expect(bip340.verify(pubkey, id, sig), isTrue); }); test('createdAt is used when provided', () async { const ts = 1234567890; final result = await signer.handle( const SignEvent(kind: 1, content: 'timestamp test', createdAt: ts), ); final event = jsonDecode((result as EventSigned).event) as Map; expect(event['created_at'], equals(ts)); }); test('createdAt defaults to now', () async { final before = DateTime.now().millisecondsSinceEpoch ~/ 1000; final result = await signer.handle( const SignEvent(kind: 1, content: 'now test'), ); final after = DateTime.now().millisecondsSinceEpoch ~/ 1000; final event = jsonDecode((result as EventSigned).event) as Map; final ts = event['created_at'] as int; expect(ts, greaterThanOrEqualTo(before)); expect(ts, lessThanOrEqualTo(after + 2)); }); test('Nip44Encrypt round-trips to self', () async { final plaintext = jsonEncode({'blur_content_warnings': 'false', 'x': '1'}); final encrypted = await signer.handle(Nip44Encrypt(plaintext)) as Nip44Text; expect(encrypted.value, isNot(equals(plaintext))); // Payload is base64 with the NIP-44 v2 version byte (0x02) first. final bytes = base64.decode(encrypted.value); expect(bytes.first, equals(0x02)); final decrypted = await signer.handle(Nip44Decrypt(encrypted.value)) as Nip44Text; expect(decrypted.value, equals(plaintext)); }); test('Nip44Decrypt with a foreign key fails', () async { final encrypted = await signer.handle(const Nip44Encrypt('secret')) as Nip44Text; final other = NostrSignerActor( '0000000000000000000000000000000000000000000000000000000000000002', ); expect( () => other.handle(Nip44Decrypt(encrypted.value)), throwsA(anything), ); }); }