Compare commits

..
21 Commits
Author SHA1 Message Date
Chris Troutner 277c9ce0fd Merge pull request #217 from Permissionless-Software-Foundation/ct-unstable
feat(ipfs): Removing the ipfs pinning libraries and dependencies
2022-03-10 16:47:43 -08:00
Chris Troutner e0c07d1542 feat(ipfs): Removing the ipfs pinning libraries and dependencies 2022-03-10 16:45:27 -08:00
Chris Troutner 56bc942e87 fix(tests): Fixing broken integration test 2022-03-10 16:38:41 -08:00
Chris Troutner 34b9cefb65 fix(psf-slp-indexer): Switching URL back 2022-03-10 16:37:11 -08:00
Chris Troutner 6a35091eec fix(psf-slp-indexer): Fixing issue with url 2022-03-10 16:24:12 -08:00
Chris Troutner 02e3ff4dcd Merge pull request #216 from Permissionless-Software-Foundation/ct-unstable
fix(psf-slp-indexer): Adding flag for tx history of tokens
2022-02-06 13:26:05 -08:00
Chris Troutner 64f8adea25 fix(psf-slp-indexer): Adding flag for tx history of tokens 2022-02-06 13:23:25 -08:00
Chris Troutner bcf817f5c7 Merge pull request #215 from Permissionless-Software-Foundation/ct-unstable
Fixing integration test
2022-01-31 21:22:25 -08:00
Chris Troutner 541377e548 Fixing integration test 2022-01-31 21:18:08 -08:00
Chris Troutner 84b44b299a Merge pull request #214 from Permissionless-Software-Foundation/ct-unstable
fix(UTXO.get()): Added detection of null-tagged UTXOs
2022-01-31 21:13:50 -08:00
Chris Troutner b6456df1f4 Fixing unit tests 2022-01-31 21:11:11 -08:00
Chris Troutner b1826c04cb fix(UTXO.get()): Added detection of null-tagged UTXOs 2022-01-31 20:57:36 -08:00
Chris Troutner 71755964c8 Merge pull request #213 from Permissionless-Software-Foundation/ct-unstable
Adding Util.chunk100()
2022-01-27 17:39:47 -08:00
Chris Troutner bd1c3b622d feat(chunk100): Adding chunk100 which works just like chunk20 2022-01-27 17:37:20 -08:00
Chris Troutner bdced13230 fix(Electrumx): Added commented code for debugging sorting 2022-01-27 08:52:12 -08:00
Chris Troutner c925bfce46 Merge pull request #212 from Permissionless-Software-Foundation/ct-unstable
Integration Test Maintenance
2022-01-23 07:11:32 -08:00
Chris Troutner 056a47f6b2 fix(integration tests): Got BCHN and ABC integration tests passing 2022-01-23 07:08:41 -08:00
Chris Troutner 51038681d3 fix(transaction.js): Added integrationt test 2022-01-23 06:46:06 -08:00
Chris Troutner 1a2f144baf Merge pull request #211 from Permissionless-Software-Foundation/ct-unstable
fix(Utxo.get()): Properly handling addresses with no SLP utxos
2022-01-19 14:48:29 -08:00
Chris Troutner 6d70568082 fix(Utxo.get()): Properly handling addresses with no SLP utxos 2022-01-19 14:46:00 -08:00
Chris Troutner 0b1cf57852 fix(integration): Updating integration tests 2022-01-19 12:20:08 -08:00
15 changed files with 5076 additions and 3520 deletions
+4738 -2476
View File
File diff suppressed because it is too large Load Diff
+5 -7
View File
@@ -18,7 +18,7 @@
"test:integration:local:abc": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:local:bchn": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v5/ mocha --timeout 30000 test/integration/chains/testnet",
"test:integration:decatur:bchn": "export RESTURL=http://192.168.2.139:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:decatur:bchn": "export RESTURL=http://192.168.2.129:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:decatur:abc": "export RESTURL=http://192.168.2.141:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v5/ && mocha --timeout 30000 test/integration/",
"test:temp": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
@@ -37,13 +37,11 @@
"url": "git+https://github.com/Permissionless-Software-Foundation/bch-js.git"
},
"dependencies": {
"@psf/bip21": "2.0.1",
"@chris.troutner/bip32-utils": "1.0.5",
"@psf/bip21": "2.0.1",
"@psf/bitcoincash-ops": "2.0.0",
"@psf/bitcoincashjs-lib": "4.0.2",
"@psf/coininfo": "4.0.0",
"@uppy/core": "1.10.4",
"@uppy/tus": "1.5.12",
"axios": "^0.21.4",
"bc-bip68": "1.0.5",
"bchaddrjs-slp": "0.2.5",
@@ -65,7 +63,7 @@
"wif": "2.0.6"
},
"devDependencies": {
"apidoc": "0.17.7",
"apidoc": "^0.50.5",
"assert": "2.0.0",
"chai": "4.1.2",
"coveralls": "3.0.2",
@@ -77,10 +75,10 @@
"eslint-plugin-standard": "4.0.0",
"husky": "^4.3.8",
"lodash.clonedeep": "4.5.0",
"mocha": "9.1.3",
"mocha": "^9.2.1",
"node-mocks-http": "1.7.0",
"nyc": "15.1.0",
"semantic-release": "18.0.0",
"semantic-release": "^19.0.2",
"sinon": "9.2.2",
"standard": "^16.0.4"
},
-2
View File
@@ -28,7 +28,6 @@ const Script = require('./script')
const Price = require('./price')
const Schnorr = require('./schnorr')
const SLP = require('./slp/slp')
const IPFS = require('./ipfs')
const Encryption = require('./encryption')
const Utxo = require('./utxo')
const Transaction = require('./transaction')
@@ -111,7 +110,6 @@ class BCHJS {
this.SLP = new SLP(libConfig)
this.SLP.HDNode = this.HDNode
this.IPFS = new IPFS()
this.Utxo = new Utxo(libConfig)
this.Transaction = new Transaction(libConfig)
+15 -2
View File
@@ -638,13 +638,24 @@ class ElectrumX {
// Sort confirmed Transactions by the block height
sortConfTxs (txs, sortingOrder = 'DESCENDING') {
try {
// console.log(`sortConfTxs txs: ${JSON.stringify(txs, null, 2)}`)
// Filter out unconfirmed transactions, with a height of 0 or less.
txs = txs.filter(elem => elem.height > 0)
if (sortingOrder === 'DESCENDING') {
return txs.sort((a, b) => b.height - a.height)
// console.log('Sorting in descending order')
return txs.sort((a, b) => {
// console.log(`descending b.height: ${b.height}, a.height: ${a.height}`)
return b.height - a.height
})
}
return txs.sort((a, b) => a.height - b.height)
// console.log('Sorting in ascending order')
return txs.sort((a, b) => {
// console.log(`ascending b.height: ${b.height}, a.height: ${a.height}`)
return a.height - b.height
})
} catch (err) {
console.log('Error in util.js/sortConfTxs()')
throw err
@@ -685,6 +696,8 @@ class ElectrumX {
// Substitute zero-conf txs with the current block-height + 1
async sortAllTxs (txs, sortingOrder = 'DESCENDING') {
try {
// console.log(`sortingOrder: ${sortingOrder}`)
// Calculate the height of the next block
const nextBlock = (await this.blockchain.getBlockCount()) + 1
-454
View File
@@ -1,454 +0,0 @@
/*
This library interacts with the IPFS REST API at FullStack.cash for uploading
and downloading data from the IPFS network.
*/
const axios = require('axios')
const Uppy = require('@uppy/core')
const Tus = require('@uppy/tus')
const fs = require('fs')
let _this
class IPFS {
constructor (config) {
this.IPFS_API = process.env.IPFS_API
? process.env.IPFS_API
: 'https://ipfs-file-upload.fullstackcash.nl'
// : `http://localhost:5001`
// Default options when calling axios.
this.axiosOptions = {
headers: {
// authorization: `Token ${this.apiToken}`,
timeout: 15000 // Timeout if the server does not respond in time.
}
}
_this = this
_this.axios = axios
_this.fs = fs
// Initialize Uppy
_this.uppy = _this.initUppy()
}
// Initializes Uppy, which is used for file uploads.
initUppy () {
const uppy = Uppy({
allowMultipleUploads: false,
meta: { test: 'avatar' },
debug: false,
restrictions: {
maxFileSize: null,
maxNumberOfFiles: 1,
minNumberOfFiles: 1,
allowedFileTypes: null // type of files allowed to load
}
})
uppy.use(Tus, { endpoint: `${_this.IPFS_API}/uppy-files` })
return uppy
}
/**
* @api IPFS.createFileModelServer() createFileModelServer()
* @apiName createFileModelServer()
* @apiGroup IPFS
* @apiDescription Creates a new model on the server. If successful, will
* return an object with file data. That object contains a BCH address,
* payment amount, and _id required to be able to upload a file.
*
* @apiExample Example usage:
* (async () => {
* try {
* const path = `${__dirname}/ipfs.js`
* let fileData = await bchjs.IPFS.createFileModelServer(path);
* console.log(fileData);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* "success": true,
* "hostingCostBCH": 0.00004197,
* "hostingCostUSD": 0.01,
* "file": {
* "payloadLink": "",
* "hasBeenPaid": false,
* "_id": "5ec562319bfacc745e8d8a52",
* "schemaVersion": 1,
* "size": 4458,
* "fileName": "ipfs.js",
* "fileExtension": "js",
* "createdTimestamp": "1589994033.655",
* "hostingCost": 4196,
* "walletIndex": 49,
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
* "__v": 0
* }
*/
async createFileModelServer (path) {
try {
// Ensure the file exists.
if (!_this.fs.existsSync(path)) {
throw new Error(`Could not find this file: ${path}`)
}
// Read in the file.
const fileBuf = _this.fs.readFileSync(path)
// console.log(`fileBuf: `, fileBuf)
// console.log(`Buffer length: ${fileBuf.length}`)
// Get the file name from the path.
const splitPath = path.split('/')
const fileName = splitPath[splitPath.length - 1]
// Get the file extension.
const splitExt = fileName.split('.')
const fileExt = splitExt[splitExt.length - 1]
const fileObj = {
schemaVersion: 1,
size: fileBuf.length,
fileName: fileName,
fileExtension: fileExt
}
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
file: fileObj
})
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
return fileData.data
} catch (err) {
console.error('Error in createFileModel()')
throw err
}
}
/**
* @api IPFS.uploadFileServer() uploadFileServer()
* @apiName uploadFile()
* @apiGroup IPFS
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
* successful, it will return an object with an ID, a BCH address, and an
* amount of BCH to pay.
*
* @apiExample Example usage:
* (async () => {
* try {
* const path = `${__dirname}/ipfs.js`
* let fileData = await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52");
* console.log(fileData);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* "schemaVersion": 1,
* "size": 2374,
* "fileId": "5ec562319bfacc745e8d8a52",
* "fileName": "ipfs.js",
* "fileExtension": "js"
* }
*/
async uploadFileServer (path, modelId) {
try {
// Ensure the file exists.
if (!_this.fs.existsSync(path)) {
throw new Error(`Could not find this file: ${path}`)
}
if (!modelId) {
throw new Error(
'Must include a file model ID in order to upload a file.'
)
}
// Read in the file.
const fileBuf = _this.fs.readFileSync(path)
// Get the file name from the path.
const splitPath = path.split('/')
const fileName = splitPath[splitPath.length - 1]
// Prepare the upload object. Get a file ID from Uppy.
const id = _this.uppy.addFile({
name: fileName,
data: fileBuf,
source: 'Local',
isRemote: false
})
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
// Add the model ID, required to be allowed to upload the file.
_this.uppy.setFileMeta(id, { fileModelId: modelId })
// Upload the file to the server.
const upData = await _this.uppy.upload()
if (upData.failed.length) {
throw new Error('The file could not be uploaded')
}
if (upData.successful.length) {
delete upData.successful[0].data
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
const fileObj = {
schemaVersion: 1,
size: upData.successful[0].progress.bytesTotal,
fileId: upData.successful[0].meta.fileModelId,
fileName: upData.successful[0].name,
fileExtension: upData.successful[0].extension
}
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
return fileObj
// return true
}
return false
} catch (err) {
console.error('Error in bch-js/src/ipfs.js/upload(): ')
throw err
}
}
/**
* @api IPFS.getStatus() getStatus()
* @apiName getStatus()
* @apiGroup IPFS
* @apiDescription Gets the status of the uploaded file. Will return an object
* that indicates the payment status. If the file is paid, it should contain
* an IPFS hash.
*
* @apiExample Example usage:
* (async () => {
* try {
* const modelId = "5ec7392c2acfe57aa62e945a"
* let fileData = await bchjs.IPFS.getStatus(modelId)
* console.log(fileData)
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* "hasBeenPaid": true,
* "satCost": 4403,
* "bchAddr": "bchtest:qz5z82u0suqh80x5tfx4ht8kdrkkw664vcy44uz0wk",
* "ipfsHash": "QmRDHPhY5hCNVRMVQvS2H9uty8P1skdwgLaHpUAkEvsjcE",
* "fileId": "5ec7392c2acfe57aa62e945a",
* "fileName": "ipfs-e2e.js"
* }
*/
async getStatus (modelId) {
try {
if (!modelId) throw new Error('Must include a file model ID.')
const fileData = await _this.axios.get(
`${this.IPFS_API}/files/${modelId}`
)
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
const fileObj = {
hasBeenPaid: fileData.data.file.hasBeenPaid,
satCost: fileData.data.file.hostingCost,
bchAddr: fileData.data.file.bchAddr,
ipfsHash: fileData.data.file.payloadLink,
fileId: fileData.data.file._id,
fileName: fileData.data.file.fileName
}
return fileObj
} catch (err) {
console.error('Error in getStatus()')
throw err
}
}
/**
* @api IPFS.createFileModelWeb() createFileModelWeb()
* @apiName createFileModelWeb()
* @apiGroup IPFS
* @apiDescription Creates a new model on the server. If successful, will
* return an object with file data. That object contains a BCH address,
* payment amount, and _id required to be able to upload a file.
*
* @apiExample Example usage:
* (async () => {
* try {
* const content = ['PSF']
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
* const options = { type: "text/plain" }
* const file = new File(content,name,options)
* let fileData = await bchjs.IPFS.createFileModelWeb(file);
* console.log(fileData);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* "success": true,
* "hostingCostBCH": 0.00004197,
* "hostingCostUSD": 0.01,
* "file": {
* "payloadLink": "",
* "hasBeenPaid": false,
* "_id": "5ec562319bfacc745e8d8a52",
* "schemaVersion": 1,
* "size": 4458,
* "fileName": "ipfs.js",
* "fileExtension": "js",
* "createdTimestamp": "1589994033.655",
* "hostingCost": 4196,
* "walletIndex": 49,
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
* "__v": 0
* }
*/
async createFileModelWeb (file) {
try {
if (!file) throw new Error('File is required')
const { name, size } = file
if (!name || typeof name !== 'string') {
throw new Error("File should have the property 'name' of string type")
}
if (!size || typeof size !== 'number') {
throw new Error("File should have the property 'size' of number type")
}
// Get the file extension.
const splitExt = name.split('.')
const fileExt = splitExt[splitExt.length - 1]
const fileObj = {
schemaVersion: 1,
size: size,
fileName: name,
fileExtension: fileExt
}
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
file: fileObj
})
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
return fileData.data
} catch (err) {
console.error('Error in createFileModelWeb()')
throw err
}
}
/**
* @api IPFS.uploadFileWeb() uploadFileWeb()
* @apiName uploadFileWeb()
* @apiGroup IPFS
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
* successful, it will return an object with an ID, a BCH address, and an
* amount of BCH to pay.
*
* @apiExample Example usage:
* (async () => {
* try {
* * const content = ['PSF']
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
* const options = { type: "text/plain" }
* const file = new File(content,name,options)
* let fileData = await bchjs.IPFS.uploadFile(file, "5ec562319bfacc745e8d8a52");
* console.log(fileData);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* "schemaVersion": 1,
* "size": 2374,
* "fileId": "5ec562319bfacc745e8d8a52",
* "fileName": "ipfs.js",
* "fileExtension": "js"
* }
*/
async uploadFileWeb (file, modelId) {
try {
if (!file) throw new Error('File is required')
const { name, type, size } = file
if (!name || typeof name !== 'string') {
throw new Error("File should have the property 'name' of string type")
}
if (!type || typeof type !== 'string') {
throw new Error("File should have the property 'type' of string type")
}
if (!size || typeof size !== 'number') {
throw new Error("File should have the property 'size' of number type")
}
if (!modelId) {
throw new Error(
'Must include a file model ID in order to upload a file.'
)
}
// Prepare the upload object. Get a file ID from Uppy.
const id = _this.uppy.addFile({
name: name,
data: file,
source: 'Local',
isRemote: false
})
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
// Add the model ID, required to be allowed to upload the file.
_this.uppy.setFileMeta(id, { fileModelId: modelId })
// Upload the file to the server.
const upData = await _this.uppy.upload()
if (upData.failed.length) {
throw new Error('The file could not be uploaded')
}
if (upData.successful.length) {
delete upData.successful[0].data
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
const fileObj = {
schemaVersion: 1,
size: upData.successful[0].progress.bytesTotal,
fileId: upData.successful[0].meta.fileModelId,
fileName: upData.successful[0].name,
fileExtension: upData.successful[0].extension
}
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
return fileObj
// return true
}
return false
} catch (err) {
console.error('Error in bch-js/src/ipfs.js/uploadFileWeb(): ')
throw err
}
}
}
module.exports = IPFS
+15 -12
View File
@@ -41,7 +41,7 @@ class PsfSlpIndexer {
this.rawTransaction = new RawTransaction(config)
this.slpUtils = new SlpUtils(config)
// _this = this
// _this = this
}
/**
@@ -53,8 +53,8 @@ class PsfSlpIndexer {
* @apiExample Example usage:
* (async () => {
* try {
* let status = await bchjs.PsfSlpIndexer.status();
* console.log(status);
* let status = await bchjs.PsfSlpIndexer.status()
* console.log(status)
* } catch(error) {
* console.error(error)
* }
@@ -91,8 +91,8 @@ class PsfSlpIndexer {
* @apiExample Example usage:
* (async () => {
* try {
* let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n');
* console.log(balance);
* let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n')
* console.log(balance)
* } catch(error) {
* console.error(error)
* }
@@ -132,7 +132,8 @@ class PsfSlpIndexer {
*/
async balance (address) {
try {
console.log('balance() address: ', address)
// console.log('balance() address: ', address)
// Handle single address.
if (typeof address === 'string') {
const response = await axios.post(
@@ -154,12 +155,14 @@ class PsfSlpIndexer {
* @apiName Token Stats
* @apiGroup PSF SLP
* @apiDescription Return list stats for a single slp token.
* The second input is a Boolean, which determins the the transaction history
* of the token is included in the returned data. The default is false.
*
* @apiExample Example usage:
* (async () => {
* try {
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
* console.log(tokenStats);
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', true)
* console.log(tokenStats)
* } catch(error) {
* console.error(error)
* }
@@ -193,13 +196,13 @@ class PsfSlpIndexer {
*
*/
async tokenStats (tokenId) {
async tokenStats (tokenId, withTxHistory = false) {
try {
// Handle single address.
if (typeof tokenId === 'string') {
const response = await axios.post(
`${this.restURL}psf/slp/token`,
{ tokenId },
{ tokenId, withTxHistory },
this.axiosOptions
)
return response.data
@@ -220,8 +223,8 @@ class PsfSlpIndexer {
* @apiExample Example usage:
* (async () => {
* try {
* let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
* console.log(txData);
* let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
* console.log(txData)
* } catch(error) {
* console.error(error)
* }
+50
View File
@@ -150,6 +150,56 @@ class Util {
}
}
/**
* @api Util.chunk100() chunk100()
* @apiName chunk100
* @apiGroup Util
* @apiDescription chunk up an array into multiple arrays of 100 elements each.
* Input: arrayToSlice - a one-dimensional array of elements.
* Returns a two-dimensional array. An array of 100-element arrays.
*
* @apiExample Example usage:
* (async () => {
* try {
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,...,148, 149, 150]
*
* const chunked = bchjs.Util.chunk20(bigArray)
* console.log(chunked)
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* [
* [0,1,2,3,4,5,6,7,8,9,10,11,...,98,99],
* [100,101,102,...,148,149,150]
* ]
*/
chunk100 (arrayToSlice) {
try {
// Validate inputs
if (!Array.isArray(arrayToSlice)) {
throw new Error('input must be an array')
}
let offset = 0
const result = []
// Loop over the array and slice off chunks of 100 elements.
while (offset < arrayToSlice.length) {
const chunk = arrayToSlice.slice(offset, offset + 100)
result.push(chunk)
offset = offset + 100
}
return result
} catch (err) {
console.error('Error in chunk100()')
throw err
}
}
/**
* @api Util.sleep() sleep()
* @apiName sleep
+27 -9
View File
@@ -311,10 +311,6 @@ class UTXO {
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
*
* Note: You can pass in an optional second Boolean argument. The default
* `false` will use the normal waterfall validation method. Set to `true`,
* SLP UTXOs will be validated with the whitelist filtered SLPDB. This will
* result is many more UTXOs in the `nullUtxos` array.
*
* @apiExample Example usage:
* (async () => {
@@ -361,10 +357,22 @@ class UTXO {
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
const utxos = utxoData.utxos
let slpUtxos = []
// Get SLP UTXOs from the psf-slp-indexer
const slpUtxoData = await this.psfSlpIndexer.balance(addr)
// console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
const slpUtxos = slpUtxoData.balance.utxos
try {
const slpUtxoData = await this.psfSlpIndexer.balance(addr)
// console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
slpUtxos = slpUtxoData.balance.utxos
} catch (err) {
// console.log('err: ', err)
// Exit quietly if address has no SLP UTXOs. Otherwise, throw the error.
if (err.error && !err.error.includes('Key not found in database')) {
throw err
}
}
// Loop through the Fulcrum UTXOs.
for (let i = 0; i < utxos.length; i++) {
@@ -396,8 +404,18 @@ class UTXO {
if (!thisUtxo.isSlp) {
thisUtxo.txid = thisUtxo.tx_hash
thisUtxo.vout = thisUtxo.tx_pos
thisUtxo.isSlp = false
thisUtxo.address = addr
// Check the transaction to see if its a 'null' token, ignored by
// the indexer.
const txData = await this.psfSlpIndexer.tx(thisUtxo.tx_hash)
// console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
if (txData.txData.isValidSlp === null) {
thisUtxo.isSlp = null
} else {
thisUtxo.isSlp = false
}
// console.log(`thisUtxo.isSlp: ${thisUtxo.isSlp}`)
}
}
@@ -430,7 +448,7 @@ class UTXO {
return outObj
} catch (err) {
// console.error('Error in bchjs.utxo.get2(): ', err)
console.error('Error in bchjs.Utxo.get(): ', err)
if (err.error) throw new Error(err.error)
throw err
@@ -2,10 +2,10 @@
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
// const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
// const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS()
describe('#UTXO', () => {
beforeEach(async () => {
@@ -14,6 +14,7 @@ describe('#UTXO', () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
/*
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
@@ -31,6 +32,7 @@ describe('#UTXO', () => {
assert.isArray(result[0].nullUtxos)
})
})
*/
})
function sleep (ms) {
@@ -40,13 +40,24 @@ describe('#psf-slp-indexer', () => {
})
describe('#tokenStats', () => {
it('should get stats on a token', async () => {
it('should get stats on a token, without tx history', async () => {
const tokenId =
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
// console.log('result: ', result)
assert.property(result.tokenData, 'documentUri')
assert.property(result.tokenData, 'totalBurned')
})
it('should get stats on a token, with tx history', async () => {
const tokenId =
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId, true)
// console.log('result: ', result)
assert.property(result.tokenData, 'documentUri')
assert.property(result.tokenData, 'txs')
assert.property(result.tokenData, 'totalBurned')
@@ -0,0 +1,28 @@
/*
Integration tests for the transaction.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#Transaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#get', () => {
it('should get a tx details for a non-SLP TX with an OP_RETURN', async () => {
const txid =
'01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
const result = await bchjs.Transaction.get(txid)
// console.log('result: ', result)
assert.property(result.txData, 'txid')
assert.property(result.txData, 'vin')
assert.property(result.txData, 'vout')
assert.equal(result.txData.isValidSlp, false)
})
})
})
+127 -110
View File
@@ -6,6 +6,7 @@ const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
// const bchjs = new BCHJS({ restURL: 'http://192.168.2.129:3000/v5/' })
describe('#UTXO', () => {
beforeEach(async () => {
@@ -13,132 +14,159 @@ describe('#UTXO', () => {
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
/*
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
if (process.env.TESTSLP) {
describe('#getOld', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
const result = await bchjs.Utxo.getOld(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle an array of addresses', async () => {
const addr = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
]
const result = await bchjs.Utxo.getOld(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle NFTs and minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.getOld(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
assert.isArray(result[0].slpUtxos.type1.mintBatons)
assert.isArray(result[0].slpUtxos.type1.tokens)
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
assert.isArray(result[0].slpUtxos.nft.groupTokens)
assert.isArray(result[0].slpUtxos.nft.tokens)
})
it('should use the whitelist when flag is set', async () => {
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const useWhitelist = true
const result = await bchjs.Utxo.getOld(addr, useWhitelist)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
// Most token UTXOs should end up in the nullUtxos array.
assert.isAbove(result[0].bchUtxos.length, 0)
assert.isAbove(result[0].nullUtxos.length, 1)
})
})
it('should handle an array of addresses', async () => {
const addr = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
]
describe('#findBiggestUtxo', () => {
it('should sort UTXOs from Electrumx', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
it('should sort UTXOs from Utxos.get()', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const utxos = await bchjs.Utxo.getOld(addr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
})
it('should handle NFTs and minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
assert.isArray(result[0].slpUtxos.type1.mintBatons)
assert.isArray(result[0].slpUtxos.type1.tokens)
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
assert.isArray(result[0].slpUtxos.nft.groupTokens)
assert.isArray(result[0].slpUtxos.nft.tokens)
})
it('should use the whitelist when flag is set', async () => {
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const useWhitelist = true
const result = await bchjs.Utxo.get(addr, useWhitelist)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
// Most token UTXOs should end up in the nullUtxos array.
assert.isAbove(result[0].bchUtxos.length, 0)
assert.isAbove(result[0].nullUtxos.length, 1)
})
})
*/
}
describe('#hydrateTokenData', () => {
it('should hydrate token UTXOs', async () => {
const utxos = [
{
txid:
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
txid: '384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
vout: 1,
type: 'token',
qty: '10000000',
tokenId:
'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
},
{
txid:
'4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
txid: '4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
vout: 1,
type: 'token',
qty: '100000000',
tokenId:
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
},
{
txid:
'42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
txid: '42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
vout: 5,
type: 'token',
qty: '4764',
tokenId:
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
},
{
txid:
'06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
txid: '06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
vout: 5,
type: 'token',
qty: '238',
tokenId:
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
}
]
const result = await bchjs.Utxo.hydrateTokenData(utxos)
console.log('result: ', result)
// console.log('result: ', result)
assert.property(result[0], 'ticker')
assert.property(result[0], 'name')
assert.property(result[0], 'qtyStr')
assert.property(result[0], 'documentUri')
assert.property(result[0], 'documentHash')
})
})
@@ -164,46 +192,35 @@ describe('#UTXO', () => {
// TODO: NFTs are currently not identified as different than normal BCH UTXOs.
// The psf-slp-indexer needs to be updated to fix this issue.
it('should handle NFTs and minting batons', async () => {
it('should handle minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
console.log(`result: ${JSON.stringify(result, null, 2)}`)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that minting batons are correctly identified.
assert.isAbove(result.slpUtxos.type1.mintBatons.length, 0)
})
})
/*
describe('#findBiggestUtxo', () => {
it('should sort UTXOs from Electrumx', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
it('should return UTXOs for address with no SLP tokens', async () => {
const addr = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
assert.isAbove(result.bchUtxos.length, 0)
assert.equal(result.slpUtxos.type1.tokens.length, 0)
})
it('should sort UTXOs from Utxos.get()', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
it('should handle Group NFTs', async () => {
const addr = 'bitcoincash:qrnghwrfgccf3s5e9wnglzxegcnhje9rkcwv2eka33'
const utxos = await bchjs.Utxo.get(addr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
assert.equal(result.nullUtxos.length, 0)
})
})
*/
})
function sleep (ms) {
+22 -1
View File
@@ -328,6 +328,19 @@ const fulcrumUtxos01 = {
]
}
const fulcrumUtxos02 = {
success: true,
utxos: [
{
height: 674513,
tx_hash:
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
tx_pos: 3,
value: 38134
}
]
}
const psfSlpIndexerUtxos01 = {
balance: {
utxos: [
@@ -457,6 +470,12 @@ const genesisData03 = {
}
}
const noUtxoErr = {
success: false,
error:
'Key not found in database [bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7]'
}
module.exports = {
mockUtxoData,
mockHydratedUtxos,
@@ -464,9 +483,11 @@ module.exports = {
mockEveryUtxoType,
electrumxUtxos,
fulcrumUtxos01,
fulcrumUtxos02,
psfSlpIndexerUtxos01,
tokenUtxos01,
genesisData01,
genesisData02,
genesisData03
genesisData03,
noUtxoErr
}
-443
View File
@@ -1,443 +0,0 @@
/*
Unit tests for the IPFS Class.
*/
const assert = require('chai').assert
const sinon = require('sinon')
const BCHJS = require('../../src/bch-js')
let bchjs
const mockData = require('./fixtures/ipfs-mock')
describe('#IPFS', () => {
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
bchjs = new BCHJS()
})
afterEach(() => sandbox.restore())
describe('#initUppy', () => {
it('should initialize uppy', () => {
bchjs.IPFS.initUppy()
})
})
describe('#createFileModelServer', () => {
it('should throw an error if file does not exist', async () => {
try {
const path = '/non-existant-file'
await bchjs.IPFS.createFileModelServer(path)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(err.message, 'Could not find this file')
}
})
it('should create a new file model', async () => {
const path = `${__dirname.toString()}/ipfs.js`
sandbox
.stub(bchjs.IPFS.axios, 'post')
.resolves({ data: mockData.mockNewFileModel })
const result = await bchjs.IPFS.createFileModelServer(path)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'hostingCostBCH')
assert.property(result, 'hostingCostUSD')
assert.property(result, 'file')
assert.property(result.file, 'payloadLink')
assert.property(result.file, 'hasBeenPaid')
assert.property(result.file, '_id')
assert.property(result.file, 'schemaVersion')
assert.property(result.file, 'size')
assert.property(result.file, 'fileName')
assert.property(result.file, 'fileExtension')
assert.property(result.file, 'createdTimestamp')
assert.property(result.file, 'hostingCost')
assert.property(result.file, 'walletIndex')
assert.property(result.file, 'bchAddr')
})
})
describe('#uploadFileServer', () => {
it('should throw an error if file does not exist', async () => {
try {
const path = '/non-existant-file'
await bchjs.IPFS.uploadFileServer(path)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(err.message, 'Could not find this file')
}
})
it('should throw an error if modelId is not included', async () => {
try {
const path = `${__dirname.toString()}/ipfs.js`
await bchjs.IPFS.uploadFileServer(path)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(err.message, 'Must include a file model ID')
}
})
it('Should throw error if the file was not uploaded', async () => {
try {
const mock = {
successful: [],
failed: [
{
id: 'file id'
}
]
}
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mock)
const path = `${__dirname.toString()}/ipfs.js`
await bchjs.IPFS.uploadFileServer(path, '5ec562319bfacc745e8d8a52')
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(err)
assert.include(err.message, 'The file could not be uploaded')
}
})
it('should return file object if the file is uploaded', async () => {
try {
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mockData.uploadData)
const path = `${__dirname.toString()}/ipfs.js`
const result = await bchjs.IPFS.uploadFileServer(
path,
'5ec562319bfacc745e8d8a52'
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'schemaVersion')
assert.property(result, 'size')
assert.property(result, 'fileId')
assert.property(result, 'fileName')
assert.property(result, 'fileExtension')
} catch (err) {
// console.log(err)
assert.equal(true, false, 'Unexpected result')
}
})
})
describe('#getStatus', () => {
it('should throw an error if modelId is not included', async () => {
try {
await bchjs.IPFS.getStatus()
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(err.message, 'Must include a file model ID')
}
})
it('should get data on an unpaid file', async () => {
const modelId = '5ec7392c2acfe57aa62e945a'
sandbox
.stub(bchjs.IPFS.axios, 'get')
.resolves({ data: mockData.unpaidFileData })
const result = await bchjs.IPFS.getStatus(modelId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'hasBeenPaid')
assert.property(result, 'satCost')
assert.property(result, 'bchAddr')
assert.property(result, 'ipfsHash')
assert.property(result, 'fileId')
assert.property(result, 'fileName')
})
it('should get data on an unpaid file', async () => {
const modelId = '5ec7392c2acfe57aa62e945a'
sandbox
.stub(bchjs.IPFS.axios, 'get')
.resolves({ data: mockData.paidFileData })
const result = await bchjs.IPFS.getStatus(modelId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'hasBeenPaid')
assert.property(result, 'satCost')
assert.property(result, 'bchAddr')
assert.property(result, 'ipfsHash')
assert.property(result, 'fileId')
assert.property(result, 'fileName')
})
})
describe('#createFileModelWeb', () => {
it('should throw an error if file is undefined', async () => {
try {
let file
await bchjs.IPFS.createFileModelWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(err.message, 'File is required')
}
})
it('should throw an error if file is empty', async () => {
try {
const file = {}
await bchjs.IPFS.createFileModelWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(
err.message,
'File should have the property \'name\' of string type'
)
}
})
it("should throw an error if 'name' property is not included", async () => {
try {
const file = {
size: 5000
}
await bchjs.IPFS.createFileModelWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(
err.message,
'File should have the property \'name\' of string type'
)
}
})
it("should throw an error if 'size' property is not included", async () => {
try {
const file = {
name: 'ipfs.js'
}
await bchjs.IPFS.createFileModelWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(
err.message,
'File should have the property \'size\' of number type'
)
}
})
it('should create a new file model', async () => {
const file = {
name: 'ipfs.js',
size: 5000,
type: 'text/plain'
}
sandbox
.stub(bchjs.IPFS.axios, 'post')
.resolves({ data: mockData.mockNewFileModel })
const result = await bchjs.IPFS.createFileModelWeb(file)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'hostingCostBCH')
assert.property(result, 'hostingCostUSD')
assert.property(result, 'file')
assert.property(result.file, 'payloadLink')
assert.property(result.file, 'hasBeenPaid')
assert.property(result.file, '_id')
assert.property(result.file, 'schemaVersion')
assert.property(result.file, 'size')
assert.property(result.file, 'fileName')
assert.property(result.file, 'fileExtension')
assert.property(result.file, 'createdTimestamp')
assert.property(result.file, 'hostingCost')
assert.property(result.file, 'walletIndex')
assert.property(result.file, 'bchAddr')
})
})
describe('#uploadFileWeb', () => {
it('should throw an error if file is undefined', async () => {
try {
let file
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(err.message, 'File is required')
}
})
it('should throw an error if file is empty', async () => {
try {
const file = {}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(
err.message,
'File should have the property \'name\' of string type'
)
}
})
it("should throw an error if 'name' property is not included", async () => {
try {
const file = {
size: 5000,
type: 'text/plain'
}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(
err.message,
'File should have the property \'name\' of string type'
)
}
})
it("should throw an error if 'size' property is not included", async () => {
try {
const file = {
name: 'ipfs.js',
type: 'text/plain'
}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(
err.message,
'File should have the property \'size\' of number type'
)
}
})
it("should throw an error if 'type' property is not included", async () => {
try {
const file = {
name: 'ipfs.js',
size: 5000
}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(
err.message,
'File should have the property \'type\' of string type'
)
}
})
it('should throw an error if modelId is not included', async () => {
try {
const file = {
name: 'ipfs.js',
size: 5000,
type: 'text/plain'
}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(err.message, 'Must include a file model ID')
}
})
it('Should throw error if the file was not uploaded', async () => {
try {
const mock = {
successful: [],
failed: [
{
id: 'file id'
}
]
}
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mock)
const file = {
name: 'ipfs.js',
size: 5000,
type: 'text/plain'
}
await bchjs.IPFS.uploadFileWeb(file, '5ec562319bfacc745e8d8a52')
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(err)
assert.include(err.message, 'The file could not be uploaded')
}
})
it('should return file object if the file is uploaded', async () => {
try {
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mockData.uploadData)
const file = {
name: 'ipfs.js',
size: 5000,
type: 'text/plain'
}
const result = await bchjs.IPFS.uploadFileWeb(
file,
'5ec562319bfacc745e8d8a52'
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'schemaVersion')
assert.property(result, 'size')
assert.property(result, 'fileId')
assert.property(result, 'fileName')
assert.property(result, 'fileExtension')
} catch (err) {
// console.log(err)
assert.equal(true, false, 'Unexpected result')
}
})
})
})
+32
View File
@@ -249,6 +249,10 @@ describe('#utxo', () => {
sandbox
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
.resolves(mockData.psfSlpIndexerUtxos01)
sandbox
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
.resolves({ txData: { isValidSlp: false } })
// Mock function to return the same input. Good enough for this test.
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x)
@@ -272,5 +276,33 @@ describe('#utxo', () => {
assert.equal(result.slpUtxos.type1.mintBatons.length, 1)
assert.equal(result.nullUtxos.length, 0)
})
it('should handle an address with no SLP UTXOs', async () => {
// mock dependencies
sandbox
.stub(bchjs.Utxo.electrumx, 'utxo')
.resolves(mockData.fulcrumUtxos02)
sandbox
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
.resolves({ txData: { isValidSlp: false } })
// Force psf-slp-indexer to return no UTXOs
sandbox
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
.rejects(mockData.noUtxoErr)
// Mock function to return the same input. Good enough for this test.
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(() => [])
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.bchUtxos.length, 1)
assert.equal(result.slpUtxos.type1.tokens.length, 0)
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
assert.equal(result.nullUtxos.length, 0)
})
})
})