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 | |
|---|---|---|---|
|
|
a7f7fc4e14 | ||
|
|
83ad0d8bfa | ||
|
|
ac4aaca66e | ||
|
|
0748106637 | ||
|
|
27dcc219b9 | ||
|
|
1362e64c42 | ||
|
|
7aad5db594 | ||
|
|
b231686a64 | ||
|
|
60668430f5 | ||
|
|
cf0fb0f572 | ||
|
|
68b0117c3d | ||
|
|
e73440f342 | ||
|
|
b9680508db | ||
|
|
acebfdef09 | ||
|
|
c3ee71a54f | ||
|
|
0d9a32488e | ||
|
|
de7629c93d | ||
|
|
3979694109 | ||
|
|
ae888d094b | ||
|
|
9b8db2adaf | ||
|
|
c8539372ae | ||
|
|
e8a3234dcd | ||
|
|
fc717e0dcd | ||
|
|
02512a8183 | ||
|
|
09a58a78ef | ||
|
|
f4708d9a49 | ||
|
|
18cec75f41 | ||
|
|
7621842952 | ||
|
|
01da1b44da | ||
|
|
b8f262b01b | ||
|
|
0e9a57a1da | ||
|
|
e374522725 | ||
|
|
e402f33132 | ||
|
|
e85655c0ec | ||
|
|
227ffbc4bc | ||
|
|
b41f819783 | ||
|
|
d6c3635c6a | ||
|
|
37e98e4248 | ||
|
|
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 |
@@ -7,6 +7,7 @@
|
||||
#logs
|
||||
logs/*.json
|
||||
*.log
|
||||
*.log.*
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
@@ -68,5 +69,6 @@ ipfsdata
|
||||
ipfs-service-provider.sh
|
||||
start-bch-wallet-service.sh
|
||||
data/
|
||||
run-dev.sh
|
||||
|
||||
!README.md
|
||||
|
||||
@@ -29,14 +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
|
||||
sudo npm install -g node-pre-gyp
|
||||
./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).
|
||||
|
||||
@@ -100,11 +100,28 @@ class Server {
|
||||
// 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)
|
||||
|
||||
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
|
||||
|
||||
Vendored
+13
-2
@@ -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,7 +34,7 @@ module.exports = {
|
||||
: 'emailpassword',
|
||||
|
||||
// FullStack.cash account information, used for automatic JWT handling.
|
||||
getJwtAtStartup: false,
|
||||
getJwtAtStartup: process.env.GET_JWT_AT_STARTUP ? true : false,
|
||||
authServer: process.env.AUTHSERVER
|
||||
? process.env.AUTHSERVER
|
||||
: 'https://auth.fullstack.cash',
|
||||
@@ -48,6 +50,8 @@ module.exports = {
|
||||
|
||||
// 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/',
|
||||
@@ -78,5 +82,12 @@ module.exports = {
|
||||
// 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
|
||||
debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2,
|
||||
|
||||
// Settings for production, using external go-ipfs node.
|
||||
isProduction: process.env.SVC_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
|
||||
}
|
||||
|
||||
Generated
+15385
-10075
File diff suppressed because it is too large
Load Diff
+45
-43
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ipfs-bch-wallet-service",
|
||||
"version": "1.11.4",
|
||||
"version": "1.11.12",
|
||||
"description": "IPFS and REST service providing blockchain access needed by a wallet. ",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -23,51 +23,53 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-service",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "^4.20.3",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"glob": "^7.1.6",
|
||||
"@chris.troutner/ipfs": "2.0.2",
|
||||
"ipfs-coord": "^6.6.3",
|
||||
"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",
|
||||
"winston": "^3.3.3",
|
||||
"winston-daily-rotate-file": "^4.5.0"
|
||||
"@psf/bch-js": "4.20.28",
|
||||
"axios": "0.21.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"glob": "7.1.6",
|
||||
"ipfs": "0.60.0",
|
||||
"ipfs-coord": "6.8.5",
|
||||
"ipfs-http-client": "55.0.0",
|
||||
"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.1",
|
||||
"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": [
|
||||
|
||||
@@ -2,32 +2,51 @@
|
||||
#
|
||||
|
||||
#IMAGE BUILD COMMANDS
|
||||
# ct-base-ubuntu = ubuntu 18.04 + nodejs v10 LTS
|
||||
FROM christroutner/ct-base-ubuntu:latest
|
||||
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
|
||||
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
|
||||
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'"
|
||||
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.
|
||||
#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/
|
||||
|
||||
# Switch to user account.
|
||||
#USER safeuser
|
||||
# Prep 'sudo' commands.
|
||||
#RUN echo 'abcd8765' | sudo -S pwd
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
# Clone the repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service
|
||||
|
||||
@@ -39,7 +58,7 @@ WORKDIR /home/safeuser/ipfs-bch-wallet-service
|
||||
RUN git checkout ct-unstable
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install -g @mapbox/node-pre-gyp
|
||||
#RUN npm install -g @mapbox/node-pre-gyp
|
||||
RUN npm install
|
||||
|
||||
# Generate the API docs
|
||||
@@ -51,6 +70,8 @@ RUN mkdir /home/safeuser/.ipfsdata
|
||||
#VOLUME /home/safeuser/ipfsdata
|
||||
#VOLUME /home/safeuser/orbitdb
|
||||
|
||||
EXPOSE 5010
|
||||
|
||||
# Start the application.
|
||||
COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
@@ -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.
|
||||
@@ -1,8 +1,9 @@
|
||||
# Start the service with the command 'docker-compose up -d'
|
||||
|
||||
version: '2'
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
bch-wallet-mongodb:
|
||||
mongo-bch-service:
|
||||
image: mongo
|
||||
container_name: mongo-bch-service
|
||||
ports:
|
||||
@@ -12,6 +13,35 @@ services:
|
||||
command: mongod --logpath=/dev/null # -- quiet
|
||||
restart: always
|
||||
|
||||
ipfs:
|
||||
image: ipfs/go-ipfs:v0.11.0
|
||||
container_name: ipfs-bch-wallet
|
||||
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
|
||||
- ../data/go-ipfs/export:/export
|
||||
# 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-wallet-service:
|
||||
build: ./
|
||||
container_name: bch-wallet-service
|
||||
@@ -20,12 +50,12 @@ services:
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '10'
|
||||
mem_limit: 1000mb
|
||||
links:
|
||||
- bch-wallet-mongodb
|
||||
- mongo-bch-service
|
||||
- ipfs-bch-wallet
|
||||
ports:
|
||||
# <host port>:<container port>
|
||||
- '5001:5001' # REST API
|
||||
- '5668:5668' # IPFS TCP Port
|
||||
- '5669:5669' # IPFS WS Port
|
||||
- '5010:5010' # <host port>:<container port>
|
||||
volumes:
|
||||
- ../data/ipfsdata:/home/safeuser/ipfs-service-provider/.ipfsdata
|
||||
- ../data/ipfsdata:/home/safeuser/ipfs-bch-wallet-service/.ipfsdata
|
||||
restart: always
|
||||
|
||||
@@ -4,16 +4,29 @@
|
||||
|
||||
# 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=ipfs-service-provider-generic
|
||||
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
|
||||
#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
|
||||
|
||||
# Debug level. 0 = minimal info. 2 = max info.
|
||||
export DEBUG_LEVEL=1
|
||||
export DEBUG_LEVEL=2
|
||||
|
||||
# BCH Wallet Service Settings
|
||||
# Change APISERVER to point to your own installation of bch-api
|
||||
export APISERVER=https://api.fullstack.cash/v5/
|
||||
# These settings are used to proxy a subscription to FullStack.cash.
|
||||
export GET_JWT_AT_STARTUP=1
|
||||
export AUTHSERVER=https://auth.fullstack.cash
|
||||
export FULLSTACKLOGIN=demo@demo.com
|
||||
export FULLSTACKPASS=demo
|
||||
|
||||
# END: Optional configuration settings
|
||||
|
||||
@@ -21,16 +34,14 @@ export DEBUG_LEVEL=1
|
||||
# Production database connection string.
|
||||
export DBURL=mongodb://172.17.0.1:5555/ipfs-service-prod
|
||||
|
||||
# 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
|
||||
# Configure REST API port
|
||||
export PORT=5010
|
||||
|
||||
# Production settings using external go-ipfs node.
|
||||
export SVC_ENV=production
|
||||
export IPFS_HOST=172.17.0.1
|
||||
export IPFS_API_PORT=5001
|
||||
export IPFS_TCP_PORT=4001
|
||||
#export IPFS_WS_PORT=4002
|
||||
|
||||
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
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is an example for running a production environment, which is
|
||||
# defined by running an external go-ipfs node.
|
||||
|
||||
# Ports
|
||||
export PORT=5010 # REST API port
|
||||
|
||||
# The human-readible name that is used when displaying data about this node.
|
||||
export COORD_NAME=ipfs-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=2
|
||||
|
||||
# Production settings that use external IPFS node.
|
||||
# https://github.com/christroutner/docker-ipfs
|
||||
export SVC_ENV=production
|
||||
#export IPFS_HOST=localhost
|
||||
export IPFS_HOST=172.17.0.1
|
||||
export IPFS_API_PORT=5001
|
||||
|
||||
# Configure IPFS ports
|
||||
export IPFS_TCP_PORT=4001
|
||||
#export IPFS_WS_PORT=5269
|
||||
|
||||
# MongoDB connection string.
|
||||
export DBURL=mongodb://localhost:27017/ipfs-service-dev
|
||||
|
||||
npm start
|
||||
@@ -43,6 +43,8 @@ class 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.
|
||||
|
||||
@@ -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,20 +45,44 @@ class IpfsCoordAdapter {
|
||||
}
|
||||
|
||||
async start (localConfig = {}) {
|
||||
// console.log('this.config.announceJsonLd: ', this.config.announceJsonLd)
|
||||
const circuitRelayInfo = {}
|
||||
|
||||
this.ipfsCoord = new this.IpfsCoord({
|
||||
// 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 */
|
||||
}
|
||||
}
|
||||
|
||||
const ipfsCoordOptions = {
|
||||
ipfs: this.ipfs,
|
||||
type: 'node.js',
|
||||
// type: 'browser',
|
||||
bchjs: this.bchjs,
|
||||
privateLog: console.log, // Default to console.log
|
||||
isCircuitRelay: this.config.isCircuitRelay,
|
||||
circuitRelayInfo,
|
||||
apiInfo: this.config.apiInfo,
|
||||
announceJsonLd: this.config.announceJsonLd,
|
||||
mnemonic: this.config.mnemonic,
|
||||
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({ type: 'node.js' })
|
||||
|
||||
@@ -2,37 +2,53 @@
|
||||
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('@chris.troutner/ipfs')
|
||||
// const IPFS = require('@chris.troutner/ipfs')
|
||||
const IPFSembedded = require('ipfs')
|
||||
const IPFSexternal = require('ipfs-http-client')
|
||||
const fs = require('fs')
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Start an IPFS node.
|
||||
async start () {
|
||||
try {
|
||||
// Ipfs Options
|
||||
const ipfsOptions = {
|
||||
repo: './.ipfsdata/ipfs',
|
||||
const ipfsOptionsEmbedded = {
|
||||
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
|
||||
@@ -47,28 +63,76 @@ 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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ipfsOptionsExternal = {
|
||||
host: this.config.ipfsHost,
|
||||
port: this.config.ipfsApiPort
|
||||
}
|
||||
|
||||
let ipfsOptions = ipfsOptionsEmbedded
|
||||
if (this.config.isProduction) {
|
||||
ipfsOptions = ipfsOptionsExternal
|
||||
}
|
||||
|
||||
// Create a new IPFS node.
|
||||
this.ipfs = await this.IPFS.create(ipfsOptions)
|
||||
|
||||
// Set the 'server' profile so the node does not scan private networks.
|
||||
await this.ipfs.config.profiles.apply('server')
|
||||
|
||||
// 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
|
||||
|
||||
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 }
|
||||
|
||||
@@ -26,18 +26,9 @@ class Controllers {
|
||||
}
|
||||
|
||||
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 this.adapters.fullStackJwt.getJWT()
|
||||
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
|
||||
this.adapters.bchjs = await this.adapters.fullStackJwt.instanceBchjs()
|
||||
|
||||
// Wait for any startup processes to complete for the Adapters libraries.
|
||||
await this.adapters.start()
|
||||
|
||||
// Attach the REST controllers to the Koa app.
|
||||
// this.attachRESTControllers(app)
|
||||
|
||||
this.attachRPCControllers()
|
||||
}
|
||||
|
||||
|
||||
@@ -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()')
|
||||
@@ -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)}`)
|
||||
@@ -400,13 +405,24 @@ class BCHRPC {
|
||||
return retObj
|
||||
} catch (err) {
|
||||
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'
|
||||
}
|
||||
}
|
||||
@@ -532,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;
|
||||
@@ -124,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())
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"uutAddr": "/ip4/127.0.0.1/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2",
|
||||
"uutId": "QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2"
|
||||
"uutAddr": "/ip4/127.0.0.1/tcp/5268/p2p/QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY",
|
||||
"uutId": "QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY"
|
||||
}
|
||||
|
||||
@@ -62,6 +62,20 @@ 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', () => {
|
||||
@@ -86,16 +100,19 @@ describe('#IPFS', () => {
|
||||
uut.attachRPCRouter(router)
|
||||
})
|
||||
|
||||
// it('should throw an error if ipfs-coord has not been instantiated', () => {
|
||||
// try {
|
||||
// const router = console.log
|
||||
//
|
||||
// uut.attachRPCRouter(router)
|
||||
//
|
||||
// assert.fail('Unexpected code path')
|
||||
// } catch (err) {
|
||||
// assert.include(err.message, 'Cannot read property')
|
||||
// }
|
||||
// })
|
||||
it('should catch and throw an error', () => {
|
||||
try {
|
||||
// Force an error
|
||||
delete uut.ipfsCoord.adapters
|
||||
|
||||
const router = console.log
|
||||
|
||||
uut.attachRPCRouter(router)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -45,9 +45,7 @@ describe('#Controllers', () => {
|
||||
it('should catch and throw errors', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.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