diff --git a/package.json b/package.json index caa5b5c..a0aa695 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ "test:integration:local": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration", "test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/", "test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet", - "test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#sort0ConfTxs' test/integration/", - "test:temp2": "mocha --timeout=30000 -g '#sortConfTxs' test/unit/", + "test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#rawtransaction' test/integration/", + "test:temp2": "mocha --timeout=30000 -g '#_getInputAddrs' test/unit/", "coverage": "nyc report --reporter=text-lcov | coveralls", "coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/", "docs": "./node_modules/.bin/apidoc -i src/ -o docs", diff --git a/src/raw-transactions.js b/src/raw-transactions.js index 5cff4c4..f2d126c 100644 --- a/src/raw-transactions.js +++ b/src/raw-transactions.js @@ -289,6 +289,49 @@ class RawTransactions { } } + // Retrieve the parent TX (pTX) for the given TXID in order to retrieve the BCH + // address of the inputs for the given TXID. + // Assumes a single TXID. Does not yet work with an array of TXIDs. + // This function returns an array of objects, each object if formated as follows: + // { + // vin: 0, // The position of the input for the given txid + // address: bitcoincash:qzhrpmu7nruyfcemeanqh5leuqcnf6zkjq4qm9nqh0 + // } + async _getInputAddrs (txid) { + try { + // Get the TX details for the transaction under consideration. + const txDetails = await this.getRawTransaction(txid, true) + // console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`) + + const retArray = [] // Return array + + for (let i = 0; i < txDetails.vin.length; i++) { + // The first input represents the sender of the BCH or tokens. + const vin = txDetails.vin[i] + const inputTxid = vin.txid + const inputVout = vin.vout + + // Get the TX details for the input, in order to retrieve the address of + // the sender. + const txDetailsParent = await this.getRawTransaction(inputTxid, true) + console.log(`txDetailsParent: ${JSON.stringify(txDetailsParent, null, 2)}`) + + // The vout from the previous tx that represents the sender. + const voutSender = txDetailsParent.vout[inputVout] + + retArray.push({ + vin: i, + address: voutSender.scriptPubKey.addresses[0] + }) + } + + return retArray + } catch (error) { + if (error.response && error.response.data) throw error.response.data + else throw error + } + } + /** * @api RawTransactions.sendRawTransaction() sendRawTransaction() * @apiName sendRawTransaction diff --git a/test/integration/rawtransaction.js b/test/integration/rawtransaction.js index 5bfcd85..0d4d68b 100644 --- a/test/integration/rawtransaction.js +++ b/test/integration/rawtransaction.js @@ -230,7 +230,7 @@ describe('#rawtransaction', () => { below expect error messages returned from the server, but at least test that the server is responding on those endpoints, and responds consistently. */ - describe('sendRawTransaction', () => { + describe('#sendRawTransaction', () => { it('should throw error on array size rate limit', async () => { try { const dataMock = @@ -248,6 +248,21 @@ describe('#rawtransaction', () => { } }) }) + + describe('#_getInputAddrs', () => { + it('should return an array of input addresses', async () => { + // const txid = '32233db13f2ae6d82b6262f335643dccf09fc0bcfcef4bc3fbe023355f02e112' + const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3' + + const result = await bchjs.RawTransactions._getInputAddrs(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.equal(result.length, 1) + assert.property(result[0], 'vin') + assert.property(result[0], 'address') + }) + }) }) function sleep (ms) { diff --git a/test/unit/fixtures/rawtransaction-mock.js b/test/unit/fixtures/rawtransaction-mock.js new file mode 100644 index 0000000..03ecfc7 --- /dev/null +++ b/test/unit/fixtures/rawtransaction-mock.js @@ -0,0 +1,54 @@ +/* + Mocking data for the RawTransactions unit tests. +*/ + +const mockTx = { + txid: '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3', + vin: [ + { + txid: '8a6b3b70569270f0bdbf68fd12a410aef8f7bf044bc88ab02386a1572024b2bd', + vout: 0, + scriptSig: { + asm: + '3044022035c42f5b10d412445c5ecc5feea42c7f885c433669306c699da0f687216c61d5022018c81cd0ea68101cf3cbe0af67165fca1ce3d667be69d0c9329f0679bbee6ba0[ALL|FORKID] 030152eb20beaa692daaa1a27596dcc98cc06ccbc6eec23d6182a08c7bdaa29ea9', + hex: + '473044022035c42f5b10d412445c5ecc5feea42c7f885c433669306c699da0f687216c61d5022018c81cd0ea68101cf3cbe0af67165fca1ce3d667be69d0c9329f0679bbee6ba04121030152eb20beaa692daaa1a27596dcc98cc06ccbc6eec23d6182a08c7bdaa29ea9' + }, + sequence: 4294967295 + } + ] +} + +const mockParentTx1 = { + vout: [ + { + value: 0.04199959, + n: 0, + scriptPubKey: { + asm: + 'OP_DUP OP_HASH160 d5258a73b5c8f94c939d7fe96f78ce97906083be OP_EQUALVERIFY OP_CHECKSIG', + hex: '76a914d5258a73b5c8f94c939d7fe96f78ce97906083be88ac', + reqSigs: 1, + type: 'pubkeyhash', + addresses: ['bitcoincash:qr2jtznnkhy0jnynn4l7jmmce6teqcyrhc8herhlgt'] + } + }, + { + value: 1.66296161, + n: 1, + scriptPubKey: { + asm: + 'OP_DUP OP_HASH160 5e95c308c25c74c64c5ffe44a60a4d9b35743e90 OP_EQUALVERIFY OP_CHECKSIG', + hex: '76a9145e95c308c25c74c64c5ffe44a60a4d9b35743e9088ac', + reqSigs: 1, + type: 'pubkeyhash', + addresses: ['bitcoincash:qp0ftscgcfw8f3jvtllyffs2fkdn2ap7jqreakn4ye'] + } + } + ] +} + +module.exports = { + mockTx, + mockParentTx1 +} diff --git a/test/unit/raw-tranactions.js b/test/unit/raw-tranactions.js index eaf682a..54c3c13 100644 --- a/test/unit/raw-tranactions.js +++ b/test/unit/raw-tranactions.js @@ -5,6 +5,7 @@ // Public npm libraries const assert = require('assert') +const assert2 = require('chai').assert const axios = require('axios') const sinon = require('sinon') // const nock = require("nock") // HTTP mocking @@ -18,16 +19,19 @@ let bchjs // const util = require("util") // util.inspect.defaultOptions = { depth: 1 } +const mockData = require('./fixtures/rawtransaction-mock') + describe('#RawTransactions', () => { + let sandbox beforeEach(() => { + sandbox = sinon.createSandbox() + bchjs = new BCHJS() }) - describe('#decodeRawTransaction', () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) + afterEach(() => sandbox.restore()) + describe('#decodeRawTransaction', () => { it('should decode raw transaction', done => { const data = { txid: @@ -132,4 +136,42 @@ describe('#RawTransactions', () => { // assert.strictEqual(data, result.data) // }) }) + + describe('#_getInputAddrs', () => { + it('should return an array of input addresses', async () => { + sandbox + .stub(bchjs.RawTransactions, 'getRawTransaction') + .onCall(0) + .resolves(mockData.mockTx) + .onCall(1) + .resolves(mockData.mockParentTx1) + + const txid = + '32233db13f2ae6d82b6262f335643dccf09fc0bcfcef4bc3fbe023355f02e112' + + const result = await bchjs.RawTransactions._getInputAddrs(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert2.isArray(result) + assert2.equal(result.length, 1) + assert2.property(result[0], 'vin') + assert2.property(result[0], 'address') + }) + + it('should catch and throw and error', async () => { + try { + sandbox + .stub(bchjs.RawTransactions, 'getRawTransaction') + .rejects(new Error('test error')) + + await bchjs.RawTransactions._getInputAddrs('fake txid') + + assert.fail('Unexpected result') + } catch (err) { + // console.log(err) + + assert2.equal(err.message, 'test error') + } + }) + }) })