You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
15 KiB

5 years ago
/* eslint-disable max-len,id-length */
/* globals requester, reset */
const sinon = require('sinon');
const LndError = require('../../../../models/errors.js').LndError;
const bitcoindMocks = require('../../../mocks/bitcoind.js');
const lndMocks = require('../../../mocks/lnd.js');
describe('v1/lnd/transaction endpoints', () => {
let token;
before(async() => {
reset();
token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InRlc3QtdXNlciIsImlhdCI6MTU3NTIyNjQxMn0.N06esl2dhN1mFqn-0o4KQmmAaDW9OsHA39calpp_N9B3Ig3aXWgl064XAR9YVK0qwX7zMOnK9UrJ48KUZ-Sb4A';
});
describe('/ GET', function() {
let lndListChainTxns;
let lndOpenChannels;
let lndClosedChannels;
let lndPendingChannels;
afterEach(() => {
lndListChainTxns.restore();
lndOpenChannels.restore();
lndClosedChannels.restore();
lndPendingChannels.restore();
});
it('should return one of each transaction type', done => {
const onChainRecieved = lndMocks.getOnChainTransaction();
const onChainSent = lndMocks.getOnChainTransaction();
onChainSent.amount = '-1000000';
const onChainChannelClosed = lndMocks.getOnChainTransaction();
const onChainChannelOpen = lndMocks.getOnChainTransaction();
const onChainChannelPreviouslyOpen = lndMocks.getOnChainTransaction();
const onChainPendingOpen = lndMocks.getOnChainTransaction('c0b7045595f4f5c024af22312055497e99ed8b7b62b0c7e181d16382a07ae58b');
const onChainPendingClose = lndMocks.getOnChainTransaction('653c87589da62b5fef18538a62ecce154f94236f158d1148efab98136756ed36');
const openChannels = [lndMocks.getChannelOpen(onChainChannelOpen.txHash)];
const closedChannel = [lndMocks.getChannelClosed(undefined, onChainChannelClosed.txHash),
lndMocks.getChannelClosed(onChainChannelPreviouslyOpen.txHash, undefined)];
const pendingChannels = lndMocks.getPendingChannels();
lndListChainTxns = sinon.stub(require('../../../../services/lnd.js'), 'getOnChainTransactions')
.resolves([onChainChannelPreviouslyOpen, onChainPendingClose, onChainPendingOpen, onChainRecieved, onChainSent,
onChainChannelClosed, onChainChannelOpen]);
lndOpenChannels = sinon.stub(require('../../../../services/lnd.js'), 'getOpenChannels')
.resolves(openChannels);
lndClosedChannels = sinon.stub(require('../../../../services/lnd.js'), 'getClosedChannels')
.resolves(closedChannel);
lndPendingChannels = sinon.stub(require('../../../../services/lnd.js'), 'getPendingChannels')
.resolves(pendingChannels);
requester
.get('/v1/lnd/transaction')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body[0].type.should.equal('CHANNEL_OPEN');
res.body[1].type.should.equal('CHANNEL_CLOSE');
res.body[2].type.should.equal('ON_CHAIN_TRANSACTION_SENT');
res.body[3].type.should.equal('ON_CHAIN_TRANSACTION_RECEIVED');
res.body[4].type.should.equal('PENDING_OPEN');
res.body[5].type.should.equal('PENDING_CLOSE');
res.body[6].type.should.equal('CHANNEL_OPEN');
done();
});
});
});
describe('/estimateFee GET', function() {
let bitcoindMempoolInfo;
let lndEstimateFee;
let lndUnspentUtxos;
let lndWalletBalance;
afterEach(() => {
bitcoindMempoolInfo.restore();
lndEstimateFee.restore();
if (lndUnspentUtxos) {
lndUnspentUtxos.restore();
}
if (lndWalletBalance) {
lndWalletBalance.restore();
}
});
it('should return a fee estimate', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
const estimateFee = lndMocks.getEstimateFee();
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.resolves(estimateFee);
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=1&sweep=false')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.not.have.property('fast');
res.body.should.not.have.property('normal');
res.body.should.not.have.property('slow');
res.body.should.not.have.property('cheapest');
res.body.should.have.property('feeSat');
res.body.should.have.property('feerateSatPerByte');
done();
});
});
it('should return a fee estimate, group', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
const estimateFee = lndMocks.getEstimateFee();
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.resolves(estimateFee);
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=0&sweep=false')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.have.property('fast');
res.body.fast.should.have.property('feeSat');
res.body.fast.should.have.property('feerateSatPerByte');
res.body.should.have.property('normal');
res.body.normal.should.have.property('feeSat');
res.body.normal.should.have.property('feerateSatPerByte');
res.body.should.have.property('slow');
res.body.slow.should.have.property('feeSat');
res.body.slow.should.have.property('feerateSatPerByte');
res.body.should.have.property('cheapest');
res.body.cheapest.should.have.property('feeSat');
res.body.cheapest.should.have.property('feerateSatPerByte');
done();
});
});
it('should return insufficient funds', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.throws(new LndError('Unable to estimate fee request', {details: 'insufficient funds available to construct transaction'}));
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=1&sweep=false')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.have.property('code');
res.body.code.should.equal('INSUFFICIENT_FUNDS');
res.body.should.have.property('text');
res.body.text.should.equal('Lower amount or increase confirmation target.');
done();
});
});
it('should return output is dust', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.throws(new LndError('Unable to estimate fee request', {details: 'transaction output is dust'}));
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=1&sweep=false')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.have.property('code');
res.body.code.should.equal('OUTPUT_IS_DUST');
res.body.should.have.property('text');
res.body.text.should.equal('Transaction output is dust.');
done();
});
});
it('should return invalid address', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.throws(new LndError('Unable to estimate fee request', {details: 'checksum mismatch'}));
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=1&sweep=false')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.have.property('code');
res.body.code.should.equal('INVALID_ADDRESS');
res.body.should.have.property('text');
res.body.text.should.equal('Please validate the Bitcoin address is correct.');
done();
});
});
it('should return a sweep estimate, group', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
const estimateFee = lndMocks.getEstimateFee();
const walletBalance = lndMocks.getWalletBalance();
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.resolves(estimateFee);
lndWalletBalance = sinon.stub(require('../../../../services/lnd.js'), 'getWalletBalance')
.resolves(walletBalance);
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=0&sweep=true')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.have.property('fast');
res.body.fast.should.have.property('feeSat');
res.body.fast.should.have.property('feerateSatPerByte');
res.body.should.have.property('normal');
res.body.normal.should.have.property('feeSat');
res.body.normal.should.have.property('feerateSatPerByte');
res.body.should.have.property('slow');
res.body.slow.should.have.property('feeSat');
res.body.slow.should.have.property('feerateSatPerByte');
res.body.should.have.property('cheapest');
res.body.cheapest.should.have.property('feeSat');
res.body.cheapest.should.have.property('feerateSatPerByte');
done();
});
});
it('should return a sweep estimate', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
const estimateFee = lndMocks.getEstimateFee();
const walletBalance = lndMocks.getWalletBalance();
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.resolves(estimateFee);
lndWalletBalance = sinon.stub(require('../../../../services/lnd.js'), 'getWalletBalance')
.resolves(walletBalance);
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=1&sweep=true')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.not.have.property('fast');
res.body.should.not.have.property('normal');
res.body.should.not.have.property('slow');
res.body.should.not.have.property('cheapest');
res.body.should.have.property('feeSat');
res.body.should.have.property('feerateSatPerByte');
done();
});
});
it('should return insufficient funds for sweep estimate', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
const walletBalance = lndMocks.getWalletBalance();
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.throws(new LndError('Unable to estimate fee request', {details: 'insufficient funds available to construct transaction'}));
lndWalletBalance = sinon.stub(require('../../../../services/lnd.js'), 'getWalletBalance')
.resolves(walletBalance);
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=1&sweep=true')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.have.property('code');
res.body.code.should.equal('INSUFFICIENT_FUNDS');
res.body.should.have.property('text');
res.body.text.should.equal('Lower amount or increase confirmation target.');
done();
});
});
it('should return a fee rate too low error', done => {
const mempoolInfo = bitcoindMocks.getMempoolInfo();
mempoolInfo.result.mempoolminfee = 0.01;
bitcoindMempoolInfo = sinon.stub(require('../../../../services/bitcoind.js'), 'getMempoolInfo')
.resolves(mempoolInfo);
const estimateFee = lndMocks.getEstimateFee();
lndEstimateFee = sinon.stub(require('../../../../services/lnd.js'), 'estimateFee')
.resolves(estimateFee);
requester
.get('/v1/lnd/transaction/estimateFee?address=2NFGwqm9N9LomEh9mzQgofr1WGqkwaxPuWg&amt=100000&confTarget=1&sweep=false')
.set('authorization', `JWT ${token}`)
.end((err, res) => {
if (err) {
done(err);
}
res.should.have.status(200);
res.should.be.json;
res.body.should.not.have.property('fast');
res.body.should.not.have.property('normal');
res.body.should.not.have.property('slow');
res.body.should.not.have.property('cheapest');
res.body.should.have.property('code');
res.body.code.should.equal('FEE_RATE_TOO_LOW');
res.body.should.have.property('text');
done();
});
});
});
});