Compare commits

..
25 Commits
Author SHA1 Message Date
Chris Troutner 4a62ae64de Merge pull request #208 from Permissionless-Software-Foundation/ct-unstable
Replacing Transaction.get() with psf-slp-indexer
2022-01-17 19:21:49 -08:00
Chris Troutner 78de9e390e Merge branch 'master' into ct-unstable 2022-01-17 19:16:21 -08:00
Chris Troutner 7d8d0c2d69 Bumping major version to 5.0.0 2022-01-17 19:12:57 -08:00
Chris Troutner 656b0f6809 feat(Transaction.get()): Replacing call with proxy to psf-slp-indexer 2022-01-17 19:12:12 -08:00
Chris Troutner 48808aec14 Merge pull request #207 from Permissionless-Software-Foundation/ct-unstable
fix(slpdb): Removing slpdb from default integration tests
2022-01-17 15:10:26 -08:00
Chris Troutner d50f4a0d50 fix(slpdb): Removing slpdb from default integration tests 2022-01-17 15:07:06 -08:00
Chris Troutner f09f96d365 Merge pull request #206 from Permissionless-Software-Foundation/dh-slp-indexer
feat(indexer): Added PSF SLP Indexer to bch-js
2022-01-17 14:43:23 -08:00
Chris Troutner 7f4745f83c fix(psf-slp-indexer): Added integration tests 2022-01-17 14:39:52 -08:00
Chris Troutner dd5de1424c fix(PsfSlpIndexer.tx): Renaming txId() to tx() 2022-01-17 14:33:49 -08:00
Daniel Gonzalez 30604591bd feat(indexer): Added PSF SLP Indexer to bch-js 2022-01-17 13:37:29 -04:00
Chris Troutner aeb1efe23e Merge pull request #205 from Permissionless-Software-Foundation/ct-unstable
Adding eCash class library
2022-01-10 19:54:31 -08:00
Chris Troutner 6e43f795d7 feat(eCash): Adding eCash class library 2022-01-10 19:51:12 -08:00
Chris Troutner e07a5b9da0 Adding getBlockchainInfo() integration test 2022-01-10 14:48:43 -08:00
Chris Troutner 5ada76eace Merge branch 'master' into ct-unstable 2021-12-20 12:38:18 -08:00
Chris Troutner e605f6f386 Merge pull request #203 from Permissionless-Software-Foundation/temp
Temp
2021-12-15 05:47:56 -08:00
Chris Troutner eb9d0bea2b Merge pull request #202 from mr-zwets/documentation-toHash160
added documentation to function toHash160()
2021-12-15 05:45:17 -08:00
Chris Troutner f185f5de16 Updating integrat test scripts 2021-12-15 05:44:27 -08:00
Chris Troutner d795174916 Merge branch 'master' into documentation-toHash160 2021-12-15 05:39:47 -08:00
mathi 1294bc2149 added documentation toHash160() 2021-12-15 12:08:58 +01:00
Chris Troutner 8117448fd3 Merge pull request #201 from Permissionless-Software-Foundation/ct-unstable
fix(utxo.get()): Throwing error if address===undefined
2021-12-01 14:48:58 -08:00
Chris Troutner c7b51a38ff fix(utxo.get()): Throwing error if address===undefined 2021-12-01 14:45:39 -08:00
Chris Troutner 07d490134a Merge pull request #198 from Permissionless-Software-Foundation/salemkode
Salemkode
2021-11-30 06:38:15 -08:00
Chris Troutner a8a4eb68a6 fix(bchjs.Crypto.sha256): Updating docs 2021-11-30 06:35:19 -08:00
Chris Troutner c05e2f14e2 Merge pull request #197 from salemkode/master
edit docs of bchjs.Crypto.sha256
2021-11-30 06:34:09 -08:00
Salem Mohammed Shamakh 3540d98910 Update docs of bchjs.Crypto.sha256 2021-11-30 08:15:12 +03:00
20 changed files with 2174 additions and 1176 deletions
+2 -1
View File
@@ -1,7 +1,8 @@
Copyright 2021 Chris Troutner
Copyright 2021 Permissionless Software Foundation contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@psf/bch-js",
"version": "4.0.0",
"version": "5.0.0",
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
"author": "Chris Troutner <chris.troutner@gmail.com>",
"contributors": [
@@ -14,12 +14,12 @@
"test:integration:nft": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 -g '#nft1' test/integration/chains/bchn/slp.js",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/chains/testnet/",
"test:integration:bchn:slpdb": "export TESTSLP=1 && export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"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.0.36:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:decatur:abc": "export RESTURL=http://192.168.0.38:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"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: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/",
"test:temp2": "mocha --timeout=30000 -g '#TransactionLib' test/unit/",
+15
View File
@@ -186,6 +186,21 @@ class Address {
return cashAddress.split(':')[1]
}
/**
* @api Address.toHash160() toHash160()
* @apiName toHash160
* @apiGroup Address
* @apiDescription Converts any address format to hash160
*
* @apiExample Example usage:
* // cash address mainnet p2pkh
* bchjs.Address.toHash160("bitcoincash:qptnmya5wkly7xf97wm5ak23yqdsz3l2cyj7k9vyyh")
* // 573d93b475be4f1925f3b74ed951201b0147eac1
*
* // cash address mainnet p2sh
* bchjs.Address.toHash160("bitcoincash:pp7ushdxf5we8mcpaa3wqgsuqt639cu59ur5xu5fug")
* // 7dc85da64d1d93ef01ef62e0221c02f512e3942f
*/
// Converts any address format to hash160
toHash160 (address) {
const legacyAddress = this.toLegacyAddress(address)
+5
View File
@@ -33,10 +33,12 @@ const Encryption = require('./encryption')
const Utxo = require('./utxo')
const Transaction = require('./transaction')
const DSProof = require('./dsproof')
const Ecash = require('./ecash')
// Indexers
const Ninsight = require('./ninsight')
const Electrumx = require('./electrumx')
const PsfSlpIndexer = require('./psf-slp-indexer')
class BCHJS {
constructor (config) {
@@ -118,6 +120,9 @@ class BCHJS {
this.Transaction = new Transaction(libConfig)
this.DSProof = new DSProof(libConfig)
this.eCash = new Ecash()
this.PsfSlpIndexer = new PsfSlpIndexer(libConfig)
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ class Crypto {
* // buffer from hex
* let buffer = Buffer.from('03123464075c7a5fa6b8680afa2c962a02e7bf071c6b2395b0ac711d462cac9354', 'hex')
* bchjs.Crypto.sha256(buffer)
* // <Buffer 26 b0 78 0a 68 3a 1e 09 8e 9c b8 cf a1 b0 92 42 28 25 00 97>
* // <Buffer 97 8c 09 dd 46 09 1d 19 22 fa 01 e9 f4 a9 75 b9 1a 37 1f 26 ba 83 99 de 27 d5 38 01 15 21 21 de>
*
* */
// Translate address from any address format into a specific format.
+47
View File
@@ -0,0 +1,47 @@
/*
Utility library for converting units with eCash.
*/
class eCash {
/**
* @api eCash.toSatoshi() toSatoshi()
* @apiName toSatoshi
* @apiGroup eCash
* @apiDescription
* Convert XEC units into satoshi units
*
* @apiExample Example usage:
* // convert 10,704.35 XEC to satoshis:
* bchjs.eCash.toSatoshi(10704.35)
* // 1070435
*/
toSatoshi (xec) {
if (typeof xec !== 'number') {
throw new Error('input must be a floating number representing XEC')
}
return Math.floor(xec * 100)
}
/**
* @api eCash.toXec() toXec()
* @apiName toXec
* @apiGroup eCash
* @apiDescription
* Convert satoshi units to XEC units
*
* @apiExample Example usage:
* // convert 1,070,435 satoshis to XEC:
* bchjs.eCash.toSatoshi(1070435)
* // 10704.35
*/
toXec (sats) {
if (typeof sats !== 'number') {
throw new Error('input must be a floating number representing satoshis')
}
return sats / 100
}
}
module.exports = eCash
+332
View File
@@ -0,0 +1,332 @@
/*
This library interacts with the PSF slp indexer REST API endpoints operated
by FullStack.cash
TODO:
- detect TXs from tokens in the blacklist.
*/
// Public npm libraries
const axios = require('axios')
// Local libraries
const RawTransaction = require('./raw-transactions')
// let _this
class PsfSlpIndexer {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
}
// Encapsulate dependencies
this.rawTransaction = new RawTransaction(config)
// _this = this
}
/**
* @api PsfSlpIndexer.status() status()
* @apiName Status
* @apiGroup PSF SLP
* @apiDescription Return status from psf slp indexer.
*
* @apiExample Example usage:
* (async () => {
* try {
* let status = await bchjs.PsfSlpIndexer.status();
* console.log(status);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* "status": {
* "startBlockHeight": 543376,
* "syncedBlockHeight": 723249,
* "chainBlockHeight": 722679
* }
* }
*
*/
async status () {
try {
const response = await axios.get(
`${this.restURL}psf/slp/status`,
this.axiosOptions
)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api PsfSlpIndexer.balance() balance()
* @apiName SLP Balance
* @apiGroup PSF SLP
* @apiDescription Return slp balance for a single address.
*
* @apiExample Example usage:
* (async () => {
* try {
* let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n');
* console.log(balance);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* balance: {
* utxos: [
* {
* txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
* vout: 1,
* type: 'token',
* qty: '1800',
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
* address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
* }
* ],
* txs: [
* {
* txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
* height: 717796
* },
* {
* txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
* height: 717832
* }
* ],
* balances: [
* {
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
* qty: '1800'
* }
* ]
* }
* }
*
*/
async balance (address) {
try {
// Handle single address.
if (typeof address === 'string') {
const response = await axios.post(
`${this.restURL}psf/slp/address`,
{ address },
this.axiosOptions
)
return response.data
}
throw new Error('Input address must be a string.')
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api PsfSlpIndexer.tokenStats() tokenStats()
* @apiName Token Stats
* @apiGroup PSF SLP
* @apiDescription Return list stats for a single slp token.
*
* @apiExample Example usage:
* (async () => {
* try {
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
* console.log(tokenStats);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* tokenData: {
* type: 1,
* ticker: 'TROUT',
* name: "Trout's test token",
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
* documentUri: 'troutsblog.com',
* documentHash: '',
* decimals: 2,
* mintBatonIsActive: true,
* tokensInCirculationBN: '100098953386',
* tokensInCirculationStr: '100098953386',
* blockCreated: 622414,
* totalBurned: '1046614',
* totalMinted: '100100000000'
* txs: [
* {
* txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
* height: 622414,
* type: 'GENESIS',
* qty: '100000000000'
* }
* ]
* }
* }
*
*/
async tokenStats (tokenId) {
try {
// Handle single address.
if (typeof tokenId === 'string') {
const response = await axios.post(
`${this.restURL}psf/slp/token`,
{ tokenId },
this.axiosOptions
)
return response.data
}
throw new Error('Input tokenId must be a string.')
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api PsfSlpIndexer.tx() tx()
* @apiName SLP Transaction Data
* @apiGroup PSF SLP
* @apiDescription Return slp transaction data.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
* console.log(txData);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* txData: {
* txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
* hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
* version: 2,
* size: 339,
* locktime: 0,
* vin: [
* {
* txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
* vout: 0,
* scriptSig: {
* asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
* hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
* },
* sequence: 4294967295,
* address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
* value: 0.00051303,
* tokenQty: 0,
* tokenQtyStr: '0',
* tokenId: null
* }
* ],
* vout: [
* {
* value: 0,
* n: 0,
* scriptPubKey: {
* asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
* hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
* type: 'nulldata'
* },
* tokenQtyStr: '0',
* tokenQty: 0
* }
* ],
* hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
* blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
* confirmations: 97398,
* time: 1581773131,
* blocktime: 1581773131,
* blockheight: 622414,
* isSlpTx: true,
* tokenTxType: 'GENESIS',
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
* tokenType: 1,
* tokenTicker: 'TROUT',
* tokenName: "Trout's test token",
* tokenDecimals: 2,
* tokenUri: 'troutsblog.com',
* tokenDocHash: '',
* isValidSlp: true
* }
* }
*
*/
async tx (txid) {
try {
// Handle single address.
if (typeof txid === 'string') {
const response = await axios.post(
`${this.restURL}psf/slp/txid`,
{ txid },
this.axiosOptions
)
return response.data
}
throw new Error('Input txid must be a string.')
} catch (error) {
// console.log('error: ', error)
// Case: txid is not stored in the psf-slp-indexer tx database.
// Response: If it's not in the database, then it can be assumed the TX
// is not a token TX?
if (
error.response &&
error.response.data &&
error.response.data.error &&
error.response.data.error.includes('Key not found in database')
) {
// console.log(
// 'TX not found in psf-slp-indexer. Retrieving from full node.'
// )
// Get the TX Details from the full node.
const txDetails = await this.rawTransaction.getTxData(txid)
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
// TODO: Run the TX through decodeOpReturn() to see if it has a tokenID
// that is in the blacklist. If it is, then set isValidSlp = null to
// signal that it's state can not be determined.
// Assumption: transaction is not a valid SLP UTXO.
txDetails.isValidSlp = false
const outObj = {
txData: txDetails
}
return outObj
} else throw error
}
}
}
module.exports = PsfSlpIndexer
+1 -1
View File
@@ -36,7 +36,7 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.toSLPAddress('qzm47qz5ue99y9yl4aca7jnz7dwgdenl85jkfx3znl')
* // simpleledger:qzm47qz5ue99y9yl4aca7jnz7dwgdenl857dzayzdp
*
* // tesnet legacy
* // testnet legacy
* bchjs.SLP.Address.toSLPAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx')
* // slptest:qzq9je6pntpva3wf6scr7mlnycr54sjgeqauyclpwv
*
+2 -2
View File
@@ -432,7 +432,7 @@ class Nft1 {
* @apiExample Example usage:
*
* const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
* const children = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
* const children = await bchjs.SLP.Nft1.listNFTGroupChildren(groupId)
*
* children = {
* "nftChildren": [
@@ -471,7 +471,7 @@ class Nft1 {
* @apiExample Example usage:
*
* const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
* const group = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
* const group = await bchjs.SLP.Nft1.parentNFTGroup(tokenId)
*
* group = {
* "nftGroup": {
+11 -2
View File
@@ -2,18 +2,27 @@
High-level functions for working with Transactions
*/
// Global npm libraries
const BigNumber = require('bignumber.js')
// Local libraries
const RawTransaction = require('./raw-transactions')
const SlpUtils = require('./slp/utils')
const Blockchain = require('./blockchain')
const PsfSlpIndexer = require('./psf-slp-indexer')
class Transaction {
constructor (config) {
constructor (config = {}) {
// Encapsulate dependencies
this.slpUtils = new SlpUtils(config)
this.rawTransaction = new RawTransaction(config)
this.blockchain = new Blockchain(config)
this.psfSlpIndexer = new PsfSlpIndexer(config)
}
// Proxy the call to the psf-slp-indexer.
async get (txid) {
return await this.psfSlpIndexer.tx(txid)
}
/**
@@ -42,7 +51,7 @@ class Transaction {
// CT 10/31/21: TODO: this function should be refactored to use get2(), but
// add waterfall validation of the TX and its inputs.
async get (txid) {
async getOld (txid) {
try {
if (typeof txid !== 'string') {
throw new Error(
+4
View File
@@ -183,6 +183,10 @@ class UTXO {
*/
async get (address, useWhitelist = false) {
try {
if (!address) {
throw new Error('Address must be an array or a string')
}
// Convert address to an array if it is a string.
if (typeof address === 'string') address = [address]
+8
View File
@@ -313,6 +313,14 @@ describe('#blockchain', () => {
assert.isString(result)
})
})
describe('#getBlockchainInfo', () => {
it('should get info about the blockchain', async () => {
const result = await bchjs.Blockchain.getBlockchainInfo()
console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
})
})
})
function sleep (ms) {
@@ -0,0 +1,105 @@
/*
Integration tests for the psf-slp-indexer.js library
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
let bchjs
describe('#psf-slp-indexer', () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS()
})
describe('#status', () => {
it('should return the status of the indexer.', async () => {
const result = await bchjs.PsfSlpIndexer.status()
// console.log('result: ', result)
assert.property(result.status, 'startBlockHeight')
assert.property(result.status, 'syncedBlockHeight')
assert.property(result.status, 'chainBlockHeight')
})
})
describe('#balance', () => {
it('should get balance data for an address.', async () => {
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
const result = await bchjs.PsfSlpIndexer.balance(addr)
// console.log('result: ', result)
assert.property(result.balance, 'utxos')
assert.property(result.balance, 'txs')
assert.property(result.balance, 'balances')
})
})
describe('#tokenStats', () => {
it('should get stats on a token', async () => {
const tokenId =
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
// console.log('result: ', result)
assert.property(result.tokenData, 'documentUri')
assert.property(result.tokenData, 'txs')
assert.property(result.tokenData, 'totalBurned')
})
})
describe('#tx', () => {
it('should get hydrated tx data for an SLP transaction', async () => {
const txid =
'83361c34cac2ea7f9ca287fca57a96cc0763719f0cdf4850f9696c1e68eb635c'
const result = await bchjs.PsfSlpIndexer.tx(txid)
// console.log('result: ', result)
assert.property(result.txData, 'vin')
assert.property(result.txData, 'vout')
assert.property(result.txData, 'isValidSlp')
assert.equal(result.txData.isValidSlp, true)
})
it('should handle non-SLP transaction', async () => {
const txid =
'03d6e6b63647ce7b02ecc73dc6d41b485be14a3e20eed4474b8a840358ddf14e'
const result = await bchjs.PsfSlpIndexer.tx(txid)
// console.log('result: ', result)
assert.property(result.txData, 'vin')
assert.property(result.txData, 'vout')
assert.property(result.txData, 'isValidSlp')
assert.equal(result.txData.isValidSlp, false)
})
// FlexUSD transactions
// Currently FlexUSD UTXOs are reported as invalid SLP UTXOs, which means
// the wallet will burn them. There is a TODO in the code. This test will
// need to be changed when it is done.
it('should handle a blacklisted token', async () => {
const txid =
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
const result = await bchjs.PsfSlpIndexer.tx(txid)
// console.log('result: ', result)
assert.property(result.txData, 'vin')
assert.property(result.txData, 'vout')
assert.property(result.txData, 'isValidSlp')
assert.equal(result.txData.isValidSlp, false)
})
})
})
// Promise-based sleep function
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+241 -237
View File
@@ -30,265 +30,269 @@ describe('#SLP', () => {
bchjs = new BCHJS()
})
describe('#util', () => {
describe('#decodeOpReturn', () => {
it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
if (process.env.TESTSLP) {
describe('#util', () => {
describe('#decodeOpReturn', () => {
it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
})
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [
// Malformed SLP tx
{
note: 'Malformed SLP tx',
tx_hash:
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1,
value: 546
},
// Normal TX (non-SLP)
{
note: 'Normal TX (non-SLP)',
tx_hash:
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
tx_pos: 0,
value: 400000
},
// Valid PSF SLP tx
{
note: 'Valid PSF SLP tx',
tx_hash:
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
tx_pos: 1,
value: 546
},
// Valid SLP token not in whitelist
{
note: 'Valid SLP token not in whitelist',
tx_hash:
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1,
value: 546
},
// Token send on BCHN network.
{
note: 'Token send on BCHN network',
tx_hash:
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
tx_pos: 1,
value: 546
},
// Token send on ABC network.
{
note: 'Token send on ABC network',
tx_hash:
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
tx_pos: 1,
value: 546
},
// Known invalid SLP token send of PSF tokens.
{
note: 'Known invalid SLP token send of PSF tokens',
tx_hash:
'2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// Malformed SLP tx
{
note: 'Malformed SLP tx',
tx_hash:
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1,
value: 546
},
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
assert.equal(data[0].isValid, false)
// Normal TX (non-SLP)
{
note: 'Normal TX (non-SLP)',
tx_hash:
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
tx_pos: 0,
value: 400000
},
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
assert.equal(data[1].isValid, false)
// Valid PSF SLP tx
{
note: 'Valid PSF SLP tx',
tx_hash:
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
tx_pos: 1,
value: 546
},
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
assert.equal(data[2].isValid, true)
// Valid SLP token not in whitelist
{
note: 'Valid SLP token not in whitelist',
tx_hash:
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1,
value: 546
},
// Token send on BCHN network.
{
note: 'Token send on BCHN network',
tx_hash:
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
tx_pos: 1,
value: 546
},
// Token send on ABC network.
{
note: 'Token send on ABC network',
tx_hash:
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
tx_pos: 1,
value: 546
},
// Known invalid SLP token send of PSF tokens.
{
note: 'Known invalid SLP token send of PSF tokens',
tx_hash:
'2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
tx_pos: 1,
value: 546
}
]
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
assert.equal(data[3].isValid, true)
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// Token send on BCHN network
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
assert.equal(data[4].isValid, true)
// Malformed SLP tx
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
assert.equal(data[0].isValid, false)
// Token send on ABC network
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
assert.equal(data[5].isValid, null)
// Normal TX (non-SLP)
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
assert.equal(data[1].isValid, false)
// Known invalid SLP token send of PSF tokens
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
assert.equal(data[6].isValid, false)
})
})
// Valid PSF SLP tx
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
assert.equal(data[2].isValid, true)
describe('#validateTxid3', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
// Valid SLP token not in whitelist
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
assert.equal(data[3].isValid, true)
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Token send on BCHN network
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
assert.equal(data[4].isValid, true)
assert.isArray(result)
// Token send on ABC network
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
assert.equal(data[5].isValid, null)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
// Known invalid SLP token send of PSF tokens
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
assert.equal(data[6].isValid, false)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, null)
})
it('should validate a known valid TXID', async () => {
const txid =
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, true)
})
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Unprocessed SLP TX
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
]
const result = await bchjs.SLP.Utils.validateTxid3(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, null)
// Note: This should change from null to true once SLPDB finishes indexing.
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, null)
})
})
describe('#validateTxid', () => {
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Token send on BCHN network.
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
// Token send on ABC network.
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
]
const result = await bchjs.SLP.Utils.validateTxid(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, true)
assert.equal(result[5].txid, txids[5])
assert.equal(result[5].valid, null)
})
})
})
describe('#validateTxid3', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, null)
describe('#nft1', () => {
describe('#listNFTGroupChildren', () => {
it('should return array of children GENESIS transactions IDs', async () => {
const groupId =
'68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
const result = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftChildren')
assert.isArray(result.nftChildren)
})
})
it('should validate a known valid TXID', async () => {
const txid =
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, true)
})
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Unprocessed SLP TX
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
]
const result = await bchjs.SLP.Utils.validateTxid3(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, null)
// Note: This should change from null to true once SLPDB finishes indexing.
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, null)
describe('#parentNFTGroup', () => {
it('should return parent NFT group information', async () => {
const tokenId =
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
const result = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftGroup')
assert.property(result.nftGroup, 'id')
assert.property(result.nftGroup, 'name')
assert.property(result.nftGroup, 'symbol')
assert.property(result.nftGroup, 'documentUri')
assert.property(result.nftGroup, 'versionType')
assert.property(result.nftGroup, 'initialTokenQty')
})
})
})
describe('#validateTxid', () => {
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Token send on BCHN network.
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
// Token send on ABC network.
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
]
const result = await bchjs.SLP.Utils.validateTxid(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, true)
assert.equal(result[5].txid, txids[5])
assert.equal(result[5].valid, null)
})
})
})
describe('#nft1', () => {
describe('#listNFTGroupChildren', () => {
it('should return array of children GENESIS transactions IDs', async () => {
const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
const result = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftChildren')
assert.isArray(result.nftChildren)
})
})
describe('#parentNFTGroup', () => {
it('should return parent NFT group information', async () => {
const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
const result = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftGroup')
assert.property(result.nftGroup, 'id')
assert.property(result.nftGroup, 'name')
assert.property(result.nftGroup, 'symbol')
assert.property(result.nftGroup, 'documentUri')
assert.property(result.nftGroup, 'versionType')
assert.property(result.nftGroup, 'initialTokenQty')
})
})
})
}
})
// Promise-based sleep function
+921 -915
View File
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -11,12 +11,12 @@ describe('#Transaction', () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#get', () => {
describe('#getOld', () => {
it('should get details about a non-SLP transaction', async () => {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
const result = await bchjs.Transaction.get(txid)
const result = await bchjs.Transaction.getOld(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
@@ -37,7 +37,7 @@ describe('#Transaction', () => {
const txid =
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
const result = await bchjs.Transaction.get(txid)
const result = await bchjs.Transaction.getOld(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
@@ -69,9 +69,10 @@ describe('#Transaction', () => {
// that is also a valid SLP tx, but is unrelated. Both TXs pass DAG validation,
// but for separate tokens.
it('should get problematic transaction', async () => {
const txid = 'a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe'
const txid =
'a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe'
const result = await bchjs.Transaction.get(txid)
const result = await bchjs.Transaction.getOld(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// The token ID should equal the txid for this Vin.
+56
View File
@@ -0,0 +1,56 @@
/*
Unit tests for eCash library.
*/
// Global npm libraries
const assert = require('chai').assert
const Ecash = require('../../src/ecash')
const uut = new Ecash()
describe('#eCash', () => {
describe('#toSatoshi', () => {
it('should convert XEC to satoshis', () => {
const xec = 10704.35
const result = uut.toSatoshi(xec)
assert.equal(result, 1070435)
})
it('should throw an error if input is not a number', () => {
try {
uut.toSatoshi('test')
assert.fail('Unexpected code path')
} catch (err) {
assert.equal(
err.message,
'input must be a floating number representing XEC'
)
}
})
})
describe('#toXec', () => {
it('should convert satoshis to XEC', () => {
const sats = 1070435
const result = uut.toXec(sats)
assert.equal(result, 10704.35)
})
it('should throw an error if input is not a number', () => {
try {
uut.toXec('test')
assert.fail('Unexpected code path')
} catch (err) {
assert.equal(
err.message,
'input must be a floating number representing satoshis'
)
}
})
})
})
+130
View File
@@ -0,0 +1,130 @@
/*
Mocking data for unit tests for the PSF SLP INDEXER library.
*/
'use strict'
const tokenStats = {
tokenData: {
type: 1,
ticker: 'TP03',
name: 'Test Plugin 03',
tokenId: '13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
documentUri: 'fullstack.cash',
documentHash: '',
decimals: 0,
mintBatonIsActive: false,
tokensInCirculationBN: '1',
tokensInCirculationStr: '1',
blockCreated: 722420,
totalBurned: '0',
totalMinted: '1',
txs: [
{
txid: '13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
height: 722420,
type: 'GENESIS',
qty: '1'
}
]
}
}
const txData = {
txData: {
txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
version: 2,
size: 339,
locktime: 0,
vin: [
{
txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
vout: 0,
scriptSig: {
asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
},
sequence: 4294967295,
address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
value: 0.00051303,
tokenQty: 0,
tokenQtyStr: '0',
tokenId: null
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
type: 'nulldata'
},
tokenQtyStr: '0',
tokenQty: 0
}
],
hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
confirmations: 97398,
time: 1581773131,
blocktime: 1581773131,
blockheight: 622414,
isSlpTx: true,
tokenTxType: 'GENESIS',
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
tokenType: 1,
tokenTicker: 'TROUT',
tokenName: "Trout's test token",
tokenDecimals: 2,
tokenUri: 'troutsblog.com',
tokenDocHash: '',
isValidSlp: true
}
}
const balance = {
balance: {
utxos: [
{
txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
vout: 1,
type: 'token',
qty: '1800',
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
}
],
txs: [
{
txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
height: 717796
},
{
txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
height: 717832
}
],
balances: [
{
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
qty: '1800'
}
]
}
}
const status = {
status: {
startBlockHeight: 543376,
syncedBlockHeight: 722860,
chainBlockHeight: 722679
}
}
module.exports = {
tokenStats,
txData,
balance,
status
}
+264
View File
@@ -0,0 +1,264 @@
const chai = require('chai')
const assert = chai.assert
const axios = require('axios')
const sinon = require('sinon')
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
const mockData = require('./fixtures/psf-slp-indexer-mock')
describe('#PsfSlpIndexer', () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
describe('#status', () => {
it('should GET status', async () => {
// Stub the network call.
sandbox.stub(axios, 'get').resolves({ data: mockData.status })
const result = await bchjs.PsfSlpIndexer.status()
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'status')
assert.property(result.status, 'startBlockHeight')
assert.property(result.status, 'syncedBlockHeight')
assert.property(result.status, 'chainBlockHeight')
})
it('should handle axios error', async () => {
try {
// Stub the network call.
sandbox.stub(axios, 'get').throws(new Error('test error'))
await bchjs.PsfSlpIndexer.status()
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should handle request error', async () => {
try {
// Stub the network call.
const testErr = new Error()
testErr.response = { data: { status: 422 } }
sandbox.stub(axios, 'get').throws(testErr)
await bchjs.PsfSlpIndexer.status()
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.equal(err.status, 422)
}
})
})
describe('#balance', () => {
it('should GET balance', async () => {
// Stub the network call.
sandbox.stub(axios, 'post').resolves({ data: mockData.balance })
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
const result = await bchjs.PsfSlpIndexer.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'balance')
assert.property(result.balance, 'utxos')
assert.property(result.balance, 'txs')
assert.property(result.balance, 'balances')
assert.isArray(result.balance.utxos)
assert.isArray(result.balance.txs)
assert.isArray(result.balance.balances)
})
it('should throw an error for improper input', async () => {
try {
const addr = 12345
await bchjs.PsfSlpIndexer.balance(addr)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, 'Input address must be a string.')
}
})
it('should handle axios error', async () => {
try {
// Stub the network call.
sandbox.stub(axios, 'post').throws(new Error('test error'))
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
await bchjs.PsfSlpIndexer.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should handle request error', async () => {
try {
// Stub the network call.
const testErr = new Error()
testErr.response = { data: { status: 422 } }
sandbox.stub(axios, 'post').throws(testErr)
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
await bchjs.PsfSlpIndexer.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.equal(err.status, 422)
}
})
})
describe('#tokenStats', () => {
it('should GET token stats', async () => {
// Stub the network call.
sandbox.stub(axios, 'post').resolves({ data: mockData.tokenStats })
const tokenId =
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'tokenData')
assert.property(result.tokenData, 'type')
assert.property(result.tokenData, 'ticker')
assert.property(result.tokenData, 'name')
assert.property(result.tokenData, 'tokenId')
assert.property(result.tokenData, 'documentUri')
assert.property(result.tokenData, 'documentHash')
assert.property(result.tokenData, 'decimals')
assert.property(result.tokenData, 'mintBatonIsActive')
assert.property(result.tokenData, 'tokensInCirculationBN')
assert.property(result.tokenData, 'tokensInCirculationStr')
assert.property(result.tokenData, 'blockCreated')
assert.property(result.tokenData, 'totalBurned')
assert.property(result.tokenData, 'totalMinted')
assert.property(result.tokenData, 'txs')
})
it('should throw an error for improper input', async () => {
try {
const tokenId = 12345
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, 'Input tokenId must be a string.')
}
})
it('should handle axios error', async () => {
try {
// Stub the network call.
sandbox.stub(axios, 'post').throws(new Error('test error'))
const tokenId =
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should handle request error', async () => {
try {
// Stub the network call.
const testErr = new Error()
testErr.response = { data: { status: 422 } }
sandbox.stub(axios, 'post').throws(testErr)
const tokenId =
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.equal(err.status, 422)
}
})
})
describe('#tx', () => {
it('should GET transaction data', async () => {
// Stub the network call.
sandbox.stub(axios, 'post').resolves({ data: mockData.txData })
const txid =
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
const result = await bchjs.PsfSlpIndexer.tx(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'txData')
assert.property(result.txData, 'txid')
assert.property(result.txData, 'hash')
assert.property(result.txData, 'version')
assert.property(result.txData, 'size')
assert.property(result.txData, 'locktime')
assert.property(result.txData, 'vin')
assert.property(result.txData, 'vout')
assert.property(result.txData, 'hex')
assert.property(result.txData, 'blockhash')
assert.property(result.txData, 'confirmations')
assert.property(result.txData, 'time')
assert.property(result.txData, 'blocktime')
assert.property(result.txData, 'blockheight')
assert.property(result.txData, 'isSlpTx')
assert.property(result.txData, 'tokenTxType')
assert.property(result.txData, 'tokenId')
assert.property(result.txData, 'tokenType')
assert.property(result.txData, 'tokenTicker')
assert.property(result.txData, 'tokenName')
assert.property(result.txData, 'tokenDecimals')
assert.property(result.txData, 'tokenUri')
assert.property(result.txData, 'tokenDocHash')
assert.property(result.txData, 'isValidSlp')
})
it('should throw an error for improper input', async () => {
try {
const txid = 12345
await bchjs.PsfSlpIndexer.tx(txid)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, 'Input txid must be a string.')
}
})
it('should handle axios error', async () => {
try {
// Stub the network call.
sandbox.stub(axios, 'post').throws(new Error('test error'))
const txid =
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
await bchjs.PsfSlpIndexer.tx(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should get tx from full node if not available from slp indexer', async () => {
// Stub the call to the SLP indexer.
const testErr = {
response: { data: { error: 'Key not found in database' } }
}
sandbox.stub(axios, 'post').rejects(testErr)
// Stub the call to the full node
sandbox
.stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
.resolves({ txid: 'fakeTxid' })
const txid =
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
const result = await bchjs.PsfSlpIndexer.tx(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.txData.txid, 'fakeTxid')
})
})
})
+19 -8
View File
@@ -22,10 +22,10 @@ describe('#TransactionLib', () => {
})
afterEach(() => sandbox.restore())
describe('#get', () => {
describe('#getOld', () => {
it('should throw an error if txid is not specified', async () => {
try {
await bchjs.Transaction.get()
await bchjs.Transaction.getOld()
assert.fail('Unexpected code path!')
} catch (err) {
@@ -45,7 +45,7 @@ describe('#TransactionLib', () => {
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.nonSlpTxDetails)
const result = await bchjs.Transaction.get(txid)
const result = await bchjs.Transaction.getOld(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
@@ -84,7 +84,7 @@ describe('#TransactionLib', () => {
const txid =
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
const result = await bchjs.Transaction.get(txid)
const result = await bchjs.Transaction.getOld(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
@@ -114,7 +114,7 @@ describe('#TransactionLib', () => {
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.rejects(new Error('test error'))
await bchjs.Transaction.get(txid)
await bchjs.Transaction.getOld(txid)
assert.fail('Unexpected code path')
} catch (err) {
@@ -147,7 +147,7 @@ describe('#TransactionLib', () => {
const txid =
'874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e'
const result = await bchjs.Transaction.get(txid)
const result = await bchjs.Transaction.getOld(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
@@ -195,7 +195,7 @@ describe('#TransactionLib', () => {
const txid =
'4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa'
const result = await bchjs.Transaction.get(txid)
const result = await bchjs.Transaction.getOld(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
@@ -246,7 +246,7 @@ describe('#TransactionLib', () => {
const txid =
'6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a'
const result = await bchjs.Transaction.get(txid)
const result = await bchjs.Transaction.getOld(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
@@ -705,4 +705,15 @@ describe('#TransactionLib', () => {
assert.equal(result.isSlpTx, true)
})
})
describe('#get', () => {
it('should proxy psf-slp-indexer', async () => {
// console.log('bchjs.Transaction: ', bchjs.Transaction)
sandbox.stub(bchjs.Transaction.psfSlpIndexer, 'tx').resolves('test data')
const result = await bchjs.Transaction.get()
assert.equal(result, 'test data')
})
})
})