Compare commits

..

3 Commits

Author SHA1 Message Date
fiatjaf
ac212cb5c8 tag v1.9.0 using @noble/curves. 2023-04-14 17:09:28 -03:00
Paul Miller
204ae0eff1 Switch from noble-secp256k1 to noble-curves 2023-04-14 16:45:01 -03:00
Alejandro Gomez
f17ab41d72 NIP-19: Add nrelay encoding and decoding 2023-04-14 13:26:31 -03:00
11 changed files with 853 additions and 875 deletions

View File

@@ -1,5 +1,6 @@
import * as secp256k1 from '@noble/secp256k1'
import {schnorr} from '@noble/curves/secp256k1'
import {sha256} from '@noble/hashes/sha256'
import {bytesToHex} from '@noble/hashes/utils'
import {utf8Encoder} from './utils'
import {getPublicKey} from './keys'
@@ -78,7 +79,7 @@ export function serializeEvent(evt: UnsignedEvent): string {
export function getEventHash(event: UnsignedEvent): string {
let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))
return secp256k1.utils.bytesToHex(eventHash)
return bytesToHex(eventHash)
}
const isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object
@@ -104,7 +105,7 @@ export function validateEvent<T>(event: T): event is T & UnsignedEvent {
}
export function verifySignature(event: Event): boolean {
return secp256k1.schnorr.verifySync(
return schnorr.verify(
event.sig,
getEventHash(event),
event.pubkey
@@ -112,7 +113,7 @@ export function verifySignature(event: Event): boolean {
}
export function signEvent(event: UnsignedEvent, key: string): string {
return secp256k1.utils.bytesToHex(
secp256k1.schnorr.signSync(getEventHash(event), key)
return bytesToHex(
schnorr.sign(getEventHash(event), key)
)
}

View File

@@ -17,12 +17,3 @@ export * as nip57 from './nip57'
export * as fj from './fakejson'
export * as utils from './utils'
// monkey patch secp256k1
import * as secp256k1 from '@noble/secp256k1'
import {hmac} from '@noble/hashes/hmac'
import {sha256} from '@noble/hashes/sha256'
secp256k1.utils.hmacSha256Sync = (key, ...msgs) =>
hmac(sha256, key, secp256k1.utils.concatBytes(...msgs))
secp256k1.utils.sha256Sync = (...msgs) =>
sha256(secp256k1.utils.concatBytes(...msgs))

View File

@@ -1,9 +1,10 @@
import * as secp256k1 from '@noble/secp256k1'
import {schnorr} from '@noble/curves/secp256k1'
import {bytesToHex} from '@noble/hashes/utils'
export function generatePrivateKey(): string {
return secp256k1.utils.bytesToHex(secp256k1.utils.randomPrivateKey())
return bytesToHex(schnorr.utils.randomPrivateKey())
}
export function getPublicKey(privateKey: string): string {
return secp256k1.utils.bytesToHex(secp256k1.schnorr.getPublicKey(privateKey))
return bytesToHex(schnorr.getPublicKey(privateKey))
}

View File

@@ -1,5 +1,5 @@
import {randomBytes} from '@noble/hashes/utils'
import * as secp256k1 from '@noble/secp256k1'
import {secp256k1} from '@noble/curves/secp256k1'
import {base64} from '@scure/base'
import {utf8Decoder, utf8Encoder} from './utils'

View File

@@ -1,4 +1,4 @@
import * as secp256k1 from '@noble/secp256k1'
import {bytesToHex} from '@noble/hashes/utils'
import {wordlist} from '@scure/bip39/wordlists/english.js'
import {
generateMnemonic,
@@ -14,7 +14,7 @@ export function privateKeyFromSeedWords(
let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))
let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey
if (!privateKey) throw new Error('could not derive private key')
return secp256k1.utils.bytesToHex(privateKey)
return bytesToHex(privateKey)
}
export function generateSeedWords(): string {

View File

@@ -1,8 +1,8 @@
import * as secp256k1 from '@noble/secp256k1'
import {hexToBytes} from '@noble/hashes/utils'
/** Get POW difficulty from a Nostr hex ID. */
export function getPow(id: string): number {
return getLeadingZeroBits(secp256k1.utils.hexToBytes(id))
return getLeadingZeroBits(hexToBytes(id))
}
/**

View File

@@ -100,3 +100,12 @@ test('decode naddr from go-nostr with different TLV ordering', () => {
expect(data.kind).toEqual(30023)
expect(data.identifier).toEqual('banana')
})
test('encode and decode nrelay', () => {
let url = "wss://relay.nostr.example"
let nrelay = nip19.nrelayEncode(url)
expect(nrelay).toMatch(/nrelay1\w+/)
let {type, data} = nip19.decode(nrelay)
expect(type).toEqual('nrelay')
expect(data).toEqual(url)
})

View File

@@ -1,4 +1,4 @@
import * as secp256k1 from '@noble/secp256k1'
import {bytesToHex, concatBytes, hexToBytes} from '@noble/hashes/utils'
import {bech32} from '@scure/base'
import {utf8Decoder, utf8Encoder} from './utils'
@@ -39,7 +39,7 @@ export function decode(nip19: string): {
return {
type: 'nprofile',
data: {
pubkey: secp256k1.utils.bytesToHex(tlv[0][0]),
pubkey: bytesToHex(tlv[0][0]),
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : []
}
}
@@ -54,10 +54,10 @@ export function decode(nip19: string): {
return {
type: 'nevent',
data: {
id: secp256k1.utils.bytesToHex(tlv[0][0]),
id: bytesToHex(tlv[0][0]),
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],
author: tlv[2]?.[0]
? secp256k1.utils.bytesToHex(tlv[2][0])
? bytesToHex(tlv[2][0])
: undefined
}
}
@@ -75,17 +75,27 @@ export function decode(nip19: string): {
type: 'naddr',
data: {
identifier: utf8Decoder.decode(tlv[0][0]),
pubkey: secp256k1.utils.bytesToHex(tlv[2][0]),
kind: parseInt(secp256k1.utils.bytesToHex(tlv[3][0]), 16),
pubkey: bytesToHex(tlv[2][0]),
kind: parseInt(bytesToHex(tlv[3][0]), 16),
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : []
}
}
}
case 'nrelay': {
let tlv = parseTLV(data)
if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nrelay')
return {
type: 'nrelay',
data: utf8Decoder.decode(tlv[0][0])
}
}
case 'nsec':
case 'npub':
case 'note':
return {type: prefix, data: secp256k1.utils.bytesToHex(data)}
return {type: prefix, data: bytesToHex(data)}
default:
throw new Error(`unknown prefix ${prefix}`)
@@ -122,14 +132,14 @@ export function noteEncode(hex: string): string {
}
function encodeBytes(prefix: string, hex: string): string {
let data = secp256k1.utils.hexToBytes(hex)
let data = hexToBytes(hex)
let words = bech32.toWords(data)
return bech32.encode(prefix, words, Bech32MaxSize)
}
export function nprofileEncode(profile: ProfilePointer): string {
let data = encodeTLV({
0: [secp256k1.utils.hexToBytes(profile.pubkey)],
0: [hexToBytes(profile.pubkey)],
1: (profile.relays || []).map(url => utf8Encoder.encode(url))
})
let words = bech32.toWords(data)
@@ -138,9 +148,9 @@ export function nprofileEncode(profile: ProfilePointer): string {
export function neventEncode(event: EventPointer): string {
let data = encodeTLV({
0: [secp256k1.utils.hexToBytes(event.id)],
0: [hexToBytes(event.id)],
1: (event.relays || []).map(url => utf8Encoder.encode(url)),
2: event.author ? [secp256k1.utils.hexToBytes(event.author)] : []
2: event.author ? [hexToBytes(event.author)] : []
})
let words = bech32.toWords(data)
return bech32.encode('nevent', words, Bech32MaxSize)
@@ -153,13 +163,21 @@ export function naddrEncode(addr: AddressPointer): string {
let data = encodeTLV({
0: [utf8Encoder.encode(addr.identifier)],
1: (addr.relays || []).map(url => utf8Encoder.encode(url)),
2: [secp256k1.utils.hexToBytes(addr.pubkey)],
2: [hexToBytes(addr.pubkey)],
3: [new Uint8Array(kind)]
})
let words = bech32.toWords(data)
return bech32.encode('naddr', words, Bech32MaxSize)
}
export function nrelayEncode(url: string): string {
let data = encodeTLV({
0: [utf8Encoder.encode(url)]
})
let words = bech32.toWords(data)
return bech32.encode('nrelay', words, Bech32MaxSize)
}
function encodeTLV(tlv: TLV): Uint8Array {
let entries: Uint8Array[] = []
@@ -173,5 +191,5 @@ function encodeTLV(tlv: TLV): Uint8Array {
})
})
return secp256k1.utils.concatBytes(...entries)
return concatBytes(...entries)
}

View File

@@ -1,4 +1,5 @@
import * as secp256k1 from '@noble/secp256k1'
import {schnorr} from '@noble/curves/secp256k1'
import {bytesToHex} from '@noble/hashes/utils'
import {sha256} from '@noble/hashes/sha256'
import {Event} from './event'
@@ -36,8 +37,8 @@ export function createDelegation(
utf8Encoder.encode(`nostr:delegation:${parameters.pubkey}:${cond}`)
)
let sig = secp256k1.utils.bytesToHex(
secp256k1.schnorr.signSync(sighash, privateKey)
let sig = bytesToHex(
schnorr.sign(sighash, privateKey)
)
return {
@@ -84,7 +85,7 @@ export function getDelegator(event: Event): string | null {
let sighash = sha256(
utf8Encoder.encode(`nostr:delegation:${event.pubkey}:${cond}`)
)
if (!secp256k1.schnorr.verifySync(sig, sighash, pubkey)) return null
if (!schnorr.verify(sig, sighash, pubkey)) return null
return pubkey
}

View File

@@ -1,6 +1,6 @@
{
"name": "nostr-tools",
"version": "1.8.4",
"version": "1.9.0",
"description": "Tools for making a Nostr client.",
"repository": {
"type": "git",
@@ -18,11 +18,11 @@
},
"license": "Public domain",
"dependencies": {
"@noble/hashes": "1.2.0",
"@noble/secp256k1": "1.7.0",
"@noble/curves": "1.0.0",
"@noble/hashes": "1.3.0",
"@scure/base": "1.1.1",
"@scure/bip32": "1.1.4",
"@scure/bip39": "1.1.1"
"@scure/bip32": "1.3.0",
"@scure/bip39": "1.2.0"
},
"keywords": [
"decentralization",

1619
yarn.lock

File diff suppressed because it is too large Load Diff