Move 9 development/testing .mjs scripts out of the package root into dev/ to keep the published package clean. Update relative imports and dev.sh reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
182 lines
5 KiB
JavaScript
182 lines
5 KiB
JavaScript
/**
|
|
* Test CLINK Debit flow
|
|
*
|
|
* This simulates what ShockWallet does when scanning an ndebit:
|
|
* 1. Decode the ndebit to get pubkey, relay, pointer
|
|
* 2. Create a bolt11 invoice (we'll get one from Alice)
|
|
* 3. Send Kind 21002 debit request to Lightning.Pub
|
|
* 4. Wait for payment response
|
|
*/
|
|
|
|
import { Relay } from 'nostr-tools/relay'
|
|
import { finalizeEvent, getPublicKey } from 'nostr-tools'
|
|
import { nip44 } from 'nostr-tools'
|
|
import { bech32 } from '@scure/base'
|
|
import { randomBytes } from 'crypto'
|
|
|
|
// Generate a random keypair for this test (simulates ShockWallet)
|
|
const WALLET_PRIVATE_KEY = randomBytes(32)
|
|
const WALLET_PUBLIC_KEY = getPublicKey(WALLET_PRIVATE_KEY)
|
|
|
|
// The ndebit to test
|
|
const NDEBIT = process.argv[2]
|
|
// The bolt11 invoice to be paid
|
|
const BOLT11 = process.argv[3]
|
|
|
|
if (!NDEBIT || !BOLT11) {
|
|
console.log('Usage: node test-debit.mjs <ndebit> <bolt11>')
|
|
console.log('')
|
|
console.log('Example:')
|
|
console.log(' # First create an invoice on Alice:')
|
|
console.log(' docker exec lamassu-lnd-alice lncli --network=regtest addinvoice --amt 1000')
|
|
console.log('')
|
|
console.log(' # Then test the debit:')
|
|
console.log(' node test-debit.mjs ndebit1... lnbcrt...')
|
|
process.exit(1)
|
|
}
|
|
|
|
function decodeNdebit(ndebit) {
|
|
const { prefix, words } = bech32.decode(ndebit, 5000)
|
|
if (prefix !== 'ndebit') throw new Error('Invalid ndebit prefix')
|
|
|
|
const data = new Uint8Array(bech32.fromWords(words))
|
|
|
|
let pubkey, relay, pointer
|
|
let offset = 0
|
|
|
|
while (offset < data.length) {
|
|
const type = data[offset]
|
|
const length = data[offset + 1]
|
|
const value = data.slice(offset + 2, offset + 2 + length)
|
|
|
|
switch (type) {
|
|
case 0:
|
|
pubkey = Buffer.from(value).toString('hex')
|
|
break
|
|
case 1:
|
|
relay = new TextDecoder().decode(value)
|
|
break
|
|
case 2:
|
|
pointer = new TextDecoder().decode(value)
|
|
break
|
|
}
|
|
|
|
offset += 2 + length
|
|
}
|
|
|
|
return { pubkey, relay, pointer }
|
|
}
|
|
|
|
async function main() {
|
|
console.log('=== CLINK Debit Test ===')
|
|
console.log('')
|
|
console.log('Test wallet pubkey:', WALLET_PUBLIC_KEY)
|
|
console.log('')
|
|
|
|
// Decode ndebit
|
|
const debit = decodeNdebit(NDEBIT)
|
|
console.log('Decoded ndebit:')
|
|
console.log(' Pubkey:', debit.pubkey)
|
|
console.log(' Relay:', debit.relay)
|
|
console.log(' Pointer:', debit.pointer || '(none)')
|
|
console.log('')
|
|
|
|
// Connect to relay (override Docker internal hostnames with localhost for local testing)
|
|
const relayUrl = debit.relay
|
|
.replace('host.docker.internal', 'localhost')
|
|
.replace('ws://strfry:', 'ws://localhost:')
|
|
console.log('Connecting to relay:', relayUrl)
|
|
const relay = await Relay.connect(relayUrl)
|
|
console.log('Connected!')
|
|
console.log('')
|
|
|
|
// Build debit request payload
|
|
const requestPayload = {
|
|
pointer: debit.pointer,
|
|
bolt11: BOLT11,
|
|
}
|
|
|
|
console.log('Request payload:', JSON.stringify(requestPayload, null, 2))
|
|
console.log('')
|
|
|
|
// Encrypt with NIP-44
|
|
const conversationKey = nip44.getConversationKey(WALLET_PRIVATE_KEY, debit.pubkey)
|
|
const encryptedContent = nip44.encrypt(JSON.stringify(requestPayload), conversationKey)
|
|
|
|
// Create Kind 21002 event
|
|
const event = finalizeEvent(
|
|
{
|
|
kind: 21002,
|
|
created_at: Math.floor(Date.now() / 1000),
|
|
tags: [
|
|
['p', debit.pubkey],
|
|
['clink_version', '1'],
|
|
],
|
|
content: encryptedContent,
|
|
},
|
|
WALLET_PRIVATE_KEY
|
|
)
|
|
|
|
console.log('Publishing debit request (event id:', event.id.substring(0, 16) + '...)...')
|
|
|
|
// Subscribe to responses
|
|
let responseReceived = false
|
|
const sub = relay.subscribe(
|
|
[
|
|
{
|
|
kinds: [21002],
|
|
authors: [debit.pubkey],
|
|
'#p': [WALLET_PUBLIC_KEY],
|
|
'#e': [event.id],
|
|
since: Math.floor(Date.now() / 1000) - 5,
|
|
},
|
|
],
|
|
{
|
|
onevent(evt) {
|
|
console.log('')
|
|
console.log('Got response event:', evt.id.substring(0, 16) + '...')
|
|
try {
|
|
const decrypted = nip44.decrypt(evt.content, conversationKey)
|
|
const response = JSON.parse(decrypted)
|
|
console.log('Response:', JSON.stringify(response, null, 2))
|
|
|
|
if (response.res === 'ok') {
|
|
console.log('')
|
|
console.log('✅ DEBIT SUCCESS!')
|
|
if (response.preimage) {
|
|
console.log('Preimage:', response.preimage)
|
|
}
|
|
} else if (response.res === 'GFY') {
|
|
console.log('')
|
|
console.log('❌ DEBIT FAILED:', response.error)
|
|
}
|
|
|
|
responseReceived = true
|
|
} catch (err) {
|
|
console.log('Failed to decrypt:', err.message)
|
|
}
|
|
},
|
|
}
|
|
)
|
|
|
|
// Publish request
|
|
await relay.publish(event)
|
|
console.log('Request published, waiting for response...')
|
|
|
|
// Wait for response
|
|
for (let i = 0; i < 30; i++) {
|
|
await new Promise((r) => setTimeout(r, 1000))
|
|
if (responseReceived) break
|
|
if (i % 5 === 4) console.log('Still waiting... (' + (i + 1) + 's)')
|
|
}
|
|
|
|
if (!responseReceived) {
|
|
console.log('')
|
|
console.log('❌ No response received within timeout')
|
|
}
|
|
|
|
sub.close()
|
|
relay.close()
|
|
}
|
|
|
|
main().catch(console.error)
|