diff --git a/decode.js b/decode.js new file mode 100755 index 00000000..c14efa18 --- /dev/null +++ b/decode.js @@ -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 ') + 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) +} + diff --git a/env.example b/env.example index edeecb32..066debda 100644 --- a/env.example +++ b/env.example @@ -16,11 +16,11 @@ #BOOTSTRAP_PEER # 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 -# To disable this feature entirely overwrite the env with "null" -#LIQUIDITY_PROVIDER_PUB=null +# The provider pubkey is extracted from PROVIDER_NPROFILE (nprofile contains both pubkey and relay URL) +# The developers node is used by default, or you may specify another. +# To disable this feature entirely set DISABLE_LIQUIDITY_PROVIDER=true #DISABLE_LIQUIDITY_PROVIDER=false -#PROVIDER_RELAY_URL= +#PROVIDER_NPROFILE=nprofile1qyd8wumn8ghj7um5wfn8y7fwwd5x7cmt9ehx2arhdaexkqpqwmk5tuqvafa6ckwc6zmaypyy3af3n4aeds2ql7m0ew42kzsn638q9s9z8p #SWAPS #BOLTZ_HTTP_URL= diff --git a/src/e2e.ts b/src/e2e.ts index bb4b4465..bb7973af 100644 --- a/src/e2e.ts +++ b/src/e2e.ts @@ -35,8 +35,8 @@ const start = async () => { name: app.name, provider: app.publicKey === liquidityProviderInfo.publicKey ? { clientId: liquidityProviderInfo.clientId, - pubDestination: settingsManager.getSettings().liquiditySettings.liquidityProviderPub, - relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl || relays[0] + pubkey: settingsManager.getSettings().liquiditySettings.liquidityProviderPub, + relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl } : undefined } }) diff --git a/src/index.ts b/src/index.ts index 2166cfb8..4b70ae85 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,8 +36,8 @@ const start = async () => { name: app.name, provider: app.publicKey === liquidityProviderInfo.publicKey ? { clientId: liquidityProviderInfo.clientId, - pubDestination: settingsManager.getSettings().liquiditySettings.liquidityProviderPub, - relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl || relays[0] + pubkey: settingsManager.getSettings().liquiditySettings.liquidityProviderPub, + relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl } : undefined } }) diff --git a/src/services/main/index.ts b/src/services/main/index.ts index 969a87d8..6185e2e2 100644 --- a/src/services/main/index.ts +++ b/src/services/main/index.ts @@ -486,8 +486,8 @@ export default class { name: app.name, provider: app.nostr_public_key === liquidityProviderInfo.publicKey ? { clientId: liquidityProviderInfo.clientId, - pubDestination: this.settings.getSettings().liquiditySettings.liquidityProviderPub, - relayUrl: this.settings.getSettings().liquiditySettings.providerRelayUrl || relays[0] + pubkey: this.settings.getSettings().liquiditySettings.liquidityProviderPub, + relayUrl: this.settings.getSettings().liquiditySettings.providerRelayUrl } : undefined } }) diff --git a/src/services/main/liquidityProvider.ts b/src/services/main/liquidityProvider.ts index a86daf90..ee8917ef 100644 --- a/src/services/main/liquidityProvider.ts +++ b/src/services/main/liquidityProvider.ts @@ -298,6 +298,7 @@ export class LiquidityProvider { } } onBeaconEvent = async (beaconData: { content: string, pub: string }) => { + this.log("received beacon event from", beaconData.pub, "expected", this.pubDestination) if (beaconData.pub !== this.pubDestination) { this.log(ERROR, "got beacon from invalid pub", beaconData.pub, this.pubDestination) return @@ -312,10 +313,13 @@ export class LiquidityProvider { this.log(ERROR, "got beacon from invalid type", beacon.type) return } + this.log("valid beacon received, updating ready state") this.lastSeenBeacon = Date.now() + this.ready = true if (beacon.fees) { this.feesCache = beacon.fees } + this.queue.forEach(q => q('ready')) } onEvent = async (res: { requestId: string }, fromPub: string) => { diff --git a/src/services/main/settings.ts b/src/services/main/settings.ts index fbd03d4d..e852de16 100644 --- a/src/services/main/settings.ts +++ b/src/services/main/settings.ts @@ -1,6 +1,7 @@ import { EnvCacher, EnvMustBeNonEmptyString, EnvMustBeInteger, chooseEnv, chooseEnvBool, chooseEnvInt } from '../helpers/envParser.js' import os from 'os' import path from 'path' +import { nip19 } from '@shocknet/clink-sdk' export type ServiceFeeSettings = { serviceFee: number @@ -169,11 +170,32 @@ export type LiquiditySettings = { providerRelayUrl: string } export const LoadLiquiditySettingsFromEnv = (dbEnv: Record, addToDb?: EnvCacher): LiquiditySettings => { - //const liquidityProviderPub = process.env.LIQUIDITY_PROVIDER_PUB === "null" ? "" : (process.env.LIQUIDITY_PROVIDER_PUB || "76ed45f00cea7bac59d8d0b7d204848f5319d7b96c140ffb6fcbaaab0a13d44e") - const liquidityProviderPub = chooseEnv("LIQUIDITY_PROVIDER_PUB", dbEnv, "76ed45f00cea7bac59d8d0b7d204848f5319d7b96c140ffb6fcbaaab0a13d44e", addToDb) + const providerNprofile = chooseEnv("PROVIDER_NPROFILE", dbEnv, "nprofile1qyd8wumn8ghj7um5wfn8y7fwwd5x7cmt9ehx2arhdaexkqpqwmk5tuqvafa6ckwc6zmaypyy3af3n4aeds2ql7m0ew42kzsn638q9s9z8p", 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 useOnlyLiquidityProvider = chooseEnvBool("USE_ONLY_LIQUIDITY_PROVIDER", dbEnv, false, addToDb) - const providerRelayUrl = chooseEnv("PROVIDER_RELAY_URL", dbEnv, "", addToDb) + return { liquidityProviderPub, useOnlyLiquidityProvider, disableLiquidityProvider, providerRelayUrl } } diff --git a/src/services/nostr/nostrPool.ts b/src/services/nostr/nostrPool.ts index 03425cc1..152406ae 100644 --- a/src/services/nostr/nostrPool.ts +++ b/src/services/nostr/nostrPool.ts @@ -18,7 +18,7 @@ export type SendData = SendDataContent | SendDataEvent export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string } 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 ClientInfo = { clientId: string, publicKey: string, privateKey: string, name: string } export type NostrSettings = { @@ -122,8 +122,11 @@ export class NostrPool { } 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) { - return { type: 'beacon', content: e.content, pub: e.pubkey } + 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 } + } } if (!actionKinds.includes(e.kind) || !e.pubkey) { return null @@ -268,7 +271,7 @@ const processApps = (settings: NostrSettings) => { const filters = [getServiceFilter(apps)] if (providerInfo && providerInfo.relayUrl === r) { providerAssigned = true - filters.push(getBeaconFilter(providerInfo.pubDestination)) + filters.push(getBeaconFilter(providerInfo.pubkey)) } rSettings.push({ relayUrl: r, @@ -283,8 +286,8 @@ const processApps = (settings: NostrSettings) => { providerRelay: true, serviceRelay: false, filters: [ - getProviderFilter(providerInfo.appPub, providerInfo.pubDestination), - getBeaconFilter(providerInfo.pubDestination), + getProviderFilter(providerInfo.appPub, providerInfo.pubkey), + getBeaconFilter(providerInfo.pubkey), ], }) }