mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service.git
synced 2026-09-21 16:52:03 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2f4369f8d | ||
|
|
bd33314c4f | ||
|
|
e6941f7f7f | ||
|
|
4fdf35a55f | ||
|
|
e6441a9c8c | ||
|
|
8a3265bff2 | ||
|
|
d871d8b392 | ||
|
|
f1fbe34b58 | ||
|
|
d232b6404d | ||
|
|
ea724f7ed8 | ||
|
|
2b04bae260 | ||
|
|
84f1e25648 | ||
|
|
ef8c6fbcec | ||
|
|
12d462b89d | ||
|
|
53065d8365 | ||
|
|
1bf8f756e5 | ||
|
|
1e513cc133 | ||
|
|
7ce53439b1 | ||
|
|
e90d664761 | ||
|
|
fc11e701e4 | ||
|
|
3c7ebef8a7 | ||
|
|
5d70c89f2d | ||
|
|
7149c0a824 | ||
|
|
74c6bc5737 | ||
|
|
bac6c8b1e9 | ||
|
|
9e7d955bc4 | ||
|
|
af7c4464bb | ||
|
|
cfb128f3c8 | ||
|
|
451a828a05 | ||
|
|
678bc7015c | ||
|
|
013aa97143 | ||
|
|
45b7e480b7 | ||
|
|
09772bae29 | ||
|
|
c01130fcc7 | ||
|
|
2d4159b4cf | ||
|
|
6dcd1ce5e7 | ||
|
|
6c913b2c36 | ||
|
|
c453705a80 | ||
|
|
bb9a2a2408 | ||
|
|
54709a8c70 | ||
|
|
64135b55a7 | ||
|
|
1ff4ba440d | ||
|
|
72da6203a1 | ||
|
|
e2bc560c7d | ||
|
|
e40241bac8 | ||
|
|
01c8c5c194 | ||
|
|
5eb5ef204e | ||
|
|
ee90bc0730 | ||
|
|
5af068e0d6 | ||
|
|
7e228b729e | ||
|
|
ec4c61f38e | ||
|
|
9ed610e3a4 | ||
|
|
a9e7fc616e | ||
|
|
9d5ffbe26e | ||
|
|
3829e90844 | ||
|
|
f181d8b095 | ||
|
|
a2270e70af | ||
|
|
34604d53b0 | ||
|
|
6257b96bc3 | ||
|
|
1d8912f60d | ||
|
|
3c9b63d009 | ||
|
|
e6f9f64b4a | ||
|
|
a8ad4149e0 | ||
|
|
3924812985 | ||
|
|
fef0f3a987 | ||
|
|
1c012322fd | ||
|
|
545e8aa670 | ||
|
|
449bd500c2 | ||
|
|
ea295f2f41 | ||
|
|
9175b8abe8 | ||
|
|
9d4a61ea99 | ||
|
|
cb50fdc70d | ||
|
|
cf488e1440 | ||
|
|
4fc6def1cd | ||
|
|
e0b2fd2525 | ||
|
|
8e08201b16 | ||
|
|
c347a2d99a | ||
|
|
5b7d8d4db4 | ||
|
|
dd0692e91f | ||
|
|
576da66194 | ||
|
|
f4851d6771 | ||
|
|
96abb0d56e | ||
|
|
fdf3141c91 | ||
|
|
6692d234da | ||
|
|
b8655e31ca | ||
|
|
e118fe4d6e | ||
|
|
86283195c5 | ||
|
|
a037646aca | ||
|
|
ae6bdcdd5f | ||
|
|
9fe822024c | ||
|
|
fc94041d17 | ||
|
|
a3f780b1f5 | ||
|
|
9487f19233 | ||
|
|
2988374769 | ||
|
|
22ab7260f4 | ||
|
|
27f6f62f8c | ||
|
|
33ab787a95 | ||
|
|
4f2cde2599 | ||
|
|
d419084216 | ||
|
|
553e0081fa | ||
|
|
0716922b7c | ||
|
|
7e227d994b | ||
|
|
dd1b702966 | ||
|
|
0963f89049 | ||
|
|
cda3f1c0a7 | ||
|
|
1247bcb8c0 | ||
|
|
36476b32ab | ||
|
|
630135ac1c | ||
|
|
0a86c7dc92 | ||
|
|
a8a08f8978 | ||
|
|
8b8d35573d | ||
|
|
36ed80212d | ||
|
|
131e6e1460 | ||
|
|
5c799a8197 | ||
|
|
488c5d3c22 | ||
|
|
e93ffc4c77 | ||
|
|
e4c4e324b8 | ||
|
|
e5e012a700 | ||
|
|
9bddcf1c8e | ||
|
|
ba2fdbc836 | ||
|
|
3f809d38fb | ||
|
|
d66d80bb0c | ||
|
|
4c707cf5b9 | ||
|
|
8670bdb588 | ||
|
|
4c5bdd089e | ||
|
|
c3eb1b1ad3 | ||
|
|
1e1e1558b4 | ||
|
|
d4abd693de | ||
|
|
d38718dcdf | ||
|
|
3a3376c303 | ||
|
|
05043ac74d | ||
|
|
5f89bfcbd8 | ||
|
|
d7d56d9ebf | ||
|
|
1592eadd8c | ||
|
|
be1a4c750a | ||
|
|
4572ce3d6b | ||
|
|
fb7d4e6074 | ||
|
|
5467c991de | ||
|
|
ba93c6cc46 | ||
|
|
60b37e4f72 | ||
|
|
02a543323c | ||
|
|
c28bc22084 | ||
|
|
f796dd6076 | ||
|
|
5d624f5c23 | ||
|
|
3eacfb27a6 | ||
|
|
29b3d32743 | ||
|
|
a58e237122 | ||
|
|
c237b33677 | ||
|
|
b48b618d4c | ||
|
|
252834d727 | ||
|
|
04bcd9fd3a | ||
|
|
cc5aba7896 | ||
|
|
50bc5c2d02 | ||
|
|
3251b0dc58 | ||
|
|
090f524b9d | ||
|
|
1846b5da64 | ||
|
|
643dfd8fc0 | ||
|
|
944d721296 | ||
|
|
96e4afd6c8 | ||
|
|
002e6657ff | ||
|
|
fb215b11b7 | ||
|
|
1618289078 | ||
|
|
2ebe42d5f4 | ||
|
|
326271f703 | ||
|
|
0480f54287 | ||
|
|
f9df8c7fab | ||
|
|
2ae550c027 | ||
|
|
eca131fefc | ||
|
|
596645ad6b | ||
|
|
2a2d940e7c | ||
|
|
46b0c595f9 | ||
|
|
eab7a30926 | ||
|
|
85a8efb5bf | ||
|
|
e33ee49879 | ||
|
|
9c53204a91 | ||
|
|
6faf1ccf86 | ||
|
|
8fafe96e01 | ||
|
|
d06c995174 | ||
|
|
2d02904ed1 | ||
|
|
51d7ddb362 | ||
|
|
ee1fbad02a | ||
|
|
f5ae98cb9c | ||
|
|
e416478522 | ||
|
|
ce85dffc97 | ||
|
|
c9f7a43339 | ||
|
|
a885fb7edf | ||
|
|
93d1542522 | ||
|
|
62fadbb394 | ||
|
|
ae37b6533b | ||
|
|
c711fd0ce1 | ||
|
|
05af23b87c | ||
|
|
54b505449b | ||
|
|
8e5f5f2dbd | ||
|
|
c06a9ec874 | ||
|
|
8639a3d363 | ||
|
|
a9ef1ebd66 | ||
|
|
1053191835 | ||
|
|
7d0b536f73 | ||
|
|
cf45a1fc7e | ||
|
|
cc2ada5169 | ||
|
|
cf4029ff00 | ||
|
|
76e4feb1ac | ||
|
|
4a5103d760 | ||
|
|
0b72895102 | ||
|
|
f41cac9fc7 | ||
|
|
8ad1400d3c | ||
|
|
e48a9213c5 | ||
|
|
60e6a8b24f | ||
|
|
85af08b1aa | ||
|
|
2d4bbd9ab0 | ||
|
|
06631a81ad | ||
|
|
24684ac25a | ||
|
|
758de6b088 | ||
|
|
0346898501 | ||
|
|
2e2d092b18 | ||
|
|
c8aefae0cc | ||
|
|
17e465b3ab | ||
|
|
40bb75bf33 | ||
|
|
02201afc59 |
@@ -7,6 +7,7 @@
|
||||
#logs
|
||||
logs/*.json
|
||||
*.log
|
||||
*.log.*
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
@@ -64,7 +65,10 @@ database/
|
||||
system-user-*.json
|
||||
orbitdb
|
||||
ipfsdata
|
||||
.ipfsdata
|
||||
ipfs-service-provider.sh
|
||||
start-bch-wallet-service.sh
|
||||
data/
|
||||
run-dev.sh
|
||||
|
||||
!README.md
|
||||
|
||||
@@ -29,13 +29,35 @@ This repository is one step in a longer path. Here are the major milestones of t
|
||||
|
||||
## Installation
|
||||
|
||||
### Development Environment
|
||||
|
||||
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)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service
|
||||
cd ipfs-bch-wallet-service
|
||||
./install-mongo.sh
|
||||
npm install
|
||||
npm start
|
||||
cd shell-scripts
|
||||
./ipfs-bch-wallet-service.sh
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
```
|
||||
docker-compose build --no-cache
|
||||
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.
|
||||
|
||||
## 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).
|
||||
@@ -77,3 +99,5 @@ Snapshots pinned to IPFS will be listed here.
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE.md)
|
||||
|
||||
a
|
||||
|
||||
+95
-71
@@ -1,3 +1,12 @@
|
||||
/*
|
||||
This Koa server has two interfaces:
|
||||
- REST API over HTTP
|
||||
- JSON RPC over IPFS
|
||||
|
||||
The architecture of the code follows the Clean Architecture pattern:
|
||||
https://troutsblog.com/blog/clean-architecture
|
||||
*/
|
||||
|
||||
// npm libraries
|
||||
const Koa = require('koa')
|
||||
const bodyParser = require('koa-bodyparser')
|
||||
@@ -12,82 +21,97 @@ const cors = require('kcors')
|
||||
|
||||
// Local libraries
|
||||
const config = require('../config') // this first.
|
||||
// const IPFSLib = require('../src/lib/ipfs')
|
||||
const AdminLib = require('../src/adapters/admin')
|
||||
const adminLib = new AdminLib()
|
||||
// const JSONRPC = require('../src/rpc')
|
||||
// const rpc = new JSONRPC()
|
||||
|
||||
const errorMiddleware = require('../src/controllers/rest-api/middleware/error')
|
||||
const { wlogger } = require('../src/adapters/wlogger')
|
||||
|
||||
async function startServer () {
|
||||
// Create a Koa instance.
|
||||
const app = new Koa()
|
||||
app.keys = [config.session]
|
||||
|
||||
// Connect to the Mongo Database.
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
console.log(
|
||||
`Connecting to MongoDB with this connection string: ${config.database}`
|
||||
)
|
||||
await mongoose.connect(config.database, {
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
})
|
||||
|
||||
// MIDDLEWARE START
|
||||
|
||||
app.use(convert(logger()))
|
||||
app.use(bodyParser())
|
||||
app.use(session())
|
||||
app.use(errorMiddleware())
|
||||
|
||||
// Used to generate the docs.
|
||||
app.use(mount('/', serve(`${process.cwd()}/docs`)))
|
||||
|
||||
// Mount the page for displaying logs.
|
||||
app.use(mount('/logs', serve(`${process.cwd()}/config/logs`)))
|
||||
|
||||
// User Authentication
|
||||
require('../config/passport')
|
||||
app.use(passport.initialize())
|
||||
app.use(passport.session())
|
||||
|
||||
// Attach REST API and JSON RPC controllersCt unstable to the app.
|
||||
const Controllers = require('../src/controllers')
|
||||
const controllers = new Controllers()
|
||||
controllers.attachControllers(app)
|
||||
|
||||
// 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}`)
|
||||
wlogger.info(`Running server in environment: ${config.env}`)
|
||||
|
||||
await app.listen(config.port)
|
||||
console.log(`Server started on ${config.port}`)
|
||||
|
||||
// Create the system admin user.
|
||||
try {
|
||||
const success = await adminLib.createSystemUser()
|
||||
if (success) console.log('System admin user created.')
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'Error trying to create system admin. Perhaps one already exists?'
|
||||
)
|
||||
class Server {
|
||||
constructor () {
|
||||
this.adminLib = new AdminLib()
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
// startServer()
|
||||
async startServer () {
|
||||
try {
|
||||
// Create a Koa instance.
|
||||
const app = new Koa()
|
||||
app.keys = [config.session]
|
||||
|
||||
// export default app
|
||||
// module.exports = app
|
||||
module.exports = {
|
||||
startServer
|
||||
// Connect to the Mongo Database.
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
console.log(
|
||||
`Connecting to MongoDB with this connection string: ${config.database}`
|
||||
)
|
||||
await mongoose.connect(config.database, {
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
})
|
||||
|
||||
console.log(`Starting environment: ${config.env}`)
|
||||
console.log(`Debug level: ${config.debugLevel}`)
|
||||
|
||||
// MIDDLEWARE START
|
||||
|
||||
app.use(convert(logger()))
|
||||
app.use(bodyParser())
|
||||
app.use(session())
|
||||
app.use(errorMiddleware())
|
||||
|
||||
// Used to generate the docs.
|
||||
app.use(mount('/', serve(`${process.cwd()}/docs`)))
|
||||
|
||||
// Mount the page for displaying logs.
|
||||
app.use(mount('/logs', serve(`${process.cwd()}/config/logs`)))
|
||||
|
||||
// User Authentication
|
||||
require('../config/passport')
|
||||
app.use(passport.initialize())
|
||||
app.use(passport.session())
|
||||
|
||||
// Attach REST API and JSON RPC controllers to the app.
|
||||
const Controllers = require('../src/controllers')
|
||||
const controllers = new Controllers()
|
||||
await controllers.attachRESTControllers(app)
|
||||
|
||||
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}`)
|
||||
wlogger.info(`Running server in environment: ${config.env}`)
|
||||
|
||||
await app.listen(config.port)
|
||||
console.log(`Server started on ${config.port}`)
|
||||
|
||||
// Create the system admin user.
|
||||
try {
|
||||
const success = await this.adminLib.createSystemUser()
|
||||
if (success) console.log('System admin user created.')
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'Error trying to create system admin. Perhaps one already exists?'
|
||||
)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
return app
|
||||
} catch (err) {
|
||||
console.error('Could not start server. Error: ', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Server
|
||||
|
||||
Vendored
+18
-5
@@ -13,6 +13,8 @@ const ipfsCoordName = process.env.COORD_NAME
|
||||
? process.env.COORD_NAME
|
||||
: 'ipfs-bch-wallet-service'
|
||||
|
||||
console.log('GET_JWT_AT_STARTUP: ', process.env.GET_JWT_AT_STARTUP)
|
||||
|
||||
module.exports = {
|
||||
// Configure TCP port.
|
||||
port: process.env.PORT || 5001,
|
||||
@@ -32,19 +34,24 @@ module.exports = {
|
||||
: 'emailpassword',
|
||||
|
||||
// FullStack.cash account information, used for automatic JWT handling.
|
||||
AUTHSERVER: process.env.AUTHSERVER
|
||||
getJwtAtStartup: process.env.GET_JWT_AT_STARTUP ? true : false,
|
||||
authServer: process.env.AUTHSERVER
|
||||
? process.env.AUTHSERVER
|
||||
: 'https://auth.fullstack.cash',
|
||||
APISERVER: process.env.APISERVER
|
||||
apiServer: process.env.APISERVER
|
||||
? process.env.APISERVER
|
||||
: 'https://api.fullstack.cash/v5/',
|
||||
FULLSTACKLOGIN: process.env.FULLSTACKLOGIN
|
||||
fullstackLogin: process.env.FULLSTACKLOGIN
|
||||
? process.env.FULLSTACKLOGIN
|
||||
: 'demo@demo.com',
|
||||
FULLSTACKPASS: process.env.FULLSTACKPASS ? process.env.FULLSTACKPASS : 'demo',
|
||||
fullstackPassword: process.env.FULLSTACKPASS
|
||||
? process.env.FULLSTACKPASS
|
||||
: 'demo',
|
||||
|
||||
// IPFS settings.
|
||||
isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false,
|
||||
// SSL domain used for websocket connection via browsers.
|
||||
crDomain: process.env.CR_DOMAIN ? process.env.CR_DOMAIN : '',
|
||||
|
||||
// Information passed to other IPFS peers about this node.
|
||||
apiInfo: 'https://ipfs-bch-wallet-service.fullstack.cash/',
|
||||
@@ -57,6 +64,7 @@ module.exports = {
|
||||
'@type': 'WebAPI',
|
||||
name: ipfsCoordName,
|
||||
version,
|
||||
protocol: 'bch-wallet',
|
||||
description:
|
||||
'IPFS service providing BCH blockchain access needed by a wallet.',
|
||||
documentation: 'https://ipfs-bch-wallet-service.fullstack.cash/',
|
||||
@@ -69,5 +77,10 @@ module.exports = {
|
||||
|
||||
// IPFS Ports
|
||||
ipfsTcpPort: process.env.IPFS_TCP_PORT ? process.env.IPFS_TCP_PORT : 5668,
|
||||
ipfsWsPort: process.env.IPFS_WS_PORT ? process.env.IPFS_WS_PORT : 5669
|
||||
ipfsWsPort: process.env.IPFS_WS_PORT ? process.env.IPFS_WS_PORT : 5669,
|
||||
|
||||
// BCH Mnemonic for generating encryption keys and payment address
|
||||
mnemonic: process.env.MNEMONIC ? process.env.MNEMONIC : '',
|
||||
|
||||
debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
This is where the mongodb docker container stores its db files.
|
||||
@@ -1,3 +1,4 @@
|
||||
const server = require('./bin/server.js')
|
||||
const Server = require('./bin/server.js')
|
||||
const server = new Server()
|
||||
|
||||
server.startServer()
|
||||
|
||||
Generated
+22717
-24107
File diff suppressed because it is too large
Load Diff
+44
-42
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ipfs-bch-wallet-service",
|
||||
"version": "1.9.0",
|
||||
"version": "1.11.12",
|
||||
"description": "IPFS and REST service providing blockchain access needed by a wallet. ",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -23,50 +23,52 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-service",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "^4.20.2",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"glob": "^7.1.6",
|
||||
"ipfs": "^0.54.4",
|
||||
"ipfs-coord": "^4.0.1",
|
||||
"jsonrpc-lite": "^2.2.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jwt-bch-lib": "^1.3.0",
|
||||
"kcors": "^2.2.2",
|
||||
"koa": "^2.13.1",
|
||||
"koa-bodyparser": "^4.3.0",
|
||||
"koa-convert": "^2.0.0",
|
||||
"koa-generic-session": "^2.1.1",
|
||||
"koa-logger": "^3.2.1",
|
||||
"koa-mount": "^4.0.0",
|
||||
"koa-passport": "^4.1.3",
|
||||
"koa-router": "^10.0.0",
|
||||
"koa-static": "^5.0.0",
|
||||
"koa2-ratelimit": "^0.9.0",
|
||||
"line-reader": "^0.4.0",
|
||||
"mongoose": "^5.11.15",
|
||||
"nodemailer": "^6.4.17",
|
||||
"passport-local": "^1.0.0",
|
||||
"winston": "^3.3.3",
|
||||
"winston-daily-rotate-file": "^4.5.0"
|
||||
"@chris.troutner/ipfs": "2.0.2",
|
||||
"@psf/bch-js": "4.20.28",
|
||||
"axios": "0.21.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"glob": "7.1.6",
|
||||
"ipfs-coord": "6.7.6",
|
||||
"jsonrpc-lite": "2.2.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jwt-bch-lib": "1.3.0",
|
||||
"kcors": "2.2.2",
|
||||
"koa": "2.13.1",
|
||||
"koa-bodyparser": "4.3.0",
|
||||
"koa-convert": "2.0.0",
|
||||
"koa-generic-session": "2.1.1",
|
||||
"koa-logger": "3.2.1",
|
||||
"koa-mount": "4.0.0",
|
||||
"koa-passport": "4.1.3",
|
||||
"koa-router": "10.0.0",
|
||||
"koa-static": "5.0.0",
|
||||
"koa2-ratelimit": "0.9.0",
|
||||
"line-reader": "0.4.0",
|
||||
"mongoose": "5.11.15",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.4.17",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "4.0.4",
|
||||
"winston": "3.3.3",
|
||||
"winston-daily-rotate-file": "4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apidoc": "^0.26.0",
|
||||
"chai": "^4.3.0",
|
||||
"coveralls": "^3.1.0",
|
||||
"apidoc": "0.26.0",
|
||||
"chai": "4.3.0",
|
||||
"coveralls": "3.1.0",
|
||||
"eslint": "7.19.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"husky": "^4.3.8",
|
||||
"mocha": "^8.2.1",
|
||||
"nyc": "^15.1.0",
|
||||
"semantic-release": "^17.4.4",
|
||||
"sinon": "^9.2.4",
|
||||
"standard": "^16.0.3",
|
||||
"uuid": "^8.3.2"
|
||||
"eslint-config-prettier": "7.2.0",
|
||||
"eslint-config-standard": "16.0.2",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-prettier": "3.3.1",
|
||||
"eslint-plugin-standard": "4.0.0",
|
||||
"husky": "4.3.8",
|
||||
"mocha": "8.2.1",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "17.4.4",
|
||||
"sinon": "9.2.4",
|
||||
"standard": "16.0.3",
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
"release": {
|
||||
"publish": [
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
# Create a Dockerized API server
|
||||
#
|
||||
|
||||
#IMAGE BUILD COMMANDS
|
||||
# ct-base-ubuntu = ubuntu 18.04 + nodejs v10 LTS
|
||||
FROM christroutner/ct-base-ubuntu
|
||||
MAINTAINER Chris Troutner <chris.troutner@gmail.com>
|
||||
|
||||
#Create the user 'safeuser' and add them to the sudo group.
|
||||
#RUN useradd -ms /bin/bash safeuser
|
||||
#RUN adduser safeuser sudo
|
||||
|
||||
#Set password to 'password' change value below if you want a different password
|
||||
#RUN echo safeuser:password | chpasswd
|
||||
|
||||
#Set the working directory to be the home directory
|
||||
WORKDIR /home/safeuser
|
||||
|
||||
#Setup NPM for non-root global install
|
||||
#RUN mkdir /home/safeuser/.npm-global
|
||||
#RUN chown -R safeuser .npm-global
|
||||
#RUN echo "export PATH=~/.npm-global/bin:$PATH" >> /home/safeuser/.profile
|
||||
#RUN runuser -l safeuser -c "npm config set prefix '~/.npm-global'"
|
||||
|
||||
# Switch to user account.
|
||||
USER safeuser
|
||||
# Prep 'sudo' commands.
|
||||
RUN echo 'abcd8765' | sudo -S pwd
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service
|
||||
# RUN mv koa-api-boilerplate koa
|
||||
# RUN mkdir keys
|
||||
|
||||
# Switch to the desired branch. `master` is usually stable,
|
||||
# and `stage` has the most up-to-date changes.
|
||||
WORKDIR /home/safeuser/ipfs-bch-wallet-service
|
||||
|
||||
# For development: switch to unstable branch
|
||||
# RUN git checkout unstable
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Generate the API docs
|
||||
RUN npm run docs
|
||||
|
||||
# Calling out the directorys that should be mounted as external volumes.
|
||||
RUN mkdir /home/safeuser/ipfsdata
|
||||
RUN mkdir /home/safeuser/orbitdb
|
||||
VOLUME /home/safeuser/ipfsdata
|
||||
VOLUME /home/safeuser/orbitdb
|
||||
|
||||
# Start the application.
|
||||
COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
#CMD ["npm", "start"]
|
||||
@@ -1,28 +0,0 @@
|
||||
# Start the testnet server with the command 'docker-compose up -d'
|
||||
|
||||
bch-wallet-mongodb:
|
||||
image: mongo
|
||||
container_name: mongo-bch-service
|
||||
ports:
|
||||
- '5555:27017' # <host port>:<container port>
|
||||
volumes:
|
||||
- ./database:/data/db
|
||||
command: mongod --logpath=/dev/null # -- quiet
|
||||
restart: always
|
||||
|
||||
bch-wallet-service:
|
||||
build: ./
|
||||
dockerfile: Dockerfile
|
||||
container_name: bch-wallet-service
|
||||
links:
|
||||
- bch-wallet-mongodb
|
||||
ports:
|
||||
# <host port>:<container port>
|
||||
- '5001:5001' # REST API
|
||||
- '5668:5668' # IPFS TCP Port
|
||||
- '5669:5669' # IPFS WS Port
|
||||
volumes:
|
||||
- ../ipfsdata:/home/safeuser/ipfsdata
|
||||
- ../orbitdb:/home/safeuser/orbitdb
|
||||
|
||||
restart: always
|
||||
@@ -0,0 +1,77 @@
|
||||
# Create a Dockerized API server
|
||||
#
|
||||
|
||||
#IMAGE BUILD COMMANDS
|
||||
FROM ubuntu:20.04
|
||||
MAINTAINER Chris Troutner <chris.troutner@gmail.com>
|
||||
|
||||
#Update the OS and install any OS packages needed.
|
||||
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 bash nodesource_setup.sh
|
||||
RUN apt-get install -y nodejs build-essential
|
||||
|
||||
#Create the user 'safeuser' and add them to the sudo group.
|
||||
RUN useradd -ms /bin/bash safeuser
|
||||
RUN adduser safeuser sudo
|
||||
|
||||
#Set password to 'password' change value below if you want a different password
|
||||
RUN echo safeuser:password | chpasswd
|
||||
|
||||
#Set the working directory to be the home directory
|
||||
WORKDIR /home/safeuser
|
||||
|
||||
#Setup NPM for non-root global install
|
||||
RUN mkdir /home/safeuser/.npm-global
|
||||
RUN chown -R safeuser .npm-global
|
||||
RUN echo "export PATH=~/.npm-global/bin:$PATH" >> /home/safeuser/.profile
|
||||
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
|
||||
|
||||
# Update to the latest version of npm.
|
||||
# Working with npm@7.21.1
|
||||
RUN npm install -g npm@7.23.0
|
||||
|
||||
# npm mirror to prevent direct dependency on npm.
|
||||
RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Switch to user account.
|
||||
#USER safeuser
|
||||
# Prep 'sudo' commands.
|
||||
#RUN echo 'abcd8765' | sudo -S pwd
|
||||
|
||||
# Clone the repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service
|
||||
|
||||
# Switch to the desired branch. `master` is usually stable,
|
||||
# and `stage` has the most up-to-date changes.
|
||||
WORKDIR /home/safeuser/ipfs-bch-wallet-service
|
||||
|
||||
# For development: switch to unstable branch
|
||||
RUN git checkout ct-unstable
|
||||
|
||||
# Install dependencies
|
||||
#RUN npm install -g @mapbox/node-pre-gyp
|
||||
RUN npm install
|
||||
|
||||
# Generate the API docs
|
||||
RUN npm run docs
|
||||
|
||||
# Calling out the directorys that should be mounted as external volumes.
|
||||
RUN mkdir /home/safeuser/.ipfsdata
|
||||
#RUN mkdir /home/safeuser/orbitdb
|
||||
#VOLUME /home/safeuser/ipfsdata
|
||||
#VOLUME /home/safeuser/orbitdb
|
||||
|
||||
# Start the application.
|
||||
COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
#CMD ["npm", "start"]
|
||||
@@ -0,0 +1,32 @@
|
||||
# Start the service with the command 'docker-compose up -d'
|
||||
|
||||
version: '2'
|
||||
services:
|
||||
bch-wallet-mongodb:
|
||||
image: mongo
|
||||
container_name: mongo-bch-service
|
||||
ports:
|
||||
- '5555:27017' # <host port>:<container port>
|
||||
volumes:
|
||||
- ../data/mongodb:/data/db
|
||||
command: mongod --logpath=/dev/null # -- quiet
|
||||
restart: always
|
||||
|
||||
bch-wallet-service:
|
||||
build: ./
|
||||
container_name: bch-wallet-service
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '10'
|
||||
mem_limit: 1000mb
|
||||
links:
|
||||
- bch-wallet-mongodb
|
||||
ports:
|
||||
# <host port>:<container port>
|
||||
- '5001:5001' # REST API
|
||||
- '5668:5668' # IPFS TCP Port
|
||||
- '5669:5669' # IPFS WS Port
|
||||
volumes:
|
||||
- ../data/ipfsdata:/home/safeuser/ipfs-bch-wallet-service/.ipfsdata
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
# BEGIN: Optional configuration settings
|
||||
|
||||
# This mnemonic is used to set up persistent public key for e2ee
|
||||
# Replace this with your own 12-word mnemonic.
|
||||
# You can get one at https://wallet.fullstack.cash.
|
||||
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=bch-wallet-service-generic
|
||||
|
||||
# Allow this node to function as a circuit relay. It must not be behind a firewall.
|
||||
#export ENABLE_CIRCUIT_RELAY=true
|
||||
# For browsers to use your circuit realy, you must set up a domain, SSL certificate,
|
||||
# and you must forward that subdomain to the IPFS_WS_PORT.
|
||||
#export CR_DOMAIN=subdomain.yourdomain.com
|
||||
|
||||
# Debug level. 0 = minimal info. 2 = max info.
|
||||
export DEBUG_LEVEL=1
|
||||
|
||||
# END: Optional configuration settings
|
||||
|
||||
|
||||
# Production database connection string.
|
||||
export DBURL=mongodb://172.17.0.1:5555/ipfs-service-prod
|
||||
|
||||
# Customize these environment variables for your own installation.
|
||||
export GET_JWT_AT_STARTUP=1
|
||||
export AUTHSERVER=https://auth.fullstack.cash
|
||||
export APISERVER=https://api.fullstack.cash/v5/
|
||||
export FULLSTACKLOGIN=demo@demo.com
|
||||
export FULLSTACKPASS=demo
|
||||
|
||||
# Ports
|
||||
export PORT=5001 # REST API
|
||||
export IPFS_TCP_PORT=5668 # IPSF TCP Port
|
||||
export IPFS_WS_PORT=5669 # IPFS WS Port
|
||||
|
||||
export SVC_ENV=production
|
||||
npm start
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Customize these environment variables for your own installation.
|
||||
export AUTHSERVER=https://auth.fullstack.cash
|
||||
export APISERVER=https://api.fullstack.cash/v5/
|
||||
export FULLSTACKLOGIN=demo@demo.com
|
||||
export FULLSTACKPASS=demo
|
||||
|
||||
# Ports
|
||||
export PORT=5001 # REST API
|
||||
export IPFS_TCP_PORT=5668 # IPSF TCP Port
|
||||
export IPFS_WS_PORT=5669 # IPFS WS Port
|
||||
|
||||
# Production database connection string.
|
||||
export DBURL=mongodb://172.17.0.1:5555/ipfs-service-prod
|
||||
|
||||
#export ENABLE_CIRCUIT_RELAY=1
|
||||
|
||||
export SVC_ENV=production
|
||||
npm start
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is an example for running a generic ipfs-service-provider instance
|
||||
# using a local installation of bch-api.
|
||||
|
||||
# Ports
|
||||
export PORT=5002 # REST API port
|
||||
export IPFS_TCP_PORT=5268
|
||||
export IPFS_WS_PORT=5269
|
||||
|
||||
# The human-readible name that is used when displaying data about this node.
|
||||
export COORD_NAME=ipfs-service-provider-generic
|
||||
|
||||
# This is used for end-to-end encryption (e2ee).
|
||||
export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran"
|
||||
|
||||
# 0 = less verbose. 3 = most verbose
|
||||
export DEBUG_LEVEL=1
|
||||
|
||||
# MongoDB connection string.
|
||||
#export DBURL=mongodb://localhost:27017/bch-service-dev
|
||||
|
||||
# Customize these environment variables for your own installation.
|
||||
export GET_JWT_AT_STARTUP=0
|
||||
export APISERVER=http://localhost:3000/v5/
|
||||
|
||||
|
||||
npm start
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is an example for running a generic ipfs-service-provider instance.
|
||||
|
||||
# Ports
|
||||
export PORT=5002 # REST API port
|
||||
export IPFS_TCP_PORT=5268
|
||||
export IPFS_WS_PORT=5269
|
||||
|
||||
# The human-readible name that is used when displaying data about this node.
|
||||
export COORD_NAME=ipfs-service-provider-generic
|
||||
|
||||
# This is used for end-to-end encryption (e2ee).
|
||||
export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran"
|
||||
|
||||
# 0 = less verbose. 3 = most verbose
|
||||
export DEBUG_LEVEL=1
|
||||
|
||||
# MongoDB connection string.
|
||||
#export DBURL=mongodb://localhost:27017/bch-service-dev
|
||||
|
||||
npm start
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is an example for running a generic ipfs-service-provider instance.
|
||||
|
||||
# Ports
|
||||
export PORT=5001 # REST API port
|
||||
export IPFS_TCP_PORT=5268
|
||||
export IPFS_WS_PORT=5269
|
||||
|
||||
# The human-readible name that is used when displaying data about this node.
|
||||
export COORD_NAME=ipfs-service-provider-generic
|
||||
|
||||
# This is used for end-to-end encryption (e2ee).
|
||||
export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran"
|
||||
|
||||
# 0 = less verbose. 3 = most verbose
|
||||
export DEBUG_LEVEL=1
|
||||
|
||||
# MongoDB connection string.
|
||||
#export DBURL=mongodb://localhost:27017/bch-service-dev
|
||||
|
||||
npm start
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is an example for running a ipfs-service-provider as a Circuit Relay.
|
||||
# Circuit Relays are help other nodes on the network communicate. They are
|
||||
# critical for reliable functioning of the network, and for circumventing
|
||||
# censorship.
|
||||
|
||||
# Ports
|
||||
export PORT=5001 # REST API port
|
||||
export IPFS_TCP_PORT=5268
|
||||
export IPFS_WS_PORT=5269
|
||||
|
||||
# The human-readible name that is used when displaying data about this node.
|
||||
export COORD_NAME=ipfs-service-provider-generic
|
||||
|
||||
# This is used for end-to-end encryption (e2ee).
|
||||
export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran"
|
||||
|
||||
# 0 = less verbose. 3 = most verbose
|
||||
export DEBUG_LEVEL=1
|
||||
|
||||
# MongoDB connection string.
|
||||
#export DBURL=mongodb://localhost:27017/bch-service-dev
|
||||
|
||||
# Comment to disable circuit relay functionality. Or set to 1 to enable.
|
||||
export ENABLE_CIRCUIT_RELAY=1
|
||||
# For browsers to use your circuit realy, you must set up a domain, SSL certificate,
|
||||
# and you must forward that subdomain to the IPFS_WS_PORT.
|
||||
#export CR_DOMAIN=subdomain.yourdomain.com
|
||||
|
||||
npm start
|
||||
@@ -23,13 +23,13 @@ class FullStackJWT {
|
||||
'Must pass a url for the API server when instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
this.login = localConfig.login
|
||||
this.login = localConfig.fullstackLogin
|
||||
if (!this.login || typeof this.login !== 'string') {
|
||||
throw new Error(
|
||||
'Must pass a FullStack.cash login (email) instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
this.password = localConfig.password
|
||||
this.password = localConfig.fullstackPassword
|
||||
if (!this.password || typeof this.password !== 'string') {
|
||||
throw new Error(
|
||||
'Must pass a FullStack.cash account password when instantiating FullStackJWT class.'
|
||||
@@ -39,7 +39,7 @@ class FullStackJWT {
|
||||
// Encapsulate dependencies
|
||||
this.jwtLib = new JwtLib({
|
||||
// Overwrite default values with the values in the config file.
|
||||
server: this.server,
|
||||
server: this.authServer,
|
||||
login: this.login,
|
||||
password: this.password
|
||||
})
|
||||
@@ -53,7 +53,7 @@ class FullStackJWT {
|
||||
async getJWT () {
|
||||
try {
|
||||
// Skip connecting FullStack.cash auth server to the network if this is an E2E test.
|
||||
if (process.env.E2ETEST) {
|
||||
if (process.env.TEST_TYPE === 'e2e') {
|
||||
this.apiToken = 'faketoken'
|
||||
return this.apiToken
|
||||
}
|
||||
|
||||
+40
-28
@@ -4,8 +4,8 @@
|
||||
https://troutsblog.com/blog/clean-architecture
|
||||
*/
|
||||
|
||||
// Load the config file
|
||||
const config = require('../../config')
|
||||
// Public NPM libraries
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
|
||||
// Load individual adapter libraries.
|
||||
const IPFSAdapter = require('./ipfs')
|
||||
@@ -13,35 +13,47 @@ const LocalDB = require('./localdb')
|
||||
const LogsAPI = require('./logapi')
|
||||
const Passport = require('./passport')
|
||||
const Nodemailer = require('./nodemailer')
|
||||
const { wlogger } = require('./wlogger')
|
||||
// const { wlogger } = require('./wlogger')
|
||||
const JSONFiles = require('./json-files')
|
||||
const FullStackJWT = require('./fullstack-jwt')
|
||||
|
||||
// Instantiate adapter libraries.
|
||||
const ipfs = new IPFSAdapter()
|
||||
const localdb = new LocalDB()
|
||||
const logapi = new LogsAPI()
|
||||
const passport = new Passport()
|
||||
const nodemailer = new Nodemailer()
|
||||
const jsonFiles = new JSONFiles()
|
||||
const config = require('../../config')
|
||||
|
||||
// Get a valid JWT API key and instance bch-js.
|
||||
const fullStackJwt = new FullStackJWT({
|
||||
authServer: config.AUTHSERVER,
|
||||
apiServer: config.APISERVER,
|
||||
login: config.FULLSTACKLOGIN,
|
||||
password: config.FULLSTACKPASS
|
||||
})
|
||||
const bchjs = {} // Placeholder.
|
||||
class Adapters {
|
||||
constructor (localConfig = {}) {
|
||||
// Encapsulate dependencies
|
||||
this.ipfs = new IPFSAdapter()
|
||||
this.localdb = new LocalDB()
|
||||
this.logapi = new LogsAPI()
|
||||
this.passport = new Passport()
|
||||
this.nodemailer = new Nodemailer()
|
||||
this.jsonFiles = new JSONFiles()
|
||||
this.bchjs = new BCHJS()
|
||||
this.config = config
|
||||
|
||||
module.exports = {
|
||||
ipfs,
|
||||
localdb,
|
||||
logapi,
|
||||
passport,
|
||||
nodemailer,
|
||||
wlogger,
|
||||
jsonFiles,
|
||||
fullStackJwt,
|
||||
bchjs
|
||||
// Get a valid JWT API key and instance bch-js.
|
||||
this.fullStackJwt = new FullStackJWT(config)
|
||||
}
|
||||
|
||||
async start () {
|
||||
try {
|
||||
if (this.config.getJwtAtStartup) {
|
||||
// Get a JWT token and instantiate bch-js with it. Then pass that instance
|
||||
// to all the rest of the apps controllers and adapters.
|
||||
await this.fullStackJwt.getJWT()
|
||||
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
|
||||
this.bchjs = await this.fullStackJwt.instanceBchjs()
|
||||
} else {
|
||||
this.bchjs = new BCHJS({ restURL: this.config.apiServer })
|
||||
}
|
||||
|
||||
// Start the IPFS node.
|
||||
await this.ipfs.start({ bchjs: this.bchjs })
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/index.js/start()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Adapters
|
||||
|
||||
@@ -10,6 +10,7 @@ class IPFS {
|
||||
// Encapsulate dependencies
|
||||
this.ipfsAdapter = new IpfsAdapter()
|
||||
this.IpfsCoordAdapter = IpfsCoordAdapter
|
||||
this.process = process
|
||||
|
||||
this.ipfsCoordAdapter = {} // placeholder
|
||||
|
||||
@@ -46,6 +47,14 @@ class IPFS {
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/ipfs/index.js/start()')
|
||||
|
||||
// If error is due to a lock file issue. Kill the process, so that
|
||||
// Docker or pm2 has a chance to restart the service.
|
||||
if (err.message.includes('Lock already being held')) {
|
||||
console.log('lock being held. Exiting.')
|
||||
this.process.exit(1)
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
// Global npm libraries
|
||||
const IpfsCoord = require('ipfs-coord')
|
||||
// const BCHJS = require('@psf/bch-js')
|
||||
const publicIp = require('public-ip')
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
@@ -35,6 +36,7 @@ class IpfsCoordAdapter {
|
||||
this.ipfsCoord = {}
|
||||
// this.rpc = new JSONRPC()
|
||||
this.config = config
|
||||
this.publicIp = publicIp
|
||||
|
||||
// Properties of this class instance.
|
||||
this.isReady = false
|
||||
@@ -43,6 +45,24 @@ class IpfsCoordAdapter {
|
||||
}
|
||||
|
||||
async start (localConfig = {}) {
|
||||
const circuitRelayInfo = {}
|
||||
|
||||
// If configured as a Circuit Relay, get the public IP addresses for this node.
|
||||
if (this.config.isCircuitRelay) {
|
||||
try {
|
||||
const ip4 = await this.publicIp.v4()
|
||||
// const ip6 = await publicIp.v6()
|
||||
|
||||
circuitRelayInfo.ip4 = ip4
|
||||
circuitRelayInfo.tcpPort = this.config.ipfsTcpPort
|
||||
|
||||
// Domain used by browser-based secure websocket connections.
|
||||
circuitRelayInfo.crDomain = this.config.crDomain
|
||||
} catch (err) {
|
||||
/* exit quietly */
|
||||
}
|
||||
}
|
||||
|
||||
this.ipfsCoord = new this.IpfsCoord({
|
||||
ipfs: this.ipfs,
|
||||
type: 'node.js',
|
||||
@@ -50,16 +70,15 @@ class IpfsCoordAdapter {
|
||||
bchjs: this.bchjs,
|
||||
privateLog: console.log, // Default to console.log
|
||||
isCircuitRelay: this.config.isCircuitRelay,
|
||||
circuitRelayInfo,
|
||||
apiInfo: this.config.apiInfo,
|
||||
announceJsonLd: this.config.announceJsonLd
|
||||
announceJsonLd: this.config.announceJsonLd,
|
||||
mnemonic: this.config.mnemonic,
|
||||
debugLevel: this.config.debugLevel
|
||||
})
|
||||
|
||||
// Skip connecting ipfs-coord to the network if this is an E2E test.
|
||||
if (!process.env.E2ETEST) {
|
||||
// Wait for the ipfs-coord library to signal that it is ready.
|
||||
await this.ipfsCoord.ipfs.start()
|
||||
await this.ipfsCoord.isReady()
|
||||
}
|
||||
// Wait for the ipfs-coord library to signal that it is ready.
|
||||
await this.ipfsCoord.start({ type: 'node.js' })
|
||||
|
||||
// Signal that this adapter is ready.
|
||||
this.isReady = true
|
||||
@@ -72,7 +91,7 @@ class IpfsCoordAdapter {
|
||||
attachRPCRouter (router) {
|
||||
try {
|
||||
_this.ipfsCoord.privateLog = router
|
||||
_this.ipfsCoord.ipfs.orbitdb.privateLog = router
|
||||
_this.ipfsCoord.adapters.orbit.privateLog = router
|
||||
} catch (err) {
|
||||
console.error('Error in attachRPCRouter()')
|
||||
throw err
|
||||
|
||||
+53
-13
@@ -2,14 +2,22 @@
|
||||
Clean Architecture Adapter for IPFS.
|
||||
This library deals with IPFS so that the apps business logic doesn't need
|
||||
to have any specific knowledge of the js-ipfs library.
|
||||
|
||||
TODO: Add the external IP address to the list of multiaddrs advertised by
|
||||
this node. See this GitHub Issue for details:
|
||||
https://github.com/Permissionless-Software-Foundation/ipfs-service-provider/issues/38
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const IPFS = require('ipfs')
|
||||
// const IPFS = require('ipfs')
|
||||
const IPFS = require('@chris.troutner/ipfs')
|
||||
const fs = require('fs')
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
|
||||
const IPFS_DIR = './.ipfsdata/ipfs'
|
||||
|
||||
class IpfsAdapter {
|
||||
constructor (localConfig) {
|
||||
// Encapsulate dependencies
|
||||
@@ -18,6 +26,7 @@ class IpfsAdapter {
|
||||
// Properties of this class instance.
|
||||
this.isReady = false
|
||||
this.config = config
|
||||
this.fs = fs
|
||||
}
|
||||
|
||||
// Start an IPFS node.
|
||||
@@ -25,13 +34,13 @@ class IpfsAdapter {
|
||||
try {
|
||||
// Ipfs Options
|
||||
const ipfsOptions = {
|
||||
repo: './ipfsdata',
|
||||
repo: IPFS_DIR,
|
||||
start: true,
|
||||
config: {
|
||||
relay: {
|
||||
enabled: true, // enable circuit relay dialer and listener
|
||||
hop: {
|
||||
enabled: true // enable circuit relay HOP (make this node a relay)
|
||||
enabled: config.isCircuitRelay // enable circuit relay HOP (make this node a relay)
|
||||
}
|
||||
},
|
||||
pubsub: true, // enable pubsub
|
||||
@@ -46,6 +55,11 @@ class IpfsAdapter {
|
||||
`/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`,
|
||||
`/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`
|
||||
]
|
||||
},
|
||||
Datastore: {
|
||||
StorageMax: '2GB',
|
||||
StorageGCWatermark: 50,
|
||||
GCPeriod: '15m'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,16 +70,9 @@ class IpfsAdapter {
|
||||
// Set the 'server' profile so the node does not scan private networks.
|
||||
await this.ipfs.config.profiles.apply('server')
|
||||
|
||||
// const nodeConfig = await this.ipfs.config.getAll()
|
||||
// console.log(
|
||||
// `IPFS node configuration: ${JSON.stringify(nodeConfig, null, 2)}`
|
||||
// )
|
||||
|
||||
// Stop the IPFS node if we're running tests.
|
||||
if (this.config.env === 'test') {
|
||||
console.log('Stopping IPFS for tests.')
|
||||
await this.ipfs.stop()
|
||||
}
|
||||
// Debugging: Display IPFS config settings.
|
||||
// const configSettings = await this.ipfs.config.getAll()
|
||||
// console.log(`configSettings: ${JSON.stringify(configSettings, null, 2)}`)
|
||||
|
||||
// Signal that this adapter is ready.
|
||||
this.isReady = true
|
||||
@@ -73,6 +80,39 @@ class IpfsAdapter {
|
||||
return this.ipfs
|
||||
} catch (err) {
|
||||
console.error('Error in ipfs.js/start()')
|
||||
|
||||
// If IPFS crashes because the /blocks directory is full, wipe the directory.
|
||||
if (err.message.includes('No space left on device')) {
|
||||
console.log('No space left in /blocks folder. Deleting blocks folder.')
|
||||
this.rmBlocksDir()
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async stop () {
|
||||
await this.ipfs.stop()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Remove the '/blocks' directory that is used to store IPFS data.
|
||||
// Dev Note: It's assumed this node is not pinning any data and that
|
||||
// everything in this directory is transient. This folder will regularly
|
||||
// fill up and prevent IPFS from starting.
|
||||
rmBlocksDir () {
|
||||
try {
|
||||
const dir = `${IPFS_DIR}/blocks`
|
||||
console.log(`Deleting ${dir} directory...`)
|
||||
|
||||
this.fs.rmdirSync(dir, { recursive: true })
|
||||
|
||||
console.log(`${dir} directory is deleted!`)
|
||||
|
||||
return true // Signal successful execution.
|
||||
} catch (err) {
|
||||
console.log('Error in rmBlocksDir()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
+4
-35
@@ -62,42 +62,11 @@ class Wlogger {
|
||||
}
|
||||
}
|
||||
|
||||
// transport.on('rotate', notifyRotation)
|
||||
|
||||
// function notifyRotation (oldFilename, newFilename) {
|
||||
// wlogger.info('Rotating log files')
|
||||
// }
|
||||
|
||||
// This controls what goes into the log FILES
|
||||
// const wlogger = winston.createLogger({
|
||||
// level: 'verbose',
|
||||
// format: winston.format.json(),
|
||||
// transports: [
|
||||
// //
|
||||
// // - Write to all logs with level `info` and below to `combined.log`
|
||||
// // - Write all logs error (and below) to `error.log`.
|
||||
// //
|
||||
// // new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
|
||||
// // new winston.transports.File({ filename: 'logs/combined.log' })
|
||||
// transport
|
||||
// ]
|
||||
// })
|
||||
|
||||
// function outputToConsole () {
|
||||
// wlogger.add(
|
||||
// new winston.transports.Console({
|
||||
// format: winston.format.simple(),
|
||||
// level: 'info'
|
||||
// })
|
||||
// )
|
||||
// }
|
||||
|
||||
// This controls the logs to CONSOLE
|
||||
// if (config.env !== 'test') {
|
||||
// outputToConsole()
|
||||
// }
|
||||
|
||||
const logger = new Wlogger()
|
||||
|
||||
// Allow the logger to write to the console.
|
||||
logger.outputToConsole()
|
||||
|
||||
const wlogger = logger.wlogger
|
||||
|
||||
module.exports = { wlogger, Wlogger }
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// Public npm libraries.
|
||||
|
||||
// Load the Clean Architecture Adapters library
|
||||
const adapters = require('../adapters')
|
||||
const Adapters = require('../adapters')
|
||||
|
||||
// Load the JSON RPC Controller.
|
||||
const JSONRPC = require('./json-rpc')
|
||||
@@ -21,22 +21,13 @@ const RESTControllers = require('./rest-api')
|
||||
|
||||
class Controllers {
|
||||
constructor (localConfig = {}) {
|
||||
this.adapters = adapters
|
||||
this.useCases = new UseCases({ adapters })
|
||||
this.adapters = new Adapters()
|
||||
this.useCases = new UseCases({ adapters: this.adapters })
|
||||
}
|
||||
|
||||
async attachControllers (app) {
|
||||
// Get a JWT token and instantiate bch-js with it. Then pass that instance
|
||||
// to all the rest of the apps controllers and adapters.
|
||||
await adapters.fullStackJwt.getJWT()
|
||||
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
|
||||
adapters.bchjs = await adapters.fullStackJwt.instanceBchjs()
|
||||
|
||||
// Attach the REST controllers to the Koa app.
|
||||
this.attachRESTControllers(app)
|
||||
|
||||
// Start IPFS.
|
||||
await this.adapters.ipfs.start({ bchjs: adapters.bchjs })
|
||||
// Wait for any startup processes to complete for the Adapters libraries.
|
||||
await this.adapters.start()
|
||||
|
||||
this.attachRPCControllers()
|
||||
}
|
||||
@@ -44,13 +35,13 @@ class Controllers {
|
||||
// Top-level function for this library.
|
||||
// Start the various Controllers and attach them to the app.
|
||||
attachRESTControllers (app) {
|
||||
const rESTControllers = new RESTControllers({
|
||||
const restControllers = new RESTControllers({
|
||||
adapters: this.adapters,
|
||||
useCases: this.useCases
|
||||
})
|
||||
|
||||
// Attach the REST API Controllers associated with the boilerplate code to the Koa app.
|
||||
rESTControllers.attachRESTControllers(app)
|
||||
restControllers.attachRESTControllers(app)
|
||||
}
|
||||
|
||||
// Add the JSON RPC router to the ipfs-coord adapter.
|
||||
|
||||
@@ -68,6 +68,10 @@ class BCHRPC {
|
||||
case 'transaction':
|
||||
await this.rateLimit.limiter(rpcData.from)
|
||||
return await this.transaction(rpcData)
|
||||
|
||||
case 'pubkey':
|
||||
await this.rateLimit.limiter(rpcData.from)
|
||||
return await this.pubKey(rpcData)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error in BCHRPC/rpcRouter()')
|
||||
@@ -146,7 +150,7 @@ class BCHRPC {
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
// console.error('Error in createUser()')
|
||||
console.error('Error in JSON RPC BCH transactions()')
|
||||
// throw err
|
||||
|
||||
// Return an error response
|
||||
@@ -223,7 +227,7 @@ class BCHRPC {
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
// console.error('Error in createUser()')
|
||||
console.error('Error in JSON RPC BCH balance()')
|
||||
// throw err
|
||||
|
||||
// Return an error response
|
||||
@@ -327,6 +331,7 @@ class BCHRPC {
|
||||
// console.log('createUser rpcData: ', rpcData)
|
||||
|
||||
const addr = rpcData.payload.params.address
|
||||
console.log('addr: ', addr)
|
||||
|
||||
const data = await this.bchjs.Utxo.get(addr)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
@@ -336,7 +341,8 @@ class BCHRPC {
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
// console.error('Error in createUser()')
|
||||
console.error('Error in JSON RPC BCH utxos()')
|
||||
console.error('Error in JSON RPC utxos(): ', err)
|
||||
// throw err
|
||||
|
||||
// Return an error response
|
||||
@@ -388,19 +394,35 @@ class BCHRPC {
|
||||
const data = await this.bchjs.RawTransactions.sendRawTransaction(hex)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
const retObj = data
|
||||
retObj.status = 200
|
||||
const retObj = {
|
||||
success: true,
|
||||
status: 200,
|
||||
endpoint: 'broadcast',
|
||||
txid: data
|
||||
}
|
||||
// retObj.status = 200
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
// console.error('Error in createUser()')
|
||||
console.error('Error in JSON RPC BCH broadcast()')
|
||||
console.log('error: ', err)
|
||||
// throw err
|
||||
|
||||
// bch-api will sometimes return an error message, not an error object.
|
||||
let message = ''
|
||||
if (!err.message && err.error) {
|
||||
// Full node error
|
||||
message = err.error
|
||||
} else {
|
||||
// Normal error
|
||||
message = err.message
|
||||
}
|
||||
|
||||
// Return an error response
|
||||
return {
|
||||
success: false,
|
||||
status: 422,
|
||||
message: err.message,
|
||||
message,
|
||||
endpoint: 'broadcast'
|
||||
}
|
||||
}
|
||||
@@ -514,7 +536,7 @@ class BCHRPC {
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
// console.error('Error in createUser()')
|
||||
console.error('Error in JSON RPC BCH transaction()')
|
||||
// throw err
|
||||
|
||||
// Return an error response
|
||||
@@ -526,6 +548,78 @@ class BCHRPC {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {JSON} /bch PubKey
|
||||
* @apiPermission public
|
||||
* @apiName PubKey
|
||||
* @apiGroup JSON BCH
|
||||
* @apiDescription Get the public key from an address.
|
||||
* Given an address the endpoint will return an object with the
|
||||
* following properties
|
||||
*
|
||||
* - jsonrpc: "" - jsonrpc version
|
||||
* - id: "" - jsonrpc id
|
||||
* - result: {} - Result of the petition with the RPC information
|
||||
* - success: - Request status
|
||||
* - publickey: - Address public key
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "pubkey", "address": "bitcoincash:qpnty9t0w93fez04h7yzevujpv8pun204qv6yfuahk"}}
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* {
|
||||
* "jsonrpc":"2.0",
|
||||
* "id":"555",
|
||||
* "result":{
|
||||
* "method":"bch",
|
||||
* "reciever":"QmU86vLVbUY1UhziKB6rak7GPKRA2QHWvzNm2AjEvXNsT6",
|
||||
* "value":{
|
||||
* "success": true,
|
||||
* "status": 200,
|
||||
* "endpoint": "pubkey",
|
||||
* "pubkey": {
|
||||
* "success": true,
|
||||
* "publicKey": "033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee"
|
||||
* }
|
||||
*
|
||||
* }
|
||||
*/
|
||||
|
||||
async pubKey (rpcData) {
|
||||
try {
|
||||
// console.log('createUser rpcData: ', rpcData)
|
||||
|
||||
const address = rpcData.payload.params.address
|
||||
|
||||
const data = await this.bchjs.encryption.getPubKey(address)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
const retObj = {
|
||||
success: true,
|
||||
status: 200,
|
||||
endpoint: 'pubkey',
|
||||
pubkey: data
|
||||
}
|
||||
// retObj.status = 200
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
console.error('Error in JSON RPC BCH pubKey()')
|
||||
// throw err
|
||||
let error = err
|
||||
if (err.error && typeof err.error === 'string') {
|
||||
error = new Error(err.error)
|
||||
}
|
||||
// Return an error response
|
||||
return {
|
||||
success: false,
|
||||
status: 422,
|
||||
message: error.message,
|
||||
endpoint: 'pubkey'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BCHRPC
|
||||
|
||||
@@ -38,6 +38,11 @@ class JSONRPC {
|
||||
this.aboutController = new AboutController()
|
||||
this.bchController = new BCHController(localConfig)
|
||||
|
||||
// Cache to store IDs of processed JSON RPC commands. Used to prevent
|
||||
// duplicate processing.
|
||||
this.msgCache = []
|
||||
this.MSG_CACHE_SIZE = 30
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
@@ -45,24 +50,48 @@ class JSONRPC {
|
||||
// which controller to route the instruction to.
|
||||
async router (str, from) {
|
||||
try {
|
||||
console.log('router str: ', str)
|
||||
console.log('router from: ', from)
|
||||
// console.log('router str: ', str)
|
||||
console.log('JSON RPC router recieved data from: ', from)
|
||||
|
||||
// Exit quietly if 'from' is not specified.
|
||||
if (!from || typeof from !== 'string') {
|
||||
// console.warn(
|
||||
// 'Warning: Can not send JSON RPC response. Can not determine which peer this message came from.'
|
||||
// )
|
||||
return
|
||||
wlogger.info(
|
||||
'Warning: Can not send JSON RPC response. Can not determine which peer this message came from.'
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// Attempt to parse the incoming data as a JSON RPC string.
|
||||
const parsedData = _this.jsonrpc.parse(str)
|
||||
// console.log('parsedData: ', parsedData)
|
||||
// wlogger.debug(`parsedData: ${JSON.stringify(parsedData, null, 2)}`)
|
||||
|
||||
// Exit quietly if the incoming string is an invalid JSON RPC string.
|
||||
if (parsedData.type === 'invalid') {
|
||||
return
|
||||
wlogger.info('Rejecting invalid JSON RPC command.')
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for duplicate entries with same 'id' value.
|
||||
const alreadyProcessed = _this._checkIfAlreadyProcessed(parsedData)
|
||||
if (alreadyProcessed) {
|
||||
return false
|
||||
} else {
|
||||
// console.log(`parsedData: ${JSON.stringify(parsedData, null, 2)}`)
|
||||
|
||||
// This node will regularly ping known circuit relays with an /about
|
||||
// JSON RPC call. These will be handled by ipfs-coord, but will percolate
|
||||
// up to this library. Ignore these messages.
|
||||
if (
|
||||
parsedData.type.includes('success') &&
|
||||
parsedData.payload.method === undefined
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Log the incoming JSON RPC command.
|
||||
wlogger.info(
|
||||
`JSON RPC received from ${from}, ID: ${parsedData.payload.id}, type: ${parsedData.type}, method: ${parsedData.payload.method}`
|
||||
)
|
||||
}
|
||||
|
||||
// Added the property "from" to the parsedData object;
|
||||
@@ -100,8 +129,19 @@ class JSONRPC {
|
||||
|
||||
// Encrypt and publish the response to the originators private OrbitDB,
|
||||
// if ipfs-coord has been initialized and the peers ID is registered.
|
||||
if (_this.ipfsCoord.ipfs) {
|
||||
await _this.ipfsCoord.ipfs.orbitdb.sendToDb(from, retStr)
|
||||
|
||||
// console.log('responding to JSON RPC command')
|
||||
const thisNode = _this.ipfsCoord.thisNode
|
||||
// console.log('thisNode: ', thisNode)
|
||||
|
||||
try {
|
||||
await _this.ipfsCoord.useCases.peer.sendPrivateMessage(
|
||||
from,
|
||||
retStr,
|
||||
thisNode
|
||||
)
|
||||
} catch (err) {
|
||||
console.log('sendPrivateMessage() err: ', err)
|
||||
}
|
||||
|
||||
// Return the response and originator. Useful for testing.
|
||||
@@ -113,6 +153,36 @@ class JSONRPC {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the ID of the JSON RPC call, to see if the message has already been
|
||||
// processed. Returns true if the ID exists in the cache of processed messages.
|
||||
// If the ID is new, the function adds it to the cache and return false.
|
||||
_checkIfAlreadyProcessed (data) {
|
||||
try {
|
||||
// console.log('data: ', data)
|
||||
|
||||
const id = data.payload.id
|
||||
|
||||
// Check if the hash is in the array of already processed message.
|
||||
const alreadyProcessed = this.msgCache.includes(id)
|
||||
|
||||
// Update the msgCache if this is a new message.
|
||||
if (!alreadyProcessed) {
|
||||
// Add the hash to the array.
|
||||
this.msgCache.push(id)
|
||||
|
||||
// If the array is at its max size, then remove the oldest element.
|
||||
if (this.msgCache.length > this.MSG_CACHE_SIZE) {
|
||||
this.msgCache.shift()
|
||||
}
|
||||
}
|
||||
|
||||
return alreadyProcessed
|
||||
} catch (err) {
|
||||
console.error('Error in _checkIfAlreadyProcessed: ', err)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// The default JSON RPC response if the incoming command could not be routed.
|
||||
defaultResponse () {
|
||||
const errorObj = {
|
||||
|
||||
@@ -94,7 +94,7 @@ class BCHRESTController {
|
||||
* @api {post} /bch/balance Balance
|
||||
* @apiName Balance
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription Devuelve el balance de un address o un array de adresses.
|
||||
* @apiDescription Returns the balance of an address or an array of addresses.
|
||||
*
|
||||
* Given the 'addresses' property returns an array of objects
|
||||
* with the following properties
|
||||
@@ -378,6 +378,57 @@ class BCHRESTController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /bch/pubkey PubKey
|
||||
* @apiName PubKey
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription Get the public key from an address
|
||||
*
|
||||
* Given the 'address' param returns an object
|
||||
* with the following properties
|
||||
*
|
||||
* - success : - Request status
|
||||
* - publicKey : '' - Public key of the provided address
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X GET localhost:5001/bch/pubkey/bitcoincash:qpnty9t0w93fez04h7yzevujpv8pun204qv6yfuahk
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "success":true,
|
||||
* "publicKey": '033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee'
|
||||
* }
|
||||
*
|
||||
* @apiError UnprocessableEntity Missing required parameters
|
||||
*
|
||||
* @apiErrorExample {json} Error-Response:
|
||||
* HTTP/1.1 422 Unprocessable Entity
|
||||
* {
|
||||
* "status": 422,
|
||||
* "error": "Unprocessable Entity"
|
||||
* }
|
||||
*/
|
||||
async pubKey (ctx) {
|
||||
try {
|
||||
const address = ctx.params.address
|
||||
console.log(`pubKey address: ${address}`)
|
||||
|
||||
const pubkey = await _this.bchjs.encryption.getPubKey(address)
|
||||
// console.log(`pubkey: ${JSON.stringify(pubkey, null, 2)}`)
|
||||
|
||||
ctx.body = pubkey
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
let error = err
|
||||
if (err.error && typeof err.error === 'string') {
|
||||
error = new Error(err.error)
|
||||
}
|
||||
_this.handleError(ctx, error)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
|
||||
@@ -50,6 +50,7 @@ class BCHRouter {
|
||||
this.router.post('/utxos', this.bchRESTController.utxos)
|
||||
this.router.post('/broadcast', this.bchRESTController.broadcast)
|
||||
this.router.post('/transaction', this.bchRESTController.transaction)
|
||||
this.router.get('/pubkey/:address', this.bchRESTController.pubKey)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(this.router.routes())
|
||||
|
||||
@@ -10,7 +10,7 @@ const axios = require('axios').default
|
||||
|
||||
// Local support libraries
|
||||
const config = require('../../../config')
|
||||
const app = require('../../../bin/server')
|
||||
const Server = require('../../../bin/server')
|
||||
const testUtils = require('../../utils/test-utils')
|
||||
const AdminLib = require('../../../src/adapters/admin')
|
||||
const adminLib = new AdminLib()
|
||||
@@ -23,11 +23,14 @@ const LOCALHOST = `http://localhost:${config.port}`
|
||||
describe('Auth', () => {
|
||||
before(async () => {
|
||||
try {
|
||||
process.env.E2ETEST = 'true'
|
||||
const app = new Server()
|
||||
|
||||
// This should be the first instruction. It starts the REST API server.
|
||||
await app.startServer()
|
||||
|
||||
// Stop the IPFS node for the rest of the e2e tests.
|
||||
// await app.controllers.adapters.ipfs.stop()
|
||||
|
||||
// Delete all previous users in the database.
|
||||
await testUtils.deleteAllUsers()
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ const LOCALHOST = `http://localhost:${config.port}`
|
||||
const context = {}
|
||||
|
||||
const UserController = require('../../../src/controllers/rest-api/users/controller')
|
||||
const adapters = require('../../../src/adapters')
|
||||
const Adapters = require('../../../src/adapters')
|
||||
const adapters = new Adapters()
|
||||
const UseCases = require('../../../src/use-cases/')
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
@@ -20,9 +20,9 @@ async function startTest () {
|
||||
|
||||
await testUtils.testTransaction(testData.uutId)
|
||||
|
||||
// ToDo:
|
||||
// testTransactions()
|
||||
// testUtxos()
|
||||
await testUtils.testTransactions(testData.uutId)
|
||||
|
||||
await testUtils.testUtxos(testData.uutId)
|
||||
|
||||
console.log('E2E TEST: Tests completed successfully!')
|
||||
process.exit()
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// Public npm libraries
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
const IpfsCoord = require('ipfs-coord')
|
||||
const IPFS = require('ipfs')
|
||||
const IPFS = require('@chris.troutner/ipfs')
|
||||
const EventEmitter = require('events')
|
||||
const { v4: uid } = require('uuid')
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
@@ -45,12 +45,17 @@ class TestUtils {
|
||||
async sendRPC (ipfsId, cmdStr) {
|
||||
try {
|
||||
// Send the RPC command to the server/service.
|
||||
await this.ipfsCoord.ipfs.orbitdb.sendToDb(ipfsId, cmdStr)
|
||||
// await this.ipfsCoord.ipfs.orbitdb.sendToDb(ipfsId, cmdStr)
|
||||
await this.ipfsCoord.useCases.peer.sendPrivateMessage(
|
||||
ipfsId,
|
||||
cmdStr,
|
||||
this.ipfsCoord.thisNode
|
||||
)
|
||||
|
||||
let retData
|
||||
|
||||
// This event is triggered when the response comes back.
|
||||
this.eventEmitter.on('rpcData', inData => {
|
||||
this.eventEmitter.on('rpcData', (inData) => {
|
||||
retData = inData
|
||||
})
|
||||
|
||||
@@ -98,8 +103,8 @@ class TestUtils {
|
||||
apiInfo: 'none',
|
||||
announceJsonLd: announceJsonLd
|
||||
})
|
||||
await this.ipfsCoord.ipfs.start()
|
||||
await this.ipfsCoord.isReady()
|
||||
|
||||
await this.ipfsCoord.start()
|
||||
|
||||
// Wait to let ipfs-coord connect to subnet peers.
|
||||
await this.bchjs.Util.sleep(30000)
|
||||
@@ -123,10 +128,17 @@ class TestUtils {
|
||||
// 'ipfs-coord peer info: ',
|
||||
// this.ipfsCoord.ipfs.peers.state.peers[ipfsId]
|
||||
// )
|
||||
if (!this.ipfsCoord.ipfs.peers.state.peers[ipfsId]) {
|
||||
|
||||
const peers = this.ipfsCoord.thisNode.peerList
|
||||
// console.log('peers: ', peers)
|
||||
// console.log('Target peer in peerList? ', peers.includes(ipfsId))
|
||||
|
||||
// if (!this.ipfsCoord.ipfs.peers.state.peers[ipfsId]) {
|
||||
if (!peers.includes(ipfsId)) {
|
||||
throw new Error('Could not find UUT in ipfs-coord list of peers.')
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
console.error('Error in connectToUut()')
|
||||
console.log(
|
||||
'Is the service running? Did you update the test-data.json file?'
|
||||
@@ -195,6 +207,72 @@ class TestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the info a on transaction over JSON RPC.
|
||||
async testTransactions (ipfsId) {
|
||||
try {
|
||||
// Generate the JSON RPC command
|
||||
const id = uid()
|
||||
const cmd = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'transactions',
|
||||
addresses: ['bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj']
|
||||
})
|
||||
const cmdStr = JSON.stringify(cmd)
|
||||
|
||||
// console.log(`Publishing message to ${ipfsId}`)
|
||||
|
||||
console.log('E2E TEST: Sending Transactions command...')
|
||||
const result = await this.sendRPC(ipfsId, cmdStr)
|
||||
// console.log('result: ', result)
|
||||
|
||||
if (
|
||||
result.result.value.status &&
|
||||
Array.isArray(result.result.value.transactions)
|
||||
) {
|
||||
console.log('E2E TEST: transactions test passed.')
|
||||
return true
|
||||
} else {
|
||||
console.log('E2E TEST: transactions test failed.')
|
||||
this.failTest()
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error in testTransactions()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Get the utxos for a BCH address over JSON RPC.
|
||||
async testUtxos (ipfsId) {
|
||||
try {
|
||||
// Generate the JSON RPC command
|
||||
const id = uid()
|
||||
const cmd = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'utxos',
|
||||
address: 'bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj'
|
||||
})
|
||||
const cmdStr = JSON.stringify(cmd)
|
||||
|
||||
// console.log(`Publishing message to ${ipfsId}`)
|
||||
|
||||
console.log('E2E TEST: Sending Utxos command...')
|
||||
const result = await this.sendRPC(ipfsId, cmdStr)
|
||||
// console.log('result', result.result.value)
|
||||
|
||||
const bchUtxos = result.result.value[0].bchUtxos
|
||||
const slpUtxos = result.result.value[0].slpUtxos
|
||||
|
||||
if (result.result.value && bchUtxos && slpUtxos) {
|
||||
console.log('E2E TEST: utxos test passed.')
|
||||
return true
|
||||
} else {
|
||||
console.log('E2E TEST: utxos test failed.')
|
||||
this.failTest()
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error in testUtxos()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
failTest () {
|
||||
console.log('Exiting test program.')
|
||||
process.exit()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"uutAddr": "/ip4/127.0.0.1/tcp/5668/p2p/QmZSyLnRQWMBknVNjUroLQnrcmDnUVAUU4pMQ2LGhh6b9v",
|
||||
"uutId": "QmZSyLnRQWMBknVNjUroLQnrcmDnUVAUU4pMQ2LGhh6b9v"
|
||||
"uutAddr": "/ip4/127.0.0.1/tcp/5268/p2p/QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY",
|
||||
"uutId": "QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY"
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ describe('#FullStackJWT', () => {
|
||||
const localConfig = {
|
||||
authServer: 'someserver',
|
||||
apiServer: 'someserver',
|
||||
login: 'somelogin',
|
||||
password: 'somepassword'
|
||||
fullstackLogin: 'somelogin',
|
||||
fullstackPassword: 'somepassword'
|
||||
}
|
||||
uut = new FullStackJWT(localConfig)
|
||||
})
|
||||
@@ -81,7 +81,7 @@ describe('#FullStackJWT', () => {
|
||||
const localConfig = {
|
||||
authServer: 'someserver',
|
||||
apiServer: 'someserver',
|
||||
login: 'somelogin'
|
||||
fullstackLogin: 'somelogin'
|
||||
}
|
||||
uut = new FullStackJWT(localConfig)
|
||||
|
||||
|
||||
@@ -62,10 +62,24 @@ describe('#IPFS', () => {
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should get the public IP address if this node is a Circuit Relay', async () => {
|
||||
// Mock dependencies.
|
||||
uut.IpfsCoord = IPFSCoordMock
|
||||
sandbox.stub(uut.publicIp, 'v4').returns('123')
|
||||
|
||||
// Force Circuit Relay
|
||||
uut.config.isCircuitRelay = true
|
||||
|
||||
const result = await uut.start()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#attachRPCRouter', () => {
|
||||
it('should attached a router output', async () => {
|
||||
it('should attach a router output', async () => {
|
||||
// Mock dependencies
|
||||
uut.ipfsCoord = {
|
||||
privateLog: {},
|
||||
@@ -73,6 +87,11 @@ describe('#IPFS', () => {
|
||||
orbitdb: {
|
||||
privateLog: {}
|
||||
}
|
||||
},
|
||||
adapters: {
|
||||
orbit: {
|
||||
privateLog: () => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +100,11 @@ describe('#IPFS', () => {
|
||||
uut.attachRPCRouter(router)
|
||||
})
|
||||
|
||||
it('should throw an error if ipfs-coord has not been instantiated', () => {
|
||||
it('should catch and throw an error', () => {
|
||||
try {
|
||||
// Force an error
|
||||
delete uut.ipfsCoord.adapters
|
||||
|
||||
const router = console.log
|
||||
|
||||
uut.attachRPCRouter(router)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
|
||||
const IPFSLib = require('../../../src/adapters/ipfs')
|
||||
const IPFSMock = require('../mocks/ipfs-mock')
|
||||
@@ -12,9 +13,11 @@ const IPFSCoordMock = require('../mocks/ipfs-coord-mock')
|
||||
describe('#IPFS-adapter-index', () => {
|
||||
let uut
|
||||
let sandbox
|
||||
let bchjs
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new IPFSLib()
|
||||
bchjs = new BCHJS()
|
||||
uut = new IPFSLib({ bchjs })
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
@@ -59,5 +62,23 @@ describe('#IPFS-adapter-index', () => {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle lock-file errors', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.ipfsAdapter, 'start')
|
||||
.rejects(new Error('Lock already being held'))
|
||||
|
||||
// Prevent process from exiting
|
||||
sandbox.stub(uut.process, 'exit').returns()
|
||||
|
||||
await uut.start({ bchjs })
|
||||
|
||||
assert.fail('Unexpected code path.')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Lock already being held')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -47,4 +47,38 @@ describe('#IPFS-adapter', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#stop', () => {
|
||||
it('should stop the IPFS node', async () => {
|
||||
// Mock dependencies
|
||||
uut.ipfs = {
|
||||
stop: () => {}
|
||||
}
|
||||
|
||||
const result = await uut.stop()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#rmBlocksDir', () => {
|
||||
it('should delete the /blocks directory', () => {
|
||||
const result = uut.rmBlocksDir()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should catch and throw an error', () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.fs, 'rmdirSync').throws(new Error('test error'))
|
||||
|
||||
uut.rmBlocksDir()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const adapters = require('../../../src/adapters')
|
||||
// const { attachControllers } = require('../../../src/controllers')
|
||||
const Controllers = require('../../../src/controllers')
|
||||
|
||||
describe('#Controllers', () => {
|
||||
@@ -24,11 +22,14 @@ describe('#Controllers', () => {
|
||||
|
||||
describe('#attachControllers', () => {
|
||||
it('should attach the controllers', async () => {
|
||||
// mock IPFS
|
||||
sandbox.stub(adapters.fullStackJwt, 'getJWT').resolves({})
|
||||
sandbox.stub(adapters.fullStackJwt, 'instanceBchjs').resolves({})
|
||||
sandbox.stub(adapters.ipfs, 'start').resolves({})
|
||||
adapters.ipfs.ipfsCoordAdapter = {
|
||||
// mock dependencies
|
||||
// sandbox.stub(adapters.fullStackJwt, 'getJWT').resolves({})
|
||||
// sandbox.stub(adapters.fullStackJwt, 'instanceBchjs').resolves({})
|
||||
// sandbox.stub(adapters.ipfs, 'start').resolves({})
|
||||
// adapters.ipfs.ipfsCoordAdapter = {
|
||||
|
||||
sandbox.stub(uut.adapters, 'start').resolves({})
|
||||
uut.adapters.ipfs.ipfsCoordAdapter = {
|
||||
attachRPCRouter: () => {}
|
||||
}
|
||||
|
||||
@@ -44,9 +45,7 @@ describe('#Controllers', () => {
|
||||
it('should catch and throw errors', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(adapters.fullStackJwt, 'getJWT')
|
||||
.rejects(new Error('test error'))
|
||||
sandbox.stub(uut.adapters, 'start').rejects(new Error('test error'))
|
||||
|
||||
const app = {
|
||||
use: () => {}
|
||||
|
||||
@@ -155,6 +155,24 @@ describe('#BCHRPC', () => {
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
it('should route to the pubkey method', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'pubKey').resolves(true)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const rpcCall = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'pubkey'
|
||||
})
|
||||
const jsonStr = JSON.stringify(rpcCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
rpcData.from = 'Origin request'
|
||||
|
||||
const result = await uut.bchRouter(rpcData)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return 500 status on routing issue', async () => {
|
||||
// Mock dependencies
|
||||
@@ -420,4 +438,81 @@ describe('#BCHRPC', () => {
|
||||
assert.equal(response.endpoint, 'transaction')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#pubKey', () => {
|
||||
it('should return data from bchjs', async () => {
|
||||
// Mock dependencies
|
||||
const mock = { success: true, publicKey: '033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee' }
|
||||
sandbox.stub(uut.bchjs.encryption, 'getPubKey').resolves(mock)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const rpcCall = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'pubkey',
|
||||
address: 'bitcoincash:qpnty9t0w93fez04h7yzevujpv8pun204qv6yfuahk'
|
||||
})
|
||||
const jsonStr = JSON.stringify(rpcCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.pubKey(rpcData)
|
||||
|
||||
assert.equal(response.success, true)
|
||||
assert.equal(response.status, 200)
|
||||
|
||||
assert.property(response, 'pubkey')
|
||||
|
||||
const pubKey = response.pubkey
|
||||
assert.property(pubKey, 'publicKey')
|
||||
assert.equal(pubKey.publicKey, mock.publicKey)
|
||||
})
|
||||
it('should throw an error if public key is not found', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.encryption, 'getPubKey')
|
||||
.rejects({ success: false, error: 'No transaction history.' })
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const rpcCall = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'pubkey',
|
||||
address: 'bitcoincash:qrnx7l2e6yejgswehf54gs30ljzumnhqdqgn8yscr2'
|
||||
})
|
||||
const jsonStr = JSON.stringify(rpcCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.pubKey(rpcData)
|
||||
// console.log('response: ', response)
|
||||
|
||||
assert.equal(response.success, false)
|
||||
assert.equal(response.status, 422)
|
||||
assert.equal(response.message, 'No transaction history.')
|
||||
assert.equal(response.endpoint, 'pubkey')
|
||||
})
|
||||
it('should return an error for invalid input', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.encryption, 'getPubKey')
|
||||
.rejects(new Error('Invalid data'))
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const rpcCall = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'pubkey',
|
||||
address: 'bitcoincash'
|
||||
})
|
||||
const jsonStr = JSON.stringify(rpcCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.pubKey(rpcData)
|
||||
// console.log('response: ', response)
|
||||
|
||||
assert.equal(response.success, false)
|
||||
assert.equal(response.status, 422)
|
||||
assert.equal(response.message, 'Invalid data')
|
||||
assert.equal(response.endpoint, 'pubkey')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -207,5 +207,120 @@ describe('#JSON RPC', () => {
|
||||
assert.equal(obj.result.method, 'bch')
|
||||
assert.equal(obj.id, id)
|
||||
})
|
||||
|
||||
it('should exit quietly for duplicate RPC message', async () => {
|
||||
const id = uid()
|
||||
const json = jsonrpc.request(id, 'unknownMethod', {})
|
||||
|
||||
const str = JSON.stringify(json)
|
||||
|
||||
// Call router once.
|
||||
await uut.router(str, 'peerA')
|
||||
|
||||
// Call the router again with the same input.
|
||||
const result = await uut.router(str, 'peerA')
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should ignore metric queries from ipfs-coord', async () => {
|
||||
const id = uid()
|
||||
const json = jsonrpc.request(id, 'unknownMethod', {})
|
||||
|
||||
const str = JSON.stringify(json)
|
||||
|
||||
// Force the RPC type to be 'success', to indicate an RPC that was
|
||||
// processed internal to ipfs-coord.
|
||||
sandbox.stub(uut.jsonrpc, 'parse').returns({
|
||||
payload: {},
|
||||
type: 'success'
|
||||
})
|
||||
|
||||
// Call the router again with the same input.
|
||||
const result = await uut.router(str, 'peerA')
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should report errors when trying to send messages to peers', async () => {
|
||||
const id = uid()
|
||||
const json = jsonrpc.request(id, 'unknownMethod', {})
|
||||
|
||||
const str = JSON.stringify(json)
|
||||
|
||||
// Force issue with sendPrivateMessage()
|
||||
sandbox
|
||||
.stub(uut.ipfsCoord.useCases.peer, 'sendPrivateMessage')
|
||||
.rejects('test error')
|
||||
|
||||
// Call the router again with the same input.
|
||||
const result = await uut.router(str, 'peerA')
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result, 'from')
|
||||
assert.property(result, 'retStr')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#_checkIfAlreadyProcessed', () => {
|
||||
it('should return false the first time an RPC command is seen', () => {
|
||||
const data = {
|
||||
payload: {
|
||||
jsonrpc: '2.0',
|
||||
id: '6c515f3c-cf8a-42ec-870e-e416edd4923f',
|
||||
method: 'unknownMethod',
|
||||
params: {}
|
||||
},
|
||||
type: 'request'
|
||||
}
|
||||
|
||||
const result = uut._checkIfAlreadyProcessed(data)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should return true the second time an RPC command is seen', () => {
|
||||
const data = {
|
||||
payload: {
|
||||
jsonrpc: '2.0',
|
||||
id: '6c515f3c-cf8a-42ec-870e-e416edd4923f',
|
||||
method: 'unknownMethod',
|
||||
params: {}
|
||||
},
|
||||
type: 'request'
|
||||
}
|
||||
|
||||
// First call.
|
||||
uut._checkIfAlreadyProcessed(data)
|
||||
|
||||
// Second call.
|
||||
const result = uut._checkIfAlreadyProcessed(data)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should push out old data from the cache for new data', () => {
|
||||
const data = {
|
||||
payload: {
|
||||
jsonrpc: '2.0',
|
||||
id: '6c515f3c-cf8a-42ec-870e-e416edd4923f',
|
||||
method: 'unknownMethod',
|
||||
params: {}
|
||||
},
|
||||
type: 'request'
|
||||
}
|
||||
|
||||
uut.MSG_CACHE_SIZE = 0
|
||||
|
||||
const result = uut._checkIfAlreadyProcessed(data)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should return true on error', () => {
|
||||
const result = uut._checkIfAlreadyProcessed()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -233,6 +233,56 @@ describe('#BCH-REST-Router', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#pubKey', () => {
|
||||
it('should return data from bchjs', async () => {
|
||||
// Mock dependencies
|
||||
const mock = { success: true, publicKey: '033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee' }
|
||||
sandbox.stub(uut.bchjs.encryption, 'getPubKey').resolves(mock)
|
||||
|
||||
ctx.params = {
|
||||
address: 'bitcoincash:qpnty9t0w93fez04h7yzevujpv8pun204qv6yfuahk'
|
||||
}
|
||||
|
||||
await uut.pubKey(ctx)
|
||||
// console.log('ctx.body: ', ctx.body)
|
||||
|
||||
assert.equal(ctx.body.success, true)
|
||||
assert.equal(ctx.body.publicKey, mock.publicKey)
|
||||
})
|
||||
it('should throw an error if public key is not found', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.encryption, 'getPubKey')
|
||||
.rejects({ success: false, error: 'No transaction history.' })
|
||||
|
||||
ctx.params = {
|
||||
address: 'bitcoincash:qrnx7l2e6yejgswehf54gs30ljzumnhqdqgn8yscr2'
|
||||
}
|
||||
await uut.pubKey(ctx)
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
assert.include(err.message, 'No transaction history')
|
||||
}
|
||||
})
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.encryption, 'getPubKey')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
ctx.params = {
|
||||
address: 'bitcoincash:qpnty9t0w93fez04h7yzevujpv8pun204qv6yfuahk'
|
||||
}
|
||||
await uut.pubKey(ctx)
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#handleError', () => {
|
||||
it('should pass an error message', () => {
|
||||
try {
|
||||
|
||||
@@ -7,7 +7,13 @@ const ipfs = {
|
||||
ipfs: {}
|
||||
},
|
||||
ipfsCoordAdapter: {
|
||||
ipfsCoord: {}
|
||||
ipfsCoord: {
|
||||
useCases: {
|
||||
peer: {
|
||||
sendPrivateMessage: () => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +66,9 @@ const bchjs = {
|
||||
},
|
||||
RawTransactions: {
|
||||
sendRawTransaction: () => {}
|
||||
},
|
||||
encryption: {
|
||||
getPubKey: () => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user