UNPKG

bitcore-wallet-service

Version:
457 lines (411 loc) 15.7 kB
'use strict'; const chai = require('chai'); const sinon = require('sinon'); const should = chai.should(); const { Utils } = require('../ts_build/lib/common/utils'); const { logger } = require('../ts_build/lib/logger'); describe('Utils', function() { describe('#getMissingFields', function() { it('should check required fields', function() { var obj = { id: 'id', name: 'name', array: ['a', 'b'], }; var fixtures = [{ args: 'id', check: [], }, { args: ['id'], check: [] }, { args: ['id, name'], check: ['id, name'], }, { args: ['id', 'name'], check: [] }, { args: 'array', check: [] }, { args: 'dummy', check: ['dummy'] }, { args: ['dummy1', 'dummy2'], check: ['dummy1', 'dummy2'] }, { args: ['id', 'dummy'], check: ['dummy'] },]; for (const f of fixtures) { Utils.getMissingFields(obj, f.args).should.deep.equal(f.check); } }); it('should fail to check required fields on non-object', function() { var obj = 'dummy'; Utils.getMissingFields(obj, 'name').should.deep.equal(['name']); }); }); describe('#hashMessage', function() { it('should create a hash', function() { var res = Utils.hashMessage('hola'); res.toString('hex').should.equal('4102b8a140ec642feaa1c645345f714bc7132d4fd2f7f6202db8db305a96172f'); }); }); describe('#verifyMessage', function() { afterEach(function() { sinon.restore(); }); it('should fail to verify a malformed signature', function() { var res = Utils.verifyMessage('hola', 'badsignature', '02555a2d45e309c00cc8c5090b6ec533c6880ab2d3bc970b3943def989b3373f16'); should.exist(res); res.should.equal(false); }); it('should fail to verify a null signature', function() { var res = Utils.verifyMessage('hola', null, '02555a2d45e309c00cc8c5090b6ec533c6880ab2d3bc970b3943def989b3373f16'); should.exist(res); res.should.equal(false); }); it('should fail to verify with wrong pubkey', function() { var res = Utils.verifyMessage('hola', '3045022100d6186930e4cd9984e3168e15535e2297988555838ad10126d6c20d4ac0e74eb502201095a6319ea0a0de1f1e5fb50f7bf10b8069de10e0083e23dbbf8de9b8e02785', '02555a2d45e309c00cc8c5090b6ec533c6880ab2d3bc970b3943def989b3373f16'); should.exist(res); res.should.equal(false); }); it('should call logger when _tryImportSignature throws', function() { var logSpy = sinon.spy(logger, 'error'); var res = Utils.verifyMessage('hola', null, '02555a2d45e309c00cc8c5090b6ec533c6880ab2d3bc970b3943def989b3373f16'); should.exist(res); res.should.equal(false); logSpy.called.should.equal(true); logSpy.calledOnceWith('_tryImportSignature encountered an error: %o').should.equal(true); }); it('should call logger when _tryImportPublicKey throws', function() { var logSpy = sinon.spy(logger, 'error'); var res = Utils.verifyMessage('hola', '3045022100d6186930e4cd9984e3168e15535e2297988555838ad10126d6c20d4ac0e74eb502201095a6319ea0a0de1f1e5fb50f7bf10b8069de10e0083e23dbbf8de9b8e02785', null); should.exist(res); res.should.equal(false); logSpy.called.should.equal(true); logSpy.calledOnceWith('_tryImportPublicKey encountered an error: %o').should.equal(true); }); it('should call logger when _tryVerifyMessage throws', function() { var logSpy = sinon.spy(logger, 'error'); var fn = () => []; sinon.stub(Utils, '_tryImportSignature').callsFake(fn); sinon.stub(Utils, '_tryImportPublicKey').callsFake(fn); var res = Utils.verifyMessage('hola', [], []); should.exist(res); res.should.equal(false); logSpy.called.should.equal(true); logSpy.calledOnceWith('_tryVerifyMessage encountered an error: %o').should.equal(true); }); it('should verify', function() { var res = Utils.verifyMessage('hola', '3045022100d6186930e4cd9984e3168e15535e2297988555838ad10126d6c20d4ac0e74eb502201095a6319ea0a0de1f1e5fb50f7bf10b8069de10e0083e23dbbf8de9b8e02785', '03bec86ad4a8a91fe7c11ec06af27246ec55094db3d86098b7d8b2f12afe47627f'); should.exist(res); res.should.equal(true); }); it('should verify paypro message', function() { const message = [ '0xf868808475727b6b82dcc694a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4880b844095ea7b300000000000000000000000034e158883efc81c5d92fde785fba48db738711ee0000000000000000000000000000000031333830363233333130303030303030018080', '0xf9014a018475727b6b830271009434e158883efc81c5d92fde785fba48db738711ee80b90124b6b4af0500000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000075727b6b0000000000000000000000000000000000000000000000000000000067d4f44bb6a77c3916143d6b6f47f77f4a2ccdaaebae426639a6971390892f47eaf362abc383935d052649b42e0b1c8b6429ba4901b1667e776eece5629bafc985bd3cab000000000000000000000000000000000000000000000000000000000000001cb93377d47517d20cc011ed69f9f31a91feafcea1c42d5c7a92c6a0aa5a1bc74175a3ed1beb148ffef9e289f6903489e227b303f7d3e4254e4eec6439ef4af518000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48018080', ]; const sig = '304402201fd726ed086f2f025041cb6ee13e8145dd41c7ecccabbe6fba59d3da7dbec40e022058bc7c174050ed46a831802819478d04f4d71270e2a837b016d49e7b0727921f'; const pubkey = { key: '03177c6fcf87bc2e13f61b199fd47fd43710d5cd5601d52487a16d9aad93262084', signature: '3045022100924c5c8396dc3a5c9e6fe28073d29876678bf6ff23eea70cc9499f75e23565550220166113dc785e4ade6aaba959f3f80eca3e667f91ef32fc82fcca7d27efecbb5e' }; var res = Utils.verifyMessage(message, sig, pubkey.key); should.exist(res); res.should.equal(true); }); }); describe('#formatAmount', function() { it('should successfully format amount', function() { var cases = [{ args: [1, 'bit'], expected: '0', }, { args: [1, 'btc'], expected: '0.00', }, { args: [0, 'bit'], expected: '0', }, { args: [12345678, 'bit'], expected: '123,457', }, { args: [12345678, 'btc'], expected: '0.123457', }, { args: [12345611, 'btc'], expected: '0.123456', }, { args: [1234, 'btc'], expected: '0.000012', }, { args: [1299, 'btc'], expected: '0.000013', }, { args: [1299, 'bch'], expected: '0.000013', }, { args: [12940, 'bch'], expected: '0.000129', }, { args: [12960, 'bch'], expected: '0.00013', }, { args: [129900000, 'bch'], expected: '1.299', }, { args: [1234567899999, 'btc'], expected: '12,345.679', }, { args: [12345678, 'bit', { thousandsSeparator: '.' }], expected: '123.457', }, { args: [12345678, 'btc', { decimalSeparator: ',' }], expected: '0,123457', }, { args: [1234567899999, 'btc', { thousandsSeparator: ' ', decimalSeparator: ',' }], expected: '12 345,679', },]; for (const testCase of cases) { Utils.formatAmount.apply(this, testCase.args).should.equal(testCase.expected); } }); }); describe('#getAddressCoin', function() { it('should identify btc as coin for 1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', function() { Utils.getAddressCoin('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA').should.equal('btc'); }); it('should identify bch as coin for CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz', function() { Utils.getAddressCoin('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz').should.equal('bch'); }); it('should return null for 1L', function() { should.not.exist(Utils.getAddressCoin('1L')); }); }); describe('#parseVersion', function() { it('should parse version', function() { Utils.parseVersion('bwc-2.3.1').should.deep.equal({ agent: 'bwc', major: 2, minor: 3, patch: 1, }); }); it('should parse version case 2', function() { Utils.parseVersion('xxss').should.deep.equal({ agent: 'xxss', }); }); it('should parse version case 3', function() { Utils.parseVersion('xxss-32').should.deep.equal({ agent: 'xxss', major: 32, minor: null, patch: null, }); }); }); describe('#parseAppVersion', function() { it('should parse user version', function() { Utils.parseAppVersion('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Copay/5.2.2 Chrome/66.0.3359.181 Electron/3.0.8 Safari/537.36').should.deep.equal({ app: 'copay', major: 5, minor: 2, patch: 2, }); }); it('should parse version case 2', function() { Utils.parseAppVersion('bitpay 5.2.2 (Android 8.0.0 - SM-N950U)').should.deep.equal({ app: 'bitpay', major: 5, minor: 2, patch: 2, }); }); it('should parse version case 3', function() { Utils.parseAppVersion('bitpay 5.2.2 (iOS 12.0 - iPhone9,2)').should.deep.equal({ app: 'bitpay', major: 5, minor: 2, patch: 2, }); }); it('should parse version case 4', function() { Utils.parseAppVersion('node-superagent/3.8.3').should.deep.equal({ app: 'other', }); }); }); describe('#translateAddress', function() { it('should translate address from btc to bch', function() { var res = Utils.translateAddress('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', 'bch'); res.should.equal('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz'); }); it('should translate address from bch to btc', function() { var res = Utils.translateAddress('HBf8isgS8EXG1r3X6GP89FmooUmiJ42wHS', 'btc'); res.should.equal('36q2G5FMGvJbPgAVEaiyAsFGmpkhPKwk2r'); }); it('should keep the address if there is nothing to do (bch)', function() { var res = Utils.translateAddress('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz', 'bch'); res.should.equal('CcJ4qUfyQ8x5NwhAeCQkrBSWVeXxXghcNz'); }); it('should keep the address if there is nothing to do (btc)', function() { var res = Utils.translateAddress('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', 'btc'); should.exist(res); res.should.equal('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA'); }); }); describe('#getIpFromReq', () => { it('should get the ip if header x-forwarded-for exists', function() { const req = { headers: { 'x-forwarded-for': '1.2.3.4' } } const ip = Utils.getIpFromReq(req); should.exist(ip); ip.should.equal('1.2.3.4'); }); it('should get the ip if header x-forwarded-for exists and has more than one ip', function() { const req = { headers: { 'x-forwarded-for': '1.2.3.4, 5.6.7.8, 9.10.11.12' } } const ip = Utils.getIpFromReq(req); should.exist(ip); ip.should.equal('1.2.3.4'); }); it('should get the ip if header x-real-ip exists', function() { const req = { headers: { 'x-real-ip': '1.2.3.4' } } const ip = Utils.getIpFromReq(req); should.exist(ip); ip.should.equal('1.2.3.4'); }); it('should get the ip if header x-real-ip exists and has more than one ip', function() { const req = { headers: { 'x-real-ip': '1.2.3.4, 5.6.7.8, 9.10.11.12' } } const ip = Utils.getIpFromReq(req); should.exist(ip); ip.should.equal('1.2.3.4'); }); it('should get the ip if req.ip exists', function() { const req = { ip: '1.2.3.4' } const ip = Utils.getIpFromReq(req); should.exist(ip); ip.should.equal('1.2.3.4'); }); it('should get the ip if req.connection.remoteAddress exists', function() { const req = { connection: { remoteAddress: '1.2.3.4' } } const ip = Utils.getIpFromReq(req); should.exist(ip); ip.should.equal('1.2.3.4'); }); it('should get an empty string if no case is met', function() { const req = {} const ip = Utils.getIpFromReq(req); ip.should.equal(''); }); }); describe('#sortAsc', function() { it('should sort a simple array', function() { const res = Utils.sortAsc([3, 1, 2]); res.should.deep.equal([1, 2, 3]); }); it('should sort a simple array with undefined values', function() { const res = Utils.sortAsc([3, undefined, 1, 2, '\uFFFE']); res.should.deep.equal([1, 2, 3, '\uFFFE', undefined]); // undefined should be in last position }); it('should sort a simple array with bool values', function() { const res = Utils.sortAsc([3, true, 1, true, 2, false]); res.should.deep.equal([false, true, 1, true, 2, 3]); // false is considered as 0, true as 1 }); it('should sort a simple array with NaN values', function() { const res = Utils.sortAsc([3, NaN, 1, 2]); res.should.deep.equal([NaN, 1, 2, 3]); // NaN should be considered 0 }); it('should sort a simple array with null values', function() { const res = Utils.sortAsc([3, 1, null, 2, 0, null]); res.should.deep.equal([null, 0, null, 1, 2, 3]); // null is considered as 0 }); it('should sort an array of objects', function() { const res = Utils.sortAsc([{ a: 3 }, { a: 1 }, { a: 2 }], 'a'); res.should.deep.equal([ { a: 1 }, { a: 2 }, { a: 3 } ]); }); it('should sort an array of objects with priority', function() { const res = Utils.sortAsc([{ a: 2, b: 2 }, { a: 1, b: 3 }, { a: 2, b: 1 }], 'a', 'b'); res.should.deep.equal([ { a: 1, b: 3 }, { a: 2, b: 1 }, { a: 2, b: 2 } ]); }); it('should sort an array of objects with nested key', function() { const res = Utils.sortAsc([{ a: { b: 3 }}, { a: { b: 1 }}, { a: { b: 2 }}], ['a', 'b']); res.should.deep.equal([ { a: { b: 1 }}, { a: { b: 2 }}, { a: { b: 3 }} ]); }); }); describe('#difference', function() { it('should return the diff', function() { const res = Utils.difference([1, 2, 3], [1, 3, 4]); res.should.deep.equal([2]); }); it('should return copy of arr1 if arr2 is not given', function() { const arr1 = [1, 2, 3]; const res = Utils.difference([1, 2, 3]); res.should.deep.equal([1, 2, 3]); (arr1 === res).should.be.false; }); it('should return empty array if arr1 is not given', function() { const res = Utils.difference(undefined, [1, 2, 3]); res.should.deep.equal([]); }); it('should return empty array if no params given', function() { const res = Utils.difference(); res.should.deep.equal([]); }); it('should return empty array if arr1 non-array is given', function() { const res = Utils.difference(1); res.should.deep.equal([]); }); it('should return all arr1 elemnts if arr2 non-array is given', function() { const res = Utils.difference([1, 2, 3], 1); res.should.deep.equal([1, 2, 3]); }); it('should return all arr1 elemnts if arr2 non-array is given', function() { const res = Utils.difference([1, 2, 3], 1); res.should.deep.equal([1, 2, 3]); }); }); });