mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service.git
synced 2026-09-22 01:02:03 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d12749043 | ||
|
|
2ebd8b998f | ||
|
|
11618a796f | ||
|
|
025ae20704 | ||
|
|
b9146bbff9 | ||
|
|
47f1b84dd8 | ||
|
|
fdf73610eb | ||
|
|
b09e77c079 | ||
|
|
5050816052 | ||
|
|
7193b9d048 | ||
|
|
c2977c5264 | ||
|
|
68ad2561b1 | ||
|
|
791d11e597 | ||
|
|
5489e76f55 | ||
|
|
2ad10180d0 | ||
|
|
dd42f5da16 | ||
|
|
496a1dd07c | ||
|
|
a7b8ca9ad5 | ||
|
|
6eb16eb11a | ||
|
|
553957a173 | ||
|
|
99a737c7e7 | ||
|
|
ffb87d09c2 | ||
|
|
65ead3568a | ||
|
|
b4b2aefac6 | ||
|
|
1844f30dc5 | ||
|
|
b6ab2f02f4 | ||
|
|
b5c76aa5fe | ||
|
|
7648c96a70 | ||
|
|
79f5adaf6b | ||
|
|
e2f84d661f | ||
|
|
31091a50a8 | ||
|
|
512775c68f | ||
|
|
c53394e547 | ||
|
|
79083502fb | ||
|
|
6049c6566f | ||
|
|
99a52ec605 | ||
|
|
33e7bc2b52 | ||
|
|
a20f30ec1e | ||
|
|
b0b12c590c | ||
|
|
286092f2e3 | ||
|
|
5be93afc38 | ||
|
|
63cb107a25 | ||
|
|
c54fb73c7d | ||
|
|
8063c0e922 | ||
|
|
b729c7ed80 | ||
|
|
5a3d91e3b0 | ||
|
|
d9fa44328a | ||
|
|
efaffa8e0f | ||
|
|
27a9e9f3f3 | ||
|
|
41a5071467 | ||
|
|
c6c3bb5d9c | ||
|
|
e4977a4351 | ||
|
|
fe3b45f6be | ||
|
|
d25b240112 | ||
|
|
7a89a06ddb | ||
|
|
74fb65df72 | ||
|
|
0b38247f5a | ||
|
|
473aaae307 | ||
|
|
284f4e4478 | ||
|
|
56f6dc273a | ||
|
|
4bd3387bde | ||
|
|
7e7e91460a | ||
|
|
e5f6f24570 | ||
|
|
818c85e3d4 | ||
|
|
4101615a4a | ||
|
|
34a4072384 | ||
|
|
5fc72be10c | ||
|
|
651c4a1346 | ||
|
|
4b90ad5009 | ||
|
|
05ddf8521a | ||
|
|
88d1805685 | ||
|
|
ea104b5586 | ||
|
|
4251e2a02d | ||
|
|
5e0eb017ef | ||
|
|
7b112a13d4 |
+5
-11
@@ -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)
|
||||
|
||||
Generated
+6661
-14933
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,12 +24,12 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-service",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "5.2.0",
|
||||
"@psf/bch-js": "5.3.2",
|
||||
"axios": "0.25.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"glob": "7.1.6",
|
||||
"ipfs": "0.60.0",
|
||||
"ipfs-coord": "6.8.10",
|
||||
"ipfs-coord": "7.1.5",
|
||||
"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",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
COMPOSE_PROJECT_NAME=bch-wallet-service
|
||||
@@ -10,7 +10,7 @@ RUN apt-get update
|
||||
RUN apt-get install -y sudo git curl nano gnupg wget
|
||||
|
||||
#Install Node and NPM
|
||||
RUN curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh
|
||||
RUN curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh
|
||||
RUN bash nodesource_setup.sh
|
||||
RUN apt-get install -y nodejs build-essential
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export DEBUG_LEVEL=2
|
||||
# Change APISERVER to point to your own installation of bch-api
|
||||
export APISERVER=https://api.fullstack.cash/v5/
|
||||
# These settings are used to proxy a subscription to FullStack.cash.
|
||||
export GET_JWT_AT_STARTUP=1
|
||||
#export GET_JWT_AT_STARTUP=1
|
||||
export AUTHSERVER=https://auth.fullstack.cash
|
||||
export FULLSTACKLOGIN=demo@demo.com
|
||||
export FULLSTACKPASS=demo
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is an example for running a production environment, which is
|
||||
# defined by running an external go-ipfs node.
|
||||
|
||||
# Ports
|
||||
export PORT=5010 # REST API port
|
||||
|
||||
# The human-readible name that is used when displaying data about this node.
|
||||
export COORD_NAME=ipfs-bch-service-generic
|
||||
|
||||
# This is used for end-to-end encryption (e2ee).
|
||||
export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran"
|
||||
|
||||
# 0 = less verbose. 3 = most verbose
|
||||
export DEBUG_LEVEL=2
|
||||
|
||||
# Production settings that use external IPFS node.
|
||||
# https://github.com/christroutner/docker-ipfs
|
||||
export SVC_ENV=production
|
||||
export IPFS_HOST=localhost
|
||||
#export IPFS_HOST=172.17.0.1
|
||||
export IPFS_API_PORT=5001
|
||||
|
||||
# Configure IPFS ports
|
||||
export IPFS_TCP_PORT=4001
|
||||
#export IPFS_WS_PORT=5269
|
||||
|
||||
# MongoDB connection string.
|
||||
export DBURL=mongodb://localhost:27017/ipfs-service-dev
|
||||
|
||||
export ENABLE_CIRCUIT_RELAY=true
|
||||
|
||||
npm start
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -98,7 +98,7 @@ class IpfsCoordAdapter {
|
||||
attachRPCRouter (router) {
|
||||
try {
|
||||
_this.ipfsCoord.privateLog = router
|
||||
_this.ipfsCoord.adapters.orbit.privateLog = router
|
||||
_this.ipfsCoord.adapters.pubsub.privateLog = router
|
||||
} catch (err) {
|
||||
console.error('Error in attachRPCRouter()')
|
||||
throw err
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/*
|
||||
This is the JSON RPC router for the users API
|
||||
|
||||
CT 3/5/22: This library can probably be deleted. Handling of the /about endpoint
|
||||
is now directly controlled by the ipfs-coord library.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
@@ -33,6 +36,8 @@ class AboutRPC {
|
||||
// This is a bit different than other router libraries, because there is
|
||||
// only one response, which is a string about this node.
|
||||
async aboutRouter (rpcData) {
|
||||
console.log('debugging: aboutRouter from ipfs-service-provider triggered')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
status: 200,
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -89,8 +89,9 @@ describe('#IPFS', () => {
|
||||
}
|
||||
},
|
||||
adapters: {
|
||||
orbit: {
|
||||
privateLog: () => {}
|
||||
pubsub: {
|
||||
privateLog: () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +112,7 @@ describe('#IPFS', () => {
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, 'Cannot read')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -52,7 +52,8 @@ describe('#IPFS-adapter', () => {
|
||||
it('should stop the IPFS node', async () => {
|
||||
// Mock dependencies
|
||||
uut.ipfs = {
|
||||
stop: () => {}
|
||||
stop: () => {
|
||||
}
|
||||
}
|
||||
|
||||
const result = await uut.stop()
|
||||
@@ -61,24 +62,24 @@ describe('#IPFS-adapter', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#rmBlocksDir', () => {
|
||||
it('should delete the /blocks directory', () => {
|
||||
const result = uut.rmBlocksDir()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should catch and throw an error', () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.fs, 'rmdirSync').throws(new Error('test error'))
|
||||
|
||||
uut.rmBlocksDir()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
// describe('#rmBlocksDir', () => {
|
||||
// it('should delete the /blocks directory', () => {
|
||||
// const result = uut.rmBlocksDir()
|
||||
//
|
||||
// assert.equal(result, true)
|
||||
// })
|
||||
//
|
||||
// it('should catch and throw an error', () => {
|
||||
// try {
|
||||
// // Force an error
|
||||
// sandbox.stub(uut.fs, 'rmdirSync').throws(new Error('test error'))
|
||||
//
|
||||
// uut.rmBlocksDir()
|
||||
//
|
||||
// assert.fail('Unexpected code path')
|
||||
// } catch (err) {
|
||||
// assert.equal(err.message, 'test error')
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -333,7 +333,7 @@ describe('#UserRPC', () => {
|
||||
assert.equal(result.endpoint, 'updateUser')
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.include(result.message, 'Cannot read property')
|
||||
assert.include(result.message, 'Cannot read')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -375,7 +375,7 @@ describe('#UserRPC', () => {
|
||||
assert.equal(result.endpoint, 'getUser')
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.include(result.message, 'Cannot read property')
|
||||
assert.include(result.message, 'Cannot read')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ let ctx
|
||||
const mockContext = require('../../../../unit/mocks/ctx-mock').context
|
||||
|
||||
describe('Contact', () => {
|
||||
before(async () => {})
|
||||
before(async () => {
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new ContactController()
|
||||
@@ -36,7 +37,7 @@ describe('Contact', () => {
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, 'Cannot read')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ let ctx
|
||||
const mockContext = require('../../../../unit/mocks/ctx-mock').context
|
||||
|
||||
describe('Logapi', () => {
|
||||
before(async () => {})
|
||||
before(async () => {
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new LogsApiController()
|
||||
@@ -35,7 +36,7 @@ describe('Logapi', () => {
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, 'Cannot read')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ describe('#Users-REST-Controller', () => {
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, 'Cannot read')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -91,9 +91,9 @@ describe('#Users-REST-Controller', () => {
|
||||
assert.property(ctx.response.body, 'user')
|
||||
assert.property(ctx.response.body, 'token')
|
||||
|
||||
// Used by downstream tests.
|
||||
// testUser = ctx.response.body.user
|
||||
// console.log('testUser: ', testUser)
|
||||
// Used by downstream tests.
|
||||
// testUser = ctx.response.body.user
|
||||
// console.log('testUser: ', testUser)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -181,7 +181,7 @@ describe('#Users-REST-Controller', () => {
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, 'Cannot read')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -223,7 +223,7 @@ describe('#Users-REST-Controller', () => {
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, 'Cannot read')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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, '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')
|
||||
|
||||
@@ -153,7 +150,7 @@ describe('#users-use-case', () => {
|
||||
await uut.getAllUsers()
|
||||
// console.log(`users: ${JSON.stringify(users, null, 2)}`)
|
||||
|
||||
// assert.isArray(users)
|
||||
// assert.isArray(users)
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
@@ -228,8 +225,8 @@ describe('#users-use-case', () => {
|
||||
// Assert that the expected properties for the user model exist.
|
||||
// assert.property(result, 'type')
|
||||
assert.property(result, '_id')
|
||||
// assert.property(result, 'email')
|
||||
// assert.property(result, 'name')
|
||||
// assert.property(result, 'email')
|
||||
// assert.property(result, 'name')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -241,7 +238,7 @@ describe('#users-use-case', () => {
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, 'Cannot read')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -346,7 +343,7 @@ describe('#users-use-case', () => {
|
||||
// assert.equal(result.name, 'testy tester')
|
||||
// })
|
||||
|
||||
// TODO: verify that an admin can change the type of a user
|
||||
// TODO: verify that an admin can change the type of a user
|
||||
})
|
||||
|
||||
describe('#authUser', () => {
|
||||
@@ -356,9 +353,9 @@ describe('#users-use-case', () => {
|
||||
await uut.authUser('test@test.com', 'password')
|
||||
// console.log('user: ', user)
|
||||
|
||||
// assert.property(user, '_id')
|
||||
// assert.property(user, 'email')
|
||||
// assert.property(user, 'name')
|
||||
// assert.property(user, '_id')
|
||||
// assert.property(user, 'email')
|
||||
// assert.property(user, 'name')
|
||||
})
|
||||
|
||||
it('should throw an error if no user matches the login', async () => {
|
||||
@@ -399,7 +396,7 @@ describe('#users-use-case', () => {
|
||||
assert.fail('Unexpected code path.')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, 'Cannot read')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user