Skip to content

Commit

Permalink
fixup! feat(suite): [wip] nostr barebones
Browse files Browse the repository at this point in the history
  • Loading branch information
mroz22 committed Dec 11, 2024
1 parent dd68277 commit f3d05e3
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 41 deletions.
21 changes: 21 additions & 0 deletions packages/connect-nostr/src/abstract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { TypedEmitter } from '@trezor/utils';

export type PeerToPeerEvent = { content: string; timestamp: number; pubKey: string };

export interface PeerToPeerCommunicationClientEvents {
event: PeerToPeerEvent;
status: 'connecting' | 'connected' | 'disconnected';
}

export abstract class PeerToPeerCommunicationClient<
T extends PeerToPeerCommunicationClientEvents,
> extends TypedEmitter<T> {
abstract connect(): Promise<void>;
abstract send({
content,
}: {
content: string;
}): Promise<{ success: false; error: string } | { success: true }>;
abstract subscribe({ pubKeys }: { pubKeys: string[] }): void;
abstract dispose(): Promise<void>;
}
56 changes: 36 additions & 20 deletions packages/connect-nostr/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,55 @@
import { getPublicKey, finalizeEvent, verifyEvent, Event } from 'nostr-tools/pure';
import {
getPublicKey,
finalizeEvent,
verifyEvent,
Event,
generateSecretKey,
} from 'nostr-tools/pure';
import { Relay } from 'nostr-tools/relay';
import * as nip19 from 'nostr-tools/nip19';
import { useWebSocketImplementation } from 'nostr-tools/pool';
import WebSocket from 'ws';

Check failure on line 11 in packages/connect-nostr/src/index.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

'ws' should be listed in the project's dependencies. Run 'npm i -S ws' to add it

Check warning on line 11 in packages/connect-nostr/src/index.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

There should be at least one empty line between import groups

import { TypedEmitter } from '@trezor/utils';
import { PeerToPeerCommunicationClient, PeerToPeerCommunicationClientEvents } from './abstract';

useWebSocketImplementation(WebSocket);

Check failure on line 14 in packages/connect-nostr/src/index.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

React Hook "useWebSocketImplementation" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function

interface NostrClientEvents {
event: Event;
status: 'connecting' | 'connected' | 'disconnected';
}

export type { Event } from 'nostr-tools/pure';

export class NostrClient extends TypedEmitter<NostrClientEvents> {
sk: Uint8Array;
pk: string;
nsec: Uint8Array;
npub: nip19.NPub;
export class NostrClient extends PeerToPeerCommunicationClient<PeerToPeerCommunicationClientEvents> {
sk?: Uint8Array;
pk?: string;
nsec?: Uint8Array;
nsecStr?: nip19.NSec;
npub?: nip19.NPub;
relay: Relay;
events: Event[] = [];

constructor({ nsecStr, relayUrl }: { nsecStr: string; relayUrl: string }) {
super();

if (nsecStr) {
this.setIdentity(nsecStr);
}

this.relay = new Relay(relayUrl);
this.emit('status', 'disconnected');
}

newIdentity() {
this.sk = generateSecretKey();
return this.setIdentity(nip19.nsecEncode(this.sk));

Check failure on line 40 in packages/connect-nostr/src/index.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

Expected blank line before this statement
}

setIdentity(nsecStr: string) {
const { data, type } = nip19.decode(nsecStr);
if (type !== 'nsec') {
throw new Error('invalid nsecStr');
}
this.sk = data;

this.pk = getPublicKey(this.sk);
const nsec = nip19.nsecEncode(this.sk);
this.nsecStr = nip19.nsecEncode(this.sk);
this.npub = nip19.npubEncode(this.pk);

this.nsec = nip19.decode(nsec).data;

this.relay = new Relay(relayUrl);
this.emit('status', 'disconnected');
this.nsec = nip19.decode(this.nsecStr).data;
}

async connect() {
Expand All @@ -50,6 +61,9 @@ export class NostrClient extends TypedEmitter<NostrClientEvents> {
}

async send({ content }: { content: string }) {
if (!this.nsec) {
return Promise.resolve({ success: false, error: 'no identity' });
}
const eventTemplate = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
Expand All @@ -65,6 +79,8 @@ export class NostrClient extends TypedEmitter<NostrClientEvents> {
console.log('isGood:', isGood);

Check failure on line 79 in packages/connect-nostr/src/index.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

Unexpected console statement
const publishRes = await this.relay.publish(signedEvent);
console.log('publishRes:', publishRes);

Check failure on line 81 in packages/connect-nostr/src/index.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

Unexpected console statement

return { success: true as true };

Check failure on line 83 in packages/connect-nostr/src/index.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

Expected a `const` instead of a literal type assertion
}

subscribe({ pubKeys }: { pubKeys: string[] }) {
Expand Down
72 changes: 51 additions & 21 deletions packages/suite/src/views/settings/SettingsDebug/Nostr.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';

import { Input, Button } from '@trezor/components';
import { NostrClient, Event } from '@trezor/connect-nostr';
Expand Down Expand Up @@ -26,7 +26,7 @@ export const Nostr = () => {
setPeerNpub(event.target.value);
};

const handleInitClick = async () => {
const initPeerToPeerClient = async () => {
const nostrClient = new NostrClient({
nsecStr: Nsec,
relayUrl: 'wss://relay.primal.net',
Expand All @@ -50,6 +50,10 @@ export const Nostr = () => {
await nostrClient.connect();
};

useEffect(() => {
initPeerToPeerClient();
}, []);

const handlePeerNpubClick = () => {
client?.subscribe({ pubKeys: [peerNpub] });
};
Expand Down Expand Up @@ -122,50 +126,76 @@ export const Nostr = () => {
return (
<>
<SectionItem>
<TextColumn
title="Example nsecs"
description="nsec1ry7mmu4chqednvud4s0nj45mkkxe9rn4mh8vlmm7c07764q7vl6s4erpcq nsec12rfalrsa6dvnxjhhf4n0d2k4rc2wc8hy49qvp34k2hj8p7cppnnq8ysujz"
/>
<TextColumn title="Client status" description={clientStatus} />
<TextColumn title="Relay" description={client?.relay.url} />
<ActionColumn>
{clientStatus === 'disconnected' && (
<Button onClick={initPeerToPeerClient}>Connect</Button>
)}
{clientStatus === 'connected' && (
<Button onClick={handleDisconnectClick}>Disconnect</Button>
)}
</ActionColumn>
</SectionItem>
<SectionItem data-testid="@settings/debug/nostr">
<TextColumn title="My Nostr keys" description="" />
<SectionItem>
<TextColumn title="Nostr identity" description="" />
<ActionColumn>
<Button
onClick={() => {
client?.newIdentity();
setNsec(client?.nsecStr!);
setMyNpub(client?.pk!);
console.log('client', client);
}}
>
Create new
</Button>
</ActionColumn>
</SectionItem>

<SectionItem>
<TextColumn title="Nsec" description="" />

<ActionColumn>
<Input
disabled={clientStatus === 'connected'}
isDisabled
placeholder="My Nsec"
value={Nsec}
onChange={handleChange}
size="small"
/>
</ActionColumn>
</SectionItem>

<SectionItem>
<TextColumn title="Pubk" description="" />
<ActionColumn>
<br />
<Input disabled={true} placeholder="My Npub" value={myNpub} size="small" />
{clientStatus === 'disconnected' && (
<Button onClick={handleInitClick}>Init</Button>
)}
{clientStatus === 'connected' && (
<Button onClick={handleDisconnectClick}>Disconnect</Button>
)}
</ActionColumn>
</SectionItem>

<SectionItem>
<TextColumn title="Peer nostr pubkey" description="" />
<TextColumn title="Peer identity" description="" />
<ActionColumn>
<Button onClick={handlePeerNpubClick}>Subscribe</Button>
</ActionColumn>
</SectionItem>

<SectionItem>
<TextColumn title="Pubk" description="" />
<ActionColumn>
<Input
placeholder="Peer Npub"
value={peerNpub}
onChange={handlePeerNpubChange}
size="small"
/>
<Button onClick={handlePeerNpubClick}>Subscribe</Button>
</ActionColumn>
</SectionItem>

{peerNpub && clientStatus === 'connected' && (
<>
<SectionItem>
<TextColumn title="Client status" description={clientStatus} />
<TextColumn title="Relay" description={client?.relay.url} />
</SectionItem>
<SectionItem>
<TextColumn title="Send message" description="Ask peer for address" />
<Button onClick={handleSendPaymentRequest} isDisabled={!unusedAddress}>
Expand Down

0 comments on commit f3d05e3

Please sign in to comment.