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 | |
|---|---|---|---|
|
|
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 |
@@ -68,5 +68,6 @@ ipfsdata
|
||||
ipfs-service-provider.sh
|
||||
start-bch-wallet-service.sh
|
||||
data/
|
||||
run-dev.sh
|
||||
|
||||
!README.md
|
||||
|
||||
@@ -29,14 +29,34 @@ 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
|
||||
./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,6 +100,13 @@ 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)
|
||||
|
||||
Vendored
+3
-1
@@ -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',
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Ports
|
||||
export PORT=5001 # REST API port
|
||||
export IPFS_TCP_PORT=5268
|
||||
export IPFS_WS_PORT=5269
|
||||
|
||||
#export ENABLE_CIRCUIT_RELAY=1
|
||||
|
||||
# Production database connection string.
|
||||
#export DBURL=mongodb://localhost:27017/bch-service-dev
|
||||
|
||||
export COORD_NAME=ipfs-service-provider-generic
|
||||
export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran"
|
||||
|
||||
export DEBUG_LEVEL=1
|
||||
|
||||
npm start
|
||||
Generated
+2775
-694
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -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,12 +23,12 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-service",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "^4.20.3",
|
||||
"@psf/bch-js": "^4.20.13",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"glob": "^7.1.6",
|
||||
"@chris.troutner/ipfs": "2.0.2",
|
||||
"ipfs-coord": "^6.6.3",
|
||||
"ipfs-coord": "^6.6.8",
|
||||
"jsonrpc-lite": "^2.2.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jwt-bch-lib": "^1.3.0",
|
||||
|
||||
@@ -2,32 +2,44 @@
|
||||
#
|
||||
|
||||
#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
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
export MNEMONIC="olive two muscle bottom coral ancient wait legend bronze useful process session"
|
||||
|
||||
# The human readable name this IPFS node identifies as.
|
||||
export COORD_NAME=ipfs-service-provider-generic
|
||||
export COORD_NAME=bch-wallet-service-generic
|
||||
|
||||
# Allow this node to function as a circuit relay. It must not be behind a firewall.
|
||||
#export ENABLE_CIRCUIT_RELAY=true
|
||||
@@ -22,6 +22,7 @@ export DEBUG_LEVEL=1
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -46,6 +46,19 @@ class IPFS {
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/ipfs/index.js/start()')
|
||||
|
||||
// If we are not in a test environment.
|
||||
if (process.env.SVC_ENV !== 'test') {
|
||||
// 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 file issue with IPFS. Shutting down so that process manager can restart app.'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
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
|
||||
|
||||
+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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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,26 +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.'
|
||||
// )
|
||||
wlogger.info(
|
||||
'Warning: Can not send JSON RPC response. Can not determine which peer this message came from.'
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 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') {
|
||||
wlogger.info('Rejecting invalid JSON RPC command.')
|
||||
return
|
||||
}
|
||||
|
||||
// Check for duplicate entries with same 'id' value.
|
||||
const alreadyProcessed = _this._checkIfAlreadyProcessed(parsedData)
|
||||
if (alreadyProcessed) {
|
||||
return
|
||||
} else {
|
||||
// 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 ipfs-coord. Ignore these messages.
|
||||
if (
|
||||
parsedData.type.includes('success') &&
|
||||
parsedData.payload.method === undefined
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// 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;
|
||||
// necessary for calculating rate limits (based on the IPFS ID).
|
||||
parsedData.from = from
|
||||
@@ -124,6 +151,34 @@ 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 {
|
||||
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 false
|
||||
}
|
||||
}
|
||||
|
||||
// The default JSON RPC response if the incoming command could not be routed.
|
||||
defaultResponse () {
|
||||
const errorObj = {
|
||||
|
||||
@@ -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: () => {}
|
||||
|
||||
Reference in New Issue
Block a user