Compare commits

...
16 Commits
Author SHA1 Message Date
TRBC - The Real Bitcoin Club 1bcbd7bfd8 Merge branch 'master' into grant/issue10 2020-11-12 16:43:12 -04:00
Chris Troutner 248b2fa2a6 Merge pull request #57 from Permissionless-Software-Foundation/ct-unstable
fix(basic auth): Fixing some bugs around basic auth
2020-11-12 09:52:29 -08:00
Chris Troutner 563cb297fe fix(basic auth): Fixing some bugs around basic auth 2020-11-12 09:48:54 -08:00
TRBC 788b07914e Merge branch 'grant/issue10' of https://github.com/Permissionless-Software-Foundation/bch-js into grant/issue10 2020-11-12 04:15:14 -04:00
TRBC 9aefdbf6be mock array in test address details 2020-11-12 04:14:51 -04:00
TRBC - The Real Bitcoin Club 481c254061 Merge branch 'master' into grant/issue10 2020-11-12 04:10:47 -04:00
TRBC bc62510b28 POST, isArray check, 100% test 2020-11-11 23:04:28 -04:00
Chris Troutner 4c020240e1 fix(rate limits): Updated e2e tests for post-fork servers 2020-11-11 18:39:57 -08:00
Chris Troutner e070409a95 Merge pull request #56 from Permissionless-Software-Foundation/ct-unstable
Basic Authentication
2020-11-11 18:20:09 -08:00
Chris Troutner fbbd855145 feat(Basic Authentication): Adding authPass for auth token to all libraries 2020-11-11 17:47:46 -08:00
Chris Troutner 731987b35b fix(Basic Authentication): Adding back in 2020-11-11 14:56:53 -08:00
Chris Troutner 1e8e7b79f2 Updating comments in e2e rate limit tests 2020-11-11 14:11:13 -08:00
TRBC a71192257e add testnet integration tests 2020-11-11 15:34:37 -04:00
TRBC 0fd9f9ec89 a little sexy refactoring to reduce maintenance cost 2020-11-11 15:12:00 -04:00
TRBC 97361b8edd replace all this calls with _this 2020-11-11 15:07:13 -04:00
TRBC 263ee1b6e1 solved issue #10 2020-11-11 15:05:33 -04:00
28 changed files with 825 additions and 204 deletions
+17
View File
@@ -11,6 +11,23 @@ class Address {
this.restURL = tmp.restURL
this.apiToken = tmp.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}`
}
}
}
}
/**
+19 -1
View File
@@ -55,9 +55,27 @@ class BCHJS {
else if (process.env.BCHJSTOKEN && process.env.BCHJSTOKEN !== "")
this.apiToken = process.env.BCHJSTOKEN
// Retrieve the Basic Authentication password.
this.authPass = "" // default value.
if (config && config.authPass && config.authPass !== "")
this.authPass = config.authPass
else if (process.env.BCHJSAUTHPASS && process.env.BCHJSAUTHPASS !== "")
this.authPass = process.env.BCHJSAUTHPASS
// Generate a Basic Authentication token from an auth password
this.authToken = ""
if (this.authPass) {
// console.log(`bch-js initialized with authPass: ${this.authPass}`)
// Generate the header for Basic Authentication.
const combined = `fullstackcash:${this.authPass}`
var base64Credential = Buffer.from(combined).toString("base64")
this.authToken = `Basic ${base64Credential}`
}
const libConfig = {
restURL: this.restURL,
apiToken: this.apiToken
apiToken: this.apiToken,
authToken: this.authToken
}
// console.log(`apiToken: ${this.apiToken}`)
+14 -4
View File
@@ -15,12 +15,22 @@ class Blockbook {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// console.log(`Blockbook apiToken: ${this.apiToken}`)
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+14 -4
View File
@@ -11,11 +11,21 @@ class Blockchain {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+14 -4
View File
@@ -10,11 +10,21 @@ class Control {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+14 -5
View File
@@ -11,12 +11,21 @@ class ElectrumX {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`,
timeout: 15000
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}`
}
}
}
+14 -5
View File
@@ -11,12 +11,21 @@ class Encryption {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.axios = axios
// console.log(`Blockbook apiToken: ${this.apiToken}`)
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+14 -4
View File
@@ -6,11 +6,21 @@ class Generating {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+14 -4
View File
@@ -6,11 +6,21 @@ class Mining {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+84 -82
View File
@@ -68,34 +68,10 @@ class Ninsight {
*/
async utxo(address) {
try {
// Handle single address.
if (typeof address === "string") {
const response = await axios.post(
`${this.ninsightURL}/address/utxo`,
{
addresses: [address]
},
_this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.ninsightURL}/address/utxo`,
{
addresses: address
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
_this._validateParam(address)
return _this._callAxios(address, 'utxo')
} catch (e) {
_this._handleError(e)
}
}
@@ -137,32 +113,10 @@ class Ninsight {
*/
async unconfirmed(address) {
try {
if (typeof address === "string") {
const response = await axios.post(
`${this.ninsightURL}/address/unconfirmed`,
{
addresses: [address]
},
_this.axiosOptions
)
return response.data
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.ninsightURL}/address/unconfirmed`,
{
addresses: address
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
_this._validateParam(address)
return _this._callAxios(address, 'unconfirmed')
} catch (e) {
_this._handleError(e)
}
}
@@ -213,32 +167,10 @@ class Ninsight {
*/
async transactions(address) {
try {
if (typeof address === "string") {
const response = await axios.post(
`${this.ninsightURL}/address/transactions`,
{
addresses: [address]
},
_this.axiosOptions
)
return response.data
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.ninsightURL}/address/transactions`,
{
addresses: address
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
_this._validateParam(address)
return _this._callAxios(address, 'transactions')
} catch (e) {
_this._handleError(e)
}
}
@@ -288,7 +220,7 @@ class Ninsight {
try {
if (typeof txid === "string") {
const response = await axios.post(
`${this.ninsightURL}/transaction/details`,
`${_this.ninsightURL}/transaction/details`,
{
txids: [txid]
},
@@ -298,7 +230,7 @@ class Ninsight {
return response.data
} else if (Array.isArray(txid)) {
const response = await axios.post(
`${this.ninsightURL}/transaction/details`,
`${_this.ninsightURL}/transaction/details`,
{
txids: txid
},
@@ -314,6 +246,76 @@ class Ninsight {
else throw error
}
}
/**
* @api Ninsight.details() details()
* @apiName Ninsight Details
* @apiGroup Ninsight
* @apiDescription Return details of address.
*
* @apiExample Example usage:
* (async () => {
* try {
* let details = await bchjs.Ninsight.details('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
* let details = await bchjs.Ninsight.details(['bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c','bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c']);
* console.log(details);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // [
* // {
* //"balance": 0.00001,
* //"balanceSat": 1000,
* //"totalReceived": 0.00001,
* //"totalReceivedSat": 1000,
* //"totalSent": 0,
* //"totalSentSat": 0,
* //"unconfirmedBalance": 0,
* //"unconfirmedBalanceSat": 0,
* //"unconfirmedTxApperances": 0,
* //"txApperances": 1,
* //"transactions": [
* //"5f09d317e24c5d376f737a2711f3bd1d381abdb41743fff3819b4f76382e1eac" ],
* //"legacyAddress": "1FZrK8HohEKyKCTM24NkAzPnX9WD9Ujhw7",
* //"cashAddress": "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr",
* //"slpAddress": "simpleledger:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ylynt40sa",
* //"currentPage": 0,
* //"pagesTotal": 1
* // }
* // ]
*
*/
async details(address) {
try {
_this._validateParam(address)
return _this._callAxios(address, 'details')
} catch (e) {
_this._handleError(e)
}
}
_validateParam(address) {
if (typeof address !== "string" && !Array.isArray(address))
throw new Error(`Input address must be a string or array of strings.`)
}
_handleError(error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
async _callAxios(address, type) {
const response = await axios.post(
`${_this.ninsightURL}/address/${type}`,
{
addresses: Array.isArray(address) ? address : [address]
},
_this.axiosOptions
)
//console.log(`SAMPLE: ${response.data}`);
return response.data
}
}
module.exports = Ninsight
+16 -8
View File
@@ -6,15 +6,23 @@ class Price {
constructor(config) {
_this = this
if (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
}
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+16 -10
View File
@@ -6,11 +6,21 @@ class RawTransactions {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
@@ -262,9 +272,7 @@ class RawTransactions {
txids: txid,
verbose: verbose
},
headers: {
authorization: `Token ${this.apiToken}`
}
headers: _this.axiosOptions.headers
}
const response = await axios(options)
@@ -337,9 +345,7 @@ class RawTransactions {
data: {
hexes: hex
},
headers: {
authorization: `Token ${this.apiToken}`
}
headers: _this.axiosOptions.headers
}
const response = await axios(options)
+14 -4
View File
@@ -4,11 +4,21 @@ class Schnorr {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
}
+20 -11
View File
@@ -18,22 +18,31 @@ const Utils = require("./utils")
// SLP is a superset of BITBOX
class SLP {
constructor(config) {
const tmp = {}
if (!config || !config.restURL) {
tmp.restURL = `https://api.fullstack.cash/v3/`
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 {
tmp.restURL = config.restURL
tmp.apiToken = config.apiToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
}
this.restURL = tmp.restURL
this.apiToken = tmp.apiToken
this.Address = new Address(tmp)
this.Address = new Address(config)
this.ECPair = ECPair
this.TokenType1 = new TokenType1(tmp)
this.TokenType1 = new TokenType1(config)
this.NFT1 = new NFT1(this.restURL)
this.Utils = new Utils(tmp)
this.Utils = new Utils(config)
}
}
+17 -6
View File
@@ -18,17 +18,28 @@ class TokenType1 {
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}`
}
}
}
addy = new Address(config)
this.Script = new Script()
this.axios = axios
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
// Instantiate the transaction builder.
TransactionBuilder.setAddress(addy)
+14 -4
View File
@@ -13,11 +13,21 @@ class Utils {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.slpParser = slpParser
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+14 -4
View File
@@ -6,11 +6,21 @@ class Util {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
@@ -5,11 +5,17 @@
To Run Test:
- Update the RESTURL for bch-api you want to test against.
- Ensure the BCHJSTOKEN environment variable is set to an empty string.
- If working with bch-api locally, eliminate the local IP address from the
whitelist in bch-api/src/middleware/route-ratelimit.js
Run this test with this command:
mocha --timeout=30000 anonymous-rate-limits.js
*/
const assert = require("chai").assert
const RESTURL = `https://api.fullstack.cash/v3/`
// const RESTURL = `https://abc.fullstack.cash/v3/`
const RESTURL = `https://bchn.fullstack.cash/v3/`
// const RESTURL = `http://localhost:3000/v3/`
const BCHJS = require("../../../src/bch-js")
@@ -24,7 +30,7 @@ describe("#anonymous rate limits", () => {
it("should allow an anonymous call to an indexer", async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
const result = await bchjs.Blockbook.balance(addr)
const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "balance")
@@ -32,7 +38,7 @@ describe("#anonymous rate limits", () => {
it("should throw error when rate limit exceeded", async () => {
try {
for (let i = 0; i < 22; i++) await bchjs.Control.getNetworkInfo()
for (let i = 0; i < 35; i++) await bchjs.Control.getNetworkInfo()
assert.fail("Unexpected result")
} catch (err) {
@@ -0,0 +1,56 @@
/*
Mocha test that verifies that the bch-api server is enforcing its
access rules for Basic Authentication.
To Run Test:
- Update the restURL for bch-api you want to test against.
- Update the PRO_PASS value with a current PRO_PASSES string used by bch-api.
*/
const assert = require("chai").assert
const PRO_PASS = "testpassword"
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v3/`,
restURL: `https://abc.fullstack.cash/v3/`,
// restURL: `http://localhost:3000/v3/`,
authPass: PRO_PASS
})
describe("#Basic Authentication rate limits", () => {
it("should allow more than 20 RPM to full node", async () => {
for (let i = 0; i < 22; i++) {
const result = await bchjs.Control.getNetworkInfo()
if (i === 5) {
// console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 3 calls allowed")
}
if (i === 15) {
// console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 10 calls allowed")
}
}
}).timeout(45000)
it("should allow more than 20 RPM to an indexer", async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
for (let i = 0; i < 22; i++) {
const result = await bchjs.Blockbook.balance(addr)
if (i === 5) {
// console.log(`validating 5th call: ${i}`)
assert.property(result, "balance", "more than 3 calls allowed")
}
if (i === 15) {
// console.log(`validating 5th call: ${i}`)
assert.property(result, "balance", "more than 10 calls allowed")
}
}
}).timeout(45000)
})
+4
View File
@@ -1,4 +1,8 @@
/*
CT 11/11/2020:
There is no longer a free tier. These tests have been deprecated.
--------
Mocha test that verifies that the bch-api server is enforcing its
FREE access rules
@@ -1,4 +1,8 @@
/*
CT 11/11/2020:
There is no longer a free tier. These tests have been deprecated.
----
Mocha test that verifies that the bch-api server is enforcing its
full-node access rules
@@ -14,7 +18,8 @@ const JWT_TOKEN =
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({
restURL: `https://api.fullstack.cash/v3/`,
// restURL: `https://bchn.fullstack.cash/v3/`,
restURL: `https:/abc.fullstack.cash/v3/`,
// restURL: `http://localhost:3000/v3/`,
apiToken: JWT_TOKEN
})
@@ -22,7 +27,7 @@ const bchjs = new BCHJS({
describe("#full node rate limits", () => {
it("should allow an free call to an indexer", async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
const result = await bchjs.Blockbook.balance(addr)
const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "balance")
+4 -3
View File
@@ -14,12 +14,13 @@ const JWT_TOKEN =
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({
restURL: `https://api.fullstack.cash/v3/`,
// restURL: `https://bchn.fullstack.cash/v3/`,
restURL: `https://abc.fullstack.cash/v3/`,
// restURL: `http://localhost:3000/v3/`,
apiToken: JWT_TOKEN
})
describe("#full node rate limits", () => {
describe("#Indexer rate limits", () => {
it("should allow more than 20 RPM to full node", async () => {
for (let i = 0; i < 22; i++) {
const result = await bchjs.Control.getNetworkInfo()
@@ -40,7 +41,7 @@ describe("#full node rate limits", () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
for (let i = 0; i < 22; i++) {
const result = await bchjs.Blockbook.balance(addr)
const result = await bchjs.Electrumx.balance(addr)
if (i === 5) {
// console.log(`validating 5th call: ${i}`)
+87
View File
@@ -126,4 +126,91 @@ describe(`#Ninsight`, () => {
assert.property(result[0], "vout")
})
})
describe(`#detailsAddress`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.Ninsight.details(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings.`
)
}
})
it(`should POST address details for a single address`, async () => {
const addr = "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7"
const result = await bchjs.Ninsight.details(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "balance")
assert.property(result[0], "balanceSat")
assert.property(result[0], "totalReceived")
assert.property(result[0], "totalReceivedSat")
assert.property(result[0], "totalSent")
assert.property(result[0], "totalSentSat")
assert.property(result[0], "unconfirmedBalance")
assert.property(result[0], "unconfirmedBalanceSat")
assert.property(result[0], "unconfirmedTxApperances")
assert.property(result[0], "txApperances")
assert.property(result[0], "transactions")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "cashAddress")
assert.property(result[0], "slpAddress")
assert.property(result[0], "currentPage")
assert.property(result[0], "pagesTotal")
})
it(`should POST address details for an array of addresses`, async () => {
const addr = [
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
]
const result = await bchjs.Ninsight.details(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "balance")
assert.property(result[0], "balanceSat")
assert.property(result[0], "totalReceived")
assert.property(result[0], "totalReceivedSat")
assert.property(result[0], "totalSent")
assert.property(result[0], "totalSentSat")
assert.property(result[0], "unconfirmedBalance")
assert.property(result[0], "unconfirmedBalanceSat")
assert.property(result[0], "unconfirmedTxApperances")
assert.property(result[0], "txApperances")
assert.property(result[0], "transactions")
assert.property(result[0], "legacyAddress")
assert.property(result[1], "cashAddress")
assert.property(result[1], "slpAddress")
assert.property(result[1], "currentPage")
assert.property(result[1], "pagesTotal")
/*
If in any way possible, I would like to refactor this test
according to the DRY principle to share it with the integration tests too
Would that make sense or does that implicate anything unwanted?
assertDetails(result[0])
assertDetails(data) {
assert.property(data, "balance")
assert.property(data, "balanceSat")
assert.property(data, "totalReceived")
assert.property(data, "totalReceivedSat")
assert.property(data, "totalSent")
assert.property(data, "totalSentSat")
}
*/
})
})
})
+88 -1
View File
@@ -3,7 +3,9 @@ const assert = chai.assert
const sinon = require("sinon")
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS({ ninsightURL: "https://rest.bitcoin.com/v2" })
const bchjs = new BCHJS({
ninsightURL: "https://rest.bitcoin.com/v2"
})
describe(`#Ninsight`, () => {
let sandbox
@@ -131,6 +133,91 @@ describe(`#Ninsight`, () => {
assert.property(result[0], "vout")
})
})
describe(`#detailsAddress`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.Ninsight.details(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings.`
)
}
})
it(`should POST address details for a single address`, async () => {
const addr = "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7"
const result = await bchjs.Ninsight.details(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "balance")
assert.property(result[0], "balanceSat")
assert.property(result[0], "totalReceived")
assert.property(result[0], "totalReceivedSat")
assert.property(result[0], "totalSent")
assert.property(result[0], "totalSentSat")
assert.property(result[0], "unconfirmedBalance")
assert.property(result[0], "unconfirmedBalanceSat")
assert.property(result[0], "unconfirmedTxApperances")
assert.property(result[0], "txApperances")
assert.property(result[0], "transactions")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "cashAddress")
assert.property(result[0], "slpAddress")
assert.property(result[0], "currentPage")
assert.property(result[0], "pagesTotal")
})
it(`should POST address details for an array of addresses`, async () => {
const addr = [
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
]
const result = await bchjs.Ninsight.details(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "balance")
assert.property(result[0], "balanceSat")
assert.property(result[0], "totalReceived")
assert.property(result[0], "totalReceivedSat")
assert.property(result[0], "totalSent")
assert.property(result[0], "totalSentSat")
assert.property(result[0], "unconfirmedBalance")
assert.property(result[0], "unconfirmedBalanceSat")
assert.property(result[0], "unconfirmedTxApperances")
assert.property(result[0], "txApperances")
assert.property(result[0], "transactions")
assert.property(result[0], "legacyAddress")
assert.property(result[1], "cashAddress")
assert.property(result[1], "slpAddress")
assert.property(result[1], "currentPage")
assert.property(result[1], "pagesTotal")
/*
If in any way possible, I would like to refactor this test
according to the DRY principle to share it with the integration tests too
Would that make sense or does that implicate anything unwanted?
assertDetails(result[0])
assertDetails(data) {
assert.property(data, "balance")
assert.property(data, "balanceSat")
assert.property(data, "totalReceived")
assert.property(data, "totalReceivedSat")
assert.property(data, "totalSent")
assert.property(data, "totalSentSat")
}
*/
})
})
})
function sleep(ms) {
+89 -1
View File
@@ -3,7 +3,9 @@ const assert = chai.assert
const sinon = require("sinon")
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({ ninsightURL: "https://trest.bitcoin.com/v2" })
const bchjs = new BCHJS({
ninsightURL: "https://trest.bitcoin.com/v2"
})
describe(`#Ninsight`, () => {
let sandbox
@@ -139,6 +141,92 @@ describe(`#Ninsight`, () => {
assert.property(result[0], "vout")
})
})
describe(`#detailsAddress`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.Ninsight.details(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings.`
)
}
})
it(`should POST address details for a single address`, async () => {
const addr = "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr"
const result = await bchjs.Ninsight.details(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "balance")
assert.property(result[0], "balanceSat")
assert.property(result[0], "totalReceived")
assert.property(result[0], "totalReceivedSat")
assert.property(result[0], "totalSent")
assert.property(result[0], "totalSentSat")
assert.property(result[0], "unconfirmedBalance")
assert.property(result[0], "unconfirmedBalanceSat")
assert.property(result[0], "unconfirmedTxApperances")
assert.property(result[0], "txApperances")
assert.property(result[0], "transactions")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "cashAddress")
assert.property(result[0], "slpAddress")
assert.property(result[0], "currentPage")
assert.property(result[0], "pagesTotal")
})
it(`should POST address details for an array of addresses`, async () => {
const addr = [
"bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr",
"bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"
]
const result = await bchjs.Ninsight.details(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "balance")
assert.property(result[0], "balanceSat")
assert.property(result[0], "totalReceived")
assert.property(result[0], "totalReceivedSat")
assert.property(result[0], "totalSent")
assert.property(result[0], "totalSentSat")
assert.property(result[0], "unconfirmedBalance")
assert.property(result[0], "unconfirmedBalanceSat")
assert.property(result[0], "unconfirmedTxApperances")
assert.property(result[0], "txApperances")
assert.property(result[0], "transactions")
assert.property(result[0], "legacyAddress")
assert.property(result[1], "cashAddress")
assert.property(result[1], "slpAddress")
assert.property(result[1], "currentPage")
assert.property(result[1], "pagesTotal")
/*
If in any way possible, I would like to refactor this test
according to the DRY principle to share it with the integration tests too
Would that make sense or does that implicate anything unwanted?
assertDetails(result[0])
assertDetails(data) {
assert.property(data, "balance")
assert.property(data, "balanceSat")
assert.property(data, "totalReceived")
assert.property(data, "totalReceivedSat")
assert.property(data, "totalSent")
assert.property(data, "totalSentSat")
}
*/
})
})
})
function sleep(ms) {
-25
View File
@@ -1,25 +0,0 @@
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
// Used for debugging and iterrogating JS objects.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
describe(`#BitboxShim`, () => {
it("should create the shim library", () => {
const BitboxShim = BCHJS.BitboxShim()
const bitbox = new BitboxShim()
//console.log(`bitbox.Address: ${util.inspect(bitbox.Address)}`)
assert.hasAnyKeys(bitbox.Address, [
// "details",
// "utxo",
// "unconfirmed",
// "transactions",
// "toLegacyAddress"
"restURL"
])
})
})
+27 -1
View File
@@ -158,6 +158,30 @@ const details = {
const detailsPost = [details, details]
const addrDetail =
{
"balance": 0.00001,
"balanceSat": 1000,
"totalReceived": 0.00001,
"totalReceivedSat": 1000,
"totalSent": 0,
"totalSentSat": 0,
"unconfirmedBalance": 0,
"unconfirmedBalanceSat": 0,
"unconfirmedTxApperances": 0,
"txApperances": 1,
"transactions": [
"5f09d317e24c5d376f737a2711f3bd1d381abdb41743fff3819b4f76382e1eac"
],
"legacyAddress": "1FZrK8HohEKyKCTM24NkAzPnX9WD9Ujhw7",
"cashAddress": "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr",
"slpAddress": "simpleledger:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ylynt40sa",
"currentPage": 0,
"pagesTotal": 1
}
const addrDetailArray = [addrDetail, addrDetail]
module.exports = {
utxo,
utxoPost,
@@ -166,5 +190,7 @@ module.exports = {
transactions,
transactionsPost,
details,
detailsPost
detailsPost,
addrDetail,
addrDetailArray
}
+125 -8
View File
@@ -31,7 +31,9 @@ describe(`#Ninsight`, () => {
it(`should GET utxos for a single address`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.utxo })
sandbox.stub(axios, "post").resolves({
data: mockData.utxo
})
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
@@ -57,7 +59,9 @@ describe(`#Ninsight`, () => {
it(`should POST utxo details for an array of addresses`, async () => {
// Mock the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.utxoPost })
sandbox.stub(axios, "post").resolves({
data: mockData.utxoPost
})
const addr = [
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
@@ -96,7 +100,9 @@ describe(`#Ninsight`, () => {
it(`should POST utxos for a single address`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmed })
sandbox.stub(axios, "post").resolves({
data: mockData.unconfirmed
})
const addr = "bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5"
@@ -121,7 +127,9 @@ describe(`#Ninsight`, () => {
it(`should POST utxo details for an array of addresses`, async () => {
// Mock the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmedPost })
sandbox.stub(axios, "post").resolves({
data: mockData.unconfirmedPost
})
const addr = [
"bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5",
@@ -159,7 +167,9 @@ describe(`#Ninsight`, () => {
})
it(`should POST transaction history for a single address`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.transactionsPost })
sandbox.stub(axios, "post").resolves({
data: mockData.transactionsPost
})
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
@@ -177,7 +187,9 @@ describe(`#Ninsight`, () => {
})
it(`should POST transaction history for an array of addresses`, async () => {
// Mock the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.transactionsPost })
sandbox.stub(axios, "post").resolves({
data: mockData.transactionsPost
})
const addr = [
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
@@ -212,7 +224,9 @@ describe(`#Ninsight`, () => {
})
it(`should POST transaction details for a single TxID`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.detailsPost })
sandbox.stub(axios, "post").resolves({
data: mockData.detailsPost
})
const txid = "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"
@@ -236,7 +250,9 @@ describe(`#Ninsight`, () => {
})
it(`should POST transaction details for an array of TxIDs`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.detailsPost })
sandbox.stub(axios, "post").resolves({
data: mockData.detailsPost
})
const txid = [
"fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33",
@@ -252,4 +268,105 @@ describe(`#Ninsight`, () => {
assert.property(result[0], "vout")
})
})
describe(`#addrDetails`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.Ninsight.details(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings.`
)
}
})
it(`should POST address details for a single address`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({
data: mockData.addrDetailArray
})
const result = await bchjs.Ninsight.details("addr")
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result[0], "balance")
assert.property(result[0], "balanceSat")
assert.property(result[0], "totalReceived")
assert.property(result[0], "totalReceivedSat")
assert.property(result[0], "totalSent")
assert.property(result[0], "totalSentSat")
assert.property(result[0], "unconfirmedBalance")
assert.property(result[0], "unconfirmedBalanceSat")
assert.property(result[0], "unconfirmedTxApperances")
assert.property(result[0], "txApperances")
assert.property(result[0], "transactions")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "cashAddress")
assert.property(result[0], "slpAddress")
assert.property(result[0], "currentPage")
assert.property(result[0], "pagesTotal")
})
it(`should call constructor with null args`, async () => {
new bchjs.Ninsight.constructor({})
})
it(`should POST address details for an array of addresses`, async () => {
// Mock the network call.
sandbox.stub(axios, "post").resolves({
data: mockData.addrDetailArray
})
const addr = [
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
]
const result = await bchjs.Ninsight.details(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "balance")
assert.property(result[0], "balanceSat")
assert.property(result[0], "totalReceived")
assert.property(result[0], "totalReceivedSat")
assert.property(result[0], "totalSent")
assert.property(result[0], "totalSentSat")
assert.property(result[0], "unconfirmedBalance")
assert.property(result[0], "unconfirmedBalanceSat")
assert.property(result[0], "unconfirmedTxApperances")
assert.property(result[0], "txApperances")
assert.property(result[0], "transactions")
assert.property(result[0], "legacyAddress")
assert.property(result[1], "cashAddress")
assert.property(result[1], "slpAddress")
assert.property(result[1], "currentPage")
assert.property(result[1], "pagesTotal")
/*
If in any way possible, I would like to refactor this test
according to the DRY principle to share it with the integration tests too
Would that make sense or does that implicate anything unwanted?
assertDetails(result[0])
assertDetails(data) {
assert.property(data, "satoshis")
assert.property(data, "height")
assert.property(data, "confirmations")
assert.property(data, "timestamp")
assert.property(data, "fees")
assert.property(data, "outputIndexes")
assert.property(data, "inputIndexes")
assert.property(data, "tx")
}
*/
})
})
})