Use nostr tools to encode/decode bech32

This commit is contained in:
Jonathan Staab 2023-01-10 06:03:18 -08:00
parent 360d68856a
commit 2b5dc20def
18 changed files with 53 additions and 47 deletions

View File

@ -49,19 +49,18 @@ If you like Coracle and want to support its development, you can donate sats via
## Current
- [x] Upgrade nostr-tools
- [ ] Publish user relays using nip 23
- [ ] Use user relays for feeds
- [x] Publish user relays using nip 23
- [x] Use user relays for feeds
- [ ] Publish to user relays + target relays:
- If a reply or reaction, publish to the parent event's best relay, which is:
- e tag relay
- p tag relay
- or pubkey's recommended relays
- [ ] Add recommended relay to tags
- [ ] Add correct recommended relay to tags
- [ ] Close connections that haven't been used in a while
- [ ] Support some read/write config on relays page
- [ ] Get real home relays for default pubkeys
- [ ] Add settings storage
- [ ] Use hexToBech32 from nostr-tools
- [ ] Warn that everything will be cleared on logout
- [ ] Clear dexie on page load, we don't need any persistence other than people/relays
- [ ] Clean up login page to prefer extension, make private key entry "advanced"

BIN
package-lock.json generated

Binary file not shown.

View File

@ -22,8 +22,6 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^6.2.1",
"@noble/secp256k1": "^1.7.0",
"bech32": "^2.0.0",
"buffer": "^6.0.3",
"classnames": "^2.3.2",
"compressorjs": "^1.1.1",
"dexie": "^3.2.2",

View File

@ -13,6 +13,7 @@
import {timedelta, now} from 'src/util/misc'
import {user} from 'src/agent'
import {modal, toast, settings, alerts, getRelays} from "src/app"
import {routes} from "src/app/ui"
import Anchor from 'src/partials/Anchor.svelte'
import NoteDetail from "src/views/NoteDetail.svelte"
import PersonSettings from "src/views/PersonSettings.svelte"
@ -92,8 +93,8 @@
<Route path="/search/:type" component={Search} />
<Route path="/notes/:activeTab" component={Notes} />
<Route path="/notes/new" component={NoteCreate} />
<Route path="/people/:pubkey/:activeTab" let:params>
{#key params.pubkey}
<Route path="/people/:npub/:activeTab" let:params>
{#key params.npub}
<Person {...params} />
{/key}
</Route>
@ -113,7 +114,7 @@
>
{#if $user}
<li>
<a href={`/people/${$user.pubkey}/notes`} class="flex gap-2 px-4 py-2 pb-6 items-center">
<a href={routes.person($user.pubkey)} class="flex gap-2 px-4 py-2 pb-6 items-center">
<div
class="overflow-hidden w-6 h-6 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
style="background-image: url({$user.picture})" />

View File

@ -1,6 +1,6 @@
import Dexie from 'dexie'
import {matchFilter} from 'nostr-tools'
import {writable, get} from 'svelte/store'
import {get} from 'svelte/store'
import {groupBy, prop, flatten, pick} from 'ramda'
import {ensurePlural, switcherFn} from 'hurdak/lib/hurdak'
import {synced, now, timedelta} from 'src/util/misc'
@ -92,7 +92,7 @@ export const processEvents = async events => {
2: () => putPerson({relays: ($people[pubkey]?.relays || []).concat(content)}),
3: () => putPerson({petnames: tags}),
12165: () => putPerson({muffle: tags}),
10001: () => putPerson({relays: tags}),
10001: () => putPerson({relays: tags.map(t => t[0])}),
default: () => {
console.log(`Received unsupported event type ${event.kind}`)
},

View File

@ -44,7 +44,9 @@ const publish = async (urls, event) => {
urls.map(async url => {
const relay = await connect(url)
return relay.publish(event)
if (relay) {
return relay.publish(event)
}
})
)
}

View File

@ -48,8 +48,10 @@ export const login = async ({privkey, pubkey}) => {
export const logout = async () => {
keys.clear()
await db.tags.clear()
await db.events.clear()
await Promise.all([
db.tags.clear(),
db.events.clear(),
])
}
export const addRelay = async url => {

View File

@ -4,6 +4,7 @@ import {ensurePlural, createMap, ellipsize} from 'hurdak/lib/hurdak'
import {renderContent} from 'src/util/html'
import {filterTags, displayPerson, getTagValues, findReply, findRoot} from 'src/util/nostr'
import {db, people, getPerson} from 'src/agent'
import {routes} from "src/app/ui"
const filterEvents = async ({limit, ...filter}) => {
let events = db.events
@ -152,8 +153,9 @@ const renderNote = async (note, {showEntire = false}) => {
const pubkey = note.tags[parseInt(i)][1]
const person = peopleByPubkey[pubkey] || {pubkey}
const name = displayPerson(person)
const path = routes.person(pubkey)
return `@<a href="/people/${pubkey}/notes" class="underline">${name}</a>`
return `@<a href="${path}" class="underline">${name}</a>`
})
return content

View File

@ -1,10 +1,17 @@
import {prop} from "ramda"
import {uuid} from "hurdak/lib/hurdak"
import {navigate} from "svelte-routing"
import {nip19} from 'nostr-tools'
import {writable, get} from "svelte/store"
import {globalHistory} from "svelte-routing/src/history"
import {synced} from "src/util/misc"
// Routing
export const routes = {
person: (pubkey, tab = 'notes') => `/people/${nip19.npubEncode(pubkey)}/${tab}`,
}
// Toast
export const toast = writable(null)

View File

@ -2,6 +2,7 @@
import {Link} from 'svelte-routing'
import {killEvent} from 'src/util/html'
import {displayPerson} from 'src/util/nostr'
import {routes} from 'src/app/ui'
export let person
export let inert = false
@ -16,7 +17,7 @@
</span>
{:else}
<Link
to={`/people/${person.pubkey}/notes`}
to={routes.person(person.pubkey)}
class="flex gap-2 items-center relative z-10"
on:click={killEvent}>
<div

View File

@ -1,14 +1,12 @@
<script>
import {propEq, identity, uniq, uniqBy, prop, sortBy} from 'ramda'
import {propEq, identity, uniq, prop, sortBy} from 'ramda'
import {onMount} from 'svelte'
import {fly} from 'svelte/transition'
import {createMap} from 'hurdak/lib/hurdak'
import {now, createScroller, timedelta} from 'src/util/misc'
import {findReply, isLike} from 'src/util/nostr'
import {now, createScroller} from 'src/util/misc'
import {getPerson, user, db} from 'src/agent'
import {alerts} from 'src/app'
import query from 'src/app/query'
import Spinner from "src/partials/Spinner.svelte"
import Note from 'src/partials/Note.svelte'
import Like from 'src/partials/Like.svelte'
@ -53,7 +51,7 @@
annotatedNotes = sortBy(
e => -e.created_at,
notes
.filter(e => e.pubkey !== $user.pubkey)
.filter(e => e && e.pubkey !== $user.pubkey)
.concat(Object.values(likesById))
).slice(0, limit)
})

View File

@ -2,8 +2,8 @@
import {onMount} from "svelte"
import {fly} from 'svelte/transition'
import {navigate} from "svelte-routing"
import {nip19} from 'nostr-tools'
import {copyToClipboard} from "src/util/html"
import {hexToBech32} from "src/util/misc"
import Input from "src/partials/Input.svelte"
import Anchor from "src/partials/Anchor.svelte"
import {user} from "src/agent"
@ -13,10 +13,11 @@
const delegationUrl = 'https://github.com/nostr-protocol/nips/blob/b62aa418dee13aac1899ea7c6946a0f55dd7ee84/26.md'
const copyKey = type => {
const prefix = type === 'private' ? 'nsec' : 'npub'
const hex = type === 'private' ? $user.privkey : $user.pubkey
copyToClipboard(hexToBech32(prefix, hex))
copyToClipboard(
type === 'private'
? nip19.nsecEncode($user.privkey)
: nip19.npubEncode($user.pubkey)
)
toast.show("info", `Your ${type} key has been copied to the clipboard.`)
}
@ -41,7 +42,7 @@
<div class="flex flex-col gap-8 w-full">
<div class="flex flex-col gap-1">
<strong>Public Key</strong>
<Input disabled value={$user ? hexToBech32('npub', $user.pubkey) : ''}>
<Input disabled value={$user ? nip19.npubEncode($user.pubkey) : ''}>
<i slot="after" class="fa-solid fa-copy cursor-pointer" on:click={() => copyKey('public')} />
</Input>
<p class="text-sm text-light">
@ -52,7 +53,7 @@
{#if $user?.privkey}
<div class="flex flex-col gap-1">
<strong>Private Key</strong>
<Input disabled type="password" value={hexToBech32('nsec', $user.privkey)}>
<Input disabled type="password" value={nip19.nsecEncode($user.privkey)}>
<i slot="after" class="fa-solid fa-copy cursor-pointer" on:click={() => copyKey('private')} />
</Input>
<p class="text-sm text-light">

View File

@ -3,7 +3,7 @@
import {fade, fly} from 'svelte/transition'
import {navigate} from 'svelte-routing'
import {generatePrivateKey, getPublicKey} from 'nostr-tools'
import {hexToBech32, bech32ToHex} from "src/util/misc"
import {nip19} from 'nostr-tools'
import {copyToClipboard} from "src/util/html"
import Anchor from "src/partials/Anchor.svelte"
import Input from "src/partials/Input.svelte"
@ -27,7 +27,7 @@
}
const generateKey = () => {
nsec = hexToBech32('nsec', generatePrivateKey())
nsec = nip19.nsecEncode(generatePrivateKey())
toast.show("info", "Your private key has been re-generated.")
}
@ -50,7 +50,7 @@
}
const logInWithPrivateKey = async () => {
const privkey = nsec.startsWith('nsec') ? bech32ToHex(nsec) : nsec
const privkey = nsec.startsWith('nsec') ? nip19.decode(nsec).data : nsec
if (!privkey.match(/[a-z0-9]{64}/)) {
toast.show("error", "Sorry, but that's an invalid private key.")

View File

@ -1,6 +1,7 @@
<script>
import {find, propEq} from 'ramda'
import {onMount, onDestroy} from 'svelte'
import {nip19} from 'nostr-tools'
import {fly} from 'svelte/transition'
import {navigate} from 'svelte-routing'
import {now, batch} from 'src/util/misc'
@ -14,12 +15,14 @@
import {getPerson, listen, user} from "src/agent"
import {modal, getRelays} from "src/app"
import loaders from "src/app/loaders"
import {routes} from "src/app/ui"
import cmd from "src/app/cmd"
export let pubkey
export let npub
export let activeTab
let subs = []
let pubkey = nip19.decode(npub).data
let following = find(t => t[1] === pubkey, $user?.petnames || [])
let followers = new Set()
let followersCount = 0
@ -61,7 +64,7 @@
}
})
const setActiveTab = tab => navigate(`/people/${pubkey}/${tab}`)
const setActiveTab = tab => navigate(routes.person(pubkey, tab))
const follow = async () => {
following = true

View File

@ -10,6 +10,7 @@
import Button from "src/partials/Button.svelte"
import {user} from "src/agent"
import {toast} from "src/app"
import {routes} from "src/app/ui"
import cmd from "src/app/cmd"
let values = {picture: null, about: null, name: null}
@ -40,7 +41,7 @@
await cmd.updateUser(values)
navigate(`/people/${$user.pubkey}/profile`)
navigate(routes.person($user.pubkey, 'profile'))
toast.show("info", "Your profile has been updated!")
}

View File

@ -1,6 +1,4 @@
import LZ from 'lz-string'
import {Buffer} from 'buffer'
import {bech32} from 'bech32'
import {debounce} from 'throttle-debounce'
import {pluck, sortBy} from "ramda"
import Fuse from "fuse.js/dist/fuse.min.js"
@ -145,14 +143,6 @@ export class Cursor {
}
}
export const hexToBech32 = (prefix, hex) =>
bech32.encode(prefix, bech32.toWords(Buffer.from(hex, 'hex')))
export const bech32ToHex = b32 =>
Buffer.from(bech32.fromWords(bech32.decode(b32).words)).toString('hex')
export const synced = (key, defaultValue = null) => {
// If it's an object, merge defaults
const store = writable(

View File

@ -1,6 +1,6 @@
import {last, find, intersection} from 'ramda'
import {nip19} from 'nostr-tools'
import {ensurePlural, first} from 'hurdak/lib/hurdak'
import {hexToBech32} from 'src/util/misc'
export const epoch = 1633046400
@ -55,7 +55,7 @@ export const displayPerson = p => {
return p.name
}
return hexToBech32('npub', p.pubkey).slice(4, 12)
return nip19.npubEncode(p.pubkey).slice(4, 12)
}
export const isLike = content => ['', '+', '🤙', '👍', '❤️'].includes(content)

View File

@ -6,6 +6,7 @@
import {renderContent} from "src/util/html"
import {displayPerson} from "src/util/nostr"
import {user, people} from 'src/agent'
import {routes} from "src/app/ui"
export let q
@ -19,7 +20,7 @@
{#each search(q).slice(0, 30) as p (p.pubkey)}
{#if p.pubkey !== $user.pubkey}
<li in:fly={{y: 20}}>
<a href="/people/{p.pubkey}/notes" class="flex gap-4 my-4">
<a href={routes.person(p.pubkey)} class="flex gap-4 my-4">
<div
class="overflow-hidden w-12 h-12 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
style="background-image: url({p.picture})" />