fix(p2wdb): Refactored p2wdb adapter and unit tests

This commit is contained in:
Chris Troutner
2022-03-09 07:53:26 -08:00
parent f812440555
commit 61bb5169b3
13 changed files with 478 additions and 1483 deletions
+4 -2
View File
@@ -37,6 +37,9 @@ module.exports = {
? process.env.CONSUMER_URL
: 'https://free-bch.fullstack.cash',
// P2WDB URL that will accept API calls from the p2wdb npm library.
p2wdbUrl: process.env.P2WDB_URL ? process.env.P2WDB_URL : 'https://p2wdb.fullstack.cash',
// FullStack.cash account information, used for automatic JWT handling.
getJwtAtStartup: process.env.GET_JWT_AT_STARTUP ? true : false,
authServer: process.env.AUTHSERVER
@@ -67,8 +70,7 @@ module.exports = {
name: ipfsCoordName,
version,
protocol: 'generic-service',
description:
'This is a generic IPFS Serivice Provider that uses JSON RPC over IPFS to communicate with it. This instance has not been customized. Source code: https://github.com/Permissionless-Software-Foundation/ipfs-service-provider',
description: 'This is a generic IPFS Serivice Provider that uses JSON RPC over IPFS to communicate with it. This instance has not been customized. Source code: https://github.com/Permissionless-Software-Foundation/ipfs-service-provider',
documentation: 'https://ipfs-service-provider.fullstack.cash/',
provider: {
'@type': 'Organization',
+1 -1
View File
@@ -7,6 +7,6 @@
module.exports = {
session: 'secret-boilerplate-token',
token: 'secret-jwt-token',
database: 'mongodb://localhost:27017/swap-service-dev',
database: 'mongodb://localhost:27017/bch-swap-service-dev',
env: 'dev'
}
+1 -1
View File
@@ -13,6 +13,6 @@ module.exports = {
// database: 'mongodb://172.17.0.1:5555/ipfs-service-prod',
database: process.env.DBURL
? process.env.DBURL
: 'mongodb://172.17.0.1:5555/swap-service-prod',
: 'mongodb://172.17.0.1:5555/bch-swap-service-prod',
env: 'prod'
}
+1 -1
View File
@@ -7,6 +7,6 @@
module.exports = {
session: 'secret-boilerplate-token',
token: 'secret-jwt-token',
database: 'mongodb://localhost:27017/swap-service-test',
database: 'mongodb://localhost:27017/bch-swap-service-test',
env: 'test'
}
+196 -1403
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -27,7 +27,7 @@
},
"repository": "Permissionless-Software-Foundation/bch-dex",
"dependencies": {
"@psf/bch-js": "5.3.2",
"@psf/bch-js": "../bch-js",
"axios": "0.21.1",
"bch-message-lib": "2.1.4",
"bcryptjs": "2.4.3",
@@ -55,6 +55,7 @@
"mongoose": "5.13.13",
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.4.17",
"p2wdb": "../p2wdb",
"passport-local": "1.0.0",
"public-ip": "^4.0.4",
"winston": "3.3.3",
+3 -2
View File
@@ -18,7 +18,7 @@ const JSONFiles = require('./json-files')
const FullStackJWT = require('./fullstack-jwt')
const BCHAdapter = require('./bch')
const WalletAdapter = require('./wallet')
const P2wdbAdapter = require('./p2wdb')
const P2wdbAdapter = require('./p2wdb-adapter')
//
// // Instantiate adapter libraries.
@@ -52,10 +52,11 @@ class Adapters {
this.nodemailer = new Nodemailer()
this.jsonFiles = new JSONFiles()
this.bchjs = new BCHJS()
localConfig.bchjs = this.bchjs
this.bch = new BCHAdapter()
this.config = config
this.wallet = new WalletAdapter()
this.p2wdb = new P2wdbAdapter()
this.p2wdb = new P2wdbAdapter(localConfig)
// Get a valid JWT API key and instance bch-js.
this.fullStackJwt = new FullStackJWT(config)
+1
View File
@@ -14,6 +14,7 @@ const Offer = new mongoose.Schema({
utxoTxid: { type: String },
utxoVout: { type: Number },
numTokens: { type: Number },
hdIndex: { type: Number }, // HD index address holding the UTXO for this offer.
//
offerIpfsId: { type: String },
+107
View File
@@ -0,0 +1,107 @@
/*
Adapter library for interacting with the P2WDB
*/
// Public npm libraries.
const axios = require('axios')
const { Write, Read } = require('p2wdb/index')
// Local libraries
const config = require('../../config')
class P2wdbAdapter {
constructor (localConfig = {}) {
// Dependency injection
this.bchjs = localConfig.bchjs
if (!this.bchjs) {
throw new Error('Must pass an instance of bch-js when instantiating p2wdb.js adapter.')
}
// Encapsulate dependencies
this.axios = axios
this.config = config
this.Write = Write
this.Read = Read
// Allow the localConfig to overwrite the config file values.
this.p2wdbURL = localConfig.p2wdbURL || config.p2wdbUrl
}
// Write some data to the P2WDB
async write (inputObj = {}) {
try {
const { appId = 'test', data = {}, wif } = inputObj
if (typeof wif !== 'string' || !wif) {
throw new Error('wif input must be a private key starting with the letter L or K')
}
const p2write = this.instantiateWriteLib(wif)
const result = await p2write.postEntry(data, appId)
return result.hash
} catch (err) {
console.error('Error in p2wdb.js/write()')
throw err
}
}
// Instantiate the Write library with a WIF, handling the various config
// settings.
instantiateWriteLib (wif) {
try {
// Object used to configure the Write library.
let configObj = {}
if (this.config.useFullStackCash) {
// Use web 2 infrastructure.
// bch-js and FullStack.cash
configObj = {
wif,
serverURL: this.p2wdbURL,
restURL: this.bchjs.restURL,
apiToken: this.bchjs.apiToken
}
} else {
// Use web 3 infrastructure.
// ipfs-bch-wallet-consumer and bch-consumer library
configObj = {
wif,
serverURL: this.p2wdbURL,
interface: 'consumer-api',
restURL: this.config.consumerUrl
}
}
const write = new this.Write(configObj)
return write
} catch (err) {
console.error('Error in p2wdb-adapter.js/instantitateWriteLib()')
throw err
}
}
// Returns a promise that resolves to true or false, to indicate if the
// WIF controls the required funds to write to the P2WDB.
async checkForSufficientFunds (wif) {
try {
if (typeof wif !== 'string' || !wif) {
throw new Error('invalid wif to check for funds')
}
// Instatiate the Write library
const p2write = this.instantiateWriteLib(wif)
return await p2write.checkForSufficientFunds()
} catch (err) {
console.error('Error in p2wdb.js/checkForSufficientFunds()')
throw err
}
}
}
module.exports = P2wdbAdapter
-57
View File
@@ -1,57 +0,0 @@
/*
Adapter library for interacting with the P2WDB
*/
// Public npm libraries.
const axios = require('axios')
// Local libraries
const config = require('../../config')
// Global constants
let P2WDB_SERVER = 'http://localhost:5001/entry/write'
// const P2WDB_SERVER = 'https://p2wdb.fullstack.cash/entry/write'
class P2wdbAdapter {
constructor (localConfig = {}) {
// Encapsulate dependencies
this.axios = axios
this.config = config
P2WDB_SERVER = `http://localhost:${config.p2wdbPort}/entry/write`
}
async write (inputObj) {
try {
const { txid, signature, message, appId, data } = inputObj
// TODO: Input validation
const now = new Date()
const dataObj = {
appId,
data,
timestamp: now.toISOString(),
localTimeStamp: now.toLocaleString()
}
const bodyData = {
txid,
message,
signature,
data: JSON.stringify(dataObj)
}
const result = await this.axios.post(P2WDB_SERVER, bodyData)
// console.log(`Response from API: ${JSON.stringify(result.data, null, 2)}`)
return result.data.hash
} catch (err) {
console.error('Error in p2wdb.js/write()')
throw err
}
}
}
module.exports = P2wdbAdapter
@@ -0,0 +1,54 @@
/*
Integration tests for hte p2wdb-adapter.js library
These integration tests may not necessarily pass. They require a WIF set
as an environment variable, and loaded with enough BCH and PSF to make
a write to the P2WDB.
If these tests fail, it does not necessarily mean there is a problem with the
code. Check that the WIF envrionment variable meets the requirements.
*/
// Public npm libraries
const BCHJS = require('@psf/bch-js')
// Local libraries
const P2wdbAdapter = require('../../../src/adapters/p2wdb-adapter')
const wif = process.env.WIF
if (!wif) {
throw new Error('You must provide a BCH WIF private key as an environment variable to run this test.')
}
describe('#p2wdb-adapter.js', () => {
let uut
beforeEach(() => {
const bchjs = new BCHJS()
uut = new P2wdbAdapter({ bchjs })
})
describe('#checkForSufficientFunds', () => {
it('should check for sufficient funds', async () => {
const result = await uut.checkForSufficientFunds(wif)
console.log('Provided WIF has funds for making a write: ', result)
})
})
describe('#write', () => {
it('should write data to the P2WDB', async () => {
const inObj = {
appId: 'test234',
data: {
key: 'value'
},
wif
}
const result = await uut.write(inObj)
console.log('result: ', result)
})
})
})
+108 -15
View File
@@ -5,48 +5,141 @@
// Public npm libraries.
const assert = require('chai').assert
const sinon = require('sinon')
const BCHJS = require('@psf/bch-js')
// Local libraries.
const P2wdbAdapter = require('../../../src/adapters/p2wdb')
const P2wdbAdapter = require('../../../src/adapters/p2wdb-adapter')
describe('#P2wdbAdapter', () => {
let uut, sandbox
beforeEach(() => {
uut = new P2wdbAdapter()
const bchjs = new BCHJS()
uut = new P2wdbAdapter({ bchjs })
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#write', () => {
it('should write an offer to the P2WDB', async () => {
const inputObj = {
txid: 'testTxid',
signature: 'testSignature',
message: 'testMessage',
appId: 'testAppId',
data: { key: 'value' }
describe('#constructor', () => {
it('should throw error if bch-js is not passed in', () => {
try {
uut = new P2wdbAdapter()
assert.fail('Unexpected coded path')
} catch (err) {
assert.include(err.message, 'Must pass an instance of bch-js when instantiating p2wdb.js adapter.')
}
})
})
// Mock axios to prevent live network call
sandbox.stub(uut.axios, 'post').resolves({ data: { hash: 'testhash' } })
describe('#instantiateWriteLib', () => {
it('should instantiate Write library using web 3 interface', async () => {
// Force code path
uut.config.useFullStackCash = false
const result = await uut.write(inputObj)
// Mock dependencies
uut.Write =
class Write {
constructor () {
this.interface = 'web3'
}
}
const wif = 'L1tcvcqa5PztqqDH4ZEcUmHA9aSHhTau5E2Zwp1xEK5CrKBrjP3m'
const result = uut.instantiateWriteLib(wif)
// console.log('result: ', result)
assert.equal(result, 'testhash')
assert.equal(result.interface, 'web3')
})
it('should instantiate Write library using web 2 interface', async () => {
// Force code path
uut.config.useFullStackCash = true
// Mock dependencies
uut.Write =
class Write {
constructor () {
this.interface = 'fullstack'
}
}
const wif = 'L1tcvcqa5PztqqDH4ZEcUmHA9aSHhTau5E2Zwp1xEK5CrKBrjP3m'
const result = uut.instantiateWriteLib(wif)
// console.log('result: ', result)
assert.equal(result.interface, 'fullstack')
})
it('should catch and throw errors', async () => {
try {
await uut.instantiateWriteLib()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'WIF private key required')
}
})
})
describe('#checkForSufficientFunds', () => {
it('should throw error if WIF is not provided', async () => {
try {
await uut.checkForSufficientFunds()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'invalid wif to check for funds')
}
})
it('should return status of funds', async () => {
// Mock dependencies
sandbox.stub(uut, 'instantiateWriteLib').returns({
checkForSufficientFunds: () => true
})
const wif = 'L1tcvcqa5PztqqDH4ZEcUmHA9aSHhTau5E2Zwp1xEK5CrKBrjP3m'
const result = await uut.checkForSufficientFunds(wif)
assert.equal(result, true)
})
})
describe('#write', async () => {
it('should throw error if WIF is not provided', async () => {
try {
await uut.write()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'Cannot destructure property')
assert.include(err.message, 'wif input must be a private key starting with the letter L or K')
}
})
it('should write data to the P2WDB', async () => {
// Mock dependencies
sandbox.stub(uut, 'instantiateWriteLib').returns({
postEntry: async () => {
return { hash: 'fake-hash' }
}
})
const wif = 'L1tcvcqa5PztqqDH4ZEcUmHA9aSHhTau5E2Zwp1xEK5CrKBrjP3m'
const inObj = { wif }
const result = await uut.write(inObj)
// console.log('result: ', result)
assert.equal(result, 'fake-hash')
})
})
})