mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service.git
synced 2026-09-22 09:12:02 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b729c7ed80 | ||
|
|
5a3d91e3b0 | ||
|
|
d9fa44328a | ||
|
|
efaffa8e0f | ||
|
|
27a9e9f3f3 | ||
|
|
41a5071467 | ||
|
|
c6c3bb5d9c | ||
|
|
e4977a4351 | ||
|
|
fe3b45f6be | ||
|
|
d25b240112 | ||
|
|
7a89a06ddb | ||
|
|
74fb65df72 | ||
|
|
0b38247f5a | ||
|
|
473aaae307 | ||
|
|
284f4e4478 | ||
|
|
56f6dc273a | ||
|
|
4bd3387bde | ||
|
|
7e7e91460a | ||
|
|
e5f6f24570 | ||
|
|
818c85e3d4 | ||
|
|
651c4a1346 | ||
|
|
4b90ad5009 | ||
|
|
05ddf8521a | ||
|
|
88d1805685 | ||
|
|
ea104b5586 | ||
|
|
4251e2a02d | ||
|
|
5e0eb017ef | ||
|
|
86946968fe | ||
|
|
9597f1963f |
Generated
+382
-335
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -9,6 +9,7 @@
|
||||
"test:all": "export SVC_ENV=test && nyc --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/",
|
||||
"test:unit": "export SVC_ENV=test && mocha --exit --timeout 15000 --recursive test/unit/",
|
||||
"test:e2e:auto": "export SVC_ENV=test && mocha --exit --timeout 30000 test/e2e/automated/",
|
||||
"test:integration": "export SVC_ENV=test && mocha --timeout 30000 --recursive test/integration/",
|
||||
"test:temp": "export SVC_ENV=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/",
|
||||
"lint": "standard --env mocha --fix",
|
||||
"docs": "./node_modules/.bin/apidoc -i src/ -o docs",
|
||||
@@ -23,8 +24,8 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-service",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "4.20.28",
|
||||
"axios": "0.21.1",
|
||||
"@psf/bch-js": "5.3.0",
|
||||
"axios": "0.25.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"glob": "7.1.6",
|
||||
"ipfs": "0.60.0",
|
||||
@@ -64,6 +65,7 @@
|
||||
"eslint-plugin-prettier": "3.3.1",
|
||||
"eslint-plugin-standard": "4.0.0",
|
||||
"husky": "4.3.8",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"mocha": "8.2.1",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "17.4.4",
|
||||
|
||||
@@ -47,8 +47,10 @@ class Adapters {
|
||||
this.bchjs = new BCHJS({ restURL: this.config.apiServer })
|
||||
}
|
||||
|
||||
// Start the IPFS node.
|
||||
await this.ipfs.start({ bchjs: this.bchjs })
|
||||
if (this.config.env !== 'test') {
|
||||
// Start the IPFS node.
|
||||
await this.ipfs.start({ bchjs: this.bchjs })
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/index.js/start()')
|
||||
throw err
|
||||
|
||||
@@ -32,7 +32,6 @@ class BCHRPC {
|
||||
this.jsonrpc = jsonrpc
|
||||
this.validators = new Validators(localConfig)
|
||||
this.rateLimit = new RateLimit()
|
||||
// this.bchjs = new BCHJS()
|
||||
this.bchjs = this.adapters.bchjs
|
||||
}
|
||||
|
||||
@@ -92,7 +91,12 @@ class BCHRPC {
|
||||
* @apiName Transactions
|
||||
* @apiGroup JSON BCH
|
||||
* @apiDescription This endpoint wraps the bchjs.Electrumx.transactions([]) function.
|
||||
* Given the 'addresses' property this endpoint returns an object with the following properties
|
||||
* There are three possible inputs:
|
||||
* - address: (required) the address to query for a transaction history
|
||||
* - sortOrder: (optional) will sort results in 'DECENDING' (default) or 'ASCENDING' order.
|
||||
* - page: (optional) will return a 'page' of 100 results. Default is 0
|
||||
*
|
||||
* Given the 'address' property this endpoint returns an object with the following properties
|
||||
*
|
||||
* - jsonrpc: "" - jsonrpc version
|
||||
* - id: "" - jsonrpc id
|
||||
@@ -101,15 +105,14 @@ class BCHRPC {
|
||||
* - receiver: "" - Receiver address
|
||||
* - value: {} - Final result value of the petition
|
||||
* - success : - Petition status
|
||||
* - transactions : [] - Transactions of the provided adresses
|
||||
* - transactions: [] - Transaction details
|
||||
* - height : - Reference to the blockchain size
|
||||
* - tx_hash: "" - Hash of the transaction
|
||||
* - address : "" - Address asociated to the transactions
|
||||
* - txs : [] - Transactions of the provided address
|
||||
* - height : - Reference to the blockchain size
|
||||
* - tx_hash: "" - Hash of the transaction
|
||||
* - address : "" - Address asociated to the transactions
|
||||
* - status: - HTTP Status Code
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "transactions", "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"]}}
|
||||
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "transactions", "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"], "sortOrder": "DESCENDING", "page": 0}}
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* {
|
||||
@@ -119,17 +122,13 @@ class BCHRPC {
|
||||
* "method":"bch",
|
||||
* "reciever":"QmU86vLVbUY1UhziKB6rak7GPKRA2QHWvzNm2AjEvXNsT6",
|
||||
* "value":{
|
||||
* "address":"bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"
|
||||
* "success":true,
|
||||
* "transactions":[
|
||||
* {
|
||||
* "transactions":[
|
||||
* {
|
||||
* "height":631219,
|
||||
* "tx_hash":"ae2daa01c8172545b5edd205ea438706bcb74e63d4084a26b9ff2a46d46dc97f"
|
||||
* }
|
||||
* ],
|
||||
* "address":"bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"
|
||||
* }
|
||||
* "txs":[
|
||||
* {
|
||||
* "height":631219,
|
||||
* "tx_hash":"ae2daa01c8172545b5edd205ea438706bcb74e63d4084a26b9ff2a46d46dc97f"
|
||||
* }
|
||||
* ],
|
||||
* "status":200
|
||||
* }
|
||||
@@ -138,17 +137,14 @@ class BCHRPC {
|
||||
*/
|
||||
async transactions (rpcData) {
|
||||
try {
|
||||
// console.log('createUser rpcData: ', rpcData)
|
||||
// console.log('transactions rpcData: ', rpcData)
|
||||
|
||||
const addrs = rpcData.payload.params.addresses
|
||||
// const addr = rpcData.payload.params.address
|
||||
// const sortOrder = rpcData.payload.params.sortOrder
|
||||
|
||||
const data = await this.bchjs.Electrumx.transactions(addrs)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
const data = await this.useCases.bch.getTransactions(rpcData)
|
||||
|
||||
const retObj = data
|
||||
retObj.status = 200
|
||||
|
||||
return retObj
|
||||
return data
|
||||
} catch (err) {
|
||||
console.error('Error in JSON RPC BCH transactions()')
|
||||
// throw err
|
||||
@@ -331,7 +327,7 @@ class BCHRPC {
|
||||
// console.log('createUser rpcData: ', rpcData)
|
||||
|
||||
const addr = rpcData.payload.params.address
|
||||
console.log('addr: ', addr)
|
||||
// console.log('addr: ', addr)
|
||||
|
||||
const data = await this.bchjs.Utxo.get(addr)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
@@ -433,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
|
||||
*
|
||||
@@ -459,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":{
|
||||
@@ -520,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()')
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Use Cases for interacting with the BCH blockchain.
|
||||
*/
|
||||
|
||||
class BCHUseCases {
|
||||
constructor (localConfig = {}) {
|
||||
// console.log('User localConfig: ', localConfig)
|
||||
this.adapters = localConfig.adapters
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Instance of adapters must be passed in when instantiating BCH Use Cases library.'
|
||||
)
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.bchjs = this.adapters.bchjs
|
||||
}
|
||||
|
||||
// Get transaction history for an address, sorted by block height.
|
||||
async getTransactions (rpcData) {
|
||||
try {
|
||||
// console.log(
|
||||
// `getTransactions rpcData: ${JSON.stringify(rpcData, null, 2)}`
|
||||
// )
|
||||
|
||||
// Get the list of addresses.
|
||||
const addr = rpcData.payload.params.address
|
||||
|
||||
// Default to descending sorting.
|
||||
let sortOrder = rpcData.payload.params.sortOrder
|
||||
if (!sortOrder) sortOrder = 'DESCENDING'
|
||||
// console.log('sortOrder: ', sortOrder)
|
||||
|
||||
// Default to page 1
|
||||
let page = rpcData.payload.params.page
|
||||
if (!page) page = 0
|
||||
// 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)}`)
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error('Could not query Fulcrum indexer.')
|
||||
}
|
||||
// console.log(`transactions: ${JSON.stringify(transactions, null, 2)}`);
|
||||
|
||||
// Sort the transactions.
|
||||
const txsArr = await this.bchjs.Electrumx.sortAllTxs(
|
||||
data.transactions[0].transactions,
|
||||
sortOrder
|
||||
)
|
||||
// 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)}`
|
||||
// )
|
||||
|
||||
const retObj = {
|
||||
address: addr,
|
||||
txs: pagedResults[page],
|
||||
status: 200,
|
||||
success: true
|
||||
}
|
||||
// console.log(`retObj: ${JSON.stringify(retObj, null, 2)}`)
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
console.error('Error in JSON RPC BCH transactions(): ', err)
|
||||
// throw err
|
||||
|
||||
// Return an error response
|
||||
return {
|
||||
success: false,
|
||||
status: 422,
|
||||
message: err.message,
|
||||
endpoint: 'transactions'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -4,7 +4,9 @@
|
||||
https://troutsblog.com/blog/clean-architecture
|
||||
*/
|
||||
|
||||
// Local libraries
|
||||
const UserUseCases = require('./user')
|
||||
const BCHUseCases = require('./bch')
|
||||
|
||||
class UseCases {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -17,6 +19,7 @@ class UseCases {
|
||||
|
||||
// console.log('use-cases/index.js localConfig: ', localConfig)
|
||||
this.user = new UserUseCases(localConfig)
|
||||
this.bch = new BCHUseCases(localConfig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
// Public npm libraries
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
const IpfsCoord = require('ipfs-coord')
|
||||
const IPFS = require('@chris.troutner/ipfs')
|
||||
const IPFS = require('ipfs-http-client')
|
||||
const EventEmitter = require('events')
|
||||
const { v4: uid } = require('uuid')
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
const http = require('http')
|
||||
|
||||
let _this
|
||||
|
||||
@@ -88,8 +89,14 @@ class TestUtils {
|
||||
|
||||
async startIpfs () {
|
||||
try {
|
||||
const ipfsOptionsExternal = {
|
||||
host: 'localhost',
|
||||
port: 5001,
|
||||
agent: http.Agent({ keepAlive: true, maxSockets: 2000 })
|
||||
}
|
||||
|
||||
// Start the IPFS node.
|
||||
this.ipfs = await IPFS.create()
|
||||
this.ipfs = await IPFS.create(ipfsOptionsExternal)
|
||||
await this.ipfs.config.profiles.apply('server')
|
||||
|
||||
// Start ipfs-coord.
|
||||
@@ -101,7 +108,8 @@ class TestUtils {
|
||||
privateLog: this.rpcHandler, // Default to console.log
|
||||
isCircuitRelay: false,
|
||||
apiInfo: 'none',
|
||||
announceJsonLd: announceJsonLd
|
||||
announceJsonLd: announceJsonLd,
|
||||
nodeType: 'external'
|
||||
})
|
||||
|
||||
await this.ipfsCoord.start()
|
||||
@@ -214,7 +222,7 @@ class TestUtils {
|
||||
const id = uid()
|
||||
const cmd = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'transactions',
|
||||
addresses: ['bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj']
|
||||
address: 'bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj'
|
||||
})
|
||||
const cmdStr = JSON.stringify(cmd)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"uutAddr": "/ip4/127.0.0.1/tcp/5268/p2p/QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY",
|
||||
"uutId": "QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY"
|
||||
"uutAddr": "/ip4/88.99.188.196/tcp/4001/p2p/12D3KooWT2tDg2pHwKz84Htzh2KsfSUZbZXmoAUFnaSwUVrDsag3/p2p-circuit/ipfs/12D3KooWNuZPDb15ENgk2CYF2dByq1oK6CQ5tUpRW9CpwwNynuJY",
|
||||
"uutId": "12D3KooWNuZPDb15ENgk2CYF2dByq1oK6CQ5tUpRW9CpwwNynuJY"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Integration tests for the BCH Use Cases library
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const assert = require('chai').assert
|
||||
|
||||
// Local libraries
|
||||
const BCHUseCases = require('../../../../src/use-cases/bch')
|
||||
const Adapters = require('../../../../src/adapters')
|
||||
|
||||
describe('#BCH', () => {
|
||||
let uut
|
||||
|
||||
before(async () => {
|
||||
const adapters = new Adapters()
|
||||
await adapters.start()
|
||||
|
||||
uut = new BCHUseCases({ adapters })
|
||||
})
|
||||
|
||||
describe('#getTransactions', () => {
|
||||
it('should get transactions for an address, with default descending sorting', async () => {
|
||||
const rpcData = {
|
||||
payload: {
|
||||
params: {
|
||||
address: 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await uut.getTransactions(rpcData)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.success, true)
|
||||
assert.equal(result.status, 200)
|
||||
assert.isArray(result.txs)
|
||||
|
||||
// Assert descending sort order.
|
||||
assert.isAbove(result.txs[0].height, result.txs[1].height)
|
||||
})
|
||||
|
||||
it('should get transactions for an address, with explicit descending sorting', async () => {
|
||||
const rpcData = {
|
||||
payload: {
|
||||
params: {
|
||||
address: 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v',
|
||||
sortOrder: 'DESCENDING'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await uut.getTransactions(rpcData)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.success, true)
|
||||
assert.equal(result.status, 200)
|
||||
assert.isArray(result.txs)
|
||||
|
||||
// Assert descending sort order.
|
||||
assert.isAbove(result.txs[0].height, result.txs[1].height)
|
||||
})
|
||||
|
||||
it('should sort in ascending order', async () => {
|
||||
const rpcData = {
|
||||
payload: {
|
||||
params: {
|
||||
address: 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v',
|
||||
sortOrder: 'ASCENDING'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await uut.getTransactions(rpcData)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.success, true)
|
||||
assert.equal(result.status, 200)
|
||||
assert.isArray(result.txs)
|
||||
|
||||
// Assert descending sort order.
|
||||
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)
|
||||
@@ -199,11 +200,6 @@ describe('#BCHRPC', () => {
|
||||
|
||||
describe('#transactions', () => {
|
||||
it('should return data from bchjs', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(uut.bchjs.Electrumx, 'transactions')
|
||||
.resolves({ success: true })
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
@@ -224,7 +220,7 @@ describe('#BCHRPC', () => {
|
||||
it('should return an error for invalid address', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.Electrumx, 'transactions')
|
||||
.stub(uut.useCases.bch, 'getTransactions')
|
||||
.rejects(new Error('Invalid address'))
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
@@ -392,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()
|
||||
@@ -416,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
|
||||
@@ -442,7 +435,11 @@ describe('#BCHRPC', () => {
|
||||
describe('#pubKey', () => {
|
||||
it('should return data from bchjs', async () => {
|
||||
// Mock dependencies
|
||||
const mock = { success: true, publicKey: '033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee' }
|
||||
const mock = {
|
||||
success: true,
|
||||
publicKey:
|
||||
'033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee'
|
||||
}
|
||||
sandbox.stub(uut.bchjs.encryption, 'getPubKey').resolves(mock)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
@@ -466,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
|
||||
|
||||
@@ -56,7 +56,8 @@ const localdb = {
|
||||
const bchjs = {
|
||||
Electrumx: {
|
||||
transactions: () => {},
|
||||
balance: () => {}
|
||||
balance: () => {},
|
||||
sortAllTxs: () => {}
|
||||
},
|
||||
Utxo: {
|
||||
get: () => {}
|
||||
@@ -69,6 +70,9 @@ const bchjs = {
|
||||
},
|
||||
encryption: {
|
||||
getPubKey: () => {}
|
||||
},
|
||||
Util: {
|
||||
chunk100: () => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Mock data for use-cases/bch/index.js unit tests
|
||||
*/
|
||||
|
||||
const fulcrumOut01 = {
|
||||
success: true,
|
||||
transactions: [
|
||||
{
|
||||
transactions: [
|
||||
{
|
||||
height: 560430,
|
||||
tx_hash:
|
||||
'3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851'
|
||||
},
|
||||
{
|
||||
height: 560534,
|
||||
tx_hash:
|
||||
'4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280'
|
||||
}
|
||||
],
|
||||
address: 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -31,12 +31,29 @@ class UserUseCaseMock {
|
||||
}
|
||||
}
|
||||
|
||||
class BCH {
|
||||
async getTransactions(rpcData) {
|
||||
return {
|
||||
success: true,
|
||||
status: 200
|
||||
}
|
||||
}
|
||||
|
||||
async getTxData(rpcData) {
|
||||
return {
|
||||
success: true,
|
||||
status: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UseCasesMock {
|
||||
constuctor(localConfig = {}) {
|
||||
// this.user = new UserUseCaseMock(localConfig)
|
||||
}
|
||||
|
||||
user = new UserUseCaseMock()
|
||||
bch = new BCH()
|
||||
}
|
||||
|
||||
module.exports = UseCasesMock
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
Unit tests for the BCH Use Cases
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
const clone = require('lodash.clonedeep')
|
||||
|
||||
// Local support libraries
|
||||
const BCHUseCases = require('../../../../src/use-cases/bch')
|
||||
const adapters = require('../../mocks/adapters')
|
||||
const mockDataLib = require('../../mocks/use-cases/bch/bch.use-cases.mocks.js')
|
||||
|
||||
describe('#bch-use-case', () => {
|
||||
let uut
|
||||
let sandbox
|
||||
let mockData
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
mockData = clone(mockDataLib)
|
||||
|
||||
uut = new BCHUseCases({ adapters })
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#constructor', () => {
|
||||
it('should throw an error if adapters are not passed in', () => {
|
||||
try {
|
||||
uut = new BCHUseCases()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Instance of adapters must be passed in when instantiating BCH Use Cases library.'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTransactions', () => {
|
||||
it('should get transactions for an address', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(uut.bchjs.Electrumx, 'transactions')
|
||||
.resolves(mockData.fulcrumOut01)
|
||||
sandbox
|
||||
.stub(uut.bchjs.Electrumx, 'sortAllTxs')
|
||||
.resolves(mockData.fulcrumOut01.transactions[0].transactions)
|
||||
sandbox
|
||||
.stub(uut.bchjs.Util, 'chunk100')
|
||||
.returns([mockData.fulcrumOut01.transactions[0].transactions])
|
||||
|
||||
const address = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
const rpcData = {
|
||||
payload: {
|
||||
params: {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await uut.getTransactions(rpcData)
|
||||
// console.log('result: ', result)
|
||||
|
||||
// Assert expected values and properties.
|
||||
assert.equal(result.address, address)
|
||||
assert.equal(result.status, 200)
|
||||
assert.equal(result.success, true)
|
||||
assert.isArray(result.txs)
|
||||
})
|
||||
|
||||
it('should return error if Fulcrum communication is not possible', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.Electrumx, 'transactions')
|
||||
.resolves({ success: false })
|
||||
|
||||
const address = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
const rpcData = {
|
||||
payload: {
|
||||
params: {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await uut.getTransactions(rpcData)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.equal(result.message, 'Could not query Fulcrum indexer.')
|
||||
assert.equal(result.endpoint, 'transactions')
|
||||
})
|
||||
|
||||
it('should return error if there is an error', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.Electrumx, 'transactions')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
const address = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
const rpcData = {
|
||||
payload: {
|
||||
params: {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await uut.getTransactions(rpcData)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.equal(result.message, 'test error')
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -7,9 +7,6 @@ const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Local support libraries
|
||||
// const testUtils = require('../../utils/test-utils')
|
||||
|
||||
// Unit under test (uut)
|
||||
const UseCases = require('../../../src/use-cases')
|
||||
const adapters = require('../mocks/adapters')
|
||||
|
||||
|
||||
@@ -9,9 +9,6 @@ const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Local support libraries
|
||||
// const testUtils = require('../../utils/test-utils')
|
||||
|
||||
// Unit under test (uut)
|
||||
const UserLib = require('../../../src/use-cases/user')
|
||||
const adapters = require('../mocks/adapters')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user