mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer.git
synced 2026-09-22 09:12:03 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d59415999f | ||
|
|
80d6a879d2 | ||
|
|
1b3681eb41 | ||
|
|
6d1d0c5823 | ||
|
|
9530a17835 | ||
|
|
d0de2569e9 | ||
|
|
3fc384909e | ||
|
|
9a8d0a6ec1 | ||
|
|
9e14e24eb4 | ||
|
|
1eb68c9ea5 | ||
|
|
c26ad11290 | ||
|
|
00a7b78fde | ||
|
|
9d80341357 | ||
|
|
00b76226ec | ||
|
|
f0552bce32 | ||
|
|
4aa7de253d | ||
|
|
abd01e647a | ||
|
|
8237fec8b7 | ||
|
|
0d06b2eaaa | ||
|
|
82ea90c45c | ||
|
|
fbe7bf9742 | ||
|
|
4447c2224e | ||
|
|
db72e0c95e | ||
|
|
9c9c3e7770 | ||
|
|
2ef0130559 | ||
|
|
610ca4ec8b | ||
|
|
1a32b941df | ||
|
|
433931111c | ||
|
|
3edc945487 | ||
|
|
c66194ceeb | ||
|
|
e35e130816 | ||
|
|
bba4bd5931 | ||
|
|
48d13ed6bc | ||
|
|
d6f9b1b754 | ||
|
|
25edd5fe71 | ||
|
|
cc84636e54 | ||
|
|
d73211191c | ||
|
|
b23acd3cdc | ||
|
|
c71fda38ff | ||
|
|
f99f49d840 | ||
|
|
2edd924c57 | ||
|
|
689c8fd8d9 | ||
|
|
ffd6c452c9 | ||
|
|
fc8e160874 | ||
|
|
d2f83307e6 | ||
|
|
1a09fcd26f | ||
|
|
e96129d7b0 | ||
|
|
4d852bb2ee | ||
|
|
cbefd5e8af | ||
|
|
bb5ea4a93d | ||
|
|
6e7396ba07 | ||
|
|
e2c5821dbe | ||
|
|
9967d5145b | ||
|
|
9d16291855 | ||
|
|
1518d1a12b | ||
|
|
b68a143071 | ||
|
|
5c25ebeeca | ||
|
|
3b17ba8e8b | ||
|
|
434a4d7bd6 | ||
|
|
dd36ebc7a0 | ||
|
|
f117fe15df | ||
|
|
3a45c96467 | ||
|
|
9ca2c1b9db | ||
|
|
7b9bde456e | ||
|
|
ed0d468c2d | ||
|
|
fe1b428dac | ||
|
|
0333f77f5a | ||
|
|
3c908d1a4d | ||
|
|
eb46a4c130 | ||
|
|
d0efdcfa07 | ||
|
|
62a7fcb16d | ||
|
|
d8addd5b27 | ||
|
|
bf6f1cf62d | ||
|
|
b7df7c47a5 | ||
|
|
ae260db4af | ||
|
|
778098ca12 | ||
|
|
0c4bb9c4a1 | ||
|
|
9cb975656d | ||
|
|
4f6564423e | ||
|
|
54e7e74ae5 | ||
|
|
324c372119 | ||
|
|
da62f1bbe3 | ||
|
|
129047fe88 | ||
|
|
3f652688b0 | ||
|
|
3ccbfdb614 | ||
|
|
5b4d00b949 | ||
|
|
a666dbcafd | ||
|
|
3d6d544c21 | ||
|
|
2ca54865bc | ||
|
|
df778d6d8e | ||
|
|
3c64f44dfd | ||
|
|
45ea47d4d0 | ||
|
|
150e3d6799 | ||
|
|
b3011bcb69 | ||
|
|
ccbe9a6707 | ||
|
|
23ebdaa7f3 | ||
|
|
fb182ced58 | ||
|
|
bec7c11e46 | ||
|
|
fb10e10226 | ||
|
|
5b37e47747 | ||
|
|
a3cea782ba | ||
|
|
14ffdae194 | ||
|
|
5792871e0a | ||
|
|
c2ec08f03d | ||
|
|
e4489cfcac | ||
|
|
e8c08beeb3 | ||
|
|
4a64423885 | ||
|
|
7e3ce1564d | ||
|
|
8154dd96f9 | ||
|
|
e37223cc88 | ||
|
|
5002ef2b90 | ||
|
|
5d95109ad4 | ||
|
|
13b3150f62 | ||
|
|
b3a4a7c1ab | ||
|
|
d6b64fce59 | ||
|
|
8c12222d9c | ||
|
|
26a79d1880 | ||
|
|
3f96b608b5 |
+1
-1
@@ -1,5 +1,5 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2021 Permissionless Software Foundation
|
||||
Copyright (c) 2022 Permissionless Software Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ This is a REST API server based on [Koa](https://koajs.com/). It's essentially a
|
||||
|
||||
When started, this web server starts an [IPFS](https://ipfs.io) node and connects to an ipfs-bch-wallet-service server over the IPFS network. It then pipes that connection over its own localized REST API.
|
||||
|
||||
More details are available at [CashStack.info](https://CashStack.info).
|
||||
|
||||
- [Video: Using ipfs-bch-wallet-consumer](https://youtu.be/7ntMPuqAX64)
|
||||
|
||||
## Requirements
|
||||
|
||||
- node **^14.18.2**
|
||||
@@ -17,20 +21,18 @@ When started, this web server starts an [IPFS](https://ipfs.io) node and connect
|
||||
|
||||
### Development Environment
|
||||
|
||||
**Note:** This software now uses an external go-ipfs IPFS node. The instructions below have not been updated to reflect this.
|
||||
To run this software, you must have an instance of go-ipfs running in daemon mode, with the `--enable-pubsub-experiment` flag set. [Here are instruction for installing go-ipfs](https://gist.github.com/christroutner/a39f656850dc022b60f25c9663dd1cdd#install-ipfs).
|
||||
|
||||
A development environment will allow you modify the code on-the-fly and contribute to the code base of this repository. [PM2](https://www.npmjs.com/package/pm2) is recommended for running this code base as an IPFS Circuit Relay.
|
||||
|
||||
- [Video: Installing ipfs-service-provider](https://youtu.be/Z0NsboIVN44)
|
||||
- [Step-by-step installation instructions](https://gist.github.com/christroutner/3304a71d4c12a3a3e1664a438f64d9d0)
|
||||
Clone this repository and install npm dependencies. You'll also need to install MongoDB if you don't already have it installed:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Permissionless-Software-Foundation/ipfs-service-provider
|
||||
cd ipfs-service-provider
|
||||
git clone https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer
|
||||
cd ipfs-bch-wallet-consumer
|
||||
./install-mongo-sh
|
||||
sudo npm install -g node-pre-gyp
|
||||
npm install
|
||||
./ipfs-service-provider.sh
|
||||
cd shell-script
|
||||
./local-external-ipfs-node.sh
|
||||
```
|
||||
|
||||
### Production Environment
|
||||
@@ -38,27 +40,15 @@ npm install
|
||||
The [docker](./production/docker) directory contains a Dockerfile for building a production deployment.
|
||||
|
||||
```
|
||||
docker-compose build --no-cache
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
- Bring the containers up once, then take them back down with `docker-compose down`.
|
||||
- Copy the [swarm.key](./swarm.key) file to `ipfs-service-provider/production/data/go-ipfs/data/swarm.key`.
|
||||
- Bring the containers back up with `docker-compose up -d`.
|
||||
You can bring the containers back up with `docker-compose up -d`.
|
||||
|
||||
### Operation Notes
|
||||
## Code Structure
|
||||
|
||||
- There is a memory leak in the version of js-ipfs. The app is currently configured to shut down every 8 hours to flush memory. It relies on a process manager like pm2, Docker, or systemd to restart the app after it shuts down, in order to ensure continuous operation.
|
||||
|
||||
- The PSF network operates as a private network. It does not connect or interact with the wider PSF network, relying instead on gateways to bridge the two networks, when they need to share content. This improves the performance and experience for everyone in the PSF network. To join the network, you'll need to add the [swarm.key](./swarm.key) file to the IPFS data folder.
|
||||
|
||||
- [Instructions on setting up IPFS private networks.](https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#private-networks)
|
||||
- For external installations, the swarm.key file will typically go in `~/.ipfs/swarm.key`
|
||||
- For production Docker containers, the key would go in `ipfs-service-provider/production/data/go-ipfs/data/swarm.key`
|
||||
|
||||
## Structure
|
||||
|
||||
The file layout of this repository differs from the koa-api-boilerplate. Instead, it follows the file layout of [Clean Architecture](troutsblog.com/blog/clean-architecture).
|
||||
The file layout of this repository differs from the koa-api-boilerplate. Instead, it follows the file layout of [Clean Architecture](https://christroutner.github.io/trouts-blog/blog/clean-architecture).
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -74,21 +64,11 @@ API documentation is written inline and generated by [apidoc](http://apidocjs.co
|
||||
|
||||
Visit `http://localhost:5000/docs/` to view docs
|
||||
|
||||
There is additional developer documentation in the [dev-docs directory](./dev-docs).
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [koa2](https://github.com/koajs/koa/tree/v2.x)
|
||||
- [koa-router](https://github.com/alexmingoia/koa-router)
|
||||
- [koa-bodyparser](https://github.com/koajs/bodyparser)
|
||||
- [koa-generic-session](https://github.com/koajs/generic-session)
|
||||
- [koa-logger](https://github.com/koajs/logger)
|
||||
- [MongoDB](http://mongodb.org/)
|
||||
- [Mongoose](http://mongoosejs.com/)
|
||||
- [Passport](http://passportjs.org/)
|
||||
- [Nodemon](http://nodemon.io/)
|
||||
- [Mocha](https://mochajs.org/)
|
||||
- [apidoc](http://apidocjs.com/)
|
||||
- [ESLint](http://eslint.org/)
|
||||
- [ipfs-coord](https://www.npmjs.com/package/ipfs-coord)
|
||||
This repository is forked from [ipfs-service-provider](https://github.com/Permissionless-Software-Foundation/ipfs-service-provider).
|
||||
|
||||
## IPFS
|
||||
|
||||
@@ -97,5 +77,3 @@ Snapshots pinned to IPFS will be listed here.
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE.md)
|
||||
|
||||
test
|
||||
|
||||
+40
-24
@@ -24,31 +24,37 @@ const config = require('../config') // this first.
|
||||
const AdminLib = require('../src/adapters/admin')
|
||||
const errorMiddleware = require('../src/controllers/rest-api/middleware/error')
|
||||
const { wlogger } = require('../src/adapters/wlogger')
|
||||
const Controllers = require('../src/controllers')
|
||||
|
||||
class Server {
|
||||
constructor () {
|
||||
// Encapsulate dependencies
|
||||
this.adminLib = new AdminLib()
|
||||
this.controllers = new Controllers()
|
||||
this.mongoose = mongoose
|
||||
this.config = config
|
||||
this.process = process
|
||||
}
|
||||
|
||||
async startServer () {
|
||||
try {
|
||||
// Create a Koa instance.
|
||||
const app = new Koa()
|
||||
app.keys = [config.session]
|
||||
app.keys = [this.config.session]
|
||||
|
||||
// Connect to the Mongo Database.
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
this.mongoose.Promise = global.Promise
|
||||
this.mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
console.log(
|
||||
`Connecting to MongoDB with this connection string: ${config.database}`
|
||||
`Connecting to MongoDB with this connection string: ${this.config.database}`
|
||||
)
|
||||
await mongoose.connect(config.database, {
|
||||
await this.mongoose.connect(this.config.database, {
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
})
|
||||
|
||||
console.log(`Starting environment: ${config.env}`)
|
||||
console.log(`Debug level: ${config.debugLevel}`)
|
||||
console.log(`Starting environment: ${this.config.env}`)
|
||||
console.log(`Debug level: ${this.config.debugLevel}`)
|
||||
|
||||
// MIDDLEWARE START
|
||||
|
||||
@@ -73,20 +79,24 @@ class Server {
|
||||
// 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()
|
||||
await controllers.attachRESTControllers(app)
|
||||
// Wait for any adapters to initialize.
|
||||
await this.controllers.initAdapters()
|
||||
|
||||
app.controllers = controllers
|
||||
// Wait for any use-libraries to initialize.
|
||||
await this.controllers.initUseCases()
|
||||
|
||||
// Attach REST API and JSON RPC controllers to the app.
|
||||
await this.controllers.attachRESTControllers(app)
|
||||
|
||||
app.controllers = this.controllers
|
||||
|
||||
// MIDDLEWARE END
|
||||
|
||||
console.log(`Running server in environment: ${config.env}`)
|
||||
wlogger.info(`Running server in environment: ${config.env}`)
|
||||
console.log(`Running server in environment: ${this.config.env}`)
|
||||
wlogger.info(`Running server in environment: ${this.config.env}`)
|
||||
|
||||
await app.listen(config.port)
|
||||
console.log(`Server started on ${config.port}`)
|
||||
this.server = await app.listen(this.config.port)
|
||||
console.log(`Server started on ${this.config.port}`)
|
||||
|
||||
// Create the system admin user.
|
||||
const success = await this.adminLib.createSystemUser()
|
||||
@@ -94,10 +104,16 @@ class Server {
|
||||
|
||||
// Attach the other IPFS controllers.
|
||||
// Skip if this is a test environment.
|
||||
if (config.env !== 'test') {
|
||||
await controllers.attachControllers(app)
|
||||
if (this.config.env !== 'test') {
|
||||
await this.controllers.attachControllers(app)
|
||||
}
|
||||
|
||||
// Display configuration settings
|
||||
console.log('\nConfiguration:')
|
||||
console.log(`Circuit Relay: ${this.config.isCircuitRelay}`)
|
||||
console.log(`IPFS TCP port: ${this.config.ipfsTcpPort}`)
|
||||
console.log(`IPFS WS port: ${this.config.ipfsWsPort}\n`)
|
||||
|
||||
return app
|
||||
} catch (err) {
|
||||
console.error('Could not start server. Error: ', err)
|
||||
@@ -105,14 +121,14 @@ class Server {
|
||||
console.log(
|
||||
'Exiting after 5 seconds. Depending on process manager to restart.'
|
||||
)
|
||||
await sleep(5000)
|
||||
process.exit(1)
|
||||
await this.sleep(5000)
|
||||
this.process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
sleep (ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
}
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
module.exports = Server
|
||||
|
||||
Vendored
+6
-2
@@ -84,6 +84,8 @@ module.exports = {
|
||||
preferredProvider: process.env.PREFERRED_PROVIDER
|
||||
? process.env.PREFERRED_PROVIDER
|
||||
: '',
|
||||
// If true, users will not be able to change the provider via a REST API call.
|
||||
freezeProvider: process.env.FREEZE_PROVIDER ? true : false,
|
||||
// : 'QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY'
|
||||
// : 'QmUTx6KqYKVZbKpKxR7vGDUgZFYVvVVyEWDeCYq4GwBCff'
|
||||
// Used for development
|
||||
@@ -94,9 +96,11 @@ module.exports = {
|
||||
: '',
|
||||
|
||||
// Settings for production, using external go-ipfs node.
|
||||
isProduction: process.env.SVC_ENV === 'production' ? true : false,
|
||||
isProduction: process.env.CONSUMER_ENV === 'production' ? true : false,
|
||||
ipfsHost: process.env.IPFS_HOST ? process.env.IPFS_HOST : 'localhost',
|
||||
ipfsApiPort: process.env.IPFS_API_PORT
|
||||
? parseInt(process.env.IPFS_API_PORT)
|
||||
: 5001
|
||||
: 5001,
|
||||
|
||||
chatPubSubChan: 'psf-ipfs-chat-001'
|
||||
}
|
||||
|
||||
@@ -7,3 +7,5 @@ This directory contains documentation for developers.
|
||||
This app provides a bridge between the Web 2.0 REST API world and the Web3.0 IPFS world. Web apps (like wallet.fullstack.cash) can interact with the REST API provided by the this web server app. This app then pulls the back-end infrastructure through a the IPFS network.
|
||||
|
||||
When started, this app will spin up an IPFS node and connect to the PSF network using [ipfs-coord](https://www.npmjs.com/package/ipfs-coord). It will find and connect with a BCH wallet service provider via IPFS. Clients connecting to the REST API can then access BCH wallet infrastructure via the REST API.
|
||||
|
||||
More details at [CashStack.info](https://cashstack.info).
|
||||
|
||||
Generated
+7656
-9396
File diff suppressed because it is too large
Load Diff
+11
-11
@@ -23,12 +23,12 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-consumer",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "5.3.2",
|
||||
"axios": "0.25.0",
|
||||
"@psf/bch-js": "6.7.3",
|
||||
"axios": "0.27.2",
|
||||
"bcryptjs": "2.4.3",
|
||||
"glob": "7.1.6",
|
||||
"ipfs": "0.60.0",
|
||||
"ipfs-coord": "7.1.6",
|
||||
"ipfs": "0.62.3",
|
||||
"ipfs-coord": "8.0.8",
|
||||
"ipfs-http-client": "55.0.0",
|
||||
"jsonrpc-lite": "2.2.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
@@ -45,17 +45,17 @@
|
||||
"koa-static": "5.0.0",
|
||||
"koa2-ratelimit": "0.9.1",
|
||||
"line-reader": "0.4.0",
|
||||
"mongoose": "5.11.15",
|
||||
"mongoose": "5.13.14",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.4.17",
|
||||
"nodemailer": "6.7.5",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "4.0.4",
|
||||
"semver": "^7.3.5",
|
||||
"semver": "7.3.5",
|
||||
"winston": "3.3.3",
|
||||
"winston-daily-rotate-file": "4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apidoc": "0.26.0",
|
||||
"apidoc": "0.51.1",
|
||||
"chai": "4.3.0",
|
||||
"coveralls": "3.1.0",
|
||||
"eslint": "7.19.0",
|
||||
@@ -65,10 +65,10 @@
|
||||
"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",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"mocha": "10.0.0",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "17.4.4",
|
||||
"semantic-release": "19.0.3",
|
||||
"sinon": "9.2.4",
|
||||
"standard": "16.0.3",
|
||||
"uuid": "8.3.2"
|
||||
|
||||
@@ -12,7 +12,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
|
||||
|
||||
@@ -46,11 +46,11 @@ RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/ipfs-service-provider
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer
|
||||
|
||||
# Switch to the desired branch. `master` is usually stable,
|
||||
# and `stage` has the most up-to-date changes.
|
||||
WORKDIR /home/safeuser/ipfs-service-provider
|
||||
WORKDIR /home/safeuser/ipfs-bch-wallet-consumer
|
||||
|
||||
# For development: switch to unstable branch
|
||||
RUN git checkout ct-unstable
|
||||
@@ -69,7 +69,7 @@ VOLUME /home/safeuser/keys
|
||||
EXPOSE 5010
|
||||
|
||||
# Start the application.
|
||||
COPY start-production.sh start-production.sh
|
||||
#COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
#CMD ["npm", "start"]
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
mongo-ipfs-service:
|
||||
image: mongo
|
||||
container_name: mongo-ipfs-service
|
||||
mongo-bch-consumer:
|
||||
image: mongo:4.2.0
|
||||
container_name: mongo-bch-consumer
|
||||
ports:
|
||||
- '5555:27017' # <host port>:<container port>
|
||||
volumes:
|
||||
@@ -13,9 +13,9 @@ services:
|
||||
command: mongod --logpath=/dev/null # -- quiet
|
||||
restart: always
|
||||
|
||||
ipfs:
|
||||
image: ipfs/go-ipfs:v0.11.0
|
||||
container_name: ipfs
|
||||
ipfs-bch-consumer:
|
||||
image: ipfs/go-ipfs:v0.14.0
|
||||
container_name: ipfs-bch-consumer
|
||||
environment:
|
||||
MY_ENV_VAR: 'placeholder'
|
||||
logging:
|
||||
@@ -26,11 +26,12 @@ services:
|
||||
mem_limit: 2000mb
|
||||
ports:
|
||||
- 4001:4001
|
||||
- 4003:4003
|
||||
- 172.17.0.1:5001:5001
|
||||
- 172.17.0.1:8080:8080
|
||||
volumes:
|
||||
- ../data/go-ipfs/data:/data/ipfs
|
||||
- ../data/go-ipfs/export:/export
|
||||
# - ../../swarm.key:/data/ipfs/swarm.key
|
||||
# https://docs.docker.com/compose/compose-file/compose-file-v3/#command
|
||||
# https://github.com/ipfs/go-ipfs/blob/91c52657166bcf86f2476926e4fe56694dc26562/Dockerfile#L115
|
||||
command:
|
||||
@@ -42,9 +43,10 @@ services:
|
||||
]
|
||||
restart: always
|
||||
|
||||
ipfs-service:
|
||||
build: .
|
||||
container_name: ipfs-service
|
||||
bch-consumer:
|
||||
#build: .
|
||||
image: christroutner/ipfs-bch-wallet-consumer:v1.8.4
|
||||
container_name: bch-consumer
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
@@ -52,10 +54,11 @@ services:
|
||||
max-file: '10'
|
||||
mem_limit: 500mb
|
||||
links:
|
||||
- mongo-ipfs-service
|
||||
- ipfs
|
||||
- mongo-bch-consumer
|
||||
- ipfs-bch-consumer
|
||||
ports:
|
||||
- '5010:5010' # <host port>:<container port>
|
||||
volumes:
|
||||
- ../data/ipfsdata:/home/safeuser/ipfs-service-provider/.ipfsdata
|
||||
- ../data/ipfsdata:/home/safeuser/ipfs-bch-wallet-consumer/.ipfsdata
|
||||
- ./start-production.sh:/home/safeuser/ipfs-bch-wallet-consumer/start-production.sh
|
||||
restart: always
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
export MNEMONIC="olive two muscle bottom coral ancient wait legend bronze useful process session"
|
||||
|
||||
# The human readable name this IPFS node identifies as.
|
||||
export COORD_NAME=ipfs-service-provider-generic
|
||||
export COORD_NAME=bch-consumer-generic
|
||||
|
||||
# Allow this node to function as a circuit relay. It must not be behind a firewall.
|
||||
#export ENABLE_CIRCUIT_RELAY=true
|
||||
@@ -17,22 +17,22 @@ export COORD_NAME=ipfs-service-provider-generic
|
||||
#export CR_DOMAIN=subdomain.yourdomain.com
|
||||
|
||||
# Debug level. 0 = minimal info. 2 = max info.
|
||||
export DEBUG_LEVEL=2
|
||||
export DEBUG_LEVEL=1
|
||||
|
||||
# END: Optional configuration settings
|
||||
|
||||
|
||||
# Production database connection string.
|
||||
export DBURL=mongodb://172.17.0.1:5555/ipfs-service-prod
|
||||
export DBURL=mongodb://172.17.0.1:5555/ipfs-bch-consumer-prod
|
||||
|
||||
# Configure REST API port
|
||||
export PORT=5010
|
||||
|
||||
# Production settings using external go-ipfs node.
|
||||
export SVC_ENV=production
|
||||
export CONSUMER_ENV=production
|
||||
export IPFS_HOST=172.17.0.1
|
||||
export IPFS_API_PORT=5001
|
||||
export IPFS_TCP_PORT=4001
|
||||
#export IPFS_WS_PORT=5269
|
||||
export IPFS_WS_PORT=4003
|
||||
|
||||
npm start
|
||||
|
||||
@@ -13,7 +13,7 @@ export COORD_NAME=trout-dev-bch-wallet-consumer
|
||||
export MNEMONIC="stomach mean catch march census author cousin size south romance pumpkin trap"
|
||||
|
||||
# 0 = less verbose. 3 = most verbose
|
||||
export DEBUG_LEVEL=2
|
||||
export DEBUG_LEVEL=1
|
||||
|
||||
# Production settings that use external IPFS node.
|
||||
# https://github.com/christroutner/docker-ipfs
|
||||
@@ -28,4 +28,9 @@ export IPFS_TCP_PORT=4001
|
||||
# MongoDB connection string.
|
||||
export DBURL=mongodb://localhost:27017/ipfs-service-dev
|
||||
|
||||
export CONSUMER_ENV=production
|
||||
|
||||
# Lock the consumer into a preferred service provider.
|
||||
#export PREFERRED_PROVIDER=12D3KooWEzE3HNH86WCb2Xocu6PgBCnSGUD7myJmuTHvoedMwp7K
|
||||
|
||||
npm start
|
||||
|
||||
+190
-7
@@ -21,15 +21,15 @@ class BchAdapter {
|
||||
'An instance of IPFS must be passed when instantiating the BCH Adapter library.'
|
||||
)
|
||||
}
|
||||
this.eventEmitter = localConfig.eventEmitter
|
||||
if (!this.eventEmitter) {
|
||||
throw new Error(
|
||||
'An instance of an EventEmitter must be passed when instantiating the adapters.'
|
||||
)
|
||||
}
|
||||
// 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)
|
||||
// this.eventEmitter.on('rpcData', this.rpcHandler)
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.uid = uid
|
||||
@@ -208,6 +208,60 @@ class BchAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
// Query the UTXOs associated with an array of up to 20 addresses.
|
||||
async getUtxosBulk (addrs) {
|
||||
try {
|
||||
// console.log('getUtxosBulk() addrs: ', addrs)
|
||||
|
||||
// Throw an error if this IPFS node has not yet made a connection to a
|
||||
// wallet service provider.
|
||||
const selectedProvider =
|
||||
this.ipfs.ipfsCoordAdapter.state.selectedServiceProvider
|
||||
if (!selectedProvider) {
|
||||
throw new Error('No BCH Wallet Service provider available yet.')
|
||||
}
|
||||
|
||||
// Input validation
|
||||
if (!Array.isArray(addrs)) {
|
||||
throw new Error('addresses parameter must be an array')
|
||||
}
|
||||
if (addrs.length > 20) {
|
||||
throw new Error('addresses parameter must not exceed 20 elements')
|
||||
}
|
||||
|
||||
const rpcData = {
|
||||
endpoint: 'utxosBulk',
|
||||
addresses: addrs
|
||||
}
|
||||
|
||||
// Generate a UUID for the call.
|
||||
const rpcId = this.uid()
|
||||
|
||||
// Generate a JSON RPC command.
|
||||
const cmd = this.jsonrpc.request(rpcId, 'bch', 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)
|
||||
// console.log(`getUtxosBulk() data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
// console.log('createUser() error: ', err)
|
||||
wlogger.error('Error in use-cases/bch.js/getUtxosBulk()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async broadcast (hex) {
|
||||
try {
|
||||
// Throw an error if this IPFS node has not yet made a connection to a
|
||||
@@ -387,6 +441,135 @@ class BchAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
async utxoIsValid (utxo) {
|
||||
try {
|
||||
// Throw an error if this IPFS node has not yet made a connection to a
|
||||
// wallet service provider.
|
||||
const selectedProvider =
|
||||
this.ipfs.ipfsCoordAdapter.state.selectedServiceProvider
|
||||
if (!selectedProvider) {
|
||||
throw new Error('No BCH Wallet Service provider available yet.')
|
||||
}
|
||||
|
||||
const rpcData = {
|
||||
endpoint: 'utxoIsValid',
|
||||
utxo
|
||||
}
|
||||
|
||||
// Generate a UUID for the call.
|
||||
const rpcId = this.uid()
|
||||
|
||||
// Generate a JSON RPC command.
|
||||
const cmd = this.jsonrpc.request(rpcId, 'bch', 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.log('utxoIsValid() error: ', err)
|
||||
wlogger.error('Error in adapters/bch.js/utxoIsValid()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async getTokenData (tokenId, withTxHistory = false) {
|
||||
try {
|
||||
// Throw an error if this IPFS node has not yet made a connection to a
|
||||
// wallet service provider.
|
||||
const selectedProvider =
|
||||
this.ipfs.ipfsCoordAdapter.state.selectedServiceProvider
|
||||
if (!selectedProvider) {
|
||||
throw new Error('No BCH Wallet Service provider available yet.')
|
||||
}
|
||||
|
||||
const rpcData = {
|
||||
endpoint: 'getTokenData',
|
||||
tokenId,
|
||||
withTxHistory
|
||||
}
|
||||
|
||||
// Generate a UUID for the call.
|
||||
const rpcId = this.uid()
|
||||
|
||||
// Generate a JSON RPC command.
|
||||
const cmd = this.jsonrpc.request(rpcId, 'bch', 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.log('getTokenData() error: ', err)
|
||||
wlogger.error('Error in adapters/bch.js/getTokenData()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Get token icon and other media associated with the token.
|
||||
async getTokenData2 (tokenId, updateCache) {
|
||||
try {
|
||||
// Throw an error if this IPFS node has not yet made a connection to a
|
||||
// wallet service provider.
|
||||
const selectedProvider =
|
||||
this.ipfs.ipfsCoordAdapter.state.selectedServiceProvider
|
||||
if (!selectedProvider) {
|
||||
throw new Error('No BCH Wallet Service provider available yet.')
|
||||
}
|
||||
|
||||
const rpcData = {
|
||||
endpoint: 'getTokenData2',
|
||||
tokenId,
|
||||
updateCache
|
||||
}
|
||||
|
||||
// Generate a UUID for the call.
|
||||
const rpcId = this.uid()
|
||||
|
||||
// Generate a JSON RPC command.
|
||||
const cmd = this.jsonrpc.request(rpcId, 'bch', 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.log('getTokenData2() error: ', err)
|
||||
wlogger.error('Error in adapters/bch.js/getTokenData2()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a promise that resolves to data when the RPC response is recieved.
|
||||
async waitForRPCResponse (rpcId) {
|
||||
try {
|
||||
|
||||
+14
-7
@@ -24,12 +24,12 @@ const config = require('../../config')
|
||||
class Adapters {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency injection.
|
||||
this.eventEmitter = localConfig.eventEmitter
|
||||
if (!this.eventEmitter) {
|
||||
throw new Error(
|
||||
'An instance of an EventEmitter must be passed when instantiating the adapters.'
|
||||
)
|
||||
}
|
||||
// this.eventEmitter = localConfig.eventEmitter
|
||||
// if (!this.eventEmitter) {
|
||||
// throw new Error(
|
||||
// 'An instance of an EventEmitter must be passed when instantiating the adapters.'
|
||||
// )
|
||||
// }
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.ipfs = new IPFSAdapter(localConfig)
|
||||
@@ -59,7 +59,14 @@ class Adapters {
|
||||
}
|
||||
|
||||
// Start the IPFS node.
|
||||
await this.ipfs.start()
|
||||
// Do not start these adapters if this is an e2e test.
|
||||
if (this.config.env !== 'test') {
|
||||
await this.ipfs.start()
|
||||
}
|
||||
|
||||
console.log('Async Adapters have been started.')
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/index.js/start()')
|
||||
throw err
|
||||
|
||||
@@ -2,23 +2,26 @@
|
||||
top-level IPFS library that combines the individual IPFS-based libraries.
|
||||
*/
|
||||
|
||||
// Local libraries
|
||||
const IpfsAdapter = require('./ipfs')
|
||||
const IpfsCoordAdapter = require('./ipfs-coord')
|
||||
const config = require('../../../config')
|
||||
|
||||
class IPFS {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency injection.
|
||||
this.eventEmitter = localConfig.eventEmitter
|
||||
if (!this.eventEmitter) {
|
||||
throw new Error(
|
||||
'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
|
||||
)
|
||||
}
|
||||
// this.eventEmitter = localConfig.eventEmitter
|
||||
// if (!this.eventEmitter) {
|
||||
// throw new Error(
|
||||
// 'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
|
||||
// )
|
||||
// }
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.ipfsAdapter = new IpfsAdapter()
|
||||
this.IpfsCoordAdapter = IpfsCoordAdapter
|
||||
this.process = process
|
||||
this.config = config
|
||||
|
||||
this.ipfsCoordAdapter = {} // placeholder
|
||||
|
||||
@@ -40,11 +43,15 @@ class IPFS {
|
||||
// Start ipfs-coord
|
||||
this.ipfsCoordAdapter = new this.IpfsCoordAdapter({
|
||||
ipfs: this.ipfs,
|
||||
eventEmitter: this.eventEmitter
|
||||
tcpPort: this.config.ipfsTcpPort,
|
||||
wsPort: this.config.ipfsWsPort
|
||||
})
|
||||
await this.ipfsCoordAdapter.start()
|
||||
console.log('ipfs-coord is ready.')
|
||||
|
||||
// Subscribe to the chat pubsub channel
|
||||
await this.ipfsCoordAdapter.subscribeToChat()
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/ipfs/index.js/start()')
|
||||
|
||||
@@ -31,12 +31,12 @@ class IpfsCoordAdapter {
|
||||
'Instance of IPFS must be passed when instantiating ipfs-coord.'
|
||||
)
|
||||
}
|
||||
this.eventEmitter = localConfig.eventEmitter
|
||||
if (!this.eventEmitter) {
|
||||
throw new Error(
|
||||
'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
|
||||
)
|
||||
}
|
||||
// this.eventEmitter = localConfig.eventEmitter
|
||||
// if (!this.eventEmitter) {
|
||||
// throw new Error(
|
||||
// 'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
|
||||
// )
|
||||
// }
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.IpfsCoord = IpfsCoord
|
||||
@@ -214,6 +214,8 @@ class IpfsCoordAdapter {
|
||||
// providers. This method is called periodically by a timer-interval.
|
||||
pollForBchServices () {
|
||||
try {
|
||||
// console.log('pollForBchServices() polling for BCH service')
|
||||
|
||||
// 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)}`)
|
||||
@@ -255,6 +257,8 @@ class IpfsCoordAdapter {
|
||||
console.log(`---->BCH wallet service selected: ${thisPeer}`)
|
||||
}
|
||||
|
||||
// console.log('preferredProvider: ', _this.config.preferredProvider)
|
||||
|
||||
// If a preferred provider is set in the config file, then connect
|
||||
// to the preferred provider when it's discovered.
|
||||
if (
|
||||
@@ -264,6 +268,8 @@ class IpfsCoordAdapter {
|
||||
_this.state.selectedServiceProvider = thisPeer
|
||||
}
|
||||
|
||||
// console.log('selectedServiceProvider: ', _this.state.selectedServiceProvider)
|
||||
|
||||
// Check if the peer has already been added to the list of providers.
|
||||
const alreadyExists = _this.state.serviceProviders.filter(
|
||||
(x) => x === thisPeer
|
||||
@@ -291,15 +297,24 @@ class IpfsCoordAdapter {
|
||||
|
||||
// This method handles input coming in from other IPFS peers.
|
||||
// It passes the data on to the REST API library by emitting an event.
|
||||
peerInputHandler (data) {
|
||||
try {
|
||||
// console.log('peerInputHandler triggered with this data: ', data)
|
||||
// peerInputHandler (data) {
|
||||
// try {
|
||||
// // console.log('peerInputHandler triggered with this data: ', data)
|
||||
//
|
||||
// this.eventEmitter.emit('rpcData', data)
|
||||
// } catch (err) {
|
||||
// console.error('Error in ipfs-coord.js/peerInputHandler(): ', err)
|
||||
// // Do not throw error. This is a top-level function.
|
||||
// }
|
||||
// }
|
||||
|
||||
this.eventEmitter.emit('rpcData', data)
|
||||
} catch (err) {
|
||||
console.error('Error in ipfs-coord.js/peerInputHandler(): ', err)
|
||||
// Do not throw error. This is a top-level function.
|
||||
}
|
||||
// Subscribe to the chat pubsub channel
|
||||
async subscribeToChat () {
|
||||
await this.ipfsCoord.adapters.pubsub.subscribeToPubsubChannel(
|
||||
this.config.chatPubSubChan,
|
||||
console.log,
|
||||
this.ipfsCoord.thisNode
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-18
@@ -102,9 +102,9 @@ class IpfsAdapter {
|
||||
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()
|
||||
}
|
||||
// if (err.message.includes('No space left on device')) {
|
||||
// this.rmBlocksDir()
|
||||
// }
|
||||
|
||||
throw err
|
||||
}
|
||||
@@ -120,21 +120,21 @@ class IpfsAdapter {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
// 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
|
||||
|
||||
@@ -135,8 +135,10 @@ class LogsApi {
|
||||
if (!filename || typeof filename !== 'string') {
|
||||
throw new Error('filename must be a string')
|
||||
}
|
||||
// Throw an error if the file does not exist.
|
||||
|
||||
console.log('readLines() filename: ', filename)
|
||||
|
||||
// Throw an error if the file does not exist.
|
||||
if (!_this.fs.existsSync(filename)) {
|
||||
throw new Error('file does not exist')
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@ class P2wdbAdapter {
|
||||
'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.'
|
||||
)
|
||||
}
|
||||
// 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)
|
||||
// this.eventEmitter.on('rpcData', this.rpcHandler)
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.uid = uid
|
||||
|
||||
+21
-13
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const EventEmitter = require('events')
|
||||
// const EventEmitter = require('events')
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../config')
|
||||
@@ -20,8 +20,8 @@ class Controllers {
|
||||
this.config = config
|
||||
|
||||
// Initialize EventEmitters used to pass event-driven data around the app.
|
||||
this.eventEmitter = new EventEmitter()
|
||||
localConfig.eventEmitter = this.eventEmitter
|
||||
// this.eventEmitter = new EventEmitter()
|
||||
// localConfig.eventEmitter = this.eventEmitter
|
||||
|
||||
// Initialize Clean Architecture classes. Inject dependencies.
|
||||
this.adapters = new Adapters(localConfig)
|
||||
@@ -29,17 +29,14 @@ class Controllers {
|
||||
this.useCases = new UseCases(localConfig)
|
||||
}
|
||||
|
||||
async attachControllers (app) {
|
||||
// Skip in a test environment.
|
||||
if (this.config.env !== 'test') {
|
||||
// Wait for any startup processes to complete for the Adapters libraries.
|
||||
await this.adapters.start()
|
||||
// Spin up any adapter libraries that have async startup needs.
|
||||
async initAdapters () {
|
||||
await this.adapters.start()
|
||||
}
|
||||
|
||||
// Attach the REST controllers to the Koa app.
|
||||
// this.attachRESTControllers(app)
|
||||
|
||||
this.attachRPCControllers()
|
||||
}
|
||||
// Run any Use Cases to startup the app.
|
||||
async initUseCases () {
|
||||
await this.useCases.start()
|
||||
}
|
||||
|
||||
// Top-level function for this library.
|
||||
@@ -54,6 +51,17 @@ class Controllers {
|
||||
restControllers.attachRESTControllers(app)
|
||||
}
|
||||
|
||||
// Attach any other controllers other than REST API controllers.
|
||||
async attachControllers (app) {
|
||||
// Wait for any startup processes to complete for the Adapters libraries.
|
||||
// await this.adapters.start()
|
||||
|
||||
// Attach the REST controllers to the Koa app.
|
||||
// this.attachRESTControllers(app)
|
||||
|
||||
this.attachRPCControllers()
|
||||
}
|
||||
|
||||
// Add the JSON RPC router to the ipfs-coord adapter.
|
||||
attachRPCControllers () {
|
||||
const jsonRpcController = new JSONRPC({
|
||||
|
||||
@@ -90,13 +90,14 @@ class JSONRPC {
|
||||
}
|
||||
|
||||
// Process response from REST API.
|
||||
_this.adapters.ipfs.ipfsCoordAdapter.peerInputHandler(parsedData)
|
||||
// _this.adapters.ipfs.ipfsCoordAdapter.peerInputHandler(parsedData)
|
||||
|
||||
// Log the incoming JSON RPC command.
|
||||
wlogger.info(
|
||||
`JSON RPC received from ${from}, ID: ${parsedData.payload.id}, type: ${parsedData.type}`
|
||||
)
|
||||
}
|
||||
// console.log(`parsedData: ${JSON.stringify(parsedData, null, 2)}`)
|
||||
|
||||
// Added the property "from" to the parsedData object;
|
||||
// necessary for calculating rate limits (based on the IPFS ID).
|
||||
@@ -105,6 +106,16 @@ class JSONRPC {
|
||||
// Default return string
|
||||
let retObj = _this.defaultResponse()
|
||||
|
||||
// Forward data on to bch adapter if this is the response of a BCH query.
|
||||
try {
|
||||
if (parsedData.payload.result.method === 'bch') {
|
||||
console.log('routing to BCH adapter')
|
||||
retObj = await _this.adapters.bch.rpcHandler(parsedData)
|
||||
}
|
||||
} catch (err) {
|
||||
/* exit quietly */
|
||||
}
|
||||
|
||||
// Route the command to the appropriate route handler.
|
||||
switch (parsedData.payload.method) {
|
||||
case 'users':
|
||||
@@ -116,6 +127,8 @@ class JSONRPC {
|
||||
case 'about':
|
||||
retObj = await _this.aboutController.aboutRouter(parsedData)
|
||||
break
|
||||
case 'bch':
|
||||
retObj = await _this.adapters.bch.rpcHandler(parsedData)
|
||||
// default:
|
||||
// // Process response from REST API.
|
||||
// _this.adapters.ipfs.ipfsCoordAdapter.peerInputHandler(parsedData)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
const { wlogger } = require('../../../adapters/wlogger')
|
||||
const config = require('../../../../config')
|
||||
|
||||
// let _this
|
||||
|
||||
@@ -23,10 +24,7 @@ class BchRESTControllerLib {
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
// this.UserModel = this.adapters.localdb.Users
|
||||
// this.userUseCases = this.useCases.user
|
||||
|
||||
// _this = this
|
||||
this.config = config
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +64,11 @@ class BchRESTControllerLib {
|
||||
try {
|
||||
const providerId = ctx.request.body.provider
|
||||
|
||||
// Throw an error, if the provider has been set by an environment variable.
|
||||
if (this.config.freezeProvider) {
|
||||
throw new Error('Consumer has preferredProvider set in environment variable. Refusing to switch providers.')
|
||||
}
|
||||
|
||||
await this.adapters.bch.selectProvider(providerId)
|
||||
|
||||
const body = {
|
||||
@@ -231,6 +234,103 @@ class BchRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /bch/utxosBulk UTXOs Bulk
|
||||
* @apiName UTXOs Bulk
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription This endpoint returns UTXOs held at an address, hydrated
|
||||
* with token information.
|
||||
* @apiDescription This endpoint is the same as the /bch/utxos endpoint, but
|
||||
* it allows an array of up to 20 addresses to be queried at the same time.
|
||||
* This reduces the number of JSON RPC calls, and is very handy
|
||||
* for HD wallets that need to quickly scan a lot of addresses.
|
||||
*
|
||||
* Given an address, this endpoint will return an object with thre following
|
||||
* properties:
|
||||
*
|
||||
* - addresses: [] - the addresses these UTXOs are associated with
|
||||
* - bchUtxos: [] - UTXOs confirmed to be spendable as normal BCH
|
||||
* - nullUtxo: [] - UTXOs that did not pass SLP validation. Should be ignored and
|
||||
* not spent, to be safe.
|
||||
* - slpUtxos: {} - UTXOs confirmed to be colored as valid SLP tokens
|
||||
* - type1: {}
|
||||
* - tokens: [] - SLP token Type 1 tokens.
|
||||
* - mintBatons: [] - SLP token Type 1 mint batons.
|
||||
* - nft: {}
|
||||
* - tokens: [] - NFT tokens
|
||||
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
|
||||
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "address": "bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj" }' localhost:5001/bch/utxos
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* [
|
||||
* {
|
||||
* "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"],
|
||||
* "bchUtxos":[
|
||||
* {
|
||||
* "height":631219,
|
||||
* "tx_hash":"ae2daa01c8172545b5edd205ea438706bcb74e63d4084a26b9ff2a46d46dc97f",
|
||||
* "tx_pos":0,
|
||||
* "value":1000,
|
||||
* "txid":"ae2daa01c8172545b5edd205ea438706bcb74e63d4084a26b9ff2a46d46dc97f",
|
||||
* "vout":0,
|
||||
* "isValid":false
|
||||
* }
|
||||
* ],
|
||||
* "nullUtxos":[
|
||||
*
|
||||
* ],
|
||||
* "slpUtxos":{
|
||||
* "type1":{
|
||||
* "mintBatons":[
|
||||
*
|
||||
* ],
|
||||
* "tokens":[
|
||||
*
|
||||
* ]
|
||||
* },
|
||||
* "nft":{
|
||||
* "groupMintBatons":[
|
||||
*
|
||||
* ],
|
||||
* "groupTokens":[
|
||||
*
|
||||
* ],
|
||||
* "tokens":[
|
||||
*
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* @apiError UnprocessableEntity Missing required parameters
|
||||
*
|
||||
* @apiErrorExample {json} Error-Response:
|
||||
* HTTP/1.1 422 Unprocessable Entity
|
||||
* {
|
||||
* "status": 422,
|
||||
* "error": "Unprocessable Entity"
|
||||
* }
|
||||
*/
|
||||
async utxosBulk (ctx) {
|
||||
try {
|
||||
const addrs = ctx.request.body.addresses
|
||||
// console.log('address: ', address)
|
||||
|
||||
const utxos = await this.adapters.bch.getUtxosBulk(addrs)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
ctx.body = utxos
|
||||
} catch (err) {
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /bch/broadcast Broadcast
|
||||
* @apiName Broadcast
|
||||
@@ -489,6 +589,8 @@ class BchRESTControllerLib {
|
||||
* "pubkey": {
|
||||
* "success": true,
|
||||
* "publicKey": "033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@@ -505,6 +607,122 @@ class BchRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {REST} /bch/utxoIsValid utxoIsValid
|
||||
* @apiPermission public
|
||||
* @apiName utxoIsValid
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription Verify if UTXO is valid
|
||||
* Given a UTXO object (txid and vout), a full node is queried to verify that
|
||||
* the UTXO still exists in the mempool (true), or if it has been spent (false).
|
||||
*
|
||||
* - jsonrpc: "" - jsonrpc version
|
||||
* - id: "" - jsonrpc id
|
||||
* - result: {} - Result of the petition with the RPC information
|
||||
* - success: - Request status
|
||||
* - isValid: - Boolean: true or false
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "utxoIsValid", "utxo": {"tx_hash": "17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a", "tx_pos": 0}}}
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* {
|
||||
* "jsonrpc":"2.0",
|
||||
* "id":"555",
|
||||
* "result":{
|
||||
* "method":"bch",
|
||||
* "reciever":"QmU86vLVbUY1UhziKB6rak7GPKRA2QHWvzNm2AjEvXNsT6",
|
||||
* "value":{
|
||||
* "success": true,
|
||||
* "status": 200,
|
||||
* "endpoint": "utxoIsValid",
|
||||
* "isValid": true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* }
|
||||
*/
|
||||
async utxoIsValid (ctx) {
|
||||
try {
|
||||
const utxo = ctx.request.body.utxo
|
||||
|
||||
const data = await this.adapters.bch.utxoIsValid(utxo)
|
||||
// console.log(`utxoIsValid data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
ctx.body = data
|
||||
} catch (err) {
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {REST} /bch getTokenData
|
||||
* @apiPermission public
|
||||
* @apiName getTokenData
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription Get data associated with a token
|
||||
*
|
||||
* Given a token ID, this endpoint will retrieve the IPFS CIDs associated with
|
||||
* the tokens mutable and immutable data. This is extension of the PS002
|
||||
* specification for mutable data for tokens:
|
||||
* https://github.com/Permissionless-Software-Foundation/specifications/blob/master/ps002-slp-mutable-data.md
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "tokenId": "43eddfb11c9941edffb8c8815574bb0a43969a7b1de39ad14cd043eaa24fd38d", "withTxHistory": true }' https://bc01-ca-bch-consumer.fullstackcash.nl/bch/getTokenData
|
||||
*/
|
||||
async getTokenData (ctx) {
|
||||
try {
|
||||
const tokenId = ctx.request.body.tokenId
|
||||
const withTxHistory = !!ctx.request.body.withTxHistory
|
||||
|
||||
const data = await this.adapters.bch.getTokenData(tokenId, withTxHistory)
|
||||
console.log(`getTokenData data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
ctx.body = data
|
||||
} catch (err) {
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {REST} /bch getTokenData2
|
||||
* @apiPermission public
|
||||
* @apiName getTokenData2
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription Get token icon and other media
|
||||
*
|
||||
* Get the icon for a token, given it's token ID.
|
||||
* This function expects a string input of a token ID property.
|
||||
* This function returns an object with a tokenIcon property that contains
|
||||
* the URL to the icon.
|
||||
*
|
||||
* The output object always have these properties:
|
||||
* - tokenIcon: A url to the token icon, if it exists.
|
||||
* - tokenStats: Data about the token from psf-slp-indexer.
|
||||
* - optimizedTokenIcon: An alternative, potentially more optimal, url to the token icon, if it exists.
|
||||
* - iconRepoCompatible: true if the token icon is available via token.bch.sx
|
||||
* - ps002Compatible: true if the token icon is compatible with PS007 specification.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "tokenId": "43eddfb11c9941edffb8c8815574bb0a43969a7b1de39ad14cd043eaa24fd38d" }' https://bc01-ca-bch-consumer.fullstackcash.nl/bch/getTokenData2
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "tokenId": "43eddfb11c9941edffb8c8815574bb0a43969a7b1de39ad14cd043eaa24fd38d", "updateCache": true }' https://bc01-ca-bch-consumer.fullstackcash.nl/bch/getTokenData2
|
||||
*/
|
||||
async getTokenData2 (ctx) {
|
||||
try {
|
||||
const tokenId = ctx.request.body.tokenId
|
||||
const updateCache = ctx.request.body.updateCache
|
||||
console.log('getTokenData2() updateCache: ', updateCache)
|
||||
|
||||
const data = await this.adapters.bch.getTokenData2(tokenId, updateCache)
|
||||
console.log(`getTokenData2 data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
ctx.body = data
|
||||
} catch (err) {
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
|
||||
@@ -55,10 +55,14 @@ class BchRouter {
|
||||
this.router.post('/provider', this.postProvider)
|
||||
this.router.post('/balance', this.postBalance)
|
||||
this.router.post('/utxos', this.postUtxos)
|
||||
this.router.post('/utxosBulk', this.postUtxosBulk)
|
||||
this.router.post('/broadcast', this.postBroadcast)
|
||||
this.router.post('/txHistory', this.postTxHistory)
|
||||
this.router.post('/txData', this.postTxData)
|
||||
this.router.post('/pubkey', this.postPubKey)
|
||||
this.router.post('/utxoIsValid', this.utxoIsValid)
|
||||
this.router.post('/getTokenData', this.getTokenData)
|
||||
this.router.post('/getTokenData2', this.getTokenData2)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(this.router.routes())
|
||||
@@ -81,6 +85,10 @@ class BchRouter {
|
||||
await _this.bchRESTController.utxos(ctx, next)
|
||||
}
|
||||
|
||||
async postUtxosBulk (ctx, next) {
|
||||
await _this.bchRESTController.utxosBulk(ctx, next)
|
||||
}
|
||||
|
||||
async postBroadcast (ctx, next) {
|
||||
await _this.bchRESTController.broadcast(ctx, next)
|
||||
}
|
||||
@@ -96,6 +104,18 @@ class BchRouter {
|
||||
async postPubKey (ctx, next) {
|
||||
await _this.bchRESTController.pubKey(ctx, next)
|
||||
}
|
||||
|
||||
async utxoIsValid (ctx, next) {
|
||||
await _this.bchRESTController.utxoIsValid(ctx, next)
|
||||
}
|
||||
|
||||
async getTokenData (ctx, next) {
|
||||
await _this.bchRESTController.getTokenData(ctx, next)
|
||||
}
|
||||
|
||||
async getTokenData2 (ctx, next) {
|
||||
await _this.bchRESTController.getTokenData2(ctx, next)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BchRouter
|
||||
|
||||
@@ -64,6 +64,31 @@ class PriceRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the XEC price.
|
||||
// TODO: Add API docs.
|
||||
async getXecPrice (ctx) {
|
||||
try {
|
||||
// Request options
|
||||
const opt = {
|
||||
method: 'get',
|
||||
baseURL: 'https://api.coinex.com',
|
||||
url: '/v1/market/ticker?market=xecusdt',
|
||||
timeout: 15000
|
||||
}
|
||||
//
|
||||
|
||||
const response = await this.axios.request(opt)
|
||||
console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
ctx.body = { usd: Number(response.data.data.ticker.last) }
|
||||
} catch (err) {
|
||||
// Write out error to error log.
|
||||
wlogger.error('Error in GET /price/xecusd.', err)
|
||||
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
|
||||
@@ -52,6 +52,7 @@ class PriceRouter {
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.get('/usd', this.getPrice)
|
||||
this.router.get('/xecusd', this.getXecPrice)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(this.router.routes())
|
||||
@@ -61,6 +62,10 @@ class PriceRouter {
|
||||
async getPrice (ctx, next) {
|
||||
await _this.priceRESTController.getUSD(ctx, next)
|
||||
}
|
||||
|
||||
async getXecPrice (ctx, next) {
|
||||
await _this.priceRESTController.getXecPrice(ctx, next)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PriceRouter
|
||||
|
||||
+19
-6
@@ -14,16 +14,29 @@ class UseCases {
|
||||
'Instance of adapters must be passed in when instantiating Use Cases library.'
|
||||
)
|
||||
}
|
||||
this.eventEmitter = localConfig.eventEmitter
|
||||
if (!this.eventEmitter) {
|
||||
throw new Error(
|
||||
'Instance of Event Emitter must be passed in when instantiating Use Cases library.'
|
||||
)
|
||||
}
|
||||
// this.eventEmitter = localConfig.eventEmitter
|
||||
// if (!this.eventEmitter) {
|
||||
// throw new Error(
|
||||
// 'Instance of Event Emitter must be passed in when instantiating Use Cases library.'
|
||||
// )
|
||||
// }
|
||||
|
||||
// console.log('use-cases/index.js localConfig: ', localConfig)
|
||||
this.user = new UserUseCases(localConfig)
|
||||
}
|
||||
|
||||
// Run any startup Use Cases at the start of the app.
|
||||
async start () {
|
||||
// try {
|
||||
console.log('Async Use Cases have been started.')
|
||||
|
||||
return true
|
||||
// } catch (err) {
|
||||
// console.error('Error in use-cases/index.js/start()')
|
||||
// // console.log(err)
|
||||
// throw err
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UseCases
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
/key/swarm/psk/1.0.0/
|
||||
/base16/
|
||||
bbd935b70105b03ebd0c6a3c2d2730cd22fc0d18c490ccf689b6c8a22e1bed2a
|
||||
@@ -7,6 +7,7 @@
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const axios = require('axios').default
|
||||
// const sinon = require('sinon')
|
||||
|
||||
// Local support libraries
|
||||
const config = require('../../../config')
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Unit tests for the adapters index.js library
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Local libraries
|
||||
const Adapters = require('../../../src/adapters')
|
||||
|
||||
describe('#adapters', () => {
|
||||
let uut, sandbox
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new Adapters()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('#start', () => {
|
||||
it('should start the async adapters', async () => {
|
||||
// Mock dependencies
|
||||
uut.config.getJwtAtStartup = true
|
||||
sandbox.stub(uut.fullStackJwt, 'getJWT').resolves()
|
||||
sandbox.stub(uut.fullStackJwt, 'instanceBchjs').resolves()
|
||||
sandbox.stub(uut.ipfs, 'start').resolves()
|
||||
|
||||
const result = await uut.start()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
uut.config.getJwtAtStartup = false
|
||||
uut.config.env = 'dev'
|
||||
sandbox.stub(uut.ipfs, 'start').rejects(new Error('test error'))
|
||||
|
||||
await uut.start()
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -54,19 +54,19 @@ describe('#bch-use-case', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if adapters are not passed in', () => {
|
||||
try {
|
||||
const ipfs = {}
|
||||
uut = new BchAdapter({ ipfs })
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'An instance of an EventEmitter must be passed when instantiating the adapters.'
|
||||
)
|
||||
}
|
||||
})
|
||||
// it('should throw an error if adapters are not passed in', () => {
|
||||
// try {
|
||||
// const ipfs = {}
|
||||
// uut = new BchAdapter({ ipfs })
|
||||
//
|
||||
// assert.fail('Unexpected code path')
|
||||
// } catch (err) {
|
||||
// assert.include(
|
||||
// err.message,
|
||||
// 'An instance of an EventEmitter must be passed when instantiating the adapters.'
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
})
|
||||
|
||||
describe('#waitForRPCResponse', () => {
|
||||
@@ -165,6 +165,82 @@ describe('#bch-use-case', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getUtxos', () => {
|
||||
it('should get the UTXOs of an address', async () => {
|
||||
// Force connection to a wallet service
|
||||
uut.ipfs.ipfsCoordAdapter.state = {
|
||||
selectedServiceProvider: 'abc123'
|
||||
}
|
||||
|
||||
// Mock depenencies
|
||||
sandbox.stub(uut, 'waitForRPCResponse').resolves({ key: 'value' })
|
||||
|
||||
const addr = 'addr'
|
||||
|
||||
const result = await uut.getUtxos(addr)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.key, 'value')
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
await uut.getUtxos()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getUtxosBulk', () => {
|
||||
it('should get the UTXOs of an address', async () => {
|
||||
// Force connection to a wallet service
|
||||
uut.ipfs.ipfsCoordAdapter.state = {
|
||||
selectedServiceProvider: 'abc123'
|
||||
}
|
||||
|
||||
// Mock depenencies
|
||||
sandbox.stub(uut, 'waitForRPCResponse').resolves({ key: 'value' })
|
||||
|
||||
const addrs = ['addr']
|
||||
|
||||
const result = await uut.getUtxosBulk(addrs)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.key, 'value')
|
||||
})
|
||||
|
||||
it('should throw an error if addresses is not an array', async () => {
|
||||
try {
|
||||
await uut.getUtxosBulk()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.message, 'addresses parameter must be an array')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if addresses array is larger than 20 elements', async () => {
|
||||
try {
|
||||
const addrs = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addrs.push(i)
|
||||
}
|
||||
|
||||
await uut.getUtxosBulk(addrs)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.message, 'addresses parameter must not exceed 20 elements')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#broadcast', () => {
|
||||
it('should broadcast a transaction', async () => {
|
||||
// Force connection to a wallet service
|
||||
@@ -284,4 +360,94 @@ describe('#bch-use-case', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#utxoIsValid', () => {
|
||||
it('should validate a utxo', async () => {
|
||||
// Force connection to a wallet service
|
||||
uut.ipfs.ipfsCoordAdapter.state = {
|
||||
selectedServiceProvider: 'abc123'
|
||||
}
|
||||
|
||||
// Mock depenencies
|
||||
sandbox.stub(uut, 'waitForRPCResponse').resolves({ key: 'value' })
|
||||
|
||||
const utxo = { a: 'b' }
|
||||
|
||||
const result = await uut.utxoIsValid(utxo)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.key, 'value')
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
await uut.utxoIsValid()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTokenData', () => {
|
||||
it('should validate a utxo', async () => {
|
||||
// Force connection to a wallet service
|
||||
uut.ipfs.ipfsCoordAdapter.state = {
|
||||
selectedServiceProvider: 'abc123'
|
||||
}
|
||||
|
||||
// Mock depenencies
|
||||
sandbox.stub(uut, 'waitForRPCResponse').resolves({ key: 'value' })
|
||||
|
||||
const tokenId = 'blah'
|
||||
|
||||
const result = await uut.getTokenData(tokenId)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.key, 'value')
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
await uut.getTokenData()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTokenData2', () => {
|
||||
it('should validate a utxo', async () => {
|
||||
// Force connection to a wallet service
|
||||
uut.ipfs.ipfsCoordAdapter.state = {
|
||||
selectedServiceProvider: 'abc123'
|
||||
}
|
||||
|
||||
// Mock depenencies
|
||||
sandbox.stub(uut, 'waitForRPCResponse').resolves({ key: 'value' })
|
||||
|
||||
const tokenId = 'blah'
|
||||
|
||||
const result = await uut.getTokenData2(tokenId)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.key, 'value')
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
await uut.getTokenData2()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ const cloneDeep = require('lodash.clonedeep')
|
||||
const IPFSCoordAdapter = require('../../../src/adapters/ipfs/ipfs-coord')
|
||||
const IPFSMock = require('../mocks/ipfs-mock')
|
||||
const IPFSCoordMock = require('../mocks/ipfs-coord-mock')
|
||||
const EventEmitter = require('events')
|
||||
const config = require('../../../config')
|
||||
const mockDataLib = require('../mocks/adapters/ipfs-coord-mocks')
|
||||
|
||||
describe('#IPFS', () => {
|
||||
@@ -19,7 +19,7 @@ describe('#IPFS', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
const ipfs = IPFSMock.create()
|
||||
uut = new IPFSCoordAdapter({ ipfs, eventEmitter: new EventEmitter() })
|
||||
uut = new IPFSCoordAdapter({ ipfs })
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
@@ -47,19 +47,19 @@ describe('#IPFS', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if EventEmitter instance is not included', () => {
|
||||
try {
|
||||
const ipfs = IPFSMock.create()
|
||||
uut = new IPFSCoordAdapter({ ipfs })
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
|
||||
)
|
||||
}
|
||||
})
|
||||
// it('should throw an error if EventEmitter instance is not included', () => {
|
||||
// try {
|
||||
// const ipfs = IPFSMock.create()
|
||||
// uut = new IPFSCoordAdapter({ ipfs })
|
||||
//
|
||||
// assert.fail('Unexpected code path')
|
||||
// } catch (err) {
|
||||
// assert.include(
|
||||
// err.message,
|
||||
// 'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
})
|
||||
|
||||
describe('#start', () => {
|
||||
@@ -86,6 +86,16 @@ describe('#IPFS', () => {
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
it('should return a promise that resolves into an instance of IPFS in production mode', async () => {
|
||||
uut.config.isProduction = true
|
||||
// Mock dependencies.
|
||||
uut.IpfsCoord = IPFSCoordMock
|
||||
|
||||
const result = await uut.start()
|
||||
// console.log('result: ', result)
|
||||
assert.equal(result, true)
|
||||
config.isProduction = false
|
||||
})
|
||||
})
|
||||
|
||||
describe('#attachRPCRouter', () => {
|
||||
@@ -152,22 +162,38 @@ describe('#IPFS', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#peerInputHandler', () => {
|
||||
it('should emit an event trigger', () => {
|
||||
const data = 'some data'
|
||||
// describe('#peerInputHandler', () => {
|
||||
// it('should emit an event trigger', () => {
|
||||
// const data = 'some data'
|
||||
//
|
||||
// uut.peerInputHandler(data)
|
||||
//
|
||||
// assert.isOk(true, 'Not throwing an error is a success')
|
||||
// })
|
||||
//
|
||||
// it('should catch and report errors', () => {
|
||||
// // Force an error
|
||||
// sandbox.stub(uut.eventEmitter, 'emit').throws(new Error('test error'))
|
||||
//
|
||||
// uut.peerInputHandler()
|
||||
//
|
||||
// assert.isOk(true, 'Not throwing an error is a success.')
|
||||
// })
|
||||
// })
|
||||
|
||||
uut.peerInputHandler(data)
|
||||
describe('#subscribeToChat', () => {
|
||||
it('should subscribe to the chat channel', async () => {
|
||||
// Mock dependencies
|
||||
uut.ipfsCoord = {
|
||||
adapters: {
|
||||
pubsub: {
|
||||
subscribeToPubsubChannel: async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.isOk(true, 'Not throwing an error is a success')
|
||||
})
|
||||
|
||||
it('should catch and report errors', () => {
|
||||
// Force an error
|
||||
sandbox.stub(uut.eventEmitter, 'emit').throws(new Error('test error'))
|
||||
|
||||
uut.peerInputHandler()
|
||||
|
||||
assert.isOk(true, 'Not throwing an error is a success.')
|
||||
await uut.subscribeToChat()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const IPFSLib = require('../../../src/adapters/ipfs/ipfs')
|
||||
const IPFSMock = require('../mocks/ipfs-mock')
|
||||
const config = require('../../../config')
|
||||
|
||||
// config.isProduction = true;
|
||||
describe('#IPFS-adapter', () => {
|
||||
let uut
|
||||
let sandbox
|
||||
@@ -18,7 +19,27 @@ describe('#IPFS-adapter', () => {
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('#constructor', () => {
|
||||
it('should instantiate IPFS Lib in dev mode.', async () => {
|
||||
const _uut = new IPFSLib()
|
||||
assert.exists(_uut)
|
||||
assert.isFunction(_uut.start)
|
||||
assert.isFunction(_uut.stop)
|
||||
})
|
||||
|
||||
it('should instantiate dev IPFS Lib in production mode.', async () => {
|
||||
config.isProduction = true
|
||||
const _uut = new IPFSLib()
|
||||
assert.exists(_uut)
|
||||
assert.isFunction(_uut.start)
|
||||
assert.isFunction(_uut.stop)
|
||||
config.isProduction = false
|
||||
})
|
||||
})
|
||||
|
||||
describe('#start', () => {
|
||||
it('should return a promise that resolves into an instance of IPFS.', async () => {
|
||||
@@ -33,6 +54,18 @@ describe('#IPFS-adapter', () => {
|
||||
assert.property(result, 'config')
|
||||
})
|
||||
|
||||
it('should return a promise that resolves into an instance of IPFS in production mode.', async () => {
|
||||
// Mock dependencies.
|
||||
uut.IPFS = IPFSMock
|
||||
uut.config.isProduction = true
|
||||
const result = await uut.start()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(uut.isReady, true)
|
||||
|
||||
assert.property(result, 'config')
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
|
||||
@@ -32,19 +32,18 @@ describe('#LogsApiLib', () => {
|
||||
})
|
||||
|
||||
it('should return log', async () => {
|
||||
try {
|
||||
const pass = 'test'
|
||||
const result = await uut.getLogs(pass)
|
||||
// console.log('result', result)
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'generateFileName').returns(`${__dirname.toString()}/../mocks/adapters/fake-log`)
|
||||
|
||||
assert.isTrue(result.success)
|
||||
assert.isArray(result.data)
|
||||
assert.property(result.data[0], 'message')
|
||||
assert.property(result.data[0], 'level')
|
||||
assert.property(result.data[0], 'timestamp')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
const pass = 'test'
|
||||
const result = await uut.getLogs(pass)
|
||||
// console.log('result', result)
|
||||
|
||||
assert.isTrue(result.success)
|
||||
assert.isArray(result.data)
|
||||
assert.property(result.data[0], 'message')
|
||||
assert.property(result.data[0], 'level')
|
||||
assert.property(result.data[0], 'timestamp')
|
||||
})
|
||||
|
||||
it('should return false if files are not found!', async () => {
|
||||
@@ -201,31 +200,25 @@ describe('#LogsApiLib', () => {
|
||||
})
|
||||
|
||||
it('should ignore fileReader callback errors', async () => {
|
||||
try {
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.lineReader, 'eachLine').yieldsRight({}, true)
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.lineReader, 'eachLine').yieldsRight({}, true)
|
||||
|
||||
const fileName = context.fileName
|
||||
const result = await uut.readLines(fileName)
|
||||
assert.isArray(result)
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
const fileName = `${__dirname.toString()}/../mocks/adapters/fake-log`
|
||||
|
||||
const result = await uut.readLines(fileName)
|
||||
assert.isArray(result)
|
||||
})
|
||||
|
||||
it('should return data', async () => {
|
||||
try {
|
||||
const fileName = context.fileName
|
||||
const result = await uut.readLines(fileName)
|
||||
const fileName = `${__dirname.toString()}/../mocks/adapters/fake-log`
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[1], 'message')
|
||||
assert.property(result[1], 'level')
|
||||
assert.property(result[1], 'timestamp')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
const result = await uut.readLines(fileName)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[1], 'message')
|
||||
assert.property(result[1], 'level')
|
||||
assert.property(result[1], 'timestamp')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -177,6 +177,42 @@ describe('#BCH-REST-Controller', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#utxosBulk', () => {
|
||||
it('should return 422 status on arbitrary error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.adapters.bch, 'getUtxosBulk')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
ctx.request.body = {
|
||||
address: 'blah'
|
||||
}
|
||||
|
||||
await uut.utxosBulk(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
console.log('err: ', err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
sandbox.stub(uut.adapters.bch, 'getUtxosBulk').resolves({ status: 200 })
|
||||
|
||||
ctx.request.body = {
|
||||
address: 'blah'
|
||||
}
|
||||
|
||||
await uut.utxosBulk(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#broadcast', () => {
|
||||
it('should return 422 status on arbitrary error', async () => {
|
||||
try {
|
||||
@@ -322,4 +358,112 @@ describe('#BCH-REST-Controller', () => {
|
||||
assert.equal(ctx.status, 200)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#utxoIsValid', () => {
|
||||
it('should return 422 status on arbitrary error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.adapters.bch, 'utxoIsValid')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
ctx.request.body = {
|
||||
utxo: 'blah'
|
||||
}
|
||||
|
||||
await uut.utxoIsValid(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
console.log('err: ', err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
sandbox.stub(uut.adapters.bch, 'utxoIsValid').resolves({ status: 200 })
|
||||
|
||||
ctx.request.body = {
|
||||
utxo: 'blah'
|
||||
}
|
||||
|
||||
await uut.utxoIsValid(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTokenData', () => {
|
||||
it('should return 422 status on arbitrary error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.adapters.bch, 'getTokenData')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
ctx.request.body = {
|
||||
tokenId: 'blah'
|
||||
}
|
||||
|
||||
await uut.getTokenData(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
console.log('err: ', err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
sandbox.stub(uut.adapters.bch, 'getTokenData').resolves({ status: 200 })
|
||||
|
||||
ctx.request.body = {
|
||||
tokenId: 'blah'
|
||||
}
|
||||
|
||||
await uut.getTokenData(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTokenData2', () => {
|
||||
it('should return 422 status on arbitrary error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.adapters.bch, 'getTokenData2')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
ctx.request.body = {
|
||||
tokenId: 'blah'
|
||||
}
|
||||
|
||||
await uut.getTokenData2(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
console.log('err: ', err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
sandbox.stub(uut.adapters.bch, 'getTokenData2').resolves({ status: 200 })
|
||||
|
||||
ctx.request.body = {
|
||||
tokenId: 'blah'
|
||||
}
|
||||
|
||||
await uut.getTokenData2(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Unit tests for the bin/server.js file
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Local libraries
|
||||
const Server = require('../../../bin/server')
|
||||
|
||||
describe('#server', () => {
|
||||
let uut, sandbox
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
uut = new Server()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#startServer', () => {
|
||||
it('should start the server', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.mongoose, 'connect').resolves()
|
||||
sandbox.stub(uut.controllers, 'initAdapters').resolves()
|
||||
sandbox.stub(uut.controllers, 'initUseCases').resolves()
|
||||
sandbox.stub(uut.controllers, 'attachRESTControllers').resolves()
|
||||
sandbox.stub(uut.adminLib, 'createSystemUser').resolves(true)
|
||||
sandbox.stub(uut.controllers, 'attachControllers').resolves()
|
||||
uut.config.env = 'dev'
|
||||
|
||||
const result = await uut.startServer()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result, 'env')
|
||||
|
||||
// Turn off the server.
|
||||
uut.server.close()
|
||||
|
||||
// Restor config env
|
||||
uut.config.env = 'test'
|
||||
})
|
||||
|
||||
it('should exit on failure', async () => {
|
||||
// Force an error
|
||||
sandbox.stub(uut.mongoose, 'connect').rejects(new Error('test error'))
|
||||
|
||||
// Prevent default behavior of exiting the program.
|
||||
sandbox.stub(uut, 'sleep').resolves()
|
||||
sandbox.stub(uut.process, 'exit').returns()
|
||||
|
||||
await uut.startServer()
|
||||
|
||||
// Not throwing an error is a success
|
||||
})
|
||||
})
|
||||
|
||||
describe('#sleep', () => {
|
||||
it('should execute', async () => {
|
||||
await uut.sleep(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,70 @@
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.907Z"}
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.909Z"}
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.910Z"}
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.912Z"}
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.915Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.979Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.980Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.981Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.982Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.983Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/validateEmailArray()","timestamp":"2022-06-27T17:36:59.002Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/validateEmailArray()","timestamp":"2022-06-27T17:36:59.003Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/getHtmlFromObject()","timestamp":"2022-06-27T17:36:59.005Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/getHtmlFromObject()","timestamp":"2022-06-27T17:36:59.006Z"}
|
||||
{"level":"error","message":"Error in lib/nodemailer.js/getHtmlFromObject()","timestamp":"2022-06-27T17:36:59.008Z"}
|
||||
{"level":"info","message":"Warning: Can not send JSON RPC response. Can not determine which peer this message came from.","timestamp":"2022-06-27T17:36:59.353Z"}
|
||||
{"level":"info","message":"Rejecting invalid JSON RPC command.","timestamp":"2022-06-27T17:36:59.354Z"}
|
||||
{"level":"info","message":"JSON RPC received from peerA, ID: ea442e78-2297-466a-823b-9cbb12c8ddd9, type: request, method: unknownMethod","timestamp":"2022-06-27T17:36:59.355Z"}
|
||||
{"level":"error","message":"Error in rpc router(): test error","stack":"Error: test error\n at Context.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/test/unit/controllers/json-rpc/a10-rpc.unit.js:110:49)\n at callFn (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:366:21)\n at Test.Runnable.run (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:354:5)\n at Runner.runTest (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:666:10)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:789:12\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:581:14)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:591:7\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:474:14)\n at Immediate._onImmediate (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:559:5)\n at processImmediate (node:internal/timers:466:21)","timestamp":"2022-06-27T17:36:59.357Z"}
|
||||
{"level":"info","message":"JSON RPC received from peerA, ID: 9ef6ad90-7fc3-4cb3-8769-4498e5512b3d, type: request, method: users","timestamp":"2022-06-27T17:36:59.358Z"}
|
||||
{"level":"info","message":"JSON RPC received from peerA, ID: bd57a1cf-7f2f-4ff2-93d4-6727eec4d778, type: request, method: auth","timestamp":"2022-06-27T17:36:59.359Z"}
|
||||
{"level":"info","message":"JSON RPC received from peerA, ID: 8fd9edc8-6a45-4042-ac06-9ace60567cbf, type: request, method: about","timestamp":"2022-06-27T17:36:59.361Z"}
|
||||
{"level":"info","message":"JSON RPC received from peerA, ID: 1b27624a-e698-4bbe-9183-b860a1a0d78d, type: request, method: unknownMethod","timestamp":"2022-06-27T17:36:59.363Z"}
|
||||
{"level":"info","message":"JSON RPC received from peerA, ID: 40fabddf-59c6-4910-a22c-63da7387f99a, type: request, method: unknownMethod","timestamp":"2022-06-27T17:36:59.365Z"}
|
||||
{"level":"error","message":"Error in authUser(): Login credential do not match","stack":"Error: Login credential do not match\n at Context.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/test/unit/controllers/json-rpc/auth.json-rpc.controller.unit.js:144:18)\n at callFn (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:366:21)\n at Test.Runnable.run (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:354:5)\n at Runner.runTest (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:666:10)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:789:12\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:581:14)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:591:7\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:474:14)\n at Immediate.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:559:5)\n at processImmediate (node:internal/timers:466:21)","timestamp":"2022-06-27T17:37:04.421Z"}
|
||||
{"level":"error","message":"Error in authUser(): login must be specified","stack":"Error: login must be specified\n at AuthRPC.authUser (/home/trout/work/psf/code/ipfs-service-provider/src/controllers/json-rpc/auth/index.js:78:93)\n at Context.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/test/unit/controllers/json-rpc/auth.json-rpc.controller.unit.js:165:34)\n at callFn (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:366:21)\n at Test.Runnable.run (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:354:5)\n at Runner.runTest (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:666:10)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:789:12\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:581:14)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:591:7\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:474:14)\n at Immediate._onImmediate (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:559:5)\n at processImmediate (node:internal/timers:466:21)","timestamp":"2022-06-27T17:37:04.423Z"}
|
||||
{"level":"error","message":"Error in authUser(): password must be specified","stack":"Error: password must be specified\n at AuthRPC.authUser (/home/trout/work/psf/code/ipfs-service-provider/src/controllers/json-rpc/auth/index.js:78:284)\n at Context.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/test/unit/controllers/json-rpc/auth.json-rpc.controller.unit.js:185:34)\n at callFn (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:366:21)\n at Test.Runnable.run (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:354:5)\n at Runner.runTest (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:666:10)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:789:12\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:581:14)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:591:7\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:474:14)\n at Immediate._onImmediate (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:559:5)\n at processImmediate (node:internal/timers:466:21)","timestamp":"2022-06-27T17:37:04.425Z"}
|
||||
{"level":"error","timestamp":"2022-06-27T17:37:04.466Z"}
|
||||
{"level":"info","message":"Running server in environment: dev","timestamp":"2022-06-27T17:37:04.487Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.495Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.495Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.496Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.497Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.497Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/getAllUsers()","timestamp":"2022-06-27T17:37:04.499Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.502Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.503Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.503Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.504Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.505Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.506Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/deleteUser()","timestamp":"2022-06-27T17:37:04.508Z"}
|
||||
{"level":"info","message":"Running server in environment: test","timestamp":"2022-06-27T17:37:04.514Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.638Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:05.273Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:05.277Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:05.280Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:05.286Z"}
|
||||
{"level":"error","timestamp":"2022-06-27T17:37:05.412Z"}
|
||||
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d27f, Target user: 1","timestamp":"2022-06-27T17:37:05.441Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.448Z"}
|
||||
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d27f, Target user: 62b9eac1b4f08950b955d27d","timestamp":"2022-06-27T17:37:05.452Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.457Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.463Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.467Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.473Z"}
|
||||
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac0b4f08950b955d274, Target user: 62b9eac1b4f08950b955d27d","timestamp":"2022-06-27T17:37:05.478Z"}
|
||||
{"level":"verbose","message":"It's ok. The user is an admin.","timestamp":"2022-06-27T17:37:05.478Z"}
|
||||
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d27f, Target user: 1","timestamp":"2022-06-27T17:37:05.600Z"}
|
||||
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d27f, Target user: 62b9eac1b4f08950b955d27d","timestamp":"2022-06-27T17:37:05.603Z"}
|
||||
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac0b4f08950b955d274, Target user: 62b9eac1b4f08950b955d27d","timestamp":"2022-06-27T17:37:05.615Z"}
|
||||
{"level":"verbose","message":"It's ok. The user is an admin.","timestamp":"2022-06-27T17:37:05.615Z"}
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:37:05.619Z"}
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:37:05.622Z"}
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:37:05.624Z"}
|
||||
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:37:05.627Z"}
|
||||
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d2a6, Target user: Target Id","timestamp":"2022-06-27T17:37:05.754Z"}
|
||||
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac0b4f08950b955d274, Target user: 62b9eac1b4f08950b955d2a6","timestamp":"2022-06-27T17:37:05.756Z"}
|
||||
{"level":"verbose","message":"It's ok. The user is an admin.","timestamp":"2022-06-27T17:37:05.756Z"}
|
||||
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:06.060Z"}
|
||||
@@ -2,12 +2,20 @@
|
||||
Mocks for the Adapter library.
|
||||
*/
|
||||
|
||||
const ipfs = {
|
||||
ipfsAdapter: {
|
||||
ipfs: {}
|
||||
},
|
||||
ipfsCoordAdapter: {
|
||||
ipfsCoord: {
|
||||
class IpfsAdapter {
|
||||
constructor () {
|
||||
this.ipfs = {
|
||||
files: {
|
||||
stat: () => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IpfsCoordAdapter {
|
||||
constructor () {
|
||||
|
||||
this.ipfsCoord = {
|
||||
useCases: {
|
||||
peer: {
|
||||
sendPrivateMessage: () => {}
|
||||
@@ -17,13 +25,21 @@ const ipfs = {
|
||||
peerData: []
|
||||
}
|
||||
},
|
||||
peerInputHandler: () => {},
|
||||
state: {
|
||||
|
||||
this.peerInputHandler = () => {}
|
||||
|
||||
this.state = {
|
||||
serviceProviders: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ipfs = {
|
||||
ipfsAdapter: new IpfsAdapter(),
|
||||
ipfsCoordAdapter: new IpfsCoordAdapter()
|
||||
}
|
||||
ipfs.ipfs = ipfs.ipfsAdapter.ipfs
|
||||
|
||||
const localdb = {
|
||||
Users: class Users {
|
||||
static findById () {}
|
||||
@@ -77,6 +93,10 @@ class BchUseCaseMock {
|
||||
return {}
|
||||
}
|
||||
|
||||
async getUtxosBulk () {
|
||||
return {}
|
||||
}
|
||||
|
||||
async broadcast () {
|
||||
return {}
|
||||
}
|
||||
@@ -93,6 +113,18 @@ class BchUseCaseMock {
|
||||
return {}
|
||||
}
|
||||
|
||||
async utxoIsValid() {
|
||||
return {}
|
||||
}
|
||||
|
||||
async getTokenData() {
|
||||
return {}
|
||||
}
|
||||
|
||||
async getTokenData2() {
|
||||
return {}
|
||||
}
|
||||
|
||||
async waitForRPCResponse () {
|
||||
return {}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ class IPFSCoord {
|
||||
}
|
||||
|
||||
async start () {}
|
||||
|
||||
async subscribeToChat() {}
|
||||
}
|
||||
|
||||
module.exports = IPFSCoord
|
||||
|
||||
@@ -48,4 +48,17 @@ describe('#use-cases', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#start', () => {
|
||||
it('should initialize async use cases', async () => {
|
||||
const result = await uut.start()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
// it('should catch and throw errors', async () => {
|
||||
// // Force an error
|
||||
// sandbox.stub()
|
||||
// })
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user