Compare commits

...
29 Commits
Author SHA1 Message Date
Chris Troutner bb44b75973 Merge pull request #7 from Permissionless-Software-Foundation/ct-unstable
syncing with upstream ipfs-service-provider
2021-12-24 15:39:31 -08:00
Chris Troutner 698cca555b Merge branch 'ct-unstable' of https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer into ct-unstable 2021-12-24 15:36:16 -08:00
Chris Troutner 76b3740e74 Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2021-12-24 15:35:28 -08:00
Chris Troutner 2622038df5 Updating api docs 2021-12-24 15:35:12 -08:00
Chris Troutner f1fbe34b58 Merge pull request #60 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v6.7.5
2021-12-24 15:26:04 -08:00
Chris Troutner d232b6404d fix(ipfs-coord): Updating to v6.7.5 2021-12-24 15:24:38 -08:00
Chris Troutner 9f92cef122 feat(p2wdb): Added endpoint for writing to P2WDB 2021-12-22 16:49:17 -08:00
Chris Troutner 18e98f9a3b feat(p2wdb): Added endpoint to select different P2WDB provider 2021-12-22 11:02:41 -08:00
Chris Troutner 938cc755f1 feat(p2wdb): Added REST API endpoints for P2WDB 2021-12-21 19:17:41 -08:00
Chris Troutner 6db6db8187 fix(ipfs-coord): Refactoring. Adding pollForP2wdbServices() 2021-12-21 18:20:55 -08:00
Chris Troutner 35af9c08fa Adding minor comments 2021-12-21 17:37:39 -08:00
Chris Troutner 2caf18977e Merge pull request #6 from Permissionless-Software-Foundation/ct-unstable
fix(bch/getStats()): Adding names to service provider IDs
2021-12-17 15:13:48 -08:00
Chris Troutner 8857242570 fix(bch/getStats()): Adding names to service provider IDs 2021-12-14 17:04:55 -08:00
Chris Troutner 4f0e59ea61 Merge pull request #5 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2021-12-06 12:57:40 -08:00
Chris Troutner a1a910c3b5 Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2021-12-06 12:56:32 -08:00
Chris Troutner 1bf8f756e5 Merge pull request #59 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs): Deleting /blocks dir if it prevents app from starting
2021-12-06 12:48:48 -08:00
Chris Troutner 1e513cc133 fix(ipfs): Deleting /blocks dir if it prevents app from starting 2021-12-06 12:46:22 -08:00
Chris Troutner 2f48f1bb61 Merge pull request #4 from Permissionless-Software-Foundation/ct-unstable
feat(REST API): Adding and upgrading REST API endpoints
2021-12-06 12:17:04 -08:00
Chris Troutner 70c81af88e Added REST endpoint for ipfs-relays 2021-12-05 12:00:24 -08:00
Chris Troutner cdbb8b18b6 Added REST API endpoint for ipfs/peers 2021-12-04 10:08:30 -08:00
Chris Troutner b3c3bb4ae2 Added ipfs-status endpoint 2021-12-04 08:57:44 -08:00
Chris Troutner 4d291f7310 Refining BCH REST API endpoints 2021-12-04 07:52:22 -08:00
Chris Troutner 8641a4100c Updating gitignore 2021-12-02 14:09:43 -08:00
Chris Troutner 3a30dd6b03 fix(mongo): Changing db name 2021-12-02 14:08:33 -08:00
Chris Troutner 762ed8dd43 Merge branch 'ct-unstable' 2021-12-02 10:25:09 -08:00
Chris Troutner 17914f2937 Removing default service-provider in config file 2021-12-02 10:24:57 -08:00
Chris Troutner 74c6bc5737 Merge pull request #58 from Permissionless-Software-Foundation/ct-unstable
fix(garbage collection): Updating IPFS config for more conservative GC
2021-11-27 15:14:30 -08:00
Chris Troutner bac6c8b1e9 Disabling debug code 2021-11-27 15:13:23 -08:00
Chris Troutner 9e7d955bc4 fix(garbage collection): Updating IPFS config for more conservative GC 2021-11-27 15:12:08 -08:00
26 changed files with 1084 additions and 57 deletions
+2
View File
@@ -7,6 +7,7 @@
#logs
logs/*.json
*.log
*.log.*
npm-debug.log*
# Runtime data
@@ -65,5 +66,6 @@ system-user-*.json
orbitdb
ipfsdata
.ipfsdata
run-dev.sh
!README.md
+8 -1
View File
@@ -80,9 +80,16 @@ module.exports = {
debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2,
// Preferred provider running ipfs-bch-wallet-service for BCH wallet.
preferredProvider: process.env.PREFERRED_PROVIDER
? process.env.PREFERRED_PROVIDER
: 'QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY'
: '',
// : 'QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY'
// : 'QmUTx6KqYKVZbKpKxR7vGDUgZFYVvVVyEWDeCYq4GwBCff'
// Used for development
// Preferred P2WDB provider
preferredP2wdbProvider: process.env.PREFERRED_P2WDB_PROVIDER
? process.env.PREFERRED_P2WDB_PROVIDER
: ''
}
+1 -1
View File
@@ -7,6 +7,6 @@
module.exports = {
session: 'secret-boilerplate-token',
token: 'secret-jwt-token',
database: 'mongodb://localhost:27017/ipfs-service-dev',
database: 'mongodb://localhost:27017/ipfs-bch-consumer-dev',
env: 'dev'
}
+1 -1
View File
@@ -13,6 +13,6 @@ module.exports = {
// database: 'mongodb://172.17.0.1:5555/ipfs-service-prod',
database: process.env.DBURL
? process.env.DBURL
: 'mongodb://172.17.0.1:5555/ipfs-service-prod',
: 'mongodb://172.17.0.1:5555/ipfs-bch-consumer-prod',
env: 'prod'
}
+1 -1
View File
@@ -7,6 +7,6 @@
module.exports = {
session: 'secret-boilerplate-token',
token: 'secret-jwt-token',
database: 'mongodb://localhost:27017/ipfs-service-test',
database: 'mongodb://localhost:27017/ipfs-bch-consumer-test',
env: 'test'
}
+1 -1
View File
@@ -1,6 +1,6 @@
const common = require('./env/common')
const env = process.env.SVC_ENV || 'development'
const env = process.env.CONSUMER_ENV || 'development'
const config = require(`./env/${env}`)
module.exports = Object.assign({}, common, config)
+7 -7
View File
@@ -14,7 +14,7 @@
"axios": "0.21.1",
"bcryptjs": "2.4.3",
"glob": "7.1.6",
"ipfs-coord": "6.7.4",
"ipfs-coord": "6.7.5",
"jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1",
"jwt-bch-lib": "1.3.0",
@@ -9611,9 +9611,9 @@
}
},
"node_modules/ipfs-coord": {
"version": "6.7.4",
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-6.7.4.tgz",
"integrity": "sha512-nboEp4xge4ZE8VWSCPhIB5PXQafKChacLOTVwxFfmjzgrXenacw3PBuR/UGxMJ3JX+t13ee/M662Jd8nzp7rZA==",
"version": "6.7.5",
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-6.7.5.tgz",
"integrity": "sha512-CwsitrjROTV0YF+H6pasR+K2AcWyJMhOLMdLLmm8e6a6kL3NShUUcOQ8lgLiPLM8qbfQF/B+cPaXW0aVyOxqWg==",
"license": "MIT",
"dependencies": {
"axios": "0.21.4",
@@ -33139,9 +33139,9 @@
}
},
"ipfs-coord": {
"version": "6.7.4",
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-6.7.4.tgz",
"integrity": "sha512-nboEp4xge4ZE8VWSCPhIB5PXQafKChacLOTVwxFfmjzgrXenacw3PBuR/UGxMJ3JX+t13ee/M662Jd8nzp7rZA==",
"version": "6.7.5",
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-6.7.5.tgz",
"integrity": "sha512-CwsitrjROTV0YF+H6pasR+K2AcWyJMhOLMdLLmm8e6a6kL3NShUUcOQ8lgLiPLM8qbfQF/B+cPaXW0aVyOxqWg==",
"requires": {
"axios": "0.21.4",
"bch-encrypt-lib": "2.0.0",
+6 -6
View File
@@ -6,14 +6,14 @@
"scripts": {
"start": "node index.js",
"test": "npm run test:all",
"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 15000 test/e2e/automated/",
"test:temp": "export SVC_ENV=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/",
"test:all": "export CONSUMER_ENV=test && nyc --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/",
"test:unit": "export CONSUMER_ENV=test && mocha --exit --timeout 15000 --recursive test/unit/",
"test:e2e:auto": "export CONSUMER_ENV=test && mocha --exit --timeout 15000 test/e2e/automated/",
"test:temp": "export CONSUMER_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",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "export SVC_ENV=test && nyc --reporter=html mocha --exit --timeout 15000 --recursive test/unit/ test/e2e/automated/"
"coverage:report": "export CONSUMER_ENV=test && nyc --reporter=html mocha --exit --timeout 15000 --recursive test/unit/ test/e2e/automated/"
},
"author": "Chris Troutner <chris.troutner@gmail.com>",
"license": "MIT",
@@ -28,7 +28,7 @@
"axios": "0.21.1",
"bcryptjs": "2.4.3",
"glob": "7.1.6",
"ipfs-coord": "6.7.4",
"ipfs-coord": "6.7.5",
"jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1",
"jwt-bch-lib": "1.3.0",
+50 -7
View File
@@ -58,18 +58,61 @@ class BchAdapter {
}
}
// Get the status of BCH wallet services this node can talk to. Returns an
// array of BCH wallet service providers.
async getStatus () {
try {
console.log(
'this.ipfs.ipfsCoordAdapter.state: ',
this.ipfs.ipfsCoordAdapter.state
)
const peerData = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
const status = {
state: this.ipfs.ipfsCoordAdapter.state
// const status = {
// state: this.ipfs.ipfsCoordAdapter.state
// }
// Add names to the IPFS IDs for each provider.
const initialServiceProviders =
this.ipfs.ipfsCoordAdapter.state.serviceProviders
const serviceProviders = []
for (let i = 0; i < initialServiceProviders.length; i++) {
const thisProvider = initialServiceProviders[i]
const providerData = peerData.filter((x) => x.from === thisProvider)
if (providerData.length) {
const provObj = {
ipfsId: thisProvider,
name: providerData[0].data.jsonLd.name
}
serviceProviders.push(provObj)
}
}
return status
// console.log(
// `serviceProviders: ${JSON.stringify(serviceProviders, null, 2)}`
// )
const outObj = {
serviceProviders,
selectedProvider:
this.ipfs.ipfsCoordAdapter.state.selectedServiceProvider
}
// console.log('outObj: ', outObj)
return outObj
} catch (err) {
// console.log('createUser() error: ', err)
wlogger.error('Error in use-cases/bch.js/getStatus()')
throw err
}
}
// Choose the BCH wallet service to use.
async selectProvider (providerId) {
try {
this.ipfs.ipfsCoordAdapter.config.preferredProvider = providerId
return true
} catch (err) {
// console.log('createUser() error: ', err)
wlogger.error('Error in use-cases/bch.js/getStatus()')
+2
View File
@@ -17,6 +17,7 @@ const Nodemailer = require('./nodemailer')
const JSONFiles = require('./json-files')
const FullStackJWT = require('./fullstack-jwt')
const BCH = require('./bch')
const P2WDB = require('./p2wdb')
const config = require('../../config')
@@ -41,6 +42,7 @@ class Adapters {
this.bchjs = new BCHJS()
this.config = config
this.bch = new BCH(localConfig)
this.p2wdb = new P2WDB(localConfig)
// Get a valid JWT API key and instance bch-js.
this.fullStackJwt = new FullStackJWT(config)
+124
View File
@@ -58,6 +58,130 @@ class IPFS {
throw err
}
}
// Get the status of this IPFS node.
getStatus () {
try {
// console.log(
// 'this.ipfsCoordAdapter.ipfsCoord.thisNode: ',
// this.ipfsCoordAdapter.ipfsCoord.thisNode
// )
const statusObj = {
ipfsId: this.ipfsCoordAdapter.ipfsCoord.thisNode.ipfsId,
multiAddrs: this.ipfsCoordAdapter.ipfsCoord.thisNode.ipfsMultiaddrs,
bchAddr: this.ipfsCoordAdapter.ipfsCoord.thisNode.bchAddr,
slpAddr: this.ipfsCoordAdapter.ipfsCoord.thisNode.slpAddr,
pubKey: this.ipfsCoordAdapter.ipfsCoord.thisNode.pubKey,
peers: this.ipfsCoordAdapter.ipfsCoord.thisNode.peerList.length,
relays: this.ipfsCoordAdapter.ipfsCoord.thisNode.relayData.length
}
return statusObj
} catch (err) {
console.error('Error in ipfs-coord.js/getStatus()')
throw err
}
}
// Get details on the other peers this node is connected to.
async getPeers (showAll) {
try {
const peerData = this.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
let ipfsPeers =
await this.ipfsCoordAdapter.ipfsCoord.adapters.ipfs.getPeers()
// console.log('ipfsPeers: ', ipfsPeers)
ipfsPeers = this._removeDuplicatePeers(ipfsPeers)
// console.log('filtered ipfsPeers: ', ipfsPeers)
// Loop through each IPFS peer and hydrate it with data from the peerData.
for (let i = 0; i < ipfsPeers.length; i++) {
const thisPeer = ipfsPeers[i]
if (!showAll) {
// Delete properties that don't contain good info.
delete thisPeer.muxer
delete thisPeer.latency
delete thisPeer.streams
}
// Get the ipfs-coord peer data for this peer.
let thisPeerData = peerData.filter((x) =>
x.from.includes(thisPeer.peer)
)
thisPeerData = thisPeerData[0]
// Skip if peerData for this IPFS peer could not be found.
if (!thisPeerData) continue
try {
// console.log('thisPeerData: ', thisPeerData)
// Add data to the IPFS peer data.
thisPeer.name = thisPeerData.data.jsonLd.name
thisPeer.protocol = thisPeerData.data.jsonLd.protocol
thisPeer.version = thisPeerData.data.jsonLd.version
if (showAll) {
// Add all the peer data.
thisPeer.peerData = thisPeerData
}
} catch (err) {
console.log(
`Error trying to hydrate peer ${thisPeer.peer}: ${err.message}`
)
}
}
return ipfsPeers
} catch (err) {
console.error('Error in getPeers(): ', err)
throw err
}
}
// Get data about the known Circuit Relays. Hydrate with data from peers list.
getRelays () {
try {
const relayData = this.ipfsCoordAdapter.ipfsCoord.thisNode.relayData
const peerData = this.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
// console.log(`relayData: ${JSON.stringify(relayData, null, 2)}`)
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
for (let i = 0; i < relayData.length; i++) {
const thisRelay = relayData[i]
// Find the peer that corresponds to this relay.
const thisPeer = peerData.filter((x) =>
x.from.includes(thisRelay.ipfsId)
)
// console.log('thisPeer: ', thisPeer)
// If the peer couldn't be found, skip.
if (!thisPeer.length) {
thisRelay.name = ''
continue
}
thisRelay.name = thisPeer[0].data.jsonLd.name
}
return relayData
} catch (err) {
console.error('Error in getRelays(): ', err)
throw err
}
}
// Expects an array of peers and returns an array of peers with duplicates
// removed.
_removeDuplicatePeers (arr) {
// https://stackoverflow.com/questions/2218999/how-to-remove-all-duplicates-from-an-array-of-objects
return arr.filter((v, i, a) => a.findIndex((t) => t.peer === v.peer) === i)
}
}
module.exports = IPFS
+87 -17
View File
@@ -15,8 +15,10 @@ const config = require('../../../config')
// const JSONRPC = require('../../controllers/json-rpc/')
// The minimum version of ipfs-bch-wallet-service that this wallet can work with.
const MIN_BCH_WALLET_VERSION = '1.11.0'
const MIN_BCH_WALLET_VERSION = '1.11.11'
const WALLET_PROTOCOL = 'bch-wallet'
const MIN_P2WDB_VERSION = '1.4.0'
const P2WDB_PROTOCOL = 'p2wdb'
let _this
@@ -48,12 +50,15 @@ class IpfsCoordAdapter {
this.isReady = false
// Periodically poll services for available wallet service providers.
this.pollServiceInterval = setInterval(this.pollForServices, 10000)
this.pollBchServiceInterval = setInterval(this.pollForBchServices, 10000)
this.pollP2wdbServiceInterval = setInterval(this.pollForP2wdbServices, 11000)
// State object. TODO: Make this more robust.
this.state = {
serviceProviders: [],
selectedServiceProvider: ''
selectedServiceProvider: '',
p2wdbProviders: [],
selectedP2wdbProvider: ''
}
_this = this
@@ -112,9 +117,84 @@ class IpfsCoordAdapter {
}
}
// Poll the ipfs-coord coordination channel for available service providers.
// This method is called periodically by a timer-interval.
pollForServices () {
// Poll the ipfs-coord coordination channel for available P2WDB service
// providers. This method is called periodically by a timer-interval.
pollForP2wdbServices () {
try {
// An array of IPFS IDs of other nodes in the coordination pubsub channel.
const peers = _this.ipfsCoord.thisNode.peerList
// console.log(`peers: ${JSON.stringify(peers, null, 2)}`)
// Array of objects. Each object is the IPFS ID of the peer and contains
// data about that peer.
const peerData = _this.ipfsCoord.thisNode.peerData
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
for (let i = 0; i < peers.length; i++) {
const thisPeer = peers[i]
const thisData = peerData.filter((x) => x.from === thisPeer)
const thisPeerData = thisData[0]
// Create a 'fingerprint' that defines the wallet service.
const protocol = thisPeerData.data.jsonLd.protocol
const version = thisPeerData.data.jsonLd.version
// console.log(
// `debug: peer ${thisPeer} uses protocol: ${protocol} v${version}`
// )
let versionMatches = false
if (version) {
versionMatches = _this.semver.gt(version, MIN_P2WDB_VERSION)
}
// p2wdbProviders: [],
// selectedP2wdbProvider: ''
// Ignore any peers that don't match the fingerprint for a BCH wallet
// service.
if (protocol && protocol.includes(P2WDB_PROTOCOL) && versionMatches) {
// console.log('Matching peer: ', thisPeerData)
// Temporary business logic.
// Use the first available wallet service detected.
if (_this.state.p2wdbProviders.length === 0) {
_this.state.selectedP2wdbProvider = thisPeer
// Persist the config setting, so it can be used by other commands.
// _this.conf.set('selectedService', thisPeer)
console.log(`---->P2WDB service selected: ${thisPeer}`)
}
// If a preferred provider is set in the config file, then connect
// to the preferred provider when it's discovered.
if (
_this.config.preferredP2wdbProvider &&
thisPeer === _this.config.preferredP2wdbProvider
) {
_this.state.selectedP2wdbProvider = thisPeer
}
// Check if the peer has already been added to the list of providers.
const alreadyExists = _this.state.p2wdbProviders.filter(
(x) => x === thisPeer
)
// Add the peer to the list of serviceProviders if it doesn't already
// exist in the list.
if (!alreadyExists.length) {
_this.state.p2wdbProviders.push(thisPeer)
}
}
}
} catch (err) {
console.error('Error in pollForP2wdbServices()')
throw err
}
}
// Poll the ipfs-coord coordination channel for available BCH wallet service
// providers. This method is called periodically by a timer-interval.
pollForBchServices () {
try {
// An array of IPFS IDs of other nodes in the coordination pubsub channel.
const peers = _this.ipfsCoord.thisNode.peerList
@@ -179,7 +259,7 @@ class IpfsCoordAdapter {
}
}
} catch (err) {
console.error('Error in pollForServices(): ', err)
console.error('Error in pollForBchServices(): ', err)
// Do not throw error. This is a top-level function.
}
}
@@ -198,14 +278,4 @@ class IpfsCoordAdapter {
}
}
// Create a random number to use in the name of this IPFS n ode.
// const randNum = Math.floor(Math.random() * 10000)
// const announceJsonLd = {
// '@context': 'https://schema.org/',
// '@type': 'Person',
// name: `wallet-consumer-${randNum}`,
// description: 'A consumer of BCH wallet services'
// }
module.exports = IpfsCoordAdapter
+36 -1
View File
@@ -11,10 +11,13 @@
// Global npm libraries
// const IPFS = require('ipfs')
const IPFS = require('@chris.troutner/ipfs')
const fs = require('fs')
// Local libraries
const config = require('../../../config')
const IPFS_DIR = './.ipfsdata/ipfs'
class IpfsAdapter {
constructor (localConfig) {
// Encapsulate dependencies
@@ -23,6 +26,7 @@ class IpfsAdapter {
// Properties of this class instance.
this.isReady = false
this.config = config
this.fs = fs
}
// Start an IPFS node.
@@ -30,7 +34,7 @@ class IpfsAdapter {
try {
// Ipfs Options
const ipfsOptions = {
repo: './.ipfsdata/ipfs',
repo: IPFS_DIR,
start: true,
config: {
relay: {
@@ -51,6 +55,11 @@ class IpfsAdapter {
`/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`,
`/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`
]
},
Datastore: {
StorageMax: '2GB',
StorageGCWatermark: 50,
GCPeriod: '15m'
}
}
}
@@ -71,6 +80,12 @@ class IpfsAdapter {
return this.ipfs
} catch (err) {
console.error('Error in ipfs.js/start()')
// If IPFS crashes because the /blocks directory is full, wipe the directory.
if (err.message.includes('No space left on device')) {
this.rmBlocksDir()
}
throw err
}
}
@@ -80,6 +95,26 @@ class IpfsAdapter {
return true
}
// Remove the '/blocks' directory that is used to store IPFS data.
// Dev Note: It's assumed this node is not pinning any data and that
// everything in this directory is transient. This folder will regularly
// fill up and prevent IPFS from starting.
rmBlocksDir () {
try {
const dir = `${IPFS_DIR}/blocks`
console.log(`Deleting ${dir} directory...`)
this.fs.rmdirSync(dir, { recursive: true })
console.log(`${dir} directory is deleted!`)
return true // Signal successful execution.
} catch (err) {
console.log('Error in rmBlocksDir()')
throw err
}
}
}
module.exports = IpfsAdapter
+268
View File
@@ -0,0 +1,268 @@
/*
This library contains business-logic for dealing with P2WDB service
providers. Most of these functions are called by the /p2wdb REST API endpoints.
*/
// Public npm libraries
const { v4: uid } = require('uuid')
const jsonrpc = require('jsonrpc-lite')
// Local libraries
const { wlogger } = require('../wlogger')
let _this
class P2wdbAdapter {
constructor (localConfig = {}) {
// console.log('BCH localConfig: ', localConfig)
this.ipfs = localConfig.ipfs
if (!this.ipfs) {
throw new Error(
'An instance of IPFS must be passed when instantiating the P2WDB Adapter library.'
)
}
this.eventEmitter = localConfig.eventEmitter
if (!this.eventEmitter) {
throw new Error(
'An instance of an EventEmitter must be passed when instantiating the adapters.'
)
}
// Connect the RPC handler when the event fires with new data.
this.eventEmitter.on('rpcData', this.rpcHandler)
// Encapsulate dependencies
this.uid = uid
this.jsonrpc = jsonrpc
// A queue for holding RPC data that has arrived.
this.rpcDataQueue = []
_this = this // Global handle on instance of this Class.
}
// This handler is triggered when RPC data comes in over IPFS.
// Handle RPC input, and add the response to the RPC queue.
// Once in the queue, it will get processed by waitForRPCResponse()
rpcHandler (data) {
try {
// Convert string input into an object.
// const jsonData = JSON.parse(data)
console.log(`JSON RPC response for ID ${data.payload.id} received.`)
_this.rpcDataQueue.push(data)
} catch (err) {
console.error('Error in p2wdb/rpcHandler(): ', err)
// Do not throw error. This is a top-level function.
}
}
// Get the status of BCH wallet services this node can talk to. Returns an
// array of BCH wallet service providers.
async getStatus () {
try {
const peerData = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
// const status = {
// state: this.ipfs.ipfsCoordAdapter.state
// }
// Add names to the IPFS IDs for each provider.
const initialServiceProviders =
this.ipfs.ipfsCoordAdapter.state.p2wdbProviders
const serviceProviders = []
for (let i = 0; i < initialServiceProviders.length; i++) {
const thisProvider = initialServiceProviders[i]
const providerData = peerData.filter((x) => x.from === thisProvider)
if (providerData.length) {
const provObj = {
ipfsId: thisProvider,
name: providerData[0].data.jsonLd.name
}
serviceProviders.push(provObj)
}
}
// console.log(
// `serviceProviders: ${JSON.stringify(serviceProviders, null, 2)}`
// )
const outObj = {
serviceProviders,
selectedProvider:
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
}
// console.log('outObj: ', outObj)
return outObj
} catch (err) {
// console.log('createUser() error: ', err)
wlogger.error('Error in adapters/p2wdb.js/getStatus()')
throw err
}
}
// Choose the BCH wallet service to use.
async selectProvider (providerId) {
try {
this.ipfs.ipfsCoordAdapter.config.preferredP2wdbProvider = providerId
return true
} catch (err) {
// console.log('createUser() error: ', err)
wlogger.error('Error in adapters/p2wdb/getStatus()')
throw err
}
}
// Read an entry from the P2WDB, given an entry hash.
async getEntryByHash (hash) {
try {
// Input validation.
if (!hash || typeof hash !== 'string') {
throw new Error('getEntry() input hash must be a string.')
}
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
if (!selectedProvider) {
throw new Error('No P2WDB Service provider available yet.')
}
const rpcData = {
endpoint: 'getByHash',
hash
}
// Generate a UUID for the call.
const rpcId = this.uid()
// Generate a JSON RPC command.
const cmd = this.jsonrpc.request(rpcId, 'p2wdb', rpcData)
const cmdStr = JSON.stringify(cmd)
// console.log('cmdStr: ', cmdStr)
// Send the RPC command to selected wallet service.
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
await this.ipfs.ipfsCoordAdapter.ipfsCoord.useCases.peer.sendPrivateMessage(
selectedProvider,
cmdStr,
thisNode
)
// Wait for data to come back from the wallet service.
const data = await this.waitForRPCResponse(rpcId)
return data
} catch (err) {
console.error('Error in getEntryByHash()')
throw err
}
}
async writeEntry (writeObj) {
try {
const { txid, signature, message, data } = writeObj
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
if (!selectedProvider) {
throw new Error('No P2WDB Service provider available yet.')
}
const rpcData = {
endpoint: 'write',
txid,
signature,
message,
data
}
// Generate a UUID for the call.
const rpcId = this.uid()
// Generate a JSON RPC command.
const cmd = this.jsonrpc.request(rpcId, 'p2wdb', rpcData)
const cmdStr = JSON.stringify(cmd)
// console.log('cmdStr: ', cmdStr)
// Send the RPC command to selected wallet service.
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
await this.ipfs.ipfsCoordAdapter.ipfsCoord.useCases.peer.sendPrivateMessage(
selectedProvider,
cmdStr,
thisNode
)
// Wait for data to come back from the wallet service.
const result = await this.waitForRPCResponse(rpcId)
return result
} catch (err) {
console.error('Error in adapters/p2wdb/writeEntry()')
throw err
}
}
// Returns a promise that resolves to data when the RPC response is recieved.
async waitForRPCResponse (rpcId) {
try {
// Initialize variables for tracking the return data.
let dataFound = false
let cnt = 0
// Default return value, if the remote computer does not respond in time.
let data = {
success: false,
message: 'request timed out',
data: ''
}
// Loop that waits for a response from the service provider.
do {
// console.log(`this.rpcDataQueue.length: ${this.rpcDataQueue.length}`)
for (let i = 0; i < this.rpcDataQueue.length; i++) {
const rawData = this.rpcDataQueue[i]
// console.log(`rawData: ${JSON.stringify(rawData, null, 2)}`)
if (rawData.payload.id === rpcId) {
dataFound = true
// console.log('data was found in the queue')
data = rawData.payload.result.value
// Remove the data from the queue
this.rpcDataQueue.splice(i, 1)
break
}
}
// Wait between loops.
// await this.sleep(1000)
await this.ipfs.ipfsCoordAdapter.bchjs.Util.sleep(2000)
cnt++
// Exit if data was returned, or the window for a response expires.
} while (!dataFound && cnt < 10)
// console.log(`dataFound: ${dataFound}, cnt: ${cnt}`)
return data
} catch (err) {
console.error('Error in waitForRPCResponse()')
throw err
}
}
}
module.exports = P2wdbAdapter
+35 -7
View File
@@ -1,5 +1,5 @@
/*
REST API Controller library for the /user route
REST API Controller library for the /bch route
*/
const { wlogger } = require('../../../adapters/wlogger')
@@ -51,6 +51,35 @@ class BchRESTControllerLib {
}
}
/**
* @api {post} /bch/provider Provider
* @apiPermission public
* @apiName Provider
* @apiGroup REST BCH
* @apiDescription Select a different BCH wallet service on the IPFS network,
* for interacting with the BCH blockchain.
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "provider": "QmXtHADckCmT6jodpAgn3TcDQWjC29gQd2fKKHDTpo8DJT" }' localhost:5001/bch/provider
*/
async postProvider (ctx) {
try {
const providerId = ctx.request.body.provider
await this.adapters.bch.selectProvider(providerId)
const body = {
success: true
}
ctx.body = body
} catch (err) {
wlogger.error('Error in bch/controller.js/postProvider(): ')
// ctx.throw(422, err.message)
this.handleError(ctx, err)
}
}
/**
* @api {post} /bch/balance Balance
* @apiName Balance
@@ -196,7 +225,7 @@ class BchRESTControllerLib {
const utxos = await this.adapters.bch.getUtxos(address)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
ctx.body = utxos
ctx.body = [utxos]
} catch (err) {
this.handleError(ctx, err)
}
@@ -402,10 +431,10 @@ class BchRESTControllerLib {
}
/**
* @api {JSON} /bch PubKey
* @api {REST} /bch/pubkey PubKey
* @apiPermission public
* @apiName PubKey
* @apiGroup JSON BCH
* @apiGroup REST BCH
* @apiDescription Get the public key from an address.
* Given an address the endpoint will return an object with the
* following properties
@@ -417,7 +446,7 @@ class BchRESTControllerLib {
* - publickey: - Address public key
*
* @apiExample Example usage:
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "pubkey", "address": "bitcoincash:qpnty9t0w93fez04h7yzevujpv8pun204qv6yfuahk"}}
* curl -H "Content-Type: application/json" -X POST -d '{ "txid": "01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b" }' localhost:5001/bch/pubkey
*
* @apiSuccessExample {json} Success-Response:
* {
@@ -434,7 +463,6 @@ class BchRESTControllerLib {
* "success": true,
* "publicKey": "033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee"
* }
*
* }
*/
async pubKey (ctx) {
@@ -442,7 +470,7 @@ class BchRESTControllerLib {
const address = ctx.request.body.address
const data = await this.adapters.bch.getPubKey(address)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
console.log(`pubkey data: ${JSON.stringify(data, null, 2)}`)
ctx.body = data
} catch (err) {
+5
View File
@@ -52,6 +52,7 @@ class BchRouter {
// Define the routes and attach the controller.
this.router.get('/', this.getStatus)
this.router.post('/provider', this.postProvider)
this.router.post('/balance', this.postBalance)
this.router.post('/utxos', this.postUtxos)
this.router.post('/broadcast', this.postBroadcast)
@@ -68,6 +69,10 @@ class BchRouter {
await _this.bchRESTController.getStatus(ctx, next)
}
async postProvider (ctx, next) {
await _this.bchRESTController.postProvider(ctx, next)
}
async postBalance (ctx, next) {
await _this.bchRESTController.balance(ctx, next)
}
+10
View File
@@ -12,6 +12,8 @@ const UserRouter = require('./users')
const ContactRESTController = require('./contact')
const LogsRESTController = require('./logs')
const BchRESTController = require('./bch')
const IpfsRESTController = require('./ipfs')
const P2wdbRESTController = require('./p2wdb')
class RESTControllers {
constructor (localConfig = {}) {
@@ -57,6 +59,14 @@ class RESTControllers {
// Attach the REST API Controllers associated with the /bch route
const bchRESTController = new BchRESTController(dependencies)
bchRESTController.attach(app)
// Attach the REST API Controllers associated with the /ipfs route
const ipfsRESTController = new IpfsRESTController(dependencies)
ipfsRESTController.attach(app)
// Attach the REST API Controllers associated with the /p2wdb route
const p2wdbRESTController = new P2wdbRESTController(dependencies)
p2wdbRESTController.attach(app)
}
}
@@ -0,0 +1,98 @@
/*
REST API Controller library for the /ipfs route
*/
const { wlogger } = require('../../../adapters/wlogger')
let _this
class IpfsRESTControllerLib {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating /ipfs REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating /ipfs REST Controller.'
)
}
// Encapsulate dependencies
// this.UserModel = this.adapters.localdb.Users
// this.userUseCases = this.useCases.user
_this = this
}
/**
* @api {get} /ipfs Get status on IPFS infrastructure
* @apiPermission public
* @apiName GetIpfsStatus
* @apiGroup REST BCH
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X GET localhost:5001/ipfs
*
*/
async getStatus (ctx) {
try {
const status = await _this.adapters.ipfs.getStatus()
ctx.body = { status }
} catch (err) {
wlogger.error('Error in ipfs/controller.js/getStatus(): ')
// ctx.throw(422, err.message)
_this.handleError(ctx, err)
}
}
// Return information on IPFS peers this node is connected to.
async getPeers (ctx) {
try {
const showAll = ctx.request.body.showAll
const peers = await _this.adapters.ipfs.getPeers(showAll)
ctx.body = { peers }
} catch (err) {
wlogger.error('Error in ipfs/controller.js/getPeers(): ')
// ctx.throw(422, err.message)
_this.handleError(ctx, err)
}
}
// Get data about the known Circuit Relays. Hydrate with data from peers list.
async getRelays (ctx) {
try {
const relays = await _this.adapters.ipfs.getRelays()
ctx.body = { relays }
} catch (err) {
wlogger.error('Error in ipfs/controller.js/getRelays(): ')
// ctx.throw(422, err.message)
_this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
// If an HTTP status is specified by the buisiness logic, use that.
if (err.status) {
if (err.message) {
ctx.throw(err.status, err.message)
} else {
ctx.throw(err.status)
}
} else {
// By default use a 422 error if the HTTP status is not specified.
ctx.throw(422, err.message)
}
}
}
module.exports = IpfsRESTControllerLib
+68
View File
@@ -0,0 +1,68 @@
/*
REST API library for the /ipfs route.
*/
// Public npm libraries.
const Router = require('koa-router')
// Local libraries.
const IPFSRESTControllerLib = require('./controller')
const Validators = require('../middleware/validators')
// let _this
class BchRouter {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating IPFS REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating IPFS REST Controller.'
)
}
const dependencies = {
adapters: this.adapters,
useCases: this.useCases
}
// Encapsulate dependencies.
this.ipfsRESTController = new IPFSRESTControllerLib(dependencies)
this.validators = new Validators()
// Instantiate the router and set the base route.
const baseUrl = '/ipfs'
this.router = new Router({ prefix: baseUrl })
// _this = this
}
attach (app) {
if (!app) {
throw new Error(
'Must pass app object when attaching REST API controllers.'
)
}
// Define the routes and attach the controller.
this.router.get('/', this.ipfsRESTController.getStatus)
this.router.post('/peers', this.ipfsRESTController.getPeers)
this.router.post('/relays', this.ipfsRESTController.getRelays)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
app.use(this.router.allowedMethods())
}
// async getStatus (ctx, next) {
// await _this.ipfsRESTController.getStatus(ctx, next)
// }
}
module.exports = BchRouter
@@ -0,0 +1,138 @@
/*
REST API Controller library for the /p2wdb route
*/
const { wlogger } = require('../../../adapters/wlogger')
// let _this
class P2wdbRESTControllerLib {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating /p2wdb REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating /p2wdb REST Controller.'
)
}
// Encapsulate dependencies
// this.UserModel = this.adapters.localdb.Users
// this.userUseCases = this.useCases.user
// _this = this
}
/**
* @api {get} /p2wdb Get status on P2WDB infrastructure
* @apiPermission public
* @apiName GetP2wdbStatus
* @apiGroup REST BCH
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X GET localhost:5001/p2wdb
*
*/
async getStatus (ctx) {
try {
const status = await this.adapters.p2wdb.getStatus()
ctx.body = { status }
} catch (err) {
wlogger.error('Error in bch/controller.js/getStatus(): ')
// ctx.throw(422, err.message)
this.handleError(ctx, err)
}
}
/**
* @api {post} /p2wdb/provider Provider
* @apiName Provider
* @apiGroup REST P2WDB
* @apiDescription Select a different P2WDB service on the IPFS network, for
* interacting with the P2WDB.
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "provider": "QmXtHADckCmT6jodpAgn3TcDQWjC29gQd2fKKHDTpo8DJT" }' localhost:5001/p2wdb/provider
*/
async postProvider (ctx) {
try {
const providerId = ctx.request.body.provider
await this.adapters.p2wdb.selectProvider(providerId)
const body = {
success: true
}
ctx.body = body
} catch (err) {
wlogger.error('Error in p2wdb/controller.js/postProvider(): ', err)
// ctx.throw(422, err.message)
this.handleError(ctx, err)
}
}
// curl -H "Content-Type: application/json" -X POST -d '{ "hash": "zdpuAzNVEHYegiazAmbnQtvQpEvJGsdXDbcH7vHFYx4NWDUqk" }' localhost:5001/p2wdb/entryFromHash
async getEntryByHash (ctx) {
try {
const hash = ctx.request.body.hash
const data = await this.adapters.p2wdb.getEntryByHash(hash)
const body = {
success: true,
data
}
ctx.body = body
} catch (err) {
wlogger.error('Error in p2wdb/controller.js/getEntryByHash(): ', err)
// ctx.throw(422, err.message)
this.handleError(ctx, err)
}
}
async writeEntry (ctx) {
try {
const txid = ctx.request.body.txid
const signature = ctx.request.body.signature
const message = ctx.request.body.message
const data = ctx.request.body.data
const writeObj = { txid, signature, message, data }
const hash = await this.adapters.p2wdb.writeEntry(writeObj)
ctx.body = {
success: true,
hash
}
} catch (err) {
wlogger.error('Error in p2wdb/controller.js/writeEntry(): ', err)
this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
// If an HTTP status is specified by the buisiness logic, use that.
if (err.status) {
if (err.message) {
ctx.throw(err.status, err.message)
} else {
ctx.throw(err.status)
}
} else {
// By default use a 422 error if the HTTP status is not specified.
ctx.throw(422, err.message)
}
}
}
module.exports = P2wdbRESTControllerLib
+101
View File
@@ -0,0 +1,101 @@
/*
REST API library for /p2wdb route.
*/
// Public npm libraries.
const Router = require('koa-router')
// Local libraries.
const P2wdbRESTControllerLib = require('./controller')
const Validators = require('../middleware/validators')
let _this
class P2wdbRouter {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating P2WDB REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating P2WDB REST Controller.'
)
}
const dependencies = {
adapters: this.adapters,
useCases: this.useCases
}
// Encapsulate dependencies.
this.p2wdbRESTController = new P2wdbRESTControllerLib(dependencies)
this.validators = new Validators()
// Instantiate the router and set the base route.
const baseUrl = '/p2wdb'
this.router = new Router({ prefix: baseUrl })
_this = this
}
attach (app) {
if (!app) {
throw new Error(
'Must pass app object when attaching REST API controllers.'
)
}
// Define the routes and attach the controller.
this.router.get('/', this.getStatus)
this.router.post('/provider', this.postProvider)
this.router.post('/entryFromHash', this.entryFromHash)
this.router.post('/write', this.write)
// this.router.post('/broadcast', this.postBroadcast)
// this.router.post('/transactions', this.postTransactions)
// this.router.post('/transaction', this.postTransaction)
// this.router.post('/pubkey', this.postPubKey)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
app.use(this.router.allowedMethods())
}
async getStatus (ctx, next) {
await _this.p2wdbRESTController.getStatus(ctx, next)
}
async postProvider (ctx, next) {
await _this.p2wdbRESTController.postProvider(ctx, next)
}
async entryFromHash (ctx, next) {
await _this.p2wdbRESTController.getEntryByHash(ctx, next)
}
async write (ctx, next) {
await _this.p2wdbRESTController.writeEntry(ctx, next)
}
// async postBroadcast (ctx, next) {
// await _this.bchRESTController.broadcast(ctx, next)
// }
//
// async postTransactions (ctx, next) {
// await _this.bchRESTController.transactions(ctx, next)
// }
//
// async postTransaction (ctx, next) {
// await _this.bchRESTController.transaction(ctx, next)
// }
//
// async postPubKey (ctx, next) {
// await _this.bchRESTController.pubKey(ctx, next)
// }
}
module.exports = P2wdbRouter
+1 -1
View File
@@ -65,7 +65,7 @@ describe('#BCH E2E Tests', () => {
describe('#broadcast', () => {
it('should attempt to broadcast a transaction', async () => {
const body = {
hex: "'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'"
hex: '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
}
const url = `${SERVER}/bch/broadcast`
@@ -29,7 +29,8 @@ describe('#IPFS', () => {
afterEach(() => {
sandbox.restore()
clearInterval(uut.pollServiceInterval)
clearInterval(uut.pollBchServiceInterval)
clearInterval(uut.pollP2wdbServiceInterval)
})
describe('#constructor', () => {
@@ -125,7 +126,7 @@ describe('#IPFS', () => {
})
})
describe('#pollForServices', () => {
describe('#pollForBchServices', () => {
it('should find and select the wallet service', () => {
uut.ipfsCoord = {
thisNode: {
@@ -134,7 +135,7 @@ describe('#IPFS', () => {
}
}
uut.pollForServices()
uut.pollForBchServices()
// It should fine the service in the mocked data.
assert.equal(
@@ -144,7 +145,7 @@ describe('#IPFS', () => {
})
it('should catch and report errors', () => {
uut.pollForServices()
uut.pollForBchServices()
assert.isOk(true, 'Not throwing an error is a success.')
})
+21
View File
@@ -60,4 +60,25 @@ describe('#IPFS-adapter', () => {
assert.equal(result, true)
})
})
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')
}
})
})
})
+7 -1
View File
@@ -12,9 +12,15 @@ const ipfs = {
peer: {
sendPrivateMessage: () => {}
}
},
thisNode: {
peerData: []
}
},
peerInputHandler: () => {}
peerInputHandler: () => {},
state: {
serviceProviders: []
}
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ const peerData = [
'@context': 'https://schema.org/',
'@type': 'WebAPI',
name: 'trout-bch-wallet-service-dev',
version: '1.11.1',
version: '1.11.12',
protocol: 'bch-wallet',
description:
'IPFS service providing BCH blockchain access needed by a wallet.',