Compare commits

..
58 Commits
Author SHA1 Message Date
Chris Troutner 553957a173 Merge pull request #69 from Permissionless-Software-Foundation/ct-unstable
fix(bch-js): Updating to 5.3.1
2022-01-31 21:20:51 -08:00
Chris Troutner 99a737c7e7 fix(bch-js): Updating to 5.3.1 2022-01-31 21:17:40 -08:00
Chris Troutner ffb87d09c2 Merge pull request #68 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2022-01-31 10:39:37 -08:00
Chris Troutner 65ead3568a Merge branch 'master' into ct-unstable 2022-01-31 10:34:52 -08:00
Chris Troutner b4b2aefac6 synced with upstream ipfs-service-provider 2022-01-31 10:33:54 -08:00
Chris Troutner 1844f30dc5 Merge pull request #76 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Bumping to v6.8.12
2022-01-31 10:31:20 -08:00
Chris Troutner b6ab2f02f4 Updating package-lock 2022-01-31 10:29:45 -08:00
Chris Troutner b5c76aa5fe fix(ipfs-coord): Bumping to v6.8.12 2022-01-31 10:28:06 -08:00
Chris Troutner 7648c96a70 Removing console logs 2022-01-29 17:37:22 -08:00
Chris Troutner 79f5adaf6b fix(bch-js): Tweeking initialization at startup 2022-01-29 17:29:26 -08:00
Chris Troutner e2f84d661f Debugging bch-js 2022-01-29 17:13:46 -08:00
Chris Troutner 31091a50a8 fix(startup): Announcing APISERVER when default init of bch-js is overridden 2022-01-29 15:53:45 -08:00
Chris Troutner 512775c68f More debugging 2022-01-29 15:42:50 -08:00
Chris Troutner c53394e547 Merge pull request #67 from Permissionless-Software-Foundation/ct-unstable
Adding debugging to txData()
2022-01-29 15:34:56 -08:00
Chris Troutner 79083502fb Adding debugging to txData() 2022-01-29 14:52:19 -08:00
Chris Troutner 6049c6566f Merge pull request #66 from Permissionless-Software-Foundation/ct-unstable
fix(getTxData): Removing txData property from output
2022-01-29 09:21:25 -08:00
Chris Troutner 99a52ec605 fix(getTxData): Removing txData property from output 2022-01-29 09:19:43 -08:00
Chris Troutner 33e7bc2b52 Merge pull request #65 from Permissionless-Software-Foundation/ct-unstable
feat(txData): Renamed transaction JSON endpoint to txData
2022-01-29 07:39:47 -08:00
Chris Troutner a20f30ec1e feat(txData): Renamed transaction JSON endpoint to txData 2022-01-29 07:38:05 -08:00
Chris Troutner b0b12c590c Merge pull request #64 from Permissionless-Software-Foundation/ct-unstable
feat(txHistory): Renamed transactions JSON endpoint to txHistory
2022-01-29 07:11:58 -08:00
Chris Troutner 286092f2e3 feat(txHistory): Renamed transactions JSON endpoint to txHistory 2022-01-29 07:09:40 -08:00
Chris Troutner 5be93afc38 fix(production): adding .env file to docker folder to prevent orphan warning 2022-01-28 19:43:51 -08:00
Chris Troutner 63cb107a25 Merge pull request #63 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to 6.8.11
2022-01-28 19:32:43 -08:00
Chris Troutner c54fb73c7d fix(bch): Adding additional debugging statements for BCH endpoints 2022-01-28 19:31:04 -08:00
Chris Troutner 8063c0e922 fix(ipfs-coord): Updating to 6.8.11 2022-01-28 19:26:41 -08:00
Chris Troutner b729c7ed80 Merge pull request #62 from Permissionless-Software-Foundation/ct-unstable
feat(transaction): Refactoring to allow up to 20 TXIDs per call
2022-01-28 12:06:12 -08:00
Chris Troutner 5a3d91e3b0 feat(transaction): Refactoring to allow up to 20 TXIDs per call 2022-01-28 12:04:35 -08:00
Chris Troutner d9fa44328a Merge pull request #61 from Permissionless-Software-Foundation/ct-unstable
Heavily refactored transaction history JSON RPC endpoint
2022-01-28 08:15:36 -08:00
Chris Troutner efaffa8e0f fix(getTransactions()): Added unit tests 2022-01-28 08:03:35 -08:00
Chris Troutner 27a9e9f3f3 Working on unit tests 2022-01-28 06:55:50 -08:00
Chris Troutner 41a5071467 fix(JSON transactions): Updated to new algorithm 2022-01-28 06:37:18 -08:00
Chris Troutner c6c3bb5d9c More debugging of pagination 2022-01-27 19:01:57 -08:00
Chris Troutner e4977a4351 Debugging pagination 2022-01-27 18:58:28 -08:00
Chris Troutner fe3b45f6be fixing typo 2022-01-27 17:54:17 -08:00
Chris Troutner d25b240112 Debugging 2022-01-27 17:52:16 -08:00
Chris Troutner 7a89a06ddb fix(getTransactions()): Adding pagination to tx history 2022-01-27 17:47:03 -08:00
Chris Troutner 74fb65df72 more debugging 2022-01-27 15:10:46 -08:00
Chris Troutner 0b38247f5a more debugging 2022-01-27 15:05:08 -08:00
Chris Troutner 473aaae307 debugging 2022-01-27 15:00:54 -08:00
Chris Troutner 284f4e4478 Debugging and reporting errors 2022-01-27 14:58:00 -08:00
Chris Troutner 56f6dc273a Restoring bch-js 2022-01-27 09:06:39 -08:00
Chris Troutner 4bd3387bde feat(transactions): Refactored JSON RPC transactions endpoint 2022-01-27 08:47:06 -08:00
Chris Troutner 7e7e91460a fix(integration): Adding integration test script to package.json 2022-01-24 13:21:29 -08:00
Chris Troutner e5f6f24570 Created mock unit tests for BCH use cases 2022-01-24 13:13:55 -08:00
Chris Troutner 818c85e3d4 Merge branch 'master' into ct-unstable 2022-01-24 12:32:24 -08:00
Chris Troutner 4101615a4a Merge pull request #75 from Permissionless-Software-Foundation/ct-unstable
Maintenance
2022-01-20 08:05:50 -08:00
Chris Troutner 34a4072384 fix(restart): Removing code the auto-restarts app after 2 hours 2022-01-20 07:56:40 -08:00
Chris Troutner 5fc72be10c fix(fullstack-jwt.js): Got unit test coverage back to 100% 2022-01-20 07:53:12 -08:00
Chris Troutner 651c4a1346 Debugging transactions JSON RPC endpoint 2022-01-19 15:58:25 -08:00
Chris Troutner 4b90ad5009 fix(bch-js): Upgrading to v5.2.2 2022-01-19 15:31:25 -08:00
Chris Troutner 05ddf8521a fix(bch-js): Updating to v5.2.1 2022-01-19 11:34:58 -08:00
Chris Troutner 88d1805685 Merge pull request #60 from Permissionless-Software-Foundation/ct-unstable
No-op
2022-01-18 15:38:12 -08:00
Chris Troutner ea104b5586 Rolling back debug console.logs 2022-01-18 15:31:15 -08:00
Chris Troutner 4251e2a02d more debugging 2022-01-18 15:22:26 -08:00
Chris Troutner 5e0eb017ef Debugging 2022-01-18 15:16:45 -08:00
Chris Troutner 86946968fe Merge pull request #59 from Permissionless-Software-Foundation/ct-unstable
fix(bch-js): Updating to bch-js v5
2022-01-18 12:40:22 -08:00
Chris Troutner 9597f1963f fix(bch-js): Updating to bch-js v5 2022-01-18 12:38:33 -08:00
Chris Troutner 7b112a13d4 fix(cors): Fixing bug leading to CORS error 2022-01-12 15:33:04 -08:00
20 changed files with 1492 additions and 8528 deletions
+5 -11
View File
@@ -68,6 +68,11 @@ class Server {
app.use(passport.initialize())
app.use(passport.session())
// Enable CORS for testing
// THIS IS A SECURITY RISK. COMMENT OUT FOR PRODUCTION
// Dev Note: This line must come BEFORE controllers.attachRESTControllers()
app.use(cors({ origin: '*' }))
// Attach REST API and JSON RPC controllers to the app.
const Controllers = require('../src/controllers')
const controllers = new Controllers()
@@ -75,10 +80,6 @@ class Server {
app.controllers = controllers
// Enable CORS for testing
// THIS IS A SECURITY RISK. COMMENT OUT FOR PRODUCTION
app.use(cors({ origin: '*' }))
// MIDDLEWARE END
console.log(`Running server in environment: ${config.env}`)
@@ -103,13 +104,6 @@ class Server {
await controllers.attachControllers(app)
}
// ipfs-coord has a memory leak. This app shuts down after 4 hours. It
// expects to be run by Docker or pm2, which can automatically restart
// the app.
setTimeout(function () {
process.exit(0)
}, 60000 * 60 * 2) // 2 hours
return app
} catch (err) {
console.error('Could not start server. Error: ', err)
+766 -8402
View File
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -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,12 +24,12 @@
},
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-service",
"dependencies": {
"@psf/bch-js": "4.20.28",
"axios": "0.21.1",
"@psf/bch-js": "5.3.1",
"axios": "0.25.0",
"bcryptjs": "2.4.3",
"glob": "7.1.6",
"ipfs": "0.60.0",
"ipfs-coord": "6.8.10",
"ipfs-coord": "6.8.12",
"ipfs-http-client": "55.0.0",
"jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1",
@@ -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",
+1
View File
@@ -0,0 +1 @@
COMPOSE_PROJECT_NAME=bch-wallet-service
-6
View File
@@ -52,12 +52,6 @@ class FullStackJWT {
// Get's a JWT token from FullStack.cash.
async getJWT () {
try {
// Skip connecting FullStack.cash auth server to the network if this is an E2E test.
if (process.env.TEST_TYPE === 'e2e') {
this.apiToken = 'faketoken'
return this.apiToken
}
// Log into the auth server.
await this.jwtLib.register()
+9 -4
View File
@@ -22,14 +22,14 @@ const config = require('../../config')
class Adapters {
constructor (localConfig = {}) {
// Encapsulate dependencies
this.config = config
this.ipfs = new IPFSAdapter()
this.localdb = new LocalDB()
this.logapi = new LogsAPI()
this.passport = new Passport()
this.nodemailer = new Nodemailer()
this.jsonFiles = new JSONFiles()
this.bchjs = new BCHJS()
this.config = config
this.bchjs = new BCHJS({ restURL: this.config.apiServer })
// Get a valid JWT API key and instance bch-js.
this.fullStackJwt = new FullStackJWT(config)
@@ -44,11 +44,16 @@ class Adapters {
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
this.bchjs = await this.fullStackJwt.instanceBchjs()
} else {
// console.log(
// `Initializing bchjs with this restURL: ${this.config.apiServer}`
// )
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
+47 -55
View File
@@ -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
}
@@ -49,9 +48,9 @@ class BCHRPC {
// Route the call based on the value of the method property.
switch (endpoint) {
case 'transactions':
case 'txHistory':
await this.rateLimit.limiter(rpcData.from)
return await this.transactions(rpcData)
return await this.txHistory(rpcData)
case 'balance':
await this.rateLimit.limiter(rpcData.from)
@@ -65,9 +64,9 @@ class BCHRPC {
await this.rateLimit.limiter(rpcData.from)
return await this.broadcast(rpcData)
case 'transaction':
case 'txData':
await this.rateLimit.limiter(rpcData.from)
return await this.transaction(rpcData)
return await this.txData(rpcData)
case 'pubkey':
await this.rateLimit.limiter(rpcData.from)
@@ -87,12 +86,20 @@ class BCHRPC {
}
/**
* @api {JSON} /bch Transactions
* @api {JSON} /bch TX History
* @apiPermission public
* @apiName Transactions
* @apiName txHistory
* @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
* It returns the transaction history for an address. This list of TXIDs is
* sorted and paginated.
*
* 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 +108,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": "txHistory", "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"], "sortOrder": "DESCENDING", "page": 0}}
*
* @apiSuccessExample {json} Success-Response:
* {
@@ -119,36 +125,29 @@ 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
* }
* }
* }
*/
async transactions (rpcData) {
async txHistory (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
@@ -158,7 +157,7 @@ class BCHRPC {
success: false,
status: 422,
message: err.message,
endpoint: 'transactions'
endpoint: 'txHistory'
}
}
}
@@ -331,7 +330,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)}`)
@@ -429,12 +428,14 @@ class BCHRPC {
}
/**
* @api {JSON} /bch Transaction
* @api {JSON} /bch Transaction Data
* @apiPermission public
* @apiName Transaction
* @apiName txData
* @apiGroup JSON BCH
* @apiDescription Get data about a specific transaction.
* Given a transaction the endpoint will return an object with the
* @apiDescription Get expanded transaction data for an array of transaction
* IDs. Each call is limited to 20 TXIDs or less.
*
* Given a transaction ID, the endpoint will return an object with the
* following properties
*
* - jsonrpc: "" - jsonrpc version
@@ -459,13 +460,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": "txData", "txids": ["01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b"]}}
*
* @apiSuccessExample {json} Success-Response:
* {
* "jsonrpc":"2.0",
* "id":"555",
* "result":{
* "txData": [{
* "method":"bch",
* "reciever":"QmU86vLVbUY1UhziKB6rak7GPKRA2QHWvzNm2AjEvXNsT6",
* "value":{
@@ -520,23 +521,14 @@ class BCHRPC {
* "status":200
* }
* }
* }
* }]
*/
async transaction (rpcData) {
async txData (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()')
console.error('Error in JSON RPC BCH txData()): ', err)
// throw err
// Return an error response
@@ -544,7 +536,7 @@ class BCHRPC {
success: false,
status: 422,
message: err.message,
endpoint: 'transaction'
endpoint: 'txData'
}
}
}
+7
View File
@@ -92,6 +92,13 @@ class JSONRPC {
wlogger.info(
`JSON RPC received from ${from}, ID: ${parsedData.payload.id}, type: ${parsedData.type}, method: ${parsedData.payload.method}`
)
// Additional debugging for BCH.
if (parsedData.payload.method === 'bch') {
wlogger.info(
`rpcData.payload: ${JSON.stringify(parsedData.payload, null, 2)}`
)
}
}
// Added the property "from" to the parsedData object;
+142
View File
@@ -0,0 +1,142 @@
/*
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: 'txHistory'
}
}
}
// 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: 'txData'
}
}
if (txids.length > 20) {
return {
success: false,
status: 422,
message: 'Array input must be 20 elements or less.',
endpoint: 'txData'
}
}
const txData = []
for (let i = 0; i < txids.length; i++) {
const thisTxid = txids[i]
// console.log(`this.bchjs.restURL: ${this.bchjs.restURL}`)
const data = await this.bchjs.Transaction.get(thisTxid.toString())
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
txData.push(data.txData)
}
const retObj = {
status: 200,
txData
}
return retObj
} catch (err) {
console.error('Error in JSON RPC BCH getTxData(): ', err)
// throw err
// Return an error response
return {
success: false,
status: 422,
message: err.message,
endpoint: 'txData'
}
}
}
}
module.exports = BCHUseCases
+3
View File
@@ -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)
}
}
+12 -4
View File
@@ -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)
+2 -2
View File
@@ -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)
})
})
})
@@ -61,15 +61,15 @@ describe('#BCHRPC', () => {
})
describe('#bchRouter', () => {
it('should route to the transactions method', async () => {
it('should route to the txHistory method', async () => {
// Mock dependencies
sandbox.stub(uut, 'transactions').resolves(true)
sandbox.stub(uut, 'txHistory').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions'
endpoint: 'txHistory'
})
const jsonStr = JSON.stringify(txCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
@@ -137,15 +137,15 @@ describe('#BCHRPC', () => {
assert.equal(result, true)
})
it('should route to the transaction method', async () => {
it('should route to the txData method', async () => {
// Mock dependencies
sandbox.stub(uut, 'transaction').resolves(true)
sandbox.stub(uut, 'txData').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const rpcCall = jsonrpc.request(id, 'bch', {
endpoint: 'transaction'
endpoint: 'txData'
})
const jsonStr = JSON.stringify(rpcCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
@@ -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)
@@ -176,13 +177,13 @@ describe('#BCHRPC', () => {
it('should return 500 status on routing issue', async () => {
// Mock dependencies
sandbox.stub(uut, 'transactions').rejects(new Error('test error'))
sandbox.stub(uut, 'txHistory').rejects(new Error('test error'))
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions'
endpoint: 'txHistory'
})
const jsonStr = JSON.stringify(txCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
@@ -193,29 +194,24 @@ describe('#BCHRPC', () => {
assert.equal(result.success, false)
assert.equal(result.status, 500)
assert.equal(result.message, 'test error')
assert.equal(result.endpoint, 'transactions')
assert.equal(result.endpoint, 'txHistory')
})
})
describe('#transactions', () => {
describe('#txHistory', () => {
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()
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions',
endpoint: 'txHistory',
addresses: 'testAddr'
})
const jsonStr = JSON.stringify(txCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.transactions(rpcData)
// console.log('response: ', response)
const response = await uut.txHistory(rpcData)
console.log('response: ', response)
assert.equal(response.success, true)
assert.equal(response.status, 200)
@@ -224,26 +220,26 @@ 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
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions',
endpoint: 'txHistory',
addresses: 'testAddr'
})
const jsonStr = JSON.stringify(txCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.transactions(rpcData)
const response = await uut.txHistory(rpcData)
// console.log('response: ', response)
assert.equal(response.success, false)
assert.equal(response.status, 422)
assert.equal(response.message, 'Invalid address')
assert.equal(response.endpoint, 'transactions')
assert.equal(response.endpoint, 'txHistory')
})
})
@@ -391,22 +387,19 @@ describe('#BCHRPC', () => {
})
})
describe('#transaction', () => {
it('should return data from bchjs', async () => {
// Mock dependencies
sandbox.stub(uut.bchjs.Transaction, 'get').resolves({ success: true })
describe('#txData', () => {
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()
const rpcCall = jsonrpc.request(id, 'bch', {
endpoint: 'transaction',
endpoint: 'txData',
txid: 'testData'
})
const jsonStr = JSON.stringify(rpcCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.transaction(rpcData)
const response = await uut.txData(rpcData)
// console.log('response: ', response)
assert.equal(response.success, true)
@@ -416,33 +409,37 @@ 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
// endpoint.
const id = uid()
const rpcCall = jsonrpc.request(id, 'bch', {
endpoint: 'transaction',
endpoint: 'txData',
txid: 'testTxid'
})
const jsonStr = JSON.stringify(rpcCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.transaction(rpcData)
const response = await uut.txData(rpcData)
// console.log('response: ', response)
assert.equal(response.success, false)
assert.equal(response.status, 422)
assert.equal(response.message, 'Invalid data')
assert.equal(response.endpoint, 'transaction')
assert.equal(response.endpoint, 'txData')
})
})
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
+5 -1
View File
@@ -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
}
+17
View File
@@ -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, 'txHistory')
})
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, 'txHistory')
})
})
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, 'txData')
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, 'txData')
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, 'txData')
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')