'use strict'; var should = require('should'); var sinon = require('sinon'); var TxController = require('../lib/transactions'); var bcoin = require('bcoin'); var _ = require('lodash'); describe('Transactions', function() { describe('/tx/:txid', function() { it('should have correct data', function(done) { var flosight = { 'txid': 'eac9723230b8b632117ac3d75288d6f8eb81cf1ea553eb9fd42562d5f767d54a', 'version': 1, 'locktime': 0, 'vin': [ { 'txid': '46e58a68bb9ec9b458a9599dc2bac1e1fa09ad15c29c7f923c8f6f0aa33d6456', 'vout': 1, 'sequence': 4294967295, 'n': 0, 'scriptSig': { 'hex': '47304402203ddb49db43074b421ec6d5604ae91aac37f4715139e0c83ea1145379e8cbf02702207fbc92c4038ad501989b097844ae4e337c9388f0713110620b40e582b85fdff3012102cd90aa18ec8e3b35c0447ffc713c945cb837429d33d075d1b0f050c72ea838d2', 'asm': '304402203ddb49db43074b421ec6d5604ae91aac37f4715139e0c83ea1145379e8cbf02702207fbc92c4038ad501989b097844ae4e337c9388f0713110620b40e582b85fdff301 02cd90aa18ec8e3b35c0447ffc713c945cb837429d33d075d1b0f050c72ea838d2' }, 'addr': '1NqgMfGUeELP2BfxD4hQuJSRq2d3DVJcCi', 'valueSat': 1546063700, 'value': 15.460637, 'doubleSpentTxID': null, 'isConfirmed': null, 'confirmations': null, 'unconfirmedInput': null } ], 'vout': [ { 'value': '0.37100000', 'n': 0, 'scriptPubKey': { 'hex': '76a914bc345e0e0e5b0dbddb7b35ef3430fedd528dd1b788ac', 'asm': 'OP_DUP OP_HASH160 bc345e0e0e5b0dbddb7b35ef3430fedd528dd1b7 OP_EQUALVERIFY OP_CHECKSIG', 'addresses': [ '1JA8mcfrBv1YYsASAp9jtohm8x2q7LnUhs' ], 'type': 'pubkeyhash' }, 'spentHeight': null, 'spentIndex': null, 'spentTxId': null }, { 'value': '15.08763700', 'n': 1, 'scriptPubKey': { 'hex': '76a914c2c74d7519d4425fc1a253f066d980164341554a88ac', 'asm': 'OP_DUP OP_HASH160 c2c74d7519d4425fc1a253f066d980164341554a OP_EQUALVERIFY OP_CHECKSIG', 'addresses': [ '1Jktr121Hm63qtWBMV8dCNJNW2KtWXy4fp' ], 'type': 'pubkeyhash' }, 'spentHeight': null, 'spentIndex': null, 'spentTxId': null } ], 'blockhash': '0000000000000000009d2e9b7a984d55c6c99ba62f98e9bc7dad8b1e779045a3', 'blockheight': 481763, 'confirmations': 4, 'time': 1503507151, 'blocktime': 1503507151, 'valueOut': 15.458637, 'size': 225, 'valueIn': 15.460637, 'fees': 0.002 }; var bcoinTx = bcoin.tx.fromRaw('010000000156643da30a6f8f3c927f9cc215ad09fae1c1bac29d59a958b4c99ebb688ae546010000006a47304402203ddb49db43074b421ec6d5604ae91aac37f4715139e0c83ea1145379e8cbf02702207fbc92c4038ad501989b097844ae4e337c9388f0713110620b40e582b85fdff3012102cd90aa18ec8e3b35c0447ffc713c945cb837429d33d075d1b0f050c72ea838d2ffffffff02e0193602000000001976a914bc345e0e0e5b0dbddb7b35ef3430fedd528dd1b788ac34e8ed59000000001976a914c2c74d7519d4425fc1a253f066d980164341554a88ac00000000', 'hex'); bcoinTx.__blockhash = '0000000000000000009d2e9b7a984d55c6c99ba62f98e9bc7dad8b1e779045a3'; bcoinTx.blockhash = '0000000000000000009d2e9b7a984d55c6c99ba62f98e9bc7dad8b1e779045a3'; bcoinTx.__height = 481763; bcoinTx.__inputValues = [ 1546063700 ]; bcoinTx.__timestamp = 1503507151; bcoinTx.inputSatoshis = [ 1546063700 ]; bcoinTx.feeSatoshis = 200000; bcoinTx.outputSatoshis = 1545863700; var node = { services: { block: { getTip: sinon.stub().returns({ height: 481766 }) }, transaction: { getDetailedTransaction: sinon.stub().callsArgWith(2, null, bcoinTx) } } }; var transactions = new TxController(node); var txid = 'eac9723230b8b632117ac3d75288d6f8eb81cf1ea553eb9fd42562d5f767d54a'; var req = { params: { txid: txid } }; var res = {}; var next = function() { should(req.transaction).eql(flosight); done(); }; transactions.transaction(req, res, next); }); }); describe('/txs', function() { var sandbox = sinon.sandbox.create(); afterEach(function() { sandbox.restore(); }); it('by block hash', function(done) { var blockOverview = { hash: '0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7', height: 533974, chainWork: '0000000000000000000000000000000000000000000000054626b1839ade284a', prevHash: '00000000000001a55f3214e9172eb34b20e0bc5bd6b8007f3f149fca2c8991a4', txids: [ '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5' ] }; var bcoinTx = bcoin.tx.fromRaw('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010bffffffff0100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac00000000', 'hex'); bcoinTx.__blockhash = '000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd'; bcoinTx.blockhash = '000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd'; bcoinTx.__height = 2; bcoinTx.__inputValues = [ 1546063700 ]; bcoinTx.__timestamp = 1231469744; bcoinTx.inputSatoshis = [ 1546063700 ]; bcoinTx.feeSatoshis = 0; bcoinTx.outputSatoshis = 5000000000; var node = { services: { block: { getTip: sinon.stub().returns({ height: 481773 }), getBlockOverview: sinon.stub().callsArgWith(1, null, blockOverview), }, transaction: { getDetailedTransaction: sinon.stub().callsArgWith(2, null, bcoinTx) } } }; var transactions = new TxController(node); var flosight = { 'pagesTotal': 1, 'txs': [ { 'txid': '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5', 'version': 1, 'locktime': 0, 'vin': [ { 'coinbase': '04ffff001d010b', 'sequence': 4294967295, 'n': 0 } ], 'vout': [ { 'value': '50.00000000', 'n': 0, 'scriptPubKey': { 'hex': '41047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac', 'asm': '047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77 OP_CHECKSIG', 'addresses': [ '1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1' ], 'type': 'pubkeyhash' }, 'spentHeight': null, 'spentIndex': null, 'spentTxId': null } ], 'blockhash': '000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd', 'blockheight': 2, 'confirmations': 481772, 'time': 1231469744, 'blocktime': 1231469744, 'isCoinBase': true, 'valueOut': 50, 'size': 134 } ] }; var req = { query: { block: '0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7' } }; var res = { jsonp: function(data) { should(data).eql(flosight); done(); } }; transactions.list(req, res); }); it('by address, single/coinbase', function(done) { var hex = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3103835807244d696e656420627920416e74506f6f6c6a2f4542312f4144362f4e59412f1d205999aaa02b1200001fff0200ffffffff026fb2a54c000000001976a914ad7309dfc032d7f6b652e0c29ee353e63fffec6688ac0000000000000000266a24aa21a9ed55882e9fed16c5d3b6d77e4160a56f58c70d354d02888a99486125b638231c8100000000'; var bcoinTx = bcoin.tx.fromRaw(hex, 'hex'); bcoinTx.__height = 481411; bcoinTx.outputSatoshis = 1285927535; bcoinTx.__timestamp = 1503242912; bcoinTx.__blockhash = '000000000000000000926a0cd4a05ef116514cbf1852edc306d13eb951ec0b54'; bcoinTx.blockhash = '000000000000000000926a0cd4a05ef116514cbf1852edc306d13eb951ec0b54'; var node = { services: { block: { getTip: sinon.stub().returns({ height: 534223 }) }, address: { getAddressHistory: sinon.stub().callsArgWith(2, null, { totalCount: 1, from: 0, to: 0xffffffff, items: [bcoinTx] }) } } }; var flosight = { 'pagesTotal': 1, 'txs': [ { 'txid': '1c01a2090db0850e1f1049bea02e4bbf44b6790dfeb8e054f2beb69339ef52d4', 'version': 1, 'locktime': 0, 'vin': [ { 'coinbase': '03835807244d696e656420627920416e74506f6f6c6a2f4542312f4144362f4e59412f1d205999aaa02b1200001fff0200', 'sequence': 4294967295, 'n': 0 } ], 'vout': [ { 'value': '12.85927535', 'n': 0, 'scriptPubKey': { 'hex': '76a914ad7309dfc032d7f6b652e0c29ee353e63fffec6688ac', 'asm': 'OP_DUP OP_HASH160 ad7309dfc032d7f6b652e0c29ee353e63fffec66 OP_EQUALVERIFY OP_CHECKSIG', 'addresses': [ '1Gp7iCzDGMZiV55Kt8uKsux6VyoHe1aJaN' ], 'type': 'pubkeyhash' }, 'spentHeight': null, 'spentIndex': null, 'spentTxId': null }, { 'value': '0.00000000', 'n': 1, 'scriptPubKey': { 'hex': '6a24aa21a9ed55882e9fed16c5d3b6d77e4160a56f58c70d354d02888a99486125b638231c81', 'asm': 'OP_RETURN aa21a9ed55882e9fed16c5d3b6d77e4160a56f58c70d354d02888a99486125b638231c81' }, 'spentHeight': null, 'spentIndex': null, 'spentTxId': null } ], 'blockhash': '000000000000000000926a0cd4a05ef116514cbf1852edc306d13eb951ec0b54', 'blockheight': 481411, 'confirmations': 52813, 'time': 1503242912, 'blocktime': 1503242912, 'isCoinBase': true, 'valueOut': 12.85927535, 'size': 181 } ] }; var req = { query: { address: 'mkPvAKZ2rar6qeG3KjBtJHHMSP1wFZH7Er' } }; var res = { jsonp: function(data) { should(data).eql(flosight); done(); } }; var transactions = new TxController(node); transactions.list(req, res); }); }); describe('/rawtx/:txid', function() { it('should give the correct data', function(done) { var hex = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2303d6250800feb0aae355fe263600000963676d696e6572343208ae5800000000000000ffffffff01c018824a000000001976a91468bedce8982d25c3b6b03f6238cbad00378b8ead88ac00000000'; var node = { services: { block: { getTip: sinon.stub().returns({ height: 534233 }) }, transaction: { getTransaction: sinon.stub().callsArgWith(1, null, bcoin.tx.fromRaw(new Buffer(hex, 'hex'))) } } }; var transactions = new TxController(node); var txid = '25a988e54b02e0e5df146a0f8fa7b9db56210533a9f04bdfda5f4ceb6f77aadd'; var res = {}; var req = { params: { txid: txid } }; var next = function() { should(req.rawTransaction.rawtx).eql(hex); done(); }; transactions.rawTransaction(req, res, next); }); }); describe('#transformInvTransaction', function() { it('should give the correct data', function() { var flosight = { 'txid': 'a15a7c257af596704390d345ff3ea2eed4cd02ce8bfb8afb700bff82257e49fb', 'valueOut': 0.02038504, 'vout': [ { '3DQYCLG6rZdtV2Xw8y4YtozZjNHYoKsLuo': 45000 }, { '12WvZmssxT85f81dD6wcmWznxbnFkEpNMS': 1993504 } ], 'isRBF': false }; var rawTx = '01000000011760bc271a397bfb65b7506d430d96ebb1faff467ed957516238a9670e806a86010000006b483045022100f0056ae68a34cdb4194d424bd727c18f82653bca2a198e0d55ab6b4ee88bbdb902202a5745af4f72a5dbdca1e3d683af4667728a8b20e8001e0f8308a4d329ce3f96012102f3af6e66b61c9d99c74d9a9c3c1bec014a8c05d28bf339c8f5f395b5ce319e7dffffffff02c8af00000000000017a9148083b541ea15f1d18c5ca5e1fd47f9035cce24ed87206b1e00000000001976a91410a0e70cd91a45e0e6e409e227ab285bd61592b188ac00000000'; var tx = bcoin.tx.fromRaw(rawTx, 'hex'); var node = { services: { block: { getTip: sinon.stub().returns({ height: 534233 }) } }, network: 'livenet' }; var transactions = new TxController(node); var result = transactions.transformInvTransaction(tx); should(result).eql(flosight); }); it('will not include null values in vout array', function() { var flosight = { 'txid': '716d54157c31e52c820494c6c2b8af1b64352049f4dcc80632aa15742a7f82c4', 'valueOut': 12.5002, 'vout': [ { 'n4eY3qiP9pi32MWC6FcJFHciSsfNiYFYgR': 12.5002 * 1e8 } ], 'isRBF': false }; var rawTx = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0403ebc108ffffffff04a0ca814a000000001976a914fdb9fb622b0db8d9121475a983288a0876f4de4888ac0000000000000000226a200000000000000000000000000000000000000000000000000000ffff0000000000000000000000001b6a1976a914fdb9fb622b0db8d9121475a983288a0876f4de4888ac0000000000000000326a303a791c8e85200500d89769b4f958e4db6b3ec388ddaa30233c4517d942d440c24ae903bff40d97ca06465fcf2714000000000000'; var tx = bcoin.tx.fromRaw(rawTx, 'hex'); var node = { services: { block: { getTip: sinon.stub().returns({ height: 534233 }) } }, network: 'testnet' }; var transactions = new TxController(node); var result = transactions.transformInvTransaction(tx); should(result).eql(flosight); }); it('should detect RBF txs', function() { var testCases = [ { rawTx: '01000000017fa897c3556271c34cb28c03c196c2d912093264c9d293cb4980a2635474467d010000000f5355540b6f93598893578893588851ffffffff01501e0000000000001976a914aa2482ce71d219018ef334f6cc551ee88abd920888ac00000000', expected: false, }, { rawTx: '01000000017fa897c3556271c34cb28c03c196c2d912093264c9d293cb4980a2635474467d010000000f5355540b6f935988935788935888510000000001501e0000000000001976a914aa2482ce71d219018ef334f6cc551ee88abd920888ac00000000', expected: true, }, ]; var node = { services: { block: { getTip: sinon.stub().returns({ height: 534233 }) } }, network: 'livenet' }; var transactions = new TxController(node); _.each(testCases, function(tc) { var tx = bcoin.tx.fromRaw(tc.rawTx, 'hex'); var result = transactions.transformInvTransaction(tx); should.exist(result.isRBF); result.isRBF.should.equal(tc.expected); }); }); }); });