UNPKG

invoices

Version:

Methods for working with BOLT 11 payment requests

138 lines (112 loc) 3.9 kB
const {featureFlagsFromWords} = require('bolt09'); const taggedFields = require('./conf/tagged_fields'); const wordsAsBuffer = require('./words_as_buffer'); const wordsAsChainAddress = require('./words_as_chain_address'); const wordsAsHopHints = require('./words_as_hop_hints'); const wordsAsNumber = require('./words_as_number'); const bufferAsHex = buffer => buffer.toString('hex'); const descriptionHashByteLength = 32; const paymentHashByteLength = 32; const trim = true; /** Interpret a tagged field as payment request details { code: <Payment Request Element Code Number> network: <Network Name String> words: [<Element Word Number>] } @throws <Error> @returns { [chain_address]: <Chain Address String> [cltv_delta]: <CLTV Delta Number> [description]: <Description String> [description_hash]: <Description Hash Hex String> [expiry_seconds]: <Seconds After Creation Until Soft Expiration Number> [features]: [{ bit: <BOLT 09 Feature Bit Number> is_required: <Feature Support is Required To Pay Bool> type: <Feature Type String> }] [id]: <Payment Request Hash String> [path]: [{ [base_fee_mtokens]: <Base Fee Millitokens String> [channel]: <Standard Format Channel Id String> [cltv_delta]: <Final CLTV Expiration Blocks Delta Number> [fee_rate]: <Fee Rate Millitokens Per Million Number> public_key: <Forward Edge Public Key Hex String> }] [payment]: <Payment Identifier Hex Encoded String> } */ module.exports = ({code, network, words}) => { const field = taggedFields[code]; // Exit early when field is unknown if (!field) { return {}; } const feature = Object.keys(taggedFields).reduce((sum, n) => { sum[taggedFields[n].label] = taggedFields[n].label; return sum; }, {}); switch (field.label) { case feature.description_hash: try { wordsAsBuffer({trim, words}).toString('hex'); } catch (err) { throw new Error('FailedToParsePaymentRequestDescriptionHash'); } const descriptionHash = wordsAsBuffer({trim, words}); if (descriptionHash.length !== descriptionHashByteLength) { throw new Error('UnexpectedByteLengthOfDescriptionHash'); } return {description_hash: descriptionHash.toString('hex')}; case feature.description: try { return {description: wordsAsBuffer({trim, words}).toString('utf8')}; } catch (err) { throw new Error('InvalidDescriptionInPaymentRequest'); } case feature.fallback_address: try { return { chain_address: wordsAsChainAddress({network, words}).chain_address, }; } catch (err) { throw new Error('FailedToParsePaymentRequestFallbackAddress'); } case feature.expiry: return {expiry_seconds: wordsAsNumber({words})}; case feature.feature_bits: return {features: featureFlagsFromWords({words}).features}; case feature.metadata: try { return {metadata: bufferAsHex(wordsAsBuffer({trim, words}))}; } catch (err) { throw new Error('FailedToParsePaymentContextualMetadata'); } case feature.min_final_cltv_expiry: return {cltv_delta: wordsAsNumber({words})}; case feature.payment_hash: try { wordsAsBuffer({trim, words}); } catch (err) { throw new Error('FailedToParsePaymentRequestPaymentHash'); } if (wordsAsBuffer({trim, words}).length !== paymentHashByteLength) { throw new Error('UnexpectedByteLengthForPaymentHash'); } return {id: wordsAsBuffer({trim, words}).toString('hex')}; case feature.payment_identifier: try { return {payment: wordsAsBuffer({trim, words}).toString('hex')}; } catch (err) { throw new Error('FailedToParsePaymentRequestPaymentIdentifier'); } case feature.routing: return {path: wordsAsHopHints({words}).hints}; default: return {}; } };