UNPKG

invoices

Version:

Methods for working with BOLT 11 payment requests

86 lines (65 loc) 2.12 kB
const {createHash} = require('crypto'); const {bech32} = require('bech32'); const {recover} = require('tiny-secp256k1'); const hexAsWords = require('./hex_as_words'); const wordsAsBuffer = require('./words_as_buffer'); const {encode} = bech32; const {isArray} = Array; const {MAX_SAFE_INTEGER} = Number; const padding = '0'; const recoveryFlags = [0, 1, 2, 3]; /** Assemble a signed payment request { destination: <Destination Public Key Hex String> hrp: <Request Human Readable Part String> signature: <Request Hash Signature Hex String> tags: [<Request Tag Word Number>] } @throws <Error> @returns { request: <BOLT 11 Encoded Payment Request String> } */ module.exports = ({destination, hrp, signature, tags}) => { if (!destination) { throw new Error('ExpectedDestinationForSignedPaymentRequest'); } if (!hrp) { throw new Error('ExpectedHrpForSignedPaymentRequest'); } if (!signature) { throw new Error('ExpectedRequestSignatureForSignedPaymentRequest'); } try { hexAsWords({hex: signature}); } catch (err) { throw new Error('ExpectedValidSignatureHexForSignedPaymentRequest'); } if (!isArray(tags)) { throw new Error('ExpectedRequestTagsForSignedPaymentRequest'); } const preimage = Buffer.concat([ Buffer.from(hrp, 'ascii'), wordsAsBuffer({words: tags}), ]); const hash = createHash('sha256').update(preimage).digest(); const destinationKey = Buffer.from(destination, 'hex'); const sig = Buffer.from(signature, 'hex'); // Find the recovery flag that works for this signature const recoveryFlag = recoveryFlags.find(flag => { try { const key = Buffer.from(recover(hash, sig, flag, true)); return key.equals(destinationKey); } catch (err) { return false; } }); if (recoveryFlag === undefined) { throw new Error('ExpectedValidSignatureForSignedPaymentRequest'); } const sigWords = hexAsWords({hex: signature + padding + recoveryFlag}).words; const words = [].concat(tags).concat(sigWords); return {request: encode(hrp, words, MAX_SAFE_INTEGER)}; };