Skip to content

Commit

Permalink
fixup! 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 f3d05e3 commit 498de4d
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 64 deletions.
77 changes: 52 additions & 25 deletions packages/connect-nostr/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import {
getPublicKey,
finalizeEvent,
verifyEvent,
Event,
generateSecretKey,
} from 'nostr-tools/pure';
import { Relay } from 'nostr-tools/relay';
import { getPublicKey, finalizeEvent, Event, generateSecretKey } from 'nostr-tools/pure';
import { Relay, Subscription } from 'nostr-tools/relay';
import * as nip19 from 'nostr-tools/nip19';
import { useWebSocketImplementation } from 'nostr-tools/pool';
import WebSocket from 'ws';
import { PeerToPeerCommunicationClient, PeerToPeerCommunicationClientEvents } from './abstract';

useWebSocketImplementation(WebSocket);
import { createDeferredManager } from '@trezor/utils';
import { PeerToPeerCommunicationClient, PeerToPeerCommunicationClientEvents } from './abstract';

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

useWebSocketImplementation(WebSocket);

export class NostrClient extends PeerToPeerCommunicationClient<PeerToPeerCommunicationClientEvents> {
sk?: Uint8Array;
pk?: string;
Expand All @@ -23,10 +19,15 @@ export class NostrClient extends PeerToPeerCommunicationClient<PeerToPeerCommuni
npub?: nip19.NPub;
relay: Relay;
events: Event[] = [];
subscription?: Subscription;

private readonly messages;

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

this.messages = createDeferredManager();

if (nsecStr) {
this.setIdentity(nsecStr);
}
Expand Down Expand Up @@ -60,10 +61,12 @@ export class NostrClient extends PeerToPeerCommunicationClient<PeerToPeerCommuni
console.log(`connected to ${this.relay.url}`);
}

async send({ content }: { content: string }) {
buildMessage({ content }: { content: string }) {
if (!this.nsec) {
return Promise.resolve({ success: false, error: 'no identity' });
// return { success: false, error: 'no identity' };
throw new Error('no identity');
}

const eventTemplate = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
Expand All @@ -73,34 +76,58 @@ export class NostrClient extends PeerToPeerCommunicationClient<PeerToPeerCommuni

// this assigns the pubkey, calculates the event id and signs the event in a single step
const signedEvent = finalizeEvent(eventTemplate, this.nsec);
const isGood = verifyEvent(signedEvent);
return signedEvent;
}

console.log('signed event', signedEvent);
console.log('isGood:', isGood);
const publishRes = await this.relay.publish(signedEvent);
console.log('publishRes:', publishRes);
async send({ content }: { content: string }) {
const signedEvent = this.buildMessage({ content });

await this.relay.publish(signedEvent);
}

async request({ content }: { content: string }) {
const { promiseId, promise } = this.messages.create();

const json = JSON.parse(content);
json.id = promiseId.toString();
const signedEvent = this.buildMessage({ content: JSON.stringify(json) });

return { success: true as true };
await this.relay.publish(signedEvent);

return promise;
}

subscribe({ pubKeys }: { pubKeys: string[] }) {
this.relay.subscribe(
subscribe({ pubKeys }: { pubKeys: nip19.NPub[] }) {
console.log('callling subscribe');
console.log('subscriribtion', this.subscription);
if (this.subscription) {
this.subscription.close();
}
this.subscription = this.relay.subscribe(
[
{
kinds: [1],
authors: pubKeys,
authors: pubKeys.map(k => nip19.decode(k).data),
limit: 1,
},
],
{
onevent: event => {
this.emit('event', event);

const resp = JSON.parse(event.content);
console.log('on event parsed', resp);
const { request_id, ...data } = resp;

if (typeof request_id !== 'undefined') {
this.messages.resolve(Number(request_id), data);
}
this.events.push(event);
},
// oneose() {
// console.log('=====oneose====');
// subscription.close();
// },
oneose() {
// "End of Stored Events".
console.log('=====all stored events processed====');
},
},
);
}
Expand Down
128 changes: 89 additions & 39 deletions packages/suite/src/views/settings/SettingsDebug/Nostr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ export const Nostr = () => {
const [client, setClient] = useState<NostrClient | null>(null);
const [events, setEvents] = useState<Event>();
const [clientStatus, setClientStatus] = useState('disconnected');
const [addressRequestState, setAddressRequestState] = useState<'none' | 'pending' | 'success'>(
'none',
);

const unusedAddress = useSelector(
state => state.wallet.accounts[0]?.addresses?.unused[0].address,
);

const handleChange = (event: any) => {
setNsec(event.target.value);
};
Expand All @@ -32,7 +37,9 @@ export const Nostr = () => {
relayUrl: 'wss://relay.primal.net',
});
setClient(nostrClient);
setMyNpub(nostrClient.pk);
if (nostrClient.npub) {
setMyNpub(nostrClient.npub);
}

nostrClient.on('event', message => {
console.log('message', message);
Expand Down Expand Up @@ -61,7 +68,7 @@ export const Nostr = () => {
const handleDisconnectClick = async () => {
client?.dispose();
};
const handleSendPaymentRequest = () => {
const handleSendPaymentData = () => {
client?.send({
content: JSON.stringify({
type: 'payment_request',
Expand All @@ -71,50 +78,68 @@ export const Nostr = () => {
};

const handleSendAddressRequest = () => {
client?.send({
content: JSON.stringify({
type: 'address_request',
}),
});
setAddressRequestState('pending');
client
?.request({
content: JSON.stringify({
type: 'address_request',
}),
})
.then(result => {
console.log('result', result);
setAddressRequestState('success');
});
};

const handleAddressRequestResponse = () => {
const handleAddressRequestResponse = (content: any) => {
console.log('handleAddressRequestResponse', content);

client?.send({
content: JSON.stringify({
type: 'address_response',
request_id: content.id,
payload: unusedAddress,
}),
});
};

const PeerRequest = ({ created_at, pubkey, content }: any) => {
const PeerRequest = ({ pubkey, content }: any) => {
if (content.type === 'payment_request') {
return (
<div>
<p>timestamp: {created_at} </p>
<p>from {pubkey}</p>
<p>
<SectionItem>
<TextColumn
title="Payment request"
description={`received payment data (invoice) from a peer. Peer pubkey: ${pubkey}`}
/>
<ActionColumn>
<a href={content.payload}>{content.payload}</a>
</p>
</div>
</ActionColumn>
</SectionItem>
);
}

if (content.type === 'address_request') {
return (
<div>
<p>timestamp: {created_at} </p>
<p>from {pubkey}</p>
<Button onClick={handleAddressRequestResponse}>Send address back</Button>
</div>
<SectionItem>
<TextColumn
title={`Address request`}
description={`received address request from a peer. Peer pubkey: ${pubkey}`}
/>

<ActionColumn>
<Button onClick={() => handleAddressRequestResponse(content)}>
Send address back
</Button>
</ActionColumn>
</SectionItem>
);
}

if (content.type === 'address_response') {
return (
<div>
<p>yay, we received address: {content.payload} </p>
</div>
<SectionItem>
<TextColumn title={`Address response`} description={content.payload} />
</SectionItem>
);
}

Expand Down Expand Up @@ -144,7 +169,7 @@ export const Nostr = () => {
onClick={() => {
client?.newIdentity();
setNsec(client?.nsecStr!);
setMyNpub(client?.pk!);
setMyNpub(client?.npub!);
console.log('client', client);
}}
>
Expand All @@ -168,7 +193,7 @@ export const Nostr = () => {
</SectionItem>

<SectionItem>
<TextColumn title="Pubk" description="" />
<TextColumn title="Npub" description="" />
<ActionColumn>
<br />
<Input disabled={true} placeholder="My Npub" value={myNpub} size="small" />
Expand All @@ -183,7 +208,7 @@ export const Nostr = () => {
</SectionItem>

<SectionItem>
<TextColumn title="Pubk" description="" />
<TextColumn title="Npub" description="" />
<ActionColumn>
<Input
placeholder="Peer Npub"
Expand All @@ -194,22 +219,47 @@ export const Nostr = () => {
</ActionColumn>
</SectionItem>

{peerNpub && clientStatus === 'connected' && (
<>
<SectionItem>
<TextColumn title="Send message" description="Ask peer for address" />
<Button onClick={handleSendPaymentRequest} isDisabled={!unusedAddress}>
Send payment request
</Button>
<Button onClick={handleSendAddressRequest} isDisabled={!unusedAddress}>
Send address request
</Button>
</SectionItem>
<SectionItem>
<TextColumn
title="Send invoice"
description="send an invoice and do not wait for response"
/>
<ActionColumn>
<Button
isDisabled={!unusedAddress || !peerNpub}
onClick={handleSendPaymentData}
>
Send payment data
</Button>
</ActionColumn>
</SectionItem>

<SectionItem>
<TextColumn
title="Send address request"
description="Ask peer for address. Wait for response"
/>

<ActionColumn>
<Button
onClick={handleSendAddressRequest}
isLoading={addressRequestState === 'pending'}
isDisabled={!peerNpub || addressRequestState === 'pending'}
>
Send address request
</Button>
</ActionColumn>
</SectionItem>

{clientStatus === 'connected' && (
<>
<SectionItem>
<TextColumn title="Last message received" description="" />
{events && <PeerRequest {...events} />}
<TextColumn
title="Inbox"
description="The last message received from your peer"
/>
</SectionItem>
{events && <PeerRequest {...events} />}
</>
)}
</>
Expand Down

0 comments on commit 498de4d

Please sign in to comment.