feat(transaction): Refactoring to allow up to 20 TXIDs per call

This commit is contained in:
Chris Troutner
2022-01-28 12:04:35 -08:00
parent d9fa44328a
commit 5a3d91e3b0
7 changed files with 285 additions and 30 deletions
+6 -14
View File
@@ -429,7 +429,8 @@ class BCHRPC {
* @apiPermission public
* @apiName Transaction
* @apiGroup JSON BCH
* @apiDescription Get data about a specific transaction.
* @apiDescription Get expanded transaction data for an array of transaction
* IDs. Each call is limited to 20 TXIDs or less.
* Given a transaction the endpoint will return an object with the
* following properties
*
@@ -455,13 +456,13 @@ class BCHRPC {
* - status: - HTTP Status Code
*
* @apiExample Example usage:
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "transaction", "txid": "01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b"}}
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "transaction", "txids": ["01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b"]}}
*
* @apiSuccessExample {json} Success-Response:
* {
* "jsonrpc":"2.0",
* "id":"555",
* "result":{
* "txData": [{
* "method":"bch",
* "reciever":"QmU86vLVbUY1UhziKB6rak7GPKRA2QHWvzNm2AjEvXNsT6",
* "value":{
@@ -516,20 +517,11 @@ class BCHRPC {
* "status":200
* }
* }
* }
* }]
*/
async transaction (rpcData) {
try {
// console.log('transaction rpcData: ', rpcData)
const txid = rpcData.payload.params.txid
const data = await this.bchjs.Transaction.get(txid.toString())
console.log(`data: ${JSON.stringify(data, null, 2)}`)
const retObj = data
retObj.status = 200
const retObj = await this.useCases.bch.getTxData(rpcData)
return retObj
} catch (err) {
console.error('Error in JSON RPC BCH transaction()')
+65 -9
View File
@@ -19,9 +19,9 @@ class BCHUseCases {
// Get transaction history for an address, sorted by block height.
async getTransactions (rpcData) {
try {
console.log(
`getTransactions rpcData: ${JSON.stringify(rpcData, null, 2)}`
)
// console.log(
// `getTransactions rpcData: ${JSON.stringify(rpcData, null, 2)}`
// )
// Get the list of addresses.
const addr = rpcData.payload.params.address
@@ -34,11 +34,11 @@ class BCHUseCases {
// Default to page 1
let page = rpcData.payload.params.page
if (!page) page = 0
console.log('page: ', page)
// console.log('page: ', page)
// Get the transaction history for the list of addresses.
const data = await this.bchjs.Electrumx.transactions([addr])
console.log(`data: ${JSON.stringify(data, null, 2)}`)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
if (!data.success) {
throw new Error('Could not query Fulcrum indexer.')
@@ -50,13 +50,13 @@ class BCHUseCases {
data.transactions[0].transactions,
sortOrder
)
console.log(`txsArr: ${JSON.stringify(txsArr, null, 2)}`)
// console.log(`txsArr: ${JSON.stringify(txsArr, null, 2)}`)
// Paginate the results
const pagedResults = this.bchjs.Util.chunk100(txsArr)
console.log(
`pagedResults[page]: ${JSON.stringify(pagedResults[page], null, 2)}`
)
// console.log(
// `pagedResults[page]: ${JSON.stringify(pagedResults[page], null, 2)}`
// )
const retObj = {
address: addr,
@@ -80,6 +80,62 @@ class BCHUseCases {
}
}
}
// Get transaction data for an array of TXIDs.
async getTxData (rpcData) {
try {
// console.log('transaction rpcData: ', rpcData)
const txids = rpcData.payload.params.txids
// console.log(`txids: ${JSON.stringify(txids, null, 2)}`)
if (!Array.isArray(txids)) {
return {
success: false,
status: 422,
message: 'Input txids must be an array of transaction IDs.',
endpoint: 'transaction'
}
}
if (txids.length > 20) {
return {
success: false,
status: 422,
message: 'Array input must be 20 elements or less.',
endpoint: 'transaction'
}
}
const txData = []
for (let i = 0; i < txids.length; i++) {
const thisTxid = txids[i]
const data = await this.bchjs.Transaction.get(thisTxid.toString())
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
txData.push(data)
}
const retObj = {
status: 200,
txData
}
return retObj
} catch (err) {
console.error('Error in JSON RPC BCH transaction()')
// throw err
// Return an error response
return {
success: false,
status: 422,
message: err.message,
endpoint: 'transaction'
}
}
}
}
module.exports = BCHUseCases
@@ -82,4 +82,49 @@ describe('#BCH', () => {
assert.isAbove(result.txs[1].height, result.txs[0].height)
})
})
describe('#getTxData', () => {
it('should get data for a single non-SLP tx', async () => {
const txids = [
'11384d7e5a8af93806591debe5bbe2d7826aeea987b874dfbe372dfdcc0ee54f'
]
const rpcData = {
payload: {
params: {
txids
}
}
}
const result = await uut.getTxData(rpcData)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.status, 200)
assert.isArray(result.txData)
assert.equal(result.txData.length, 1)
})
it('should get data on multiple non-SLP txs', async () => {
const txids = [
'11384d7e5a8af93806591debe5bbe2d7826aeea987b874dfbe372dfdcc0ee54f',
'f9b54fd8d27b0237923437ed4df8d45557f52b3ad4d8d03d4104e925c84ab4ca'
]
const rpcData = {
payload: {
params: {
txids
}
}
}
const result = await uut.getTxData(rpcData)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.status, 200)
assert.isArray(result.txData)
assert.equal(result.txData.length, 2)
})
})
})
@@ -155,6 +155,7 @@ describe('#BCHRPC', () => {
assert.equal(result, true)
})
it('should route to the pubkey method', async () => {
// Mock dependencies
sandbox.stub(uut, 'pubKey').resolves(true)
@@ -387,10 +388,7 @@ describe('#BCHRPC', () => {
})
describe('#transaction', () => {
it('should return data from bchjs', async () => {
// Mock dependencies
sandbox.stub(uut.bchjs.Transaction, 'get').resolves({ success: true })
it('should route data to the use-case library', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
@@ -411,7 +409,7 @@ describe('#BCHRPC', () => {
it('should return an error for invalid input', async () => {
// Force an error
sandbox
.stub(uut.bchjs.Transaction, 'get')
.stub(uut.useCases.bch, 'getTxData')
.rejects(new Error('Invalid data'))
// Generate the parsed data that the main router would pass to this
@@ -465,6 +463,7 @@ describe('#BCHRPC', () => {
assert.property(pubKey, 'publicKey')
assert.equal(pubKey.publicKey, mock.publicKey)
})
it('should throw an error if public key is not found', async () => {
// Force an error
sandbox
@@ -23,6 +23,72 @@ const fulcrumOut01 = {
]
}
module.exports = {
fulcrumOut01
const txData01 = {
txData: {
txid: '11384d7e5a8af93806591debe5bbe2d7826aeea987b874dfbe372dfdcc0ee54f',
hash: '11384d7e5a8af93806591debe5bbe2d7826aeea987b874dfbe372dfdcc0ee54f',
version: 1,
size: 371,
locktime: 0,
vin: [
{
txid: 'c4f3e3d6f146f1d54bd5c22cad6b189637b44a655b057cd0303d85558c033594',
vout: 310,
scriptSig: {
asm: '304402200d40ad599c7e33c7f5245080c0ea35c837c0d142b82eb6824fceaf049c2a355902200590e35951111f6c603c0fb4e513cf0e47734d7bc8b45f105585bea0f2b923de[ALL|FORKID] 0352ede8ab8f2ae49b3921c385e59f700ae1e6f73cbb37f61cd1244e70c99bf496',
hex: '47304402200d40ad599c7e33c7f5245080c0ea35c837c0d142b82eb6824fceaf049c2a355902200590e35951111f6c603c0fb4e513cf0e47734d7bc8b45f105585bea0f2b923de41210352ede8ab8f2ae49b3921c385e59f700ae1e6f73cbb37f61cd1244e70c99bf496'
},
sequence: 4294967295,
address: 'bitcoincash:qzgkqac0tlrfsajg2f7e4e8nyh3glmn4s523tysd6r',
value: 0.08809511
},
{
txid: 'c32265f89b2d5d1d5ffd5212b3245105d7d0ba726306fbeb93cd866ce0fd2d61',
vout: 1,
scriptSig: {
asm: '3045022100be9cc5e982110b0f563460baa19a42f34ab775708ad3461a6cf065db9dffb1b7022031ffb2721828be4aa1f4967a367e8af2848cdfce41c68565d0a2a2a70117ea5d[ALL|FORKID] 02d2eabc002e25fc08622a71d9ef96c17b21176f438d67bc537ac49a2e0e594372',
hex: '483045022100be9cc5e982110b0f563460baa19a42f34ab775708ad3461a6cf065db9dffb1b7022031ffb2721828be4aa1f4967a367e8af2848cdfce41c68565d0a2a2a70117ea5d412102d2eabc002e25fc08622a71d9ef96c17b21176f438d67bc537ac49a2e0e594372'
},
sequence: 4294967295,
address: 'bitcoincash:qr92r3ms2696ghpmk9467px90jrzzdesy5fmqc05p3',
value: 0.00011805
}
],
vout: [
{
value: 0.0814,
n: 0,
scriptPubKey: {
asm: 'OP_HASH160 84459c796307fe2c0316ce7a8bffc511c27bcd55 OP_EQUAL',
hex: 'a91484459c796307fe2c0316ce7a8bffc511c27bcd5587',
reqSigs: 1,
type: 'scripthash',
addresses: ['bitcoincash:pzzyt8revvrlutqrzm884zllc5guy77d2545uyqe8h']
}
},
{
value: 0.00680944,
n: 1,
scriptPubKey: {
asm: 'OP_DUP OP_HASH160 aab2f091ea97e02c58c4e27fb77afb99538ea39e OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914aab2f091ea97e02c58c4e27fb77afb99538ea39e88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qz4t9uy3a2t7qtzccn38ldm6lwv48r4rnc9mxdtvt9']
}
}
],
hex: '01000000029435038c55853d30d07c055b654ab43796186bad2cc2d54bd5f146f1d6e3f3c4360100006a47304402200d40ad599c7e33c7f5245080c0ea35c837c0d142b82eb6824fceaf049c2a355902200590e35951111f6c603c0fb4e513cf0e47734d7bc8b45f105585bea0f2b923de41210352ede8ab8f2ae49b3921c385e59f700ae1e6f73cbb37f61cd1244e70c99bf496ffffffff612dfde06c86cd93ebfb066372bad0d7055124b31252fd5f1d5d2d9bf86522c3010000006b483045022100be9cc5e982110b0f563460baa19a42f34ab775708ad3461a6cf065db9dffb1b7022031ffb2721828be4aa1f4967a367e8af2848cdfce41c68565d0a2a2a70117ea5d412102d2eabc002e25fc08622a71d9ef96c17b21176f438d67bc537ac49a2e0e594372ffffffff02e0347c000000000017a91484459c796307fe2c0316ce7a8bffc511c27bcd5587f0630a00000000001976a914aab2f091ea97e02c58c4e27fb77afb99538ea39e88ac00000000',
blockhash:
'000000000000000001e8e99be82172d5fe913e21f5a78e4e3bdce2c7dc6315ce',
confirmations: 94517,
time: 1586584650,
blocktime: 1586584650,
isValidSlp: false
}
}
module.exports = {
fulcrumOut01,
txData01
}
+7
View File
@@ -38,6 +38,13 @@ class BCH {
status: 200
}
}
async getTxData(rpcData) {
return {
success: true,
status: 200
}
}
}
class UseCasesMock {
@@ -122,4 +122,94 @@ describe('#bch-use-case', () => {
assert.equal(result.endpoint, 'transactions')
})
})
describe('#getTxData', () => {
it('should get tx data', async () => {
// Mock dependencies
sandbox.stub(uut.bchjs.Transaction, 'get').resolves(mockData.txData01)
const rpcData = {
payload: {
params: {
txids: [
'11384d7e5a8af93806591debe5bbe2d7826aeea987b874dfbe372dfdcc0ee54f'
]
}
}
}
const result = await uut.getTxData(rpcData)
// console.log('result: ', result)
assert.equal(result.status, 200)
assert.isArray(result.txData)
})
it('should return error if txids is not an array', async () => {
const rpcData = {
payload: {
params: {
txids: 1234
}
}
}
const result = await uut.getTxData(rpcData)
// console.log('result: ', result)
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.equal(result.endpoint, 'transaction')
assert.equal(
result.message,
'Input txids must be an array of transaction IDs.'
)
})
it('should return error if txids array has more than 20 elements', async () => {
const rpcData = {
payload: {
params: {
txids: [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25
]
}
}
}
const result = await uut.getTxData(rpcData)
// console.log('result: ', result)
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.equal(result.endpoint, 'transaction')
assert.equal(result.message, 'Array input must be 20 elements or less.')
})
it('should handle errors', async () => {
// Force an error
sandbox
.stub(uut.bchjs.Transaction, 'get')
.rejects(new Error('test error'))
const rpcData = {
payload: {
params: {
txids: [
'11384d7e5a8af93806591debe5bbe2d7826aeea987b874dfbe372dfdcc0ee54f'
]
}
}
}
const result = await uut.getTxData(rpcData)
// console.log('result: ', result)
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.equal(result.endpoint, 'transaction')
assert.equal(result.message, 'test error')
})
})
})