diff --git a/payreq.d.ts b/payreq.d.ts index 515b614..2631742 100644 --- a/payreq.d.ts +++ b/payreq.d.ts @@ -88,3 +88,6 @@ export declare function satToHrp(satoshis: number | string): string; export declare function millisatToHrp(millisatoshis: number | string): string; export declare function hrpToSat(hrpString: string, outputString?: boolean): string | BN; export declare function hrpToMillisat(hrpString: string, outputString?: boolean): string | BN; +export declare function setDefaultNetworks(newDefaults: Network[]): void; +export declare function appendNetwork(network: Network): void; +export declare function prependNetwork(network: Network): void; diff --git a/payreq.js b/payreq.js index f22147f..a52d95a 100644 --- a/payreq.js +++ b/payreq.js @@ -34,6 +34,12 @@ const SIMNETWORK = { scriptHash: 0x7b, validWitnessVersions: [0, 1] } +let DEFAULT_NETWORKS = [ + DEFAULTNETWORK, // First network has special meaning and is default + TESTNETWORK, + REGTESTNETWORK, + SIMNETWORK +] const DEFAULTEXPIRETIME = 3600 const DEFAULTCLTVEXPIRY = 9 const DEFAULTDESCRIPTION = '' @@ -570,8 +576,8 @@ function encode (inputData, addDefaults) { // if no cointype is defined, set to testnet let coinTypeObj if (data.network === undefined && !canReconstruct) { - data.network = DEFAULTNETWORK - coinTypeObj = DEFAULTNETWORK + data.network = DEFAULT_NETWORKS[0] + coinTypeObj = DEFAULT_NETWORKS[0] } else if (data.network === undefined && canReconstruct) { throw new Error('Need network for proper payment request reconstruction') } else { @@ -897,27 +903,14 @@ function decode (paymentRequest, network) { const bech32Prefix = prefixMatches[1] let coinNetwork if (!network) { - switch (bech32Prefix) { - case DEFAULTNETWORK.bech32: - coinNetwork = DEFAULTNETWORK - break - case TESTNETWORK.bech32: - coinNetwork = TESTNETWORK - break - case REGTESTNETWORK.bech32: - coinNetwork = REGTESTNETWORK - break - case SIMNETWORK.bech32: - coinNetwork = SIMNETWORK + for (const defaultNetwork of DEFAULT_NETWORKS) { + if (defaultNetwork.bech32 === bech32Prefix) { + coinNetwork = defaultNetwork break + } } } else { - if ( - network.bech32 === undefined || - network.pubKeyHash === undefined || - network.scriptHash === undefined || - !Array.isArray(network.validWitnessVersions) - ) throw new Error('Invalid network') + if (!isNetwork(network)) throw new Error('Invalid network') coinNetwork = network } if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) { @@ -1026,6 +1019,35 @@ function getTagsObject (tags) { return result } +function isNetwork (network) { + return typeof network.bech32 === 'string' && + typeof network.pubKeyHash === 'number' && + typeof network.scriptHash === 'number' && + Array.isArray(network.validWitnessVersions) && + network.validWitnessVersions.every(item => typeof item === 'number') +} + +function setDefaultNetworks (newDefaults) { + if (!Array.isArray(newDefaults) || newDefaults.length === 0 || newDefaults.some(network => !isNetwork(network))) { + throw new Error('setDefaultNetworks argument contained invalid data') + } + DEFAULT_NETWORKS = newDefaults +} + +function appendNetwork (network) { + if (!isNetwork(network)) { + throw new Error('appendNetwork argument contained invalid data') + } + DEFAULT_NETWORKS.push(network) +} + +function prependNetwork (network) { + if (!isNetwork(network)) { + throw new Error('prependNetwork argument contained invalid data') + } + DEFAULT_NETWORKS.unshift(network) +} + module.exports = { encode, decode, @@ -1033,5 +1055,8 @@ module.exports = { satToHrp, millisatToHrp, hrpToSat, - hrpToMillisat + hrpToMillisat, + setDefaultNetworks, + appendNetwork, + prependNetwork } diff --git a/test/index.js b/test/index.js index 54819ad..fb2320c 100644 --- a/test/index.js +++ b/test/index.js @@ -323,3 +323,43 @@ tape('can encode and decode small timestamp', (t) => { t.same(reEncoded.paymentRequest, signedData.paymentRequest) t.end() }) + +tape('can set default networks', (t) => { + t.throws(() => { + lnpayreq.setDefaultNetworks('not an array') + }, new RegExp('setDefaultNetworks argument contained invalid data')) + t.throws(() => { + lnpayreq.setDefaultNetworks([]) + }, new RegExp('setDefaultNetworks argument contained invalid data')) + t.throws(() => { + lnpayreq.setDefaultNetworks([{}]) + }, new RegExp('setDefaultNetworks argument contained invalid data')) + lnpayreq.setDefaultNetworks([{ + bech32: 'bcrt', + pubKeyHash: 0x6f, + scriptHash: 0xc4, + validWitnessVersions: [0, 1] + }]) + + t.throws(() => { + lnpayreq.prependNetwork({}) + }, new RegExp('prependNetwork argument contained invalid data')) + lnpayreq.prependNetwork({ + bech32: 'bcrt', + pubKeyHash: 0x6f, + scriptHash: 0xc4, + validWitnessVersions: [0, 1] + }) + + t.throws(() => { + lnpayreq.appendNetwork({}) + }, new RegExp('appendNetwork argument contained invalid data')) + lnpayreq.appendNetwork({ + bech32: 'bcrt', + pubKeyHash: 0x6f, + scriptHash: 0xc4, + validWitnessVersions: [0, 1] + }) + + t.end() +})