mirror of
https://github.com/irislib/iris-messenger.git
synced 2024-10-18 06:03:22 +00:00
Hex, PublicKey and EventID classes
This commit is contained in:
parent
de72141343
commit
a7ead39015
@ -183,7 +183,7 @@ class SearchBox extends Component<Props, State> {
|
|||||||
Key.getPubKeyByNip05Address(query).then((pubKey) => {
|
Key.getPubKeyByNip05Address(query).then((pubKey) => {
|
||||||
// if query hasn't changed since we started the request
|
// if query hasn't changed since we started the request
|
||||||
if (pubKey && query === String(this.props.query || this.inputRef.current.value)) {
|
if (pubKey && query === String(this.props.query || this.inputRef.current.value)) {
|
||||||
this.props.onSelect?.({ key: pubKey });
|
this.props.onSelect?.({ key: pubKey.toHex() });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ import {
|
|||||||
} from 'nostr-tools';
|
} from 'nostr-tools';
|
||||||
import { route } from 'preact-router';
|
import { route } from 'preact-router';
|
||||||
|
|
||||||
|
import { PublicKey } from '@/utils/Hex.ts';
|
||||||
|
|
||||||
import localState from '../state/LocalState.ts';
|
import localState from '../state/LocalState.ts';
|
||||||
import Helpers from '../utils/Helpers';
|
import Helpers from '../utils/Helpers';
|
||||||
|
|
||||||
@ -193,14 +195,14 @@ export default {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getPubKeyByNip05Address(address: string): Promise<string | null> {
|
async getPubKeyByNip05Address(address: string): Promise<PublicKey | null> {
|
||||||
try {
|
try {
|
||||||
const [localPart, domain] = address.split('@');
|
const [localPart, domain] = address.split('@');
|
||||||
const url = `https://${domain}/.well-known/nostr.json?name=${localPart}`;
|
const url = `https://${domain}/.well-known/nostr.json?name=${localPart}`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
const names = json.names;
|
const names = json.names;
|
||||||
return names[localPart] || null;
|
return new PublicKey(names[localPart]) || null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return null;
|
return null;
|
||||||
|
57
src/js/utils/Hex.test.ts
Normal file
57
src/js/utils/Hex.test.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { EventID, PublicKey } from '@/utils/Hex';
|
||||||
|
|
||||||
|
describe('PublicKey', () => {
|
||||||
|
it('should convert npub bech32 to hex', () => {
|
||||||
|
const bech32 = 'npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk';
|
||||||
|
const hex = '4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0';
|
||||||
|
const publicKey = new PublicKey(bech32);
|
||||||
|
expect(publicKey.toHex()).toEqual(hex);
|
||||||
|
expect(publicKey.toBech32()).toEqual(bech32);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should init from hex', () => {
|
||||||
|
const hex = '4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0';
|
||||||
|
const publicKey = new PublicKey(hex);
|
||||||
|
expect(publicKey.toHex()).toEqual(hex);
|
||||||
|
expect(publicKey.toBech32()).toEqual(
|
||||||
|
'npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail with too long hex', () => {
|
||||||
|
const hex =
|
||||||
|
'4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd04523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0';
|
||||||
|
expect(() => new PublicKey(hex)).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('equals(hexStr)', () => {
|
||||||
|
const hex = '4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0';
|
||||||
|
const publicKey = new PublicKey(hex);
|
||||||
|
expect(publicKey.equals(hex)).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('equals(PublicKey)', () => {
|
||||||
|
const hex = '4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0';
|
||||||
|
const publicKey = new PublicKey(hex);
|
||||||
|
const publicKey2 = new PublicKey(hex);
|
||||||
|
expect(publicKey.equals(publicKey2)).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('equals(bech32)', () => {
|
||||||
|
const bech32 = 'npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk';
|
||||||
|
const publicKey = new PublicKey(bech32);
|
||||||
|
expect(publicKey.equals(bech32)).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EventID', () => {
|
||||||
|
it('should convert note id bech32 to hex', () => {
|
||||||
|
const noteBech32 = 'note1wdyajan9c9d72wanqe2l34lxgdu3q5esglhquusfkg34fqq6462qh4cjd5';
|
||||||
|
const noteHex = '7349d97665c15be53bb30655f8d7e6437910533047ee0e7209b22354801aae94';
|
||||||
|
const eventId = new EventID(noteBech32);
|
||||||
|
expect(eventId.toHex()).toEqual(noteHex);
|
||||||
|
expect(eventId.toBech32()).toEqual(noteBech32);
|
||||||
|
});
|
||||||
|
});
|
88
src/js/utils/Hex.ts
Normal file
88
src/js/utils/Hex.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import * as bech32 from 'bech32-buffer';
|
||||||
|
|
||||||
|
import Helpers from '@/utils/Helpers.tsx';
|
||||||
|
|
||||||
|
function bech32ToHex(str: string): string {
|
||||||
|
try {
|
||||||
|
const { data } = bech32.decode(str);
|
||||||
|
const addr = Helpers.arrayToHex(data);
|
||||||
|
return addr;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('The provided string is not a valid bech32 address: ' + str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Hex {
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
constructor(str: string, expectedLength?: number) {
|
||||||
|
this.validateHex(str, expectedLength);
|
||||||
|
this.value = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateHex(str: string, expectedLength?: number): void {
|
||||||
|
if (!/^[0-9a-fA-F]+$/.test(str)) {
|
||||||
|
throw new Error(`The provided string is not a valid hex value: "${str}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedLength && str.length !== expectedLength) {
|
||||||
|
throw new Error(
|
||||||
|
`The provided hex value does not match the expected length of ${expectedLength} characters: ${str}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toBech32(prefix: string): string {
|
||||||
|
if (!prefix) {
|
||||||
|
throw new Error('prefix is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const bytesArray = this.value.match(/.{1,2}/g);
|
||||||
|
const bytes = new Uint8Array(bytesArray!.map((byte) => parseInt(byte, 16)));
|
||||||
|
return bech32.encode(prefix, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
toHex(): string {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EventID extends Hex {
|
||||||
|
constructor(str: string) {
|
||||||
|
if (str.startsWith('note')) {
|
||||||
|
str = bech32ToHex(str);
|
||||||
|
}
|
||||||
|
super(str, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
toBech32(): string {
|
||||||
|
return super.toBech32('note');
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(other: EventID | string): boolean {
|
||||||
|
if (typeof other === 'string') {
|
||||||
|
other = new EventID(other);
|
||||||
|
}
|
||||||
|
return this.value === other.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PublicKey extends Hex {
|
||||||
|
constructor(str: string) {
|
||||||
|
if (str.startsWith('npub')) {
|
||||||
|
str = bech32ToHex(str);
|
||||||
|
}
|
||||||
|
super(str, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
toBech32(): string {
|
||||||
|
return super.toBech32('npub');
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(other: PublicKey | string): boolean {
|
||||||
|
if (typeof other === 'string') {
|
||||||
|
other = new PublicKey(other);
|
||||||
|
}
|
||||||
|
return this.value === other.value;
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,16 @@
|
|||||||
import { useEffect } from 'preact/hooks';
|
import { useEffect } from 'preact/hooks';
|
||||||
import { route } from 'preact-router';
|
import { route } from 'preact-router';
|
||||||
|
|
||||||
|
import { EventID } from '@/utils/Hex.ts';
|
||||||
import View from '@/views/View.tsx';
|
import View from '@/views/View.tsx';
|
||||||
|
|
||||||
import CreateNoteForm from '../components/create/CreateNoteForm';
|
import CreateNoteForm from '../components/create/CreateNoteForm';
|
||||||
import EventComponent from '../components/events/EventComponent';
|
import EventComponent from '../components/events/EventComponent';
|
||||||
import Key from '../nostr/Key';
|
|
||||||
import { translate as t } from '../translations/Translation.mjs';
|
import { translate as t } from '../translations/Translation.mjs';
|
||||||
|
|
||||||
const Note = (props) => {
|
const Note = (props) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const nostrBech32Id = Key.toNostrBech32Address(props.id, 'note');
|
const nostrBech32Id = new EventID(props.id).toBech32();
|
||||||
if (nostrBech32Id && props.id !== nostrBech32Id) {
|
if (nostrBech32Id && props.id !== nostrBech32Id) {
|
||||||
route(`/${nostrBech32Id}`, true);
|
route(`/${nostrBech32Id}`, true);
|
||||||
return;
|
return;
|
||||||
|
@ -5,6 +5,7 @@ import SimpleImageModal from '@/components/modal/Image.tsx';
|
|||||||
import { useProfile } from '@/nostr/hooks/useProfile.ts';
|
import { useProfile } from '@/nostr/hooks/useProfile.ts';
|
||||||
import { getEventReplyingTo, isRepost } from '@/nostr/utils.ts';
|
import { getEventReplyingTo, isRepost } from '@/nostr/utils.ts';
|
||||||
import useLocalState from '@/state/useLocalState.ts';
|
import useLocalState from '@/state/useLocalState.ts';
|
||||||
|
import { PublicKey } from '@/utils/Hex.ts';
|
||||||
import ProfileHelmet from '@/views/profile/Helmet.tsx';
|
import ProfileHelmet from '@/views/profile/Helmet.tsx';
|
||||||
|
|
||||||
import Feed from '../../components/feed/Feed.tsx';
|
import Feed from '../../components/feed/Feed.tsx';
|
||||||
@ -60,21 +61,20 @@ function Profile(props) {
|
|||||||
}, [profile]);
|
}, [profile]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const pub = props.id;
|
try {
|
||||||
const npubComputed = Key.toNostrBech32Address(pub, 'npub');
|
const pub = new PublicKey(props.id);
|
||||||
|
const npubComputed = pub.toBech32();
|
||||||
|
|
||||||
if (npubComputed && npubComputed !== pub) {
|
if (npubComputed !== props.id) {
|
||||||
route(`/${npubComputed}`, true);
|
route(`/${npubComputed}`, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hexPubComputed = Key.toNostrHexAddress(pub) || '';
|
setHexPub(pub.toHex());
|
||||||
|
setNpub(npubComputed);
|
||||||
|
} catch (e) {
|
||||||
|
let nostrAddress = props.id;
|
||||||
|
|
||||||
if (hexPubComputed) {
|
|
||||||
setHexPub(hexPubComputed);
|
|
||||||
setNpub(Key.toNostrBech32Address(hexPubComputed, 'npub') || '');
|
|
||||||
} else {
|
|
||||||
let nostrAddress = pub;
|
|
||||||
if (!nostrAddress.match(/.+@.+\..+/)) {
|
if (!nostrAddress.match(/.+@.+\..+/)) {
|
||||||
if (nostrAddress.match(/.+\..+/)) {
|
if (nostrAddress.match(/.+\..+/)) {
|
||||||
nostrAddress = '_@' + nostrAddress;
|
nostrAddress = '_@' + nostrAddress;
|
||||||
@ -85,11 +85,8 @@ function Profile(props) {
|
|||||||
|
|
||||||
Key.getPubKeyByNip05Address(nostrAddress).then((pubKey) => {
|
Key.getPubKeyByNip05Address(nostrAddress).then((pubKey) => {
|
||||||
if (pubKey) {
|
if (pubKey) {
|
||||||
const npubComputed = Key.toNostrBech32Address(pubKey, 'npub');
|
setNpub(pubKey.toBech32());
|
||||||
if (npubComputed && npubComputed !== pubKey) {
|
setHexPub(pubKey.toHex());
|
||||||
setNpub(npubComputed);
|
|
||||||
setHexPub(pubKey);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setNpub(''); // To indicate not found
|
setNpub(''); // To indicate not found
|
||||||
}
|
}
|
||||||
@ -99,6 +96,7 @@ function Profile(props) {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.prerenderReady = true;
|
window.prerenderReady = true;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
setIsMyProfile(false);
|
setIsMyProfile(false);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user