provider nprofile

This commit is contained in:
shocknet-justin 2025-12-19 01:32:39 -05:00
parent 602146fa2c
commit 2f4713ebae
8 changed files with 121 additions and 19 deletions

73
decode.js Executable file
View file

@ -0,0 +1,73 @@
#!/usr/bin/env node
import { decodeBech32, nip19 } from '@shocknet/clink-sdk'
const nip19String = process.argv[2]
if (!nip19String) {
console.error('Usage: node decode.js <nip19_string>')
console.error('Example: node decode.js nprofile1qyd8wumn8ghj7um5wfn8y7fwwd5x7cmt9ehx2arhdaexkqpqwmk5tuqvafa6ckwc6zmaypyy3af3n4aeds2ql7m0ew42kzsn638q9s9z8p')
process.exit(1)
}
try {
// Check prefix to determine which decoder to use
const prefix = nip19String.split('1')[0]
const decoded = (prefix === 'noffer' || prefix === 'ndebit' || prefix === 'nmanage')
? decodeBech32(nip19String)
: nip19.decode(nip19String)
console.log('\nDecoded:')
console.log('Type:', decoded.type)
console.log('\nData:')
console.log(JSON.stringify(decoded.data, null, 2))
if (decoded.type === 'nprofile') {
console.log('\nDetails:')
console.log(' Pubkey:', decoded.data.pubkey)
if (decoded.data.relays) {
console.log(' Relays:')
decoded.data.relays.forEach((relay, i) => {
console.log(` ${i + 1}. ${relay}`)
})
}
} else if (decoded.type === 'npub') {
console.log('\nDetails:')
console.log(' Pubkey:', decoded.data)
} else if (decoded.type === 'nsec') {
console.log('\nDetails:')
console.log(' Private Key:', decoded.data)
} else if (decoded.type === 'note') {
console.log('\nDetails:')
console.log(' Event ID:', decoded.data)
} else if (decoded.type === 'noffer') {
console.log('\nDetails:')
console.log(' Pubkey:', decoded.data.pubkey)
console.log(' Offer:', decoded.data.offer)
if (decoded.data.relay) {
console.log(' Relay:', decoded.data.relay)
}
if (decoded.data.priceType) {
console.log(' Price Type:', decoded.data.priceType)
}
} else if (decoded.type === 'ndebit') {
console.log('\nDetails:')
console.log(' Pubkey:', decoded.data.pubkey)
console.log(' Pointer:', decoded.data.pointer)
if (decoded.data.relay) {
console.log(' Relay:', decoded.data.relay)
}
} else if (decoded.type === 'nmanage') {
console.log('\nDetails:')
console.log(' Pubkey:', decoded.data.pubkey)
console.log(' Pointer:', decoded.data.pointer)
if (decoded.data.relay) {
console.log(' Relay:', decoded.data.relay)
}
}
} catch (error) {
console.error('Error decoding bech32 string:', error.message)
process.exit(1)
}

View file

@ -16,11 +16,11 @@
#BOOTSTRAP_PEER #BOOTSTRAP_PEER
# A trusted peer that will hold a node-level account until channel automation becomes affordable # A trusted peer that will hold a node-level account until channel automation becomes affordable
# The developer is used by default or you may specify your own # The provider pubkey is extracted from PROVIDER_NPROFILE (nprofile contains both pubkey and relay URL)
# To disable this feature entirely overwrite the env with "null" # The developers node is used by default, or you may specify another.
#LIQUIDITY_PROVIDER_PUB=null # To disable this feature entirely set DISABLE_LIQUIDITY_PROVIDER=true
#DISABLE_LIQUIDITY_PROVIDER=false #DISABLE_LIQUIDITY_PROVIDER=false
#PROVIDER_RELAY_URL= #PROVIDER_NPROFILE=nprofile1qyd8wumn8ghj7um5wfn8y7fwwd5x7cmt9ehx2arhdaexkqpqwmk5tuqvafa6ckwc6zmaypyy3af3n4aeds2ql7m0ew42kzsn638q9s9z8p
#SWAPS #SWAPS
#BOLTZ_HTTP_URL= #BOLTZ_HTTP_URL=

View file

@ -35,8 +35,8 @@ const start = async () => {
name: app.name, name: app.name,
provider: app.publicKey === liquidityProviderInfo.publicKey ? { provider: app.publicKey === liquidityProviderInfo.publicKey ? {
clientId: liquidityProviderInfo.clientId, clientId: liquidityProviderInfo.clientId,
pubDestination: settingsManager.getSettings().liquiditySettings.liquidityProviderPub, pubkey: settingsManager.getSettings().liquiditySettings.liquidityProviderPub,
relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl || relays[0] relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl
} : undefined } : undefined
} }
}) })

View file

@ -36,8 +36,8 @@ const start = async () => {
name: app.name, name: app.name,
provider: app.publicKey === liquidityProviderInfo.publicKey ? { provider: app.publicKey === liquidityProviderInfo.publicKey ? {
clientId: liquidityProviderInfo.clientId, clientId: liquidityProviderInfo.clientId,
pubDestination: settingsManager.getSettings().liquiditySettings.liquidityProviderPub, pubkey: settingsManager.getSettings().liquiditySettings.liquidityProviderPub,
relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl || relays[0] relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl
} : undefined } : undefined
} }
}) })

View file

@ -486,8 +486,8 @@ export default class {
name: app.name, name: app.name,
provider: app.nostr_public_key === liquidityProviderInfo.publicKey ? { provider: app.nostr_public_key === liquidityProviderInfo.publicKey ? {
clientId: liquidityProviderInfo.clientId, clientId: liquidityProviderInfo.clientId,
pubDestination: this.settings.getSettings().liquiditySettings.liquidityProviderPub, pubkey: this.settings.getSettings().liquiditySettings.liquidityProviderPub,
relayUrl: this.settings.getSettings().liquiditySettings.providerRelayUrl || relays[0] relayUrl: this.settings.getSettings().liquiditySettings.providerRelayUrl
} : undefined } : undefined
} }
}) })

View file

@ -298,6 +298,7 @@ export class LiquidityProvider {
} }
} }
onBeaconEvent = async (beaconData: { content: string, pub: string }) => { onBeaconEvent = async (beaconData: { content: string, pub: string }) => {
this.log("received beacon event from", beaconData.pub, "expected", this.pubDestination)
if (beaconData.pub !== this.pubDestination) { if (beaconData.pub !== this.pubDestination) {
this.log(ERROR, "got beacon from invalid pub", beaconData.pub, this.pubDestination) this.log(ERROR, "got beacon from invalid pub", beaconData.pub, this.pubDestination)
return return
@ -312,10 +313,13 @@ export class LiquidityProvider {
this.log(ERROR, "got beacon from invalid type", beacon.type) this.log(ERROR, "got beacon from invalid type", beacon.type)
return return
} }
this.log("valid beacon received, updating ready state")
this.lastSeenBeacon = Date.now() this.lastSeenBeacon = Date.now()
this.ready = true
if (beacon.fees) { if (beacon.fees) {
this.feesCache = beacon.fees this.feesCache = beacon.fees
} }
this.queue.forEach(q => q('ready'))
} }
onEvent = async (res: { requestId: string }, fromPub: string) => { onEvent = async (res: { requestId: string }, fromPub: string) => {

View file

@ -1,6 +1,7 @@
import { EnvCacher, EnvMustBeNonEmptyString, EnvMustBeInteger, chooseEnv, chooseEnvBool, chooseEnvInt } from '../helpers/envParser.js' import { EnvCacher, EnvMustBeNonEmptyString, EnvMustBeInteger, chooseEnv, chooseEnvBool, chooseEnvInt } from '../helpers/envParser.js'
import os from 'os' import os from 'os'
import path from 'path' import path from 'path'
import { nip19 } from '@shocknet/clink-sdk'
export type ServiceFeeSettings = { export type ServiceFeeSettings = {
serviceFee: number serviceFee: number
@ -169,11 +170,32 @@ export type LiquiditySettings = {
providerRelayUrl: string providerRelayUrl: string
} }
export const LoadLiquiditySettingsFromEnv = (dbEnv: Record<string, string | undefined>, addToDb?: EnvCacher): LiquiditySettings => { export const LoadLiquiditySettingsFromEnv = (dbEnv: Record<string, string | undefined>, addToDb?: EnvCacher): LiquiditySettings => {
//const liquidityProviderPub = process.env.LIQUIDITY_PROVIDER_PUB === "null" ? "" : (process.env.LIQUIDITY_PROVIDER_PUB || "76ed45f00cea7bac59d8d0b7d204848f5319d7b96c140ffb6fcbaaab0a13d44e") const providerNprofile = chooseEnv("PROVIDER_NPROFILE", dbEnv, "nprofile1qyd8wumn8ghj7um5wfn8y7fwwd5x7cmt9ehx2arhdaexkqpqwmk5tuqvafa6ckwc6zmaypyy3af3n4aeds2ql7m0ew42kzsn638q9s9z8p", addToDb)
const liquidityProviderPub = chooseEnv("LIQUIDITY_PROVIDER_PUB", dbEnv, "76ed45f00cea7bac59d8d0b7d204848f5319d7b96c140ffb6fcbaaab0a13d44e", addToDb)
// Decode nprofile to extract pubkey and relay URL
let liquidityProviderPub = ""
let providerRelayUrl = ""
if (providerNprofile) {
try {
const decoded = nip19.decode(providerNprofile)
if (decoded.type === 'nprofile') {
liquidityProviderPub = decoded.data.pubkey
if (decoded.data.relays && decoded.data.relays.length > 0) {
providerRelayUrl = decoded.data.relays[0]
} else {
throw new Error("PROVIDER_NPROFILE must contain at least one relay")
}
} else {
throw new Error("PROVIDER_NPROFILE must be a valid nprofile")
}
} catch (e) {
throw new Error(`Failed to decode PROVIDER_NPROFILE as nprofile: ${e}`)
}
}
const disableLiquidityProvider = chooseEnvBool("DISABLE_LIQUIDITY_PROVIDER", dbEnv, false, addToDb) || liquidityProviderPub === "null" const disableLiquidityProvider = chooseEnvBool("DISABLE_LIQUIDITY_PROVIDER", dbEnv, false, addToDb) || liquidityProviderPub === "null"
const useOnlyLiquidityProvider = chooseEnvBool("USE_ONLY_LIQUIDITY_PROVIDER", dbEnv, false, addToDb) const useOnlyLiquidityProvider = chooseEnvBool("USE_ONLY_LIQUIDITY_PROVIDER", dbEnv, false, addToDb)
const providerRelayUrl = chooseEnv("PROVIDER_RELAY_URL", dbEnv, "", addToDb)
return { liquidityProviderPub, useOnlyLiquidityProvider, disableLiquidityProvider, providerRelayUrl } return { liquidityProviderPub, useOnlyLiquidityProvider, disableLiquidityProvider, providerRelayUrl }
} }

View file

@ -18,7 +18,7 @@ export type SendData = SendDataContent | SendDataEvent
export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string } export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string }
export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => void export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => void
export type LinkedProviderInfo = { pubDestination: string, clientId: string, relayUrl: string } export type LinkedProviderInfo = { pubkey: string, clientId: string, relayUrl: string }
export type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string, provider?: LinkedProviderInfo } export type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string, provider?: LinkedProviderInfo }
// export type ClientInfo = { clientId: string, publicKey: string, privateKey: string, name: string } // export type ClientInfo = { clientId: string, publicKey: string, privateKey: string, name: string }
export type NostrSettings = { export type NostrSettings = {
@ -122,9 +122,12 @@ export class NostrPool {
} }
private validateEvent(e: Event, relay: RelayConnection): { type: 'event', pub: string, app: AppInfo } | { type: 'beacon', content: string, pub: string } | null { private validateEvent(e: Event, relay: RelayConnection): { type: 'event', pub: string, app: AppInfo } | { type: 'beacon', content: string, pub: string } | null {
if (e.kind === 30078 && this.providerInfo && relay.isProviderRelay() && e.pubkey === this.providerInfo.pubDestination) { if (e.kind === 30078 && this.providerInfo && e.pubkey === this.providerInfo.pubkey) {
// Accept beacons from provider relay (which may also be a service relay)
if (relay.isProviderRelay()) {
return { type: 'beacon', content: e.content, pub: e.pubkey } return { type: 'beacon', content: e.content, pub: e.pubkey }
} }
}
if (!actionKinds.includes(e.kind) || !e.pubkey) { if (!actionKinds.includes(e.kind) || !e.pubkey) {
return null return null
} }
@ -268,7 +271,7 @@ const processApps = (settings: NostrSettings) => {
const filters = [getServiceFilter(apps)] const filters = [getServiceFilter(apps)]
if (providerInfo && providerInfo.relayUrl === r) { if (providerInfo && providerInfo.relayUrl === r) {
providerAssigned = true providerAssigned = true
filters.push(getBeaconFilter(providerInfo.pubDestination)) filters.push(getBeaconFilter(providerInfo.pubkey))
} }
rSettings.push({ rSettings.push({
relayUrl: r, relayUrl: r,
@ -283,8 +286,8 @@ const processApps = (settings: NostrSettings) => {
providerRelay: true, providerRelay: true,
serviceRelay: false, serviceRelay: false,
filters: [ filters: [
getProviderFilter(providerInfo.appPub, providerInfo.pubDestination), getProviderFilter(providerInfo.appPub, providerInfo.pubkey),
getBeaconFilter(providerInfo.pubDestination), getBeaconFilter(providerInfo.pubkey),
], ],
}) })
} }