mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
perf(mobile): reuse the derived pubkey when signing auth events (#8460)
## Summary Each relay `/query` signs a NIP-98 event, and each relay media image GET signs a Blossom auth event. Both run on the UI isolate. `nostr`'s `Event.from` derives the pubkey from the secret key on every call, which is about a third of the cost of a signature. Both call sites now pass the pubkey that `myPubkeyProvider` already caches for the active key. The provider is derived from the same `relayConfigProvider` state as the key, so the two always match. This is the quick win. Moving signing to native secp256k1 or a worker isolate would remove the rest of the cost, and that would be a separate change. ## Before and after AOT-compiled benchmark on Apple silicon, `nostr` 2.0.0 `Event.from`, three runs of 50 signatures each: | | ms per signature | |---|---| | `main` (pubkey derived) | 8.8 to 9.6 | | this PR (pubkey passed) | 5.9 to 6.4 | A scroll that shows 20 uncached relay-media images now spends about 60 ms less on the UI thread. ## Test There are no new tests. The existing `queryRelay` test runs through the real `relayConfigProvider` and `myPubkeyProvider`, and asserts the signed event's pubkey for one identity. It does not verify the signature or switch identities, and it would still pass with this optimization removed. The media tests build the service directly, so they only cover the fallback where the pubkey is derived. Review traced the pairing instead. Both the key and the pubkey come from `relayConfigProvider`. `_postForEvents` reads both and signs with no await in between, and the media provider watches both. So the key and pubkey change together on community switch and sign-out. ## Validation - `flutter analyze` - Full mobile suite: 3,534 passed. **Human test:** Wes ran #8456, #8460 and #8462 together on the simulator with his real account and confirmed they work. buzz-review-completed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ const _mediaGetAuthRefreshMarginSeconds = 60;
|
||||
class MediaGetAuthService {
|
||||
final String _baseUrl;
|
||||
final String? _nsec;
|
||||
final String? _pubkey;
|
||||
final DateTime Function() _now;
|
||||
|
||||
Map<String, String>? _cachedHeaders;
|
||||
@@ -39,9 +40,11 @@ class MediaGetAuthService {
|
||||
MediaGetAuthService({
|
||||
required String baseUrl,
|
||||
required String? nsec,
|
||||
String? pubkey,
|
||||
DateTime Function()? now,
|
||||
}) : _baseUrl = baseUrl,
|
||||
_nsec = nsec,
|
||||
_pubkey = pubkey,
|
||||
_now = now ?? DateTime.now;
|
||||
|
||||
bool isRelayMediaUrl(String url) {
|
||||
@@ -122,6 +125,8 @@ class MediaGetAuthService {
|
||||
content: 'Get buzz-media',
|
||||
tags: tags,
|
||||
secretKey: privkeyHex,
|
||||
// Deriving the pubkey costs about a third of a signature, per image.
|
||||
pubkey: _pubkey,
|
||||
verify: false,
|
||||
);
|
||||
}
|
||||
@@ -129,7 +134,11 @@ class MediaGetAuthService {
|
||||
|
||||
final mediaGetAuthServiceProvider = Provider<MediaGetAuthService>((ref) {
|
||||
final config = ref.watch(relayConfigProvider);
|
||||
return MediaGetAuthService(baseUrl: config.baseUrl, nsec: config.nsec);
|
||||
return MediaGetAuthService(
|
||||
baseUrl: config.baseUrl,
|
||||
nsec: config.nsec,
|
||||
pubkey: ref.watch(myPubkeyProvider),
|
||||
);
|
||||
});
|
||||
|
||||
Map<String, String> mediaGetHeadersFor(WidgetRef ref, String url) {
|
||||
|
||||
@@ -245,6 +245,7 @@ class RelaySessionNotifier extends Notifier<SessionState> {
|
||||
url: url,
|
||||
bodyBytes: bodyBytes,
|
||||
nsec: config.nsec,
|
||||
pubkey: ref.read(myPubkeyProvider),
|
||||
),
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
part of 'relay_session.dart';
|
||||
|
||||
/// Pass the signer's hex [pubkey] when it is already known: deriving it from
|
||||
/// the key costs about a third of a signature, on the UI isolate.
|
||||
String buildNip98AuthHeader({
|
||||
required String method,
|
||||
required String url,
|
||||
required List<int> bodyBytes,
|
||||
required String? nsec,
|
||||
String? pubkey,
|
||||
}) {
|
||||
if (nsec == null || nsec.isEmpty) {
|
||||
throw Exception('Cannot query relay: no signing key available');
|
||||
@@ -27,6 +30,7 @@ String buildNip98AuthHeader({
|
||||
['nonce', const Uuid().v4()],
|
||||
],
|
||||
secretKey: privkeyHex,
|
||||
pubkey: pubkey,
|
||||
verify: false,
|
||||
);
|
||||
return 'Nostr ${base64.encode(utf8.encode(event.toJson()))}';
|
||||
|
||||
Reference in New Issue
Block a user