From 44b0d6baeb4cfba16a0ecaf1cd17778a183c012f Mon Sep 17 00:00:00 2001 From: Alex Bosworth Date: Wed, 9 Oct 2019 15:58:36 -0700 Subject: [PATCH] add additional tests --- README.md | 19 ++++ chain/index.js | 4 + chain/is_hash.js | 18 +++ chain/is_transaction.js | 25 ++++ grpc/index.js | 3 +- grpc/is_lnd.js | 14 +++ lightning/delete_payments.js | 2 + lightning/get_backup.js | 5 +- package.json | 4 +- test/backups/test_get_backup.js | 99 ++++++++++++++++ test/chain/test_create_chain_address.js | 107 ++++++++++++++++++ test/graph/test_add_peer.js | 56 ++++++++- test/integration/test_get_routes.js | 97 ++++++---------- .../test_recover_funds_from_channels.js | 4 + test/invoices/test_delete_payments.js | 37 ++++++ .../test_get_forwarding_reputations.js | 5 +- .../routerrpc-integration/test_get_payment.js | 42 ++----- test/towers/test_get_tower_server_info.js | 31 +++++ test/unlocker/test_change_password.js | 53 +++++++++ test/unlocker/test_create_seed.js | 82 ++++++++++++++ test/unlocker/test_create_wallet.js | 51 +++++++++ test/unlocker/test_unlock_wallet.js | 53 +++++++++ .../test_broadcast_chain_transaction.js | 71 ++++++++++++ test/wallet/test_get_chain_fee_rate.js | 65 +++++++++++ test/wallet/test_get_public_key.js | 85 ++++++++++++++ tower_server/get_tower_server_info.js | 4 +- unlocker/change_password.js | 5 +- unlocker/create_seed.js | 10 +- unlocker/create_wallet.js | 4 +- unlocker/unlock_wallet.js | 4 +- wallet/broadcast_chain_transaction.js | 16 +-- 31 files changed, 954 insertions(+), 121 deletions(-) create mode 100644 chain/is_hash.js create mode 100644 chain/is_transaction.js create mode 100644 grpc/is_lnd.js create mode 100644 test/backups/test_get_backup.js create mode 100644 test/chain/test_create_chain_address.js create mode 100644 test/invoices/test_delete_payments.js create mode 100644 test/towers/test_get_tower_server_info.js create mode 100644 test/unlocker/test_change_password.js create mode 100644 test/unlocker/test_create_seed.js create mode 100644 test/unlocker/test_create_wallet.js create mode 100644 test/unlocker/test_unlock_wallet.js create mode 100644 test/wallet/test_broadcast_chain_transaction.js create mode 100644 test/wallet/test_get_chain_fee_rate.js create mode 100644 test/wallet/test_get_public_key.js diff --git a/README.md b/README.md index 211cea05..13e96e4e 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ for `unlocker` methods. - [createWallet](#createWallet) - Make a new wallet - [decodePaymentRequest](#decodePaymentRequest) - Decode a Lightning invoice - [deleteForwardingReputations](#deleteForwardingReputations) - Wipe node reps +- [deletePayments](#deletePayments) - Delete entire history of past payments - [disconnectWatchtower](#disconnectWatchtower) - Disconnect a watchtower - [getAutopilot](#getAutopilot) - Get autopilot status or node scores - [getBackup](#getBackup) - Get a backup of a channel @@ -675,11 +676,29 @@ Requires LND built with routerrpc build tag @returns via cbk or Promise +Example: + ```node const {deleteForwardingReputations} = require('ln-service'); await deleteForwardingReputations({}); ``` +### deletePayments + +Delete all records of payments + + { + lnd: + } + + @returns via cbk or Promise + +Example: +```node +const {deletePayments} = require('ln-service'); +await deletePayments({lnd}); +``` + ### disconnectWatchtower Disconnect a watchtower diff --git a/chain/index.js b/chain/index.js index 4b5e981c..45acef8f 100644 --- a/chain/index.js +++ b/chain/index.js @@ -1,8 +1,12 @@ +const isHash = require('./is_hash'); +const isTransaction = require('./is_transaction'); const subscribeToBlocks = require('./subscribe_to_blocks'); const subscribeToChainAddress = require('./subscribe_to_chain_address'); const subscribeToChainSpend = require('./subscribe_to_chain_spend'); module.exports = { + isHash, + isTransaction, subscribeToBlocks, subscribeToChainAddress, subscribeToChainSpend, diff --git a/chain/is_hash.js b/chain/is_hash.js new file mode 100644 index 00000000..88e0da2c --- /dev/null +++ b/chain/is_hash.js @@ -0,0 +1,18 @@ +const isHex = require('is-hex'); + +const hashHexLength = 64; + +/** Determine if a string looks like a regular 256 bit hex encoded hash + + { + [hash]: + } + + @returns + { + is_hash: + } +*/ +module.exports = ({hash}) => { + return {is_hash: !!hash && isHex(hash) && hash.length === hashHexLength}; +}; diff --git a/chain/is_transaction.js b/chain/is_transaction.js new file mode 100644 index 00000000..d7b9c037 --- /dev/null +++ b/chain/is_transaction.js @@ -0,0 +1,25 @@ +const isHex = require('is-hex'); +const {Transaction} = require('bitcoinjs-lib'); + +/** Determine if a hex string is a regular Transaction + + { + [transaction]: + } + + @returns + +*/ +module.exports = ({transaction}) => { + if (!transaction || !isHex(transaction)) { + return false; + } + + try { + Transaction.fromHex(transaction); + } catch (err) { + return false; + } + + return true; +}; diff --git a/grpc/index.js b/grpc/index.js index 08a9c603..22defe32 100644 --- a/grpc/index.js +++ b/grpc/index.js @@ -1,4 +1,5 @@ const authenticatedLndGrpc = require('./authenticated_lnd_grpc'); +const isLnd = require('./is_lnd'); const unauthenticatedLndGrpc = require('./unauthenticated_lnd_grpc'); -module.exports = {authenticatedLndGrpc, unauthenticatedLndGrpc}; +module.exports = {authenticatedLndGrpc, isLnd, unauthenticatedLndGrpc}; diff --git a/grpc/is_lnd.js b/grpc/is_lnd.js new file mode 100644 index 00000000..0c81071a --- /dev/null +++ b/grpc/is_lnd.js @@ -0,0 +1,14 @@ +/** Determine if object is an expected LND Object + + { + [lnd]: + [method]: + [type]: + } + + @returns + +*/ +module.exports = ({lnd, method, type}) => { + return !!lnd && !!lnd[type] && !!lnd[type][method]; +}; diff --git a/lightning/delete_payments.js b/lightning/delete_payments.js index 6a2d31ec..49abecd1 100644 --- a/lightning/delete_payments.js +++ b/lightning/delete_payments.js @@ -6,6 +6,8 @@ const {returnResult} = require('asyncjs-util'); { lnd: } + + @returns via cbk or Promise */ module.exports = ({lnd}, cbk) => { return new Promise((resolve, reject) => { diff --git a/lightning/get_backup.js b/lightning/get_backup.js index fb10f432..da6b11c4 100644 --- a/lightning/get_backup.js +++ b/lightning/get_backup.js @@ -1,7 +1,8 @@ const asyncAuto = require('async/auto'); -const isHex = require('is-hex'); const {returnResult} = require('asyncjs-util'); +const {isHash} = require('./../chain'); + /** Get the static channel backup for a channel { @@ -24,7 +25,7 @@ module.exports = (args, cbk) => { return cbk([400, 'ExpectedGrpcApiConnectionToGetChannelBackup']); } - if (!args.transaction_id || !isHex(args.transaction_id)) { + if (!isHash({hash: args.transaction_id}).is_hash) { return cbk([400, 'ExpectedTxIdOfChannelToGetChannelBackup']); } diff --git a/package.json b/package.json index 54ade0c9..faed6bd0 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "url": "https://github.com/alexbosworth/ln-service.git" }, "scripts": { - "all-integration-tests": "tap -j 1 -t 120 test/autopilot/*.js test/autopilotrpc-integration/*.js test/backups/*.js test/chain/*.js test/bolt02/*.js test/bolt11/*.js test/chainrpc-integration/*.js test/graph/*.js test/grpc/*.js test/grpc-integration/*.js test/integration/*.js test/invoices/*.js test/invoicesrpc-integration/*.js test/router/*.js test/routerrpc-integration/*.js test/routing/*.js test/signerrpc-integration/*.js test/tower_serverrpc-integration/*.js test/walletrpc-integration/*.js", + "all-integration-tests": "tap -j 1 -t 120 test/autopilot/*.js test/autopilotrpc-integration/*.js test/backups/*.js test/chain/*.js test/bolt02/*.js test/bolt11/*.js test/chainrpc-integration/*.js test/graph/*.js test/grpc/*.js test/grpc-integration/*.js test/integration/*.js test/invoices/*.js test/invoicesrpc-integration/*.js test/router/*.js test/routerrpc-integration/*.js test/routing/*.js test/signerrpc-integration/*.js test/tower_serverrpc-integration/*.js test/towers/*.js test/unlocker/*.js test/wallet/*.js test/walletrpc-integration/*.js", "autopilot-integration-tests": "tap --no-coverage -t 90 test/autopilotrpc-integration/*.js", "chain-integration-tests": "tap --no-coverage -t 90 test/chainrpc-integration/*.js", "integration-tests": "tap --no-coverage -t 90 test/grpc-integration/*.js test/integration/*.js", @@ -62,7 +62,7 @@ "router-integration-tests": "tap --no-coverage -j 2 -t 90 test/routerrpc-integration/*.js", "signer-integration-tests": "tap --no-coverage test/signerrpc-integration/*.js", "start": "node server.js", - "test": "tap test/autopilot/*.js test/backups/*.js test/bolt02/*.js test/bolt11/*.js test/chain/*.js test/graph/*.js test/grpc/*.js test/invoices/*.js test/router/*.js test/routing/*.js", + "test": "tap test/autopilot/*.js test/backups/*.js test/bolt02/*.js test/bolt11/*.js test/chain/*.js test/graph/*.js test/grpc/*.js test/invoices/*.js test/router/*.js test/routing/*.js test/towers/*.js test/unlocker/*.js test/wallet/*.js", "tower_client-integration-tests": "tap --no-coverage test/tower_clientrpc-integration/*.js", "tower_server-integration-tests": "tap --no-coverage test/tower_serverrpc-integration/*.js", "wallet-integration-tests": "tap --no-coverage test/walletrpc-integration/*.js" diff --git a/test/backups/test_get_backup.js b/test/backups/test_get_backup.js new file mode 100644 index 00000000..c2ae97f9 --- /dev/null +++ b/test/backups/test_get_backup.js @@ -0,0 +1,99 @@ +const {test} = require('tap'); + +const {getBackup} = require('./../../'); + +const txId = Buffer.alloc(32).toString('hex'); + +const tests = [ + { + args: {}, + description: 'LND object is required', + error: [400, 'ExpectedGrpcApiConnectionToGetChannelBackup'], + }, + { + args: {lnd: {}}, + description: 'LND object with default methods is required', + error: [400, 'ExpectedGrpcApiConnectionToGetChannelBackup'], + }, + { + args: {lnd: {default: {}}}, + description: 'A funding transaction id is required', + error: [400, 'ExpectedTxIdOfChannelToGetChannelBackup'], + }, + { + args: {lnd: {default: {}}, transaction_id: txId}, + description: 'A funding transaction vout is required', + error: [400, 'ExpectedTxOutputIndexToGetChannelBackup'], + }, + { + args: { + lnd: {default: {exportChannelBackup: ({}, cbk) => cbk('err')}}, + transaction_id: txId, + transaction_vout: 0, + }, + description: 'An unexpected error is returned', + error: [503, 'UnexpectedErrExportingBackupForChannel', {err: 'err'}], + }, + { + args: { + lnd: {default: {exportChannelBackup: ({}, cbk) => cbk()}}, + transaction_id: txId, + transaction_vout: 0, + }, + description: 'A result is returned', + error: [503, 'ExpectedResultOfGetChannelBackupRequest'], + }, + { + args: { + lnd: {default: {exportChannelBackup: ({}, cbk) => cbk(null, {})}}, + transaction_id: txId, + transaction_vout: 0, + }, + description: 'A chan backup result is returned', + error: [503, 'UnexpectedResponseForChannelBackupRequest'], + }, + { + args: { + lnd: { + default: { + exportChannelBackup: ({}, cbk) => cbk(null, { + chan_backup: Buffer.alloc(0), + }), + }, + }, + transaction_id: txId, + transaction_vout: 0, + }, + description: 'A non empty chan backup result is returned', + error: [503, 'UnexpectedResponseForChannelBackupRequest'], + }, + { + args: { + lnd: { + default: { + exportChannelBackup: ({}, cbk) => cbk(null, { + chan_backup: Buffer.alloc(1), + }), + }, + }, + transaction_id: txId, + transaction_vout: 0, + }, + description: 'A non empty chan backup result is returned', + expected: {backup: '00'}, + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => getBackup(args), error, 'Got expected error'); + } else { + const {backup} = await getBackup(args); + + equal(backup, expected.backup, 'Got expected backup'); + } + + return end(); + }); +}); diff --git a/test/chain/test_create_chain_address.js b/test/chain/test_create_chain_address.js new file mode 100644 index 00000000..e4ec733b --- /dev/null +++ b/test/chain/test_create_chain_address.js @@ -0,0 +1,107 @@ +const {test} = require('tap'); + +const {createChainAddress} = require('./../../'); + +const tests = [ + { + args: {}, + description: 'An address format is required', + error: [400, 'ExpectedKnownAddressFormat'], + }, + { + args: {format: 'foo'}, + description: 'A known address format is required', + error: [400, 'ExpectedKnownAddressFormat'], + }, + { + args: {format: 'p2wpkh'}, + description: 'LND is required', + error: [400, 'ExpectedLndForAddressCreation'], + }, + { + args: {format: 'p2wpkh', lnd: {}}, + description: 'LND with default is required', + error: [400, 'ExpectedLndForAddressCreation'], + }, + { + args: {format: 'p2wpkh', lnd: {default: {}}}, + description: 'LND with default is required', + error: [400, 'ExpectedLndForAddressCreation'], + }, + { + args: { + format: 'p2wpkh', + lnd: { + default: { + newAddress: ({}, cbk) => cbk({ + message: '14 UNAVAILABLE: Connect Failed', + }), + }, + }, + }, + description: 'Connection failure error is returned', + error: [503, 'FailedToConnectToDaemonToCreateChainAddress'], + }, + { + args: { + format: 'p2wpkh', + lnd: {default: {newAddress: ({}, cbk) => cbk('err')}}, + }, + description: 'Unanticipated errors are returned', + error: [503, 'UnexpectedErrorCreatingAddress', {err: 'err'}], + }, + { + args: {format: 'p2wpkh', lnd: {default: {newAddress: ({}, cbk) => cbk()}}}, + description: 'A result is required', + error: [503, 'ExpectedResponseForAddressCreation'], + }, + { + args: { + format: 'p2wpkh', + lnd: {default: {newAddress: ({}, cbk) => cbk(null, {})}}, + }, + description: 'An address is required', + error: [503, 'ExpectedAddressInCreateAddressResponse'], + }, + { + args: { + format: 'p2wpkh', + lnd: {default: {newAddress: ({}, cbk) => cbk(null, {address: 'addr'})}}, + }, + description: 'An address is required', + expected: {address: 'addr'}, + }, + { + args: { + format: 'p2wpkh', + is_unused: true, + lnd: { + default: { + newAddress: (args, cbk) => { + if (args.type !== 2) { + return cbk([500, 'FailedToSetUnusedFlagForAddress', args.type]); + } + + return cbk(null, {address: 'addr'}); + }, + }, + }, + }, + description: 'An unused address gets an unused address', + expected: {address: 'addr'}, + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => createChainAddress(args), error, 'Got expected error'); + } else { + const {address} = await createChainAddress(args); + + equal(address, expected.address, 'Got expected new address'); + } + + return end(); + }); +}); diff --git a/test/graph/test_add_peer.js b/test/graph/test_add_peer.js index 944deabd..45901b25 100644 --- a/test/graph/test_add_peer.js +++ b/test/graph/test_add_peer.js @@ -88,9 +88,63 @@ const tests = [ retry_count: 0, socket: 'socket', }, - description: 'Still syncing returns known error', + description: 'Random error returns error', error: [503, 'UnexpectedErrorAddingPeer', {err: 'e'}], }, + { + args: { + lnd: { + default: { + connectPeer: ({}, cbk) => cbk(null, {}), + listPeers: ({}, cbk) => cbk('err'), + }, + }, + public_key: Buffer.alloc(33).toString('hex'), + retry_count: 0, + socket: 'socket', + }, + description: 'List peers error returns error', + error: [503, 'UnexpectedGetPeersError', {err: 'err'}], + }, + { + args: { + lnd: { + default: { + connectPeer: ({}, cbk) => cbk(null, {}), + listPeers: ({}, cbk) => cbk(null, {peers: []}), + }, + }, + public_key: Buffer.alloc(33).toString('hex'), + retry_count: 0, + socket: 'socket', + }, + description: 'List peers error returns error', + error: [503, 'FailedToSuccessfullyConnectToRemotePeer'], + }, + { + args: { + lnd: { + default: { + connectPeer: ({}, cbk) => cbk(null, {}), + listPeers: ({}, cbk) => cbk(null, {peers: [{ + address: 'address', + bytes_recv: '0', + bytes_sent: '0', + inbound: false, + ping_time: '0', + pub_key: Buffer.alloc(33).toString('hex'), + sat_recv: '0', + sat_sent: '0', + sync_type: 'ACTIVE_SYNC', + }]}), + }, + }, + public_key: Buffer.alloc(33).toString('hex'), + retry_count: 0, + socket: 'socket', + }, + description: 'List peers error returns error', + }, ]; tests.forEach(({args, description, error}) => { diff --git a/test/integration/test_get_routes.js b/test/integration/test_get_routes.js index 07a6c807..086d14cf 100644 --- a/test/integration/test_get_routes.js +++ b/test/integration/test_get_routes.js @@ -1,31 +1,23 @@ -const {randomBytes} = require('crypto'); - +const asyncRetry = require('async/retry'); const {test} = require('tap'); const {addPeer} = require('./../../'); const {createCluster} = require('./../macros'); const {createInvoice} = require('./../../'); -const {delay} = require('./../macros'); const {decodePaymentRequest} = require('./../../'); const {getChannels} = require('./../../'); const {getNetworkGraph} = require('./../../'); const {getRoutes} = require('./../../'); const {getWalletInfo} = require('./../../'); -const {openChannel} = require('./../../'); const {pay} = require('./../../'); const {routeFromHops} = require('./../../routing'); -const {waitForChannel} = require('./../macros'); -const {waitForPendingChannel} = require('./../macros'); +const {setupChannel} = require('./../macros'); const {waitForRoute} = require('./../macros'); const buffer = 6; -const channelCapacityTokens = 1e6; -const confirmationCount = 20; -const defaultFee = 1e3; -const defaultVout = 0; -const mtokPadding = '000'; +const interval = retryCount => 50 * Math.pow(2, retryCount); +const times = 15; const tokens = 100; -const txIdHexLength = 32 * 2; // Getting routes to a destination should return routes to the destination test(`Get routes`, async ({end, equal}) => { @@ -33,54 +25,23 @@ test(`Get routes`, async ({end, equal}) => { const {lnd} = cluster.control; - const controlToTargetChannel = await openChannel({ - lnd, - chain_fee_tokens_per_vbyte: defaultFee, - local_tokens: channelCapacityTokens, - partner_public_key: cluster.target_node_public_key, - socket: cluster.target.socket, - }); - - await waitForPendingChannel({ - lnd, - id: controlToTargetChannel.transaction_id, - }); - - await cluster.generate({count: confirmationCount, node: cluster.control}); - - await waitForChannel({ - lnd, - id: controlToTargetChannel.transaction_id, - }); + await setupChannel({lnd, generate: cluster.generate, to: cluster.target}); const [channel] = (await getChannels({lnd})).channels; - const targetToRemoteChannel = await openChannel({ - chain_fee_tokens_per_vbyte: defaultFee, - give_tokens: Math.round(channelCapacityTokens / 2), - lnd: cluster.target.lnd, - local_tokens: channelCapacityTokens, - partner_public_key: cluster.remote_node_public_key, - socket: cluster.remote.socket, - }); - - await waitForPendingChannel({ - id: targetToRemoteChannel.transaction_id, - lnd: cluster.target.lnd, - }); - - await cluster.generate({count: confirmationCount, node: cluster.target}); - - await waitForChannel({ - id: targetToRemoteChannel.transaction_id, + await setupChannel({ + generate: cluster.generate, + generator: cluster.target, + give: Math.round(1e6 / 2), lnd: cluster.target.lnd, + to: cluster.remote, }); const [remoteChan] = (await getChannels({lnd: cluster.remote.lnd})).channels; await addPeer({ lnd, - public_key: cluster.remote_node_public_key, + public_key: cluster.remote.public_key, socket: cluster.remote.socket, }); @@ -90,15 +51,29 @@ test(`Get routes`, async ({end, equal}) => { const {destination} = decodedRequest; - await cluster.generate({count: confirmationCount}); - const {routes} = await waitForRoute({destination, lnd, tokens}); + // Wait for backwards route + await asyncRetry({interval, times}, async () => { + const backwardsRoutes = await getRoutes({ + lnd, + tokens, + destination: cluster.control.public_key, + start: cluster.remote.public_key, + }); + + if (backwardsRoutes.routes.length !== 1) { + throw new Error('WaitingForRouteToExist'); + } + + return; + }); + const backwardsRoutes = await getRoutes({ lnd, tokens, - destination: (await getWalletInfo({lnd})).public_key, - start: cluster.remote_node_public_key, + destination: cluster.control.public_key, + start: cluster.remote.public_key, }); equal(backwardsRoutes.routes.length, 1, 'Route can be calculated backwards'); @@ -109,8 +84,8 @@ test(`Get routes`, async ({end, equal}) => { tokens, ignore: [{ channel: remoteChan.id, - from_public_key: cluster.target_node_public_key, - to_public_key: cluster.remote_node_public_key, + from_public_key: cluster.target.public_key, + to_public_key: cluster.remote.public_key, }], }); @@ -120,7 +95,7 @@ test(`Get routes`, async ({end, equal}) => { destination, lnd, tokens, - ignore: [{from_public_key: cluster.target_node_public_key}], + ignore: [{from_public_key: cluster.target.public_key}], }); equal(ignoreNodes.routes.length, [].length, 'Ignore nodes ignores nodes'); @@ -135,7 +110,7 @@ test(`Get routes`, async ({end, equal}) => { lnd, routes: [[ { - public_key: cluster.target_node_public_key, + public_key: cluster.target.public_key, }, { base_fee_mtokens: '1000', @@ -143,7 +118,7 @@ test(`Get routes`, async ({end, equal}) => { channel_capacity: remoteChannel.capacity, cltv_delta: 40, fee_rate: 1, - public_key: cluster.remote_node_public_key, + public_key: cluster.remote.public_key, }, ]], tokens: decodedRequest.tokens, @@ -169,11 +144,11 @@ test(`Get routes`, async ({end, equal}) => { channel_capacity: remoteChannel.capacity, cltv_delta: 40, fee_rate: 1, - public_key: cluster.remote_node_public_key, + public_key: cluster.remote.public_key, }, ], initial_cltv: 40, - mtokens: `${tokens}${mtokPadding}`, + mtokens: (BigInt(tokens) * BigInt(1e3)).toString(), }); const [direct] = routes; diff --git a/test/integration/test_recover_funds_from_channels.js b/test/integration/test_recover_funds_from_channels.js index 7a01546b..9e02ea35 100644 --- a/test/integration/test_recover_funds_from_channels.js +++ b/test/integration/test_recover_funds_from_channels.js @@ -38,8 +38,12 @@ test(`Recover funds with backup`, async ({end, equal}) => { to: cluster.control, }); + await delay(3000); + const {backup} = await getBackups({lnd}); + await delay(3000); + await stopDaemon({lnd}); try { diff --git a/test/invoices/test_delete_payments.js b/test/invoices/test_delete_payments.js new file mode 100644 index 00000000..63ee0426 --- /dev/null +++ b/test/invoices/test_delete_payments.js @@ -0,0 +1,37 @@ +const {test} = require('tap'); + +const {deletePayments} = require('./../../'); + +const tests = [ + { + args: {}, + description: 'An authenticated lnd is required to delete payments', + error: [400, 'ExpectedAuthenticatedLndToDeleteAllPayments'], + }, + { + args: {lnd: {}}, + description: 'An lnd with default methods is required', + error: [400, 'ExpectedAuthenticatedLndToDeleteAllPayments'], + }, + { + args: {lnd: {default: {deleteAllPayments: ({}, cbk) => cbk('err')}}}, + description: 'An error deleting is reported', + error: [503, 'UnexpectedErrorDeletingAllPayments', {err: 'err'}], + }, + { + args: {lnd: {default: {deleteAllPayments: ({}, cbk) => cbk()}}}, + description: 'Successful deletion of all past payments', + }, +]; + +tests.forEach(({args, description, error}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => deletePayments(args), error, 'Got expected error'); + } else { + await deletePayments(args); + } + + return end(); + }); +}); diff --git a/test/routerrpc-integration/test_get_forwarding_reputations.js b/test/routerrpc-integration/test_get_forwarding_reputations.js index f61ea8b8..b7d7cb9c 100644 --- a/test/routerrpc-integration/test_get_forwarding_reputations.js +++ b/test/routerrpc-integration/test_get_forwarding_reputations.js @@ -16,6 +16,7 @@ const {payViaRoutes} = require('./../../'); const {probeForRoute} = require('./../../'); const {waitForChannel} = require('./../macros'); const {waitForPendingChannel} = require('./../macros'); +const {waitForRoute} = require('./../macros'); const chain = '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206'; const channelCapacityTokens = 1e6; @@ -86,7 +87,7 @@ test('Get forwarding reputations', async ({deepIs, end, equal}) => { await createInvoice({tokens, lnd: cluster.remote.lnd}); - await delay(3000); + await waitForRoute({lnd, tokens, destination: cluster.remote.public_key}); try { const res = await probeForRoute({ @@ -103,7 +104,7 @@ test('Get forwarding reputations', async ({deepIs, end, equal}) => { const [node] = nodes; - equal(node.public_key, cluster.target.public_key, 'Temp fail node added'); + equal(!!node.public_key, true, 'Temp fail node added'); if (!!node.channels.length) { const [channel] = node.channels; diff --git a/test/routerrpc-integration/test_get_payment.js b/test/routerrpc-integration/test_get_payment.js index c8665027..7982b98f 100644 --- a/test/routerrpc-integration/test_get_payment.js +++ b/test/routerrpc-integration/test_get_payment.js @@ -17,8 +17,10 @@ const {hopsFromChannels} = require('./../../routing'); const {openChannel} = require('./../../'); const {payViaPaymentRequest} = require('./../../'); const {routeFromHops} = require('./../../routing'); +const {setupChannel} = require('./../macros'); const {waitForChannel} = require('./../macros'); const {waitForPendingChannel} = require('./../macros'); +const {waitForRoute} = require('./../macros'); const channelCapacityTokens = 1e6; const confirmationCount = 6; @@ -37,22 +39,7 @@ test(`Pay`, async ({deepIs, end, equal, rejects}) => { const {id} = invoice; - const controlToTargetChannel = await openChannel({ - lnd, - chain_fee_tokens_per_vbyte: defaultFee, - local_tokens: channelCapacityTokens, - partner_public_key: cluster.target_node_public_key, - socket: cluster.target.socket, - }); - - await waitForPendingChannel({ - lnd, - id: controlToTargetChannel.transaction_id, - }); - - await cluster.generate({count: confirmationCount, node: cluster.control}); - - await waitForChannel({lnd, id: controlToTargetChannel.transaction_id}); + await setupChannel({lnd, generate: cluster.generate, to: cluster.target}); rejects(getPayment({lnd, id}), [404, 'SentPaymentNotFound'], 'Not found'); @@ -70,24 +57,11 @@ test(`Pay`, async ({deepIs, end, equal, rejects}) => { const [channel] = (await getChannels({lnd})).channels; - const targetToRemoteChannel = await openChannel({ - chain_fee_tokens_per_vbyte: defaultFee, - lnd: cluster.target.lnd, - local_tokens: channelCapacityTokens, - partner_public_key: cluster.remote_node_public_key, - socket: `${cluster.remote.listen_ip}:${cluster.remote.listen_port}`, - }); - - await waitForPendingChannel({ - id: targetToRemoteChannel.transaction_id, - lnd: cluster.target.lnd, - }); - - await cluster.generate({count: confirmationCount, node: cluster.target}); - - await waitForChannel({ - id: targetToRemoteChannel.transaction_id, + await setupChannel({ lnd: cluster.target.lnd, + generate: cluster.generate, + generator: cluster.target, + to: cluster.remote, }); const [remoteChan] = (await getChannels({lnd: cluster.remote.lnd})).channels; @@ -98,7 +72,7 @@ test(`Pay`, async ({deepIs, end, equal, rejects}) => { socket: cluster.remote.socket, }); - await delay(3000); + await waitForRoute({lnd, tokens, destination: cluster.remote.public_key}); await payViaPaymentRequest({lnd, request: invoice.request}); diff --git a/test/towers/test_get_tower_server_info.js b/test/towers/test_get_tower_server_info.js new file mode 100644 index 00000000..9d074914 --- /dev/null +++ b/test/towers/test_get_tower_server_info.js @@ -0,0 +1,31 @@ +const {test} = require('tap'); + +const {getTowerServerInfo} = require('./../../'); + +const makeLnd = (err, res) => { + return {tower_server: {getInfo: ({}, cbk) => cbk(err, res)}}; +}; + +const tests = [ + { + args: {}, + description: 'LND is required', + error: [400, 'ExpectedAuthedLndGrpcToGetWatchtowerServerInfo'], + }, + { + args: {lnd: makeLnd({message: '2 UNKNOWN: watchtower not active'})}, + description: 'Inactive tower returns no details', + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => getTowerServerInfo(args), error, 'Got expected error'); + } else { + await getTowerServerInfo(args); + } + + return end(); + }); +}); diff --git a/test/unlocker/test_change_password.js b/test/unlocker/test_change_password.js new file mode 100644 index 00000000..d82c06c2 --- /dev/null +++ b/test/unlocker/test_change_password.js @@ -0,0 +1,53 @@ +const {test} = require('tap'); + +const {changePassword} = require('./../../'); + +const tests = [ + { + args: {}, + description: 'The current password is required', + error: [400, 'ExpectedCurrentPasswordToChangePassword'], + }, + { + args: {current_password: 'password'}, + description: 'LND is required', + error: [400, 'ExpectedUnauthenticatedLndGrpcToChangePassword'], + }, + { + args: { + current_password: 'password', + lnd: {unlocker: {changePassword: ({}, cbk) => cbk()}}, + }, + description: 'LND is required', + error: [400, 'ExpectedNewPasswordForChangePasswordRequest'], + }, + { + args: { + current_password: 'password', + lnd: {unlocker: {changePassword: ({}, cbk) => cbk('err')}}, + new_password: 'new_password', + }, + description: 'Errors are passed back', + error: [503, 'FailedToChangeLndPassword', {err: 'err'}], + }, + { + args: { + current_password: 'password', + lnd: {unlocker: {changePassword: ({}, cbk) => cbk(null, {})}}, + new_password: 'new_password', + }, + description: 'Password is changed', + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => changePassword(args), error, 'Got expected error'); + } else { + await changePassword(args); + } + + return end(); + }); +}); diff --git a/test/unlocker/test_create_seed.js b/test/unlocker/test_create_seed.js new file mode 100644 index 00000000..2ee395c5 --- /dev/null +++ b/test/unlocker/test_create_seed.js @@ -0,0 +1,82 @@ +const {test} = require('tap'); + +const {createSeed} = require('./../../'); + +const message = '14 UNAVAILABLE: Connect Failed'; + +const tests = [ + { + args: {}, + description: 'LND is required', + error: [400, 'ExpectedNonAuthenticatedLndForSeedCreation'], + }, + { + args: {lnd: {unlocker: {genSeed: ({}, cbk) => cbk({message})}}}, + description: 'Connection failure errors returned', + error: [503, 'UnexpectedConnectionFailure'], + }, + { + args: { + lnd: {unlocker: {genSeed: ({}, cbk) => cbk('err')}}, + passphrase: 'passphrase', + }, + description: 'Unexpected errors passed back', + error: [503, 'UnexpectedCreateSeedError', {err: 'err'}], + }, + { + args: {lnd: {unlocker: {genSeed: ({}, cbk) => cbk()}}}, + description: 'A response is expected', + error: [503, 'ExpectedResponseForSeedCreation'], + }, + { + args: { + lnd: { + unlocker: { + genSeed: ({}, cbk) => cbk(null, {cipher_seed_mnemonic: 'seed'}), + }, + }, + }, + description: 'A cipher seed mnemonic is expected', + error: [503, 'ExpectedCipherSeedMnemonic'], + }, + { + args: { + lnd: { + unlocker: { + genSeed: ({}, cbk) => cbk(null, { + cipher_seed_mnemonic: Array.from({length: 23}), + }), + }, + }, + }, + description: 'A long cipher seed mnemonic is expected', + error: [503, 'UnexpectedCipherSeedMnemonicLength'], + }, + { + args: { + lnd: { + unlocker: { + genSeed: ({}, cbk) => cbk(null, { + cipher_seed_mnemonic: Array.from({length: 24}).map(n => 'foo'), + }), + }, + }, + }, + description: 'A long cipher seed mnemonic is expected', + expected: {seed: Array.from({length: 24}).map(n => 'foo').join(' ')}, + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => createSeed(args), error, 'Got expected error'); + } else { + const {seed} = await createSeed(args); + + equal(seed, expected.seed, 'Got expected seed'); + } + + return end(); + }); +}); diff --git a/test/unlocker/test_create_wallet.js b/test/unlocker/test_create_wallet.js new file mode 100644 index 00000000..2100dc60 --- /dev/null +++ b/test/unlocker/test_create_wallet.js @@ -0,0 +1,51 @@ +const {test} = require('tap'); + +const {createWallet} = require('./../../'); + +const tests = [ + { + args: {}, + description: 'LND is required', + error: [400, 'ExpectedLndForWalletCreation'], + }, + { + args: {lnd: {unlocker: {initWallet: ({}, cbk) => {}}}}, + description: 'Wallet password is required', + error: [400, 'ExpectedWalletPasswordForWalletCreation'], + }, + { + args: {lnd: {unlocker: {initWallet: ({}, cbk) => {}}}, password: 'pass'}, + description: 'Seed is required', + error: [400, 'ExpectedSeedMnemonicForWalletCreation'], + }, + { + args: { + lnd: {unlocker: {initWallet: ({}, cbk) => cbk('err')}}, + passphrase: 'passphrase', + password: 'pass', + seed: 'seed', + }, + description: 'Errors are passed back', + error: [503, 'UnexpectedInitWalletError', {err: 'err'}], + }, + { + args: { + lnd: {unlocker: {initWallet: ({}, cbk) => cbk()}}, + password: 'pass', + seed: 'seed', + }, + description: 'Errors are passed back', + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => createWallet(args), error, 'Got expected error'); + } else { + await createWallet(args); + } + + return end(); + }); +}); diff --git a/test/unlocker/test_unlock_wallet.js b/test/unlocker/test_unlock_wallet.js new file mode 100644 index 00000000..a192b132 --- /dev/null +++ b/test/unlocker/test_unlock_wallet.js @@ -0,0 +1,53 @@ +const {test} = require('tap'); + +const {unlockWallet} = require('./../../'); + +const details = 'invalid passphrase for master public key'; + +const tests = [ + { + args: {}, + description: 'LND is required', + error: [400, 'ExpectedLndWhenUnlockingWallet'], + }, + { + args: {lnd: {unlocker: {unlockWallet: ({}, cbk) => cbk()}}}, + description: 'Password is required', + error: [400, 'ExpectedUnlockPassword'], + }, + { + args: { + lnd: {unlocker: {unlockWallet: ({}, cbk) => cbk({details})}}, + password: 'password', + }, + description: 'Valid password is required', + error: [401, 'InvalidWalletUnlockPassword'], + }, + { + args: { + lnd: {unlocker: {unlockWallet: ({}, cbk) => cbk('err')}}, + password: 'password', + }, + description: 'Errors are passed along', + error: [503, 'UnexpectedUnlockWalletErr', {err: 'err'}], + }, + { + args: { + lnd: {unlocker: {unlockWallet: ({}, cbk) => cbk()}}, + password: 'password', + }, + description: 'No errors means the wallet is unlocked', + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => unlockWallet(args), error, 'Got expected error'); + } else { + await unlockWallet(args); + } + + return end(); + }); +}); diff --git a/test/wallet/test_broadcast_chain_transaction.js b/test/wallet/test_broadcast_chain_transaction.js new file mode 100644 index 00000000..7495a3c9 --- /dev/null +++ b/test/wallet/test_broadcast_chain_transaction.js @@ -0,0 +1,71 @@ +const {Transaction} = require('bitcoinjs-lib'); +const {test} = require('tap'); + +const {broadcastChainTransaction} = require('./../../'); + +const tests = [ + { + args: {}, + description: 'LND is required', + error: [400, 'ExpectedWalletRpcLndToSendRawTransaction'], + }, + { + args: {lnd: {wallet: {publishTransaction: ({}, cbk) => cbk()}}}, + description: 'Raw transaction is required', + error: [400, 'ExpectedTransactionHexStringToBroadcastToPeers'], + }, + { + args: { + lnd: {wallet: {publishTransaction: ({}, cbk) => cbk('err')}}, + transaction: new Transaction().toHex(), + }, + description: 'Expected error is returned', + error: [503, 'UnexpectedErrBroadcastingRawTx', {err: 'err'}], + }, + { + args: { + lnd: {wallet: {publishTransaction: ({}, cbk) => cbk()}}, + transaction: new Transaction().toHex(), + }, + description: 'A result is required', + error: [503, 'ExpectedResultOfBroadcastRawTransaction'], + }, + { + args: { + lnd: { + wallet: { + publishTransaction: ({}, cbk) => cbk(null, {publish_error: 'err'}), + }, + }, + transaction: new Transaction().toHex(), + }, + description: 'Failure to broadcast error is returned', + error: [ + 503, + 'FailedToBroadcastRawTransaction', + {res: {publish_error: 'err'}}, + ], + }, + { + args: { + lnd: {wallet: {publishTransaction: ({}, cbk) => cbk(null, {})}}, + transaction: new Transaction().toHex(), + }, + description: 'A transaction is published', + expected: {id: 'd21633ba23f70118185227be58a63527675641ad37967e2aa461559f577aec43'}, + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => broadcastChainTransaction(args), error, 'Got expected error'); + } else { + const {id} = await broadcastChainTransaction(args); + + equal(id, expected.id, 'Got fee rate'); + } + + return end(); + }); +}); diff --git a/test/wallet/test_get_chain_fee_rate.js b/test/wallet/test_get_chain_fee_rate.js new file mode 100644 index 00000000..047800d7 --- /dev/null +++ b/test/wallet/test_get_chain_fee_rate.js @@ -0,0 +1,65 @@ +const {test} = require('tap'); + +const {getChainFeeRate} = require('./../../'); + +const tests = [ + { + args: {}, + description: 'LND is required', + error: [400, 'ExpecteAuthenticatedLndToGetFeeEstimate'], + }, + { + args: {lnd: {}}, + description: 'LND with wallet object is required', + error: [400, 'ExpecteAuthenticatedLndToGetFeeEstimate'], + }, + { + args: {lnd: {wallet: {}}}, + description: 'LND with estimateFee method is required', + error: [400, 'ExpecteAuthenticatedLndToGetFeeEstimate'], + }, + { + args: { + lnd: {wallet: {estimateFee: ({}, cbk) => cbk('err')}}, + }, + description: 'Unexpected errors are passed back', + error: [503, 'UnexpectedErrorGettingFeeFromLnd', {err: 'err'}], + }, + { + args: { + lnd: {wallet: {estimateFee: ({}, cbk) => cbk()}}, + }, + description: 'A response is expected', + error: [503, 'ExpectedSatPerKwResponseForFeeEstimate'], + }, + { + args: { + lnd: {wallet: {estimateFee: ({}, cbk) => cbk(null, {})}}, + }, + description: 'A response is expected', + error: [503, 'ExpectedSatPerKwResponseForFeeEstimate'], + }, + { + args: { + lnd: { + wallet: {estimateFee: ({}, cbk) => cbk(null, {sat_per_kw: '250'})}, + }, + }, + description: 'Tokens per vbyte are returned', + expected: {tokens_per_vbyte: 1}, + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => getChainFeeRate(args), error, 'Got expected error'); + } else { + const res = await getChainFeeRate(args); + + equal(res.tokens_per_vbyte, expected.tokens_per_vbyte, 'Got fee rate'); + } + + return end(); + }); +}); diff --git a/test/wallet/test_get_public_key.js b/test/wallet/test_get_public_key.js new file mode 100644 index 00000000..a31411b0 --- /dev/null +++ b/test/wallet/test_get_public_key.js @@ -0,0 +1,85 @@ +const {test} = require('tap'); + +const {getPublicKey} = require('./../../'); + +const tests = [ + { + args: {}, + description: 'Key family is required', + error: [400, 'ExpectedKeyFamilyToGetPublicKey'], + }, + { + args: {family: 1}, + description: 'Index is required', + error: [400, 'ExpectedKeyIndexToGetPublicKey'], + }, + { + args: {family: 1, index: 1}, + description: 'LND is required', + error: [400, 'ExpectedWalletRpcLndToGetPublicKey'], + }, + { + args: {family: 1, index: 1, lnd: {}}, + description: 'LND is required', + error: [400, 'ExpectedWalletRpcLndToGetPublicKey'], + }, + { + args: {family: 1, index: 1, lnd: {wallet: {}}}, + description: 'LND is required', + error: [400, 'ExpectedWalletRpcLndToGetPublicKey'], + }, + { + args: { + family: 1, + index: 1, + lnd: {wallet: {deriveKey: ({}, cbk) => cbk('err')}}, + }, + description: 'Unexpected errors are passed back', + error: [503, 'UnexpectedErrGettingPublicKeyFromSeed', {err: 'err'}], + }, + { + args: { + family: 1, + index: 1, + lnd: {wallet: {deriveKey: ({}, cbk) => cbk()}}, + }, + description: 'Expects result back', + error: [503, 'UnexpectedResultInDerivePublicKeyResponse'], + }, + { + args: { + family: 1, + index: 1, + lnd: {wallet: {deriveKey: ({}, cbk) => cbk(null, {})}}, + }, + description: 'Expects result back', + error: [503, 'ExpectedRawPubKeyBytesInDerivePubKeyResponse'], + }, + { + args: { + family: 1, + index: 1, + lnd: { + wallet: { + deriveKey: ({}, cbk) => cbk(null, {raw_key_bytes: Buffer.alloc(1)}), + }, + }, + }, + description: 'Got public key result', + expected: {public_key: '00'}, + }, +]; + +tests.forEach(({args, description, error, expected}) => { + return test(description, async ({deepEqual, end, equal, rejects}) => { + if (!!error) { + rejects(() => getPublicKey(args), error, 'Got expected error'); + } else { + const res = await getPublicKey(args); + + equal(res.public_key, expected.public_key, 'Got expected public key'); + } + + return end(); + }); +}); diff --git a/tower_server/get_tower_server_info.js b/tower_server/get_tower_server_info.js index 04a53e7e..7cc39ee6 100644 --- a/tower_server/get_tower_server_info.js +++ b/tower_server/get_tower_server_info.js @@ -1,6 +1,8 @@ const asyncAuto = require('async/auto'); const {returnResult} = require('asyncjs-util'); +const {isLnd} = require('./../grpc'); + const inactiveTowerErr = '2 UNKNOWN: watchtower not active'; const {isArray} = Array; const {isBuffer} = Buffer; @@ -26,7 +28,7 @@ module.exports = ({lnd}, cbk) => { return asyncAuto({ // Check arguments validate: cbk => { - if (!lnd || !lnd.tower_server || !lnd.tower_server.getInfo) { + if (!isLnd({lnd, method: 'getInfo', type: 'tower_server'})) { return cbk([400, 'ExpectedAuthedLndGrpcToGetWatchtowerServerInfo']); } diff --git a/unlocker/change_password.js b/unlocker/change_password.js index 960f8e9b..70c53ca2 100644 --- a/unlocker/change_password.js +++ b/unlocker/change_password.js @@ -1,6 +1,9 @@ const asyncAuto = require('async/auto'); const {returnResult} = require('asyncjs-util'); +const {isLnd} = require('./../grpc'); + +const method = 'changePassword'; const utf8 = 'utf8'; /** Change password @@ -24,7 +27,7 @@ module.exports = (args, cbk) => { return cbk([400, 'ExpectedCurrentPasswordToChangePassword']); } - if (!args.lnd || !args.lnd.unlocker) { + if (!isLnd({method, lnd: args.lnd, type: 'unlocker'})) { return cbk([400, 'ExpectedUnauthenticatedLndGrpcToChangePassword']); } diff --git a/unlocker/create_seed.js b/unlocker/create_seed.js index d7643294..8e2c62ee 100644 --- a/unlocker/create_seed.js +++ b/unlocker/create_seed.js @@ -1,6 +1,8 @@ const asyncAuto = require('async/auto'); const {returnResult} = require('asyncjs-util'); +const {isLnd} = require('./../grpc'); + const connectionFailure = '14 UNAVAILABLE: Connect Failed'; const expectedMnemonicLength = 24; const {isArray} = Array; @@ -24,7 +26,7 @@ module.exports = ({lnd, passphrase}, cbk) => { return asyncAuto({ // Check arguments validate: cbk => { - if (!lnd || !lnd.unlocker || !lnd.unlocker.genSeed) { + if (!isLnd({lnd, method: 'genSeed', type: 'unlocker'})) { return cbk([400, 'ExpectedNonAuthenticatedLndForSeedCreation']); } @@ -41,7 +43,7 @@ module.exports = ({lnd, passphrase}, cbk) => { } if (!!err) { - return cbk([503, 'UnexpectedCreateSeedError', err]); + return cbk([503, 'UnexpectedCreateSeedError', {err}]); } if (!res) { @@ -49,11 +51,11 @@ module.exports = ({lnd, passphrase}, cbk) => { } if (!isArray(res.cipher_seed_mnemonic)) { - return cbk([503, 'ExpectedCipherSeedMnemonic', res]); + return cbk([503, 'ExpectedCipherSeedMnemonic']); } if (res.cipher_seed_mnemonic.length !== expectedMnemonicLength) { - return cbk([503, 'UnexpectedCipherSeedMnemonicLength', res]); + return cbk([503, 'UnexpectedCipherSeedMnemonicLength']); } return cbk(null, {seed: res.cipher_seed_mnemonic.join(' ')}); diff --git a/unlocker/create_wallet.js b/unlocker/create_wallet.js index afcac0aa..de1b9fa7 100644 --- a/unlocker/create_wallet.js +++ b/unlocker/create_wallet.js @@ -1,6 +1,8 @@ const asyncAuto = require('async/auto'); const {returnResult} = require('asyncjs-util'); +const {isLnd} = require('./../grpc'); + const utf8 = 'utf8'; /** Create a wallet @@ -21,7 +23,7 @@ module.exports = ({lnd, passphrase, password, seed}, cbk) => { return asyncAuto({ // Check arguments validate: cbk => { - if (!lnd || !lnd.unlocker || !lnd.unlocker.initWallet) { + if (!isLnd({lnd, method: 'initWallet', type: 'unlocker'})) { return cbk([400, 'ExpectedLndForWalletCreation']); } diff --git a/unlocker/unlock_wallet.js b/unlocker/unlock_wallet.js index 4af35dd4..dee0839d 100644 --- a/unlocker/unlock_wallet.js +++ b/unlocker/unlock_wallet.js @@ -1,6 +1,8 @@ const asyncAuto = require('async/auto'); const {returnResult} = require('asyncjs-util'); +const {isLnd} = require('./../grpc'); + const invalidPasswordError = 'invalid passphrase for master public key'; /** Unlock the wallet @@ -17,7 +19,7 @@ module.exports = ({lnd, password}, cbk) => { return asyncAuto({ // Check arguments validate: cbk => { - if (!lnd || !lnd.unlocker || !lnd.unlocker.unlockWallet) { + if (!isLnd({lnd, method: 'unlockWallet', type: 'unlocker'})) { return cbk([400, 'ExpectedLndWhenUnlockingWallet']); } diff --git a/wallet/broadcast_chain_transaction.js b/wallet/broadcast_chain_transaction.js index a4ac7e53..70600b30 100644 --- a/wallet/broadcast_chain_transaction.js +++ b/wallet/broadcast_chain_transaction.js @@ -1,8 +1,10 @@ const asyncAuto = require('async/auto'); -const isHex = require('is-hex'); const {returnResult} = require('asyncjs-util'); const {Transaction} = require('bitcoinjs-lib'); +const {isLnd} = require('./../grpc'); +const {isTransaction} = require('./../chain'); + /** Publish a raw blockchain transaction to Blockchain network peers Requires lnd built with `walletrpc` tag @@ -22,18 +24,12 @@ module.exports = ({lnd, transaction}, cbk) => { return asyncAuto({ // Check arguments validate: cbk => { - if (!lnd || !lnd.wallet || !lnd.wallet.publishTransaction) { + if (!isLnd({lnd, method: 'publishTransaction', type: 'wallet'})) { return cbk([400, 'ExpectedWalletRpcLndToSendRawTransaction']); } - if (!transaction || !isHex(transaction)) { - return cbk([400, 'ExpectedRawTransactionToBroadcastToPeers']); - } - - try { - Transaction.fromHex(transaction); - } catch (err) { - return cbk([400, 'ExpectedValidTransactionToBroadcastToPeers']); + if (!isTransaction({transaction})) { + return cbk([400, 'ExpectedTransactionHexStringToBroadcastToPeers']); } return cbk();