mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service.git
synced 2026-09-22 09:12:02 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f41cac9fc7 | ||
|
|
8ad1400d3c | ||
|
|
e48a9213c5 | ||
|
|
2e2d092b18 | ||
|
|
c8aefae0cc | ||
|
|
17e465b3ab | ||
|
|
40bb75bf33 | ||
|
|
02201afc59 | ||
|
|
bae98bba28 | ||
|
|
0eb7aeaed1 | ||
|
|
dbd5022c4c | ||
|
|
72ac73c410 | ||
|
|
9b52c81b25 | ||
|
|
7cc13b9ba9 | ||
|
|
3ba866d2d6 | ||
|
|
3d058d9164 | ||
|
|
fde8b16fef | ||
|
|
c5a1eb6f81 | ||
|
|
74f8b60472 | ||
|
|
d57085949c | ||
|
|
9dc40a417a | ||
|
|
81e5592b81 | ||
|
|
44867f0173 | ||
|
|
c46f7357c3 |
@@ -65,5 +65,6 @@ system-user-*.json
|
||||
orbitdb
|
||||
ipfsdata
|
||||
ipfs-service-provider.sh
|
||||
start-bch-wallet-service.sh
|
||||
|
||||
!README.md
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
This is a censorship-resistant, IPFS-based microservice that provides access for wallets to access the Bitcoin Cash (BCH) blockchain. It leverages [bch-js](https://github.com/Permissionless-Software-Foundation/bch-js).
|
||||
|
||||
- [Decentralized Blockchain Service Providers](https://youtu.be/m_33rRXEats) YouTube video demos this software.
|
||||
- [JSON RPC and REST API Documentation](https://ipfs-bch-wallet-service.fullstack.cash/)
|
||||
|
||||
## About This Repository
|
||||
|
||||
|
||||
+4
-3
@@ -55,9 +55,10 @@ async function startServer () {
|
||||
app.use(passport.initialize())
|
||||
app.use(passport.session())
|
||||
|
||||
// Attach REST API and JSON RPC controllers to the app.
|
||||
const controllers = require('../src/controllers')
|
||||
await controllers.attachControllers(app)
|
||||
// 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
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
This file should be customized for your app. The string in this file is
|
||||
displayed when a user makes the 'about' JSON RPC call.
|
||||
*/
|
||||
|
||||
module.exports = `
|
||||
This is the about string for the ipfs-service-provider repository:
|
||||
https://github.com/Permissionless-Software-Foundation/ipfs-service-provider
|
||||
|
||||
Brought to you by the Permissionless Software Foundation:
|
||||
https://PSFoundation.cash
|
||||
|
||||
Documentation:
|
||||
https://ipfs-service-provider.fullstack.cash/
|
||||
`
|
||||
Vendored
+6
@@ -5,6 +5,10 @@
|
||||
|
||||
/* eslint no-unneeded-ternary:0 */
|
||||
|
||||
// Get the version from the package.json file.
|
||||
const pkgInfo = require('../../package.json')
|
||||
const version = pkgInfo.version
|
||||
|
||||
const ipfsCoordName = process.env.COORD_NAME
|
||||
? process.env.COORD_NAME
|
||||
: 'ipfs-bch-wallet-service'
|
||||
@@ -52,6 +56,8 @@ module.exports = {
|
||||
'@context': 'https://schema.org/',
|
||||
'@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/',
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ipfs-bch-wallet-service",
|
||||
"version": "1.0.0",
|
||||
"version": "1.9.1",
|
||||
"description": "IPFS and REST service providing blockchain access needed by a wallet. ",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
+78
-37
@@ -11,52 +11,93 @@ require('winston-daily-rotate-file')
|
||||
|
||||
const config = require('../../config')
|
||||
|
||||
// Configure daily-rotation transport.
|
||||
const transport = new winston.transports.DailyRotateFile({
|
||||
filename: `${__dirname.toString()}/../../logs/koa-${config.env}-%DATE%.log`,
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: false,
|
||||
maxSize: '1m', // 1 megabyte
|
||||
maxFiles: '5d', // 5 days
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.json()
|
||||
)
|
||||
})
|
||||
class Wlogger {
|
||||
constructor (localConfig = {}) {
|
||||
this.config = config
|
||||
|
||||
transport.on('rotate', notifyRotation)
|
||||
// Configure daily-rotation transport.
|
||||
this.transport = new winston.transports.DailyRotateFile({
|
||||
filename: `${__dirname.toString()}/../../logs/koa-${
|
||||
this.config.env
|
||||
}-%DATE%.log`,
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: false,
|
||||
maxSize: '1m', // 1 megabyte
|
||||
maxFiles: '5d', // 5 days
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.json()
|
||||
)
|
||||
})
|
||||
|
||||
function notifyRotation (oldFilename, newFilename) {
|
||||
wlogger.info('Rotating log files')
|
||||
this.transport.on('rotate', this.notifyRotation)
|
||||
|
||||
// This controls what goes into the log FILES
|
||||
this.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' })
|
||||
this.transport
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
notifyRotation (oldFilename, newFilename) {
|
||||
this.wlogger.info('Rotating log files')
|
||||
}
|
||||
|
||||
outputToConsole () {
|
||||
this.wlogger.add(
|
||||
new winston.transports.Console({
|
||||
format: winston.format.simple(),
|
||||
level: 'info'
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
]
|
||||
})
|
||||
// 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'
|
||||
})
|
||||
)
|
||||
}
|
||||
// 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()
|
||||
// }
|
||||
|
||||
module.exports = { wlogger, notifyRotation, outputToConsole }
|
||||
const logger = new Wlogger()
|
||||
const wlogger = logger.wlogger
|
||||
|
||||
module.exports = { wlogger, Wlogger }
|
||||
|
||||
+37
-30
@@ -14,15 +14,18 @@ const JSONRPC = require('./json-rpc')
|
||||
|
||||
// Load the Clean Architecture Use Case libraries.
|
||||
const UseCases = require('../use-cases')
|
||||
const useCases = new UseCases({ adapters })
|
||||
// const useCases = new UseCases({ adapters })
|
||||
|
||||
// Load the REST API Controllers.
|
||||
const RESTControllers = require('./rest-api')
|
||||
|
||||
// Top-level function for this library.
|
||||
// Start the various Controllers and attach them to the app.
|
||||
async function attachControllers (app) {
|
||||
try {
|
||||
class Controllers {
|
||||
constructor (localConfig = {}) {
|
||||
this.adapters = adapters
|
||||
this.useCases = new UseCases({ 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()
|
||||
@@ -30,34 +33,38 @@ async function attachControllers (app) {
|
||||
adapters.bchjs = await adapters.fullStackJwt.instanceBchjs()
|
||||
|
||||
// Attach the REST controllers to the Koa app.
|
||||
attachRESTControllers(app)
|
||||
this.attachRESTControllers(app)
|
||||
|
||||
// Start IPFS.
|
||||
await adapters.ipfs.start({ bchjs: adapters.bchjs })
|
||||
await this.adapters.ipfs.start({ bchjs: adapters.bchjs })
|
||||
|
||||
attachRPCControllers()
|
||||
} catch (err) {
|
||||
console.error('Error in attachControllers()')
|
||||
throw err
|
||||
this.attachRPCControllers()
|
||||
}
|
||||
|
||||
// Top-level function for this library.
|
||||
// Start the various Controllers and attach them to the app.
|
||||
attachRESTControllers (app) {
|
||||
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)
|
||||
}
|
||||
|
||||
// Add the JSON RPC router to the ipfs-coord adapter.
|
||||
attachRPCControllers () {
|
||||
const jsonRpcController = new JSONRPC({
|
||||
adapters: this.adapters,
|
||||
useCases: this.useCases
|
||||
})
|
||||
|
||||
// Attach the input of the JSON RPC router to the output of ipfs-coord.
|
||||
this.adapters.ipfs.ipfsCoordAdapter.attachRPCRouter(
|
||||
jsonRpcController.router
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function attachRESTControllers (app) {
|
||||
const rESTControllers = new RESTControllers({
|
||||
adapters,
|
||||
useCases
|
||||
})
|
||||
|
||||
// Attach the REST API Controllers associated with the boilerplate code to the Koa app.
|
||||
rESTControllers.attachRESTControllers(app)
|
||||
}
|
||||
|
||||
// Add the JSON RPC router to the ipfs-coord adapter.
|
||||
function attachRPCControllers () {
|
||||
const jsonRpcController = new JSONRPC({ adapters, useCases })
|
||||
|
||||
// Attach the input of the JSON RPC router to the output of ipfs-coord.
|
||||
adapters.ipfs.ipfsCoordAdapter.attachRPCRouter(jsonRpcController.router)
|
||||
}
|
||||
|
||||
module.exports = { attachControllers }
|
||||
module.exports = Controllers
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
|
||||
// Local libraries
|
||||
const aboutStr = require('../../../../config/about')
|
||||
const config = require('../../../../config')
|
||||
|
||||
class AboutRPC {
|
||||
constructor (localConfig) {
|
||||
@@ -36,7 +36,8 @@ class AboutRPC {
|
||||
return {
|
||||
success: true,
|
||||
status: 200,
|
||||
message: aboutStr,
|
||||
// message: aboutStr,
|
||||
message: JSON.stringify(config.announceJsonLd),
|
||||
endpoint: 'about'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,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 +223,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
|
||||
@@ -336,7 +336,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,12 +389,17 @@ 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()')
|
||||
// throw err
|
||||
|
||||
// Return an error response
|
||||
@@ -514,7 +520,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
|
||||
|
||||
+4
-11
@@ -3,7 +3,7 @@
|
||||
functions are called by the /user REST API endpoints.
|
||||
*/
|
||||
|
||||
// const UserModel = require('../adapters/localdb/models/users')
|
||||
const UserEntity = require('../entities/user')
|
||||
const { wlogger } = require('../adapters/wlogger')
|
||||
|
||||
class UserLib {
|
||||
@@ -17,6 +17,7 @@ class UserLib {
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.UserEntity = new UserEntity()
|
||||
this.UserModel = this.adapters.localdb.Users
|
||||
}
|
||||
|
||||
@@ -24,17 +25,9 @@ class UserLib {
|
||||
async createUser (userObj) {
|
||||
try {
|
||||
// Input Validation
|
||||
if (!userObj.email || typeof userObj.email !== 'string') {
|
||||
throw new Error("Property 'email' must be a string!")
|
||||
}
|
||||
if (!userObj.password || typeof userObj.password !== 'string') {
|
||||
throw new Error("Property 'password' must be a string!")
|
||||
}
|
||||
if (!userObj.name || typeof userObj.name !== 'string') {
|
||||
throw new Error("Property 'name' must be a string!")
|
||||
}
|
||||
|
||||
const user = new this.UserModel(userObj)
|
||||
const userEntity = this.UserEntity.validate(userObj)
|
||||
const user = new this.UserModel(userEntity)
|
||||
|
||||
// Enforce default value of 'user'
|
||||
user.type = 'user'
|
||||
|
||||
@@ -20,6 +20,10 @@ async function startTest () {
|
||||
|
||||
await testUtils.testTransaction(testData.uutId)
|
||||
|
||||
// ToDo:
|
||||
// testTransactions()
|
||||
// testUtxos()
|
||||
|
||||
console.log('E2E TEST: Tests completed successfully!')
|
||||
process.exit()
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
// const assert = require('chai').assert
|
||||
const {
|
||||
notifyRotation,
|
||||
outputToConsole
|
||||
} = require('../../../src/adapters/wlogger')
|
||||
const assert = require('chai').assert
|
||||
const { Wlogger } = require('../../../src/adapters/wlogger')
|
||||
|
||||
const sinon = require('sinon')
|
||||
|
||||
// let uut
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
describe('#wlogger.js', () => {
|
||||
describe('#wlogger', () => {
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
|
||||
uut = new Wlogger()
|
||||
})
|
||||
|
||||
describe('#constructor', () => {
|
||||
it('should create a new wlogger instance', () => {
|
||||
uut = new Wlogger()
|
||||
// console.log('uut: ', uut)
|
||||
|
||||
assert.property(uut, 'transport')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#notifyRotation', () => {
|
||||
it('should notify of a log rotation', () => {
|
||||
notifyRotation()
|
||||
uut.notifyRotation()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#envronment', () => {
|
||||
it('should write to console in non-test environment', () => {
|
||||
outputToConsole()
|
||||
uut.outputToConsole()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,14 +7,17 @@ const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const adapters = require('../../../src/adapters')
|
||||
const { attachControllers } = require('../../../src/controllers')
|
||||
// const { attachControllers } = require('../../../src/controllers')
|
||||
const Controllers = require('../../../src/controllers')
|
||||
|
||||
describe('#Controllers', () => {
|
||||
// let uut
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
uut = new Controllers()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
@@ -22,9 +25,9 @@ describe('#Controllers', () => {
|
||||
describe('#attachControllers', () => {
|
||||
it('should attach the controllers', async () => {
|
||||
// mock IPFS
|
||||
sandbox.stub(adapters.ipfs, 'start').resolves({})
|
||||
sandbox.stub(adapters.fullStackJwt, 'getJWT').resolves({})
|
||||
sandbox.stub(adapters.fullStackJwt, 'instanceBchjs').resolves({})
|
||||
sandbox.stub(adapters.ipfs, 'start').resolves({})
|
||||
adapters.ipfs.ipfsCoordAdapter = {
|
||||
attachRPCRouter: () => {}
|
||||
}
|
||||
@@ -33,7 +36,9 @@ describe('#Controllers', () => {
|
||||
use: () => {}
|
||||
}
|
||||
|
||||
await attachControllers(app)
|
||||
await uut.attachControllers(app)
|
||||
|
||||
assert.isOk(true, 'Not throwing an error is a success')
|
||||
})
|
||||
|
||||
it('should catch and throw errors', async () => {
|
||||
@@ -43,7 +48,13 @@ describe('#Controllers', () => {
|
||||
.stub(adapters.fullStackJwt, 'getJWT')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
await attachControllers()
|
||||
const app = {
|
||||
use: () => {}
|
||||
}
|
||||
|
||||
await uut.attachControllers(app)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log('err.message: ', err.message)
|
||||
assert.include(err.message, 'test error')
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('#users-use-case', () => {
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
// assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
assert.include(err.message, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user