Compare commits

...
136 Commits
Author SHA1 Message Date
Chris Troutner 3d6d544c21 Merge pull request #28 from Permissionless-Software-Foundation/ct-unstable
fix(docker): Using image rather than dockerfile
2022-05-16 14:51:22 -07:00
Chris Troutner 2ca54865bc fix(docker): Using image rather than dockerfile 2022-05-16 14:18:09 -07:00
Chris Troutner df778d6d8e Merge pull request #27 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider & removing event emitter dependency.
2022-05-16 14:08:55 -07:00
Chris Troutner 3c64f44dfd Reducing screen spam 2022-05-16 14:06:37 -07:00
Chris Troutner 45ea47d4d0 fix(config): Fixing env var setting 2022-05-16 13:56:19 -07:00
Chris Troutner 150e3d6799 Fixing env var 2022-05-16 13:54:04 -07:00
Chris Troutner b3011bcb69 linting 2022-05-16 13:32:46 -07:00
Chris Troutner ccbe9a6707 feat(event emitter): Removing dependency on event emitter 2022-05-16 13:32:16 -07:00
Chris Troutner 23ebdaa7f3 Reducing debug level 2022-05-16 12:35:51 -07:00
Chris Troutner fb182ced58 Merge pull request #86 from Permissionless-Software-Foundation/ct-unstable
Updating docker-compose file
2022-05-16 12:33:03 -07:00
Chris Troutner bec7c11e46 Updating README 2022-05-16 12:31:53 -07:00
Chris Troutner fb10e10226 fix(production): Updating docker images 2022-05-16 12:30:58 -07:00
Chris Troutner 5b37e47747 Merge pull request #85 from Permissionless-Software-Foundation/ct-unstable
fix(bch-js): Updating to v6.4.1
2022-05-16 12:15:47 -07:00
Chris Troutner a3cea782ba fix(bch-js): Updating to v6.4.1 2022-05-16 12:14:20 -07:00
Chris Troutner 14ffdae194 Merge pull request #84 from Permissionless-Software-Foundation/dh-test-coverage
feat(coverage): Got ipfs-coord.js to 100% test coverage
2022-05-16 12:09:18 -07:00
Chris Troutner 5792871e0a Merge pull request #26 from Permissionless-Software-Foundation/ct-unstable
Adding utxoIsValid and getTokenData endpoints
2022-05-10 13:19:27 -07:00
Chris Troutner c2ec08f03d feat(getTokenData): Added endpoint for getting data associated with a token 2022-05-10 13:18:24 -07:00
Chris Troutner e4489cfcac linting 2022-05-10 13:06:21 -07:00
Chris Troutner e8c08beeb3 feat(utxoIsValid): Adding endpoint support for utxoIsValid 2022-05-10 13:06:03 -07:00
Chris Troutner 4a64423885 fix(bch-js): Updated to v6.3.4 2022-05-10 11:53:55 -07:00
Daniel Gonzalez 7e3ce1564d feat(coverage): Got ipfs-coord.js to 100% test coverage 2022-03-25 22:12:11 -04:00
Chris Troutner 8154dd96f9 Merge pull request #83 from Permissionless-Software-Foundation/ct-unstable
fix(deps): Updating bch-js
2022-03-20 07:48:42 -07:00
Chris Troutner e37223cc88 fix(deps): Updating bch-js 2022-03-20 07:46:08 -07:00
Chris Troutner 5002ef2b90 Merge pull request #82 from Permissionless-Software-Foundation/dh-test-coverage-ipfsjs
feat(test): Got ipfs.js to 100% test coverage
2022-03-18 11:27:32 -07:00
Chris Troutner 5d95109ad4 more linting 2022-03-18 11:24:15 -07:00
Chris Troutner 13b3150f62 minor linting 2022-03-18 11:24:04 -07:00
Daniel Gonzalez b3a4a7c1ab feat(test): Got ipfs.js to 100% test coverage 2022-03-15 18:58:55 -04:00
Chris Troutner d6b64fce59 Merge pull request #25 from Permissionless-Software-Foundation/ct-unstable
fix(production): Updating IPFS container from v11 to v12
2022-03-15 14:15:22 -07:00
Chris Troutner 8c12222d9c fix(production): Updating IPFS container from v11 to v12 2022-03-15 14:13:22 -07:00
Chris Troutner 26a79d1880 Merge pull request #24 from Permissionless-Software-Foundation/ct-unstable
fix(production): Slight tweeks to docker container
2022-03-15 13:33:12 -07:00
Chris Troutner 3f96b608b5 fix(production): Slight tweeks to docker container 2022-03-15 13:31:00 -07:00
Chris Troutner a669fc6440 Merge pull request #23 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.5
2022-03-10 09:58:44 -08:00
Chris Troutner 42ee03a6d9 Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-03-10 09:56:50 -08:00
Chris Troutner 733e8fd405 Merge pull request #81 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.6
2022-03-10 09:29:32 -08:00
Chris Troutner baee72c1c6 fix(ipfs-coord): Updating to v7.1.6 2022-03-10 09:27:13 -08:00
Chris Troutner 5c2ab8a8cf Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-03-10 08:47:54 -08:00
Chris Troutner 11618a796f Merge pull request #80 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.5
2022-03-10 08:35:21 -08:00
Chris Troutner 025ae20704 fix(ipfs-coord): Updating to v7.1.5 2022-03-10 08:33:53 -08:00
Chris Troutner 5f6b98a1e5 Merge pull request #22 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2022-03-08 09:58:21 -08:00
Chris Troutner e1f5e3fa9c Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-03-08 09:54:23 -08:00
Chris Troutner fdf73610eb Merge pull request #79 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Updating tests for node.js v16
2022-03-08 09:42:42 -08:00
Chris Troutner b09e77c079 fix(tests): Updating tests for node.js v16 2022-03-08 09:39:54 -08:00
Chris Troutner d583acd6f3 Merge pull request #21 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.4
2022-03-05 14:53:33 -08:00
Chris Troutner 079bd1bebc Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-03-05 14:48:35 -08:00
Chris Troutner 68ad2561b1 Merge pull request #78 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.4
2022-03-05 14:22:25 -08:00
Chris Troutner 791d11e597 Merge branch 'master' into ct-unstable 2022-03-05 14:20:19 -08:00
Chris Troutner 5489e76f55 fix(ipfs-coord): Updating to v7.1.4 2022-03-05 14:20:13 -08:00
Chris Troutner a205f7967f lint 2022-03-05 09:30:36 -08:00
Chris Troutner 346ba79929 Removing redundent debug log 2022-03-05 09:30:25 -08:00
Chris Troutner 14a9917670 Merge pull request #20 from Permissionless-Software-Foundation/ct-upstream
fix(ipfs-coord): Updating to v7.1.3
2022-03-05 09:21:16 -08:00
Chris Troutner e4fe851871 fix(upstream): Merging with upstream. Updating ipfs-coord 2022-03-05 09:19:35 -08:00
Chris Troutner 496a1dd07c Merge pull request #77 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.3
2022-03-05 08:26:32 -08:00
Chris Troutner a7b8ca9ad5 linting 2022-03-05 08:24:50 -08:00
Chris Troutner 6eb16eb11a fix(ipfs-coord): Updating to v7.1.3 2022-03-05 08:24:36 -08:00
Chris Troutner 589b723629 Increasing sockets to 200 2022-02-24 17:56:41 -08:00
Chris Troutner 5ec45ee9cb fix(ipfs): Reducing sockets from 2000 to 20 2022-02-24 17:35:34 -08:00
Chris Troutner 915d3563c2 Merge pull request #19 from Permissionless-Software-Foundation/ct-unstable
Adding /price/usd REST endpoint
2022-02-02 15:57:05 -08:00
Chris Troutner 9446efe1ae feat(price): Adding /price/usd endpoint 2022-02-02 15:55:31 -08:00
Chris Troutner c573b71a23 Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-01-31 19:52:43 -08:00
Chris Troutner 1844f30dc5 Merge pull request #76 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Bumping to v6.8.12
2022-01-31 10:31:20 -08:00
Chris Troutner b6ab2f02f4 Updating package-lock 2022-01-31 10:29:45 -08:00
Chris Troutner b5c76aa5fe fix(ipfs-coord): Bumping to v6.8.12 2022-01-31 10:28:06 -08:00
Chris Troutner 26c0611c51 Merge pull request #17 from Permissionless-Software-Foundation/ct-unstable
Renaming transaction and transactions to txData and txHistory
2022-01-29 09:49:18 -08:00
Chris Troutner 3345160447 fixed unit tests 2022-01-29 09:47:19 -08:00
Chris Troutner 8d4984c4ac fix(txData): Renamed transaction REST API endpoint to txData 2022-01-29 09:31:59 -08:00
Chris Troutner b2cc85df86 feat(txHistory): Renamed transactions endpoint to txHistory 2022-01-29 08:28:13 -08:00
Chris Troutner 51977adad8 fix(production): Adding .env file to supress docker orphan warnings 2022-01-28 20:09:56 -08:00
Chris Troutner 5c7e722808 Merge pull request #16 from Permissionless-Software-Foundation/ct-unstable
Refactored transaction and transactions endpoints
2022-01-28 18:54:43 -08:00
Chris Troutner 03572e9c3d Restoring e2e tests 2022-01-28 15:50:25 -08:00
Chris Troutner 817b40c489 fix(transaction): Refactored transaction 2022-01-28 15:49:54 -08:00
Chris Troutner 744cf60f90 Replacing previous versions of transactions() with new version 2022-01-28 06:25:29 -08:00
Chris Troutner f979cabd50 Merge pull request #15 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2022-01-20 09:17:03 -08:00
Chris Troutner 30942a4d6b Fixing merge conflicts 2022-01-20 09:14:37 -08:00
Chris Troutner 4101615a4a Merge pull request #75 from Permissionless-Software-Foundation/ct-unstable
Maintenance
2022-01-20 08:05:50 -08:00
Chris Troutner 34a4072384 fix(restart): Removing code the auto-restarts app after 2 hours 2022-01-20 07:56:40 -08:00
Chris Troutner 5fc72be10c fix(fullstack-jwt.js): Got unit test coverage back to 100% 2022-01-20 07:53:12 -08:00
Chris Troutner 9a9f1427fd Merge pull request #14 from Permissionless-Software-Foundation/ct-unstable
fix(bch-js): Updating to v5
2022-01-18 15:43:56 -08:00
Chris Troutner 5c9539373c Merge branch 'master' into ct-unstable 2022-01-18 15:41:08 -08:00
Chris Troutner 79484f706d fix(bch-js): Updating to v5 2022-01-18 15:40:21 -08:00
Chris Troutner 7b112a13d4 fix(cors): Fixing bug leading to CORS error 2022-01-12 15:33:04 -08:00
Chris Troutner 6f6b5874d9 Merge pull request #13 from Permissionless-Software-Foundation/ct-unstable
fix(cors): slight tweak to avoid CORS error
2022-01-12 14:50:28 -08:00
Chris Troutner d0ef1a8669 fix(cors): slight tweak to avoid CORS error 2022-01-12 14:48:33 -08:00
Chris Troutner fe5c573291 Merge pull request #74 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Skipping IPFS stuff in test environment
2022-01-09 11:54:41 -08:00
Chris Troutner 463d7df582 fix(tests): Skipping IPFS stuff in test environment 2022-01-09 11:51:15 -08:00
Chris Troutner 5a1ec640a4 Merge pull request #73 from Permissionless-Software-Foundation/ct-unstable
Adding swarm.key
2022-01-03 11:39:57 -08:00
Chris Troutner 8bb8dbcd23 Updating READMe 2022-01-03 11:35:26 -08:00
Chris Troutner ba212b0997 fix(ipfs): Adding swarm.key file for joining network 2022-01-03 11:22:15 -08:00
Chris Troutner 9cff05311b Merge pull request #12 from Permissionless-Software-Foundation/ct-unstable
Syncing upstream ipfs-service-provider
2022-01-02 19:19:21 -08:00
Chris Troutner 986d843360 Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-01-02 19:17:11 -08:00
Chris Troutner 4670bd22af Merge pull request #72 from Permissionless-Software-Foundation/ct-unstable
fix(pubsub): Applying fix for pubsub in external node
2022-01-02 19:13:44 -08:00
Chris Troutner b3e7c63e61 fix(pubsub): Applying fix for pubsub in external node 2022-01-02 19:00:50 -08:00
Chris Troutner c352c3d6d1 Merge pull request #11 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2022-01-01 21:31:14 -08:00
Chris Troutner 5bd425a3d3 Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-01-01 21:19:25 -08:00
Chris Troutner 1fcc92e10d Merge pull request #71 from Permissionless-Software-Foundation/ct-unstable
fix(ipsf-coord): Updating to v6.8.10
2022-01-01 21:01:14 -08:00
Chris Troutner f332aece98 fix(ipsf-coord): Updating to v6.8.10 2022-01-01 20:58:56 -08:00
Chris Troutner 89f24e9940 Merge pull request #70 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Switching to private network. Updating ipfs-coord
2022-01-01 20:40:23 -08:00
Chris Troutner 25a75dc369 fix(ipfs-coord): Switching to private network. Updating ipfs-coord to v6.8.9 2022-01-01 20:25:40 -08:00
Chris Troutner f36ce14087 fix(ipfs-coord): Updating v6.8.8 2022-01-01 18:58:47 -08:00
Chris Troutner df45792f47 fix(ipfs-coord): Updating to v6.8.7 2022-01-01 18:43:24 -08:00
Chris Troutner 9e71e33ccd Merge pull request #69 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v6.8.6
2022-01-01 18:20:30 -08:00
Chris Troutner c7b0b0c1d5 fix(ipfs-coord): Updating to v6.8.6 2022-01-01 18:19:07 -08:00
Chris Troutner e475e5f4c3 Downgrading ipfs-http-client 2022-01-01 16:41:56 -08:00
Chris Troutner 18964d3ee4 Merge pull request #10 from Permissionless-Software-Foundation/ct-unstable
Switching to external go-ipfs as default
2022-01-01 07:40:33 -08:00
Chris Troutner 197ece4d0d Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-01-01 07:39:10 -08:00
Chris Troutner a52798b1b7 fix(ipfs-coord): Updating to v6.8.5 2022-01-01 07:37:42 -08:00
Chris Troutner 0748106637 Merge pull request #68 from Permissionless-Software-Foundation/ct-unstable
fix(production): using explcit v0.11.0 ipfs docker container
2022-01-01 06:41:36 -08:00
Chris Troutner 27dcc219b9 fix(production): using explcit v0.11.0 ipfs docker container 2022-01-01 06:40:15 -08:00
Chris Troutner b231686a64 Merge pull request #67 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v6.8.5
2021-12-31 18:40:55 -08:00
Chris Troutner 60668430f5 fix(ipfs-coord): Updating to v6.8.5 2021-12-31 18:38:17 -08:00
Chris Troutner 37ab66ef23 updated ipfs-coord 2021-12-31 16:30:38 -08:00
Chris Troutner 3382e03e69 Debug level 2 by default 2021-12-31 16:23:26 -08:00
Chris Troutner b9680508db Merge pull request #66 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v6.8.4
2021-12-31 16:01:32 -08:00
Chris Troutner acebfdef09 fix(ipfs-coord): Updating to v6.8.4 2021-12-31 15:59:06 -08:00
Chris Troutner 3979694109 Merge pull request #65 from Permissionless-Software-Foundation/ct-unstable
fix(production): Adding instructions for customizing IPFS docker cont…
2021-12-31 15:38:39 -08:00
Chris Troutner ae888d094b fix(production): Adding instructions for customizing IPFS docker container 2021-12-31 15:34:40 -08:00
Chris Troutner e8a3234dcd Merge pull request #64 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): updating to v6.8.2
2021-12-31 12:13:13 -08:00
Chris Troutner fc717e0dcd fix(ipfs-coord): updating to v6.8.2 2021-12-31 12:11:47 -08:00
Chris Troutner 17f3afbf1f fix(e2e tests): Disabling IPFS controllers and adapters for e2e tests 2021-12-30 10:45:20 -08:00
Chris Troutner 18cec75f41 Merge pull request #63 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v6.8.1
2021-12-29 19:33:54 -08:00
Chris Troutner 7621842952 fix(ipfs-coord): Updating to v6.8.1 2021-12-29 19:32:39 -08:00
Chris Troutner 1e27b477a0 feat(ipfs): Updating to js-ipfs v0.60.0 2021-12-29 19:05:45 -08:00
Chris Troutner 08ec38e3a5 changed port 2021-12-29 18:50:09 -08:00
Chris Troutner 0e9a57a1da Merge pull request #62 from Permissionless-Software-Foundation/ct-unstable
Kicking off v2.0.0
2021-12-29 18:04:29 -08:00
Chris Troutner e374522725 fix(version): Bumping to v2.0.0 2021-12-29 18:03:09 -08:00
Chris Troutner e402f33132 Adding more properties to docker-compose 2021-12-29 18:00:17 -08:00
Chris Troutner e85655c0ec Adding data/ directory to gitignore 2021-12-29 17:46:52 -08:00
Chris Troutner 227ffbc4bc Removing data directory 2021-12-29 17:46:28 -08:00
Chris Troutner b41f819783 fix(startup): Exiting if server can't stop 2021-12-29 17:46:05 -08:00
Chris Troutner d6c3635c6a fix(ipfs): Got running with external IPFS node 2021-12-29 17:28:35 -08:00
Chris Troutner 37e98e4248 fix(ipfs): Switching from @chris.troutner/ipfs to ipfs v0.60.0 2021-12-29 16:59:32 -08:00
Chris Troutner 6b23d00ffe Merge pull request #9 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord.js): better error handling
2021-12-24 18:56:51 -08:00
Chris Troutner 53cd4650bd fix(ipfs-coord.js): better error handling 2021-12-24 17:59:52 -08:00
Chris Troutner d734a754d6 Merge pull request #8 from Permissionless-Software-Foundation/ct-unstable
syncing upstream ipfs-service-provider
2021-12-24 17:25:18 -08:00
Chris Troutner d4863e7d7d Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2021-12-24 17:16:47 -08:00
Chris Troutner e6941f7f7f Merge pull request #61 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-cood): Updating to v6.7.6
2021-12-24 17:10:49 -08:00
Chris Troutner 4fdf35a55f fix(ipfs-cood): Updating to v6.7.6 2021-12-24 17:07:54 -08:00
41 changed files with 11655 additions and 18007 deletions
+1
View File
@@ -67,5 +67,6 @@ orbitdb
ipfsdata
.ipfsdata
run-dev.sh
data/
!README.md
+16 -4
View File
@@ -8,13 +8,17 @@ When started, this web server starts an [IPFS](https://ipfs.io) node and connect
## Requirements
- node **^14.17.0**
- npm **^7.13.0**
- node **^14.18.2**
- npm **^8.3.0**
- Docker **^20.10.8**
- Docker Compose **^1.27.4**
## Installation
### Development Environment
**Note:** This software now uses an external go-ipfs IPFS node. The instructions below have not been updated to reflect this.
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)
@@ -31,17 +35,25 @@ npm install
### Production Environment
The [docker](./production/docker) directory contains a Dockerfile for building a production deployment. However, there is currently [a bug](https://github.com/Permissionless-Software-Foundation/ipfs-service-provider/issues/38) preventing the Docker container from being used as a Circuit Relay.
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
```
You can bring the containers back up with `docker-compose up -d`.
### Operation Notes
- 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).
+20 -13
View File
@@ -68,6 +68,11 @@ class Server {
app.use(passport.initialize())
app.use(passport.session())
// Enable CORS for testing
// THIS IS A SECURITY RISK. COMMENT OUT FOR PRODUCTION
// Dev Note: This line must come BEFORE controllers.attachRESTControllers()
app.use(cors({ origin: '*' }))
// Attach REST API and JSON RPC controllers to the app.
const Controllers = require('../src/controllers')
const controllers = new Controllers()
@@ -75,10 +80,6 @@ class Server {
app.controllers = controllers
// Enable CORS for testing
// THIS IS A SECURITY RISK. COMMENT OUT FOR PRODUCTION
app.use(cors({ origin: '*' }))
// MIDDLEWARE END
console.log(`Running server in environment: ${config.env}`)
@@ -91,21 +92,27 @@ class Server {
const success = await this.adminLib.createSystemUser()
if (success) console.log('System admin user created.')
// Attach the other IPFS controllers
await controllers.attachControllers(app)
// ipfs-coord has a memory leak. This app shuts down after 4 hours. It
// expects to be run by Docker or pm2, which can automatically restart
// the app.
setTimeout(function () {
process.exit(0)
}, 60000 * 60 * 2) // 2 hours
// Attach the other IPFS controllers.
// Skip if this is a test environment.
if (config.env !== 'test') {
await controllers.attachControllers(app)
}
return app
} catch (err) {
console.error('Could not start server. Error: ', err)
console.log(
'Exiting after 5 seconds. Depending on process manager to restart.'
)
await sleep(5000)
process.exit(1)
}
}
}
function sleep (ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
module.exports = Server
+11 -4
View File
@@ -15,7 +15,7 @@ const ipfsCoordName = process.env.COORD_NAME
module.exports = {
// Configure TCP port.
port: process.env.PORT || 5001,
port: process.env.PORT || 5005,
// Password for HTML UI that displays logs.
logPass: 'test',
@@ -72,8 +72,8 @@ module.exports = {
},
// IPFS Ports
ipfsTcpPort: process.env.IPFS_TCP_PORT ? process.env.IPFS_TCP_PORT : 4001,
ipfsWsPort: process.env.IPFS_WS_PORT ? process.env.IPFS_WS_PORT : 4003,
ipfsTcpPort: process.env.IPFS_TCP_PORT ? process.env.IPFS_TCP_PORT : 4101,
ipfsWsPort: process.env.IPFS_WS_PORT ? process.env.IPFS_WS_PORT : 4103,
// BCH Mnemonic for generating encryption keys and payment address
mnemonic: process.env.MNEMONIC ? process.env.MNEMONIC : '',
@@ -91,5 +91,12 @@ module.exports = {
// Preferred P2WDB provider
preferredP2wdbProvider: process.env.PREFERRED_P2WDB_PROVIDER
? process.env.PREFERRED_P2WDB_PROVIDER
: ''
: '',
// Settings for production, using external go-ipfs node.
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
}
+10461 -17677
View File
File diff suppressed because it is too large Load Diff
+7 -5
View File
@@ -23,12 +23,13 @@
},
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-consumer",
"dependencies": {
"@chris.troutner/ipfs": "2.0.2",
"@psf/bch-js": "4.20.26",
"axios": "0.21.1",
"@psf/bch-js": "6.4.1",
"axios": "0.25.0",
"bcryptjs": "2.4.3",
"glob": "7.1.6",
"ipfs-coord": "6.7.5",
"ipfs": "0.60.0",
"ipfs-coord": "7.1.6",
"ipfs-http-client": "55.0.0",
"jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1",
"jwt-bch-lib": "1.3.0",
@@ -42,7 +43,7 @@
"koa-passport": "4.1.3",
"koa-router": "10.0.0",
"koa-static": "5.0.0",
"koa2-ratelimit": "0.9.0",
"koa2-ratelimit": "0.9.1",
"line-reader": "0.4.0",
"mongoose": "5.11.15",
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
@@ -64,6 +65,7 @@
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-standard": "4.0.0",
"husky": "4.3.8",
"lodash.clonedeep": "^4.5.0",
"mocha": "8.2.1",
"nyc": "15.1.0",
"semantic-release": "17.4.4",
+1
View File
@@ -0,0 +1 @@
COMPOSE_PROJECT_NAME=bch-wallet-consumer
+6 -6
View File
@@ -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
@@ -33,8 +33,8 @@ WORKDIR /home/safeuser
#RUN runuser -l safeuser -c "npm config set prefix '~/.npm-global'"
# Update to the latest version of npm.
# Working with npm@7.21.1
RUN npm install -g npm@7.23.0
#RUN npm install -g npm@7.23.0
RUN npm install -g npm
# npm mirror to prevent direct dependency on npm.
RUN npm set registry http://94.130.170.209:4873/
@@ -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
@@ -66,7 +66,7 @@ RUN npm run docs
VOLUME /home/safeuser/keys
# Expose the port the API will be served on.
EXPOSE 5001
EXPOSE 5010
# Start the application.
COPY start-production.sh start-production.sh
+61
View File
@@ -0,0 +1,61 @@
# Docker Containers
The 'production' environment is assumed to be a set of Docker containers orchestrated with Docker Compose. The files in this directory will stand up three Docker containers:
1 An instance of go-ipfs.
2 An instance of MongoDB.
3 The JavaScript software in this repository.
The software in this repository depends on the first two containers, so if they aren't running correctly, the application won't run correctly either.
## IPFS
IPFS can be a little tricky to set up. By default, the container uses the following ports:
- 4001 for TCP connections, exposed publicly.
- 5001 for control by the application, exposed privately.
- 8080 for an IPFS gateway, consumed by the application, exposed privately.
If you already have an IPFS node running on a the computer, you will need to change the ports to avaid a conflict. To change the ports from the default, you'll need to perform a series of steps, and the order of the steps matter.
1. Edit the `docker-compose.yml` file and change the ports. Then save the file. Here is an example:
```
ports:
- 4101:4101
- 172.17.0.1:5101:5101
- 172.17.0.1:8180:8180
```
2. Bring the Docker containers up, and then back down. This will allow the IPFS container to create the config file that you'll need to edit.
- `docker-compose up -d`
- Wait a few seconds.
- `docker-compose down`
3. Update the generated config file at `../data/go-ipfs/data/config`, to update the ports in the config file, like this:
```
"Addresses": {
"API": "/ip4/0.0.0.0/tcp/5101",
"Announce": [],
"AppendAnnounce": [],
"Gateway": "/ip4/0.0.0.0/tcp/8180",
"NoAnnounce": [],
"Swarm": [
"/ip4/0.0.0.0/tcp/4101",
"/ip6/::/tcp/4101",
"/ip4/0.0.0.0/udp/4101/quic",
"/ip6/::/udp/4101/quic"
]
},
```
4. Update the port changes in the `start-production.sh` shell script. This tells the application which ports to use, in order to control the IPFS node, are are used when signaling other nodes.
5. Quickly rebuild the containers, to add the modified `start-production.sh` shell script to the application Docker container:
- `docker-compose build`
6. Now start the containers, and the port changes to IPFS should be complete.
+39 -10
View File
@@ -1,11 +1,11 @@
# Start the service with the command 'docker-compose up -d'
version: '2'
version: '3.9'
services:
mongo-ipfs-service:
mongo-bch-consumer:
image: mongo
container_name: mongo-ipfs-service
container_name: mongo-bch-consumer
ports:
- '5555:27017' # <host port>:<container port>
volumes:
@@ -13,9 +13,39 @@ services:
command: mongod --logpath=/dev/null # -- quiet
restart: always
ipfs-service:
build: .
container_name: ipfs-service
ipfs-bch-consumer:
image: ipfs/go-ipfs:v0.12.0
container_name: ipfs-bch-consumer
environment:
MY_ENV_VAR: 'placeholder'
logging:
driver: 'json-file'
options:
max-size: '10m'
max-file: '10'
mem_limit: 2000mb
ports:
- 4001:4001
- 172.17.0.1:5001:5001
- 172.17.0.1:8080:8080
volumes:
- ../data/go-ipfs/data:/data/ipfs
- ../../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:
[
'daemon',
'--migrate=true',
'--agent-version-suffix=docker',
'--enable-pubsub-experiment'
]
restart: always
bch-consumer:
#build: .
image: christroutner/ipfs-bch-wallet-consumer:v1.8.0
container_name: bch-consumer
logging:
driver: 'json-file'
options:
@@ -23,11 +53,10 @@ services:
max-file: '10'
mem_limit: 500mb
links:
- mongo-ipfs-service
- mongo-bch-consumer
- ipfs-bch-consumer
ports:
- '5001:5001' # <host port>:<container port>
- '5268:5268' # IPFS TCP
- '5269:5269' # IPFS WS
- '5010:5010' # <host port>:<container port>
volumes:
- ../data/ipfsdata:/home/safeuser/ipfs-service-provider/.ipfsdata
restart: always
+10 -9
View File
@@ -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
@@ -23,15 +23,16 @@ export DEBUG_LEVEL=1
# Production database connection string.
export DBURL=mongodb://172.17.0.1:5555/ipfs-service-prod
# Configure IPFS ports
export IPFS_TCP_PORT=5268
export IPFS_WS_PORT=5269
export DBURL=mongodb://172.17.0.1:5555/ipfs-bch-consumer-prod
# Configure REST API port
export PORT=5001
export PORT=5010
# Production settings using external go-ipfs node.
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 SVC_ENV=production
npm start
@@ -3,7 +3,7 @@
# This script is an example for running a generic ipfs-service-provider instance.
# Ports
export PORT=5001 # REST API port
export PORT=5005 # REST API port
export IPFS_TCP_PORT=5268
export IPFS_WS_PORT=5269
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
# This script is an example for running a production environment, which is
# defined by running an external go-ipfs node.
# Ports
export PORT=5005 # REST API port
# The human-readible name that is used when displaying data about this node.
export COORD_NAME=trout-dev-bch-wallet-consumer
# This is used for end-to-end encryption (e2ee).
export MNEMONIC="stomach mean catch march census author cousin size south romance pumpkin trap"
# 0 = less verbose. 3 = most verbose
export DEBUG_LEVEL=1
# Production settings that use external IPFS node.
# https://github.com/christroutner/docker-ipfs
export SVC_ENV=production
export IPFS_HOST=localhost
export IPFS_API_PORT=5001
# Configure IPFS ports
export IPFS_TCP_PORT=4001
#export IPFS_WS_PORT=5269
# MongoDB connection string.
export DBURL=mongodb://localhost:27017/ipfs-service-dev
npm start
+116 -17
View File
@@ -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
@@ -198,8 +198,9 @@ class BchAdapter {
// Wait for data to come back from the wallet service.
const data = await this.waitForRPCResponse(rpcId)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
return data[0]
return data
} catch (err) {
// console.log('createUser() error: ', err)
wlogger.error('Error in use-cases/bch.js/getUtxos()')
@@ -249,8 +250,11 @@ class BchAdapter {
}
}
async getTransactions (address) {
// Get the transaction history for an address.
async getTransactions (address, sortOrder = 'DESCENDING', page = 0) {
try {
console.log('Executing getTransaction() bch adapter')
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
@@ -260,8 +264,10 @@ class BchAdapter {
}
const rpcData = {
endpoint: 'transactions',
addresses: [address]
endpoint: 'txHistory',
address: address,
sortOrder,
page
}
// Generate a UUID for the call.
@@ -270,7 +276,7 @@ class BchAdapter {
// Generate a JSON RPC command.
const cmd = this.jsonrpc.request(rpcId, 'bch', rpcData)
const cmdStr = JSON.stringify(cmd)
// console.log('cmdStr: ', cmdStr)
console.log('cmdStr: ', cmdStr)
// Send the RPC command to selected wallet service.
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
@@ -291,8 +297,13 @@ class BchAdapter {
}
}
async getTransaction (txid) {
// Get details on an array of TXIDs.
async getTransaction (txids) {
try {
console.log(
`Getting txData on these txids: ${JSON.stringify(txids, null, 2)}`
)
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
@@ -302,8 +313,8 @@ class BchAdapter {
}
const rpcData = {
endpoint: 'transaction',
txid
endpoint: 'txData',
txids
}
// Generate a UUID for the call.
@@ -312,7 +323,7 @@ class BchAdapter {
// Generate a JSON RPC command.
const cmd = this.jsonrpc.request(rpcId, 'bch', rpcData)
const cmdStr = JSON.stringify(cmd)
// console.log('cmdStr: ', cmdStr)
console.log('cmdStr: ', cmdStr)
// Send the RPC command to selected wallet service.
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
@@ -324,11 +335,12 @@ class BchAdapter {
// Wait for data to come back from the wallet service.
const data = await this.waitForRPCResponse(rpcId)
console.log(`data: ${JSON.stringify(data, null, 2)}`)
return data
} catch (err) {
// console.log('createUser() error: ', err)
wlogger.error('Error in adapters/bch.js/transaction()')
wlogger.error('Error in adapters/bch.js/getTransaction()')
throw err
}
}
@@ -375,6 +387,90 @@ 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) {
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
}
// 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
}
}
// Returns a promise that resolves to data when the RPC response is recieved.
async waitForRPCResponse (rpcId) {
try {
@@ -400,6 +496,9 @@ class BchAdapter {
dataFound = true
// console.log('data was found in the queue')
// console.log(
// `rawData.payload: ${JSON.stringify(rawData.payload, null, 2)}`
// )
data = rawData.payload.result.value
// Remove the data from the queue
-6
View File
@@ -52,12 +52,6 @@ class FullStackJWT {
// Get's a JWT token from FullStack.cash.
async getJWT () {
try {
// Skip connecting FullStack.cash auth server to the network if this is an E2E test.
if (process.env.TEST_TYPE === 'e2e') {
this.apiToken = 'faketoken'
return this.apiToken
}
// Log into the auth server.
await this.jwtLib.register()
+6 -6
View File
@@ -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)
+8 -8
View File
@@ -8,12 +8,12 @@ const IpfsCoordAdapter = require('./ipfs-coord')
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()
@@ -39,8 +39,8 @@ class IPFS {
// Start ipfs-coord
this.ipfsCoordAdapter = new this.IpfsCoordAdapter({
ipfs: this.ipfs,
eventEmitter: this.eventEmitter
ipfs: this.ipfs
// eventEmitter: this.eventEmitter
})
await this.ipfsCoordAdapter.start()
console.log('ipfs-coord is ready.')
+47 -22
View File
@@ -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
@@ -51,7 +51,10 @@ class IpfsCoordAdapter {
// Periodically poll services for available wallet service providers.
this.pollBchServiceInterval = setInterval(this.pollForBchServices, 10000)
this.pollP2wdbServiceInterval = setInterval(this.pollForP2wdbServices, 11000)
this.pollP2wdbServiceInterval = setInterval(
this.pollForP2wdbServices,
11000
)
// State object. TODO: Make this more robust.
this.state = {
@@ -75,6 +78,7 @@ class IpfsCoordAdapter {
circuitRelayInfo.ip4 = ip4
circuitRelayInfo.tcpPort = this.config.ipfsTcpPort
circuitRelayInfo.wsPort = this.config.ipfsWsPort
// Domain used by browser-based secure websocket connections.
circuitRelayInfo.crDomain = this.config.crDomain
@@ -83,7 +87,7 @@ class IpfsCoordAdapter {
}
}
this.ipfsCoord = new this.IpfsCoord({
const ipfsCoordOptions = {
ipfs: this.ipfs,
type: 'node.js',
// type: 'browser',
@@ -94,7 +98,14 @@ class IpfsCoordAdapter {
apiInfo: this.config.apiInfo,
announceJsonLd: this.config.announceJsonLd,
debugLevel: this.config.debugLevel
})
}
// Production env uses external go-ipfs node.
if (this.config.isProduction) {
ipfsCoordOptions.nodeType = 'external'
}
this.ipfsCoord = new this.IpfsCoord(ipfsCoordOptions)
// Wait for the ipfs-coord library to signal that it is ready.
await this.ipfsCoord.start()
@@ -110,7 +121,7 @@ class IpfsCoordAdapter {
attachRPCRouter (router) {
try {
_this.ipfsCoord.privateLog = router
_this.ipfsCoord.adapters.orbit.privateLog = router
_this.ipfsCoord.adapters.pubsub.privateLog = router
} catch (err) {
console.error('Error in attachRPCRouter()')
throw err
@@ -169,7 +180,7 @@ class IpfsCoordAdapter {
// to the preferred provider when it's discovered.
if (
_this.config.preferredP2wdbProvider &&
thisPeer === _this.config.preferredP2wdbProvider
thisPeer === _this.config.preferredP2wdbProvider
) {
_this.state.selectedP2wdbProvider = thisPeer
}
@@ -187,8 +198,15 @@ class IpfsCoordAdapter {
}
}
} catch (err) {
// catch and handle known failure mode.
if (
err.message.includes("Cannot read property 'peerList' of undefined")
) {
return
}
console.error('Error in pollForP2wdbServices()')
throw err
// Do not throw error. This is a top-level function.
}
}
@@ -259,6 +277,13 @@ class IpfsCoordAdapter {
}
}
} catch (err) {
// catch and handle known failure mode.
if (
err.message.includes("Cannot read property 'peerList' of undefined")
) {
return
}
console.error('Error in pollForBchServices(): ', err)
// Do not throw error. This is a top-level function.
}
@@ -266,16 +291,16 @@ 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)
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.
}
}
// 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.
// }
// }
}
module.exports = IpfsCoordAdapter
+42 -22
View File
@@ -10,8 +10,11 @@
// Global npm libraries
// const IPFS = require('ipfs')
const IPFS = require('@chris.troutner/ipfs')
// const IPFS = require('@chris.troutner/ipfs')
const IPFSembedded = require('ipfs')
const IPFSexternal = require('ipfs-http-client')
const fs = require('fs')
const http = require('http')
// Local libraries
const config = require('../../../config')
@@ -21,11 +24,17 @@ const IPFS_DIR = './.ipfsdata/ipfs'
class IpfsAdapter {
constructor (localConfig) {
// Encapsulate dependencies
this.IPFS = IPFS
this.config = config
// Choose the IPFS constructor based on the config settings.
this.IPFS = IPFSembedded // default
if (this.config.isProduction) {
this.IPFS = IPFSexternal
}
// Properties of this class instance.
this.isReady = false
this.config = config
this.fs = fs
}
@@ -33,7 +42,7 @@ class IpfsAdapter {
async start () {
try {
// Ipfs Options
const ipfsOptions = {
const ipfsOptionsEmbedded = {
repo: IPFS_DIR,
start: true,
config: {
@@ -64,6 +73,17 @@ class IpfsAdapter {
}
}
const ipfsOptionsExternal = {
host: this.config.ipfsHost,
port: this.config.ipfsApiPort,
agent: http.Agent({ keepAlive: true, maxSockets: 200 })
}
let ipfsOptions = ipfsOptionsEmbedded
if (this.config.isProduction) {
ipfsOptions = ipfsOptionsExternal
}
// Create a new IPFS node.
this.ipfs = await this.IPFS.create(ipfsOptions)
@@ -82,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
}
@@ -100,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
+14 -15
View File
@@ -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
@@ -49,12 +49,12 @@ class P2wdbAdapter {
// Convert string input into an object.
// const jsonData = JSON.parse(data)
console.log(`JSON RPC response for ID ${data.payload.id} received.`)
// console.log(`JSON RPC response for ID ${data.payload.id} received.`)
_this.rpcDataQueue.push(data)
} catch (err) {
console.error('Error in p2wdb/rpcHandler(): ', err)
// Do not throw error. This is a top-level function.
// Do not throw error. This is a top-level function.
}
}
@@ -71,7 +71,7 @@ class P2wdbAdapter {
// Add names to the IPFS IDs for each provider.
const initialServiceProviders =
this.ipfs.ipfsCoordAdapter.state.p2wdbProviders
this.ipfs.ipfsCoordAdapter.state.p2wdbProviders
const serviceProviders = []
for (let i = 0; i < initialServiceProviders.length; i++) {
const thisProvider = initialServiceProviders[i]
@@ -94,8 +94,7 @@ class P2wdbAdapter {
const outObj = {
serviceProviders,
selectedProvider:
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
selectedProvider: this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
}
// console.log('outObj: ', outObj)
@@ -131,7 +130,7 @@ class P2wdbAdapter {
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
if (!selectedProvider) {
throw new Error('No P2WDB Service provider available yet.')
}
@@ -174,7 +173,7 @@ class P2wdbAdapter {
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
if (!selectedProvider) {
throw new Error('No P2WDB Service provider available yet.')
}
@@ -253,7 +252,7 @@ class P2wdbAdapter {
cnt++
// Exit if data was returned, or the window for a response expires.
// Exit if data was returned, or the window for a response expires.
} while (!dataFound && cnt < 10)
// console.log(`dataFound: ${dataFound}, cnt: ${cnt}`)
+16 -16
View File
@@ -5,26 +5,23 @@
*/
// Public npm libraries.
const EventEmitter = require('events')
// const EventEmitter = require('events')
// Load the Clean Architecture Adapters library
// Local libraries
const config = require('../../config')
const Adapters = require('../adapters')
// Load the JSON RPC Controller.
const JSONRPC = require('./json-rpc')
// Load the Clean Architecture Use Case libraries.
const UseCases = require('../use-cases')
// const useCases = new UseCases({ adapters })
// Load the REST API Controllers.
const RESTControllers = require('./rest-api')
class Controllers {
constructor (localConfig = {}) {
// Encapsulate dependencies
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)
@@ -33,13 +30,16 @@ class Controllers {
}
async attachControllers (app) {
// Wait for any startup processes to complete for the Adapters libraries.
await this.adapters.start()
// 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()
// Attach the REST controllers to the Koa app.
// this.attachRESTControllers(app)
// Attach the REST controllers to the Koa app.
// this.attachRESTControllers(app)
this.attachRPCControllers()
this.attachRPCControllers()
}
}
// Top-level function for this library.
+5
View File
@@ -1,5 +1,8 @@
/*
This is the JSON RPC router for the users API
CT 3/5/22: This library can probably be deleted. Handling of the /about endpoint
is now directly controlled by the ipfs-coord library.
*/
// Public npm libraries
@@ -33,6 +36,8 @@ class AboutRPC {
// This is a bit different than other router libraries, because there is
// only one response, which is a string about this node.
async aboutRouter (rpcData) {
console.log('debugging: aboutRouter from ipfs-service-provider triggered')
return {
success: true,
status: 200,
+14 -1
View File
@@ -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)
+160 -16
View File
@@ -271,10 +271,17 @@ class BchRESTControllerLib {
/**
*
* @api {post} /bch/transactions Transactions
* @apiName Transactions
* @api {post} /bch/txHistory TX History
* @apiName TX History
* @apiGroup REST BCH
* @apiDescription This endpoint wraps the bchjs.Electrumx.transactions([]) function.
* It returns the transaction history for an address. This list of TXIDs is
* sorted and paginated.
*
* There are three possible inputs:
* - address: (required) the address to query for a transaction history
* - sortOrder: (optional) will sort results in 'DECENDING' (default) or 'ASCENDING' order.
* - page: (optional) will return a 'page' of 100 results. Default is 0
*
* Given the 'addresses' property returns an array of objects
* with the following properties
@@ -289,7 +296,7 @@ class BchRESTControllerLib {
* Note: For a single address pass the 'addresses' of string type
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"] }' localhost:5001/bch/transactions
* curl -H "Content-Type: application/json" -X POST -d '{ "address": "bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj" }' localhost:5001/bch/txHistory
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
@@ -317,11 +324,20 @@ class BchRESTControllerLib {
* "error": "Unprocessable Entity"
* }
*/
async transactions (ctx) {
async txHistory (ctx) {
try {
const addr = ctx.request.body.address
// console.log('transactions REST API handler called.')
// console.log(`body: ${JSON.stringify(ctx.request.body, null, 2)}`)
const data = await this.adapters.bch.getTransactions(addr)
const addr = ctx.request.body.address
const sortOrder = ctx.request.body.sortOrder
const page = ctx.request.body.page
const data = await this.adapters.bch.getTransactions(
addr,
sortOrder,
page
)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
ctx.body = data
@@ -331,11 +347,14 @@ class BchRESTControllerLib {
}
/**
* @api {post} /bch/transaction Transaction
* @apiName Transaction
* @api {post} /bch/txData TX Data
* @apiName txData
* @apiGroup REST BCH
* @apiDescription Get data about a specific transaction.
* Given a transaction id this endpoint will return an object
* @apiDescription Get expanded transaction data for an array of transaction
* IDs. Each call is limited to 20 TXIDs or less.
*
* Get data about specific transactions.
* Given an array of transaction IDs this endpoint will return an array of objects
* with the following properties
*
* - txid: "" - Transaction ID
@@ -353,11 +372,11 @@ class BchRESTControllerLib {
* - isValidSLPTx: - Determines if the transaction was under SLP
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "txid": "01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b" }' localhost:5001/bch/transaction
* curl -H "Content-Type: application/json" -X POST -d '{ "txids": ["01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b"] }' localhost:5001/bch/transaction
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* [{
* "txid":"01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b",
* "hash":"01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b",
* "version":1,
@@ -406,7 +425,7 @@ class BchRESTControllerLib {
* "time":1568338904,
* "blocktime":1568338904,
* "isValidSLPTx":false
* }
* }]
*
* @apiError UnprocessableEntity Missing required parameters
*
@@ -417,11 +436,19 @@ class BchRESTControllerLib {
* "error": "Unprocessable Entity"
* }
*/
async transaction (ctx) {
async txData (ctx) {
try {
const txid = ctx.request.body.txid
console.log(
`txData called with this body data: ${JSON.stringify(
ctx.request.body,
null,
2
)}`
)
const data = await this.adapters.bch.getTransaction(txid)
const txids = ctx.request.body.txids
const data = await this.adapters.bch.getTransaction(txids)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
ctx.body = data
@@ -462,6 +489,8 @@ class BchRESTControllerLib {
* "pubkey": {
* "success": true,
* "publicKey": "033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee"
* }
* }
* }
* }
*/
@@ -478,6 +507,121 @@ 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
*
* - jsonrpc: "" - jsonrpc version
* - id: "" - jsonrpc id
* - result: {} - Result of the petition with the RPC information
* - success: - Request status
* - getTokenData: - Address public key
*
* @apiExample Example usage:
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "getTokenData", "tokenId": "c85042ab08a2099f27de880a30f9a42874202751d834c42717a20801a00aab0d" }}
*
* @apiSuccessExample {json} Success-Response:
* {
* "jsonrpc":"2.0",
* "id":"555",
* "result":{
* "method":"bch",
* "reciever":"QmU86vLVbUY1UhziKB6rak7GPKRA2QHWvzNm2AjEvXNsT6",
* "value":{
* "success": true,
* "status": 200,
* "endpoint": "getTokenData",
* "tokenData": {
* "genesisData": {
* "type": 1,
* "ticker": 'MT2',
* "name": 'Mutable Token',
* "tokenId": 'c85042ab08a2099f27de880a30f9a42874202751d834c42717a20801a00aab0d',
* "documentUri": 'ipfs://bafybeie7oxpsr7evcnlptecxfdhaqlot4732phukd2ekgvuzoir2frost4',
* "documentHash": '56ed1a5768076a318d02b5db64e125544dca57ab6b2cc7ca61dfa4645d244463',
* "decimals": 0,
* "mintBatonIsActive": true,
* "tokensInCirculationBN": '1000',
* "tokensInCirculationStr": '1000',
* "blockCreated": 739412,
* "totalBurned": '0',
* "totalMinted": '1000'
* },
* "immutableData": 'ipfs://bafybeie7oxpsr7evcnlptecxfdhaqlot4732phukd2ekgvuzoir2frost4',
* "mutableData": 'ipfs://bafybeigotuony53ley3n63hqwyxiqruqn5uamskmci6f645putnc46jju4'
* }
* }
* }
* }
*/
async getTokenData (ctx) {
try {
const tokenId = ctx.request.body.tokenId
const data = await this.adapters.bch.getTokenData(tokenId)
console.log(`getTokenData 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.
+16 -6
View File
@@ -56,9 +56,11 @@ class BchRouter {
this.router.post('/balance', this.postBalance)
this.router.post('/utxos', this.postUtxos)
this.router.post('/broadcast', this.postBroadcast)
this.router.post('/transactions', this.postTransactions)
this.router.post('/transaction', this.postTransaction)
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)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
@@ -85,17 +87,25 @@ class BchRouter {
await _this.bchRESTController.broadcast(ctx, next)
}
async postTransactions (ctx, next) {
await _this.bchRESTController.transactions(ctx, next)
async postTxHistory (ctx, next) {
await _this.bchRESTController.txHistory(ctx, next)
}
async postTransaction (ctx, next) {
await _this.bchRESTController.transaction(ctx, next)
async postTxData (ctx, next) {
await _this.bchRESTController.txData(ctx, next)
}
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)
}
}
module.exports = BchRouter
+5
View File
@@ -14,6 +14,7 @@ const LogsRESTController = require('./logs')
const BchRESTController = require('./bch')
const IpfsRESTController = require('./ipfs')
const P2wdbRESTController = require('./p2wdb')
const PriceRESTController = require('./price')
class RESTControllers {
constructor (localConfig = {}) {
@@ -67,6 +68,10 @@ class RESTControllers {
// Attach the REST API Controllers associated with the /p2wdb route
const p2wdbRESTController = new P2wdbRESTController(dependencies)
p2wdbRESTController.attach(app)
// Attach the REST API Controllers associated with the /price route
const priceRESTController = new PriceRESTController(dependencies)
priceRESTController.attach(app)
}
}
@@ -0,0 +1,83 @@
/*
REST API Controller library for the /bch route
*/
const axios = require('axios')
const { wlogger } = require('../../../adapters/wlogger')
// let _this
class PriceRESTControllerLib {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating /price REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating /price REST Controller.'
)
}
// Encapsulate dependencies
this.axios = axios
// this.UserModel = this.adapters.localdb.Users
// this.userUseCases = this.useCases.user
// _this = this
}
/**
* @api {get} /price/usd Get the USD price of BCH
* @apiName Get the USD price of BCH
* @apiGroup Price
* @apiDescription Get the USD price of BCH from Coinbase.
*
*
* @apiExample Example usage:
* curl -X GET "http://localhost:5005/price/usd" -H "accept: application/json"
*
*/
async getUSD (ctx) {
try {
// Request options
const opt = {
method: 'get',
baseURL: 'https://api.coinbase.com/v2/',
url: '/exchange-rates?currency=BCH',
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.rates.USD) }
} catch (err) {
// Write out error to error log.
wlogger.error('Error in GET /price/usd.', err)
this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
// If an HTTP status is specified by the buisiness logic, use that.
if (err.status) {
if (err.message) {
ctx.throw(err.status, err.message)
} else {
ctx.throw(err.status)
}
} else {
// By default use a 422 error if the HTTP status is not specified.
ctx.throw(422, err.message)
}
}
}
module.exports = PriceRESTControllerLib
+66
View File
@@ -0,0 +1,66 @@
/*
REST API library for /bch route.
*/
// Public npm libraries.
const Router = require('koa-router')
// Local libraries.
const PriceRESTControllerLib = require('./controller')
const Validators = require('../middleware/validators')
let _this
class PriceRouter {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating Price REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating Price REST Controller.'
)
}
const dependencies = {
adapters: this.adapters,
useCases: this.useCases
}
// Encapsulate dependencies.
this.priceRESTController = new PriceRESTControllerLib(dependencies)
this.validators = new Validators()
// Instantiate the router and set the base route.
const baseUrl = '/price'
this.router = new Router({ prefix: baseUrl })
_this = this
}
attach (app) {
if (!app) {
throw new Error(
'Must pass app object when attaching REST API controllers.'
)
}
// Define the routes and attach the controller.
this.router.get('/usd', this.getPrice)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
app.use(this.router.allowedMethods())
}
async getPrice (ctx, next) {
await _this.priceRESTController.getUSD(ctx, next)
}
}
module.exports = PriceRouter
+6 -6
View File
@@ -14,12 +14,12 @@ 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)
+3
View File
@@ -0,0 +1,3 @@
/key/swarm/psk/1.0.0/
/base16/
bbd935b70105b03ebd0c6a3c2d2730cd22fc0d18c490ccf689b6c8a22e1bed2a
+97 -38
View File
@@ -7,7 +7,7 @@
This test will query each REST API endpoint and ensure they are working correctly.
*/
const SERVER = 'http://localhost:5001'
const SERVER = 'http://localhost:5005'
const axios = require('axios')
const assert = require('chai').assert
@@ -23,8 +23,8 @@ describe('#BCH E2E Tests', () => {
const result = await axios.get(url)
// console.log(`data: ${JSON.stringify(result.data, null, 2)}`)
assert.isAbove(result.data.status.state.serviceProviders.length, 0)
assert.isOk(result.data.status.state.selectedServiceProvider)
assert.isAbove(result.data.status.serviceProviders.length, 0)
assert.isOk(result.data.status.selectedProvider)
})
})
@@ -56,9 +56,9 @@ describe('#BCH E2E Tests', () => {
const result = await axios.post(url, body)
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
assert.equal(result.data.address, body.address)
assert.property(result.data, 'bchUtxos')
assert.property(result.data, 'slpUtxos')
assert.equal(result.data[0].address, body.address)
assert.property(result.data[0], 'bchUtxos')
assert.property(result.data[0], 'slpUtxos')
})
})
@@ -74,42 +74,11 @@ describe('#BCH E2E Tests', () => {
assert.equal(result.data.status, 422)
assert.equal(result.data.success, false)
assert.equal(result.data.message, 'TX decode failed')
assert.equal(result.data.message, 'Missing inputs')
assert.equal(result.data.endpoint, 'broadcast')
})
})
describe('#transactions', () => {
it('should get transaction history for an address', async () => {
const body = {
address: 'bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj'
}
const url = `${SERVER}/bch/transactions`
const result = await axios.post(url, body)
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
assert.equal(result.data.status, 200)
assert.equal(result.data.success, true)
assert.property(result.data, 'transactions')
})
})
describe('#transaction', () => {
it('should get transaction details for a txid', async () => {
const body = {
txid: '01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
}
const url = `${SERVER}/bch/transaction`
const result = await axios.post(url, body)
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
assert.property(result.data, 'txid')
assert.property(result.data, 'vin')
})
})
describe('#pubkey', () => {
it('should return "not found" if pubkey is not on chain', async () => {
const body = {
@@ -146,4 +115,94 @@ describe('#BCH E2E Tests', () => {
)
})
})
describe('#txHistory', () => {
it('should get transaction history for an address, and sort by default in descending order.', async () => {
const address = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
const body = {
address
}
const url = `${SERVER}/bch/txHistory`
const result = await axios.post(url, body)
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
// Assert expected values and properties.
assert.equal(result.data.status, 200)
assert.equal(result.data.success, true)
assert.equal(result.data.address, address)
assert.isArray(result.data.txs)
// Assert expected sorting
assert.isAbove(result.data.txs[0].height, result.data.txs[1].height)
})
it('should sort in ascending order.', async () => {
const address = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
const body = {
address,
sortOrder: 'ASCENDING'
}
const url = `${SERVER}/bch/txHistory`
const result = await axios.post(url, body)
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
// Assert expected values and properties.
assert.equal(result.data.status, 200)
assert.equal(result.data.success, true)
assert.equal(result.data.address, address)
assert.isArray(result.data.txs)
// Assert expected sorting
assert.isAbove(result.data.txs[1].height, result.data.txs[0].height)
})
it('should paginate an address with deep tx history', async () => {
const body = {
address: 'bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d'
}
const url = `${SERVER}/bch/txHistory`
const result = await axios.post(url, body)
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
// Assert there is no more than 100 entries
assert.isBelow(result.data.txs.length, 101)
})
it('should get the second page of results', async () => {
const body = {
address: 'bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d',
page: 1
}
const url = `${SERVER}/bch/txHistory`
const result = await axios.post(url, body)
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
// Assert there is no more than 100 entries
assert.isBelow(result.data.txs.length, 101)
})
})
describe('#txData', () => {
it('should get transaction details for a txid', async () => {
const body = {
txids: [
'01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
]
}
const url = `${SERVER}/bch/txData`
const result = await axios.post(url, body)
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
assert.equal(result.data.status, 200)
assert.isArray(result.data.txData)
assert.property(result.data.txData[0], 'txid')
assert.property(result.data.txData[0], 'vin')
})
})
})
+73 -13
View File
@@ -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', () => {
@@ -284,4 +284,64 @@ 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')
}
})
})
})
+47 -36
View File
@@ -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', () => {
@@ -99,8 +109,9 @@ describe('#IPFS', () => {
}
},
adapters: {
orbit: {
privateLog: () => {}
pubsub: {
privateLog: () => {
}
}
}
}
@@ -121,7 +132,7 @@ describe('#IPFS', () => {
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
})
@@ -151,22 +162,22 @@ describe('#IPFS', () => {
})
})
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.')
})
})
// 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.')
// })
// })
})
+57 -23
View File
@@ -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
@@ -52,7 +85,8 @@ describe('#IPFS-adapter', () => {
it('should stop the IPFS node', async () => {
// Mock dependencies
uut.ipfs = {
stop: () => {}
stop: () => {
}
}
const result = await uut.stop()
@@ -61,24 +95,24 @@ describe('#IPFS-adapter', () => {
})
})
describe('#rmBlocksDir', () => {
it('should delete the /blocks directory', () => {
const result = uut.rmBlocksDir()
assert.equal(result, true)
})
it('should catch and throw an error', () => {
try {
// Force an error
sandbox.stub(uut.fs, 'rmdirSync').throws(new Error('test error'))
uut.rmBlocksDir()
assert.fail('Unexpected code path')
} catch (err) {
assert.equal(err.message, 'test error')
}
})
})
// describe('#rmBlocksDir', () => {
// it('should delete the /blocks directory', () => {
// const result = uut.rmBlocksDir()
//
// assert.equal(result, true)
// })
//
// it('should catch and throw an error', () => {
// try {
// // Force an error
// sandbox.stub(uut.fs, 'rmdirSync').throws(new Error('test error'))
//
// uut.rmBlocksDir()
//
// assert.fail('Unexpected code path')
// } catch (err) {
// assert.equal(err.message, 'test error')
// }
// })
// })
})
@@ -333,7 +333,7 @@ describe('#UserRPC', () => {
assert.equal(result.endpoint, 'updateUser')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.include(result.message, 'Cannot read property')
assert.include(result.message, 'Cannot read')
})
})
@@ -375,7 +375,7 @@ describe('#UserRPC', () => {
assert.equal(result.endpoint, 'getUser')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.include(result.message, 'Cannot read property')
assert.include(result.message, 'Cannot read')
})
})
@@ -213,7 +213,7 @@ describe('#BCH-REST-Controller', () => {
})
})
describe('#transactions', () => {
describe('#txHistory', () => {
it('should return 422 status on arbitrary error', async () => {
try {
// Force an error
@@ -225,7 +225,7 @@ describe('#BCH-REST-Controller', () => {
address: 'blah'
}
await uut.transactions(ctx)
await uut.txHistory(ctx)
assert.fail('Unexpected result')
} catch (err) {
@@ -242,14 +242,14 @@ describe('#BCH-REST-Controller', () => {
address: 'blah'
}
await uut.transactions(ctx)
await uut.txHistory(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
})
})
describe('#transaction', () => {
describe('#txData', () => {
it('should return 422 status on arbitrary error', async () => {
try {
// Force an error
@@ -261,7 +261,7 @@ describe('#BCH-REST-Controller', () => {
txid: 'blah'
}
await uut.transaction(ctx)
await uut.txData(ctx)
assert.fail('Unexpected result')
} catch (err) {
@@ -280,7 +280,7 @@ describe('#BCH-REST-Controller', () => {
txid: 'blah'
}
await uut.transaction(ctx)
await uut.txData(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
@@ -322,4 +322,76 @@ 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)
})
})
})
@@ -14,7 +14,8 @@ let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Contact', () => {
before(async () => {})
before(async () => {
})
beforeEach(() => {
uut = new ContactController()
@@ -36,7 +37,7 @@ describe('Contact', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -14,7 +14,8 @@ let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Logapi', () => {
before(async () => {})
before(async () => {
})
beforeEach(() => {
uut = new LogsApiController()
@@ -35,7 +36,7 @@ describe('Logapi', () => {
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -69,7 +69,7 @@ describe('#Users-REST-Controller', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -91,9 +91,9 @@ describe('#Users-REST-Controller', () => {
assert.property(ctx.response.body, 'user')
assert.property(ctx.response.body, 'token')
// Used by downstream tests.
// testUser = ctx.response.body.user
// console.log('testUser: ', testUser)
// Used by downstream tests.
// testUser = ctx.response.body.user
// console.log('testUser: ', testUser)
})
})
@@ -181,7 +181,7 @@ describe('#Users-REST-Controller', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -223,7 +223,7 @@ describe('#Users-REST-Controller', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
+8
View File
@@ -93,6 +93,14 @@ class BchUseCaseMock {
return {}
}
async utxoIsValid() {
return {}
}
async getTokenData() {
return {}
}
async waitForRPCResponse () {
return {}
}
+9 -9
View File
@@ -153,7 +153,7 @@ describe('#users-use-case', () => {
await uut.getAllUsers()
// console.log(`users: ${JSON.stringify(users, null, 2)}`)
// assert.isArray(users)
// assert.isArray(users)
})
it('should catch and throw an error', async () => {
@@ -228,8 +228,8 @@ describe('#users-use-case', () => {
// Assert that the expected properties for the user model exist.
// assert.property(result, 'type')
assert.property(result, '_id')
// assert.property(result, 'email')
// assert.property(result, 'name')
// assert.property(result, 'email')
// assert.property(result, 'name')
})
})
@@ -241,7 +241,7 @@ describe('#users-use-case', () => {
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -346,7 +346,7 @@ describe('#users-use-case', () => {
// assert.equal(result.name, 'testy tester')
// })
// TODO: verify that an admin can change the type of a user
// TODO: verify that an admin can change the type of a user
})
describe('#authUser', () => {
@@ -356,9 +356,9 @@ describe('#users-use-case', () => {
await uut.authUser('test@test.com', 'password')
// console.log('user: ', user)
// assert.property(user, '_id')
// assert.property(user, 'email')
// assert.property(user, 'name')
// assert.property(user, '_id')
// assert.property(user, 'email')
// assert.property(user, 'name')
})
it('should throw an error if no user matches the login', async () => {
@@ -399,7 +399,7 @@ describe('#users-use-case', () => {
assert.fail('Unexpected code path.')
} catch (err) {
// console.log(err)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})