mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer.git
synced 2026-09-22 09:12:03 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b23d00ffe | ||
|
|
53cd4650bd | ||
|
|
d734a754d6 | ||
|
|
d4863e7d7d | ||
|
|
e6941f7f7f | ||
|
|
4fdf35a55f | ||
|
|
bb44b75973 | ||
|
|
698cca555b | ||
|
|
76b3740e74 | ||
|
|
2622038df5 | ||
|
|
f1fbe34b58 | ||
|
|
d232b6404d | ||
|
|
9f92cef122 | ||
|
|
18e98f9a3b | ||
|
|
938cc755f1 | ||
|
|
6db6db8187 | ||
|
|
35af9c08fa |
Vendored
+7
-1
@@ -80,10 +80,16 @@ module.exports = {
|
||||
|
||||
debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2,
|
||||
|
||||
// Preferred provider running ipfs-bch-wallet-service for BCH wallet.
|
||||
preferredProvider: process.env.PREFERRED_PROVIDER
|
||||
? process.env.PREFERRED_PROVIDER
|
||||
: ''
|
||||
: '',
|
||||
// : 'QmdBGB8S6sEr19gaAxJYjhRbz5ZmMiH5a1JyyMmaxcRKnY'
|
||||
// : 'QmUTx6KqYKVZbKpKxR7vGDUgZFYVvVVyEWDeCYq4GwBCff'
|
||||
// Used for development
|
||||
|
||||
// Preferred P2WDB provider
|
||||
preferredP2wdbProvider: process.env.PREFERRED_P2WDB_PROVIDER
|
||||
? process.env.PREFERRED_P2WDB_PROVIDER
|
||||
: ''
|
||||
}
|
||||
|
||||
Generated
+7
-7
@@ -14,7 +14,7 @@
|
||||
"axios": "0.21.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"glob": "7.1.6",
|
||||
"ipfs-coord": "6.7.4",
|
||||
"ipfs-coord": "6.7.6",
|
||||
"jsonrpc-lite": "2.2.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jwt-bch-lib": "1.3.0",
|
||||
@@ -9611,9 +9611,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ipfs-coord": {
|
||||
"version": "6.7.4",
|
||||
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-6.7.4.tgz",
|
||||
"integrity": "sha512-nboEp4xge4ZE8VWSCPhIB5PXQafKChacLOTVwxFfmjzgrXenacw3PBuR/UGxMJ3JX+t13ee/M662Jd8nzp7rZA==",
|
||||
"version": "6.7.6",
|
||||
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-6.7.6.tgz",
|
||||
"integrity": "sha512-Uif7Th0+jMb6E7/LhALgpuVFHNV2jlRWzOW8T/u9AuF83CyiHFZJIgJY06pd/TH6r7wH2VUahrrcCOlxgiQJQg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "0.21.4",
|
||||
@@ -33139,9 +33139,9 @@
|
||||
}
|
||||
},
|
||||
"ipfs-coord": {
|
||||
"version": "6.7.4",
|
||||
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-6.7.4.tgz",
|
||||
"integrity": "sha512-nboEp4xge4ZE8VWSCPhIB5PXQafKChacLOTVwxFfmjzgrXenacw3PBuR/UGxMJ3JX+t13ee/M662Jd8nzp7rZA==",
|
||||
"version": "6.7.6",
|
||||
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-6.7.6.tgz",
|
||||
"integrity": "sha512-Uif7Th0+jMb6E7/LhALgpuVFHNV2jlRWzOW8T/u9AuF83CyiHFZJIgJY06pd/TH6r7wH2VUahrrcCOlxgiQJQg==",
|
||||
"requires": {
|
||||
"axios": "0.21.4",
|
||||
"bch-encrypt-lib": "2.0.0",
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
"axios": "0.21.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"glob": "7.1.6",
|
||||
"ipfs-coord": "6.7.4",
|
||||
"ipfs-coord": "6.7.6",
|
||||
"jsonrpc-lite": "2.2.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jwt-bch-lib": "1.3.0",
|
||||
|
||||
@@ -58,6 +58,8 @@ class BchAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the status of BCH wallet services this node can talk to. Returns an
|
||||
// array of BCH wallet service providers.
|
||||
async getStatus () {
|
||||
try {
|
||||
const peerData = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
|
||||
|
||||
@@ -17,6 +17,7 @@ const Nodemailer = require('./nodemailer')
|
||||
const JSONFiles = require('./json-files')
|
||||
const FullStackJWT = require('./fullstack-jwt')
|
||||
const BCH = require('./bch')
|
||||
const P2WDB = require('./p2wdb')
|
||||
|
||||
const config = require('../../config')
|
||||
|
||||
@@ -41,6 +42,7 @@ class Adapters {
|
||||
this.bchjs = new BCHJS()
|
||||
this.config = config
|
||||
this.bch = new BCH(localConfig)
|
||||
this.p2wdb = new P2WDB(localConfig)
|
||||
|
||||
// Get a valid JWT API key and instance bch-js.
|
||||
this.fullStackJwt = new FullStackJWT(config)
|
||||
|
||||
@@ -15,8 +15,10 @@ const config = require('../../../config')
|
||||
// const JSONRPC = require('../../controllers/json-rpc/')
|
||||
|
||||
// The minimum version of ipfs-bch-wallet-service that this wallet can work with.
|
||||
const MIN_BCH_WALLET_VERSION = '1.11.0'
|
||||
const MIN_BCH_WALLET_VERSION = '1.11.11'
|
||||
const WALLET_PROTOCOL = 'bch-wallet'
|
||||
const MIN_P2WDB_VERSION = '1.4.0'
|
||||
const P2WDB_PROTOCOL = 'p2wdb'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -48,12 +50,18 @@ class IpfsCoordAdapter {
|
||||
this.isReady = false
|
||||
|
||||
// Periodically poll services for available wallet service providers.
|
||||
this.pollServiceInterval = setInterval(this.pollForServices, 10000)
|
||||
this.pollBchServiceInterval = setInterval(this.pollForBchServices, 10000)
|
||||
this.pollP2wdbServiceInterval = setInterval(
|
||||
this.pollForP2wdbServices,
|
||||
11000
|
||||
)
|
||||
|
||||
// State object. TODO: Make this more robust.
|
||||
this.state = {
|
||||
serviceProviders: [],
|
||||
selectedServiceProvider: ''
|
||||
selectedServiceProvider: '',
|
||||
p2wdbProviders: [],
|
||||
selectedP2wdbProvider: ''
|
||||
}
|
||||
|
||||
_this = this
|
||||
@@ -112,9 +120,91 @@ class IpfsCoordAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
// Poll the ipfs-coord coordination channel for available service providers.
|
||||
// This method is called periodically by a timer-interval.
|
||||
pollForServices () {
|
||||
// Poll the ipfs-coord coordination channel for available P2WDB service
|
||||
// providers. This method is called periodically by a timer-interval.
|
||||
pollForP2wdbServices () {
|
||||
try {
|
||||
// An array of IPFS IDs of other nodes in the coordination pubsub channel.
|
||||
const peers = _this.ipfsCoord.thisNode.peerList
|
||||
// console.log(`peers: ${JSON.stringify(peers, null, 2)}`)
|
||||
|
||||
// Array of objects. Each object is the IPFS ID of the peer and contains
|
||||
// data about that peer.
|
||||
const peerData = _this.ipfsCoord.thisNode.peerData
|
||||
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
|
||||
|
||||
for (let i = 0; i < peers.length; i++) {
|
||||
const thisPeer = peers[i]
|
||||
const thisData = peerData.filter((x) => x.from === thisPeer)
|
||||
const thisPeerData = thisData[0]
|
||||
|
||||
// Create a 'fingerprint' that defines the wallet service.
|
||||
const protocol = thisPeerData.data.jsonLd.protocol
|
||||
const version = thisPeerData.data.jsonLd.version
|
||||
// console.log(
|
||||
// `debug: peer ${thisPeer} uses protocol: ${protocol} v${version}`
|
||||
// )
|
||||
|
||||
let versionMatches = false
|
||||
if (version) {
|
||||
versionMatches = _this.semver.gt(version, MIN_P2WDB_VERSION)
|
||||
}
|
||||
|
||||
// p2wdbProviders: [],
|
||||
// selectedP2wdbProvider: ''
|
||||
|
||||
// Ignore any peers that don't match the fingerprint for a BCH wallet
|
||||
// service.
|
||||
if (protocol && protocol.includes(P2WDB_PROTOCOL) && versionMatches) {
|
||||
// console.log('Matching peer: ', thisPeerData)
|
||||
|
||||
// Temporary business logic.
|
||||
// Use the first available wallet service detected.
|
||||
if (_this.state.p2wdbProviders.length === 0) {
|
||||
_this.state.selectedP2wdbProvider = thisPeer
|
||||
|
||||
// Persist the config setting, so it can be used by other commands.
|
||||
// _this.conf.set('selectedService', thisPeer)
|
||||
console.log(`---->P2WDB service selected: ${thisPeer}`)
|
||||
}
|
||||
|
||||
// If a preferred provider is set in the config file, then connect
|
||||
// to the preferred provider when it's discovered.
|
||||
if (
|
||||
_this.config.preferredP2wdbProvider &&
|
||||
thisPeer === _this.config.preferredP2wdbProvider
|
||||
) {
|
||||
_this.state.selectedP2wdbProvider = thisPeer
|
||||
}
|
||||
|
||||
// Check if the peer has already been added to the list of providers.
|
||||
const alreadyExists = _this.state.p2wdbProviders.filter(
|
||||
(x) => x === thisPeer
|
||||
)
|
||||
|
||||
// Add the peer to the list of serviceProviders if it doesn't already
|
||||
// exist in the list.
|
||||
if (!alreadyExists.length) {
|
||||
_this.state.p2wdbProviders.push(thisPeer)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// catch and handle known failure mode.
|
||||
if (
|
||||
err.message.includes("Cannot read property 'peerList' of undefined")
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
console.error('Error in pollForP2wdbServices()')
|
||||
// Do not throw error. This is a top-level function.
|
||||
}
|
||||
}
|
||||
|
||||
// Poll the ipfs-coord coordination channel for available BCH wallet service
|
||||
// providers. This method is called periodically by a timer-interval.
|
||||
pollForBchServices () {
|
||||
try {
|
||||
// An array of IPFS IDs of other nodes in the coordination pubsub channel.
|
||||
const peers = _this.ipfsCoord.thisNode.peerList
|
||||
@@ -179,7 +269,14 @@ class IpfsCoordAdapter {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error in pollForServices(): ', err)
|
||||
// catch and handle known failure mode.
|
||||
if (
|
||||
err.message.includes("Cannot read property 'peerList' of undefined")
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
console.error('Error in pollForBchServices(): ', err)
|
||||
// Do not throw error. This is a top-level function.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
This library contains business-logic for dealing with P2WDB service
|
||||
providers. Most of these functions are called by the /p2wdb REST API endpoints.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const { v4: uid } = require('uuid')
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
|
||||
// Local libraries
|
||||
const { wlogger } = require('../wlogger')
|
||||
|
||||
let _this
|
||||
|
||||
class P2wdbAdapter {
|
||||
constructor (localConfig = {}) {
|
||||
// console.log('BCH localConfig: ', localConfig)
|
||||
this.ipfs = localConfig.ipfs
|
||||
if (!this.ipfs) {
|
||||
throw new Error(
|
||||
'An instance of IPFS must be passed when instantiating the P2WDB Adapter library.'
|
||||
)
|
||||
}
|
||||
this.eventEmitter = localConfig.eventEmitter
|
||||
if (!this.eventEmitter) {
|
||||
throw new Error(
|
||||
'An instance of an EventEmitter must be passed when instantiating the adapters.'
|
||||
)
|
||||
}
|
||||
|
||||
// Connect the RPC handler when the event fires with new data.
|
||||
this.eventEmitter.on('rpcData', this.rpcHandler)
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.uid = uid
|
||||
this.jsonrpc = jsonrpc
|
||||
|
||||
// A queue for holding RPC data that has arrived.
|
||||
this.rpcDataQueue = []
|
||||
|
||||
_this = this // Global handle on instance of this Class.
|
||||
}
|
||||
|
||||
// This handler is triggered when RPC data comes in over IPFS.
|
||||
// Handle RPC input, and add the response to the RPC queue.
|
||||
// Once in the queue, it will get processed by waitForRPCResponse()
|
||||
rpcHandler (data) {
|
||||
try {
|
||||
// Convert string input into an object.
|
||||
// const jsonData = JSON.parse(data)
|
||||
|
||||
console.log(`JSON RPC response for ID ${data.payload.id} received.`)
|
||||
|
||||
_this.rpcDataQueue.push(data)
|
||||
} catch (err) {
|
||||
console.error('Error in p2wdb/rpcHandler(): ', err)
|
||||
// Do not throw error. This is a top-level function.
|
||||
}
|
||||
}
|
||||
|
||||
// Get the status of BCH wallet services this node can talk to. Returns an
|
||||
// array of BCH wallet service providers.
|
||||
async getStatus () {
|
||||
try {
|
||||
const peerData = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
|
||||
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
|
||||
|
||||
// const status = {
|
||||
// state: this.ipfs.ipfsCoordAdapter.state
|
||||
// }
|
||||
|
||||
// Add names to the IPFS IDs for each provider.
|
||||
const initialServiceProviders =
|
||||
this.ipfs.ipfsCoordAdapter.state.p2wdbProviders
|
||||
const serviceProviders = []
|
||||
for (let i = 0; i < initialServiceProviders.length; i++) {
|
||||
const thisProvider = initialServiceProviders[i]
|
||||
|
||||
const providerData = peerData.filter((x) => x.from === thisProvider)
|
||||
|
||||
if (providerData.length) {
|
||||
const provObj = {
|
||||
ipfsId: thisProvider,
|
||||
name: providerData[0].data.jsonLd.name
|
||||
}
|
||||
|
||||
serviceProviders.push(provObj)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(
|
||||
// `serviceProviders: ${JSON.stringify(serviceProviders, null, 2)}`
|
||||
// )
|
||||
|
||||
const outObj = {
|
||||
serviceProviders,
|
||||
selectedProvider:
|
||||
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
|
||||
}
|
||||
// console.log('outObj: ', outObj)
|
||||
|
||||
return outObj
|
||||
} catch (err) {
|
||||
// console.log('createUser() error: ', err)
|
||||
wlogger.error('Error in adapters/p2wdb.js/getStatus()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Choose the BCH wallet service to use.
|
||||
async selectProvider (providerId) {
|
||||
try {
|
||||
this.ipfs.ipfsCoordAdapter.config.preferredP2wdbProvider = providerId
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
// console.log('createUser() error: ', err)
|
||||
wlogger.error('Error in adapters/p2wdb/getStatus()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Read an entry from the P2WDB, given an entry hash.
|
||||
async getEntryByHash (hash) {
|
||||
try {
|
||||
// Input validation.
|
||||
if (!hash || typeof hash !== 'string') {
|
||||
throw new Error('getEntry() input hash must be a string.')
|
||||
}
|
||||
|
||||
// Throw an error if this IPFS node has not yet made a connection to a
|
||||
// wallet service provider.
|
||||
const selectedProvider =
|
||||
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
|
||||
if (!selectedProvider) {
|
||||
throw new Error('No P2WDB Service provider available yet.')
|
||||
}
|
||||
|
||||
const rpcData = {
|
||||
endpoint: 'getByHash',
|
||||
hash
|
||||
}
|
||||
|
||||
// Generate a UUID for the call.
|
||||
const rpcId = this.uid()
|
||||
|
||||
// Generate a JSON RPC command.
|
||||
const cmd = this.jsonrpc.request(rpcId, 'p2wdb', rpcData)
|
||||
const cmdStr = JSON.stringify(cmd)
|
||||
// console.log('cmdStr: ', cmdStr)
|
||||
|
||||
// Send the RPC command to selected wallet service.
|
||||
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
|
||||
await this.ipfs.ipfsCoordAdapter.ipfsCoord.useCases.peer.sendPrivateMessage(
|
||||
selectedProvider,
|
||||
cmdStr,
|
||||
thisNode
|
||||
)
|
||||
|
||||
// Wait for data to come back from the wallet service.
|
||||
const data = await this.waitForRPCResponse(rpcId)
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
console.error('Error in getEntryByHash()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async writeEntry (writeObj) {
|
||||
try {
|
||||
const { txid, signature, message, data } = writeObj
|
||||
|
||||
// Throw an error if this IPFS node has not yet made a connection to a
|
||||
// wallet service provider.
|
||||
const selectedProvider =
|
||||
this.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider
|
||||
if (!selectedProvider) {
|
||||
throw new Error('No P2WDB Service provider available yet.')
|
||||
}
|
||||
|
||||
const rpcData = {
|
||||
endpoint: 'write',
|
||||
txid,
|
||||
signature,
|
||||
message,
|
||||
data
|
||||
}
|
||||
|
||||
// Generate a UUID for the call.
|
||||
const rpcId = this.uid()
|
||||
|
||||
// Generate a JSON RPC command.
|
||||
const cmd = this.jsonrpc.request(rpcId, 'p2wdb', rpcData)
|
||||
const cmdStr = JSON.stringify(cmd)
|
||||
// console.log('cmdStr: ', cmdStr)
|
||||
|
||||
// Send the RPC command to selected wallet service.
|
||||
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
|
||||
await this.ipfs.ipfsCoordAdapter.ipfsCoord.useCases.peer.sendPrivateMessage(
|
||||
selectedProvider,
|
||||
cmdStr,
|
||||
thisNode
|
||||
)
|
||||
|
||||
// Wait for data to come back from the wallet service.
|
||||
const result = await this.waitForRPCResponse(rpcId)
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/p2wdb/writeEntry()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a promise that resolves to data when the RPC response is recieved.
|
||||
async waitForRPCResponse (rpcId) {
|
||||
try {
|
||||
// Initialize variables for tracking the return data.
|
||||
let dataFound = false
|
||||
let cnt = 0
|
||||
|
||||
// Default return value, if the remote computer does not respond in time.
|
||||
let data = {
|
||||
success: false,
|
||||
message: 'request timed out',
|
||||
data: ''
|
||||
}
|
||||
|
||||
// Loop that waits for a response from the service provider.
|
||||
do {
|
||||
// console.log(`this.rpcDataQueue.length: ${this.rpcDataQueue.length}`)
|
||||
for (let i = 0; i < this.rpcDataQueue.length; i++) {
|
||||
const rawData = this.rpcDataQueue[i]
|
||||
// console.log(`rawData: ${JSON.stringify(rawData, null, 2)}`)
|
||||
|
||||
if (rawData.payload.id === rpcId) {
|
||||
dataFound = true
|
||||
// console.log('data was found in the queue')
|
||||
|
||||
data = rawData.payload.result.value
|
||||
|
||||
// Remove the data from the queue
|
||||
this.rpcDataQueue.splice(i, 1)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Wait between loops.
|
||||
// await this.sleep(1000)
|
||||
await this.ipfs.ipfsCoordAdapter.bchjs.Util.sleep(2000)
|
||||
|
||||
cnt++
|
||||
|
||||
// Exit if data was returned, or the window for a response expires.
|
||||
} while (!dataFound && cnt < 10)
|
||||
// console.log(`dataFound: ${dataFound}, cnt: ${cnt}`)
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
console.error('Error in waitForRPCResponse()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = P2wdbAdapter
|
||||
@@ -51,6 +51,17 @@ class BchRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /bch/provider Provider
|
||||
* @apiPermission public
|
||||
* @apiName Provider
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription Select a different BCH wallet service on the IPFS network,
|
||||
* for interacting with the BCH blockchain.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "provider": "QmXtHADckCmT6jodpAgn3TcDQWjC29gQd2fKKHDTpo8DJT" }' localhost:5001/bch/provider
|
||||
*/
|
||||
async postProvider (ctx) {
|
||||
try {
|
||||
const providerId = ctx.request.body.provider
|
||||
@@ -420,10 +431,10 @@ class BchRESTControllerLib {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {JSON} /bch PubKey
|
||||
* @api {REST} /bch/pubkey PubKey
|
||||
* @apiPermission public
|
||||
* @apiName PubKey
|
||||
* @apiGroup JSON BCH
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription Get the public key from an address.
|
||||
* Given an address the endpoint will return an object with the
|
||||
* following properties
|
||||
@@ -435,7 +446,7 @@ class BchRESTControllerLib {
|
||||
* - publickey: - Address public key
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "pubkey", "address": "bitcoincash:qpnty9t0w93fez04h7yzevujpv8pun204qv6yfuahk"}}
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "txid": "01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b" }' localhost:5001/bch/pubkey
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* {
|
||||
@@ -452,7 +463,6 @@ class BchRESTControllerLib {
|
||||
* "success": true,
|
||||
* "publicKey": "033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee"
|
||||
* }
|
||||
*
|
||||
* }
|
||||
*/
|
||||
async pubKey (ctx) {
|
||||
@@ -460,7 +470,7 @@ class BchRESTControllerLib {
|
||||
const address = ctx.request.body.address
|
||||
|
||||
const data = await this.adapters.bch.getPubKey(address)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
console.log(`pubkey data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
ctx.body = data
|
||||
} catch (err) {
|
||||
|
||||
@@ -13,6 +13,7 @@ const ContactRESTController = require('./contact')
|
||||
const LogsRESTController = require('./logs')
|
||||
const BchRESTController = require('./bch')
|
||||
const IpfsRESTController = require('./ipfs')
|
||||
const P2wdbRESTController = require('./p2wdb')
|
||||
|
||||
class RESTControllers {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -62,6 +63,10 @@ class RESTControllers {
|
||||
// Attach the REST API Controllers associated with the /ipfs route
|
||||
const ipfsRESTController = new IpfsRESTController(dependencies)
|
||||
ipfsRESTController.attach(app)
|
||||
|
||||
// Attach the REST API Controllers associated with the /p2wdb route
|
||||
const p2wdbRESTController = new P2wdbRESTController(dependencies)
|
||||
p2wdbRESTController.attach(app)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
REST API Controller library for the /p2wdb route
|
||||
*/
|
||||
|
||||
const { wlogger } = require('../../../adapters/wlogger')
|
||||
|
||||
// let _this
|
||||
|
||||
class P2wdbRESTControllerLib {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency Injection.
|
||||
this.adapters = localConfig.adapters
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Instance of Adapters library required when instantiating /p2wdb REST Controller.'
|
||||
)
|
||||
}
|
||||
this.useCases = localConfig.useCases
|
||||
if (!this.useCases) {
|
||||
throw new Error(
|
||||
'Instance of Use Cases library required when instantiating /p2wdb REST Controller.'
|
||||
)
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
// this.UserModel = this.adapters.localdb.Users
|
||||
// this.userUseCases = this.useCases.user
|
||||
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /p2wdb Get status on P2WDB infrastructure
|
||||
* @apiPermission public
|
||||
* @apiName GetP2wdbStatus
|
||||
* @apiGroup REST BCH
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X GET localhost:5001/p2wdb
|
||||
*
|
||||
*/
|
||||
async getStatus (ctx) {
|
||||
try {
|
||||
const status = await this.adapters.p2wdb.getStatus()
|
||||
|
||||
ctx.body = { status }
|
||||
} catch (err) {
|
||||
wlogger.error('Error in bch/controller.js/getStatus(): ')
|
||||
// ctx.throw(422, err.message)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /p2wdb/provider Provider
|
||||
* @apiName Provider
|
||||
* @apiGroup REST P2WDB
|
||||
* @apiDescription Select a different P2WDB service on the IPFS network, for
|
||||
* interacting with the P2WDB.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "provider": "QmXtHADckCmT6jodpAgn3TcDQWjC29gQd2fKKHDTpo8DJT" }' localhost:5001/p2wdb/provider
|
||||
*/
|
||||
async postProvider (ctx) {
|
||||
try {
|
||||
const providerId = ctx.request.body.provider
|
||||
|
||||
await this.adapters.p2wdb.selectProvider(providerId)
|
||||
|
||||
const body = {
|
||||
success: true
|
||||
}
|
||||
|
||||
ctx.body = body
|
||||
} catch (err) {
|
||||
wlogger.error('Error in p2wdb/controller.js/postProvider(): ', err)
|
||||
// ctx.throw(422, err.message)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// curl -H "Content-Type: application/json" -X POST -d '{ "hash": "zdpuAzNVEHYegiazAmbnQtvQpEvJGsdXDbcH7vHFYx4NWDUqk" }' localhost:5001/p2wdb/entryFromHash
|
||||
async getEntryByHash (ctx) {
|
||||
try {
|
||||
const hash = ctx.request.body.hash
|
||||
|
||||
const data = await this.adapters.p2wdb.getEntryByHash(hash)
|
||||
|
||||
const body = {
|
||||
success: true,
|
||||
data
|
||||
}
|
||||
|
||||
ctx.body = body
|
||||
} catch (err) {
|
||||
wlogger.error('Error in p2wdb/controller.js/getEntryByHash(): ', err)
|
||||
// ctx.throw(422, err.message)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
async writeEntry (ctx) {
|
||||
try {
|
||||
const txid = ctx.request.body.txid
|
||||
const signature = ctx.request.body.signature
|
||||
const message = ctx.request.body.message
|
||||
const data = ctx.request.body.data
|
||||
const writeObj = { txid, signature, message, data }
|
||||
|
||||
const hash = await this.adapters.p2wdb.writeEntry(writeObj)
|
||||
|
||||
ctx.body = {
|
||||
success: true,
|
||||
hash
|
||||
}
|
||||
} catch (err) {
|
||||
wlogger.error('Error in p2wdb/controller.js/writeEntry(): ', err)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
if (err.status) {
|
||||
if (err.message) {
|
||||
ctx.throw(err.status, err.message)
|
||||
} else {
|
||||
ctx.throw(err.status)
|
||||
}
|
||||
} else {
|
||||
// By default use a 422 error if the HTTP status is not specified.
|
||||
ctx.throw(422, err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = P2wdbRESTControllerLib
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
REST API library for /p2wdb route.
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
|
||||
// Local libraries.
|
||||
const P2wdbRESTControllerLib = require('./controller')
|
||||
const Validators = require('../middleware/validators')
|
||||
|
||||
let _this
|
||||
|
||||
class P2wdbRouter {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency Injection.
|
||||
this.adapters = localConfig.adapters
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Instance of Adapters library required when instantiating P2WDB REST Controller.'
|
||||
)
|
||||
}
|
||||
this.useCases = localConfig.useCases
|
||||
if (!this.useCases) {
|
||||
throw new Error(
|
||||
'Instance of Use Cases library required when instantiating P2WDB REST Controller.'
|
||||
)
|
||||
}
|
||||
|
||||
const dependencies = {
|
||||
adapters: this.adapters,
|
||||
useCases: this.useCases
|
||||
}
|
||||
|
||||
// Encapsulate dependencies.
|
||||
this.p2wdbRESTController = new P2wdbRESTControllerLib(dependencies)
|
||||
this.validators = new Validators()
|
||||
|
||||
// Instantiate the router and set the base route.
|
||||
const baseUrl = '/p2wdb'
|
||||
this.router = new Router({ prefix: baseUrl })
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
attach (app) {
|
||||
if (!app) {
|
||||
throw new Error(
|
||||
'Must pass app object when attaching REST API controllers.'
|
||||
)
|
||||
}
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.get('/', this.getStatus)
|
||||
this.router.post('/provider', this.postProvider)
|
||||
this.router.post('/entryFromHash', this.entryFromHash)
|
||||
this.router.post('/write', this.write)
|
||||
// this.router.post('/broadcast', this.postBroadcast)
|
||||
// this.router.post('/transactions', this.postTransactions)
|
||||
// this.router.post('/transaction', this.postTransaction)
|
||||
// this.router.post('/pubkey', this.postPubKey)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(this.router.routes())
|
||||
app.use(this.router.allowedMethods())
|
||||
}
|
||||
|
||||
async getStatus (ctx, next) {
|
||||
await _this.p2wdbRESTController.getStatus(ctx, next)
|
||||
}
|
||||
|
||||
async postProvider (ctx, next) {
|
||||
await _this.p2wdbRESTController.postProvider(ctx, next)
|
||||
}
|
||||
|
||||
async entryFromHash (ctx, next) {
|
||||
await _this.p2wdbRESTController.getEntryByHash(ctx, next)
|
||||
}
|
||||
|
||||
async write (ctx, next) {
|
||||
await _this.p2wdbRESTController.writeEntry(ctx, next)
|
||||
}
|
||||
|
||||
// async postBroadcast (ctx, next) {
|
||||
// await _this.bchRESTController.broadcast(ctx, next)
|
||||
// }
|
||||
//
|
||||
// async postTransactions (ctx, next) {
|
||||
// await _this.bchRESTController.transactions(ctx, next)
|
||||
// }
|
||||
//
|
||||
// async postTransaction (ctx, next) {
|
||||
// await _this.bchRESTController.transaction(ctx, next)
|
||||
// }
|
||||
//
|
||||
// async postPubKey (ctx, next) {
|
||||
// await _this.bchRESTController.pubKey(ctx, next)
|
||||
// }
|
||||
}
|
||||
|
||||
module.exports = P2wdbRouter
|
||||
@@ -29,7 +29,8 @@ describe('#IPFS', () => {
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
|
||||
clearInterval(uut.pollServiceInterval)
|
||||
clearInterval(uut.pollBchServiceInterval)
|
||||
clearInterval(uut.pollP2wdbServiceInterval)
|
||||
})
|
||||
|
||||
describe('#constructor', () => {
|
||||
@@ -125,7 +126,7 @@ describe('#IPFS', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#pollForServices', () => {
|
||||
describe('#pollForBchServices', () => {
|
||||
it('should find and select the wallet service', () => {
|
||||
uut.ipfsCoord = {
|
||||
thisNode: {
|
||||
@@ -134,7 +135,7 @@ describe('#IPFS', () => {
|
||||
}
|
||||
}
|
||||
|
||||
uut.pollForServices()
|
||||
uut.pollForBchServices()
|
||||
|
||||
// It should fine the service in the mocked data.
|
||||
assert.equal(
|
||||
@@ -144,7 +145,7 @@ describe('#IPFS', () => {
|
||||
})
|
||||
|
||||
it('should catch and report errors', () => {
|
||||
uut.pollForServices()
|
||||
uut.pollForBchServices()
|
||||
|
||||
assert.isOk(true, 'Not throwing an error is a success.')
|
||||
})
|
||||
|
||||
@@ -46,7 +46,7 @@ const peerData = [
|
||||
'@context': 'https://schema.org/',
|
||||
'@type': 'WebAPI',
|
||||
name: 'trout-bch-wallet-service-dev',
|
||||
version: '1.11.1',
|
||||
version: '1.11.12',
|
||||
protocol: 'bch-wallet',
|
||||
description:
|
||||
'IPFS service providing BCH blockchain access needed by a wallet.',
|
||||
|
||||
Reference in New Issue
Block a user