From 348a67a79bfd4964a2c1c5db12fd0d2943bfccda Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 09:49:30 -0800 Subject: [PATCH 1/9] tokenUtxoDetails: prototyped different validation methods --- package.json | 3 +- src/slp/utils.js | 75 ++++++++++++++++++++++++++--- test/integration/bchn/slp.js | 92 ++++++++++++++++++++++++++++++++++++ test/unit/slp-utils.js | 8 ++-- 4 files changed, 167 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index deb3304..d0f3605 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,11 @@ "test:integration:local": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration", "test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ bash test/integration/test-bchn-integration.sh", "test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet", + "test:integration:api": "RESTURL=https://api.fullstack.cash/v4/ mocha --timeout 30000 test/integration", "test:temp": "RESTURL=https://bchn.fullstack.cash/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/bchn/", "test:temp2": "RESTURL=https://abc.fullstack.cash/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/", "test:temp3": "ISBCHN=true RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/bchn/", - "test:integration:api": "RESTURL=https://api.fullstack.cash/v4/ mocha --timeout 30000 test/integration", + "test:temp4": "mocha -g '#SLP Utils' test/unit/", "coverage": "nyc report --reporter=text-lcov | coveralls", "coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/", "docs": "./node_modules/.bin/apidoc -i src/ -o docs" diff --git a/src/slp/utils.js b/src/slp/utils.js index 694c27d..671821c 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -14,6 +14,7 @@ class Utils { this.apiToken = config.apiToken this.slpParser = slpParser this.authToken = config.authToken + this.axios = axios if (this.authToken) { // Add Basic Authentication token to the authorization header. @@ -32,6 +33,12 @@ class Utils { } _this = this + + this.whitelist = [] + if (process.env.TEST !== "unit") { + // Get the whitelist. + this.getWhitelist() + } } /** @@ -565,14 +572,19 @@ class Utils { * } * ] */ - async getWhitelist(txid) { + async getWhitelist() { try { const path = `${this.restURL}slp/whitelist` - const response = await axios.get(path, _this.axiosOptions) - // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) + // Retrieve the whitelist from the REST API if we haven't gotten it yet. + if (this.whitelist.length === 0) { + const response = await axios.get(path, _this.axiosOptions) + console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) - return response.data + this.whitelist = response.data + } + + return this.whitelist } catch (error) { if (error.response && error.response.data) throw error.response.data throw error @@ -1074,6 +1086,7 @@ class Utils { } } + // Ensure the UTXO has a vout or tx_pos property. if (!Number.isInteger(utxo.vout)) { if (Number.isInteger(utxo.tx_pos)) { utxo.vout = utxo.tx_pos @@ -1262,16 +1275,64 @@ class Utils { } } - // Finally, validate the SLP txid with SLPDB. + // *After* the UTXO has been hydrated with SLP data, + // validate the TXID with SLPDB. if (outAry[i].tokenType) { - var isValid = cachedTxValidation[utxo.txid] + // Only execute this block if the current UTXO has a 'tokenType' + // property. i.e. it has been hydrated with SLP information. + + // If the value has been cached, use the cached version first. + let isValid = cachedTxValidation[utxo.txid] + + // If not in the cache, try the general SLPDB. if (isValid == null) { isValid = await this.validateTxid(utxo.txid) + isValid = isValid[0].valid + + // Save the result to the local cache. cachedTxValidation[utxo.txid] = isValid } // console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`) - outAry[i].isValid = isValid[0].valid + // If still null, check the whitelist SLPDB + if (isValid === null) { + console.log(`outAry[${i}]: ${JSON.stringify(outAry[i], null, 2)}`) + + // Figure out if the token UTXO is in the whitelist. + let utxoInWhitelist = false + for (let j = 0; j < this.whitelist.length; j++) { + if (outAry[i].tokenId === this.whitelist[j].tokenId) { + utxoInWhitelist = true + break + } + } + + // If the utxo.tokenId is in the whitelist, check the validity with + // the whitelist SLPDB. This should still be functioning properly + // if the general SLPDB is not. + if (utxoInWhitelist) { + isValid = await this.validateTxid3(utxo.txid) + isValid = isValid[0].valid + + // Save the result to the local cache. + cachedTxValidation[utxo.txid] = isValid + } + } + + // If still null, as a last resort, check it against slp-validate + if (isValid === null) { + isValid = await this.validateTxid2(utxo.txid) + console.log( + `slp-validate isValid: ${JSON.stringify(isValid, null, 2)}` + ) + isValid = isValid.valid + + // Save the result to the local cache. + cachedTxValidation[utxo.txid] = isValid + } + + console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`) + outAry[i].isValid = isValid } } diff --git a/test/integration/bchn/slp.js b/test/integration/bchn/slp.js index be71f78..e29cbe5 100644 --- a/test/integration/bchn/slp.js +++ b/test/integration/bchn/slp.js @@ -463,6 +463,98 @@ describe(`#SLP`, () => { assert.equal(data[0].isValid, false) }) + + 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 + assert.equal(data[0].tx_hash, utxos[0].tx_hash) + assert.equal(data[0].isValid, null) + + // Normal TX (non-SLP) + assert.equal(data[1].tx_hash, utxos[1].tx_hash) + assert.equal(data[1].isValid, false) + + // Valid PSF SLP tx + assert.equal(data[2].tx_hash, utxos[2].tx_hash) + assert.equal(data[2].isValid, true) + + // Valid SLP token not in whitelist + assert.equal(data[3].tx_hash, utxos[3].tx_hash) + assert.equal(data[3].isValid, true) + + // Token send on BCHN network + assert.equal(data[4].tx_hash, utxos[4].tx_hash) + assert.equal(data[4].isValid, true) + + // Token send on ABC network + assert.equal(data[5].tx_hash, utxos[5].tx_hash) + assert.equal(data[5].isValid, null) + + // Known invalid SLP token send of PSF tokens + assert.equal(data[6].tx_hash, utxos[6].tx_hash) + assert.equal(data[6].isValid, false) + }) }) describe("#balancesForAddress", () => { diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 6b85d3f..3c1a419 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -1737,7 +1737,7 @@ describe("#SLP Utils", () => { }) // it("should handle a dust attack", async () => { - it("#dustattack", async () => { + it("should handle dust attack UTXOs", async () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox @@ -1765,6 +1765,8 @@ describe("#SLP Utils", () => { assert.equal(data[0].isValid, false) }) + + // }) describe("#txDetails", () => { @@ -2005,9 +2007,9 @@ describe("#SLP Utils", () => { await slp.Utils.getWhitelist() - assert2.equal(true, false, "Unexpected result") + assert2.fail("Unexpected result") } catch (err) { - // console.log("err: ", err) + console.log("err: ", err) assert2.include(err.error, "Network error") } }) From 98f7cb8f3aea7c58b7c9a2149001348d4e79291f Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 09:59:01 -0800 Subject: [PATCH 2/9] encapsulating and mocking axios in the slp-utils lib and unit tests --- src/slp/utils.js | 30 ++--- test/unit/slp-utils.js | 267 +++++++++++++++++++++-------------------- 2 files changed, 155 insertions(+), 142 deletions(-) diff --git a/src/slp/utils.js b/src/slp/utils.js index 671821c..94eaad0 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -216,9 +216,9 @@ class Utils { try { let response if (method === "get") { - response = await axios.get(path, _this.axiosOptions) + response = await _this.axios.get(path, _this.axiosOptions) } else { - response = await axios.post( + response = await _this.axios.post( path, { tokenIds: id @@ -314,7 +314,7 @@ class Utils { if (typeof address === "string") { const path = `${this.restURL}slp/balancesForAddress/${address}` - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) return response.data // Array of addresses. @@ -322,7 +322,7 @@ class Utils { const path = `${this.restURL}slp/balancesForAddress` // Dev note: must use axios.post for unit test stubbing. - const response = await axios.post( + const response = await _this.axios.post( path, { addresses: address @@ -379,7 +379,7 @@ class Utils { const path = `${this.restURL}slp/balancesForToken/${tokenId}` try { - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data @@ -444,7 +444,7 @@ class Utils { else txids = txid try { - const response = await axios.post( + const response = await _this.axios.post( path, { txids: txids @@ -510,7 +510,7 @@ class Utils { const path = `${this.restURL}slp/validateTxid2/${txid}` - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data @@ -578,7 +578,7 @@ class Utils { // Retrieve the whitelist from the REST API if we haven't gotten it yet. if (this.whitelist.length === 0) { - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) this.whitelist = response.data @@ -652,7 +652,7 @@ class Utils { else txids = txid try { - const response = await axios.post( + const response = await _this.axios.post( path, { txids: txids @@ -709,7 +709,7 @@ class Utils { const path = `${this.restURL}slp/tokenStats/${tokenId}` try { - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data @@ -779,7 +779,7 @@ class Utils { const path = `${this.restURL}slp/transactions/${tokenId}/${address}` try { - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data @@ -818,7 +818,7 @@ class Utils { const path = `${this.restURL}slp/burnTotal/${transactionId}` try { - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data @@ -860,7 +860,7 @@ class Utils { // console.log(`this.restURL: ${this.restURL}`) const path = `${this.restURL}slp/txDetails/${txid}` - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data @@ -935,7 +935,7 @@ class Utils { // Retrieve the transaction object from the full node. const path = `${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=true` - const response = await axios.get(path, _this.axiosOptions) + const response = await _this.axios.get(path, _this.axiosOptions) const txDetails = response.data // console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`) @@ -1513,7 +1513,7 @@ class Utils { // Throw error if input is not an array. if (!Array.isArray(utxos)) throw new Error("Input must be an array.") - const response = await axios.post( + const response = await _this.axios.post( `${this.restURL}slp/hydrateUtxos`, { utxos: utxos diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 3c1a419..fa56855 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -1,28 +1,24 @@ +// Public npm libraries const assert = require("assert") const assert2 = require("chai").assert - -const SLP = require("../../src/slp/slp") -//const slp = new SLP("http://decatur.hopto.org:12400/v4/") -//const slp = new SLP("https://rest.bitcoin.com/v2/") -const slp = new SLP({ - restURL: "https://api.fullstack.cash/v4/", - apiToken: process.env.BCHJSTOKEN -}) - const nock = require("nock") // http call mocking const sinon = require("sinon") -const axios = require("axios") +// const axios = require("axios") + +// Unit under test +const SLP = require("../../src/slp/slp") +let uut +let SERVER + +const REST_URL = process.env.RESTURL + ? process.env.RESTURL + : "https://api.fullstack.cash/v4/" // Mock data used for unit tests const mockData = require("./fixtures/slp/mock-utils") // Default to unit tests unless some other value for TEST is passed. if (!process.env.TEST) process.env.TEST = "unit" -const SERVER = slp.restURL - -// Used for debugging and iterrogating JS objects. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } describe("#SLP Utils", () => { let sandbox @@ -32,6 +28,12 @@ describe("#SLP Utils", () => { if (!nock.isActive()) nock.activate() sandbox = sinon.createSandbox() + + uut = new SLP({ + restURL: REST_URL, + apiToken: process.env.BCHJSTOKEN + }) + SERVER = uut.restURL }) afterEach(() => { @@ -51,7 +53,7 @@ describe("#SLP Utils", () => { .reply(200, mockData.mockList) } - const list = await slp.Utils.list() + const list = await uut.Utils.list() //console.log(`list: ${JSON.stringify(list, null, 2)}`) assert2.isArray(list) @@ -90,7 +92,7 @@ describe("#SLP Utils", () => { const tokenId = "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84" - const list = await slp.Utils.list(tokenId) + const list = await uut.Utils.list(tokenId) //console.log(`list: ${JSON.stringify(list, null, 2)}`) assert2.hasAllKeys(list, [ @@ -131,7 +133,7 @@ describe("#SLP Utils", () => { "b3f4f132dc3b9c8c96316346993a8d54d729715147b7b11aa6c8cd909e955313" ] - const list = await slp.Utils.list(tokenIds) + const list = await uut.Utils.list(tokenIds) // console.log(`list: ${JSON.stringify(list, null, 2)}`) assert2.hasAllKeys(list[0], [ @@ -165,7 +167,7 @@ describe("#SLP Utils", () => { try { const address = 1234 - await slp.Utils.balancesForAddress(address) + await uut.Utils.balancesForAddress(address) assert2.equal(true, false, "Uh oh. Code path should not end here.") } catch (err) { @@ -181,14 +183,14 @@ describe("#SLP Utils", () => { // Mock the call to the REST API if (process.env.TEST === "unit") { sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") .resolves({ data: mockData.balancesForAddress }) } //sandbox - // .stub(slp.Utils, "balancesForAddress") + // .stub(uut.Utils, "balancesForAddress") // .resolves(mockData.balancesForAddress) - const balances = await slp.Utils.balancesForAddress( + const balances = await uut.Utils.balancesForAddress( "simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9" ) // console.log(`balances: ${JSON.stringify(balances, null, 2)}`) @@ -212,11 +214,11 @@ describe("#SLP Utils", () => { // Mock the call to the REST API if (process.env.TEST === "unit") { sandbox - .stub(axios, "post") + .stub(uut.Utils.axios, "post") .resolves({ data: mockData.balancesForAddresses }) } - const balances = await slp.Utils.balancesForAddress(addresses) + const balances = await uut.Utils.balancesForAddress(addresses) //console.log(`balances: ${JSON.stringify(balances, null, 2)}`) assert2.isArray(balances) @@ -240,7 +242,7 @@ describe("#SLP Utils", () => { .reply(200, mockData.mockIsValidTxid) } - const isValid = await slp.Utils.validateTxid( + const isValid = await uut.Utils.validateTxid( "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb" ) assert.deepEqual(isValid, [ @@ -262,7 +264,7 @@ describe("#SLP Utils", () => { .reply(200, mockData.mockBalancesForToken) } - const balances = await slp.Utils.balancesForToken( + const balances = await uut.Utils.balancesForToken( "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb" ) assert2.hasAnyKeys(balances[0], ["tokenBalance", "slpAddress"]) @@ -278,7 +280,7 @@ describe("#SLP Utils", () => { .reply(200, mockData.mockTokenStats) } - const tokenStats = await slp.Utils.tokenStats( + const tokenStats = await uut.Utils.tokenStats( "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb" ) assert2.hasAnyKeys(tokenStats, [ @@ -307,7 +309,7 @@ describe("#SLP Utils", () => { .reply(200, mockData.mockTransactions) } - const transactions = await slp.Utils.transactions( + const transactions = await uut.Utils.transactions( "495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a", "simpleledger:qrhvcy5xlegs858fjqf8ssl6a4f7wpstaqnt0wauwu" ) @@ -324,7 +326,7 @@ describe("#SLP Utils", () => { .reply(200, mockData.mockBurnTotal) } - const burnTotal = await slp.Utils.burnTotal( + const burnTotal = await uut.Utils.burnTotal( "c7078a6c7400518a513a0bde1f4158cf740d08d3b5bfb19aa7b6657e2f4160de" ) //console.log(`burnTotal: ${JSON.stringify(burnTotal, null, 2)}`) @@ -343,7 +345,7 @@ describe("#SLP Utils", () => { try { const txid = 53423 // Not a string. - await slp.Utils.decodeOpReturn(txid) + await uut.Utils.decodeOpReturn(txid) assert2.equal(true, false, "Unexpected result.") } catch (err) { @@ -357,14 +359,14 @@ describe("#SLP Utils", () => { // Mock the call to the REST API if (process.env.TEST === "unit") { sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") .resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn }) } const txid = "3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec" - await slp.Utils.decodeOpReturn(txid) + await uut.Utils.decodeOpReturn(txid) assert2.equal(true, false, "Unexpected result.") } catch (err) { @@ -378,14 +380,14 @@ describe("#SLP Utils", () => { // Mock the call to the REST API if (process.env.TEST === "unit") { sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") .resolves({ data: mockData.nonSLPTxDetailsWithOpReturn }) } const txid = "2ff74c48a5d657cf45f699601990bffbbe7a2a516d5480674cbf6c6a4497908f" - await slp.Utils.decodeOpReturn(txid) + await uut.Utils.decodeOpReturn(txid) assert2.equal(true, false, "Unexpected result.") } catch (err) { @@ -398,14 +400,14 @@ describe("#SLP Utils", () => { // Mock the call to the REST API if (process.env.TEST === "unit") { sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") .resolves({ data: mockData.txDetailsSLPGenesis }) } const txid = "bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90" - const result = await slp.Utils.decodeOpReturn(txid) + const result = await uut.Utils.decodeOpReturn(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.hasAllKeys(result, [ @@ -424,13 +426,16 @@ describe("#SLP Utils", () => { it("should decode a mint transaction", async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") - sandbox.stub(axios, "get").resolves({ data: mockData.txDetailsSLPMint }) + if (process.env.TEST === "unit") { + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.txDetailsSLPMint }) + } const txid = "65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4" - const result = await slp.Utils.decodeOpReturn(txid) + const result = await uut.Utils.decodeOpReturn(txid) //console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.hasAllKeys(result, [ @@ -444,13 +449,16 @@ describe("#SLP Utils", () => { it("should decode a send transaction", async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") - sandbox.stub(axios, "get").resolves({ data: mockData.txDetailsSLPSend }) + if (process.env.TEST === "unit") { + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.txDetailsSLPSend }) + } const txid = "4f922565af664b6fdf0a1ba3924487344be721b3d8815c62cafc8a51e04a8afa" - const result = await slp.Utils.decodeOpReturn(txid) + const result = await uut.Utils.decodeOpReturn(txid) //console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.hasAllKeys(result, ["tokenType", "txType", "tokenId", "amounts"]) @@ -460,14 +468,14 @@ describe("#SLP Utils", () => { // Mock the call to the REST API. if (process.env.TEST === "unit") { sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") .resolves({ data: mockData.txDetailsSLPGenesisNoBaton }) } const txid = "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" - const data = await slp.Utils.decodeOpReturn(txid) + const data = await uut.Utils.decodeOpReturn(txid) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.equal(data.mintBatonVout, 0) @@ -477,14 +485,14 @@ describe("#SLP Utils", () => { // Mock the call to rest.bitcoin.com if (process.env.TEST === "unit") { sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") .resolves({ data: mockData.txDetailsSLPSendAlt }) } const txid = "d94357179775425ebc59c93173bd6dc9854095f090a2eb9dcfe9797398bc8eae" - const data = await slp.Utils.decodeOpReturn(txid) + const data = await uut.Utils.decodeOpReturn(txid) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.hasAnyKeys(data, [ @@ -503,14 +511,14 @@ describe("#SLP Utils", () => { // Mock the call to rest.bitcoin.com if (process.env.TEST === "unit") { sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") .resolves({ data: mockData.mockInvalidSlpSend }) } const txid = "a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415" - await slp.Utils.decodeOpReturn(txid) + await uut.Utils.decodeOpReturn(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) } catch (err) { // console.log(`err: `, err) @@ -521,13 +529,13 @@ describe("#SLP Utils", () => { it("should decode a NFT Parent transaction", async () => { // Mock the call to the REST API. sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") .resolves({ data: mockData.txDetailsSLPNftGenesis }) const txid = "4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4" - const data = await slp.Utils.decodeOpReturn(txid) + const data = await uut.Utils.decodeOpReturn(txid) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.property(data, "tokenType") @@ -549,13 +557,13 @@ describe("#SLP Utils", () => { // it("should decode a NFT Child transaction", async () => { // // Mock the call to the REST API. // // sandbox - // // .stub(axios, "get") + // // .stub(uut.Utils.axios, "get") // // .resolves({ data: mockData.txDetailsSLPNftGenesis }) // // const txid = // "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76" // - // const data = await slp.Utils.decodeOpReturn(txid) + // const data = await uut.Utils.decodeOpReturn(txid) // console.log(`data: ${JSON.stringify(data, null, 2)}`) // // // assert2.property(data, "tokenType") @@ -578,7 +586,7 @@ describe("#SLP Utils", () => { describe("#tokenUtxoDetails", () => { it("should throw error if input is not an array.", async () => { try { - await slp.Utils.tokenUtxoDetails("test") + await uut.Utils.tokenUtxoDetails("test") assert2.equal(true, false, "Unexpected result.") } catch (err) { @@ -612,7 +620,7 @@ describe("#SLP Utils", () => { } ] - await slp.Utils.tokenUtxoDetails(utxos) + await uut.Utils.tokenUtxoDetails(utxos) assert2.equal(true, false, "Unexpected result.") } catch (err) { @@ -645,7 +653,7 @@ describe("#SLP Utils", () => { } ] - await slp.Utils.tokenUtxoDetails(utxos) + await uut.Utils.tokenUtxoDetails(utxos) assert2.equal(true, false, "Unexpected result.") } catch (err) { @@ -662,7 +670,7 @@ describe("#SLP Utils", () => { it("should return details on minting baton from genesis transaction", async () => { // Mock the call to REST API // Stub the call to validateTxid - sandbox.stub(slp.Utils, "validateTxid").resolves([ + sandbox.stub(uut.Utils, "validateTxid").resolves([ { txid: "bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90", @@ -671,7 +679,7 @@ describe("#SLP Utils", () => { ]) // Stub the calls to decodeOpReturn. - sandbox.stub(slp.Utils, "decodeOpReturn").resolves({ + sandbox.stub(uut.Utils, "decodeOpReturn").resolves({ tokenType: 1, txType: "GENESIS", ticker: "SLPSDK", @@ -706,7 +714,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) // assert2.equal(data[0], false, "Change UTXO marked as false.") @@ -741,7 +749,7 @@ describe("#SLP Utils", () => { // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .onCall(0) .resolves({ tokenType: 1, @@ -767,7 +775,7 @@ describe("#SLP Utils", () => { }) // Stub the call to validateTxid - sandbox.stub(slp.Utils, "validateTxid").resolves([ + sandbox.stub(uut.Utils, "validateTxid").resolves([ { txid: "cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76", @@ -787,7 +795,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.property(data[0], "txid") @@ -814,7 +822,7 @@ describe("#SLP Utils", () => { // Mock the call to REST API // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .onCall(0) .resolves({ tokenType: 1, @@ -839,7 +847,7 @@ describe("#SLP Utils", () => { }) // Stub the call to validateTxid - sandbox.stub(slp.Utils, "validateTxid").resolves([ + sandbox.stub(uut.Utils, "validateTxid").resolves([ { txid: "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", @@ -859,7 +867,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.property(data[0], "txid") @@ -883,11 +891,11 @@ describe("#SLP Utils", () => { it("should handle BCH and SLP utxos in the same TX", async () => { // Mock external dependencies. sandbox - .stub(slp.Utils, "validateTxid") + .stub(uut.Utils, "validateTxid") .resolves(mockData.mockDualValidation) sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .onCall(0) .resolves({ tokenType: 1, @@ -940,7 +948,7 @@ describe("#SLP Utils", () => { } ] - const result = await slp.Utils.tokenUtxoDetails(utxos) + const result = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.isArray(result) @@ -964,7 +972,7 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .onCall(0) .throws({ message: "scriptpubkey not op_return" }) .onCall(1) @@ -991,7 +999,7 @@ describe("#SLP Utils", () => { }) // Stub the call to validateTxid - sandbox.stub(slp.Utils, "validateTxid").resolves([ + sandbox.stub(uut.Utils, "validateTxid").resolves([ { txid: "67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19", @@ -1020,7 +1028,7 @@ describe("#SLP Utils", () => { } ] - const result = await slp.Utils.tokenUtxoDetails(utxos) + const result = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.isArray(result) @@ -1044,7 +1052,7 @@ describe("#SLP Utils", () => { // Mock live network calls sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .throws(new Error("scriptpubkey not op_return")) const utxos = [ @@ -1068,7 +1076,7 @@ describe("#SLP Utils", () => { } ] - const result = await slp.Utils.tokenUtxoDetails(utxos) + const result = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.isArray(result) @@ -1107,10 +1115,10 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. - sandbox.stub(slp.Utils, "decodeOpReturn").resolves(slpData) + sandbox.stub(uut.Utils, "decodeOpReturn").resolves(slpData) // Stub the call to validateTxid - sandbox.stub(slp.Utils, "validateTxid").resolves([ + sandbox.stub(uut.Utils, "validateTxid").resolves([ { txid: "d2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954", @@ -1145,7 +1153,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) @@ -1198,7 +1206,7 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .resolves(slpData) .onCall(1) .resolves(genesisData) @@ -1211,7 +1219,7 @@ describe("#SLP Utils", () => { // Stub the call to validateTxid sandbox - .stub(slp.Utils, "validateTxid") + .stub(uut.Utils, "validateTxid") .resolves(stubValid) .onCall(1) .resolves(stubValid) @@ -1243,7 +1251,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) @@ -1287,7 +1295,7 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .resolves(slpData) .onCall(1) .resolves(slpData) @@ -1300,7 +1308,7 @@ describe("#SLP Utils", () => { // Stub the call to validateTxid sandbox - .stub(slp.Utils, "validateTxid") + .stub(uut.Utils, "validateTxid") .resolves(stubValid) .onCall(1) .resolves(stubValid) @@ -1337,7 +1345,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) @@ -1389,7 +1397,7 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .resolves(slpData) .onCall(1) .resolves(slpData) @@ -1402,7 +1410,7 @@ describe("#SLP Utils", () => { // Stub the call to validateTxid sandbox - .stub(slp.Utils, "validateTxid") + .stub(uut.Utils, "validateTxid") .resolves(stubValid) .onCall(1) .resolves(stubValid) @@ -1439,7 +1447,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) @@ -1482,13 +1490,13 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .resolves(slpData) .onCall(1) .resolves(slpData) // Stub the call to validateTxid - sandbox.stub(slp.Utils, "validateTxid").resolves(stubValid) + sandbox.stub(uut.Utils, "validateTxid").resolves(stubValid) const utxos = [ { @@ -1509,7 +1517,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) @@ -1556,7 +1564,7 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .resolves(slpData) .onCall(1) .resolves(genesisData) @@ -1564,7 +1572,7 @@ describe("#SLP Utils", () => { .resolves(slpData) // Stub the call to validateTxid - sandbox.stub(slp.Utils, "validateTxid").resolves(stubValid) + sandbox.stub(uut.Utils, "validateTxid").resolves(stubValid) const utxos = [ { @@ -1585,7 +1593,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) @@ -1633,7 +1641,7 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .resolves(slpData) .onCall(1) .resolves(genesisData) @@ -1646,7 +1654,7 @@ describe("#SLP Utils", () => { // Stub the call to validateTxid sandbox - .stub(slp.Utils, "validateTxid") + .stub(uut.Utils, "validateTxid") .resolves(stubValid) .onCall(1) .resolves(stubValid) @@ -1678,7 +1686,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) @@ -1722,12 +1730,12 @@ describe("#SLP Utils", () => { } ] - sandbox.stub(slp.Utils, "decodeOpReturn").rejects({ + sandbox.stub(uut.Utils, "decodeOpReturn").rejects({ error: "Too many requests. Your limits are currently 3 requests per minute. Increase rate limits at https://fullstack.cash" }) - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) // Values should be 'null' to signal that a determination could not be made @@ -1741,7 +1749,7 @@ describe("#SLP Utils", () => { // Mock external dependencies. // Stub the calls to decodeOpReturn. sandbox - .stub(slp.Utils, "decodeOpReturn") + .stub(uut.Utils, "decodeOpReturn") .rejects(new Error("lokad id wrong size")) const utxos = [ @@ -1760,7 +1768,7 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) + const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert.equal(data[0].isValid, false) @@ -1772,7 +1780,7 @@ describe("#SLP Utils", () => { describe("#txDetails", () => { it("should throw an error if txid is not included", async () => { try { - await slp.Utils.txDetails() + await uut.Utils.txDetails() } catch (err) { assert2.include( err.message, @@ -1787,14 +1795,14 @@ describe("#SLP Utils", () => { // Mock the call to the REST API if (process.env.TEST === "unit") { sandbox - .stub(axios, "get") + .stub(uut.Utils.axios, "get") //.resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn }) .throws({ error: `TXID not found` }) } const txid = `d284e71227ec89f714b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e` - const result = await slp.Utils.txDetails(txid) + const result = await uut.Utils.txDetails(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) } catch (err) { // console.log(`err: `, err) @@ -1804,12 +1812,15 @@ describe("#SLP Utils", () => { it("should return details for an SLP txid", async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") - sandbox.stub(axios, "get").resolves({ data: mockData.mockTxDetails }) + if (process.env.TEST === "unit") { + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.mockTxDetails }) + } const txid = `9dbaaafc48c49a21beabada8de632009288a2cd52eecefd0c00edcffca9955d0` - const result = await slp.Utils.txDetails(txid) + const result = await uut.Utils.txDetails(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.hasAnyKeys(result, [ @@ -1838,7 +1849,7 @@ describe("#SLP Utils", () => { try { const utxos = 1234 - await slp.Utils.hydrateUtxos(utxos) + await uut.Utils.hydrateUtxos(utxos) assert2.equal(true, false, "Uh oh. Code path should not end here.") } catch (err) { @@ -1855,7 +1866,7 @@ describe("#SLP Utils", () => { "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" ] - await slp.Utils.validateTxid2(txid) + await uut.Utils.validateTxid2(txid) assert2.equal(true, false, "Unexpected result") } catch (err) { @@ -1869,7 +1880,7 @@ describe("#SLP Utils", () => { const txid = "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783" - await slp.Utils.validateTxid2(txid) + await uut.Utils.validateTxid2(txid) assert2.equal(true, false, "Unexpected result") } catch (err) { @@ -1883,7 +1894,7 @@ describe("#SLP Utils", () => { "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" // Mock live network calls. - sandbox.stub(axios, "get").resolves({ + sandbox.stub(uut.Utils.axios, "get").resolves({ data: { txid: txid, isValid: false, @@ -1891,7 +1902,7 @@ describe("#SLP Utils", () => { } }) - const result = await slp.Utils.validateTxid2(txid) + const result = await uut.Utils.validateTxid2(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.property(result, "txid") @@ -1906,7 +1917,7 @@ describe("#SLP Utils", () => { "3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488" // Mock live network calls. - sandbox.stub(axios, "get").resolves({ + sandbox.stub(uut.Utils.axios, "get").resolves({ data: { txid: txid, isValid: true, @@ -1914,7 +1925,7 @@ describe("#SLP Utils", () => { } }) - const result = await slp.Utils.validateTxid2(txid) + const result = await uut.Utils.validateTxid2(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.property(result, "txid") @@ -1932,12 +1943,12 @@ describe("#SLP Utils", () => { "eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579" // Mock live network calls. - sandbox.stub(axios, "get").rejects({ + sandbox.stub(uut.Utils.axios, "get").rejects({ error: "Network error: Could not communicate with full node or other external service." }) - await slp.Utils.validateTxid2(txid) + await uut.Utils.validateTxid2(txid) assert2.equal(true, false, "Unexpected result") } catch (err) { @@ -1953,11 +1964,11 @@ describe("#SLP Utils", () => { "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" // Mock live network calls. - sandbox.stub(axios, "post").resolves({ + sandbox.stub(uut.Utils.axios, "post").resolves({ data: mockData.mockValidateTxid3Invalid }) - const result = await slp.Utils.validateTxid(txid) + const result = await uut.Utils.validateTxid(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.isArray(result) @@ -1981,7 +1992,7 @@ describe("#SLP Utils", () => { "3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488" ] - const result = await slp.Utils.validateTxid(txids) + const result = await uut.Utils.validateTxid(txids) console.log(`result: ${JSON.stringify(result, null, 2)}`) }) */ @@ -1989,9 +2000,11 @@ describe("#SLP Utils", () => { describe("#getWhitelist", () => { it("should return the list", async () => { - sandbox.stub(axios, "get").resolves({ data: mockData.mockWhitelist }) + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.mockWhitelist }) - const result = await slp.Utils.getWhitelist() + const result = await uut.Utils.getWhitelist() assert2.isArray(result) assert2.property(result[0], "name") @@ -2000,12 +2013,12 @@ describe("#SLP Utils", () => { it("catches and throws an error", async () => { try { - sandbox.stub(axios, "get").rejects({ + sandbox.stub(uut.Utils.axios, "get").rejects({ error: "Network error: Could not communicate with full node or other external service." }) - await slp.Utils.getWhitelist() + await uut.Utils.getWhitelist() assert2.fail("Unexpected result") } catch (err) { @@ -2021,11 +2034,11 @@ describe("#SLP Utils", () => { "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" // Mock live network calls. - sandbox.stub(axios, "post").resolves({ + sandbox.stub(uut.Utils.axios, "post").resolves({ data: mockData.mockValidateTxid3Invalid }) - const result = await slp.Utils.validateTxid3(txid) + const result = await uut.Utils.validateTxid3(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert2.isArray(result) @@ -2039,14 +2052,14 @@ describe("#SLP Utils", () => { it("should handle an array with a single element", async () => { sandbox - .stub(axios, "post") + .stub(uut.Utils.axios, "post") .resolves({ data: mockData.mockValidateTxid3Valid }) const txid = [ "daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd" ] - const result = await slp.Utils.validateTxid3(txid) + const result = await uut.Utils.validateTxid3(txid) assert2.isArray(result) @@ -2059,13 +2072,13 @@ describe("#SLP Utils", () => { it("should handle an single string input", async () => { sandbox - .stub(axios, "post") + .stub(uut.Utils.axios, "post") .resolves({ data: mockData.mockValidateTxid3Valid }) const txid = "daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd" - const result = await slp.Utils.validateTxid3(txid) + const result = await uut.Utils.validateTxid3(txid) assert2.isArray(result) @@ -2078,12 +2091,12 @@ describe("#SLP Utils", () => { it("catches and throws an error", async () => { try { - sandbox.stub(axios, "post").rejects({ + sandbox.stub(uut.Utils.axios, "post").rejects({ error: "Network error: Could not communicate with full node or other external service." }) - await slp.Utils.validateTxid3() + await uut.Utils.validateTxid3() assert2.equal(true, false, "Unexpected result") } catch (err) { From 4af6716631b64adad99c5dc1ce4b1695745623f3 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 13:07:35 -0800 Subject: [PATCH 3/9] Editing slp-util unit tests --- package.json | 2 +- src/slp/utils.js | 2 +- test/integration/bchn/slp.js | 30 +++- test/integration/test-bchn-integration.sh | 3 +- test/unit/slp-utils.js | 192 ++++++++-------------- 5 files changed, 105 insertions(+), 124 deletions(-) diff --git a/package.json b/package.json index d0f3605..3e4ea02 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "test:temp": "RESTURL=https://bchn.fullstack.cash/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/bchn/", "test:temp2": "RESTURL=https://abc.fullstack.cash/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/", "test:temp3": "ISBCHN=true RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/bchn/", - "test:temp4": "mocha -g '#SLP Utils' test/unit/", + "test:temp4": "mocha --trace-warnings --unhandled-rejections=strict -g '#SLP Utils' test/unit/", "coverage": "nyc report --reporter=text-lcov | coveralls", "coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/", "docs": "./node_modules/.bin/apidoc -i src/ -o docs" diff --git a/src/slp/utils.js b/src/slp/utils.js index 94eaad0..ef7852a 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -579,7 +579,7 @@ class Utils { // Retrieve the whitelist from the REST API if we haven't gotten it yet. if (this.whitelist.length === 0) { const response = await _this.axios.get(path, _this.axiosOptions) - console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) + // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) this.whitelist = response.data } diff --git a/test/integration/bchn/slp.js b/test/integration/bchn/slp.js index e29cbe5..ee73195 100644 --- a/test/integration/bchn/slp.js +++ b/test/integration/bchn/slp.js @@ -35,7 +35,7 @@ describe(`#SLP`, () => { const tokenId = `4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf` const result = await bchjs.SLP.Utils.list(tokenId) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.hasAnyKeys(result, [ "decimals", @@ -60,6 +60,34 @@ describe(`#SLP`, () => { "mintingBatonStatus" ]) }) + + it("should NOT get a list of all tokens in existence", async () => { + try { + await bchjs.SLP.Utils.list() + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.fail("Unexpected result!") + } catch (err) { + // console.log(err) + assert.include(err.message, "Not Found") + } + }) + + it("should get information on multiple tokens", async () => { + const tokenIds = [ + // Spice + `4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf`, + // PSF + "38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0" + ] + + const result = await bchjs.SLP.Utils.list(tokenIds) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.equal(result[0].symbol, "SPICE") + assert.equal(result[1].symbol, "PSF") + }) }) // describe("#decodeOpReturn", () => { diff --git a/test/integration/test-bchn-integration.sh b/test/integration/test-bchn-integration.sh index fcc7811..da7a11b 100755 --- a/test/integration/test-bchn-integration.sh +++ b/test/integration/test-bchn-integration.sh @@ -5,4 +5,5 @@ #export IS_USING_FREE_TIER=true cd test/integration/bchn/ -mocha --timeout 30000 blockchain.js control.js encryption.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js +#mocha --timeout 30000 blockchain.js control.js encryption.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js +mocha --timeout 30000 slp.js diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index fa56855..3f5dd4c 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -1,7 +1,7 @@ // Public npm libraries const assert = require("assert") const assert2 = require("chai").assert -const nock = require("nock") // http call mocking +// const nock = require("nock") // http call mocking const sinon = require("sinon") // const axios = require("axios") @@ -12,7 +12,7 @@ let SERVER const REST_URL = process.env.RESTURL ? process.env.RESTURL - : "https://api.fullstack.cash/v4/" + : "https://bchn.fullstack.cash/v4/" // Mock data used for unit tests const mockData = require("./fixtures/slp/mock-utils") @@ -25,7 +25,7 @@ describe("#SLP Utils", () => { beforeEach(() => { // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() + // if (!nock.isActive()) nock.activate() sandbox = sinon.createSandbox() @@ -38,130 +38,78 @@ describe("#SLP Utils", () => { afterEach(() => { // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() + // nock.cleanAll() // clear interceptor list. + // nock.restore() sandbox.restore() }) describe("#list", () => { - it(`should list all SLP tokens`, async () => { - // Mock the call to the REST API - if (process.env.TEST === "unit") { - nock(SERVER) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockList) - } - - const list = await uut.Utils.list() - //console.log(`list: ${JSON.stringify(list, null, 2)}`) - - assert2.isArray(list) - assert2.hasAllKeys(list[0], [ - "blockCreated", - "blockLastActiveMint", - "blockLastActiveSend", - "circulatingSupply", - "containsBaton", - "decimals", - "documentHash", - "documentUri", - "id", - "initialTokenQty", - "mintingBatonStatus", - "name", - "symbol", - "timestamp", - "timestampUnix", - "totalBurned", - "totalMinted", - "txnsSinceGenesis", - "validAddresses", - "versionType" - ]) + it("should do something", () => { + console.log("hello world") }) + // + // it(`should list single SLP token by id`, async () => { + // // Mock the call to the REST API + // // if (process.env.TEST === "unit") { + // // nock(SERVER) + // // .get(uri => uri.includes("/")) + // // .reply(200, mockData.mockToken) + // // } + // + // const tokenId = + // "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84" + // + // console.log(`uut.restURL: ${uut.restURL}`) + // + // const list = await uut.Utils.list(tokenId) + // console.log(`list: ${JSON.stringify(list, null, 2)}`) + // + // // assert.equal(list.id, tokenId) + // }) - it(`should list single SLP token by id: 4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84`, async () => { - // Mock the call to the REST API - if (process.env.TEST === "unit") { - nock(SERVER) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockToken) - } - - const tokenId = - "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84" - - const list = await uut.Utils.list(tokenId) - //console.log(`list: ${JSON.stringify(list, null, 2)}`) - - assert2.hasAllKeys(list, [ - "blockCreated", - "blockLastActiveMint", - "blockLastActiveSend", - "circulatingSupply", - "containsBaton", - "decimals", - "documentHash", - "documentUri", - "id", - "initialTokenQty", - "mintingBatonStatus", - "name", - "symbol", - "timestamp", - "timestampUnix", - "totalBurned", - "totalMinted", - "txnsSinceGenesis", - "validAddresses", - "versionType" - ]) - assert.equal(list.id, tokenId) - }) - - it(`should list multiple SLP tokens by array of ids`, async () => { - // Mock the call to the REST API - if (process.env.TEST === "unit") { - nock(SERVER) - .post(uri => uri.includes("/")) - .reply(200, mockData.mockTokens) - } - - const tokenIds = [ - "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84", - "b3f4f132dc3b9c8c96316346993a8d54d729715147b7b11aa6c8cd909e955313" - ] - - const list = await uut.Utils.list(tokenIds) - // console.log(`list: ${JSON.stringify(list, null, 2)}`) - - assert2.hasAllKeys(list[0], [ - "blockCreated", - "blockLastActiveMint", - "blockLastActiveSend", - "circulatingSupply", - "containsBaton", - "decimals", - "documentHash", - "documentUri", - "id", - "initialTokenQty", - "mintingBatonStatus", - "name", - "symbol", - "timestamp", - "timestampUnix", - "totalBurned", - "totalMinted", - "txnsSinceGenesis", - "validAddresses", - "versionType" - ]) - assert.equal(list[0].id, tokenIds[0]) - }) + // it(`should list multiple SLP tokens by array of ids`, async () => { + // // Mock the call to the REST API + // if (process.env.TEST === "unit") { + // nock(SERVER) + // .post(uri => uri.includes("/")) + // .reply(200, mockData.mockTokens) + // } + // + // const tokenIds = [ + // "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84", + // "b3f4f132dc3b9c8c96316346993a8d54d729715147b7b11aa6c8cd909e955313" + // ] + // + // const list = await uut.Utils.list(tokenIds) + // // console.log(`list: ${JSON.stringify(list, null, 2)}`) + // + // assert2.hasAllKeys(list[0], [ + // "blockCreated", + // "blockLastActiveMint", + // "blockLastActiveSend", + // "circulatingSupply", + // "containsBaton", + // "decimals", + // "documentHash", + // "documentUri", + // "id", + // "initialTokenQty", + // "mintingBatonStatus", + // "name", + // "symbol", + // "timestamp", + // "timestampUnix", + // "totalBurned", + // "totalMinted", + // "txnsSinceGenesis", + // "validAddresses", + // "versionType" + // ]) + // assert.equal(list[0].id, tokenIds[0]) + // }) }) - + /* describe("#balancesForAddress", () => { it(`should throw an error if input is not a string or array of strings`, async () => { try { @@ -1802,8 +1750,10 @@ describe("#SLP Utils", () => { const txid = `d284e71227ec89f714b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e` - const result = await uut.Utils.txDetails(txid) + await uut.Utils.txDetails(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.fail("Unexpected result") } catch (err) { // console.log(`err: `, err) assert2.include(err.error, `TXID not found`, "Expected error message.") @@ -1996,6 +1946,7 @@ describe("#SLP Utils", () => { console.log(`result: ${JSON.stringify(result, null, 2)}`) }) */ + /* }) describe("#getWhitelist", () => { @@ -2105,4 +2056,5 @@ describe("#SLP Utils", () => { } }) }) + */ }) From a81dec068ca56b8f800cfe90722e5d68fea27505 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 13:48:06 -0800 Subject: [PATCH 4/9] fix(address unit tests): Fixed to prevent network call --- package.json | 2 +- test/unit/address.js | 1536 +++++++++++----------- test/unit/bitcoin-cash.js | 314 ----- test/unit/blockbook.js | 275 ---- test/unit/blockchain.js | 494 -------- test/unit/control.js | 64 - test/unit/crypto.js | 100 -- test/unit/ecpairs.js | 146 --- test/unit/electrumx.js | 421 ------ test/unit/encryption.js | 68 - test/unit/generating.js | 28 - test/unit/hdnode.js | 345 ----- test/unit/ipfs.js | 443 ------- test/unit/mining.js | 104 -- test/unit/mnemonic.js | 340 ----- test/unit/ninsight.js | 255 ---- test/unit/openbazaar.js | 129 -- test/unit/raw-tranactions.js | 129 -- test/unit/scripts.js | 476 ------- test/unit/slp-address.js | 838 ------------ test/unit/slp-ecpair.js | 17 - test/unit/slp-nft1.js | 264 ---- test/unit/slp-tokentype1.js | 319 ----- test/unit/transaction-builder.js | 2039 ------------------------------ test/unit/util.js | 35 - 25 files changed, 786 insertions(+), 8395 deletions(-) delete mode 100644 test/unit/bitcoin-cash.js delete mode 100644 test/unit/blockbook.js delete mode 100644 test/unit/blockchain.js delete mode 100644 test/unit/control.js delete mode 100644 test/unit/crypto.js delete mode 100644 test/unit/ecpairs.js delete mode 100644 test/unit/electrumx.js delete mode 100644 test/unit/encryption.js delete mode 100644 test/unit/generating.js delete mode 100644 test/unit/hdnode.js delete mode 100644 test/unit/ipfs.js delete mode 100644 test/unit/mining.js delete mode 100644 test/unit/mnemonic.js delete mode 100644 test/unit/ninsight.js delete mode 100644 test/unit/openbazaar.js delete mode 100644 test/unit/raw-tranactions.js delete mode 100644 test/unit/scripts.js delete mode 100644 test/unit/slp-address.js delete mode 100644 test/unit/slp-ecpair.js delete mode 100644 test/unit/slp-nft1.js delete mode 100644 test/unit/slp-tokentype1.js delete mode 100644 test/unit/transaction-builder.js delete mode 100644 test/unit/util.js diff --git a/package.json b/package.json index 3e4ea02..e0d0142 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ ], "main": "src/bch-js", "scripts": { - "test": "nyc mocha --timeout 30000 test/unit/", + "test": "nyc mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/", "test:integration": "bash test/integration/test-bchn-integration.sh", "test:integration:bchn": "bash test/integration/test-bchn-integration.sh", "test:integration:abc": "RESTURL=https://abc.fullstack.cash/v4/ IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/", diff --git a/test/unit/address.js b/test/unit/address.js index 1a48a5c..56a7224 100644 --- a/test/unit/address.js +++ b/test/unit/address.js @@ -1,9 +1,16 @@ -const fixtures = require("./fixtures/address.json") +// Public npm libraries. const assert = require("assert") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() const Bitcoin = require("bitcoincashjs-lib") +// Mocks +const fixtures = require("./fixtures/address.json") + +// Unit under test (uut) +const BCHJS = require("../../src/bch-js") +let bchjs + +console.log("ping01") + function flatten(arrays) { return [].concat.apply([], arrays) } @@ -74,832 +81,859 @@ const P2SH_ADDRESSES = flatten([ fixtures.cashaddrMainnetP2SH ]) -describe("#addressConversion", () => { - describe("#toLegacyAddress", () => { - it("should translate legacy address format to itself correctly", () => { - assert.deepEqual( - LEGACY_ADDRESSES.map(address => bchjs.Address.toLegacyAddress(address)), - LEGACY_ADDRESSES - ) +console.log("ping02") + +describe("#address.js", () => { + beforeEach(() => { + bchjs = new BCHJS() + }) + + describe("#addressConversion", () => { + describe("#toLegacyAddress", () => { + it("should translate legacy address format to itself correctly", () => { + assert.deepEqual( + LEGACY_ADDRESSES.map(address => + bchjs.Address.toLegacyAddress(address) + ), + LEGACY_ADDRESSES + ) + }) + + it("should convert cashaddr address to legacy base58Check", () => { + assert.deepEqual( + CASHADDR_ADDRESSES.map(address => + bchjs.Address.toLegacyAddress(address) + ), + LEGACY_ADDRESSES + ) + }) + + it("should convert cashaddr regtest address to legacy base58Check", () => { + assert.deepEqual( + REGTEST_ADDRESSES.map(address => + bchjs.Address.toLegacyAddress(address) + ), + fixtures.legacyTestnetP2PKH + ) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.toLegacyAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.toLegacyAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) }) - it("should convert cashaddr address to legacy base58Check", () => { - assert.deepEqual( - CASHADDR_ADDRESSES.map(address => - bchjs.Address.toLegacyAddress(address) - ), - LEGACY_ADDRESSES - ) + describe("#toCashAddress", () => { + it("should convert legacy base58Check address to cashaddr", () => { + assert.deepEqual( + LEGACY_ADDRESSES.map(address => + bchjs.Address.toCashAddress(address, true) + ), + CASHADDR_ADDRESSES + ) + }) + + it("should convert legacy base58Check address to regtest cashaddr", () => { + assert.deepEqual( + fixtures.legacyTestnetP2PKH.map(address => + bchjs.Address.toCashAddress(address, true, true) + ), + REGTEST_ADDRESSES + ) + }) + + it("should translate cashaddr address format to itself correctly", () => { + assert.deepEqual( + CASHADDR_ADDRESSES.map(address => + bchjs.Address.toCashAddress(address, true) + ), + CASHADDR_ADDRESSES + ) + }) + + it("should translate regtest cashaddr address format to itself correctly", () => { + assert.deepEqual( + REGTEST_ADDRESSES.map(address => + bchjs.Address.toCashAddress(address, true, true) + ), + REGTEST_ADDRESSES + ) + }) + + it("should translate no-prefix cashaddr address format to itself correctly", () => { + assert.deepEqual( + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.toCashAddress(address, true) + ), + CASHADDR_ADDRESSES + ) + }) + + it("should translate no-prefix regtest cashaddr address format to itself correctly", () => { + assert.deepEqual( + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.toCashAddress(address, true, true) + ), + REGTEST_ADDRESSES + ) + }) + + it("should translate cashaddr address format to itself of no-prefix correctly", () => { + CASHADDR_ADDRESSES.forEach(address => { + const noPrefix = bchjs.Address.toCashAddress(address, false) + assert.equal(address.split(":")[1], noPrefix) + }) + }) + + it("should translate regtest cashaddr address format to itself of no-prefix correctly", () => { + REGTEST_ADDRESSES.forEach(address => { + const noPrefix = bchjs.Address.toCashAddress(address, false, true) + assert.equal(address.split(":")[1], noPrefix) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.BitcoinCash.Address.toCashAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.BitcoinCash.Address.toCashAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) }) - it("should convert cashaddr regtest address to legacy base58Check", () => { - assert.deepEqual( - REGTEST_ADDRESSES.map(address => - bchjs.Address.toLegacyAddress(address) - ), - fixtures.legacyTestnetP2PKH - ) + describe("#toHash160", () => { + it("should convert legacy base58check address to hash160", () => { + assert.deepEqual( + LEGACY_ADDRESSES.map(address => bchjs.Address.toHash160(address)), + HASH160_HASHES + ) + }) + + it("should convert cashaddr address to hash160", () => { + assert.deepEqual( + CASHADDR_ADDRESSES.map(address => bchjs.Address.toHash160(address)), + HASH160_HASHES + ) + }) + + it("should convert regtest cashaddr address to hash160", () => { + assert.deepEqual( + REGTEST_ADDRESSES.map(address => bchjs.Address.toHash160(address)), + fixtures.hash160TestnetP2PKH + ) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.toHash160() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.toHash160("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) }) - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.toLegacyAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.toLegacyAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) + describe("#fromHash160", () => { + it("should convert hash160 to mainnet P2PKH legacy base58check address", () => { + assert.deepEqual( + fixtures.hash160MainnetP2PKH.map(hash160 => + bchjs.Address.hash160ToLegacy(hash160) + ), + fixtures.legacyMainnetP2PKH + ) + }) + + it("should convert hash160 to mainnet P2SH legacy base58check address", () => { + assert.deepEqual( + fixtures.hash160MainnetP2SH.map(hash160 => + bchjs.Address.hash160ToLegacy( + hash160, + Bitcoin.networks.bitcoin.scriptHash + ) + ), + fixtures.legacyMainnetP2SH + ) + }) + + it("should convert hash160 to testnet P2PKH legacy base58check address", () => { + assert.deepEqual( + fixtures.hash160TestnetP2PKH.map(hash160 => + bchjs.Address.hash160ToLegacy( + hash160, + Bitcoin.networks.testnet.pubKeyHash + ) + ), + fixtures.legacyTestnetP2PKH + ) + }) + + it("should convert hash160 to mainnet P2PKH cash address", () => { + assert.deepEqual( + fixtures.hash160MainnetP2PKH.map(hash160 => + bchjs.Address.hash160ToCash(hash160) + ), + fixtures.cashaddrMainnetP2PKH + ) + }) + + it("should convert hash160 to mainnet P2SH cash address", () => { + assert.deepEqual( + fixtures.hash160MainnetP2SH.map(hash160 => + bchjs.Address.hash160ToCash( + hash160, + Bitcoin.networks.bitcoin.scriptHash + ) + ), + fixtures.cashaddrMainnetP2SH + ) + }) + + it("should convert hash160 to testnet P2PKH cash address", () => { + assert.deepEqual( + fixtures.hash160TestnetP2PKH.map(hash160 => + bchjs.Address.hash160ToCash( + hash160, + Bitcoin.networks.testnet.pubKeyHash + ) + ), + fixtures.cashaddrTestnetP2PKH + ) + }) + + it("should convert hash160 to regtest P2PKH cash address", () => { + assert.deepEqual( + fixtures.hash160TestnetP2PKH.map(hash160 => + bchjs.Address.hash160ToCash( + hash160, + Bitcoin.networks.testnet.pubKeyHash, + true + ) + ), + REGTEST_ADDRESSES + ) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.hash160ToLegacy() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.hash160ToLegacy("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.hash160ToCash() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.hash160ToCash("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) }) }) }) - describe("#toCashAddress", () => { - it("should convert legacy base58Check address to cashaddr", () => { - assert.deepEqual( - LEGACY_ADDRESSES.map(address => - bchjs.Address.toCashAddress(address, true) - ), - CASHADDR_ADDRESSES - ) + describe("address format detection", () => { + describe("#isLegacyAddress", () => { + describe("is legacy", () => { + LEGACY_ADDRESSES.forEach(address => { + it(`should detect ${address} is a legacy base58Check address`, () => { + const isBase58Check = bchjs.Address.isLegacyAddress(address) + assert.equal(isBase58Check, true) + }) + }) + }) + describe("is not legacy", () => { + CASHADDR_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a legacy address`, () => { + const isBase58Check = bchjs.Address.isLegacyAddress(address) + assert.equal(isBase58Check, false) + }) + }) + + REGTEST_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a legacy address`, () => { + const isBase58Check = bchjs.Address.isLegacyAddress(address) + assert.equal(isBase58Check, false) + }) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.isLegacyAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.isLegacyAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) }) - it("should convert legacy base58Check address to regtest cashaddr", () => { - assert.deepEqual( - fixtures.legacyTestnetP2PKH.map(address => - bchjs.Address.toCashAddress(address, true, true) - ), - REGTEST_ADDRESSES - ) + describe("#isCashAddress", () => { + describe("is cashaddr", () => { + CASHADDR_ADDRESSES.forEach(address => { + it(`should detect ${address} is a cashaddr address`, () => { + const isCashaddr = bchjs.Address.isCashAddress(address) + assert.equal(isCashaddr, true) + }) + }) + + REGTEST_ADDRESSES.forEach(address => { + it(`should detect ${address} is a cashaddr address`, () => { + const isCashaddr = bchjs.Address.isCashAddress(address) + assert.equal(isCashaddr, true) + }) + }) + }) + + describe("is not cashaddr", () => { + LEGACY_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a cashaddr address`, () => { + const isCashaddr = bchjs.Address.isCashAddress(address) + assert.equal(isCashaddr, false) + }) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.isCashAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.isCashAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) + }) + describe("#isHash160", () => { + describe("is hash160", () => { + HASH160_HASHES.forEach(address => { + it(`should detect ${address} is a hash160 hash`, () => { + const isHash160 = bchjs.Address.isHash160(address) + assert.equal(isHash160, true) + }) + }) + }) + describe("is not hash160", () => { + LEGACY_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a hash160 hash`, () => { + const isHash160 = bchjs.Address.isHash160(address) + assert.equal(isHash160, false) + }) + }) + + CASHADDR_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a hash160 hash`, () => { + const isHash160 = bchjs.Address.isHash160(address) + assert.equal(isHash160, false) + }) + }) + + REGTEST_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a legacy address`, () => { + const isHash160 = bchjs.Address.isHash160(address) + assert.equal(isHash160, false) + }) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.isHash160() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.isHash160("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) + }) + }) + + describe("network detection", () => { + describe("#isMainnetAddress", () => { + describe("is mainnet", () => { + MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a mainnet address`, () => { + const isMainnet = bchjs.Address.isMainnetAddress(address) + assert.equal(isMainnet, true) + }) + }) + }) + + describe("is not mainnet", () => { + TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a mainnet address`, () => { + const isMainnet = bchjs.Address.isMainnetAddress(address) + assert.equal(isMainnet, false) + }) + }) + + REGTEST_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a mainnet address`, () => { + const isMainnet = bchjs.Address.isMainnetAddress(address) + assert.equal(isMainnet, false) + }) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.isMainnetAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.isMainnetAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) }) - it("should translate cashaddr address format to itself correctly", () => { - assert.deepEqual( - CASHADDR_ADDRESSES.map(address => - bchjs.Address.toCashAddress(address, true) - ), - CASHADDR_ADDRESSES - ) + describe("#isTestnetAddress", () => { + describe("is testnet", () => { + TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnet = bchjs.Address.isTestnetAddress(address) + assert.equal(isTestnet, true) + }) + }) + }) + + describe("is not testnet", () => { + MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a testnet address`, () => { + const isTestnet = bchjs.Address.isTestnetAddress(address) + assert.equal(isTestnet, false) + }) + }) + + REGTEST_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a testnet address`, () => { + const isTestnet = bchjs.Address.isTestnetAddress(address) + assert.equal(isTestnet, false) + }) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.isTestnetAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.isTestnetAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) }) - it("should translate regtest cashaddr address format to itself correctly", () => { - assert.deepEqual( - REGTEST_ADDRESSES.map(address => - bchjs.Address.toCashAddress(address, true, true) - ), - REGTEST_ADDRESSES - ) + describe("#isRegTestAddress", () => { + describe("is testnet", () => { + REGTEST_ADDRESSES.forEach(address => { + it(`should detect ${address} is a regtest address`, () => { + const isRegTest = bchjs.Address.isRegTestAddress(address) + assert.equal(isRegTest, true) + }) + }) + }) + + describe("is not testnet", () => { + MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a regtest address`, () => { + const isRegTest = bchjs.Address.isRegTestAddress(address) + assert.equal(isRegTest, false) + }) + }) + + TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a regtest address`, () => { + const isRegTest = bchjs.Address.isRegTestAddress(address) + assert.equal(isRegTest, false) + }) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.isRegTestAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.isRegTestAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) + }) + }) + + describe("address type detection", () => { + describe("#isP2PKHAddress", () => { + describe("is P2PKH", () => { + P2PKH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a P2PKH address`, () => { + const isP2PKH = bchjs.Address.isP2PKHAddress(address) + assert.equal(isP2PKH, true) + }) + }) + }) + + describe("is not P2PKH", () => { + P2SH_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a P2PKH address`, () => { + const isP2PKH = bchjs.Address.isP2PKHAddress(address) + assert.equal(isP2PKH, false) + }) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.isP2PKHAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.isP2PKHAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) }) - it("should translate no-prefix cashaddr address format to itself correctly", () => { + describe("#isP2SHAddress", () => { + describe("is P2SH", () => { + P2SH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a P2SH address`, () => { + const isP2SH = bchjs.Address.isP2SHAddress(address) + assert.equal(isP2SH, true) + }) + }) + }) + + describe("is not P2SH", () => { + P2PKH_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a P2SH address`, () => { + const isP2SH = bchjs.Address.isP2SHAddress(address) + assert.equal(isP2SH, false) + }) + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.isP2SHAddress() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.isP2SHAddress("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) + }) + }) + }) + + describe("cashaddr prefix detection", () => { + it("should return the same result for detectAddressFormat", () => { assert.deepEqual( CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.toCashAddress(address, true) + bchjs.Address.detectAddressFormat(address) ), - CASHADDR_ADDRESSES + CASHADDR_ADDRESSES.map(address => + bchjs.Address.detectAddressFormat(address) + ) ) - }) - - it("should translate no-prefix regtest cashaddr address format to itself correctly", () => { assert.deepEqual( REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.toCashAddress(address, true, true) + bchjs.Address.detectAddressFormat(address) ), - REGTEST_ADDRESSES + REGTEST_ADDRESSES.map(address => + bchjs.Address.detectAddressFormat(address) + ) ) }) - - it("should translate cashaddr address format to itself of no-prefix correctly", () => { - CASHADDR_ADDRESSES.forEach(address => { - const noPrefix = bchjs.Address.toCashAddress(address, false) - assert.equal(address.split(":")[1], noPrefix) - }) - }) - - it("should translate regtest cashaddr address format to itself of no-prefix correctly", () => { - REGTEST_ADDRESSES.forEach(address => { - const noPrefix = bchjs.Address.toCashAddress(address, false, true) - assert.equal(address.split(":")[1], noPrefix) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.BitcoinCash.Address.toCashAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.BitcoinCash.Address.toCashAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) - describe("#toHash160", () => { - it("should convert legacy base58check address to hash160", () => { + it("should return the same result for detectAddressNetwork", () => { assert.deepEqual( - LEGACY_ADDRESSES.map(address => bchjs.Address.toHash160(address)), - HASH160_HASHES - ) - }) - - it("should convert cashaddr address to hash160", () => { - assert.deepEqual( - CASHADDR_ADDRESSES.map(address => bchjs.Address.toHash160(address)), - HASH160_HASHES - ) - }) - - it("should convert regtest cashaddr address to hash160", () => { - assert.deepEqual( - REGTEST_ADDRESSES.map(address => bchjs.Address.toHash160(address)), - fixtures.hash160TestnetP2PKH - ) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.toHash160() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.toHash160("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) - describe("#fromHash160", () => { - it("should convert hash160 to mainnet P2PKH legacy base58check address", () => { - assert.deepEqual( - fixtures.hash160MainnetP2PKH.map(hash160 => - bchjs.Address.hash160ToLegacy(hash160) + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.detectAddressNetwork(address) ), - fixtures.legacyMainnetP2PKH + CASHADDR_ADDRESSES.map(address => + bchjs.Address.detectAddressNetwork(address) + ) ) - }) - - it("should convert hash160 to mainnet P2SH legacy base58check address", () => { assert.deepEqual( - fixtures.hash160MainnetP2SH.map(hash160 => - bchjs.Address.hash160ToLegacy( - hash160, - Bitcoin.networks.bitcoin.scriptHash - ) + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.detectAddressNetwork(address) ), - fixtures.legacyMainnetP2SH + REGTEST_ADDRESSES.map(address => + bchjs.Address.detectAddressNetwork(address) + ) ) }) - - it("should convert hash160 to testnet P2PKH legacy base58check address", () => { + it("should return the same result for detectAddressType", () => { assert.deepEqual( - fixtures.hash160TestnetP2PKH.map(hash160 => - bchjs.Address.hash160ToLegacy( - hash160, - Bitcoin.networks.testnet.pubKeyHash - ) + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.detectAddressType(address) ), - fixtures.legacyTestnetP2PKH + CASHADDR_ADDRESSES.map(address => + bchjs.Address.detectAddressType(address) + ) ) - }) - - it("should convert hash160 to mainnet P2PKH cash address", () => { assert.deepEqual( - fixtures.hash160MainnetP2PKH.map(hash160 => - bchjs.Address.hash160ToCash(hash160) + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.detectAddressType(address) ), - fixtures.cashaddrMainnetP2PKH + REGTEST_ADDRESSES.map(address => + bchjs.Address.detectAddressType(address) + ) ) }) - - it("should convert hash160 to mainnet P2SH cash address", () => { + it("should return the same result for toLegacyAddress", () => { assert.deepEqual( - fixtures.hash160MainnetP2SH.map(hash160 => - bchjs.Address.hash160ToCash( - hash160, - Bitcoin.networks.bitcoin.scriptHash - ) + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.toLegacyAddress(address) ), - fixtures.cashaddrMainnetP2SH + CASHADDR_ADDRESSES.map(address => + bchjs.Address.toLegacyAddress(address) + ) ) - }) - - it("should convert hash160 to testnet P2PKH cash address", () => { assert.deepEqual( - fixtures.hash160TestnetP2PKH.map(hash160 => - bchjs.Address.hash160ToCash( - hash160, - Bitcoin.networks.testnet.pubKeyHash - ) + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.toLegacyAddress(address) ), - fixtures.cashaddrTestnetP2PKH + REGTEST_ADDRESSES.map(address => bchjs.Address.toLegacyAddress(address)) ) }) - - it("should convert hash160 to regtest P2PKH cash address", () => { + it("should return the same result for isLegacyAddress", () => { assert.deepEqual( - fixtures.hash160TestnetP2PKH.map(hash160 => - bchjs.Address.hash160ToCash( - hash160, - Bitcoin.networks.testnet.pubKeyHash, - true - ) + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isLegacyAddress(address) ), - REGTEST_ADDRESSES + CASHADDR_ADDRESSES.map(address => + bchjs.Address.isLegacyAddress(address) + ) + ) + assert.deepEqual( + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isLegacyAddress(address) + ), + REGTEST_ADDRESSES.map(address => bchjs.Address.isLegacyAddress(address)) ) }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.hash160ToLegacy() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.hash160ToLegacy("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.hash160ToCash() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.hash160ToCash("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) -}) - -describe("address format detection", () => { - describe("#isLegacyAddress", () => { - describe("is legacy", () => { - LEGACY_ADDRESSES.forEach(address => { - it(`should detect ${address} is a legacy base58Check address`, () => { - const isBase58Check = bchjs.Address.isLegacyAddress(address) - assert.equal(isBase58Check, true) - }) - }) - }) - describe("is not legacy", () => { - CASHADDR_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a legacy address`, () => { - const isBase58Check = bchjs.Address.isLegacyAddress(address) - assert.equal(isBase58Check, false) - }) - }) - - REGTEST_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a legacy address`, () => { - const isBase58Check = bchjs.Address.isLegacyAddress(address) - assert.equal(isBase58Check, false) - }) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.isLegacyAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.isLegacyAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) - - describe("#isCashAddress", () => { - describe("is cashaddr", () => { - CASHADDR_ADDRESSES.forEach(address => { - it(`should detect ${address} is a cashaddr address`, () => { - const isCashaddr = bchjs.Address.isCashAddress(address) - assert.equal(isCashaddr, true) - }) - }) - - REGTEST_ADDRESSES.forEach(address => { - it(`should detect ${address} is a cashaddr address`, () => { - const isCashaddr = bchjs.Address.isCashAddress(address) - assert.equal(isCashaddr, true) - }) - }) - }) - - describe("is not cashaddr", () => { - LEGACY_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a cashaddr address`, () => { - const isCashaddr = bchjs.Address.isCashAddress(address) - assert.equal(isCashaddr, false) - }) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.isCashAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.isCashAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) - describe("#isHash160", () => { - describe("is hash160", () => { - HASH160_HASHES.forEach(address => { - it(`should detect ${address} is a hash160 hash`, () => { - const isHash160 = bchjs.Address.isHash160(address) - assert.equal(isHash160, true) - }) - }) - }) - describe("is not hash160", () => { - LEGACY_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a hash160 hash`, () => { - const isHash160 = bchjs.Address.isHash160(address) - assert.equal(isHash160, false) - }) - }) - - CASHADDR_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a hash160 hash`, () => { - const isHash160 = bchjs.Address.isHash160(address) - assert.equal(isHash160, false) - }) - }) - - REGTEST_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a legacy address`, () => { - const isHash160 = bchjs.Address.isHash160(address) - assert.equal(isHash160, false) - }) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.isHash160() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.isHash160("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) -}) - -describe("network detection", () => { - describe("#isMainnetAddress", () => { - describe("is mainnet", () => { - MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a mainnet address`, () => { - const isMainnet = bchjs.Address.isMainnetAddress(address) - assert.equal(isMainnet, true) - }) - }) - }) - - describe("is not mainnet", () => { - TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a mainnet address`, () => { - const isMainnet = bchjs.Address.isMainnetAddress(address) - assert.equal(isMainnet, false) - }) - }) - - REGTEST_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a mainnet address`, () => { - const isMainnet = bchjs.Address.isMainnetAddress(address) - assert.equal(isMainnet, false) - }) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.isMainnetAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.isMainnetAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) - - describe("#isTestnetAddress", () => { - describe("is testnet", () => { - TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnet = bchjs.Address.isTestnetAddress(address) - assert.equal(isTestnet, true) - }) - }) - }) - - describe("is not testnet", () => { - MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a testnet address`, () => { - const isTestnet = bchjs.Address.isTestnetAddress(address) - assert.equal(isTestnet, false) - }) - }) - - REGTEST_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a testnet address`, () => { - const isTestnet = bchjs.Address.isTestnetAddress(address) - assert.equal(isTestnet, false) - }) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.isTestnetAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.isTestnetAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) - - describe("#isRegTestAddress", () => { - describe("is testnet", () => { - REGTEST_ADDRESSES.forEach(address => { - it(`should detect ${address} is a regtest address`, () => { - const isRegTest = bchjs.Address.isRegTestAddress(address) - assert.equal(isRegTest, true) - }) - }) - }) - - describe("is not testnet", () => { - MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a regtest address`, () => { - const isRegTest = bchjs.Address.isRegTestAddress(address) - assert.equal(isRegTest, false) - }) - }) - - TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a regtest address`, () => { - const isRegTest = bchjs.Address.isRegTestAddress(address) - assert.equal(isRegTest, false) - }) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.isRegTestAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.isRegTestAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) -}) - -describe("address type detection", () => { - describe("#isP2PKHAddress", () => { - describe("is P2PKH", () => { - P2PKH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a P2PKH address`, () => { - const isP2PKH = bchjs.Address.isP2PKHAddress(address) - assert.equal(isP2PKH, true) - }) - }) - }) - - describe("is not P2PKH", () => { - P2SH_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a P2PKH address`, () => { - const isP2PKH = bchjs.Address.isP2PKHAddress(address) - assert.equal(isP2PKH, false) - }) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.isP2PKHAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.isP2PKHAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) - - describe("#isP2SHAddress", () => { - describe("is P2SH", () => { - P2SH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a P2SH address`, () => { - const isP2SH = bchjs.Address.isP2SHAddress(address) - assert.equal(isP2SH, true) - }) - }) - }) - - describe("is not P2SH", () => { - P2PKH_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a P2SH address`, () => { - const isP2SH = bchjs.Address.isP2SHAddress(address) - assert.equal(isP2SH, false) - }) - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.isP2SHAddress() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.isP2SHAddress("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) - }) -}) - -describe("cashaddr prefix detection", () => { - it("should return the same result for detectAddressFormat", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.detectAddressFormat(address) - ), - CASHADDR_ADDRESSES.map(address => - bchjs.Address.detectAddressFormat(address) + it("should return the same result for isCashAddress", () => { + assert.deepEqual( + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isCashAddress(address) + ), + CASHADDR_ADDRESSES.map(address => bchjs.Address.isCashAddress(address)) ) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.detectAddressFormat(address) - ), - REGTEST_ADDRESSES.map(address => - bchjs.Address.detectAddressFormat(address) + assert.deepEqual( + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isCashAddress(address) + ), + REGTEST_ADDRESSES.map(address => bchjs.Address.isCashAddress(address)) ) - ) - }) - it("should return the same result for detectAddressNetwork", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.detectAddressNetwork(address) - ), - CASHADDR_ADDRESSES.map(address => - bchjs.Address.detectAddressNetwork(address) + }) + it("should return the same result for isMainnetAddress", () => { + assert.deepEqual( + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isMainnetAddress(address) + ), + CASHADDR_ADDRESSES.map(address => + bchjs.Address.isMainnetAddress(address) + ) ) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.detectAddressNetwork(address) - ), - REGTEST_ADDRESSES.map(address => - bchjs.Address.detectAddressNetwork(address) + assert.deepEqual( + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isMainnetAddress(address) + ), + REGTEST_ADDRESSES.map(address => + bchjs.Address.isMainnetAddress(address) + ) ) - ) - }) - it("should return the same result for detectAddressType", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.detectAddressType(address) - ), - CASHADDR_ADDRESSES.map(address => - bchjs.Address.detectAddressType(address) + }) + it("should return the same result for isTestnetAddress", () => { + assert.deepEqual( + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isTestnetAddress(address) + ), + CASHADDR_ADDRESSES.map(address => + bchjs.Address.isTestnetAddress(address) + ) + ) + assert.deepEqual( + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isTestnetAddress(address) + ), + REGTEST_ADDRESSES.map(address => + bchjs.Address.isTestnetAddress(address) + ) + ) + }) + it("should return the same result for isP2PKHAddress", () => { + assert.deepEqual( + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isP2PKHAddress(address) + ), + CASHADDR_ADDRESSES.map(address => bchjs.Address.isP2PKHAddress(address)) + ) + assert.deepEqual( + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isP2PKHAddress(address) + ), + REGTEST_ADDRESSES.map(address => bchjs.Address.isP2PKHAddress(address)) + ) + }) + it("should return the same result for isP2SHAddress", () => { + assert.deepEqual( + CASHADDR_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isP2SHAddress(address) + ), + CASHADDR_ADDRESSES.map(address => bchjs.Address.isP2SHAddress(address)) + ) + assert.deepEqual( + REGTEST_ADDRESSES_NO_PREFIX.map(address => + bchjs.Address.isP2SHAddress(address) + ), + REGTEST_ADDRESSES.map(address => bchjs.Address.isP2SHAddress(address)) ) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.detectAddressType(address) - ), - REGTEST_ADDRESSES.map(address => bchjs.Address.detectAddressType(address)) - ) - }) - it("should return the same result for toLegacyAddress", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.toLegacyAddress(address) - ), - CASHADDR_ADDRESSES.map(address => bchjs.Address.toLegacyAddress(address)) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.toLegacyAddress(address) - ), - REGTEST_ADDRESSES.map(address => bchjs.Address.toLegacyAddress(address)) - ) - }) - it("should return the same result for isLegacyAddress", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isLegacyAddress(address) - ), - CASHADDR_ADDRESSES.map(address => bchjs.Address.isLegacyAddress(address)) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isLegacyAddress(address) - ), - REGTEST_ADDRESSES.map(address => bchjs.Address.isLegacyAddress(address)) - ) - }) - it("should return the same result for isCashAddress", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isCashAddress(address) - ), - CASHADDR_ADDRESSES.map(address => bchjs.Address.isCashAddress(address)) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isCashAddress(address) - ), - REGTEST_ADDRESSES.map(address => bchjs.Address.isCashAddress(address)) - ) - }) - it("should return the same result for isMainnetAddress", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isMainnetAddress(address) - ), - CASHADDR_ADDRESSES.map(address => bchjs.Address.isMainnetAddress(address)) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isMainnetAddress(address) - ), - REGTEST_ADDRESSES.map(address => bchjs.Address.isMainnetAddress(address)) - ) - }) - it("should return the same result for isTestnetAddress", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isTestnetAddress(address) - ), - CASHADDR_ADDRESSES.map(address => bchjs.Address.isTestnetAddress(address)) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isTestnetAddress(address) - ), - REGTEST_ADDRESSES.map(address => bchjs.Address.isTestnetAddress(address)) - ) - }) - it("should return the same result for isP2PKHAddress", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isP2PKHAddress(address) - ), - CASHADDR_ADDRESSES.map(address => bchjs.Address.isP2PKHAddress(address)) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isP2PKHAddress(address) - ), - REGTEST_ADDRESSES.map(address => bchjs.Address.isP2PKHAddress(address)) - ) - }) - it("should return the same result for isP2SHAddress", () => { - assert.deepEqual( - CASHADDR_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isP2SHAddress(address) - ), - CASHADDR_ADDRESSES.map(address => bchjs.Address.isP2SHAddress(address)) - ) - assert.deepEqual( - REGTEST_ADDRESSES_NO_PREFIX.map(address => - bchjs.Address.isP2SHAddress(address) - ), - REGTEST_ADDRESSES.map(address => bchjs.Address.isP2SHAddress(address)) - ) - }) -}) - -describe("#detectAddressFormat", () => { - LEGACY_ADDRESSES.forEach(address => { - it(`should detect ${address} is a legacy base58Check address`, () => { - const isBase58Check = bchjs.Address.detectAddressFormat(address) - assert.equal(isBase58Check, "legacy") }) }) - CASHADDR_ADDRESSES.forEach(address => { - it(`should detect ${address} is a legacy cashaddr address`, () => { - const isCashaddr = bchjs.Address.detectAddressFormat(address) - assert.equal(isCashaddr, "cashaddr") + describe("#detectAddressFormat", () => { + LEGACY_ADDRESSES.forEach(address => { + it(`should detect ${address} is a legacy base58Check address`, () => { + const isBase58Check = bchjs.Address.detectAddressFormat(address) + assert.equal(isBase58Check, "legacy") + }) + }) + + CASHADDR_ADDRESSES.forEach(address => { + it(`should detect ${address} is a legacy cashaddr address`, () => { + const isCashaddr = bchjs.Address.detectAddressFormat(address) + assert.equal(isCashaddr, "cashaddr") + }) + }) + + REGTEST_ADDRESSES.forEach(address => { + it(`should detect ${address} is a legacy cashaddr address`, () => { + const isCashaddr = bchjs.Address.detectAddressFormat(address) + assert.equal(isCashaddr, "cashaddr") + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.detectAddressFormat() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.detectAddressFormat("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) }) }) - REGTEST_ADDRESSES.forEach(address => { - it(`should detect ${address} is a legacy cashaddr address`, () => { - const isCashaddr = bchjs.Address.detectAddressFormat(address) - assert.equal(isCashaddr, "cashaddr") + describe("#detectAddressNetwork", () => { + MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a mainnet address`, () => { + const isMainnet = bchjs.Address.detectAddressNetwork(address) + assert.equal(isMainnet, "mainnet") + }) + }) + + TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnet = bchjs.Address.detectAddressNetwork(address) + assert.equal(isTestnet, "testnet") + }) + }) + + REGTEST_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnet = bchjs.Address.detectAddressNetwork(address) + assert.equal(isTestnet, "regtest") + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.detectAddressNetwork() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.detectAddressNetwork("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) }) }) - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.detectAddressFormat() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.detectAddressFormat("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) + describe("#detectAddressType", () => { + P2PKH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a P2PKH address`, () => { + const isP2PKH = bchjs.Address.detectAddressType(address) + assert.equal(isP2PKH, "p2pkh") + }) }) - }) -}) -describe("#detectAddressNetwork", () => { - MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a mainnet address`, () => { - const isMainnet = bchjs.Address.detectAddressNetwork(address) - assert.equal(isMainnet, "mainnet") + P2SH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a P2SH address`, () => { + const isP2SH = bchjs.Address.detectAddressType(address) + assert.equal(isP2SH, "p2sh") + }) + }) + + describe("errors", () => { + it("should fail when called with an invalid address", () => { + assert.throws(() => { + bchjs.Address.detectAddressType() + }, bchjs.BitcoinCash.InvalidAddressError) + assert.throws(() => { + bchjs.Address.detectAddressType("some invalid address") + }, bchjs.BitcoinCash.InvalidAddressError) + }) }) }) - TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnet = bchjs.Address.detectAddressNetwork(address) - assert.equal(isTestnet, "testnet") + describe("#fromXPub", () => { + XPUBS.forEach((xpub, i) => { + xpub.addresses.forEach((address, j) => { + it(`generate public external change address ${j} for ${xpub.xpub}`, () => { + assert.equal(bchjs.Address.fromXPub(xpub.xpub, `0/${j}`), address) + }) + }) }) }) - REGTEST_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnet = bchjs.Address.detectAddressNetwork(address) - assert.equal(isTestnet, "regtest") - }) - }) + describe("#fromOutputScript", () => { + it("generate address from output script", () => { + const script = bchjs.Script.encode([ + Buffer.from("BOX", "ascii"), + bchjs.Script.opcodes.OP_CAT, + Buffer.from("BITBOX", "ascii"), + bchjs.Script.opcodes.OP_EQUAL + ]) - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.detectAddressNetwork() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.detectAddressNetwork("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) -}) + // hash160 script buffer + const p2sh_hash160 = bchjs.Crypto.hash160(script) -describe("#detectAddressType", () => { - P2PKH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a P2PKH address`, () => { - const isP2PKH = bchjs.Address.detectAddressType(address) - assert.equal(isP2PKH, "p2pkh") - }) - }) + // encode hash160 as P2SH output + const scriptPubKey = bchjs.Script.scriptHash.output.encode(p2sh_hash160) + const p2shAddress = bchjs.Address.fromOutputScript(scriptPubKey) - P2SH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a P2SH address`, () => { - const isP2SH = bchjs.Address.detectAddressType(address) - assert.equal(isP2SH, "p2sh") - }) - }) - - describe("errors", () => { - it("should fail when called with an invalid address", () => { - assert.throws(() => { - bchjs.Address.detectAddressType() - }, bchjs.BitcoinCash.InvalidAddressError) - assert.throws(() => { - bchjs.Address.detectAddressType("some invalid address") - }, bchjs.BitcoinCash.InvalidAddressError) - }) - }) -}) - -describe("#fromXPub", () => { - XPUBS.forEach((xpub, i) => { - xpub.addresses.forEach((address, j) => { - it(`generate public external change address ${j} for ${xpub.xpub}`, () => { - assert.equal(bchjs.Address.fromXPub(xpub.xpub, `0/${j}`), address) + fixtures.p2shMainnet.forEach((address, i) => { + assert.equal(p2shAddress, address) }) }) }) }) - -describe("#fromOutputScript", () => { - const script = bchjs.Script.encode([ - Buffer.from("BOX", "ascii"), - bchjs.Script.opcodes.OP_CAT, - Buffer.from("BITBOX", "ascii"), - bchjs.Script.opcodes.OP_EQUAL - ]) - - // hash160 script buffer - const p2sh_hash160 = bchjs.Crypto.hash160(script) - - // encode hash160 as P2SH output - const scriptPubKey = bchjs.Script.scriptHash.output.encode(p2sh_hash160) - const p2shAddress = bchjs.Address.fromOutputScript(scriptPubKey) - fixtures.p2shMainnet.forEach((address, i) => { - it(`generate address from output script`, () => { - assert.equal(p2shAddress, address) - }) - }) -}) diff --git a/test/unit/bitcoin-cash.js b/test/unit/bitcoin-cash.js deleted file mode 100644 index e64901b..0000000 --- a/test/unit/bitcoin-cash.js +++ /dev/null @@ -1,314 +0,0 @@ -const fixtures = require("./fixtures/bitcoincash.json") -const assert = require("assert") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() - -// TODO -// 1. generate testnet p2sh -// 2. generate cashaddr mainnet p2sh -// 3. generate cashaddr testnet p2sh -// 4. create bchjs fromBase58 method -// * confirm xpub cannot generate WIF -// * confirm xpriv can generate WIF -// 5. create fromXPriv method w/ tests and docs -// 1. mainnet -// * confirm xpriv generates address -// * confirm xpriv generates WIF -// 2. testnet -// * confirm xpriv generates address -// * confirm xpriv generates WIF -// 6. More error test cases. - -describe("#BitcoinCash", () => { - describe("price conversion", () => { - describe("#toBitcoinCash", () => { - fixtures.conversion.toBCH.satoshis.forEach(satoshi => { - it(`should convert ${satoshi[0]} Satoshis to ${ - satoshi[1] - } $BCH`, () => { - assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1]) - }) - }) - - fixtures.conversion.toBCH.strings.forEach(satoshi => { - it(`should convert "${satoshi[0]}" Satoshis as a string to ${ - satoshi[1] - } $BCH`, () => { - assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1]) - }) - }) - - fixtures.conversion.toBCH.not.forEach(bch => { - it(`converts ${bch[0]} to Bitcoin Cash, not to ${ - bch[1] - } Satoshi`, () => { - assert.notEqual(bchjs.BitcoinCash.toBitcoinCash(bch[0]), bch[1]) - }) - }) - - fixtures.conversion.toBCH.rounding.forEach(satoshi => { - it(`rounding ${satoshi[0]} to ${satoshi[1]} $BCH`, () => { - assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1]) - }) - }) - }) - - describe("#toSatoshi", () => { - fixtures.conversion.toSatoshi.bch.forEach(bch => { - it(`should convert ${bch[0]} $BCH to ${bch[1]} Satoshis`, () => { - assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1]) - }) - }) - - fixtures.conversion.toSatoshi.strings.forEach(bch => { - it(`should convert "${bch[0]}" $BCH as a string to ${ - bch[1] - } Satoshis`, () => { - assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1]) - }) - }) - - fixtures.conversion.toSatoshi.not.forEach(satoshi => { - it(`converts ${satoshi[0]} to Satoshi, not to ${ - satoshi[1] - } Bitcoin Cash`, () => { - assert.notEqual(bchjs.BitcoinCash.toSatoshi(satoshi[0]), satoshi[1]) - }) - }) - - fixtures.conversion.toSatoshi.rounding.forEach(bch => { - it(`rounding ${bch[0]} to ${bch[1]} Satoshi`, () => { - assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1]) - }) - }) - }) - - describe("#satsToBits", () => { - fixtures.conversion.satsToBits.bch.forEach(bch => { - it(`should convert ${bch[0]} BCH to ${bch[1]} bits`, () => { - assert.equal( - bchjs.BitcoinCash.satsToBits(bchjs.BitcoinCash.toSatoshi(bch[0])), - bch[1] - ) - }) - }) - - fixtures.conversion.satsToBits.strings.forEach(bch => { - it(`should convert "${bch[0]}" BCH as a string to ${ - bch[1] - } bits`, () => { - assert.equal( - bchjs.BitcoinCash.satsToBits(bchjs.BitcoinCash.toSatoshi(bch[0])), - bch[1] - ) - }) - }) - }) - // - // describe('#satsFromBits', () => { - // fixtures.conversion.satsFromBits.bch.forEach((bch) => { - // it(`should convert ${bch[1]} bits to ${bch[0]} satoshis`, () => { - // assert.equal(bchjs.BitcoinCash.satsFromBits(bch[1]), bch[0]); - // }); - // }); - // - // fixtures.conversion.satsFromBits.strings.forEach((bch) => { - // it(`should convert "${bch[1]}" bits as a string to ${bch[0]} satoshis`, () => { - // assert.equal(bchjs.BitcoinCash.satsFromBits(bch[1]), bch[0]); - // }); - // }); - // }); - }) - - describe("sign and verify messages", () => { - describe("#signMessageWithPrivKey", () => { - fixtures.signatures.sign.forEach(sign => { - it(`should sign a message w/ ${sign.network} ${sign.privateKeyWIF}`, () => { - const privateKeyWIF = sign.privateKeyWIF - const message = sign.message - const signature = bchjs.BitcoinCash.signMessageWithPrivKey( - privateKeyWIF, - message - ) - assert.equal(signature, sign.signature) - }) - }) - }) - - describe("#verifyMessage", () => { - fixtures.signatures.verify.forEach(sign => { - it(`should verify a valid signed message from ${sign.network} cashaddr address ${sign.address}`, () => { - assert.equal( - bchjs.BitcoinCash.verifyMessage( - sign.address, - sign.signature, - sign.message - ), - true - ) - }) - }) - - fixtures.signatures.verify.forEach(sign => { - const legacyAddress = bchjs.Address.toLegacyAddress(sign.address) - it(`should verify a valid signed message from ${sign.network} legacy address ${legacyAddress}`, () => { - assert.equal( - bchjs.BitcoinCash.verifyMessage( - legacyAddress, - sign.signature, - sign.message - ), - true - ) - }) - }) - - fixtures.signatures.verify.forEach(sign => { - const legacyAddress = bchjs.Address.toLegacyAddress(sign.address) - it(`should not verify an invalid signed message from ${sign.network} cashaddr address ${sign.address}`, () => { - assert.equal( - bchjs.BitcoinCash.verifyMessage( - sign.address, - sign.signature, - "nope" - ), - false - ) - }) - }) - }) - }) - - describe("encode and decode to base58Check", () => { - describe("#encodeBase58Check", () => { - fixtures.encodeBase58Check.forEach((base58Check, i) => { - it(`encode ${base58Check.hex} as base58Check ${base58Check.base58Check}`, () => { - assert.equal( - bchjs.BitcoinCash.encodeBase58Check(base58Check.hex), - base58Check.base58Check - ) - }) - }) - }) - - describe("#decodeBase58Check", () => { - fixtures.encodeBase58Check.forEach((base58Check, i) => { - it(`decode ${base58Check.base58Check} as ${base58Check.hex}`, () => { - assert.equal( - bchjs.BitcoinCash.decodeBase58Check(base58Check.base58Check), - base58Check.hex - ) - }) - }) - }) - }) - - describe("encode and decode BIP21 urls", () => { - describe("#encodeBIP21", () => { - fixtures.bip21.valid.forEach((bip21, i) => { - it(`encode ${bip21.address} as url`, () => { - const url = bchjs.BitcoinCash.encodeBIP21( - bip21.address, - bip21.options - ) - assert.equal(url, bip21.url) - }) - }) - fixtures.bip21.valid_regtest.forEach((bip21, i) => { - it(`encode ${bip21.address} as url`, () => { - const url = bchjs.BitcoinCash.encodeBIP21( - bip21.address, - bip21.options, - true - ) - assert.equal(url, bip21.url) - }) - }) - }) - - describe("#decodeBIP21", () => { - fixtures.bip21.valid.forEach((bip21, i) => { - it(`decodes ${bip21.url}`, () => { - const decoded = bchjs.BitcoinCash.decodeBIP21(bip21.url) - assert.equal(decoded.options.amount, bip21.options.amount) - assert.equal(decoded.options.label, bip21.options.label) - assert.equal( - bchjs.Address.toCashAddress(decoded.address), - bchjs.Address.toCashAddress(bip21.address) - ) - }) - }) - // fixtures.bip21.valid_regtest.forEach((bip21, i) => { - // it(`decodes ${bip21.url}`, () => { - // const decoded = bchjs.BitcoinCash.decodeBIP21(bip21.url) - // assert.equal(decoded.options.amount, bip21.options.amount) - // assert.equal(decoded.options.label, bip21.options.label) - // assert.equal( - // bchjs.Address.toCashAddress(decoded.address, true, true), - // bchjs.Address.toCashAddress(bip21.address, true, true) - // ) - // }) - // }) - }) - }) - - describe("#getByteCount", () => { - fixtures.getByteCount.forEach(fixture => { - it(`get byte count`, () => { - const byteCount = bchjs.BitcoinCash.getByteCount( - fixture.inputs, - fixture.outputs - ) - assert.equal(byteCount, fixture.byteCount) - }) - }) - }) - - describe("#bip38", () => { - describe("#encryptBIP38", () => { - fixtures.bip38.encrypt.mainnet.forEach(fixture => { - it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on mainnet`, () => { - const encryptedKey = bchjs.BitcoinCash.encryptBIP38( - fixture.wif, - fixture.password - ) - assert.equal(encryptedKey, fixture.encryptedKey) - }) - }) - - fixtures.bip38.encrypt.testnet.forEach(fixture => { - it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on testnet`, () => { - const encryptedKey = bchjs.BitcoinCash.encryptBIP38( - fixture.wif, - fixture.password - ) - assert.equal(encryptedKey, fixture.encryptedKey) - }) - }) - }) - - describe("#decryptBIP38", () => { - fixtures.bip38.decrypt.mainnet.forEach(fixture => { - it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on mainnet`, () => { - const wif = bchjs.BitcoinCash.decryptBIP38( - fixture.encryptedKey, - fixture.password, - "mainnet" - ) - assert.equal(wif, fixture.wif) - }) - }) - - fixtures.bip38.decrypt.testnet.forEach(fixture => { - it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on testnet`, () => { - const wif = bchjs.BitcoinCash.decryptBIP38( - fixture.encryptedKey, - fixture.password, - "testnet" - ) - assert.equal(wif, fixture.wif) - }) - }) - }) - }) -}) diff --git a/test/unit/blockbook.js b/test/unit/blockbook.js deleted file mode 100644 index 9866c81..0000000 --- a/test/unit/blockbook.js +++ /dev/null @@ -1,275 +0,0 @@ -const chai = require("chai") -const assert = chai.assert -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const axios = require("axios") -const sinon = require("sinon") - -const mockData = require("./fixtures/blockbook-mock") - -describe(`#Blockbook`, () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - describe(`#Balance`, () => { - it(`should GET balance for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.balance }) - - const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" - - const result = await bchjs.Blockbook.balance(addr) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "page", - "totalPages", - "itemsOnPage", - "address", - "balance", - "totalReceived", - "totalSent", - "unconfirmedBalance", - "unconfirmedTxs", - "txs", - "txids" - ]) - assert.isArray(result.txids) - }) - - it(`should POST request balances for an array of addresses`, async () => { - // Stub the network call. - sandbox - .stub(axios, "post") - .resolves({ data: [mockData.balance, mockData.balance] }) - - const addr = [ - "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", - "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" - ] - - const result = await bchjs.Blockbook.balance(addr) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "page", - "totalPages", - "itemsOnPage", - "address", - "balance", - "totalReceived", - "totalSent", - "unconfirmedBalance", - "unconfirmedTxs", - "txs", - "txids" - ]) - assert.isArray(result[0].txids) - }) - - it(`should throw an error for improper input`, async () => { - try { - // Stub the network call. - sandbox - .stub(axios, "post") - .throws(`Input address must be a string or array of strings`) - - const addr = 12345 - - await bchjs.Blockbook.balance(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 throw error on array size rate limit`, async () => { - try { - // Stub the network call. - sandbox.stub(axios, "post").throws(`Array too large`) - - const addr = [] - for (let i = 0; i < 25; i++) - addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") - - const result = await bchjs.Blockbook.balance(addr) - - console.log(`result: ${util.inspect(result)}`) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - assert.hasAnyKeys(err, ["error"]) - assert.include(err.error, "Array too large") - } - }) - */ - }) - - describe(`#utxo`, () => { - it(`should GET utxos for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) - - const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" - - const result = await bchjs.Blockbook.utxo(addr) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "txid", - "vout", - "value", - "height", - "confirmations" - ]) - }) - - it(`should POST utxo details for an array of addresses`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.utxos }) - - const addr = [ - "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", - "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" - ] - - const result = await bchjs.Blockbook.utxo(addr) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.isArray(result[0]) - assert.hasAnyKeys(result[0][0], [ - "txid", - "vout", - "value", - "height", - "confirmations" - ]) - }) - - it(`should throw an error for improper input`, async () => { - try { - // Stub the network call. - sandbox - .stub(axios, "post") - .throws(`Input address must be a string or array of strings`) - - const addr = 12345 - - await bchjs.Blockbook.utxo(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 throw error on array size rate limit`, async () => { - try { - const addr = [] - for (let i = 0; i < 25; i++) - addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") - - const result = await bchjs.Blockbook.utxo(addr) - - console.log(`result: ${util.inspect(result)}`) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - assert.hasAnyKeys(err, ["error"]) - assert.include(err.error, "Array too large") - } - }) - */ - }) - - describe(`#tx`, () => { - it(`should GET tx details for a single txid`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) - - const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" - - const result = await bchjs.Blockbook.utxo(addr) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "txid", - "vout", - "value", - "height", - "confirmations" - ]) - }) - - // it(`should POST utxo details for an array of addresses`, async () => { - // // Stub the network call. - // sandbox.stub(axios, "post").resolves({ data: mockData.utxos }) - // - // const addr = [ - // "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", - // "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" - // ] - // - // const result = await bchjs.Blockbook.utxo(addr) - // //console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.isArray(result) - // assert.isArray(result[0]) - // assert.hasAnyKeys(result[0][0], [ - // "txid", - // "vout", - // "value", - // "height", - // "confirmations" - // ]) - // }) - - // it(`should throw an error for improper input`, async () => { - // try { - // // Stub the network call. - // sandbox - // .stub(axios, "post") - // .throws(`Input address must be a string or array of strings`) - // - // const addr = 12345 - // - // await bchjs.Blockbook.utxo(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 throw error on array size rate limit`, async () => { - try { - const addr = [] - for (let i = 0; i < 25; i++) - addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") - - const result = await bchjs.Blockbook.utxo(addr) - - console.log(`result: ${util.inspect(result)}`) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - assert.hasAnyKeys(err, ["error"]) - assert.include(err.error, "Array too large") - } - }) - */ - }) -}) diff --git a/test/unit/blockchain.js b/test/unit/blockchain.js deleted file mode 100644 index e088ffe..0000000 --- a/test/unit/blockchain.js +++ /dev/null @@ -1,494 +0,0 @@ -const assert = require("assert") -const assert2 = require("chai").assert -const axios = require("axios") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const sinon = require("sinon") - -const mockData = require("./fixtures/blockchain-mock") - -describe("#Blockchain", () => { - describe("#getBestBlockHash", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should get best block hash", done => { - const resolved = new Promise(r => - r({ - data: - "0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d" - }) - ) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getBestBlockHash() - .then(result => { - const hash = - "0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d" - assert.equal(hash, result) - }) - .then(done, done) - }) - }) - - describe("#getBlock", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = { - hash: "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", - confirmations: 526807, - size: 216, - height: 1000, - version: 1, - versionHex: "00000001", - merkleroot: - "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33", - tx: ["fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"], - time: 1232346882, - mediantime: 1232344831, - nonce: 2595206198, - bits: "1d00ffff", - difficulty: 1, - chainwork: - "000000000000000000000000000000000000000000000000000003e903e903e9", - previousblockhash: - "0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d", - nextblockhash: - "00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6" - } - - it("should get block by hash", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getBlock( - "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09" - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getBlockchainInfo", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = { - chain: "main", - blocks: 527810, - headers: 527810, - bestblockhash: - "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", - difficulty: 576023394804.6666, - mediantime: 1524878499, - verificationprogress: 0.9999990106793685, - chainwork: - "00000000000000000000000000000000000000000096da5b040913fa09249b4e", - pruned: false, - softforks: [ - { id: "bip34", version: 2, reject: [Object] }, - { id: "bip66", version: 3, reject: [Object] }, - { id: "bip65", version: 4, reject: [Object] } - ], - bip9_softforks: { - csv: { - status: "active", - startTime: 1462060800, - timeout: 1493596800, - since: 419328 - } - } - } - - it("should get blockchain info", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getBlockchainInfo() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getBlockCount", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = 527810 - - it("should get block count", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getBlockCount() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getBlockHash", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = - "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0" - - it("should get block hash by height", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getBlockHash(527810) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getBlockHeader", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = { - hash: "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", - confirmations: 1, - height: 527810, - version: 536870912, - versionHex: "20000000", - merkleroot: - "9298432bbebe4638456aa19cb7ef91639da87668a285d88d0ecd6080424d223b", - time: 1524881438, - mediantime: 1524878499, - nonce: 3326843941, - bits: "1801e8a5", - difficulty: 576023394804.6666, - chainwork: - "00000000000000000000000000000000000000000096da5b040913fa09249b4e", - previousblockhash: - "000000000000000000b33251708bc7a7b4540e61880d8c376e8e2db6a19a4789" - } - - it("should get block header by hash", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getBlockHeader( - "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", - true - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getDifficulty", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = "577528469277.1339" - - it("should get difficulty", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getDifficulty() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getMempoolAncestors", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = "Transaction not in mempool" - - it("should get mempool ancestors", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getMempoolAncestors( - "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", - true - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getMempoolDescendants", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = { - result: "Transaction not in mempool" - } - - it("should get mempool descendants", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getMempoolDescendants( - "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", - true - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getMempoolEntry", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = { - result: "Transaction not in mempool" - } - - it("should get mempool entry", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getMempoolEntry( - "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88" - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getMempoolInfo", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = { - result: { - size: 317, - bytes: 208583, - usage: 554944, - maxmempool: 300000000, - mempoolminfee: 0 - } - } - - it("should get mempool info", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getMempoolInfo() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getRawMempool", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = { - result: { - transactions: [ - { - txid: - "ab36d68dd0a618592fe34e4a898e8beeeb4049133547dbb16f9338384084af96", - size: 191, - fee: 0.00047703, - modifiedfee: 0.00047703, - time: 1524883317, - height: 527811, - startingpriority: 5287822727.272727, - currentpriority: 5287822727.272727, - descendantcount: 1, - descendantsize: 191, - descendantfees: 47703, - ancestorcount: 1, - ancestorsize: 191, - ancestorfees: 47703, - depends: [] - } - ] - } - } - - it("should get mempool info", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.getRawMempool() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getTxOut", () => { - // TODO finish this test - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - // const data = { - // result: {} - // } - - it("should throw an error for improper txid.", async () => { - try { - await bchjs.Blockchain.getTxOut("badtxid") - } catch (err) { - assert2.include(err.message, "txid needs to be a proper transaction ID") - } - }) - - it("should throw an error if no vout value is provided.", async () => { - try { - await bchjs.Blockchain.getTxOut( - "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88" - ) - } catch (err) { - assert2.include(err.message, "n must be an integer") - } - }) - - it("should throw an error if include_mempool is not a boolean", async () => { - try { - await bchjs.Blockchain.getTxOut( - "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", - 0, - "bad value" - ) - } catch (err) { - assert2.include( - err.message, - "include_mempool input must be of type boolean" - ) - } - }) - - it("should get information on an unspent tx", async () => { - sandbox.stub(axios, "post").resolves({ data: mockData.txOutUnspent }) - - const result = await bchjs.Blockchain.getTxOut( - "62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8", - 0, - true - ) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert2.hasAllKeys(result, [ - "bestblock", - "confirmations", - "value", - "scriptPubKey", - "coinbase" - ]) - }) - - it("should get information on a spent tx", async () => { - sandbox.stub(axios, "post").resolves({ data: null }) - - const result = await bchjs.Blockchain.getTxOut( - "87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871", - 0, - true - ) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert2.equal(result, null) - }) - }) - - describe("#preciousBlock", () => { - // TODO finish this test - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = { - result: {} - } - - it("should get TODO", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.preciousBlock() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#pruneBlockchain", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = "Cannot prune blocks because node is not in prune mode." - - it("should prune blockchain", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "post").returns(resolved) - - bchjs.Blockchain.pruneBlockchain(507) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#verifyChain", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = true - - it("should verify blockchain", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.verifyChain(3, 6) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#verifyTxOutProof", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - const data = "proof must be hexadecimal string (not '')" - - it("should verify utxo proof", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Blockchain.verifyTxOutProof("3") - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) -}) diff --git a/test/unit/control.js b/test/unit/control.js deleted file mode 100644 index 2ec8219..0000000 --- a/test/unit/control.js +++ /dev/null @@ -1,64 +0,0 @@ -const assert = require("assert") -const axios = require("axios") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const sinon = require("sinon") - -describe("#Control", () => { - describe("#getNetworkInfo", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should get info", done => { - const data = { - version: 170000, - protocolversion: 70015, - blocks: 527813, - timeoffset: 0, - connections: 21, - proxy: "", - difficulty: 581086703759.5878, - testnet: false, - paytxfee: 0, - relayfee: 0.00001, - errors: "" - } - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Control.getNetworkInfo() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getMemoryInfo", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should get memory info", done => { - const data = { - locked: { - used: 0, - free: 65536, - total: 65536, - locked: 65536, - chunks_used: 0, - chunks_free: 1 - } - } - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Control.getMemoryInfo() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) -}) diff --git a/test/unit/crypto.js b/test/unit/crypto.js deleted file mode 100644 index 73f6d23..0000000 --- a/test/unit/crypto.js +++ /dev/null @@ -1,100 +0,0 @@ -const fixtures = require("./fixtures/crypto.json") -const assert = require("assert") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const Buffer = require("safe-buffer").Buffer - -describe("#Crypto", () => { - describe("#sha256", () => { - fixtures.sha256.forEach(fixture => { - it(`should create SHA256Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { - const data = Buffer.from(fixture.hex, "hex") - const sha256Hash = bchjs.Crypto.sha256(data).toString("hex") - assert.equal(sha256Hash, fixture.hash) - }) - - it(`should create 64 character SHA256Hash hex encoded`, () => { - const data = Buffer.from(fixture.hex, "hex") - const sha256Hash = bchjs.Crypto.sha256(data).toString("hex") - assert.equal(sha256Hash.length, 64) - }) - }) - }) - - describe("#ripemd160", () => { - fixtures.ripemd160.forEach(fixture => { - it(`should create RIPEMD160Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { - const data = Buffer.from(fixture.hex, "hex") - const ripemd160 = bchjs.Crypto.ripemd160(data).toString("hex") - assert.equal(ripemd160, fixture.hash) - }) - - it(`should create 64 character RIPEMD160Hash hex encoded`, () => { - const data = Buffer.from(fixture.hex, "hex") - const ripemd160 = bchjs.Crypto.ripemd160(data).toString("hex") - assert.equal(ripemd160.length, 40) - }) - }) - }) - - describe("#hash256", () => { - fixtures.hash256.forEach(fixture => { - it(`should create double SHA256 Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { - const data = Buffer.from(fixture.hex, "hex") - const hash256 = bchjs.Crypto.hash256(data).toString("hex") - assert.equal(hash256, fixture.hash) - }) - - it(`should create 64 character SHA256 Hash hex encoded`, () => { - const data = Buffer.from(fixture.hex, "hex") - const hash256 = bchjs.Crypto.hash256(data).toString("hex") - assert.equal(hash256.length, 64) - }) - }) - }) - - describe("#hash160", () => { - fixtures.hash160.forEach(fixture => { - it(`should create RIPEMD160(SHA256()) hex encoded ${fixture.hash} from ${fixture.hex}`, () => { - const data = Buffer.from(fixture.hex, "hex") - const hash160 = bchjs.Crypto.hash160(data).toString("hex") - assert.equal(hash160, fixture.hash) - }) - - it(`should create 64 character SHA256Hash hex encoded`, () => { - const data = Buffer.from(fixture.hex, "hex") - const hash160 = bchjs.Crypto.hash160(data).toString("hex") - assert.equal(hash160.length, 40) - }) - }) - }) - - describe("#randomBytes", () => { - for (let i = 0; i < 6; i++) { - it("should return 16 bytes of entropy hex encoded", () => { - const entropy = bchjs.Crypto.randomBytes(16) - assert.equal(Buffer.byteLength(entropy), 16) - }) - - it("should return 20 bytes of entropy hex encoded", () => { - const entropy = bchjs.Crypto.randomBytes(20) - assert.equal(Buffer.byteLength(entropy), 20) - }) - - it("should return 24 bytes of entropy hex encoded", () => { - const entropy = bchjs.Crypto.randomBytes(24) - assert.equal(Buffer.byteLength(entropy), 24) - }) - - it("should return 28 bytes of entropy hex encoded", () => { - const entropy = bchjs.Crypto.randomBytes(28) - assert.equal(Buffer.byteLength(entropy), 28) - }) - - it("should return 32 bytes of entropy hex encoded", () => { - const entropy = bchjs.Crypto.randomBytes(32) - assert.equal(Buffer.byteLength(entropy), 32) - }) - } - }) -}) diff --git a/test/unit/ecpairs.js b/test/unit/ecpairs.js deleted file mode 100644 index d3dbb6f..0000000 --- a/test/unit/ecpairs.js +++ /dev/null @@ -1,146 +0,0 @@ -const assert = require("assert") - -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() - -const Buffer = require("safe-buffer").Buffer - -const fixtures = require("./fixtures/ecpair.json") - -describe("#ECPair", () => { - describe("#fromWIF", () => { - fixtures.fromWIF.forEach(fixture => { - it(`should create ECPair from WIF ${fixture.privateKeyWIF}`, () => { - const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - assert.equal(typeof ecpair, "object") - }) - - it(`should get ${fixture.legacy} legacy address`, () => { - const legacy = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - assert.equal(bchjs.HDNode.toLegacyAddress(legacy), fixture.legacy) - }) - - it(`should get ${fixture.cashAddr} cash address`, () => { - const cashAddr = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - assert.equal(bchjs.HDNode.toCashAddress(cashAddr), fixture.cashAddr) - }) - - it(`should get ${fixture.regtestAddr} cash address`, () => { - const cashAddr = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - assert.equal( - bchjs.HDNode.toCashAddress(cashAddr, true), - fixture.regtestAddr - ) - }) - }) - }) - - describe("#toWIF", () => { - fixtures.toWIF.forEach(fixture => { - it(`should get WIF ${fixture.privateKeyWIF} from ECPair`, () => { - const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - const wif = bchjs.ECPair.toWIF(ecpair) - assert.equal(wif, fixture.privateKeyWIF) - }) - }) - }) - - describe("#fromPublicKey", () => { - fixtures.fromPublicKey.forEach(fixture => { - it(`should create ECPair from public key buffer`, () => { - const ecpair = bchjs.ECPair.fromPublicKey( - Buffer.from(fixture.pubkeyHex, "hex") - ) - assert.equal(typeof ecpair, "object") - }) - - it(`should get ${fixture.legacy} legacy address`, () => { - const ecpair = bchjs.ECPair.fromPublicKey( - Buffer.from(fixture.pubkeyHex, "hex") - ) - assert.equal(bchjs.HDNode.toLegacyAddress(ecpair), fixture.legacy) - }) - - it(`should get ${fixture.cashAddr} cash address`, () => { - const ecpair = bchjs.ECPair.fromPublicKey( - Buffer.from(fixture.pubkeyHex, "hex") - ) - assert.equal(bchjs.HDNode.toCashAddress(ecpair), fixture.cashAddr) - }) - - it(`should get ${fixture.regtestAddr} cash address`, () => { - const ecpair = bchjs.ECPair.fromPublicKey( - Buffer.from(fixture.pubkeyHex, "hex") - ) - assert.equal( - bchjs.HDNode.toCashAddress(ecpair, true), - fixture.regtestAddr - ) - }) - }) - }) - - describe("#toPublicKey", () => { - fixtures.toPublicKey.forEach(fixture => { - it(`should create a public key buffer from an ECPair`, () => { - const ecpair = bchjs.ECPair.fromPublicKey( - Buffer.from(fixture.pubkeyHex, "hex") - ) - const pubkeyBuffer = bchjs.ECPair.toPublicKey(ecpair) - assert.equal(typeof pubkeyBuffer, "object") - }) - }) - }) - - describe("#toLegacyAddress", () => { - fixtures.toLegacyAddress.forEach(fixture => { - it(`should create legacy address ${fixture.legacy} from an ECPair`, () => { - const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - const legacyAddress = bchjs.ECPair.toLegacyAddress(ecpair) - assert.equal(legacyAddress, fixture.legacy) - }) - }) - }) - - describe("#toCashAddress", () => { - fixtures.toCashAddress.forEach(fixture => { - it(`should create cash address ${fixture.cashAddr} from an ECPair`, () => { - const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - const cashAddr = bchjs.ECPair.toCashAddress(ecpair) - assert.equal(cashAddr, fixture.cashAddr) - }) - }) - - fixtures.toCashAddress.forEach(fixture => { - it(`should create regtest cash address ${fixture.regtestAddr} from an ECPair`, () => { - const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - const regtestAddr = bchjs.ECPair.toCashAddress(ecpair, true) - assert.equal(regtestAddr, fixture.regtestAddr) - }) - }) - }) - - describe("#sign", () => { - fixtures.sign.forEach(fixture => { - it(`should sign 32 byte hash buffer`, () => { - const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) - const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") - const signatureBuf = bchjs.ECPair.sign(ecpair, buf) - assert.equal(typeof signatureBuf, "object") - }) - }) - }) - - describe("#verify", () => { - fixtures.verify.forEach(fixture => { - it(`should verify signed 32 byte hash buffer`, () => { - const ecpair1 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF1) - //const ecpair2 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF2) - const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") - const signature = bchjs.ECPair.sign(ecpair1, buf) - const verify = bchjs.ECPair.verify(ecpair1, buf, signature) - assert.equal(verify, true) - }) - }) - }) -}) diff --git a/test/unit/electrumx.js b/test/unit/electrumx.js deleted file mode 100644 index 479d77b..0000000 --- a/test/unit/electrumx.js +++ /dev/null @@ -1,421 +0,0 @@ -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/electrumx-mock") - -describe(`#ElectrumX`, () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - describe(`#utxo`, () => { - it(`should throw an error for improper input`, async () => { - try { - const addr = 12345 - - await bchjs.Electrumx.utxo(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 GET utxos for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) - - const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" - - const result = await bchjs.Electrumx.utxo(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "utxos") - assert.isArray(result.utxos) - - assert.property(result.utxos[0], "height") - assert.property(result.utxos[0], "tx_hash") - assert.property(result.utxos[0], "tx_pos") - assert.property(result.utxos[0], "value") - }) - - it(`should POST utxo details for an array of addresses`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.utxos }) - - const addr = [ - "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", - "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" - ] - - const result = await bchjs.Electrumx.utxo(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "utxos") - assert.isArray(result.utxos) - - assert.property(result.utxos[0], "utxos") - assert.isArray(result.utxos[0].utxos) - assert.property(result.utxos[0], "address") - - assert.property(result.utxos[0].utxos[0], "height") - assert.property(result.utxos[0].utxos[0], "tx_hash") - assert.property(result.utxos[0].utxos[0], "tx_pos") - assert.property(result.utxos[0].utxos[0], "value") - }) - }) - - describe(`#balance`, () => { - it(`should throw an error for improper input`, async () => { - try { - const addr = 12345 - - await bchjs.Electrumx.balance(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 GET balance for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.balance }) - - const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" - - const result = await bchjs.Electrumx.balance(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "balance") - assert.property(result.balance, "confirmed") - assert.property(result.balance, "unconfirmed") - }) - - it(`should POST balance for an array of addresses`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.balances }) - - const addr = [ - "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", - "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" - ] - - const result = await bchjs.Electrumx.balance(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "balances") - assert.isArray(result.balances) - - assert.property(result.balances[0], "address") - assert.property(result.balances[0], "balance") - assert.property(result.balances[0].balance, "confirmed") - assert.property(result.balances[0].balance, "unconfirmed") - }) - }) - - describe(`#transactions`, () => { - it(`should throw an error for improper input`, async () => { - try { - const addr = 12345 - - await bchjs.Electrumx.transactions(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 GET transactions for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.transaction }) - - const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" - - const result = await bchjs.Electrumx.transactions(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "transactions") - assert.isArray(result.transactions) - assert.property(result.transactions[0], "height") - assert.property(result.transactions[0], "tx_hash") - }) - - it(`should POST transaction history for an array of addresses`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.transactions }) - - const addr = [ - "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", - "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" - ] - - const result = await bchjs.Electrumx.transactions(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "transactions") - assert.isArray(result.transactions) - - assert.property(result.transactions[0], "address") - assert.property(result.transactions[0], "transactions") - assert.isArray(result.transactions[0].transactions) - assert.property(result.transactions[0].transactions[0], "height") - assert.property(result.transactions[0].transactions[0], "tx_hash") - }) - }) - - describe(`#unconfirmed`, () => { - it(`should throw an error for improper input`, async () => { - try { - const addr = 12345 - - await bchjs.Electrumx.unconfirmed(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 GET unconfirmed utxos for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.unconfirmed }) - - const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" - - const result = await bchjs.Electrumx.unconfirmed(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "utxos") - assert.isArray(result.utxos) - - assert.property(result.utxos[0], "height") - assert.property(result.utxos[0], "tx_hash") - assert.property(result.utxos[0], "fee") - }) - - it(`should POST unconfirmed utxo details for an array of addresses`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmedArray }) - - const addr = [ - "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", - "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" - ] - - const result = await bchjs.Electrumx.unconfirmed(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "utxos") - assert.isArray(result.utxos) - - assert.property(result.utxos[0], "utxos") - assert.isArray(result.utxos[0].utxos) - assert.property(result.utxos[0], "address") - - assert.property(result.utxos[0].utxos[0], "height") - assert.property(result.utxos[0].utxos[0], "tx_hash") - assert.property(result.utxos[0].utxos[0], "fee") - }) - }) - - describe(`#blockHeader`, () => { - it(`should throw an error for improper height input`, async () => { - try { - // Mock network calls. - sandbox.stub(axios, "get").rejects({ - response: { - data: { - success: false, - error: "height must be a positive number" - } - } - }) - - const height = -10 - await bchjs.Electrumx.blockHeader(height) - - assert.equal(true, false, "Unexpected result!") - } catch (err) { - // console.log(`err: `, err) - assert.include(err.message, `height must be a positive number`) - } - }) - - it(`should throw an error for improper count input`, async () => { - try { - // Mock network calls. - sandbox.stub(axios, "get").rejects({ - response: { - data: { - success: false, - error: "count must be a positive number" - } - } - }) - - const height = 42 - const count = -10 - - await bchjs.Electrumx.blockHeader(height, count) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - // console.log(`err: `, err) - assert.include(err.message, `count must be a positive number`) - } - }) - - it(`should GET block headers for a given height`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.blockHeaders }) - - const height = 42 - - const result = await bchjs.Electrumx.blockHeader(height) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert.isArray(result) - assert.equal(result.length, 2) - }) - }) - - describe(`#txData`, () => { - it(`should throw an error for improper input`, async () => { - try { - const txid = 12345 - - await bchjs.Electrumx.txData(txid) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - // console.log(`err: `, err) - assert.include( - err.message, - `Input txId must be a string or array of strings` - ) - } - }) - - it(`should GET details data for a single transaction`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.details }) - - const txid = - "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" - - const result = await bchjs.Electrumx.txData(txid) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "details") - assert.isObject(result.details) - - assert.property(result.details, "blockhash") - assert.property(result.details, "hash") - assert.property(result.details, "hex") - assert.property(result.details, "vin") - assert.property(result.details, "vout") - assert.equal(result.details.hash, txid) - }) - - it(`should POST details for an array of transactions`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.detailsArray }) - - const txids = [ - "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251", - "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" - ] - - const result = await bchjs.Electrumx.txData(txids) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "transactions") - assert.isArray(result.transactions) - - assert.property(result.transactions[0], "txid") - assert.property(result.transactions[0], "details") - - assert.property(result.transactions[0].details, "blockhash") - assert.property(result.transactions[0].details, "hash") - assert.property(result.transactions[0].details, "hex") - assert.property(result.transactions[0].details, "vin") - assert.property(result.transactions[0].details, "vout") - - assert.equal(result.transactions.length, 2, "2 outputs for 2 inputs") - }) - }) - describe(`#broadcast`, () => { - it(`should throw an error for improper input`, async () => { - try { - const txHex = 12345 - - await bchjs.Electrumx.broadcast(txHex) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - // console.log(`err: `, err) - assert.include(err.message, `Input txHex must be a string.`) - } - }) - it(`should broadcast a single transaction`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.broadcast }) - - const tx = mockData.details - const txid = tx.details.txid - const result = await bchjs.Electrumx.broadcast(tx.details.hex) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "txid") - assert.equal(result.txid, txid) - }) - }) -}) diff --git a/test/unit/encryption.js b/test/unit/encryption.js deleted file mode 100644 index 9242348..0000000 --- a/test/unit/encryption.js +++ /dev/null @@ -1,68 +0,0 @@ -const assert = require("chai").assert -const sinon = require("sinon") - -const BCHJS = require("../../src/bch-js") -// const bchjs = new BCHJS() -let bchjs - -const mockData = require("./fixtures/encryption-mock") - -describe("#Encryption", () => { - let sandbox - - beforeEach(() => { - bchjs = new BCHJS() - sandbox = sinon.createSandbox() - }) - - afterEach(() => sandbox.restore()) - - describe("#getPubKey", () => { - it("should throw error if BCH address is not provided.", async () => { - try { - await bchjs.encryption.getPubKey() - - assert.equal(true, false, "Unexpected result!") - } catch (err) { - // console.log(`err: `, err) - assert.include( - err.message, - "Input must be a valid Bitcoin Cash address" - ) - } - }) - - it("should report when public key can not be found", async () => { - // Stub the network call. - sandbox - .stub(bchjs.encryption.axios, "get") - .resolves({ data: mockData.failureMock }) - - const addr = "bitcoincash:qpxqr2pmcverj4vukgjqssvk2zju8tp9xsgz2nqagx" - - const result = await bchjs.encryption.getPubKey(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, false) - assert.property(result, "publicKey") - assert.equal(result.publicKey, "not found") - }) - - it("should get a public key", async () => { - // Stub the network call. - sandbox - .stub(bchjs.encryption.axios, "get") - .resolves({ data: mockData.successMock }) - - const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq" - - const result = await bchjs.encryption.getPubKey(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - assert.property(result, "publicKey") - }) - }) -}) diff --git a/test/unit/generating.js b/test/unit/generating.js deleted file mode 100644 index 8937467..0000000 --- a/test/unit/generating.js +++ /dev/null @@ -1,28 +0,0 @@ -const assert = require("assert") -const axios = require("axios") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const sinon = require("sinon") - -describe("#Generating", () => { - describe("#generateToAddress", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should generate", done => { - const data = [] - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "post").returns(resolved) - - bchjs.Generating.generateToAddress( - 1, - "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) -}) diff --git a/test/unit/hdnode.js b/test/unit/hdnode.js deleted file mode 100644 index 44c105f..0000000 --- a/test/unit/hdnode.js +++ /dev/null @@ -1,345 +0,0 @@ -const fixtures = require("./fixtures/hdnode.json") -const slpFixtures = require("./fixtures/slp/address.json") -const assert = require("assert") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const Buffer = require("safe-buffer").Buffer - -describe("#HDNode", () => { - describe("#fromSeed", () => { - fixtures.fromSeed.forEach(mnemonic => { - it(`should create an HDNode from root seed buffer`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - assert.notEqual(hdNode, null) - }) - }) - }) - - describe("#derive", () => { - fixtures.derive.forEach(derive => { - it(`should derive non hardened child HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const childHDNode = bchjs.HDNode.derive(hdNode, 0) - assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) - assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) - }) - }) - }) - - describe("#deriveHardened", () => { - fixtures.deriveHardened.forEach(derive => { - it(`should derive hardened child HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const childHDNode = bchjs.HDNode.deriveHardened(hdNode, 0) - assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) - assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) - }) - }) - - describe("derive BIP44 $BCH account", () => { - fixtures.deriveBIP44.forEach(derive => { - it(`should derive BIP44 $BCH account`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const purpose = bchjs.HDNode.deriveHardened(hdNode, 44) - const coin = bchjs.HDNode.deriveHardened(purpose, 145) - const childHDNode = bchjs.HDNode.deriveHardened(coin, 0) - assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) - assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) - }) - }) - }) - }) - - describe("#derivePath", () => { - describe("derive non hardened Path", () => { - fixtures.derivePath.forEach(derive => { - it(`should derive non hardened child HDNode from path`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const childHDNode = bchjs.HDNode.derivePath(hdNode, "0") - assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) - assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) - }) - }) - }) - - describe("derive hardened Path", () => { - fixtures.deriveHardenedPath.forEach(derive => { - it(`should derive hardened child HDNode from path`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const childHDNode = bchjs.HDNode.derivePath(hdNode, "0'") - assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) - assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) - }) - }) - }) - - describe("derive BIP44 $BCH account", () => { - fixtures.deriveBIP44.forEach(derive => { - it(`should derive BIP44 $BCH account`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const childHDNode = bchjs.HDNode.derivePath(hdNode, "44'/145'/0'") - assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) - assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) - }) - }) - }) - }) - - describe("#toLegacyAddress", () => { - fixtures.toLegacyAddress.forEach(fixture => { - it(`should get address ${fixture.address} from HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const childHDNode = bchjs.HDNode.derivePath(hdNode, "0") - const addy = bchjs.HDNode.toLegacyAddress(childHDNode) - assert.equal(addy, fixture.address) - }) - }) - }) - - describe("#toCashAddress", () => { - fixtures.toCashAddress.forEach(fixture => { - it(`should get address ${fixture.address} from HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const childHDNode = bchjs.HDNode.derivePath(hdNode, "0") - const addy = bchjs.HDNode.toCashAddress(childHDNode) - assert.equal(addy, fixture.address) - }) - - it(`should get address ${fixture.regtestAddress} from HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const childHDNode = bchjs.HDNode.derivePath(hdNode, "0") - const addr = bchjs.HDNode.toCashAddress(childHDNode, true) - assert.equal(addr, fixture.regtestAddress) - }) - }) - }) - - describe("#toWIF", () => { - fixtures.toWIF.forEach(fixture => { - it(`should get privateKeyWIF ${fixture.privateKeyWIF} from HDNode`, () => { - const hdNode = bchjs.HDNode.fromXPriv(fixture.xpriv) - assert.equal(bchjs.HDNode.toWIF(hdNode), fixture.privateKeyWIF) - }) - }) - }) - - describe("#toXPub", () => { - fixtures.toXPub.forEach(fixture => { - it(`should create xpub ${fixture.xpub} from an HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const xpub = bchjs.HDNode.toXPub(hdNode) - assert.equal(xpub, fixture.xpub) - }) - }) - }) - - describe("#toXPriv", () => { - fixtures.toXPriv.forEach(fixture => { - it(`should create xpriv ${fixture.xpriv} from an HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const xpriv = bchjs.HDNode.toXPriv(hdNode) - assert.equal(xpriv, fixture.xpriv) - }) - }) - }) - - describe("#toKeyPair", () => { - fixtures.toKeyPair.forEach(fixture => { - it(`should get ECPair from an HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const keyPair = bchjs.HDNode.toKeyPair(hdNode) - assert.equal(typeof keyPair, "object") - }) - }) - }) - - describe("#toPublicKey", () => { - fixtures.toPublicKey.forEach(fixture => { - it(`should create public key buffer from an HDNode`, async () => { - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) - const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) - const publicKeyBuffer = bchjs.HDNode.toPublicKey(hdNode) - assert.equal(typeof publicKeyBuffer, "object") - }) - }) - }) - - describe("#fromXPriv", () => { - fixtures.fromXPriv.forEach(fixture => { - const hdNode = bchjs.HDNode.fromXPriv(fixture.xpriv) - it(`should create HDNode from xpriv ${fixture.xpriv}`, () => { - assert.notEqual(hdNode, null) - }) - - it(`should export xpriv ${fixture.xpriv}`, () => { - assert.equal(bchjs.HDNode.toXPriv(hdNode), fixture.xpriv) - }) - - it(`should export xpub ${fixture.xpub}`, () => { - assert.equal(bchjs.HDNode.toXPub(hdNode), fixture.xpub) - }) - - it(`should export legacy address ${fixture.legacy}`, () => { - assert.equal(bchjs.HDNode.toLegacyAddress(hdNode), fixture.legacy) - }) - - it(`should export cashaddress ${fixture.cashaddress}`, () => { - assert.equal(bchjs.HDNode.toCashAddress(hdNode), fixture.cashaddress) - }) - - it(`should export regtest cashaddress ${fixture.regtestaddress}`, () => { - assert.equal( - bchjs.HDNode.toCashAddress(hdNode, true), - fixture.regtestaddress - ) - }) - - it(`should export privateKeyWIF ${fixture.privateKeyWIF}`, () => { - assert.equal(bchjs.HDNode.toWIF(hdNode), fixture.privateKeyWIF) - }) - }) - }) - - describe("#fromXPub", () => { - fixtures.fromXPub.forEach(fixture => { - const hdNode = bchjs.HDNode.fromXPub(fixture.xpub) - it(`should create HDNode from xpub ${fixture.xpub}`, () => { - assert.notEqual(hdNode, null) - }) - - it(`should export xpub ${fixture.xpub}`, () => { - assert.equal(bchjs.HDNode.toXPub(hdNode), fixture.xpub) - }) - - it(`should export legacy address ${fixture.legacy}`, () => { - assert.equal(bchjs.HDNode.toLegacyAddress(hdNode), fixture.legacy) - }) - - it(`should export cashaddress ${fixture.cashaddress}`, () => { - assert.equal(bchjs.HDNode.toCashAddress(hdNode), fixture.cashaddress) - }) - - it(`should export regtest cashaddress ${fixture.regtestaddress}`, () => { - assert.equal( - bchjs.HDNode.toCashAddress(hdNode, true), - fixture.regtestaddress - ) - }) - }) - }) - - describe("#bip32", () => { - describe("create accounts and addresses", () => { - fixtures.accounts.forEach(async fixture => { - const seedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) - console.log(`seedBuffer: ${seedBuffer.toString()}`) - const hdNode = bchjs.HDNode.fromSeed(seedBuffer) - const a = bchjs.HDNode.derivePath(hdNode, "0'") - const external = bchjs.HDNode.derivePath(a, "0") - const account = bchjs.HDNode.createAccount([external]) - - it(`#createAccount`, () => { - assert.notEqual(account, null) - }) - - describe("#getChainAddress", () => { - const external1 = bchjs.Address.toCashAddress( - account.getChainAddress(0) - ) - it(`should create external change address ${external1}`, () => { - assert.equal(external1, fixture.externals[0]) - }) - }) - - describe("#nextChainAddress", () => { - for (let i = 0; i < 4; i++) { - const ex = bchjs.Address.toCashAddress(account.nextChainAddress(0)) - it(`should create external change address ${ex}`, () => { - assert.equal(ex, fixture.externals[i + 1]) - }) - } - }) - }) - }) - }) - - describe("#sign", () => { - fixtures.sign.forEach(fixture => { - it(`should sign 32 byte hash buffer`, () => { - const hdnode = bchjs.HDNode.fromXPriv(fixture.privateKeyWIF) - const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") - const signatureBuf = bchjs.HDNode.sign(hdnode, buf) - assert.equal(typeof signatureBuf, "object") - }) - }) - }) - - describe("#verify", () => { - fixtures.verify.forEach(fixture => { - it(`should verify signed 32 byte hash buffer`, () => { - const hdnode1 = bchjs.HDNode.fromXPriv(fixture.privateKeyWIF1) - const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") - const signature = bchjs.HDNode.sign(hdnode1, buf) - const verify = bchjs.HDNode.verify(hdnode1, buf, signature) - assert.equal(verify, true) - }) - }) - }) - - describe("#isPublic", () => { - fixtures.isPublic.forEach(fixture => { - it(`should verify hdnode is public`, () => { - const node = bchjs.HDNode.fromXPub(fixture.xpub) - assert.equal(bchjs.HDNode.isPublic(node), true) - }) - }) - - fixtures.isPublic.forEach(fixture => { - it(`should verify hdnode is not public`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - assert.equal(bchjs.HDNode.isPublic(node), false) - }) - }) - }) - - describe("#isPrivate", () => { - fixtures.isPrivate.forEach(fixture => { - it(`should verify hdnode is not private`, () => { - const node = bchjs.HDNode.fromXPub(fixture.xpub) - assert.equal(bchjs.HDNode.isPrivate(node), false) - }) - }) - - fixtures.isPrivate.forEach(fixture => { - it(`should verify hdnode is private`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - assert.equal(bchjs.HDNode.isPrivate(node), true) - }) - }) - }) - - describe("#toIdentifier", () => { - fixtures.toIdentifier.forEach(fixture => { - it(`should get identifier of hdnode`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const publicKeyBuffer = bchjs.HDNode.toPublicKey(node) - const hash160 = bchjs.Crypto.hash160(publicKeyBuffer) - const identifier = bchjs.HDNode.toIdentifier(node) - assert.equal(identifier.toString("hex"), hash160.toString("hex")) - }) - }) - }) -}) diff --git a/test/unit/ipfs.js b/test/unit/ipfs.js deleted file mode 100644 index 3c6ea3d..0000000 --- a/test/unit/ipfs.js +++ /dev/null @@ -1,443 +0,0 @@ -/* - Unit tests for the IPFS Class. -*/ - -const assert = require("chai").assert -const sinon = require("sinon") -const BCHJS = require("../../src/bch-js") -let bchjs - -const mockData = require("./fixtures/ipfs-mock") - -describe(`#IPFS`, () => { - let sandbox - - beforeEach(() => { - sandbox = sinon.createSandbox() - - bchjs = new BCHJS() - }) - - afterEach(() => sandbox.restore()) - - describe("#initUppy", () => { - it("should initialize uppy", () => { - bchjs.IPFS.initUppy() - }) - }) - - describe("#createFileModelServer", () => { - it("should throw an error if file does not exist", async () => { - try { - const path = "/non-existant-file" - - await bchjs.IPFS.createFileModelServer(path) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include(err.message, `Could not find this file`) - } - }) - - it("should create a new file model", async () => { - const path = `${__dirname}/ipfs.js` - - sandbox - .stub(bchjs.IPFS.axios, "post") - .resolves({ data: mockData.mockNewFileModel }) - - const result = await bchjs.IPFS.createFileModelServer(path) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "hostingCostBCH") - assert.property(result, "hostingCostUSD") - assert.property(result, "file") - - assert.property(result.file, "payloadLink") - assert.property(result.file, "hasBeenPaid") - assert.property(result.file, "_id") - assert.property(result.file, "schemaVersion") - assert.property(result.file, "size") - assert.property(result.file, "fileName") - assert.property(result.file, "fileExtension") - assert.property(result.file, "createdTimestamp") - assert.property(result.file, "hostingCost") - assert.property(result.file, "walletIndex") - assert.property(result.file, "bchAddr") - }) - }) - - describe("#uploadFileServer", () => { - it("should throw an error if file does not exist", async () => { - try { - const path = "/non-existant-file" - - await bchjs.IPFS.uploadFileServer(path) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include(err.message, `Could not find this file`) - } - }) - - it("should throw an error if modelId is not included", async () => { - try { - const path = `${__dirname}/ipfs.js` - - await bchjs.IPFS.uploadFileServer(path) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include(err.message, `Must include a file model ID`) - } - }) - - it("Should throw error if the file was not uploaded", async () => { - try { - const mock = { - successful: [], - failed: [ - { - id: "file id" - } - ] - } - sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mock) - - const path = `${__dirname}/ipfs.js` - await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52") - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(err) - assert.include(err.message, `The file could not be uploaded`) - } - }) - - it("should return file object if the file is uploaded", async () => { - try { - sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mockData.uploadData) - - const path = `${__dirname}/ipfs.js` - const result = await bchjs.IPFS.uploadFileServer( - path, - "5ec562319bfacc745e8d8a52" - ) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "schemaVersion") - assert.property(result, "size") - assert.property(result, "fileId") - assert.property(result, "fileName") - assert.property(result, "fileExtension") - } catch (err) { - //console.log(err) - assert.equal(true, false, "Unexpected result") - } - }) - }) - - describe("#getStatus", () => { - it("should throw an error if modelId is not included", async () => { - try { - await bchjs.IPFS.getStatus() - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include(err.message, `Must include a file model ID`) - } - }) - - it("should get data on an unpaid file", async () => { - const modelId = "5ec7392c2acfe57aa62e945a" - - sandbox - .stub(bchjs.IPFS.axios, "get") - .resolves({ data: mockData.unpaidFileData }) - - const result = await bchjs.IPFS.getStatus(modelId) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "hasBeenPaid") - assert.property(result, "satCost") - assert.property(result, "bchAddr") - assert.property(result, "ipfsHash") - assert.property(result, "fileId") - assert.property(result, "fileName") - }) - - it("should get data on an unpaid file", async () => { - const modelId = "5ec7392c2acfe57aa62e945a" - - sandbox - .stub(bchjs.IPFS.axios, "get") - .resolves({ data: mockData.paidFileData }) - - const result = await bchjs.IPFS.getStatus(modelId) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "hasBeenPaid") - assert.property(result, "satCost") - assert.property(result, "bchAddr") - assert.property(result, "ipfsHash") - assert.property(result, "fileId") - assert.property(result, "fileName") - }) - }) - - describe("#createFileModelWeb", () => { - it("should throw an error if file is undefined", async () => { - try { - let file - - await bchjs.IPFS.createFileModelWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include(err.message, `File is required`) - } - }) - it("should throw an error if file is empty", async () => { - try { - const file = {} - - await bchjs.IPFS.createFileModelWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include( - err.message, - `File should have the property 'name' of string type` - ) - } - }) - it("should throw an error if 'name' property is not included", async () => { - try { - const file = { - size: 5000 - } - - await bchjs.IPFS.createFileModelWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include( - err.message, - `File should have the property 'name' of string type` - ) - } - }) - - it("should throw an error if 'size' property is not included", async () => { - try { - const file = { - name: "ipfs.js" - } - - await bchjs.IPFS.createFileModelWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include( - err.message, - `File should have the property 'size' of number type` - ) - } - }) - - it("should create a new file model", async () => { - const file = { - name: "ipfs.js", - size: 5000, - type: "text/plain" - } - sandbox - .stub(bchjs.IPFS.axios, "post") - .resolves({ data: mockData.mockNewFileModel }) - - const result = await bchjs.IPFS.createFileModelWeb(file) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "success") - assert.equal(result.success, true) - - assert.property(result, "hostingCostBCH") - assert.property(result, "hostingCostUSD") - assert.property(result, "file") - - assert.property(result.file, "payloadLink") - assert.property(result.file, "hasBeenPaid") - assert.property(result.file, "_id") - assert.property(result.file, "schemaVersion") - assert.property(result.file, "size") - assert.property(result.file, "fileName") - assert.property(result.file, "fileExtension") - assert.property(result.file, "createdTimestamp") - assert.property(result.file, "hostingCost") - assert.property(result.file, "walletIndex") - assert.property(result.file, "bchAddr") - }) - }) - - describe("#uploadFileWeb", () => { - it("should throw an error if file is undefined", async () => { - try { - let file - - await bchjs.IPFS.uploadFileWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include(err.message, `File is required`) - } - }) - it("should throw an error if file is empty", async () => { - try { - const file = {} - - await bchjs.IPFS.uploadFileWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include( - err.message, - `File should have the property 'name' of string type` - ) - } - }) - it("should throw an error if 'name' property is not included", async () => { - try { - const file = { - size: 5000, - type: "text/plain" - } - - await bchjs.IPFS.uploadFileWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include( - err.message, - `File should have the property 'name' of string type` - ) - } - }) - it("should throw an error if 'size' property is not included", async () => { - try { - const file = { - name: "ipfs.js", - type: "text/plain" - } - - await bchjs.IPFS.uploadFileWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include( - err.message, - `File should have the property 'size' of number type` - ) - } - }) - it("should throw an error if 'type' property is not included", async () => { - try { - const file = { - name: "ipfs.js", - size: 5000 - } - - await bchjs.IPFS.uploadFileWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include( - err.message, - `File should have the property 'type' of string type` - ) - } - }) - it("should throw an error if modelId is not included", async () => { - try { - const file = { - name: "ipfs.js", - size: 5000, - type: "text/plain" - } - await bchjs.IPFS.uploadFileWeb(file) - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(`err.message: ${err.message}`) - assert.include(err.message, `Must include a file model ID`) - } - }) - - it("Should throw error if the file was not uploaded", async () => { - try { - const mock = { - successful: [], - failed: [ - { - id: "file id" - } - ] - } - sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mock) - - const file = { - name: "ipfs.js", - size: 5000, - type: "text/plain" - } - await bchjs.IPFS.uploadFileWeb(file, "5ec562319bfacc745e8d8a52") - - assert.equal(true, false, "Unexpected result") - } catch (err) { - //console.log(err) - assert.include(err.message, `The file could not be uploaded`) - } - }) - - it("should return file object if the file is uploaded", async () => { - try { - sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mockData.uploadData) - - const file = { - name: "ipfs.js", - size: 5000, - type: "text/plain" - } - const result = await bchjs.IPFS.uploadFileWeb( - file, - "5ec562319bfacc745e8d8a52" - ) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "schemaVersion") - assert.property(result, "size") - assert.property(result, "fileId") - assert.property(result, "fileName") - assert.property(result, "fileExtension") - } catch (err) { - //console.log(err) - assert.equal(true, false, "Unexpected result") - } - }) - }) -}) diff --git a/test/unit/mining.js b/test/unit/mining.js deleted file mode 100644 index 1586397..0000000 --- a/test/unit/mining.js +++ /dev/null @@ -1,104 +0,0 @@ -const assert = require("assert") -const axios = require("axios") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const sinon = require("sinon") - -describe("#Mining", () => { - describe("#getBlockTemplate", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should get block template", done => { - const data = { - data: - "01000000017f6305e3b0b05f5b57a82f4e6d4187e148bbe56a947208390e488bad36472368000000006a47304402203b0079ff5b896187feb02e2679c87ac2fb8d483b60e0721ed33601e2c0eecc700220590f8a0e1a51b53b368294861fd5fc99db3a6607d0f4e543f6217108e208c1834121024c93c841d7f576584ffbf513b7abd8283e6562669905f6554f788fce4cc67a34ffffffff0228100000000000001976a914af78709a76abc8a28e568c9210c8247dd10cff2c88ac22020000000000001976a914f339927678803f451b41400737e7dc83c6a8682188ac00000000", - txid: - "7f462d71c649a0d8cfbaa2d20d8ff86677966b308f0ac9906ee015bf4453f97a", - hash: - "7f462d71c649a0d8cfbaa2d20d8ff86677966b308f0ac9906ee015bf4453f97a", - depends: [], - fee: 226, - sigops: 2 - } - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Mining.getBlockTemplate("") - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getMiningInfo", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should get mining info", done => { - const data = { - blocks: 527816, - currentblocksize: 89408, - currentblocktx: 156, - difficulty: 568757800682.7649, - blockprioritypercentage: 5, - errors: "", - networkhashps: 4347259225696976000, - pooledtx: 184, - chain: "main" - } - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Mining.getMiningInfo() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) - - describe("#getNetworkHashps", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should get network hashps", done => { - const data = 3586365937646890000 - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Mining.getNetworkHashps() - .then(result => { - assert.equal(data, result) - }) - .then(done, done) - }) - }) - - describe("#submitBlock", () => { - // TODO finish - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should TODO", done => { - const data = {} - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "post").returns(resolved) - - bchjs.Mining.submitBlock() - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) -}) diff --git a/test/unit/mnemonic.js b/test/unit/mnemonic.js deleted file mode 100644 index 2028653..0000000 --- a/test/unit/mnemonic.js +++ /dev/null @@ -1,340 +0,0 @@ -const fixtures = require("./fixtures/mnemonic.json") -const assert = require("assert") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() - -describe("#Mnemonic", () => { - describe("#generate", () => { - it("should generate a 12 word mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(128) - assert.equal(mnemonic.split(" ").length, 12) - }) - - it("should generate a 15 word mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(160) - assert.equal(mnemonic.split(" ").length, 15) - }) - - it("should generate a 18 word mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(192) - assert.equal(mnemonic.split(" ").length, 18) - }) - - it("should generate an 21 word mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(224) - assert.equal(mnemonic.split(" ").length, 21) - }) - - it("should generate an 24 word mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(256) - assert.equal(mnemonic.split(" ").length, 24) - }) - - it("should generate an 24 word italian mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate( - 256, - bchjs.Mnemonic.wordLists().italian - ) - assert.equal(mnemonic.split(" ").length, 24) - }) - }) - - describe("#fromEntropy", () => { - it("should generate a 12 word mnemonic from 16 bytes of entropy", () => { - const rand = bchjs.Crypto.randomBytes(16) - const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) - assert.equal(mnemonic.split(" ").length, 12) - }) - - it("should generate a 15 word mnemonic from 20 bytes of entropy", () => { - const rand = bchjs.Crypto.randomBytes(20) - const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) - assert.equal(mnemonic.split(" ").length, 15) - }) - - it("should generate an 18 word mnemonic from 24 bytes of entropy", () => { - const rand = bchjs.Crypto.randomBytes(24) - const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) - assert.equal(mnemonic.split(" ").length, 18) - }) - - it("should generate an 21 word mnemonic from 28 bytes of entropy", () => { - const rand = bchjs.Crypto.randomBytes(28) - const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) - assert.equal(mnemonic.split(" ").length, 21) - }) - - it("should generate an 24 word mnemonic from 32 bytes of entropy", () => { - const rand = bchjs.Crypto.randomBytes(32) - const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) - assert.equal(mnemonic.split(" ").length, 24) - }) - - it("should generate an 24 french word mnemonic 32 bytes of entropy", () => { - const rand = bchjs.Crypto.randomBytes(32) - const mnemonic = bchjs.Mnemonic.fromEntropy( - rand.toString("hex"), - bchjs.Mnemonic.wordLists().french - ) - assert.equal(mnemonic.split(" ").length, 24) - }) - - fixtures.fromEntropy.forEach(entropy => { - const mnemonic = bchjs.Mnemonic.fromEntropy(entropy.entropy) - it(`should convert ${entropy.entropy} to ${entropy.mnemonic}`, () => { - assert.equal(mnemonic, entropy.mnemonic) - }) - }) - }) - - describe("#toEntropy", () => { - it("should turn a 12 word mnemonic to entropy", () => { - const mnemonic = bchjs.Mnemonic.generate(128) - const entropy = bchjs.Mnemonic.toEntropy(mnemonic) - assert.equal(entropy.length, 16) - }) - - it("should turn a 15 word mnemonic to entropy", () => { - const mnemonic = bchjs.Mnemonic.generate(160) - const entropy = bchjs.Mnemonic.toEntropy(mnemonic) - assert.equal(entropy.length, 20) - }) - - it("should turn a 18 word mnemonic to entropy", () => { - const mnemonic = bchjs.Mnemonic.generate(192) - const entropy = bchjs.Mnemonic.toEntropy(mnemonic) - assert.equal(entropy.length, 24) - }) - - it("should turn a 21 word mnemonic to entropy", () => { - const mnemonic = bchjs.Mnemonic.generate(224) - const entropy = bchjs.Mnemonic.toEntropy(mnemonic) - assert.equal(entropy.length, 28) - }) - - it("should turn a 24 word mnemonic to entropy", () => { - const mnemonic = bchjs.Mnemonic.generate(256) - const entropy = bchjs.Mnemonic.toEntropy(mnemonic) - assert.equal(entropy.length, 32) - }) - - it("should turn a 24 word spanish mnemonic to entropy", () => { - const mnemonic = bchjs.Mnemonic.generate( - 256, - bchjs.Mnemonic.wordLists().spanish - ) - const entropy = bchjs.Mnemonic.toEntropy( - mnemonic, - bchjs.Mnemonic.wordLists().spanish - ) - assert.equal(entropy.length, 32) - }) - - fixtures.fromEntropy.forEach(fixture => { - const entropy = bchjs.Mnemonic.toEntropy(fixture.mnemonic) - it(`should convert ${fixture.mnemonic} to ${fixture.entropy}`, () => { - assert.equal(entropy.toString("hex"), fixture.entropy) - }) - }) - }) - - describe("#validate", () => { - it("fails for a mnemonic that is too short", () => { - assert.equal( - bchjs.Mnemonic.validate( - "mixed winner", - bchjs.Mnemonic.wordLists().english - ), - "Invalid mnemonic" - ) - }) - - it("fails for a mnemonic that is too long", () => { - assert.equal( - bchjs.Mnemonic.validate( - "mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake", - bchjs.Mnemonic.wordLists().english - ), - "Invalid mnemonic" - ) - }) - - it("fails if mnemonic words are not in the word list", () => { - assert.equal( - bchjs.Mnemonic.validate( - "failsauce one two three four five six seven eight nine ten eleven", - bchjs.Mnemonic.wordLists().english - ), - "failsauce is not in wordlist, did you mean balance?" - ) - }) - - it("validate a 128 bit mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(128) - assert.equal( - bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), - "Valid mnemonic" - ) - }) - - it("validate a 160 bit mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(160) - assert.equal( - bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), - "Valid mnemonic" - ) - }) - - it("validate a 192 bit mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(192) - assert.equal( - bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), - "Valid mnemonic" - ) - }) - - it("validate a 224 bit mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(224) - assert.equal( - bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), - "Valid mnemonic" - ) - }) - - it("validate a 256 bit mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate(256) - assert.equal( - bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), - "Valid mnemonic" - ) - }) - - it("validate a 256 bit chinese simplified mnemonic", () => { - const mnemonic = bchjs.Mnemonic.generate( - 256, - bchjs.Mnemonic.wordLists().chinese_simplified - ) - assert.equal( - bchjs.Mnemonic.validate( - mnemonic, - bchjs.Mnemonic.wordLists().chinese_simplified - ), - "Valid mnemonic" - ) - }) - }) - - describe("#toSeed", () => { - it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 128 bit mnemonic", async () => { - const mnemonic = bchjs.Mnemonic.generate(128) - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") - assert.equal(rootSeedBuffer.byteLength, 64) - }) - - it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 160 bit mnemonic", async () => { - const mnemonic = bchjs.Mnemonic.generate(160) - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") - assert.equal(rootSeedBuffer.byteLength, 64) - }) - - it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 192 bit mnemonic", async () => { - const mnemonic = bchjs.Mnemonic.generate(192) - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") - assert.equal(rootSeedBuffer.byteLength, 64) - }) - - it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 224 bit mnemonic", async () => { - const mnemonic = bchjs.Mnemonic.generate(224) - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") - assert.equal(rootSeedBuffer.byteLength, 64) - }) - - it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 256 bit mnemonic", async () => { - const mnemonic = bchjs.Mnemonic.generate(256) - const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") - assert.equal(rootSeedBuffer.byteLength, 64) - }) - }) - - describe("#wordLists", () => { - it("return a list of 2048 english words", () => { - assert.equal(bchjs.Mnemonic.wordLists().english.length, 2048) - }) - - it("return a list of 2048 japanese words", () => { - assert.equal(bchjs.Mnemonic.wordLists().japanese.length, 2048) - }) - - it("return a list of 2048 chinese simplified words", () => { - assert.equal(bchjs.Mnemonic.wordLists().chinese_simplified.length, 2048) - }) - - it("return a list of 2048 chinese traditional words", () => { - assert.equal(bchjs.Mnemonic.wordLists().chinese_traditional.length, 2048) - }) - - it("return a list of 2048 french words", () => { - assert.equal(bchjs.Mnemonic.wordLists().french.length, 2048) - }) - - it("return a list of 2048 italian words", () => { - assert.equal(bchjs.Mnemonic.wordLists().italian.length, 2048) - }) - - it("return a list of 2048 korean words", () => { - assert.equal(bchjs.Mnemonic.wordLists().korean.length, 2048) - }) - - it("return a list of 2048 spanish words", () => { - assert.equal(bchjs.Mnemonic.wordLists().spanish.length, 2048) - }) - }) - - describe("#toKeypairs", async () => { - fixtures.toKeypairs.forEach(async (fixture, i) => { - const keypairs = await bchjs.Mnemonic.toKeypairs(fixture.mnemonic, 5) - keypairs.forEach((keypair, j) => { - it(`Generate keypair from mnemonic`, () => { - assert.equal( - keypair.privateKeyWIF, - fixtures.toKeypairs[i].output[j].privateKeyWIF - ) - // assert.equal( - // keypair.address, - // fixtures.toKeypairs[i].output[j].address - // ) - }) - }) - - const regtestKeypairs = await bchjs.Mnemonic.toKeypairs( - fixture.mnemonic, - 5, - true - ) - regtestKeypairs.forEach((keypair, j) => { - it(`Generate keypair from mnemonic`, () => { - assert.equal( - keypair.privateKeyWIF, - fixtures.toKeypairs[i].output[j].privateKeyWIFRegTest - ) - // assert.equal( - // keypair.address, - // fixtures.toKeypairs[i].output[j].regtestAddress - // ) - }) - }) - }) - }) - - describe("#findNearestWord", () => { - fixtures.findNearestWord.forEach((fixture, i) => { - const word = bchjs.Mnemonic.findNearestWord( - fixture.word, - bchjs.Mnemonic.wordLists()[fixture.language] - ) - it(`find word ${fixture.foundWord} near ${fixture.word} in ${fixture.language}`, () => { - assert.equal(word, fixture.foundWord) - }) - }) - }) -}) diff --git a/test/unit/ninsight.js b/test/unit/ninsight.js deleted file mode 100644 index 9670586..0000000 --- a/test/unit/ninsight.js +++ /dev/null @@ -1,255 +0,0 @@ -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/ninsight-mock") - -describe(`#Ninsight`, () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - describe(`#utxo`, () => { - it(`should throw an error for improper input`, async () => { - try { - const addr = 12345 - - await bchjs.Ninsight.utxo(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 GET utxos for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.utxo }) - - const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" - - const result = await bchjs.Ninsight.utxo(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "utxos") - assert.property(result, "legacyAddress") - assert.property(result, "cashAddress") - assert.property(result, "slpAddress") - assert.property(result, "scriptPubKey") - assert.property(result, "asm") - - assert.isArray(result.utxos) - - assert.property(result.utxos[0], "txid") - assert.property(result.utxos[0], "vout") - assert.property(result.utxos[0], "amount") - assert.property(result.utxos[0], "satoshis") - assert.property(result.utxos[0], "height") - assert.property(result.utxos[0], "confirmations") - }) - - it(`should POST utxo details for an array of addresses`, async () => { - // Mock the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.utxoPost }) - - const addr = [ - "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7", - "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr" - ] - - const result = await bchjs.Ninsight.utxo(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.isArray(result[0].utxos) - - assert.property(result[0], "utxos") - assert.property(result[0], "legacyAddress") - assert.property(result[0], "cashAddress") - assert.property(result[0], "slpAddress") - assert.property(result[0], "scriptPubKey") - assert.property(result[0], "asm") - }) - }) - - describe("#unconfirmed", () => { - it("should throw an error for improper input", async () => { - try { - const addr = 12345 - - await bchjs.Ninsight.unconfirmed(addr) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - assert.include( - err.message, - "Input address must be a string or array of strings." - ) - } - }) - - it(`should POST utxos for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmed }) - - const addr = "bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5" - - const result = await bchjs.Ninsight.unconfirmed(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "utxos") - assert.property(result, "legacyAddress") - assert.property(result, "cashAddress") - assert.property(result, "slpAddress") - assert.property(result, "scriptPubKey") - - assert.isArray(result.utxos) - - assert.property(result.utxos[0], "txid") - assert.property(result.utxos[0], "vout") - assert.property(result.utxos[0], "amount") - assert.property(result.utxos[0], "satoshis") - assert.property(result.utxos[0], "confirmations") - assert.property(result.utxos[0], "ts") - }) - - it(`should POST utxo details for an array of addresses`, async () => { - // Mock the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmedPost }) - - const addr = [ - "bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5", - "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr" - ] - - const result = await bchjs.Ninsight.unconfirmed(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.isArray(result[0].utxos) - - assert.property(result[0], "utxos") - assert.property(result[0], "legacyAddress") - assert.property(result[0], "cashAddress") - assert.property(result[0], "slpAddress") - assert.property(result[0], "scriptPubKey") - }) - }) - - describe(`#transactions`, () => { - it(`should throw an error for improper input`, async () => { - try { - const addr = 12345 - - await bchjs.Ninsight.transactions(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 transaction history for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.transactionsPost }) - - const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" - - const result = await bchjs.Ninsight.transactions(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.property(result[0], "cashAddress") - assert.property(result[0], "legacyAddress") - assert.property(result[0], "txs") - assert.isArray(result[0].txs) - assert.property(result[0].txs[0], "txid") - assert.property(result[0].txs[0], "vin") - assert.property(result[0].txs[0], "vout") - }) - it(`should POST transaction history for an array of addresses`, async () => { - // Mock the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.transactionsPost }) - - const addr = [ - "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7", - "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr" - ] - - const result = await bchjs.Ninsight.transactions(addr) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.property(result[0], "cashAddress") - assert.property(result[0], "legacyAddress") - assert.property(result[0], "txs") - assert.isArray(result[0].txs) - assert.property(result[0].txs[0], "txid") - }) - }) - describe(`#txDetails`, () => { - it(`should throw an error for improper input`, async () => { - try { - const txid = 12345 - - await bchjs.Ninsight.txDetails(txid) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - //console.log(`err: `, err) - assert.include( - err.message, - `Transaction ID must be a string or array of strings.` - ) - } - }) - it(`should POST transaction details for a single TxID`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.detailsPost }) - - const txid = "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" - - const result = await bchjs.Ninsight.txDetails(txid) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.property(result[0], "txid") - assert.property(result[0], "version") - assert.property(result[0], "locktime") - assert.property(result[0], "vin") - assert.property(result[0], "vout") - assert.property(result[0], "blockhash") - assert.property(result[0], "blockheight") - assert.property(result[0], "confirmations") - assert.property(result[0], "time") - assert.property(result[0], "blocktime") - assert.property(result[0], "isCoinBase") - assert.property(result[0], "valueOut") - assert.property(result[0], "size") - }) - it(`should POST transaction details for an array of TxIDs`, async () => { - // Stub the network call. - sandbox.stub(axios, "post").resolves({ data: mockData.detailsPost }) - - const txid = [ - "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33", - "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" - ] - - const result = await bchjs.Ninsight.txDetails(txid) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.property(result[0], "txid") - assert.property(result[0], "vin") - assert.property(result[0], "vout") - }) - }) -}) diff --git a/test/unit/openbazaar.js b/test/unit/openbazaar.js deleted file mode 100644 index d663fb0..0000000 --- a/test/unit/openbazaar.js +++ /dev/null @@ -1,129 +0,0 @@ -const chai = require("chai") -const assert = chai.assert -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const axios = require("axios") -const sinon = require("sinon") - -const mockData = require("./fixtures/openbazaar-mock") - -describe(`#OpenBazaar`, () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - describe(`#Balance`, () => { - it(`should throw an error for improper input`, async () => { - try { - const addr = 12345 - - await bchjs.OpenBazaar.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 GET balance for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.balance }) - - const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" - - const result = await bchjs.OpenBazaar.balance(addr) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, [ - "page", - "totalPages", - "itemsOnPage", - "addrStr", - "balance", - "totalReceived", - "totalSent", - "unconfirmedBalance", - "unconfirmedTxApperances", - "txApperances", - "transactions" - ]) - assert.isArray(result.transactions) - }) - }) - - describe(`#utxo`, () => { - it(`should throw an error for improper input`, async () => { - try { - const addr = 12345 - - await bchjs.OpenBazaar.utxo(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 GET utxos for a single address`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) - - const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" - - const result = await bchjs.OpenBazaar.utxo(addr) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "txid", - "vout", - "amount", - "height", - "confirmations", - "satoshis" - ]) - }) - }) - - describe(`#tx`, () => { - it(`should throw an error for improper input`, async () => { - try { - const txid = 12345 - - await bchjs.OpenBazaar.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 GET tx details for a single txid`, async () => { - // Stub the network call. - sandbox.stub(axios, "get").resolves({ data: mockData.tx }) - - const txid = - "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7" - - const result = await bchjs.OpenBazaar.tx(txid) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAllKeys(result, [ - "txid", - "version", - "vin", - "vout", - "blockhash", - "blockheight", - "confirmations", - "blocktime", - "valueOut", - "valueIn", - "fees", - "hex" - ]) - assert.isArray(result.vin) - assert.isArray(result.vout) - }) - }) -}) diff --git a/test/unit/raw-tranactions.js b/test/unit/raw-tranactions.js deleted file mode 100644 index 0eec0d8..0000000 --- a/test/unit/raw-tranactions.js +++ /dev/null @@ -1,129 +0,0 @@ -/* - TODO: - -Replace old unit tests mocking axios with the more generalized nock library. - See the sendRawTransaction test for an example. - -Create a mocking library of data to compare unit and integration tests. -*/ - -const assert = require("assert") -const axios = require("axios") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() - -const sinon = require("sinon") -const nock = require("nock") // HTTP mocking - -// Used for debugging -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#RawTransactions", () => { - describe("#decodeRawTransaction", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should decode raw transaction", done => { - const data = { - txid: - "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a", - hash: - "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a", - size: 10, - version: 2, - locktime: 0, - vin: [], - vout: [] - } - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.RawTransactions.decodeRawTransaction("02000000000000000000") - .then(result => { - assert.equal(data, result) - }) - .then(done, done) - }) - }) - - describe("#decodeScript", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should decode script", async () => { - const data = { - asm: "OP_RETURN 5361746f736869204e616b616d6f746f", - type: "nulldata", - p2sh: "bitcoincash:prswx5965nfumux9qng5kj8hw603vcne7q08t8c6jp" - } - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - const result = await bchjs.RawTransactions.decodeScript( - "6a105361746f736869204e616b616d6f746f" - ) - //console.log(`result: ${util.inspect(result)}`) - - assert.deepEqual(data, result) - }) - }) - - describe("#getRawTransaction", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should get raw transaction", done => { - const data = - "020000000160d663961c63c7f0a07f22ec07b8f55b3935bfdbed8b1d8454916e8932fbf109010000006b4830450221008479fab4cfdcb111833d250a43f98ac26d43272b7a29cb1b9a0491eae5c44b3502203448b17253632395c29a7d62058bbfe93efb20fc8636ba6837002d464195aec04121029123258f7cdcd45b864066bcaa9b71f24d5ed1fa1dd36eaf107d8432b5014658ffffffff016d180000000000001976a91479d3297d1823149f4ec61df31d19f2fad5390c0288ac00000000" - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.RawTransactions.getRawTransaction( - "808d617eccaad4f1397fe07a06ec5ed15a0821cf22a3e0931c0c92aef9e572b6" - ) - .then(result => { - assert.equal(data, result) - }) - .then(done, done) - }) - }) - - describe("#sendRawTransaction", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should send single raw transaction", async () => { - const data = "Error: transaction already in block chain" - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - const result = await bchjs.RawTransactions.sendRawTransaction( - "020000000160d663961c63c7f0a07f22ec07b8f55b3935bfdbed8b1d8454916e8932fbf109010000006b4830450221008479fab4cfdcb111833d250a43f98ac26d43272b7a29cb1b9a0491eae5c44b3502203448b17253632395c29a7d62058bbfe93efb20fc8636ba6837002d464195aec04121029123258f7cdcd45b864066bcaa9b71f24d5ed1fa1dd36eaf107d8432b5014658ffffffff016d180000000000001976a91479d3297d1823149f4ec61df31d19f2fad5390c0288ac00000000" - ) - - assert.equal(data, result) - }) - - it("should send an array of raw transactions", async () => { - const data = "Error: transaction already in block chain" - - // Mock the http call to rest.bitcoin.com - nock(`${bchjs.RawTransactions.restURL}`) - .post(uri => uri.includes(`/`)) - .reply(200, { data: data }) - - const result = await bchjs.RawTransactions.sendRawTransaction([ - "020000000160d663961c63c7f0a07f22ec07b8f55b3935bfdbed8b1d8454916e8932fbf109010000006b4830450221008479fab4cfdcb111833d250a43f98ac26d43272b7a29cb1b9a0491eae5c44b3502203448b17253632395c29a7d62058bbfe93efb20fc8636ba6837002d464195aec04121029123258f7cdcd45b864066bcaa9b71f24d5ed1fa1dd36eaf107d8432b5014658ffffffff016d180000000000001976a91479d3297d1823149f4ec61df31d19f2fad5390c0288ac00000000" - ]) - - assert.equal(data, result.data) - }) - }) -}) diff --git a/test/unit/scripts.js b/test/unit/scripts.js deleted file mode 100644 index f327a14..0000000 --- a/test/unit/scripts.js +++ /dev/null @@ -1,476 +0,0 @@ -const fixtures = require("./fixtures/script.json") -const assert = require("assert") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const Buffer = require("safe-buffer").Buffer - -describe("#Script", () => { - describe("#decode", () => { - describe("P2PKH scriptSig", () => { - fixtures.decodeScriptSig.forEach(fixture => { - it(`should decode scriptSig buffer`, () => { - const decodedScriptSig = bchjs.Script.decode( - Buffer.from(fixture.scriptSigHex, "hex") - ) - assert.equal(typeof decodedScriptSig, "object") - }) - - it(`should decode scriptSig buffer to cash address ${fixture.cashAddress}`, () => { - const decodedScriptSig = bchjs.Script.decode( - Buffer.from(fixture.scriptSigHex, "hex") - ) - const address = bchjs.HDNode.toCashAddress( - bchjs.ECPair.fromPublicKey(decodedScriptSig[1]) - ) - assert.equal(address, fixture.cashAddress) - }) - - it(`should decode scriptSig buffer to legacy address ${fixture.legacyAddress}`, () => { - const decodedScriptSig = bchjs.Script.decode( - Buffer.from(fixture.scriptSigHex, "hex") - ) - const address = bchjs.HDNode.toLegacyAddress( - bchjs.ECPair.fromPublicKey(decodedScriptSig[1]) - ) - assert.equal(address, fixture.legacyAddress) - }) - }) - }) - - describe("P2PKH scriptPubKey", () => { - fixtures.decodeScriptPubKey.forEach(fixture => { - it(`should decode scriptSig buffer`, () => { - const decodedScriptPubKey = bchjs.Script.decode( - Buffer.from(fixture.scriptPubKeyHex, "hex") - ) - assert.equal(decodedScriptPubKey.length, 5) - }) - - it(`should match hashed pubKey ${fixture.pubKeyHex}`, () => { - const decodedScriptPubKey = bchjs.Script.decode( - Buffer.from(fixture.scriptPubKeyHex, "hex") - ) - const data = Buffer.from(fixture.pubKeyHex, "hex") - const hash160 = bchjs.Crypto.hash160(data).toString("hex") - assert.equal(decodedScriptPubKey[2].toString("hex"), hash160) - }) - }) - }) - }) - - describe("#encode", () => { - describe("P2PKH scriptSig", () => { - fixtures.encodeScriptSig.forEach(fixture => { - it(`should encode scriptSig chunks to buffer`, () => { - const arr = [ - Buffer.from(fixture.scriptSigChunks[0], "hex"), - Buffer.from(fixture.scriptSigChunks[1], "hex") - ] - const encodedScriptSig = bchjs.Script.encode(arr) - assert.equal(typeof encodedScriptSig, "object") - }) - }) - }) - - describe("P2PKH scriptPubKey", () => { - fixtures.encodeScriptPubKey.forEach(fixture => { - it(`should encode scriptPubKey buffer`, () => { - const decodedScriptPubKey = bchjs.Script.decode( - Buffer.from(fixture.scriptPubKeyHex, "hex") - ) - const compiledScriptPubKey = bchjs.Script.encode(decodedScriptPubKey) - assert.equal( - compiledScriptPubKey.toString("hex"), - fixture.scriptPubKeyHex - ) - }) - }) - }) - - describe("Encode SLP SEND OP_RETURN properly", () => { - it("should correctly compile OP_RETURN SLP SEND transaction", () => { - const scriptArr = [ - bchjs.Script.opcodes.OP_RETURN, - Buffer.from("534c5000", "hex"), - Buffer.from("01", "hex"), - Buffer.from(`SEND`), - Buffer.from( - "73db55368981e4878440637e448d4abe7f661be5c3efdcbcb63bd86a01a76b5a", - "hex" - ), - Buffer.from("00000001", "hex") - ] - - const data = bchjs.Script.encode2(scriptArr) - - // convert data to a hex string - let str = "" - for (let i = 0; i < data.length; i++) { - let hex = Number(data[i]).toString(16) - - // zero pad when its a single digit. - hex = `0${hex}` - hex = hex.slice(-2) - //console.log(`hex: ${hex}`) - - str += hex - } - console.log(`Hex string: ${str}`) - - //console.log(`scriptArr: ${JSON.stringify(data,null,2)}`) - - const correctStr = - "6a04534c500001010453454e442073db55368981e4878440637e448d4abe7f661be5c3efdcbcb63bd86a01a76b5a0400000001" - - assert.equal(str, correctStr) - }) - }) - }) - - describe("#toASM", () => { - describe("P2PKH scriptSig", () => { - fixtures.scriptSigToASM.forEach(fixture => { - it(`should encode scriptSig buffer to ${fixture.asm}`, () => { - const arr = [ - Buffer.from(fixture.scriptSigChunks[0], "hex"), - Buffer.from(fixture.scriptSigChunks[1], "hex") - ] - const compiledScriptSig = bchjs.Script.encode(arr) - const asm = bchjs.Script.toASM(compiledScriptSig) - assert.equal(asm, fixture.asm) - }) - }) - }) - - describe("P2PKH scriptPubKey", () => { - fixtures.scriptPubKeyToASM.forEach(fixture => { - it(`should compile scriptPubKey buffer to ${fixture.asm}`, () => { - const asm = bchjs.Script.toASM( - Buffer.from(fixture.scriptPubKeyHex, "hex") - ) - assert.equal(asm, fixture.asm) - }) - }) - }) - }) - - describe("#fromASM", () => { - describe("P2PKH scriptSig", () => { - fixtures.scriptSigFromASM.forEach(fixture => { - it(`should decode scriptSig asm to buffer`, () => { - const buf = bchjs.Script.fromASM(fixture.asm) - assert.equal(typeof buf, "object") - }) - }) - }) - - describe("P2PKH scriptPubKey", () => { - fixtures.scriptPubKeyFromASM.forEach(fixture => { - it(`should decode scriptPubKey asm to buffer`, () => { - const buf = bchjs.Script.fromASM(fixture.asm) - assert.equal(typeof buf, "object") - }) - }) - }) - }) - - describe("#OPCodes", () => { - for (const opcode in fixtures.opcodes) { - it(`should have OP Code ${opcode}`, () => { - assert.equal(bchjs.Script.opcodes[opcode], fixtures.opcodes[opcode]) - }) - } - }) - - describe("#classifyInput", () => { - fixtures.classifyInput.forEach(fixture => { - it(`should classify input type ${fixture.type}`, () => { - const type = bchjs.Script.classifyInput( - bchjs.Script.fromASM(fixture.script) - ) - assert.equal(type, fixture.type) - }) - }) - }) - - describe("#classifyOutput", () => { - fixtures.classifyOutput.forEach(fixture => { - it(`should classify ouput type ${fixture.type}`, () => { - const type = bchjs.Script.classifyOutput( - bchjs.Script.fromASM(fixture.script) - ) - assert.equal(type, fixture.type) - }) - }) - }) - - describe("#nullDataTemplate", () => { - fixtures.nullDataTemplate.forEach(fixture => { - it(`should encode nulldata output`, () => { - const buf = bchjs.Script.nullData.output.encode( - Buffer.from(`${fixture.data}`, "ascii") - ) - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode nulldata output`, () => { - const buf = bchjs.Script.nullData.output.decode( - Buffer.from(`${fixture.hex}`, "hex") - ) - assert.equal(buf.toString("ascii"), fixture.data) - }) - - it(`should confirm correctly formatted nulldata output`, () => { - const buf = bchjs.Script.nullData.output.encode( - Buffer.from(`${fixture.data}`, "ascii") - ) - const valid = bchjs.Script.nullData.output.check(buf) - assert.equal(valid, true) - }) - }) - }) - - describe("#pubKeyTemplate", () => { - describe("#pubKeyInputTemplate", () => { - fixtures.pubKeyInputTemplate.forEach(fixture => { - it(`should encode pubKey input`, () => { - const buf = bchjs.Script.pubKey.input.encode( - Buffer.from(fixture.signature, "hex") - ) - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode pubKey input`, () => { - const buf = bchjs.Script.pubKey.input.decode( - Buffer.from(fixture.hex, "hex") - ) - assert.equal(buf.toString("hex"), fixture.signature) - }) - - it(`should confirm correctly formatted pubKeyHash input`, () => { - const buf = bchjs.Script.pubKey.input.encode( - Buffer.from(fixture.signature, "hex") - ) - const valid = bchjs.Script.pubKey.input.check(buf) - assert.equal(valid, true) - }) - }) - }) - - describe("#pubKeyOutputTemplate", () => { - fixtures.pubKeyOutputTemplate.forEach(fixture => { - it(`should encode pubKey output`, () => { - const buf = bchjs.Script.pubKey.output.encode( - Buffer.from(fixture.pubKey, "hex") - ) - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode pubKey output`, () => { - const buf = bchjs.Script.pubKey.output.decode( - Buffer.from(`${fixture.hex}`, "hex") - ) - assert.equal(buf.toString("hex"), fixture.pubKey) - }) - - it(`should confirm correctly formatted pubKey output`, () => { - const buf = bchjs.Script.pubKey.output.encode( - Buffer.from(fixture.pubKey, "hex") - ) - const valid = bchjs.Script.pubKey.output.check(buf) - assert.equal(valid, true) - }) - }) - }) - }) - - describe("#pubKeyHashTemplate", () => { - describe("#pubKeyHashInputTemplate", () => { - fixtures.pubKeyHashInputTemplate.forEach(fixture => { - it(`should encode pubKeyHash input`, () => { - const buf = bchjs.Script.pubKeyHash.input.encode( - Buffer.from(fixture.signature, "hex"), - Buffer.from(fixture.pubKey, "hex") - ) - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode pubKeyHash input signature`, () => { - const buf = bchjs.Script.pubKeyHash.input.decode( - Buffer.from(fixture.hex, "hex") - ) - assert.equal(buf.signature.toString("hex"), fixture.signature) - }) - - it(`should decode pubKeyHash input pubkey`, () => { - const buf = bchjs.Script.pubKeyHash.input.decode( - Buffer.from(fixture.hex, "hex") - ) - assert.equal(buf.pubKey.toString("hex"), fixture.pubKey) - }) - - it(`should confirm correctly formatted pubKeyHash input`, () => { - const buf = bchjs.Script.pubKeyHash.input.encode( - Buffer.from(fixture.signature, "hex"), - Buffer.from(fixture.pubKey, "hex") - ) - const valid = bchjs.Script.pubKeyHash.input.check(buf) - assert.equal(valid, true) - }) - }) - }) - - describe("#pubKeyHashOutputTemplate", () => { - fixtures.pubKeyHashOutputTemplate.forEach(fixture => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const identifier = bchjs.HDNode.toIdentifier(node) - it(`should encode pubKeyHash output`, () => { - const buf = bchjs.Script.pubKeyHash.output.encode(identifier) - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode pubKeyHash output`, () => { - const buf = bchjs.Script.pubKeyHash.output.decode( - Buffer.from(`${fixture.hex}`, "hex") - ) - assert.equal(buf.toString("hex"), identifier.toString("hex")) - }) - - it(`should confirm correctly formatted pubKeyHash output`, () => { - const buf = bchjs.Script.pubKeyHash.output.encode(identifier) - const valid = bchjs.Script.pubKeyHash.output.check(buf) - assert.equal(valid, true) - }) - }) - }) - }) - - describe("#multisigTemplate", () => { - describe("#multisigInputTemplate", () => { - fixtures.multisigInputTemplate.forEach(fixture => { - it(`should encode multisig input`, () => { - const signatures = fixture.signatures.map(signature => - signature - ? Buffer.from(signature, "hex") - : bchjs.Script.opcodes.OP_0 - ) - - const buf = bchjs.Script.multisig.input.encode(signatures) - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode multisig input`, () => { - const buf = bchjs.Script.multisig.input.decode( - Buffer.from(fixture.hex, "hex") - ) - assert.equal(buf[0].toString("hex"), fixture.signatures[0]) - }) - - it(`should confirm correctly formatted multisig input`, () => { - const signatures = fixture.signatures.map(signature => - signature - ? Buffer.from(signature, "hex") - : bchjs.Script.opcodes.OP_0 - ) - - const buf = bchjs.Script.multisig.input.encode(signatures) - const valid = bchjs.Script.multisig.input.check(buf) - assert.equal(valid, true) - }) - }) - }) - - describe("#multisigOutputTemplate", () => { - fixtures.multisigOutputTemplate.forEach(fixture => { - it(`should encode multisig output`, () => { - const pubKeys = fixture.pubKeys.map(p => Buffer.from(p, "hex")) - const m = pubKeys.length - const buf = bchjs.Script.multisig.output.encode(m, pubKeys) - - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode multisig output`, () => { - const output = bchjs.Script.multisig.output.decode( - Buffer.from(`${fixture.hex}`, "hex") - ) - assert.equal(output.m, fixture.pubKeys.length) - }) - - it(`should confirm correctly formatted multisig output`, () => { - const pubKeys = fixture.pubKeys.map(p => Buffer.from(p, "hex")) - const m = pubKeys.length - const buf = bchjs.Script.multisig.output.encode(m, pubKeys) - const valid = bchjs.Script.multisig.output.check(buf) - assert.equal(valid, true) - }) - }) - }) - }) - - describe("#scriptHashTemplate", () => { - describe("#scriptHashInputTemplate", () => { - fixtures.scriptHashInputTemplate.forEach(fixture => { - it(`should encode scriptHash input`, () => { - const buf = bchjs.Script.scriptHash.input.encode( - bchjs.Script.fromASM(fixture.redeemScriptSig), - bchjs.Script.fromASM(fixture.redeemScript) - ) - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode scriptHash input`, () => { - const redeemScriptSig = bchjs.Script.fromASM(fixture.redeemScriptSig) - const redeemScript = bchjs.Script.fromASM(fixture.redeemScript) - assert.deepEqual( - bchjs.Script.scriptHash.input.decode( - Buffer.from(fixture.hex, "hex") - ), - { - redeemScriptSig: redeemScriptSig, - redeemScript: redeemScript - } - ) - }) - - it(`should confirm correctly formatted scriptHash input`, () => { - const buf = bchjs.Script.scriptHash.input.encode( - bchjs.Script.fromASM(fixture.redeemScriptSig), - bchjs.Script.fromASM(fixture.redeemScript) - ) - const valid = bchjs.Script.scriptHash.input.check(buf) - assert.equal(valid, true) - }) - }) - }) - - describe("#scriptHashOutputTemplate", () => { - fixtures.scriptHashOutputTemplate.forEach(fixture => { - it(`should encode scriptHash output`, () => { - const redeemScript = bchjs.Script.fromASM(fixture.output) - const scriptHash = bchjs.Crypto.hash160(redeemScript) - const buf = bchjs.Script.scriptHash.output.encode(scriptHash) - - assert.equal(buf.toString("hex"), fixture.hex) - }) - - it(`should decode scriptHash output`, () => { - const redeemScript = bchjs.Script.fromASM(fixture.output) - const scriptHash = bchjs.Crypto.hash160(redeemScript) - const buf = bchjs.Script.scriptHash.output.decode( - Buffer.from(`${fixture.hex}`, "hex") - ) - assert.deepEqual(buf, scriptHash) - }) - - it(`should confirm correctly formatted scriptHash output`, () => { - const redeemScript = bchjs.Script.fromASM(fixture.output) - const scriptHash = bchjs.Crypto.hash160(redeemScript) - const buf = bchjs.Script.scriptHash.output.encode(scriptHash) - const valid = bchjs.Script.scriptHash.output.check(buf) - assert.equal(valid, true) - }) - }) - }) - }) -}) diff --git a/test/unit/slp-address.js b/test/unit/slp-address.js deleted file mode 100644 index 6e68bcd..0000000 --- a/test/unit/slp-address.js +++ /dev/null @@ -1,838 +0,0 @@ -const assert = require("assert") - -const slp = require("../../src/slp/slp") -const SLP = new slp({ restURL: "http://fakeurl.com/" }) - -const fixtures = require("./fixtures/slp/address.json") -//const axios = require("axios") -//const sinon = require("sinon") - -function flatten(arrays) { - return [].concat.apply([], arrays) -} - -const LEGACY_MAINNET_ADDRESSES = flatten([ - fixtures.mainnet.legacyP2PKH, - fixtures.mainnet.legacyP2SH -]) - -const CASH_MAINNET_ADDRESSES = flatten([ - fixtures.mainnet.cashAddressP2PKH, - fixtures.mainnet.cashAddressP2SH -]) - -const SLP_MAINNET_ADDRESSES = flatten([ - fixtures.mainnet.slpAddressP2PKH, - fixtures.mainnet.slpAddressP2SH -]) - -const LEGACY_TESTNET_ADDRESSES = flatten([ - fixtures.testnet.legacyP2PKH, - fixtures.testnet.legacyP2SH -]) - -const CASH_TESTNET_ADDRESSES = flatten([ - fixtures.testnet.cashAddressP2PKH, - fixtures.testnet.cashAddressP2SH -]) - -const SLP_TESTNET_ADDRESSES = flatten([ - fixtures.testnet.slpAddressP2PKH, - fixtures.testnet.slpAddressP2SH -]) - -const MAINNET_P2PKH_ADDRESSES = flatten([ - fixtures.mainnet.legacyP2PKH, - fixtures.mainnet.cashAddressP2PKH, - fixtures.mainnet.slpAddressP2PKH -]) - -const TESTNET_P2PKH_ADDRESSES = flatten([ - fixtures.testnet.legacyP2PKH, - fixtures.testnet.cashAddressP2PKH, - fixtures.testnet.slpAddressP2PKH -]) - -const MAINNET_P2SH_ADDRESSES = flatten([ - fixtures.mainnet.legacyP2SH, - fixtures.mainnet.cashAddressP2SH, - fixtures.mainnet.slpAddressP2SH -]) - -const TESTNET_P2SH_ADDRESSES = flatten([ - fixtures.testnet.legacyP2SH, - fixtures.testnet.cashAddressP2SH, - fixtures.testnet.slpAddressP2SH -]) -/* -const CASH_MAINNET_ADDRESSES_NO_PREFIX = CASH_MAINNET_ADDRESSES.map(address => { - const parts = address.split(":") - return parts[1] -}) - -const CASH_TESTNET_ADDRESSES_NO_PREFIX = CASH_TESTNET_ADDRESSES.map(address => { - const parts = address.split(":") - return parts[1] -}) - -const SLP_MAINNET_ADDRESSES_NO_PREFIX = SLP_MAINNET_ADDRESSES.map(address => { - const parts = address.split(":") - return parts[1] -}) - -const SLP_TESTNET_ADDRESSES_NO_PREFIX = SLP_TESTNET_ADDRESSES.map(address => { - const parts = address.split(":") - return parts[1] -}) -*/ -describe("#SLP Address", () => { - describe("#mainnet", () => { - describe("#toLegacyAddress", () => { - it("should convert mainnet legacy address format to itself correctly", () => { - assert.deepEqual( - LEGACY_MAINNET_ADDRESSES.map(address => - SLP.Address.toLegacyAddress(address) - ), - LEGACY_MAINNET_ADDRESSES - ) - }) - - it(`should convert cashAddr to legacyAddr`, async () => { - assert.deepEqual( - CASH_MAINNET_ADDRESSES.map(address => - SLP.Address.toLegacyAddress(address) - ), - LEGACY_MAINNET_ADDRESSES - ) - }) - - it(`should convert slpAddr to legacyAddr`, async () => { - assert.deepEqual( - SLP_MAINNET_ADDRESSES.map(address => - SLP.Address.toLegacyAddress(address) - ), - LEGACY_MAINNET_ADDRESSES - ) - }) - }) - - describe("#toCashAddress", () => { - it("should convert mainnet cash address format to itself correctly", () => { - assert.deepEqual( - CASH_MAINNET_ADDRESSES.map(address => - SLP.Address.toCashAddress(address) - ), - CASH_MAINNET_ADDRESSES - ) - }) - - it(`should convert legacyAddr to cashAddr`, async () => { - assert.deepEqual( - LEGACY_MAINNET_ADDRESSES.map(address => - SLP.Address.toCashAddress(address) - ), - CASH_MAINNET_ADDRESSES - ) - }) - - it(`should convert slpAddr to cashAddr`, async () => { - assert.deepEqual( - SLP_MAINNET_ADDRESSES.map(address => - SLP.Address.toCashAddress(address) - ), - CASH_MAINNET_ADDRESSES - ) - }) - }) - - describe("#toSLPAddress", () => { - it("should convert mainnet slp address format to itself correctly", () => { - assert.deepEqual( - SLP_MAINNET_ADDRESSES.map(address => - SLP.Address.toSLPAddress(address) - ), - SLP_MAINNET_ADDRESSES - ) - }) - - it(`should convert legacyAddr to slpAddr`, async () => { - assert.deepEqual( - LEGACY_MAINNET_ADDRESSES.map(address => - SLP.Address.toSLPAddress(address) - ), - SLP_MAINNET_ADDRESSES - ) - }) - - it(`should convert cashAddr to slpAddr`, async () => { - assert.deepEqual( - CASH_MAINNET_ADDRESSES.map(address => - SLP.Address.toSLPAddress(address) - ), - SLP_MAINNET_ADDRESSES - ) - }) - }) - - describe("#isLegacyAddress", () => { - describe("is legacy addr", () => { - LEGACY_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a legacy address`, () => { - const isLegacyaddr = SLP.Address.isLegacyAddress(address) - assert.equal(isLegacyaddr, true) - }) - }) - }) - - describe("cashaddr is not legacy addr", () => { - CASH_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a legacy address`, () => { - const isLegacyaddr = SLP.Address.isLegacyAddress(address) - assert.equal(isLegacyaddr, false) - }) - }) - }) - - describe("slpaddr is not legacy addr", () => { - SLP_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a legacy address`, () => { - const isLegacyaddr = SLP.Address.isLegacyAddress(address) - assert.equal(isLegacyaddr, false) - }) - }) - }) - }) - - describe("#isCashAddress", () => { - describe("is cashaddr", () => { - CASH_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a cashaddr address`, () => { - const isCashaddr = SLP.Address.isCashAddress(address) - assert.equal(isCashaddr, true) - }) - }) - }) - - describe("legacy is not cash addr", () => { - LEGACY_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a cash address`, () => { - const isCashaddr = SLP.Address.isCashAddress(address) - assert.equal(isCashaddr, false) - }) - }) - }) - - describe("slpaddr is not cash addr", () => { - SLP_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a cash address`, () => { - const isCashaddr = SLP.Address.isCashAddress(address) - assert.equal(isCashaddr, false) - }) - }) - }) - }) - - describe("#isSLPAddress", () => { - describe("is slpaddr", () => { - SLP_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is an slp address`, () => { - const isSLPaddr = SLP.Address.isSLPAddress(address) - assert.equal(isSLPaddr, true) - }) - }) - }) - - describe("legacy is not slp addr", () => { - LEGACY_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not an slp address`, () => { - const isSLPaddr = SLP.Address.isSLPAddress(address) - assert.equal(isSLPaddr, false) - }) - }) - }) - - describe("cash is not slp addr", () => { - CASH_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not an slp address`, () => { - const isSLPaddr = SLP.Address.isSLPAddress(address) - assert.equal(isSLPaddr, false) - }) - }) - }) - }) - - describe("#isMainnetAddress", () => { - describe("mainnet legacy addr", () => { - LEGACY_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a mainnet address`, () => { - const isMainnetaddr = SLP.Address.isMainnetAddress(address) - assert.equal(isMainnetaddr, true) - }) - }) - }) - - describe("mainnet cash addr", () => { - CASH_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a mainnet address`, () => { - const isMainnetaddr = SLP.Address.isMainnetAddress(address) - assert.equal(isMainnetaddr, true) - }) - }) - }) - - describe("mainnet slp addr", () => { - SLP_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a mainnet address`, () => { - const isMainnetaddr = SLP.Address.isMainnetAddress(address) - assert.equal(isMainnetaddr, true) - }) - }) - }) - - describe("testnet legacy addr", () => { - LEGACY_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a mainnet address`, () => { - const isMainnetaddr = SLP.Address.isMainnetAddress(address) - assert.equal(isMainnetaddr, false) - }) - }) - }) - - describe("testnet cash addr", () => { - CASH_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a mainnet address`, () => { - const isMainnetaddr = SLP.Address.isMainnetAddress(address) - assert.equal(isMainnetaddr, false) - }) - }) - }) - - describe("testnet slp addr", () => { - SLP_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a mainnet address`, () => { - const isMainnetaddr = SLP.Address.isMainnetAddress(address) - assert.equal(isMainnetaddr, false) - }) - }) - }) - }) - - describe("#isP2PKHAddress", () => { - describe("mainnet legacy addr", () => { - MAINNET_P2PKH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a P2PKH address`, () => { - const isP2PKHaddr = SLP.Address.isP2PKHAddress(address) - assert.equal(isP2PKHaddr, true) - }) - }) - }) - }) - - describe("#isP2SHAddress", () => { - describe("mainnet legacy addr", () => { - MAINNET_P2SH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a P2SH address`, () => { - const isP2SHaddr = SLP.Address.isP2SHAddress(address) - assert.equal(isP2SHaddr, true) - }) - }) - }) - }) - - describe("#detectAddressFormat", () => { - LEGACY_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a legacy address`, () => { - const isLegacy = SLP.Address.detectAddressFormat(address) - assert.equal(isLegacy, "legacy") - }) - }) - - CASH_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a cash address`, () => { - const isCashaddr = SLP.Address.detectAddressFormat(address) - assert.equal(isCashaddr, "cashaddr") - }) - }) - - SLP_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is an slp address`, () => { - const isSlpaddr = SLP.Address.detectAddressFormat(address) - assert.equal(isSlpaddr, "slpaddr") - }) - }) - }) - - describe("#detectAddressNetwork", () => { - LEGACY_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a mainnet address`, () => { - const isMainnet = SLP.Address.detectAddressNetwork(address) - assert.equal(isMainnet, "mainnet") - }) - }) - - CASH_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a mainnet address`, () => { - const isMainnet = SLP.Address.detectAddressNetwork(address) - assert.equal(isMainnet, "mainnet") - }) - }) - - SLP_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a mainnet address`, () => { - const isMainnet = SLP.Address.detectAddressNetwork(address) - assert.equal(isMainnet, "mainnet") - }) - }) - }) - - describe("#detectAddressType", () => { - MAINNET_P2PKH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a p2pkh address`, () => { - const isp2pkh = SLP.Address.detectAddressType(address) - assert.equal(isp2pkh, "p2pkh") - }) - }) - - MAINNET_P2SH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a p2sh address`, () => { - const isp2sh = SLP.Address.detectAddressType(address) - assert.equal(isp2sh, "p2sh") - }) - }) - }) - }) - - describe("#testnet", () => { - describe("#toLegacyAddress", () => { - it("should convert testnet legacy address format to itself correctly", () => { - assert.deepEqual( - LEGACY_TESTNET_ADDRESSES.map(address => - SLP.Address.toLegacyAddress(address) - ), - LEGACY_TESTNET_ADDRESSES - ) - }) - - it(`should convert cashAddr to legacyAddr`, async () => { - assert.deepEqual( - CASH_TESTNET_ADDRESSES.map(address => - SLP.Address.toLegacyAddress(address) - ), - LEGACY_TESTNET_ADDRESSES - ) - }) - - it(`should convert slpAddr to legacyAddr`, async () => { - assert.deepEqual( - SLP_TESTNET_ADDRESSES.map(address => - SLP.Address.toLegacyAddress(address) - ), - LEGACY_TESTNET_ADDRESSES - ) - }) - }) - - describe("#toCashAddress", () => { - it("should convert testnet cash address format to itself correctly", () => { - assert.deepEqual( - CASH_TESTNET_ADDRESSES.map(address => - SLP.Address.toCashAddress(address) - ), - CASH_TESTNET_ADDRESSES - ) - }) - - it(`should convert legacyAddr to cashAddr`, async () => { - assert.deepEqual( - LEGACY_TESTNET_ADDRESSES.map(address => - SLP.Address.toCashAddress(address) - ), - CASH_TESTNET_ADDRESSES - ) - }) - - it(`should convert slpAddr to cashAddr`, async () => { - assert.deepEqual( - SLP_TESTNET_ADDRESSES.map(address => - SLP.Address.toCashAddress(address) - ), - CASH_TESTNET_ADDRESSES - ) - }) - }) - - describe("#toSLPAddress", () => { - it("should convert testnet slp address format to itself correctly", () => { - assert.deepEqual( - SLP_TESTNET_ADDRESSES.map(address => - SLP.Address.toSLPAddress(address) - ), - SLP_TESTNET_ADDRESSES - ) - }) - - it(`should convert legacyAddr to slpAddr`, async () => { - assert.deepEqual( - LEGACY_TESTNET_ADDRESSES.map(address => - SLP.Address.toSLPAddress(address) - ), - SLP_TESTNET_ADDRESSES - ) - }) - - it(`should convert cashAddr to slpAddr`, async () => { - assert.deepEqual( - CASH_TESTNET_ADDRESSES.map(address => - SLP.Address.toSLPAddress(address) - ), - SLP_TESTNET_ADDRESSES - ) - }) - }) - - describe("#isLegacyAddress", () => { - describe("is legacy addr", () => { - LEGACY_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a legacy address`, () => { - const isLegacyaddr = SLP.Address.isLegacyAddress(address) - assert.equal(isLegacyaddr, true) - }) - }) - }) - - describe("cashaddr is not legacy addr", () => { - CASH_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a legacy address`, () => { - const isLegacyaddr = SLP.Address.isLegacyAddress(address) - assert.equal(isLegacyaddr, false) - }) - }) - }) - - describe("slpaddr is not legacy addr", () => { - SLP_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a legacy address`, () => { - const isLegacyaddr = SLP.Address.isLegacyAddress(address) - assert.equal(isLegacyaddr, false) - }) - }) - }) - }) - - describe("#isCashAddress", () => { - describe("is cashaddr", () => { - CASH_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a cashaddr address`, () => { - const isCashaddr = SLP.Address.isCashAddress(address) - assert.equal(isCashaddr, true) - }) - }) - }) - - describe("legacy is not cash addr", () => { - LEGACY_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a cash address`, () => { - const isCashaddr = SLP.Address.isCashAddress(address) - assert.equal(isCashaddr, false) - }) - }) - }) - - describe("slpaddr is not cash addr", () => { - SLP_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a cash address`, () => { - const isCashaddr = SLP.Address.isCashAddress(address) - assert.equal(isCashaddr, false) - }) - }) - }) - }) - - describe("#isSLPAddress", () => { - describe("is slpaddr", () => { - SLP_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is an slp address`, () => { - const isSLPaddr = SLP.Address.isSLPAddress(address) - assert.equal(isSLPaddr, true) - }) - }) - }) - - describe("legacy is not slp addr", () => { - LEGACY_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not an slp address`, () => { - const isSLPaddr = SLP.Address.isSLPAddress(address) - assert.equal(isSLPaddr, false) - }) - }) - }) - - describe("cash is not slp addr", () => { - CASH_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not an slp address`, () => { - const isSLPaddr = SLP.Address.isSLPAddress(address) - assert.equal(isSLPaddr, false) - }) - }) - }) - }) - - describe("#isTestnetAddress", () => { - describe("testnet legacy addr", () => { - LEGACY_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnetaddr = SLP.Address.isTestnetAddress(address) - assert.equal(isTestnetaddr, true) - }) - }) - }) - - describe("testnet cash addr", () => { - CASH_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnetaddr = SLP.Address.isTestnetAddress(address) - assert.equal(isTestnetaddr, true) - }) - }) - }) - - describe("testnet slp addr", () => { - SLP_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnetaddr = SLP.Address.isTestnetAddress(address) - assert.equal(isTestnetaddr, true) - }) - }) - }) - - describe("mainnet legacy addr", () => { - LEGACY_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a testnet address`, () => { - const isTestnetaddr = SLP.Address.isTestnetAddress(address) - assert.equal(isTestnetaddr, false) - }) - }) - }) - - describe("mainnet cash addr", () => { - CASH_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a testnet address`, () => { - const isTestnetaddr = SLP.Address.isTestnetAddress(address) - assert.equal(isTestnetaddr, false) - }) - }) - }) - - describe("mainnet slp addr", () => { - SLP_MAINNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is not a testnet address`, () => { - const isTestnetaddr = SLP.Address.isTestnetAddress(address) - assert.equal(isTestnetaddr, false) - }) - }) - }) - }) - - describe("#isP2PKHAddress", () => { - describe("testnet legacy addr", () => { - TESTNET_P2PKH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a P2PKH address`, () => { - const isP2PKHaddr = SLP.Address.isP2PKHAddress(address) - assert.equal(isP2PKHaddr, true) - }) - }) - }) - }) - - describe("#isP2SHAddress", () => { - describe("testnet legacy addr", () => { - TESTNET_P2SH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a P2SH address`, () => { - const isP2SHaddr = SLP.Address.isP2SHAddress(address) - assert.equal(isP2SHaddr, true) - }) - }) - }) - }) - - describe("#detectAddressFormat", () => { - LEGACY_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a legacy address`, () => { - const isLegacy = SLP.Address.detectAddressFormat(address) - assert.equal(isLegacy, "legacy") - }) - }) - - CASH_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a cash address`, () => { - const isCashaddr = SLP.Address.detectAddressFormat(address) - assert.equal(isCashaddr, "cashaddr") - }) - }) - - SLP_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is an slp address`, () => { - const isSlpaddr = SLP.Address.detectAddressFormat(address) - assert.equal(isSlpaddr, "slpaddr") - }) - }) - }) - - describe("#detectAddressNetwork", () => { - LEGACY_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnet = SLP.Address.detectAddressNetwork(address) - assert.equal(isTestnet, "testnet") - }) - }) - - CASH_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnet = SLP.Address.detectAddressNetwork(address) - assert.equal(isTestnet, "testnet") - }) - }) - - SLP_TESTNET_ADDRESSES.forEach(address => { - it(`should detect ${address} is a testnet address`, () => { - const isTestnet = SLP.Address.detectAddressNetwork(address) - assert.equal(isTestnet, "testnet") - }) - }) - }) - - describe("#detectAddressType", () => { - TESTNET_P2PKH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a p2pkh address`, () => { - const isp2pkh = SLP.Address.detectAddressType(address) - assert.equal(isp2pkh, "p2pkh") - }) - }) - }) - - describe("#detectAddressType", () => { - TESTNET_P2SH_ADDRESSES.forEach(address => { - it(`should detect ${address} is a p2sh address`, () => { - const isp2sh = SLP.Address.detectAddressType(address) - assert.equal(isp2sh, "p2sh") - }) - }) - }) - }) -}) -/* -describe("#details", () => { - let sandbox - beforeEach(() => (sandbox = sinon.sandbox.create())) - afterEach(() => sandbox.restore()) - - it("should get details", done => { - const data = { - balance: 0.00000546, - balanceSat: 546, - totalReceived: 0.00426132, - totalReceivedSat: 426132, - totalSent: 0.00425586, - totalSentSat: 425586, - unconfirmedBalance: 0, - unconfirmedBalanceSat: 0, - unconfirmedTxApperances: 0, - txApperances: 3, - transactions: [ - "902fe6ed7a19570c032b3ba4c4d7af92804662b486a15c8ca2d284166c658dd4", - "467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5", - "c1d9f3490e96a1fe4f77195067f1ab12c787f79b39d107424e0c4c810098e11b" - ], - legacyAddress: "1NM2ozrXVSnMRm66ua6aGeXgMsU7yqwqLS", - cashAddress: "bitcoincash:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3g4hl47n9y", - slpAddress: "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6", - currentPage: 0, - pagesTotal: 1 - } - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - SLP.Address.details( - "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6" - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) -}) - -describe("#utxo", () => { - let sandbox - beforeEach(() => (sandbox = sinon.sandbox.create())) - afterEach(() => sandbox.restore()) - - it("should get utxo", done => { - const data = { - utxos: [ - { - txid: - "467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5", - vout: 1, - amount: 0.00000546, - satoshis: 546, - height: 568215, - confirmations: 24 - } - ], - legacyAddress: "1NM2ozrXVSnMRm66ua6aGeXgMsU7yqwqLS", - cashAddress: "bitcoincash:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3g4hl47n9y", - scriptPubKey: "76a914ea2478cbb9f44100b547cf264d34155dce044b8a88ac" - } - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - SLP.Address.utxo("simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6") - .then(result => { - assert.deepEqual( - "467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5", - result.utxos[0].txid - ) - }) - .then(done, done) - }) -}) - -describe("#unconfirmed", () => { - let sandbox - beforeEach(() => (sandbox = sinon.sandbox.create())) - afterEach(() => sandbox.restore()) - - it("should get unconfirmed transactions", done => { - const data = { - utxos: [], - legacyAddress: "1NM2ozrXVSnMRm66ua6aGeXgMsU7yqwqLS", - cashAddress: "bitcoincash:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3g4hl47n9y", - slpAddress: "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6", - scriptPubKey: "76a914ea2478cbb9f44100b547cf264d34155dce044b8a88ac" - } - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - SLP.Address.unconfirmed( - "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6" - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) -}) -*/ - -// (async () => { -// try { -// const details = await SLP.Address.unconfirmed( -// "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6" -// ) -// console.log(details) -// } catch (error) { -// console.error(error) -// } -// })() diff --git a/test/unit/slp-ecpair.js b/test/unit/slp-ecpair.js deleted file mode 100644 index 3b77972..0000000 --- a/test/unit/slp-ecpair.js +++ /dev/null @@ -1,17 +0,0 @@ -const fixtures = require("./fixtures/slp/ecpair.json") -const assert = require("assert") - -const SLP = require("../../src/slp/slp") -const slp = new SLP({ restURL: "http://fakeurl.com/" }) - -describe("#SLP ECPair", () => { - describe("#toSLPAddress", () => { - it(`should return slp address for ecpair`, async () => { - fixtures.wif.forEach((wif, index) => { - const ecpair = slp.ECPair.fromWIF(wif) - const slpAddr = slp.ECPair.toSLPAddress(ecpair) - assert.equal(slpAddr, fixtures.address[index]) - }) - }) - }) -}) diff --git a/test/unit/slp-nft1.js b/test/unit/slp-nft1.js deleted file mode 100644 index 9e9640b..0000000 --- a/test/unit/slp-nft1.js +++ /dev/null @@ -1,264 +0,0 @@ -/* - Unit tests for the TokenType1 library. -*/ - -const assert = require("chai").assert -const nock = require("nock") // http call mocking -const sinon = require("sinon") -// const axios = require("axios") - -// Default to unit tests unless some other value for TEST is passed. -if (!process.env.TEST) process.env.TEST = "unit" -// const SERVER = bchjs.restURL - -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() - -// Mock data used for unit tests -// const mockData = require("./fixtures/slp/mock-utils") - -// Default to unit tests unless some other value for TEST is passed. -if (!process.env.TEST) process.env.TEST = "unit" - -describe("#SLP NFT1", () => { - let sandbox - - beforeEach(() => { - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - - sandbox = sinon.createSandbox() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - - sandbox.restore() - }) - - describe("#newNFTGroupOpReturn", () => { - it("should generate new NFT Group OP_RETURN code", () => { - const configObj = { - name: "SLP Test Token", - ticker: "SLPTEST", - documentUrl: "https://bchjs.cash", - documentHash: "", - initialQty: 5 - } - - const result = bchjs.SLP.NFT1.newNFTGroupOpReturn(configObj) - // console.log(`result: `, result) - - assert.equal(Buffer.isBuffer(result), true) - }) - }) - - describe("#mintNFTGroupOpReturn", () => { - it("should generate NFT Group Mint OP_RETURN code", () => { - const tokenUtxoData = [ - { - txid: - "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76", - vout: 2, - value: "546", - height: 638207, - confirmations: 63, - satoshis: 546, - utxoType: "minting-baton", - transactionType: "mint", - tokenId: - "680967b3f6fe080dbca8dbd370665bd29742e3490db24e2f28d08b424511807e", - tokenType: 129, - tokenTicker: "NFTP", - tokenName: "NFT Parent", - tokenDocumentUrl: "FullStack.cash", - tokenDocumentHash: "", - decimals: 0, - mintBatonVout: 2, - isValid: true - }, - { - txid: - "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76", - vout: 1, - value: "546", - height: 638207, - confirmations: 63, - satoshis: 546, - utxoType: "token", - tokenQty: 10, - transactionType: "mint", - tokenId: - "680967b3f6fe080dbca8dbd370665bd29742e3490db24e2f28d08b424511807e", - tokenType: 129, - tokenTicker: "NFTP", - tokenName: "NFT Parent", - tokenDocumentUrl: "FullStack.cash", - tokenDocumentHash: "", - decimals: 0, - mintBatonVout: 2, - isValid: true - } - ] - - const result = bchjs.SLP.NFT1.mintNFTGroupOpReturn(tokenUtxoData, 10) - // console.log(`result: `, result) - - assert.equal(Buffer.isBuffer(result), true) - }) - }) - - describe("#generateNFTChildOpReturn", () => { - it("should generate NFT Genesis OP_RETURN code", () => { - const configObj = { - name: "SLP Test Token", - ticker: "SLPTEST", - documentUrl: "https://bchjs.cash", - documentHash: "" - } - - const result = bchjs.SLP.NFT1.generateNFTChildGenesisOpReturn(configObj) - // console.log(`result: `, result) - - assert.equal(Buffer.isBuffer(result), true) - }) - }) - - describe("#generateNFTChildSendOpReturn", () => { - it("should generate send OP_RETURN code for no change", () => { - // Mock UTXO. - const tokenUtxos = [ - { - txid: - "81955624a8eb7011769ff5faa607f78f84b0f8152b9145e7db8b1e521c895ee3", - vout: 1, - value: "546", - height: 638273, - confirmations: 42, - satoshis: 546, - utxoType: "token", - tokenQty: 1, - tokenId: - "81955624a8eb7011769ff5faa607f78f84b0f8152b9145e7db8b1e521c895ee3", - tokenTicker: "NFTC", - tokenName: "NFT Child", - tokenDocumentUrl: "https://FullStack.cash", - tokenDocumentHash: "", - decimals: 0, - tokenType: 129, - isValid: true - } - ] - - const result = bchjs.SLP.NFT1.generateNFTChildSendOpReturn(tokenUtxos, 1) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAllKeys(result, ["script", "outputs"]) - assert.isNumber(result.outputs) - }) - - it("should generate send OP_RETURN code with change", () => { - // Mock UTXO. - const tokenUtxos = [ - { - txid: - "81955624a8eb7011769ff5faa607f78f84b0f8152b9145e7db8b1e521c895ee3", - vout: 1, - value: "546", - height: 638273, - confirmations: 42, - satoshis: 546, - utxoType: "token", - tokenQty: 4, - tokenId: - "81955624a8eb7011769ff5faa607f78f84b0f8152b9145e7db8b1e521c895ee3", - tokenTicker: "NFTC", - tokenName: "NFT Child", - tokenDocumentUrl: "https://FullStack.cash", - tokenDocumentHash: "", - decimals: 0, - tokenType: 129, - isValid: true - } - ] - - const result = bchjs.SLP.NFT1.generateNFTChildSendOpReturn(tokenUtxos, 1) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAllKeys(result, ["script", "outputs"]) - assert.isNumber(result.outputs) - }) - }) - - describe("#generateNFTGroupSendOpReturn", () => { - it("should generate send OP_RETURN with change", () => { - // Mock UTXO. - const tokenUtxos = [ - { - txid: - "35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919", - vout: 1, - value: "546", - height: 638207, - confirmations: 3, - satoshis: 546, - utxoType: "token", - tokenQty: 10, - transactionType: "mint", - tokenId: - "eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981", - tokenType: 129, - tokenTicker: "NFTTT", - tokenName: "NFT Test Token", - tokenDocumentUrl: "https://FullStack.cash", - tokenDocumentHash: "", - decimals: 0, - mintBatonVout: 2, - isValid: true - } - ] - - const result = bchjs.SLP.NFT1.generateNFTGroupSendOpReturn(tokenUtxos, 1) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAllKeys(result, ["script", "outputs"]) - assert.isNumber(result.outputs) - }) - - it("should generate send OP_RETURN with no change", () => { - // Mock UTXO. - const tokenUtxos = [ - { - txid: - "35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919", - vout: 1, - value: "546", - height: 638207, - confirmations: 3, - satoshis: 546, - utxoType: "token", - tokenQty: 10, - transactionType: "mint", - tokenId: - "eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981", - tokenType: 129, - tokenTicker: "NFTTT", - tokenName: "NFT Test Token", - tokenDocumentUrl: "https://FullStack.cash", - tokenDocumentHash: "", - decimals: 0, - mintBatonVout: 2, - isValid: true - } - ] - - const result = bchjs.SLP.NFT1.generateNFTGroupSendOpReturn(tokenUtxos, 10) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAllKeys(result, ["script", "outputs"]) - assert.isNumber(result.outputs) - }) - }) -}) diff --git a/test/unit/slp-tokentype1.js b/test/unit/slp-tokentype1.js deleted file mode 100644 index 23fa94f..0000000 --- a/test/unit/slp-tokentype1.js +++ /dev/null @@ -1,319 +0,0 @@ -/* - Unit tests for the TokenType1 library. -*/ - -const assert = require("chai").assert -const nock = require("nock") // http call mocking -const sinon = require("sinon") -// const axios = require("axios") - -// Default to unit tests unless some other value for TEST is passed. -if (!process.env.TEST) process.env.TEST = "unit" -// const SERVER = bchjs.restURL - -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() - -// Mock data used for unit tests -// const mockData = require("./fixtures/slp/mock-utils") - -// Default to unit tests unless some other value for TEST is passed. -if (!process.env.TEST) process.env.TEST = "unit" - -describe("#SLP TokenType1", () => { - let sandbox - - beforeEach(() => { - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - - sandbox = sinon.createSandbox() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - - sandbox.restore() - }) - - describe("#generateSendOpReturn", () => { - it("should generate send OP_RETURN code", () => { - // Mock UTXO. - const tokenUtxos = [ - { - txid: - "a8eb788b8ddda6faea00e6e2756624b8feb97655363d0400dd66839ea619d36e", - vout: 2, - value: "546", - confirmations: 0, - satoshis: 546, - utxoType: "token", - transactionType: "send", - tokenId: - "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", - tokenTicker: "TOK-CH", - tokenName: "TokyoCash", - tokenDocumentUrl: "", - tokenDocumentHash: "", - decimals: 8, - tokenQty: 7 - } - ] - - const result = bchjs.SLP.TokenType1.generateSendOpReturn(tokenUtxos, 1) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAllKeys(result, ["script", "outputs"]) - assert.isNumber(result.outputs) - }) - }) - - describe("#generateBurnOpReturn", () => { - it("should generate burn OP_RETURN code", () => { - // Mock UTXO. - const tokenUtxos = [ - { - txid: - "a8eb788b8ddda6faea00e6e2756624b8feb97655363d0400dd66839ea619d36e", - vout: 2, - value: "546", - confirmations: 0, - satoshis: 546, - utxoType: "token", - transactionType: "send", - tokenId: - "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", - tokenTicker: "TOK-CH", - tokenName: "TokyoCash", - tokenDocumentUrl: "", - tokenDocumentHash: "", - decimals: 8, - tokenQty: 7 - } - ] - - const result = bchjs.SLP.TokenType1.generateBurnOpReturn(tokenUtxos, 1) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - // console.log(`result: `, result) - - assert.equal(Buffer.isBuffer(result), true) - }) - }) - - describe("#generateGenesisOpReturn", () => { - it("should generate genesis OP_RETURN code", () => { - const configObj = { - name: "SLP Test Token", - ticker: "SLPTEST", - documentUrl: "https://bchjs.cash", - documentHash: "", - decimals: 8, - initialQty: 10 - } - - const result = bchjs.SLP.TokenType1.generateGenesisOpReturn(configObj) - // console.log(`result: `, result) - - assert.equal(Buffer.isBuffer(result), true) - }) - - it("should work if user does not specify doc hash", () => { - const configObj = { - name: "SLP Test Token", - ticker: "SLPTEST", - documentUrl: "https://bchjs.cash", - decimals: 8, - initialQty: 10 - } - - const result = bchjs.SLP.TokenType1.generateGenesisOpReturn(configObj) - // console.log(`result: `, result) - - assert.equal(Buffer.isBuffer(result), true) - }) - }) - - describe("#generateMintOpReturn", () => { - it("should throw error if tokenUtxos is not an array.", () => { - try { - bchjs.SLP.TokenType1.generateMintOpReturn({}, 100) - - assert.equal(true, false, "Unexpected result.") - } catch (err) { - assert.include( - err.message, - `tokenUtxos must be an array`, - "Expected error message." - ) - } - }) - - it("should throw error if minting baton is not in UTXOs.", () => { - try { - const utxos = [ - { - txid: - "ccc6d336399e26d98afcd3821b41fb1535cd50f57063ed7593eaed5108659606", - vout: 1, - value: "546", - height: 637618, - confirmations: 239, - satoshis: 546, - utxoType: "token", - tokenQty: 100, - tokenId: - "ccc6d336399e26d98afcd3821b41fb1535cd50f57063ed7593eaed5108659606", - tokenTicker: "SLPTEST", - tokenName: "SLP Test Token", - tokenDocumentUrl: "https://FullStack.cash", - tokenDocumentHash: "", - decimals: 8, - isValid: true - } - ] - - bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) - - assert.equal(true, false, "Unexpected result.") - } catch (err) { - assert.include( - err.message, - `Minting baton could not be found in tokenUtxos array`, - "Expected error message." - ) - } - }) - - it("should throw error if tokenId is not included in minting-baton UTXO.", () => { - try { - const utxos = [ - { - txid: - "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", - vout: 2, - value: "546", - height: 637625, - confirmations: 207, - satoshis: 546, - utxoType: "minting-baton", - tokenTicker: "SLPTEST", - tokenName: "SLP Test Token", - tokenDocumentUrl: "https://FullStack.cash", - tokenDocumentHash: "", - decimals: 8, - isValid: true - } - ] - - bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) - - assert.equal(true, false, "Unexpected result.") - } catch (err) { - assert.include( - err.message, - `tokenId property not found in mint-baton UTXO`, - "Expected error message." - ) - } - }) - - it("should throw error if decimals is not included in minting-baton UTXO.", () => { - try { - const utxos = [ - { - txid: - "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", - vout: 2, - value: "546", - height: 637625, - confirmations: 207, - satoshis: 546, - utxoType: "minting-baton", - tokenId: - "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", - tokenTicker: "SLPTEST", - tokenName: "SLP Test Token", - tokenDocumentUrl: "https://FullStack.cash", - tokenDocumentHash: "", - isValid: true - } - ] - - bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) - - assert.equal(true, false, "Unexpected result.") - } catch (err) { - assert.include( - err.message, - `decimals property not found in mint-baton UTXO`, - "Expected error message." - ) - } - }) - - it("should generate genesis OP_RETURN code", () => { - tokenUtxo = [ - { - txid: - "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", - vout: 2, - value: "546", - height: 637625, - confirmations: 207, - satoshis: 546, - utxoType: "minting-baton", - tokenId: - "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", - tokenTicker: "SLPTEST", - tokenName: "SLP Test Token", - tokenDocumentUrl: "https://FullStack.cash", - tokenDocumentHash: "", - decimals: 8, - isValid: true - } - ] - - const result = bchjs.SLP.TokenType1.generateMintOpReturn(tokenUtxo, 100) - // console.log(`result: `, result) - - assert.equal(Buffer.isBuffer(result), true) - }) - }) - - describe("#getHexOpReturn", () => { - it("should return OP_RETURN object ", async () => { - const tokenUtxos = [ - { - tokenId: - "0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1", - decimals: 0, - tokenQty: 2 - } - ] - - const sendQty = 1.5 - - sandbox.stub(bchjs.SLP.TokenType1.axios, "post").resolves({ - data: { - script: - "6a04534c500001010453454e44200a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1080000000000000001080000000000000000", - outputs: 2 - } - }) - - const result = await bchjs.SLP.TokenType1.getHexOpReturn( - tokenUtxos, - sendQty - ) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, "script") - assert.isString(result.script) - - assert.property(result, "outputs") - assert.isNumber(result.outputs) - }) - }) -}) diff --git a/test/unit/transaction-builder.js b/test/unit/transaction-builder.js deleted file mode 100644 index aa5f29f..0000000 --- a/test/unit/transaction-builder.js +++ /dev/null @@ -1,2039 +0,0 @@ -const fixtures = require("./fixtures/transaction-builder.json") -const assert = require("assert") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const Buffer = require("safe-buffer").Buffer - -describe("#TransactionBuilder", () => { - describe("#hashTypes", () => { - const transactionBuilder = new bchjs.TransactionBuilder("mainnet") - fixtures.hashTypes.forEach(fixture => { - it(`should match hash type`, () => { - assert.equal( - fixture[Object.keys(fixture)[0]], - transactionBuilder.hashTypes[Object.keys(fixture)[0]] - ) - }) - }) - }) - - describe("#P2PK", () => { - describe("#toOne", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2pk.toOne.mainnet.forEach(fixture => { - it(`should create 1-to-1 P2PK transaction on mainnet`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder() - const originalAmount = fixture.amount - const txid = fixture.txHash - const pubKey = bchjs.HDNode.toPublicKey(node) - const buf = bchjs.Script.pubKey.output.encode(pubKey) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf, sendAmount) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.scripts.p2pk.toOne.testnet.forEach(fixture => { - it(`should create 1-to-1 P2PK transaction on testnet`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const originalAmount = fixture.amount - const txid = fixture.txHash - const pubKey = bchjs.HDNode.toPublicKey(node) - const buf = bchjs.Script.pubKey.output.encode(pubKey) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf, sendAmount) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - }) - - describe("#toMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2pk.toMany.mainnet.forEach(fixture => { - it(`should create 1-to-many P2PK transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const transactionBuilder = new bchjs.TransactionBuilder() - const originalAmount = fixture.amount - const txid = fixture.txHash - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) - const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) - const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf2, Math.floor(sendAmount / 2)) - transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) - const keyPair = bchjs.HDNode.toKeyPair(node1) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.scripts.p2pk.toMany.testnet.forEach(fixture => { - it(`should create 1-to-many P2PK transaction on testnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const originalAmount = fixture.amount - const txid = fixture.txHash - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) - const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) - const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf2, Math.floor(sendAmount / 2)) - transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) - const keyPair = bchjs.HDNode.toKeyPair(node1) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - }) - - describe("#manyToMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2pk.manyToMany.mainnet.forEach(fixture => { - it(`should create many-to-many P2PK transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) - const transactionBuilder = new bchjs.TransactionBuilder() - const originalAmount = fixture.amount - const txid = fixture.txHash - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const pubKey4 = bchjs.HDNode.toPublicKey(node4) - const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) - const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) - const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) - const buf4 = bchjs.Script.pubKey.output.encode(pubKey4) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - transactionBuilder.addInput( - txid, - 1, - transactionBuilder.DEFAULT_SEQUENCE, - buf2 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) - transactionBuilder.addOutput(buf4, Math.floor(sendAmount / 2)) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.scripts.p2pk.manyToMany.testnet.forEach(fixture => { - it(`should create many-to-many P2PK transaction on testnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const originalAmount = fixture.amount - const txid = fixture.txHash - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const pubKey4 = bchjs.HDNode.toPublicKey(node4) - const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) - const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) - const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) - const buf4 = bchjs.Script.pubKey.output.encode(pubKey4) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - transactionBuilder.addInput( - txid, - 1, - transactionBuilder.DEFAULT_SEQUENCE, - buf2 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) - transactionBuilder.addOutput(buf4, Math.floor(sendAmount / 2)) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - }) - - describe("#fromMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2pk.fromMany.mainnet.forEach(fixture => { - it(`should create many-to-1 P2PK transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const transactionBuilder = new bchjs.TransactionBuilder() - const originalAmount = fixture.amount - const txid = fixture.txHash - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) - const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) - const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - transactionBuilder.addInput( - txid, - 1, - transactionBuilder.DEFAULT_SEQUENCE, - buf2 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf3, sendAmount) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.scripts.p2pk.fromMany.testnet.forEach(fixture => { - it(`should create many-to-1 P2PK transaction on testnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const originalAmount = fixture.amount - const txid = fixture.txHash - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) - const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) - const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - transactionBuilder.addInput( - txid, - 1, - transactionBuilder.DEFAULT_SEQUENCE, - buf2 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf3, sendAmount) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - }) - }) - - describe("#P2PKH", () => { - describe("#toOne", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2pkh.toOne.mainnet.forEach(fixture => { - it(`should create 1-to-1 P2PKH transaction on mainnet`, () => { - const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder() - const keyPair = bchjs.HDNode.toKeyPair(hdnode) - const txHash = fixture.txHash - // original amount of satoshis in vin - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, fixture.vout) - // get byte count to calculate fee. paying 1 sat/byte - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 1 } - ) - // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - const sendAmount = originalAmount - byteCount - // add output w/ address and amount to send - let redeemScript - transactionBuilder.addOutput(fixture.outputs[0], sendAmount) - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - - // build tx - const tx = transactionBuilder.build() - // output rawhex - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.scripts.p2pkh.toOne.testnet.forEach(fixture => { - it(`should create 1-to-1 P2PKH transaction on testnet`, () => { - const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv, "testnet") - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const keyPair = bchjs.HDNode.toKeyPair(hdnode) - const txHash = fixture.txHash - // original amount of satoshis in vin - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, fixture.vout) - // get byte count to calculate fee. paying 1 sat/byte - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 1 } - ) - // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - const sendAmount = originalAmount - byteCount * 15 - // add output w/ address and amount to send - let redeemScript - transactionBuilder.addOutput(fixture.outputs[0], sendAmount) - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - - // build tx - const tx = transactionBuilder.build() - // output rawhex - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - /* - describe("#RegTest", () => { - fixtures.scripts.p2pkh.toOne.regtest.forEach(fixture => { - it(`should create 1-to-1 P2PKH transaction on regtest`, () => { - const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder("regtest") - const keyPair = bchjs.HDNode.toKeyPair(hdnode) - const txHash = fixture.txHash - // original amount of satoshis in vin - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, fixture.vout) - // get byte count to calculate fee. paying 1 sat/byte - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 1 } - ) - // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - const sendAmount = originalAmount - byteCount * 15 - // add output w/ address and amount to send - let redeemScript - transactionBuilder.addOutput(fixture.outputs[0], sendAmount) - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - - // build tx - const tx = transactionBuilder.build() - // output rawhex - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - */ - }) - - describe("#toMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2pkh.toMany.mainnet.forEach(fixture => { - it(`should create 1-to-2 P2PKH transaction on mainnet`, () => { - const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder() - const keyPair = bchjs.HDNode.toKeyPair(hdnode) - const txHash = fixture.txHash - // original amount of satoshis in vin - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, fixture.vout) - // get byte count to calculate fee. paying 1 sat/byte - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 2 } - ) - // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - const sendAmount = originalAmount - byteCount - // add output w/ address and amount to send - transactionBuilder.addOutput( - fixture.outputs[0], - Math.floor(sendAmount / 2) - ) - transactionBuilder.addOutput( - fixture.outputs[1], - Math.floor(sendAmount / 2) - ) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - // build tx - const tx = transactionBuilder.build() - // output rawhex - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.scripts.p2pkh.toMany.testnet.forEach(fixture => { - // TODO pass in tesnet network config - it(`should create 1-to-2 P2PKH transaction on testnet`, () => { - const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const keyPair = bchjs.HDNode.toKeyPair(hdnode) - const txHash = fixture.txHash - // original amount of satoshis in vin - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, fixture.vout) - // get byte count to calculate fee. paying 1 sat/byte - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 2 } - ) - // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - const sendAmount = originalAmount - byteCount * 15 - // add output w/ address and amount to send - transactionBuilder.addOutput( - fixture.outputs[0], - Math.floor(sendAmount / 2) - ) - transactionBuilder.addOutput( - fixture.outputs[1], - Math.floor(sendAmount / 2) - ) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - // build tx - const tx = transactionBuilder.build() - // output rawhex - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - /* - describe("#RegTest", () => { - fixtures.scripts.p2pkh.toMany.regtest.forEach(fixture => { - // TODO pass in tesnet network config - it(`should create 1-to-2 P2PKH transaction on regtest`, () => { - const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder("regtest") - const keyPair = bchjs.HDNode.toKeyPair(hdnode) - const txHash = fixture.txHash - // original amount of satoshis in vin - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, fixture.vout) - // get byte count to calculate fee. paying 1 sat/byte - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 2 } - ) - // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - const sendAmount = originalAmount - byteCount * 15 - // add output w/ address and amount to send - transactionBuilder.addOutput( - fixture.outputs[0], - Math.floor(sendAmount / 2) - ) - transactionBuilder.addOutput( - fixture.outputs[1], - Math.floor(sendAmount / 2) - ) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - // build tx - const tx = transactionBuilder.build() - // output rawhex - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - })*/ - }) - - describe("#manyToMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2pkh.manyToMany.mainnet.forEach(fixture => { - it(`should create 2-to-2 P2PKH transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txHash = fixture.txHash - const originalAmount = fixture.amounts[0] + fixture.amounts[1] - transactionBuilder.addInput(txHash, 0) - transactionBuilder.addInput(txHash, 1) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 2 }, - { P2PKH: 2 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput( - fixture.outputs[0], - Math.floor(sendAmount / 2) - ) - transactionBuilder.addOutput( - fixture.outputs[1], - Math.floor(sendAmount / 2) - ) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[0] - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[1] - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.scripts.p2pkh.manyToMany.testnet.forEach(fixture => { - it(`should create 2-to-2 P2PKH transaction on testnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const txHash = fixture.txHash - const originalAmount = fixture.amounts[0] + fixture.amounts[1] - transactionBuilder.addInput(txHash, 0) - transactionBuilder.addInput(txHash, 1) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 2 }, - { P2PKH: 2 } - ) - const sendAmount = originalAmount - byteCount * 15 - transactionBuilder.addOutput( - fixture.outputs[0], - Math.floor(sendAmount / 2) - ) - transactionBuilder.addOutput( - fixture.outputs[1], - Math.floor(sendAmount / 2) - ) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[0] - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[1] - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - /* - describe("#RegTest", () => { - fixtures.scripts.p2pkh.manyToMany.regtest.forEach(fixture => { - it(`should create 2-to-2 P2PKH transaction on regtest`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const transactionBuilder = new bchjs.TransactionBuilder("regtest") - const txHash = fixture.txHash - const originalAmount = fixture.amounts[0] + fixture.amounts[1] - transactionBuilder.addInput(txHash, 0) - transactionBuilder.addInput(txHash, 1) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 2 }, - { P2PKH: 2 } - ) - const sendAmount = originalAmount - byteCount * 15 - transactionBuilder.addOutput( - fixture.outputs[0], - Math.floor(sendAmount / 2) - ) - transactionBuilder.addOutput( - fixture.outputs[1], - Math.floor(sendAmount / 2) - ) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[0] - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[1] - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - */ - }) - - describe("#fromMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2pkh.fromMany.mainnet.forEach(fixture => { - it(`should create 2-to-1 P2PKH transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txHash = fixture.txHash - const originalAmount = fixture.amounts[0] + fixture.amounts[1] - transactionBuilder.addInput(txHash, 0) - transactionBuilder.addInput(txHash, 1) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 2 }, - { P2PKH: 1 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(fixture.outputs[0], sendAmount) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[0] - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[1] - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.scripts.p2pkh.fromMany.testnet.forEach(fixture => { - it(`should create 2-to-1 P2PKH transaction on testnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const txHash = fixture.txHash - const originalAmount = fixture.amounts[0] + fixture.amounts[1] - transactionBuilder.addInput(txHash, 0) - transactionBuilder.addInput(txHash, 1) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 2 }, - { P2PKH: 1 } - ) - const sendAmount = originalAmount - byteCount * 15 - transactionBuilder.addOutput(fixture.outputs[0], sendAmount) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[0] - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[1] - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - /* - describe("#RegTest", () => { - fixtures.scripts.p2pkh.fromMany.regtest.forEach(fixture => { - it(`should create 2-to-1 P2PKH transaction on regtest`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const transactionBuilder = new bchjs.TransactionBuilder("regtest") - const txHash = fixture.txHash - const originalAmount = fixture.amounts[0] + fixture.amounts[1] - transactionBuilder.addInput(txHash, 0) - transactionBuilder.addInput(txHash, 1) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 2 }, - { P2PKH: 1 } - ) - const sendAmount = originalAmount - byteCount * 15 - transactionBuilder.addOutput(fixture.outputs[0], sendAmount) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - let redeemScript - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[0] - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amounts[1] - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - */ - }) - }) - - describe("#op_return", () => { - describe("#Mainnet", () => { - fixtures.nulldata.mainnet.forEach(fixture => { - it(`should create transaction w/ OP_RETURN output on mainnet`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder() - const txHash = fixture.txHash - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, 0) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 5 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(fixture.output, sendAmount) - const data = fixture.data - const buf = bchjs.Script.nullData.output.encode( - Buffer.from(data, "ascii") - ) - transactionBuilder.addOutput(buf, 0) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.nulldata.testnet.forEach(fixture => { - it(`should create transaction w/ OP_RETURN output on testnet`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const txHash = fixture.txHash - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, 0) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 5 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(fixture.output, sendAmount) - const data = fixture.data - const buf = bchjs.Script.nullData.output.encode( - Buffer.from(data, "ascii") - ) - transactionBuilder.addOutput(buf, 0) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - /* - describe("#RegTest", () => { - fixtures.nulldata.regtest.forEach(fixture => { - it(`should create transaction w/ OP_RETURN output on regtest`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder("regtest") - const txHash = fixture.txHash - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, 0) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 5 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(fixture.output, sendAmount) - const data = fixture.data - const buf = bchjs.Script.nullData.output.encode( - Buffer.from(data, "ascii") - ) - transactionBuilder.addOutput(buf, 0) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - */ - }) - - describe("#P2MS", () => { - describe("#toOne", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2ms.toOne.mainnet.forEach(fixture => { - it(`should create 1-to-1 1-of-2 P2MS transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txid = fixture.txHash - const originalAmount = fixture.amount - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const buf1 = bchjs.Script.multisig.output.encode(1, [ - pubKey1, - pubKey2 - ]) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const pubKey4 = bchjs.HDNode.toPublicKey(node4) - const buf2 = bchjs.Script.multisig.output.encode(1, [ - pubKey3, - pubKey4 - ]) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(buf2, sendAmount) - let redeemScript - const keyPair = bchjs.HDNode.toKeyPair(node1) - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - // describe('#Testnet', () => { - // fixtures.scripts.p2ms.toOne.testnet.forEach((fixture) => { - // it(`should create 1-to-1 P2MS transaction on testnet`, () => { - // let hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv, 'testnet'); - // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); - // let keyPair = bchjs.HDNode.toKeyPair(hdnode); - // let txHash = fixture.txHash; - // // original amount of satoshis in vin - // let originalAmount = fixture.amount; - // transactionBuilder.addInput(txHash, fixture.vout); - // // get byte count to calculate fee. paying 1 sat/byte - // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 1 }); - // // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - // let sendAmount = originalAmount - (byteCount * 15); - // // add output w/ address and amount to send - // let redeemScript - // transactionBuilder.addOutput(fixture.outputs[0], sendAmount); - // transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount); - // - // // build tx - // let tx = transactionBuilder.build(); - // // output rawhex - // let hex = tx.toHex(); - // assert.equal(hex, fixture.hex); - // }); - // }); - // }); - }) - - describe("#toMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2ms.toMany.mainnet.forEach(fixture => { - it(`should create 1-to-2 P2MS transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) - const node5 = bchjs.HDNode.fromXPriv(fixture.xprivs[4]) - const node6 = bchjs.HDNode.fromXPriv(fixture.xprivs[5]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txid = fixture.txHash - const originalAmount = fixture.amount - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const buf1 = bchjs.Script.multisig.output.encode(1, [ - pubKey1, - pubKey2 - ]) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const pubKey4 = bchjs.HDNode.toPublicKey(node4) - const buf2 = bchjs.Script.multisig.output.encode(1, [ - pubKey3, - pubKey4 - ]) - transactionBuilder.addOutput(buf2, Math.floor(sendAmount / 2)) - const pubKey5 = bchjs.HDNode.toPublicKey(node5) - const pubKey6 = bchjs.HDNode.toPublicKey(node6) - const buf3 = bchjs.Script.multisig.output.encode(1, [ - pubKey5, - pubKey6 - ]) - transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) - let redeemScript - const keyPair = bchjs.HDNode.toKeyPair(node1) - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - // describe('#Testnet', () => { - // fixtures.scripts.p2ms.toMany.testnet.forEach((fixture) => { - // // TODO pass in tesnet network config - // it(`should create 1-to-2 P2MS transaction on testnet`, () => { - // let hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv); - // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); - // let keyPair = bchjs.HDNode.toKeyPair(hdnode); - // let txHash = fixture.txHash; - // // original amount of satoshis in vin - // let originalAmount = fixture.amount; - // transactionBuilder.addInput(txHash, fixture.vout); - // // get byte count to calculate fee. paying 1 sat/byte - // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 2 }); - // // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - // let sendAmount = originalAmount - (byteCount * 15); - // // add output w/ address and amount to send - // transactionBuilder.addOutput(fixture.outputs[0], Math.floor(sendAmount / 2)); - // transactionBuilder.addOutput(fixture.outputs[1], Math.floor(sendAmount / 2)); - // let redeemScript - // transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount); - // // build tx - // let tx = transactionBuilder.build(); - // // output rawhex - // let hex = tx.toHex(); - // assert.equal(hex, fixture.hex); - // }); - // }); - // }); - }) - - describe("#manyToMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2ms.manyToMany.mainnet.forEach(fixture => { - it(`should create 2-to-2 P2MS transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) - const node5 = bchjs.HDNode.fromXPriv(fixture.xprivs[4]) - const node6 = bchjs.HDNode.fromXPriv(fixture.xprivs[5]) - const node7 = bchjs.HDNode.fromXPriv(fixture.xprivs[6]) - const node8 = bchjs.HDNode.fromXPriv(fixture.xprivs[7]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txid = fixture.txHash - const originalAmount = fixture.amount - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const buf1 = bchjs.Script.multisig.output.encode(1, [ - pubKey1, - pubKey2 - ]) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const pubKey4 = bchjs.HDNode.toPublicKey(node4) - const buf2 = bchjs.Script.multisig.output.encode(1, [ - pubKey3, - pubKey4 - ]) - transactionBuilder.addInput( - txid, - 1, - transactionBuilder.DEFAULT_SEQUENCE, - buf2 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - const pubKey5 = bchjs.HDNode.toPublicKey(node5) - const pubKey6 = bchjs.HDNode.toPublicKey(node6) - const buf3 = bchjs.Script.multisig.output.encode(1, [ - pubKey5, - pubKey6 - ]) - transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) - const pubKey7 = bchjs.HDNode.toPublicKey(node7) - const pubKey8 = bchjs.HDNode.toPublicKey(node8) - const buf4 = bchjs.Script.multisig.output.encode(1, [ - pubKey7, - pubKey8 - ]) - transactionBuilder.addOutput(buf4, Math.floor(sendAmount / 2)) - let redeemScript - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node3) - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - // describe('#Testnet', () => { - // fixtures.scripts.p2ms.manyToMany.testnet.forEach((fixture) => { - // it(`should create 2-to-2 P2MS transaction on testnet`, () => { - // let node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]); - // let node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]); - // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); - // let txHash = fixture.txHash; - // let originalAmount = fixture.amounts[0] + fixture.amounts[1]; - // transactionBuilder.addInput(txHash, 0); - // transactionBuilder.addInput(txHash, 1); - // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 2 }); - // let sendAmount = originalAmount - (byteCount * 15); - // transactionBuilder.addOutput(fixture.outputs[0], Math.floor(sendAmount / 2)); - // transactionBuilder.addOutput(fixture.outputs[1], Math.floor(sendAmount / 2)); - // let keyPair1 = bchjs.HDNode.toKeyPair(node1); - // let keyPair2 = bchjs.HDNode.toKeyPair(node2); - // let redeemScript; - // transactionBuilder.sign(0, keyPair1, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[0]); - // transactionBuilder.sign(1, keyPair2, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[1]); - // let tx = transactionBuilder.build(); - // let hex = tx.toHex(); - // assert.equal(hex, fixture.hex); - // }); - // }); - // }); - }) - - describe("#fromMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2ms.fromMany.mainnet.forEach(fixture => { - it(`should create 2-to-1 P2MS transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) - const node5 = bchjs.HDNode.fromXPriv(fixture.xprivs[4]) - const node6 = bchjs.HDNode.fromXPriv(fixture.xprivs[5]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txid = fixture.txHash - const originalAmount = fixture.amount - const pubKey1 = bchjs.HDNode.toPublicKey(node1) - const pubKey2 = bchjs.HDNode.toPublicKey(node2) - const buf1 = bchjs.Script.multisig.output.encode(1, [ - pubKey1, - pubKey2 - ]) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - buf1 - ) - const pubKey3 = bchjs.HDNode.toPublicKey(node3) - const pubKey4 = bchjs.HDNode.toPublicKey(node4) - const buf2 = bchjs.Script.multisig.output.encode(1, [ - pubKey3, - pubKey4 - ]) - transactionBuilder.addInput( - txid, - 1, - transactionBuilder.DEFAULT_SEQUENCE, - buf2 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 3 } - ) - const sendAmount = originalAmount - byteCount - const pubKey5 = bchjs.HDNode.toPublicKey(node5) - const pubKey6 = bchjs.HDNode.toPublicKey(node6) - const buf3 = bchjs.Script.multisig.output.encode(1, [ - pubKey5, - pubKey6 - ]) - transactionBuilder.addOutput(buf3, sendAmount) - let redeemScript - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node3) - transactionBuilder.sign( - 0, - keyPair1, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - transactionBuilder.sign( - 1, - keyPair2, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - // describe('#Testnet', () => { - // fixtures.scripts.p2ms.fromMany.testnet.forEach((fixture) => { - // it(`should create 2-to-1 P2MS transaction on testnet`, () => { - // let node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]); - // let node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]); - // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); - // let txHash = fixture.txHash; - // let originalAmount = fixture.amounts[0] + fixture.amounts[1]; - // transactionBuilder.addInput(txHash, 0); - // transactionBuilder.addInput(txHash, 1); - // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 1 }); - // let sendAmount = originalAmount - (byteCount * 15); - // transactionBuilder.addOutput(fixture.outputs[0], sendAmount); - // let keyPair1 = bchjs.HDNode.toKeyPair(node1); - // let keyPair2 = bchjs.HDNode.toKeyPair(node2); - // let redeemScript; - // transactionBuilder.sign(0, keyPair1, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[0]); - // transactionBuilder.sign(1, keyPair2, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[1]); - // let tx = transactionBuilder.build(); - // let hex = tx.toHex(); - // assert.equal(hex, fixture.hex); - // }); - // }); - // }); - }) - }) - - describe("#P2SH", () => { - describe("#toOne", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2sh.toOne.mainnet.forEach(fixture => { - it(`should create 1-to-1 P2SH transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txid = fixture.txHash - const originalAmount = fixture.amount - const identifier1 = bchjs.HDNode.toIdentifier(node1) - const buf1 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier1, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash1 = bchjs.Crypto.hash160(buf1) - const data1 = bchjs.Script.scriptHash.output.encode(scriptHash1) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - data1 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 2 }, - { P2PKH: 1 } - ) - const sendAmount = originalAmount - byteCount - const identifier2 = bchjs.HDNode.toIdentifier(node2) - const buf2 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier2, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - - const scriptHash2 = bchjs.Crypto.hash160(buf2) - const data2 = bchjs.Script.scriptHash.output.encode(scriptHash2) - transactionBuilder.addOutput(data2, sendAmount) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - transactionBuilder.sign( - 0, - keyPair1, - buf1, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - // describe('#Testnet', () => { - // fixtures.scripts.p2sh.toOne.testnet.forEach((fixture) => { - // it(`should create 1-to-1 P2SH transaction on testnet`, () => { - // let hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv, 'testnet'); - // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); - // let keyPair = bchjs.HDNode.toKeyPair(hdnode); - // let txHash = fixture.txHash; - // // original amount of satoshis in vin - // let originalAmount = fixture.amount; - // transactionBuilder.addInput(txHash, fixture.vout); - // // get byte count to calculate fee. paying 1 sat/byte - // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 1 }); - // // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - // let sendAmount = originalAmount - (byteCount * 15); - // // add output w/ address and amount to send - // let redeemScript - // transactionBuilder.addOutput(fixture.outputs[0], sendAmount); - // transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount); - // - // // build tx - // let tx = transactionBuilder.build(); - // // output rawhex - // let hex = tx.toHex(); - // assert.equal(hex, fixture.hex); - // }); - // }); - // }); - }) - - describe("#toMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2sh.toMany.mainnet.forEach(fixture => { - it(`should create 1-to-2 P2SH transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txid = fixture.txHash - const originalAmount = fixture.amount - const identifier1 = bchjs.HDNode.toIdentifier(node1) - const buf1 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier1, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash1 = bchjs.Crypto.hash160(buf1) - const data1 = bchjs.Script.scriptHash.output.encode(scriptHash1) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - data1 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 2 } - ) - const sendAmount = originalAmount - byteCount - const identifier2 = bchjs.HDNode.toIdentifier(node2) - const buf2 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier2, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash2 = bchjs.Crypto.hash160(buf2) - const data2 = bchjs.Script.scriptHash.output.encode(scriptHash2) - transactionBuilder.addOutput(data2, Math.floor(sendAmount / 2)) - const identifier3 = bchjs.HDNode.toIdentifier(node3) - const buf3 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier3, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash3 = bchjs.Crypto.hash160(buf3) - const data3 = bchjs.Script.scriptHash.output.encode(scriptHash3) - transactionBuilder.addOutput(data3, Math.floor(sendAmount / 2)) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - transactionBuilder.sign( - 0, - keyPair1, - buf1, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - // describe('#Testnet', () => { - // fixtures.scripts.p2sh.toMany.testnet.forEach((fixture) => { - // // TODO pass in tesnet network config - // it(`should create 1-to-2 P2SH transaction on testnet`, () => { - // let hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv); - // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); - // let keyPair = bchjs.HDNode.toKeyPair(hdnode); - // let txHash = fixture.txHash; - // // original amount of satoshis in vin - // let originalAmount = fixture.amount; - // transactionBuilder.addInput(txHash, fixture.vout); - // // get byte count to calculate fee. paying 1 sat/byte - // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 2 }); - // // amount to send to receiver. It's the original amount - 1 sat/byte for tx size - // let sendAmount = originalAmount - (byteCount * 15); - // // add output w/ address and amount to send - // transactionBuilder.addOutput(fixture.outputs[0], Math.floor(sendAmount / 2)); - // transactionBuilder.addOutput(fixture.outputs[1], Math.floor(sendAmount / 2)); - // let redeemScript - // transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount); - // // build tx - // let tx = transactionBuilder.build(); - // // output rawhex - // let hex = tx.toHex(); - // assert.equal(hex, fixture.hex); - // }); - // }); - // }); - }) - - describe("#manyToMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2sh.manyToMany.mainnet.forEach(fixture => { - it(`should create 2-to-2 P2SH transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txid = fixture.txHash - const originalAmount = fixture.amount - const identifier1 = bchjs.HDNode.toIdentifier(node1) - const buf1 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier1, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash1 = bchjs.Crypto.hash160(buf1) - const data1 = bchjs.Script.scriptHash.output.encode(scriptHash1) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - data1 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 5 }, - { P2PKH: 5 } - ) - const sendAmount = originalAmount - byteCount - const identifier2 = bchjs.HDNode.toIdentifier(node2) - const buf2 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier2, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash2 = bchjs.Crypto.hash160(buf2) - const data2 = bchjs.Script.scriptHash.output.encode(scriptHash2) - transactionBuilder.addInput( - txid, - 1, - transactionBuilder.DEFAULT_SEQUENCE, - data2 - ) - const identifier3 = bchjs.HDNode.toIdentifier(node3) - const buf3 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier3, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash3 = bchjs.Crypto.hash160(buf3) - const data3 = bchjs.Script.scriptHash.output.encode(scriptHash3) - transactionBuilder.addOutput(data3, Math.floor(sendAmount / 2)) - const identifier4 = bchjs.HDNode.toIdentifier(node4) - const buf4 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier4, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash4 = bchjs.Crypto.hash160(buf4) - const data4 = bchjs.Script.scriptHash.output.encode(scriptHash4) - transactionBuilder.addOutput(data4, Math.floor(sendAmount / 2)) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - transactionBuilder.sign( - 0, - keyPair1, - buf1, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - transactionBuilder.sign( - 1, - keyPair2, - buf2, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - // describe('#Testnet', () => { - // fixtures.scripts.p2sh.manyToMany.testnet.forEach((fixture) => { - // it(`should create 2-to-2 P2SH transaction on testnet`, () => { - // let node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]); - // let node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]); - // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); - // let txHash = fixture.txHash; - // let originalAmount = fixture.amounts[0] + fixture.amounts[1]; - // transactionBuilder.addInput(txHash, 0); - // transactionBuilder.addInput(txHash, 1); - // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 2 }); - // let sendAmount = originalAmount - (byteCount * 15); - // transactionBuilder.addOutput(fixture.outputs[0], Math.floor(sendAmount / 2)); - // transactionBuilder.addOutput(fixture.outputs[1], Math.floor(sendAmount / 2)); - // let keyPair1 = bchjs.HDNode.toKeyPair(node1); - // let keyPair2 = bchjs.HDNode.toKeyPair(node2); - // let redeemScript; - // transactionBuilder.sign(0, keyPair1, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[0]); - // transactionBuilder.sign(1, keyPair2, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[1]); - // let tx = transactionBuilder.build(); - // let hex = tx.toHex(); - // assert.equal(hex, fixture.hex); - // }); - // }); - // }); - }) - - describe("#fromMany", () => { - describe("#Mainnet", () => { - fixtures.scripts.p2sh.fromMany.mainnet.forEach(fixture => { - it(`should create 2-to-1 P2SH transaction on mainnet`, () => { - const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) - const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) - const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) - const transactionBuilder = new bchjs.TransactionBuilder() - const txid = fixture.txHash - const originalAmount = fixture.amount - const identifier1 = bchjs.HDNode.toIdentifier(node1) - const buf1 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier1, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash1 = bchjs.Crypto.hash160(buf1) - const data1 = bchjs.Script.scriptHash.output.encode(scriptHash1) - transactionBuilder.addInput( - txid, - 0, - transactionBuilder.DEFAULT_SEQUENCE, - data1 - ) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 3 }, - { P2PKH: 2 } - ) - const sendAmount = originalAmount - byteCount - const identifier2 = bchjs.HDNode.toIdentifier(node2) - const buf2 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier2, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash2 = bchjs.Crypto.hash160(buf2) - const data2 = bchjs.Script.scriptHash.output.encode(scriptHash2) - transactionBuilder.addInput( - txid, - 1, - transactionBuilder.DEFAULT_SEQUENCE, - data2 - ) - const identifier3 = bchjs.HDNode.toIdentifier(node3) - const buf3 = bchjs.Script.encode([ - bchjs.Script.opcodes.OP_DUP, - bchjs.Script.opcodes.OP_HASH160, - identifier3, - bchjs.Script.opcodes.OP_EQUALVERIFY, - bchjs.Script.opcodes.OP_CHECKSIG - ]) - const scriptHash3 = bchjs.Crypto.hash160(buf3) - const data3 = bchjs.Script.scriptHash.output.encode(scriptHash3) - transactionBuilder.addOutput(data3, sendAmount) - const keyPair1 = bchjs.HDNode.toKeyPair(node1) - const keyPair2 = bchjs.HDNode.toKeyPair(node2) - transactionBuilder.sign( - 0, - keyPair1, - buf1, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - transactionBuilder.sign( - 1, - keyPair2, - buf2, - transactionBuilder.hashTypes.SIGHASH_ALL, - originalAmount / 2 - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - // describe('#Testnet', () => { - // fixtures.scripts.p2sh.fromMany.testnet.forEach((fixture) => { - // it(`should create 2-to-1 P2SH transaction on testnet`, () => { - // let node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]); - // let node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]); - // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); - // let txHash = fixture.txHash; - // let originalAmount = fixture.amounts[0] + fixture.amounts[1]; - // transactionBuilder.addInput(txHash, 0); - // transactionBuilder.addInput(txHash, 1); - // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 1 }); - // let sendAmount = originalAmount - (byteCount * 15); - // transactionBuilder.addOutput(fixture.outputs[0], sendAmount); - // let keyPair1 = bchjs.HDNode.toKeyPair(node1); - // let keyPair2 = bchjs.HDNode.toKeyPair(node2); - // let redeemScript; - // transactionBuilder.sign(0, keyPair1, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[0]); - // transactionBuilder.sign(1, keyPair2, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[1]); - // let tx = transactionBuilder.build(); - // let hex = tx.toHex(); - // assert.equal(hex, fixture.hex); - // }); - // }); - // }); - }) - }) - - describe("#op_return", () => { - describe("#Mainnet", () => { - fixtures.nulldata.mainnet.forEach(fixture => { - it(`should create transaction w/ OP_RETURN output on mainnet`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder() - const txHash = fixture.txHash - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, 0) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 5 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(fixture.output, sendAmount) - const data = fixture.data - const buf = bchjs.Script.nullData.output.encode( - Buffer.from(data, "ascii") - ) - transactionBuilder.addOutput(buf, 0) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - - describe("#Testnet", () => { - fixtures.nulldata.testnet.forEach(fixture => { - it(`should create transaction w/ OP_RETURN output on testnet`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder("testnet") - const txHash = fixture.txHash - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, 0) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 5 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(fixture.output, sendAmount) - const data = fixture.data - const buf = bchjs.Script.nullData.output.encode( - Buffer.from(data, "ascii") - ) - transactionBuilder.addOutput(buf, 0) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - /* - describe("#RegTest", () => { - fixtures.nulldata.regtest.forEach(fixture => { - it(`should create transaction w/ OP_RETURN output on regtest`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder("regtest") - const txHash = fixture.txHash - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, 0) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 5 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(fixture.output, sendAmount) - const data = fixture.data - const buf = bchjs.Script.nullData.output.encode( - Buffer.from(data, "ascii") - ) - transactionBuilder.addOutput(buf, 0) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) -*/ - }) - - describe("#bip66", () => { - fixtures.bip66.forEach(fixture => { - it(`should bip66 encode as ${fixture.DER}`, () => { - const transactionBuilder = new bchjs.TransactionBuilder() - const r = Buffer.from(fixture.r, "hex") - const s = Buffer.from(fixture.s, "hex") - const DER = transactionBuilder.bip66.encode(r, s) - assert.equal(DER.toString("hex"), fixture.DER) - }) - }) - - fixtures.bip66.forEach(fixture => { - it(`should bip66 decode ${fixture.DER}`, () => { - const transactionBuilder = new bchjs.TransactionBuilder() - const buffer = Buffer.from(fixture.DER, "hex") - const signature = transactionBuilder.bip66.decode(buffer) - assert.equal(signature.r.toString("hex"), fixture.r) - assert.equal(signature.s.toString("hex"), fixture.s) - }) - }) - - fixtures.bip66.forEach(fixture => { - it(`should bip66 check ${fixture.DER}`, () => { - const transactionBuilder = new bchjs.TransactionBuilder() - const buffer = Buffer.from(fixture.DER, "hex") - assert.equal(transactionBuilder.bip66.check(buffer), true) - }) - }) - }) - - describe("#bip68", () => { - fixtures.bip68.encode.forEach(fixture => { - it(`should bip68 encode as ${fixture.result}`, () => { - const transactionBuilder = new bchjs.TransactionBuilder() - const obj = {} - obj[fixture.type] = fixture.value - const encode = transactionBuilder.bip68.encode(obj) - assert.equal(encode, fixture.result) - }) - }) - - fixtures.bip68.decode.forEach(fixture => { - it(`should bip68 decode ${fixture.result}`, () => { - const transactionBuilder = new bchjs.TransactionBuilder() - const obj = {} - const decode = transactionBuilder.bip68.decode(fixture.result) - assert.equal(Object.keys(decode)[0], fixture.type) - assert.deepEqual(decode[Object.keys(decode)[0]], fixture.value) - }) - }) - }) - - describe("#LockTime", () => { - describe("#Mainnet", () => { - fixtures.locktime.mainnet.forEach(fixture => { - it(`should create transaction with nLockTime on mainnet`, () => { - const node = bchjs.HDNode.fromXPriv(fixture.xpriv) - const transactionBuilder = new bchjs.TransactionBuilder() - - const txHash = fixture.txHash - const originalAmount = fixture.amount - transactionBuilder.addInput(txHash, 0, 1) - const byteCount = bchjs.BitcoinCash.getByteCount( - { P2PKH: 1 }, - { P2PKH: 1 } - ) - const sendAmount = originalAmount - byteCount - transactionBuilder.addOutput(fixture.output, sendAmount) - const lockTime = fixture.lockTime - transactionBuilder.setLockTime(lockTime) - const keyPair = bchjs.HDNode.toKeyPair(node) - let redeemScript - transactionBuilder.sign( - 0, - keyPair, - redeemScript, - transactionBuilder.hashTypes.SIGHASH_ALL, - fixture.amount - ) - const tx = transactionBuilder.build() - const hex = tx.toHex() - assert.equal(hex, fixture.hex) - }) - }) - }) - }) -}) diff --git a/test/unit/util.js b/test/unit/util.js deleted file mode 100644 index 7a17955..0000000 --- a/test/unit/util.js +++ /dev/null @@ -1,35 +0,0 @@ -const assert = require("assert") -const axios = require("axios") -const BCHJS = require("../../src/bch-js") -const bchjs = new BCHJS() -const sinon = require("sinon") - -describe("#Util", () => { - describe("#validateAddress", () => { - let sandbox - beforeEach(() => (sandbox = sinon.createSandbox())) - afterEach(() => sandbox.restore()) - - it("should validate address", done => { - const data = { - isvalid: true, - address: "bitcoincash:qpz7qtkuyhrsz4qmnnrvf8gz9zd0u9v7eqsewyk4w5", - scriptPubKey: "76a91445e02edc25c701541b9cc6c49d02289afe159ec888ac", - ismine: false, - iswatchonly: false, - isscript: false - } - - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - - bchjs.Util.validateAddress( - "bitcoincash:qpz7qtkuyhrsz4qmnnrvf8gz9zd0u9v7eqsewyk4w5" - ) - .then(result => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) - }) -}) From 3df211301cb5d5921f2e835c8f9c4448e9f94f83 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 14:51:48 -0800 Subject: [PATCH 5/9] fix(unit tests): Refactored a lot of code flow --- src/bch-js.js | 1 + src/raw-transactions.js | 7 +- src/slp/utils.js | 4 - test/unit/address.js | 7 +- test/unit/bitcoin-cash.js | 314 +++++ test/unit/blockbook.js | 275 ++++ test/unit/blockchain.js | 494 ++++++++ test/unit/control.js | 64 + test/unit/crypto.js | 100 ++ test/unit/ecpairs.js | 153 +++ test/unit/electrumx.js | 421 ++++++ test/unit/encryption.js | 68 + test/unit/generating.js | 36 + test/unit/hdnode.js | 358 ++++++ test/unit/ipfs.js | 443 +++++++ test/unit/mining.js | 111 ++ test/unit/mnemonic.js | 340 +++++ test/unit/ninsight.js | 255 ++++ test/unit/openbazaar.js | 129 ++ test/unit/raw-tranactions.js | 135 ++ test/unit/scripts.js | 487 +++++++ test/unit/slp-address.js | 846 +++++++++++++ test/unit/slp-ecpair.js | 26 + test/unit/slp-nft1.js | 264 ++++ test/unit/slp-tokentype1.js | 319 +++++ test/unit/transaction-builder.js | 2039 ++++++++++++++++++++++++++++++ test/unit/util.js | 35 + 27 files changed, 7720 insertions(+), 11 deletions(-) create mode 100644 test/unit/bitcoin-cash.js create mode 100644 test/unit/blockbook.js create mode 100644 test/unit/blockchain.js create mode 100644 test/unit/control.js create mode 100644 test/unit/crypto.js create mode 100644 test/unit/ecpairs.js create mode 100644 test/unit/electrumx.js create mode 100644 test/unit/encryption.js create mode 100644 test/unit/generating.js create mode 100644 test/unit/hdnode.js create mode 100644 test/unit/ipfs.js create mode 100644 test/unit/mining.js create mode 100644 test/unit/mnemonic.js create mode 100644 test/unit/ninsight.js create mode 100644 test/unit/openbazaar.js create mode 100644 test/unit/raw-tranactions.js create mode 100644 test/unit/scripts.js create mode 100644 test/unit/slp-address.js create mode 100644 test/unit/slp-ecpair.js create mode 100644 test/unit/slp-nft1.js create mode 100644 test/unit/slp-tokentype1.js create mode 100644 test/unit/transaction-builder.js create mode 100644 test/unit/util.js diff --git a/src/bch-js.js b/src/bch-js.js index 175fd66..17be54f 100644 --- a/src/bch-js.js +++ b/src/bch-js.js @@ -8,6 +8,7 @@ // bch-api mainnet. const DEFAULT_REST_API = "https://api.fullstack.cash/v4/" +// const DEFAULT_REST_API = "http://localhost:3000/v4/" // local deps const BitcoinCash = require("./bitcoincash") diff --git a/src/raw-transactions.js b/src/raw-transactions.js index 31a02b3..3bd1aec 100644 --- a/src/raw-transactions.js +++ b/src/raw-transactions.js @@ -24,6 +24,9 @@ class RawTransactions { } } + // Encapsulate dependencies + this.axios = axios + _this = this } @@ -322,7 +325,7 @@ class RawTransactions { try { // Single tx hex. if (typeof hex === "string") { - const response = await axios.get( + const response = await this.axios.get( `${this.restURL}rawtransactions/sendRawTransaction/${hex}`, _this.axiosOptions ) @@ -347,7 +350,7 @@ class RawTransactions { }, headers: _this.axiosOptions.headers } - const response = await axios(options) + const response = await _this.axios(options) return response.data } diff --git a/src/slp/utils.js b/src/slp/utils.js index ef7852a..3f50ddc 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -35,10 +35,6 @@ class Utils { _this = this this.whitelist = [] - if (process.env.TEST !== "unit") { - // Get the whitelist. - this.getWhitelist() - } } /** diff --git a/test/unit/address.js b/test/unit/address.js index 56a7224..57beb97 100644 --- a/test/unit/address.js +++ b/test/unit/address.js @@ -9,8 +9,6 @@ const fixtures = require("./fixtures/address.json") const BCHJS = require("../../src/bch-js") let bchjs -console.log("ping01") - function flatten(arrays) { return [].concat.apply([], arrays) } @@ -81,8 +79,6 @@ const P2SH_ADDRESSES = flatten([ fixtures.cashaddrMainnetP2SH ]) -console.log("ping02") - describe("#address.js", () => { beforeEach(() => { bchjs = new BCHJS() @@ -342,7 +338,7 @@ describe("#address.js", () => { }) }) }) - + /* describe("address format detection", () => { describe("#isLegacyAddress", () => { describe("is legacy", () => { @@ -936,4 +932,5 @@ describe("#address.js", () => { }) }) }) + */ }) diff --git a/test/unit/bitcoin-cash.js b/test/unit/bitcoin-cash.js new file mode 100644 index 0000000..f8ae038 --- /dev/null +++ b/test/unit/bitcoin-cash.js @@ -0,0 +1,314 @@ +// Public npm libraries +const assert = require("assert") + +// Mocks +const fixtures = require("./fixtures/bitcoincash.json") + +// Unit under test (uut) +const BCHJS = require("../../src/bch-js") +// const bchjs = new BCHJS() +let bchjs + +// TODO +// 1. generate testnet p2sh +// 2. generate cashaddr mainnet p2sh +// 3. generate cashaddr testnet p2sh +// 4. create bchjs fromBase58 method +// * confirm xpub cannot generate WIF +// * confirm xpriv can generate WIF +// 5. create fromXPriv method w/ tests and docs +// 1. mainnet +// * confirm xpriv generates address +// * confirm xpriv generates WIF +// 2. testnet +// * confirm xpriv generates address +// * confirm xpriv generates WIF +// 6. More error test cases. + +describe("#BitcoinCash", () => { + beforeEach(() => { + bchjs = new BCHJS() + }) + + describe("price conversion", () => { + it("should exercise toBitcoinCash", () => { + fixtures.conversion.toBCH.satoshis.forEach(satoshi => { + it(`should convert ${satoshi[0]} Satoshis to ${satoshi[1]} $BCH`, () => { + assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1]) + }) + }) + + fixtures.conversion.toBCH.strings.forEach(satoshi => { + it(`should convert "${satoshi[0]}" Satoshis as a string to ${satoshi[1]} $BCH`, () => { + assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1]) + }) + }) + + fixtures.conversion.toBCH.not.forEach(bch => { + it(`converts ${bch[0]} to Bitcoin Cash, not to ${bch[1]} Satoshi`, () => { + assert.notEqual(bchjs.BitcoinCash.toBitcoinCash(bch[0]), bch[1]) + }) + }) + + fixtures.conversion.toBCH.rounding.forEach(satoshi => { + it(`rounding ${satoshi[0]} to ${satoshi[1]} $BCH`, () => { + assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1]) + }) + }) + }) + + it("should exercise toSatoshi", () => { + fixtures.conversion.toSatoshi.bch.forEach(bch => { + it(`should convert ${bch[0]} $BCH to ${bch[1]} Satoshis`, () => { + assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1]) + }) + }) + + fixtures.conversion.toSatoshi.strings.forEach(bch => { + it(`should convert "${bch[0]}" $BCH as a string to ${bch[1]} Satoshis`, () => { + assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1]) + }) + }) + + fixtures.conversion.toSatoshi.not.forEach(satoshi => { + it(`converts ${satoshi[0]} to Satoshi, not to ${satoshi[1]} Bitcoin Cash`, () => { + assert.notEqual(bchjs.BitcoinCash.toSatoshi(satoshi[0]), satoshi[1]) + }) + }) + + fixtures.conversion.toSatoshi.rounding.forEach(bch => { + it(`rounding ${bch[0]} to ${bch[1]} Satoshi`, () => { + assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1]) + }) + }) + }) + + it("should exercise satsToBits", () => { + fixtures.conversion.satsToBits.bch.forEach(bch => { + it(`should convert ${bch[0]} BCH to ${bch[1]} bits`, () => { + assert.equal( + bchjs.BitcoinCash.satsToBits(bchjs.BitcoinCash.toSatoshi(bch[0])), + bch[1] + ) + }) + }) + + fixtures.conversion.satsToBits.strings.forEach(bch => { + it(`should convert "${bch[0]}" BCH as a string to ${bch[1]} bits`, () => { + assert.equal( + bchjs.BitcoinCash.satsToBits(bchjs.BitcoinCash.toSatoshi(bch[0])), + bch[1] + ) + }) + }) + }) + // + // describe('#satsFromBits', () => { + // fixtures.conversion.satsFromBits.bch.forEach((bch) => { + // it(`should convert ${bch[1]} bits to ${bch[0]} satoshis`, () => { + // assert.equal(bchjs.BitcoinCash.satsFromBits(bch[1]), bch[0]); + // }); + // }); + // + // fixtures.conversion.satsFromBits.strings.forEach((bch) => { + // it(`should convert "${bch[1]}" bits as a string to ${bch[0]} satoshis`, () => { + // assert.equal(bchjs.BitcoinCash.satsFromBits(bch[1]), bch[0]); + // }); + // }); + // }); + }) + + describe("sign and verify messages", () => { + it("should exercise signMessageWithPrivKey", () => { + fixtures.signatures.sign.forEach(sign => { + it(`should sign a message w/ ${sign.network} ${sign.privateKeyWIF}`, () => { + const privateKeyWIF = sign.privateKeyWIF + const message = sign.message + const signature = bchjs.BitcoinCash.signMessageWithPrivKey( + privateKeyWIF, + message + ) + assert.equal(signature, sign.signature) + }) + }) + }) + + it("shoudl exercise verifyMessage", () => { + fixtures.signatures.verify.forEach(sign => { + it(`should verify a valid signed message from ${sign.network} cashaddr address ${sign.address}`, () => { + assert.equal( + bchjs.BitcoinCash.verifyMessage( + sign.address, + sign.signature, + sign.message + ), + true + ) + }) + }) + + it("should verify legacy addresses", () => { + fixtures.signatures.verify.forEach(sign => { + const legacyAddress = bchjs.Address.toLegacyAddress(sign.address) + it(`should verify a valid signed message from ${sign.network} legacy address ${legacyAddress}`, () => { + assert.equal( + bchjs.BitcoinCash.verifyMessage( + legacyAddress, + sign.signature, + sign.message + ), + true + ) + }) + }) + }) + + fixtures.signatures.verify.forEach(sign => { + // const legacyAddress = bchjs.Address.toLegacyAddress(sign.address) + it(`should not verify an invalid signed message from ${sign.network} cashaddr address ${sign.address}`, () => { + assert.equal( + bchjs.BitcoinCash.verifyMessage( + sign.address, + sign.signature, + "nope" + ), + false + ) + }) + }) + }) + }) + + describe("encode and decode to base58Check", () => { + describe("#encodeBase58Check", () => { + fixtures.encodeBase58Check.forEach((base58Check, i) => { + it(`encode ${base58Check.hex} as base58Check ${base58Check.base58Check}`, () => { + assert.equal( + bchjs.BitcoinCash.encodeBase58Check(base58Check.hex), + base58Check.base58Check + ) + }) + }) + }) + + describe("#decodeBase58Check", () => { + fixtures.encodeBase58Check.forEach((base58Check, i) => { + it(`decode ${base58Check.base58Check} as ${base58Check.hex}`, () => { + assert.equal( + bchjs.BitcoinCash.decodeBase58Check(base58Check.base58Check), + base58Check.hex + ) + }) + }) + }) + }) + + describe("encode and decode BIP21 urls", () => { + describe("#encodeBIP21", () => { + fixtures.bip21.valid.forEach((bip21, i) => { + it(`encode ${bip21.address} as url`, () => { + const url = bchjs.BitcoinCash.encodeBIP21( + bip21.address, + bip21.options + ) + assert.equal(url, bip21.url) + }) + }) + fixtures.bip21.valid_regtest.forEach((bip21, i) => { + it(`encode ${bip21.address} as url`, () => { + const url = bchjs.BitcoinCash.encodeBIP21( + bip21.address, + bip21.options, + true + ) + assert.equal(url, bip21.url) + }) + }) + }) + + describe("#decodeBIP21", () => { + fixtures.bip21.valid.forEach((bip21, i) => { + it(`decodes ${bip21.url}`, () => { + const decoded = bchjs.BitcoinCash.decodeBIP21(bip21.url) + assert.equal(decoded.options.amount, bip21.options.amount) + assert.equal(decoded.options.label, bip21.options.label) + assert.equal( + bchjs.Address.toCashAddress(decoded.address), + bchjs.Address.toCashAddress(bip21.address) + ) + }) + }) + // fixtures.bip21.valid_regtest.forEach((bip21, i) => { + // it(`decodes ${bip21.url}`, () => { + // const decoded = bchjs.BitcoinCash.decodeBIP21(bip21.url) + // assert.equal(decoded.options.amount, bip21.options.amount) + // assert.equal(decoded.options.label, bip21.options.label) + // assert.equal( + // bchjs.Address.toCashAddress(decoded.address, true, true), + // bchjs.Address.toCashAddress(bip21.address, true, true) + // ) + // }) + // }) + }) + }) + + describe("#getByteCount", () => { + fixtures.getByteCount.forEach(fixture => { + it(`get byte count`, () => { + const byteCount = bchjs.BitcoinCash.getByteCount( + fixture.inputs, + fixture.outputs + ) + assert.equal(byteCount, fixture.byteCount) + }) + }) + }) + + describe("#bip38", () => { + describe("#encryptBIP38", () => { + fixtures.bip38.encrypt.mainnet.forEach(fixture => { + it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on mainnet`, () => { + const encryptedKey = bchjs.BitcoinCash.encryptBIP38( + fixture.wif, + fixture.password + ) + assert.equal(encryptedKey, fixture.encryptedKey) + }) + }) + + fixtures.bip38.encrypt.testnet.forEach(fixture => { + it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on testnet`, () => { + const encryptedKey = bchjs.BitcoinCash.encryptBIP38( + fixture.wif, + fixture.password + ) + assert.equal(encryptedKey, fixture.encryptedKey) + }) + }) + }) + + describe("#decryptBIP38", () => { + fixtures.bip38.decrypt.mainnet.forEach(fixture => { + it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on mainnet`, () => { + const wif = bchjs.BitcoinCash.decryptBIP38( + fixture.encryptedKey, + fixture.password, + "mainnet" + ) + assert.equal(wif, fixture.wif) + }) + }) + + fixtures.bip38.decrypt.testnet.forEach(fixture => { + it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on testnet`, () => { + const wif = bchjs.BitcoinCash.decryptBIP38( + fixture.encryptedKey, + fixture.password, + "testnet" + ) + assert.equal(wif, fixture.wif) + }) + }) + }) + }) +}) diff --git a/test/unit/blockbook.js b/test/unit/blockbook.js new file mode 100644 index 0000000..9866c81 --- /dev/null +++ b/test/unit/blockbook.js @@ -0,0 +1,275 @@ +const chai = require("chai") +const assert = chai.assert +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() +const axios = require("axios") +const sinon = require("sinon") + +const mockData = require("./fixtures/blockbook-mock") + +describe(`#Blockbook`, () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + describe(`#Balance`, () => { + it(`should GET balance for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.balance }) + + const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" + + const result = await bchjs.Blockbook.balance(addr) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAnyKeys(result, [ + "page", + "totalPages", + "itemsOnPage", + "address", + "balance", + "totalReceived", + "totalSent", + "unconfirmedBalance", + "unconfirmedTxs", + "txs", + "txids" + ]) + assert.isArray(result.txids) + }) + + it(`should POST request balances for an array of addresses`, async () => { + // Stub the network call. + sandbox + .stub(axios, "post") + .resolves({ data: [mockData.balance, mockData.balance] }) + + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Blockbook.balance(addr) + //console.log(`result: ${util.inspect(result)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "page", + "totalPages", + "itemsOnPage", + "address", + "balance", + "totalReceived", + "totalSent", + "unconfirmedBalance", + "unconfirmedTxs", + "txs", + "txids" + ]) + assert.isArray(result[0].txids) + }) + + it(`should throw an error for improper input`, async () => { + try { + // Stub the network call. + sandbox + .stub(axios, "post") + .throws(`Input address must be a string or array of strings`) + + const addr = 12345 + + await bchjs.Blockbook.balance(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 throw error on array size rate limit`, async () => { + try { + // Stub the network call. + sandbox.stub(axios, "post").throws(`Array too large`) + + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") + + const result = await bchjs.Blockbook.balance(addr) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + */ + }) + + describe(`#utxo`, () => { + it(`should GET utxos for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) + + const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" + + const result = await bchjs.Blockbook.utxo(addr) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "txid", + "vout", + "value", + "height", + "confirmations" + ]) + }) + + it(`should POST utxo details for an array of addresses`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.utxos }) + + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Blockbook.utxo(addr) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isArray(result[0]) + assert.hasAnyKeys(result[0][0], [ + "txid", + "vout", + "value", + "height", + "confirmations" + ]) + }) + + it(`should throw an error for improper input`, async () => { + try { + // Stub the network call. + sandbox + .stub(axios, "post") + .throws(`Input address must be a string or array of strings`) + + const addr = 12345 + + await bchjs.Blockbook.utxo(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 throw error on array size rate limit`, async () => { + try { + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") + + const result = await bchjs.Blockbook.utxo(addr) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + */ + }) + + describe(`#tx`, () => { + it(`should GET tx details for a single txid`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) + + const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" + + const result = await bchjs.Blockbook.utxo(addr) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "txid", + "vout", + "value", + "height", + "confirmations" + ]) + }) + + // it(`should POST utxo details for an array of addresses`, async () => { + // // Stub the network call. + // sandbox.stub(axios, "post").resolves({ data: mockData.utxos }) + // + // const addr = [ + // "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + // "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + // ] + // + // const result = await bchjs.Blockbook.utxo(addr) + // //console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.isArray(result) + // assert.isArray(result[0]) + // assert.hasAnyKeys(result[0][0], [ + // "txid", + // "vout", + // "value", + // "height", + // "confirmations" + // ]) + // }) + + // it(`should throw an error for improper input`, async () => { + // try { + // // Stub the network call. + // sandbox + // .stub(axios, "post") + // .throws(`Input address must be a string or array of strings`) + // + // const addr = 12345 + // + // await bchjs.Blockbook.utxo(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 throw error on array size rate limit`, async () => { + try { + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") + + const result = await bchjs.Blockbook.utxo(addr) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + */ + }) +}) diff --git a/test/unit/blockchain.js b/test/unit/blockchain.js new file mode 100644 index 0000000..e088ffe --- /dev/null +++ b/test/unit/blockchain.js @@ -0,0 +1,494 @@ +const assert = require("assert") +const assert2 = require("chai").assert +const axios = require("axios") +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() +const sinon = require("sinon") + +const mockData = require("./fixtures/blockchain-mock") + +describe("#Blockchain", () => { + describe("#getBestBlockHash", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should get best block hash", done => { + const resolved = new Promise(r => + r({ + data: + "0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d" + }) + ) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getBestBlockHash() + .then(result => { + const hash = + "0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d" + assert.equal(hash, result) + }) + .then(done, done) + }) + }) + + describe("#getBlock", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = { + hash: "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", + confirmations: 526807, + size: 216, + height: 1000, + version: 1, + versionHex: "00000001", + merkleroot: + "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33", + tx: ["fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"], + time: 1232346882, + mediantime: 1232344831, + nonce: 2595206198, + bits: "1d00ffff", + difficulty: 1, + chainwork: + "000000000000000000000000000000000000000000000000000003e903e903e9", + previousblockhash: + "0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d", + nextblockhash: + "00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6" + } + + it("should get block by hash", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getBlock( + "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09" + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getBlockchainInfo", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = { + chain: "main", + blocks: 527810, + headers: 527810, + bestblockhash: + "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", + difficulty: 576023394804.6666, + mediantime: 1524878499, + verificationprogress: 0.9999990106793685, + chainwork: + "00000000000000000000000000000000000000000096da5b040913fa09249b4e", + pruned: false, + softforks: [ + { id: "bip34", version: 2, reject: [Object] }, + { id: "bip66", version: 3, reject: [Object] }, + { id: "bip65", version: 4, reject: [Object] } + ], + bip9_softforks: { + csv: { + status: "active", + startTime: 1462060800, + timeout: 1493596800, + since: 419328 + } + } + } + + it("should get blockchain info", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getBlockchainInfo() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getBlockCount", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = 527810 + + it("should get block count", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getBlockCount() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getBlockHash", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = + "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0" + + it("should get block hash by height", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getBlockHash(527810) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getBlockHeader", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = { + hash: "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", + confirmations: 1, + height: 527810, + version: 536870912, + versionHex: "20000000", + merkleroot: + "9298432bbebe4638456aa19cb7ef91639da87668a285d88d0ecd6080424d223b", + time: 1524881438, + mediantime: 1524878499, + nonce: 3326843941, + bits: "1801e8a5", + difficulty: 576023394804.6666, + chainwork: + "00000000000000000000000000000000000000000096da5b040913fa09249b4e", + previousblockhash: + "000000000000000000b33251708bc7a7b4540e61880d8c376e8e2db6a19a4789" + } + + it("should get block header by hash", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getBlockHeader( + "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", + true + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getDifficulty", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = "577528469277.1339" + + it("should get difficulty", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getDifficulty() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getMempoolAncestors", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = "Transaction not in mempool" + + it("should get mempool ancestors", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getMempoolAncestors( + "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", + true + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getMempoolDescendants", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = { + result: "Transaction not in mempool" + } + + it("should get mempool descendants", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getMempoolDescendants( + "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", + true + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getMempoolEntry", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = { + result: "Transaction not in mempool" + } + + it("should get mempool entry", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getMempoolEntry( + "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88" + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getMempoolInfo", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = { + result: { + size: 317, + bytes: 208583, + usage: 554944, + maxmempool: 300000000, + mempoolminfee: 0 + } + } + + it("should get mempool info", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getMempoolInfo() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getRawMempool", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = { + result: { + transactions: [ + { + txid: + "ab36d68dd0a618592fe34e4a898e8beeeb4049133547dbb16f9338384084af96", + size: 191, + fee: 0.00047703, + modifiedfee: 0.00047703, + time: 1524883317, + height: 527811, + startingpriority: 5287822727.272727, + currentpriority: 5287822727.272727, + descendantcount: 1, + descendantsize: 191, + descendantfees: 47703, + ancestorcount: 1, + ancestorsize: 191, + ancestorfees: 47703, + depends: [] + } + ] + } + } + + it("should get mempool info", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.getRawMempool() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getTxOut", () => { + // TODO finish this test + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + // const data = { + // result: {} + // } + + it("should throw an error for improper txid.", async () => { + try { + await bchjs.Blockchain.getTxOut("badtxid") + } catch (err) { + assert2.include(err.message, "txid needs to be a proper transaction ID") + } + }) + + it("should throw an error if no vout value is provided.", async () => { + try { + await bchjs.Blockchain.getTxOut( + "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88" + ) + } catch (err) { + assert2.include(err.message, "n must be an integer") + } + }) + + it("should throw an error if include_mempool is not a boolean", async () => { + try { + await bchjs.Blockchain.getTxOut( + "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", + 0, + "bad value" + ) + } catch (err) { + assert2.include( + err.message, + "include_mempool input must be of type boolean" + ) + } + }) + + it("should get information on an unspent tx", async () => { + sandbox.stub(axios, "post").resolves({ data: mockData.txOutUnspent }) + + const result = await bchjs.Blockchain.getTxOut( + "62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8", + 0, + true + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert2.hasAllKeys(result, [ + "bestblock", + "confirmations", + "value", + "scriptPubKey", + "coinbase" + ]) + }) + + it("should get information on a spent tx", async () => { + sandbox.stub(axios, "post").resolves({ data: null }) + + const result = await bchjs.Blockchain.getTxOut( + "87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871", + 0, + true + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert2.equal(result, null) + }) + }) + + describe("#preciousBlock", () => { + // TODO finish this test + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = { + result: {} + } + + it("should get TODO", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.preciousBlock() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#pruneBlockchain", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = "Cannot prune blocks because node is not in prune mode." + + it("should prune blockchain", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "post").returns(resolved) + + bchjs.Blockchain.pruneBlockchain(507) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#verifyChain", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = true + + it("should verify blockchain", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.verifyChain(3, 6) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#verifyTxOutProof", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + const data = "proof must be hexadecimal string (not '')" + + it("should verify utxo proof", done => { + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Blockchain.verifyTxOutProof("3") + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) +}) diff --git a/test/unit/control.js b/test/unit/control.js new file mode 100644 index 0000000..2ec8219 --- /dev/null +++ b/test/unit/control.js @@ -0,0 +1,64 @@ +const assert = require("assert") +const axios = require("axios") +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() +const sinon = require("sinon") + +describe("#Control", () => { + describe("#getNetworkInfo", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should get info", done => { + const data = { + version: 170000, + protocolversion: 70015, + blocks: 527813, + timeoffset: 0, + connections: 21, + proxy: "", + difficulty: 581086703759.5878, + testnet: false, + paytxfee: 0, + relayfee: 0.00001, + errors: "" + } + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Control.getNetworkInfo() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getMemoryInfo", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should get memory info", done => { + const data = { + locked: { + used: 0, + free: 65536, + total: 65536, + locked: 65536, + chunks_used: 0, + chunks_free: 1 + } + } + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Control.getMemoryInfo() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) +}) diff --git a/test/unit/crypto.js b/test/unit/crypto.js new file mode 100644 index 0000000..73f6d23 --- /dev/null +++ b/test/unit/crypto.js @@ -0,0 +1,100 @@ +const fixtures = require("./fixtures/crypto.json") +const assert = require("assert") +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() +const Buffer = require("safe-buffer").Buffer + +describe("#Crypto", () => { + describe("#sha256", () => { + fixtures.sha256.forEach(fixture => { + it(`should create SHA256Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { + const data = Buffer.from(fixture.hex, "hex") + const sha256Hash = bchjs.Crypto.sha256(data).toString("hex") + assert.equal(sha256Hash, fixture.hash) + }) + + it(`should create 64 character SHA256Hash hex encoded`, () => { + const data = Buffer.from(fixture.hex, "hex") + const sha256Hash = bchjs.Crypto.sha256(data).toString("hex") + assert.equal(sha256Hash.length, 64) + }) + }) + }) + + describe("#ripemd160", () => { + fixtures.ripemd160.forEach(fixture => { + it(`should create RIPEMD160Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { + const data = Buffer.from(fixture.hex, "hex") + const ripemd160 = bchjs.Crypto.ripemd160(data).toString("hex") + assert.equal(ripemd160, fixture.hash) + }) + + it(`should create 64 character RIPEMD160Hash hex encoded`, () => { + const data = Buffer.from(fixture.hex, "hex") + const ripemd160 = bchjs.Crypto.ripemd160(data).toString("hex") + assert.equal(ripemd160.length, 40) + }) + }) + }) + + describe("#hash256", () => { + fixtures.hash256.forEach(fixture => { + it(`should create double SHA256 Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { + const data = Buffer.from(fixture.hex, "hex") + const hash256 = bchjs.Crypto.hash256(data).toString("hex") + assert.equal(hash256, fixture.hash) + }) + + it(`should create 64 character SHA256 Hash hex encoded`, () => { + const data = Buffer.from(fixture.hex, "hex") + const hash256 = bchjs.Crypto.hash256(data).toString("hex") + assert.equal(hash256.length, 64) + }) + }) + }) + + describe("#hash160", () => { + fixtures.hash160.forEach(fixture => { + it(`should create RIPEMD160(SHA256()) hex encoded ${fixture.hash} from ${fixture.hex}`, () => { + const data = Buffer.from(fixture.hex, "hex") + const hash160 = bchjs.Crypto.hash160(data).toString("hex") + assert.equal(hash160, fixture.hash) + }) + + it(`should create 64 character SHA256Hash hex encoded`, () => { + const data = Buffer.from(fixture.hex, "hex") + const hash160 = bchjs.Crypto.hash160(data).toString("hex") + assert.equal(hash160.length, 40) + }) + }) + }) + + describe("#randomBytes", () => { + for (let i = 0; i < 6; i++) { + it("should return 16 bytes of entropy hex encoded", () => { + const entropy = bchjs.Crypto.randomBytes(16) + assert.equal(Buffer.byteLength(entropy), 16) + }) + + it("should return 20 bytes of entropy hex encoded", () => { + const entropy = bchjs.Crypto.randomBytes(20) + assert.equal(Buffer.byteLength(entropy), 20) + }) + + it("should return 24 bytes of entropy hex encoded", () => { + const entropy = bchjs.Crypto.randomBytes(24) + assert.equal(Buffer.byteLength(entropy), 24) + }) + + it("should return 28 bytes of entropy hex encoded", () => { + const entropy = bchjs.Crypto.randomBytes(28) + assert.equal(Buffer.byteLength(entropy), 28) + }) + + it("should return 32 bytes of entropy hex encoded", () => { + const entropy = bchjs.Crypto.randomBytes(32) + assert.equal(Buffer.byteLength(entropy), 32) + }) + } + }) +}) diff --git a/test/unit/ecpairs.js b/test/unit/ecpairs.js new file mode 100644 index 0000000..7cd4f8f --- /dev/null +++ b/test/unit/ecpairs.js @@ -0,0 +1,153 @@ +// Public npm libraries. +const assert = require("assert") +const Buffer = require("safe-buffer").Buffer + +// Mocks +const fixtures = require("./fixtures/ecpair.json") + +// Unit under test (uut) +const BCHJS = require("../../src/bch-js") +// const bchjs = new BCHJS() +let bchjs + +describe("#ECPair", () => { + beforeEach(() => { + bchjs = new BCHJS() + }) + + describe("#fromWIF", () => { + fixtures.fromWIF.forEach(fixture => { + it(`should create ECPair from WIF ${fixture.privateKeyWIF}`, () => { + const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + assert.equal(typeof ecpair, "object") + }) + + it(`should get ${fixture.legacy} legacy address`, () => { + const legacy = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + assert.equal(bchjs.HDNode.toLegacyAddress(legacy), fixture.legacy) + }) + + it(`should get ${fixture.cashAddr} cash address`, () => { + const cashAddr = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + assert.equal(bchjs.HDNode.toCashAddress(cashAddr), fixture.cashAddr) + }) + + it(`should get ${fixture.regtestAddr} cash address`, () => { + const cashAddr = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + assert.equal( + bchjs.HDNode.toCashAddress(cashAddr, true), + fixture.regtestAddr + ) + }) + }) + }) + + describe("#toWIF", () => { + fixtures.toWIF.forEach(fixture => { + it(`should get WIF ${fixture.privateKeyWIF} from ECPair`, () => { + const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + const wif = bchjs.ECPair.toWIF(ecpair) + assert.equal(wif, fixture.privateKeyWIF) + }) + }) + }) + + describe("#fromPublicKey", () => { + fixtures.fromPublicKey.forEach(fixture => { + it(`should create ECPair from public key buffer`, () => { + const ecpair = bchjs.ECPair.fromPublicKey( + Buffer.from(fixture.pubkeyHex, "hex") + ) + assert.equal(typeof ecpair, "object") + }) + + it(`should get ${fixture.legacy} legacy address`, () => { + const ecpair = bchjs.ECPair.fromPublicKey( + Buffer.from(fixture.pubkeyHex, "hex") + ) + assert.equal(bchjs.HDNode.toLegacyAddress(ecpair), fixture.legacy) + }) + + it(`should get ${fixture.cashAddr} cash address`, () => { + const ecpair = bchjs.ECPair.fromPublicKey( + Buffer.from(fixture.pubkeyHex, "hex") + ) + assert.equal(bchjs.HDNode.toCashAddress(ecpair), fixture.cashAddr) + }) + + it(`should get ${fixture.regtestAddr} cash address`, () => { + const ecpair = bchjs.ECPair.fromPublicKey( + Buffer.from(fixture.pubkeyHex, "hex") + ) + assert.equal( + bchjs.HDNode.toCashAddress(ecpair, true), + fixture.regtestAddr + ) + }) + }) + }) + + describe("#toPublicKey", () => { + fixtures.toPublicKey.forEach(fixture => { + it(`should create a public key buffer from an ECPair`, () => { + const ecpair = bchjs.ECPair.fromPublicKey( + Buffer.from(fixture.pubkeyHex, "hex") + ) + const pubkeyBuffer = bchjs.ECPair.toPublicKey(ecpair) + assert.equal(typeof pubkeyBuffer, "object") + }) + }) + }) + + describe("#toLegacyAddress", () => { + fixtures.toLegacyAddress.forEach(fixture => { + it(`should create legacy address ${fixture.legacy} from an ECPair`, () => { + const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + const legacyAddress = bchjs.ECPair.toLegacyAddress(ecpair) + assert.equal(legacyAddress, fixture.legacy) + }) + }) + }) + + describe("#toCashAddress", () => { + fixtures.toCashAddress.forEach(fixture => { + it(`should create cash address ${fixture.cashAddr} from an ECPair`, () => { + const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + const cashAddr = bchjs.ECPair.toCashAddress(ecpair) + assert.equal(cashAddr, fixture.cashAddr) + }) + }) + + fixtures.toCashAddress.forEach(fixture => { + it(`should create regtest cash address ${fixture.regtestAddr} from an ECPair`, () => { + const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + const regtestAddr = bchjs.ECPair.toCashAddress(ecpair, true) + assert.equal(regtestAddr, fixture.regtestAddr) + }) + }) + }) + + describe("#sign", () => { + fixtures.sign.forEach(fixture => { + it(`should sign 32 byte hash buffer`, () => { + const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) + const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") + const signatureBuf = bchjs.ECPair.sign(ecpair, buf) + assert.equal(typeof signatureBuf, "object") + }) + }) + }) + + describe("#verify", () => { + fixtures.verify.forEach(fixture => { + it(`should verify signed 32 byte hash buffer`, () => { + const ecpair1 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF1) + //const ecpair2 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF2) + const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") + const signature = bchjs.ECPair.sign(ecpair1, buf) + const verify = bchjs.ECPair.verify(ecpair1, buf, signature) + assert.equal(verify, true) + }) + }) + }) +}) diff --git a/test/unit/electrumx.js b/test/unit/electrumx.js new file mode 100644 index 0000000..479d77b --- /dev/null +++ b/test/unit/electrumx.js @@ -0,0 +1,421 @@ +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/electrumx-mock") + +describe(`#ElectrumX`, () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + describe(`#utxo`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.Electrumx.utxo(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 GET utxos for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) + + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Electrumx.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "utxos") + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "height") + assert.property(result.utxos[0], "tx_hash") + assert.property(result.utxos[0], "tx_pos") + assert.property(result.utxos[0], "value") + }) + + it(`should POST utxo details for an array of addresses`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.utxos }) + + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Electrumx.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "utxos") + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "utxos") + assert.isArray(result.utxos[0].utxos) + assert.property(result.utxos[0], "address") + + assert.property(result.utxos[0].utxos[0], "height") + assert.property(result.utxos[0].utxos[0], "tx_hash") + assert.property(result.utxos[0].utxos[0], "tx_pos") + assert.property(result.utxos[0].utxos[0], "value") + }) + }) + + describe(`#balance`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.Electrumx.balance(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 GET balance for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.balance }) + + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Electrumx.balance(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "balance") + assert.property(result.balance, "confirmed") + assert.property(result.balance, "unconfirmed") + }) + + it(`should POST balance for an array of addresses`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.balances }) + + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Electrumx.balance(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "balances") + assert.isArray(result.balances) + + assert.property(result.balances[0], "address") + assert.property(result.balances[0], "balance") + assert.property(result.balances[0].balance, "confirmed") + assert.property(result.balances[0].balance, "unconfirmed") + }) + }) + + describe(`#transactions`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.Electrumx.transactions(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 GET transactions for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.transaction }) + + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Electrumx.transactions(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "transactions") + assert.isArray(result.transactions) + assert.property(result.transactions[0], "height") + assert.property(result.transactions[0], "tx_hash") + }) + + it(`should POST transaction history for an array of addresses`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.transactions }) + + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Electrumx.transactions(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "transactions") + assert.isArray(result.transactions) + + assert.property(result.transactions[0], "address") + assert.property(result.transactions[0], "transactions") + assert.isArray(result.transactions[0].transactions) + assert.property(result.transactions[0].transactions[0], "height") + assert.property(result.transactions[0].transactions[0], "tx_hash") + }) + }) + + describe(`#unconfirmed`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.Electrumx.unconfirmed(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 GET unconfirmed utxos for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.unconfirmed }) + + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Electrumx.unconfirmed(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "utxos") + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "height") + assert.property(result.utxos[0], "tx_hash") + assert.property(result.utxos[0], "fee") + }) + + it(`should POST unconfirmed utxo details for an array of addresses`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmedArray }) + + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Electrumx.unconfirmed(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "utxos") + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "utxos") + assert.isArray(result.utxos[0].utxos) + assert.property(result.utxos[0], "address") + + assert.property(result.utxos[0].utxos[0], "height") + assert.property(result.utxos[0].utxos[0], "tx_hash") + assert.property(result.utxos[0].utxos[0], "fee") + }) + }) + + describe(`#blockHeader`, () => { + it(`should throw an error for improper height input`, async () => { + try { + // Mock network calls. + sandbox.stub(axios, "get").rejects({ + response: { + data: { + success: false, + error: "height must be a positive number" + } + } + }) + + const height = -10 + await bchjs.Electrumx.blockHeader(height) + + assert.equal(true, false, "Unexpected result!") + } catch (err) { + // console.log(`err: `, err) + assert.include(err.message, `height must be a positive number`) + } + }) + + it(`should throw an error for improper count input`, async () => { + try { + // Mock network calls. + sandbox.stub(axios, "get").rejects({ + response: { + data: { + success: false, + error: "count must be a positive number" + } + } + }) + + const height = 42 + const count = -10 + + await bchjs.Electrumx.blockHeader(height, count) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + // console.log(`err: `, err) + assert.include(err.message, `count must be a positive number`) + } + }) + + it(`should GET block headers for a given height`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.blockHeaders }) + + const height = 42 + + const result = await bchjs.Electrumx.blockHeader(height) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + assert.isArray(result) + assert.equal(result.length, 2) + }) + }) + + describe(`#txData`, () => { + it(`should throw an error for improper input`, async () => { + try { + const txid = 12345 + + await bchjs.Electrumx.txData(txid) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + // console.log(`err: `, err) + assert.include( + err.message, + `Input txId must be a string or array of strings` + ) + } + }) + + it(`should GET details data for a single transaction`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.details }) + + const txid = + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" + + const result = await bchjs.Electrumx.txData(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "details") + assert.isObject(result.details) + + assert.property(result.details, "blockhash") + assert.property(result.details, "hash") + assert.property(result.details, "hex") + assert.property(result.details, "vin") + assert.property(result.details, "vout") + assert.equal(result.details.hash, txid) + }) + + it(`should POST details for an array of transactions`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.detailsArray }) + + const txids = [ + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251", + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" + ] + + const result = await bchjs.Electrumx.txData(txids) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "transactions") + assert.isArray(result.transactions) + + assert.property(result.transactions[0], "txid") + assert.property(result.transactions[0], "details") + + assert.property(result.transactions[0].details, "blockhash") + assert.property(result.transactions[0].details, "hash") + assert.property(result.transactions[0].details, "hex") + assert.property(result.transactions[0].details, "vin") + assert.property(result.transactions[0].details, "vout") + + assert.equal(result.transactions.length, 2, "2 outputs for 2 inputs") + }) + }) + describe(`#broadcast`, () => { + it(`should throw an error for improper input`, async () => { + try { + const txHex = 12345 + + await bchjs.Electrumx.broadcast(txHex) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + // console.log(`err: `, err) + assert.include(err.message, `Input txHex must be a string.`) + } + }) + it(`should broadcast a single transaction`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.broadcast }) + + const tx = mockData.details + const txid = tx.details.txid + const result = await bchjs.Electrumx.broadcast(tx.details.hex) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "txid") + assert.equal(result.txid, txid) + }) + }) +}) diff --git a/test/unit/encryption.js b/test/unit/encryption.js new file mode 100644 index 0000000..9242348 --- /dev/null +++ b/test/unit/encryption.js @@ -0,0 +1,68 @@ +const assert = require("chai").assert +const sinon = require("sinon") + +const BCHJS = require("../../src/bch-js") +// const bchjs = new BCHJS() +let bchjs + +const mockData = require("./fixtures/encryption-mock") + +describe("#Encryption", () => { + let sandbox + + beforeEach(() => { + bchjs = new BCHJS() + sandbox = sinon.createSandbox() + }) + + afterEach(() => sandbox.restore()) + + describe("#getPubKey", () => { + it("should throw error if BCH address is not provided.", async () => { + try { + await bchjs.encryption.getPubKey() + + assert.equal(true, false, "Unexpected result!") + } catch (err) { + // console.log(`err: `, err) + assert.include( + err.message, + "Input must be a valid Bitcoin Cash address" + ) + } + }) + + it("should report when public key can not be found", async () => { + // Stub the network call. + sandbox + .stub(bchjs.encryption.axios, "get") + .resolves({ data: mockData.failureMock }) + + const addr = "bitcoincash:qpxqr2pmcverj4vukgjqssvk2zju8tp9xsgz2nqagx" + + const result = await bchjs.encryption.getPubKey(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, false) + assert.property(result, "publicKey") + assert.equal(result.publicKey, "not found") + }) + + it("should get a public key", async () => { + // Stub the network call. + sandbox + .stub(bchjs.encryption.axios, "get") + .resolves({ data: mockData.successMock }) + + const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq" + + const result = await bchjs.encryption.getPubKey(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + assert.property(result, "publicKey") + }) + }) +}) diff --git a/test/unit/generating.js b/test/unit/generating.js new file mode 100644 index 0000000..068d68f --- /dev/null +++ b/test/unit/generating.js @@ -0,0 +1,36 @@ +// Public npm libraries +const assert = require("assert") +const axios = require("axios") +const sinon = require("sinon") + +// Unit under test (uut) +const BCHJS = require("../../src/bch-js") +// const bchjs = new BCHJS() +let bchjs + +describe("#Generating", () => { + beforeEach(() => { + bchjs = new BCHJS() + }) + + describe("#generateToAddress", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should generate", done => { + const data = [] + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "post").returns(resolved) + + bchjs.Generating.generateToAddress( + 1, + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) +}) diff --git a/test/unit/hdnode.js b/test/unit/hdnode.js new file mode 100644 index 0000000..e5c7792 --- /dev/null +++ b/test/unit/hdnode.js @@ -0,0 +1,358 @@ +// Public npm libraries +const assert = require("assert") +const Buffer = require("safe-buffer").Buffer + +// Mocks +const fixtures = require("./fixtures/hdnode.json") +const slpFixtures = require("./fixtures/slp/address.json") + +// Unit under test (uut) +const BCHJS = require("../../src/bch-js") +let bchjs + +describe("#HDNode", () => { + beforeEach(() => { + bchjs = new BCHJS() + }) + + describe("#fromSeed", () => { + fixtures.fromSeed.forEach(mnemonic => { + it(`should create an HDNode from root seed buffer`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + assert.notEqual(hdNode, null) + }) + }) + }) + + describe("#derive", () => { + fixtures.derive.forEach(derive => { + it(`should derive non hardened child HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const childHDNode = bchjs.HDNode.derive(hdNode, 0) + assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) + assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) + }) + }) + }) + + describe("#deriveHardened", () => { + fixtures.deriveHardened.forEach(derive => { + it(`should derive hardened child HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const childHDNode = bchjs.HDNode.deriveHardened(hdNode, 0) + assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) + assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) + }) + }) + + describe("derive BIP44 $BCH account", () => { + fixtures.deriveBIP44.forEach(derive => { + it(`should derive BIP44 $BCH account`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const purpose = bchjs.HDNode.deriveHardened(hdNode, 44) + const coin = bchjs.HDNode.deriveHardened(purpose, 145) + const childHDNode = bchjs.HDNode.deriveHardened(coin, 0) + assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) + assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) + }) + }) + }) + }) + + describe("#derivePath", () => { + describe("derive non hardened Path", () => { + fixtures.derivePath.forEach(derive => { + it(`should derive non hardened child HDNode from path`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const childHDNode = bchjs.HDNode.derivePath(hdNode, "0") + assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) + assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) + }) + }) + }) + + describe("derive hardened Path", () => { + fixtures.deriveHardenedPath.forEach(derive => { + it(`should derive hardened child HDNode from path`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const childHDNode = bchjs.HDNode.derivePath(hdNode, "0'") + assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) + assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) + }) + }) + }) + + describe("derive BIP44 $BCH account", () => { + fixtures.deriveBIP44.forEach(derive => { + it(`should derive BIP44 $BCH account`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(derive.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const childHDNode = bchjs.HDNode.derivePath(hdNode, "44'/145'/0'") + assert.equal(bchjs.HDNode.toXPub(childHDNode), derive.xpub) + assert.equal(bchjs.HDNode.toXPriv(childHDNode), derive.xpriv) + }) + }) + }) + }) + + describe("#toLegacyAddress", () => { + fixtures.toLegacyAddress.forEach(fixture => { + it(`should get address ${fixture.address} from HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const childHDNode = bchjs.HDNode.derivePath(hdNode, "0") + const addy = bchjs.HDNode.toLegacyAddress(childHDNode) + assert.equal(addy, fixture.address) + }) + }) + }) + + describe("#toCashAddress", () => { + fixtures.toCashAddress.forEach(fixture => { + it(`should get address ${fixture.address} from HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const childHDNode = bchjs.HDNode.derivePath(hdNode, "0") + const addy = bchjs.HDNode.toCashAddress(childHDNode) + assert.equal(addy, fixture.address) + }) + + it(`should get address ${fixture.regtestAddress} from HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const childHDNode = bchjs.HDNode.derivePath(hdNode, "0") + const addr = bchjs.HDNode.toCashAddress(childHDNode, true) + assert.equal(addr, fixture.regtestAddress) + }) + }) + }) + + describe("#toWIF", () => { + fixtures.toWIF.forEach(fixture => { + it(`should get privateKeyWIF ${fixture.privateKeyWIF} from HDNode`, () => { + const hdNode = bchjs.HDNode.fromXPriv(fixture.xpriv) + assert.equal(bchjs.HDNode.toWIF(hdNode), fixture.privateKeyWIF) + }) + }) + }) + + describe("#toXPub", () => { + fixtures.toXPub.forEach(fixture => { + it(`should create xpub ${fixture.xpub} from an HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const xpub = bchjs.HDNode.toXPub(hdNode) + assert.equal(xpub, fixture.xpub) + }) + }) + }) + + describe("#toXPriv", () => { + fixtures.toXPriv.forEach(fixture => { + it(`should create xpriv ${fixture.xpriv} from an HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const xpriv = bchjs.HDNode.toXPriv(hdNode) + assert.equal(xpriv, fixture.xpriv) + }) + }) + }) + + describe("#toKeyPair", () => { + fixtures.toKeyPair.forEach(fixture => { + it(`should get ECPair from an HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const keyPair = bchjs.HDNode.toKeyPair(hdNode) + assert.equal(typeof keyPair, "object") + }) + }) + }) + + describe("#toPublicKey", () => { + fixtures.toPublicKey.forEach(fixture => { + it(`should create public key buffer from an HDNode`, async () => { + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) + const hdNode = bchjs.HDNode.fromSeed(rootSeedBuffer) + const publicKeyBuffer = bchjs.HDNode.toPublicKey(hdNode) + assert.equal(typeof publicKeyBuffer, "object") + }) + }) + }) + + describe("#fromXPriv", () => { + it("should exercise fromXPriv", () => { + fixtures.fromXPriv.forEach(fixture => { + const hdNode = bchjs.HDNode.fromXPriv(fixture.xpriv) + it(`should create HDNode from xpriv ${fixture.xpriv}`, () => { + assert.notEqual(hdNode, null) + }) + + it(`should export xpriv ${fixture.xpriv}`, () => { + assert.equal(bchjs.HDNode.toXPriv(hdNode), fixture.xpriv) + }) + + it(`should export xpub ${fixture.xpub}`, () => { + assert.equal(bchjs.HDNode.toXPub(hdNode), fixture.xpub) + }) + + it(`should export legacy address ${fixture.legacy}`, () => { + assert.equal(bchjs.HDNode.toLegacyAddress(hdNode), fixture.legacy) + }) + + it(`should export cashaddress ${fixture.cashaddress}`, () => { + assert.equal(bchjs.HDNode.toCashAddress(hdNode), fixture.cashaddress) + }) + + it(`should export regtest cashaddress ${fixture.regtestaddress}`, () => { + assert.equal( + bchjs.HDNode.toCashAddress(hdNode, true), + fixture.regtestaddress + ) + }) + + it(`should export privateKeyWIF ${fixture.privateKeyWIF}`, () => { + assert.equal(bchjs.HDNode.toWIF(hdNode), fixture.privateKeyWIF) + }) + }) + }) + }) + + describe("#fromXPub", () => { + it("should exercise fromXPub", () => { + fixtures.fromXPub.forEach(fixture => { + const hdNode = bchjs.HDNode.fromXPub(fixture.xpub) + it(`should create HDNode from xpub ${fixture.xpub}`, () => { + assert.notEqual(hdNode, null) + }) + + it(`should export xpub ${fixture.xpub}`, () => { + assert.equal(bchjs.HDNode.toXPub(hdNode), fixture.xpub) + }) + + it(`should export legacy address ${fixture.legacy}`, () => { + assert.equal(bchjs.HDNode.toLegacyAddress(hdNode), fixture.legacy) + }) + + it(`should export cashaddress ${fixture.cashaddress}`, () => { + assert.equal(bchjs.HDNode.toCashAddress(hdNode), fixture.cashaddress) + }) + + it(`should export regtest cashaddress ${fixture.regtestaddress}`, () => { + assert.equal( + bchjs.HDNode.toCashAddress(hdNode, true), + fixture.regtestaddress + ) + }) + }) + }) + }) + + describe("#bip32", () => { + it("should create accounts and addresses", () => { + fixtures.accounts.forEach(async fixture => { + const seedBuffer = await bchjs.Mnemonic.toSeed(fixture.mnemonic) + console.log(`seedBuffer: ${seedBuffer.toString()}`) + const hdNode = bchjs.HDNode.fromSeed(seedBuffer) + const a = bchjs.HDNode.derivePath(hdNode, "0'") + const external = bchjs.HDNode.derivePath(a, "0") + const account = bchjs.HDNode.createAccount([external]) + + it(`#createAccount`, () => { + assert.notEqual(account, null) + }) + + describe("#getChainAddress", () => { + const external1 = bchjs.Address.toCashAddress( + account.getChainAddress(0) + ) + it(`should create external change address ${external1}`, () => { + assert.equal(external1, fixture.externals[0]) + }) + }) + + describe("#nextChainAddress", () => { + for (let i = 0; i < 4; i++) { + const ex = bchjs.Address.toCashAddress(account.nextChainAddress(0)) + it(`should create external change address ${ex}`, () => { + assert.equal(ex, fixture.externals[i + 1]) + }) + } + }) + }) + }) + }) + + describe("#sign", () => { + fixtures.sign.forEach(fixture => { + it(`should sign 32 byte hash buffer`, () => { + const hdnode = bchjs.HDNode.fromXPriv(fixture.privateKeyWIF) + const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") + const signatureBuf = bchjs.HDNode.sign(hdnode, buf) + assert.equal(typeof signatureBuf, "object") + }) + }) + }) + + describe("#verify", () => { + fixtures.verify.forEach(fixture => { + it(`should verify signed 32 byte hash buffer`, () => { + const hdnode1 = bchjs.HDNode.fromXPriv(fixture.privateKeyWIF1) + const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") + const signature = bchjs.HDNode.sign(hdnode1, buf) + const verify = bchjs.HDNode.verify(hdnode1, buf, signature) + assert.equal(verify, true) + }) + }) + }) + + describe("#isPublic", () => { + fixtures.isPublic.forEach(fixture => { + it(`should verify hdnode is public`, () => { + const node = bchjs.HDNode.fromXPub(fixture.xpub) + assert.equal(bchjs.HDNode.isPublic(node), true) + }) + }) + + fixtures.isPublic.forEach(fixture => { + it(`should verify hdnode is not public`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + assert.equal(bchjs.HDNode.isPublic(node), false) + }) + }) + }) + + describe("#isPrivate", () => { + fixtures.isPrivate.forEach(fixture => { + it(`should verify hdnode is not private`, () => { + const node = bchjs.HDNode.fromXPub(fixture.xpub) + assert.equal(bchjs.HDNode.isPrivate(node), false) + }) + }) + + fixtures.isPrivate.forEach(fixture => { + it(`should verify hdnode is private`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + assert.equal(bchjs.HDNode.isPrivate(node), true) + }) + }) + }) + + describe("#toIdentifier", () => { + fixtures.toIdentifier.forEach(fixture => { + it(`should get identifier of hdnode`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const publicKeyBuffer = bchjs.HDNode.toPublicKey(node) + const hash160 = bchjs.Crypto.hash160(publicKeyBuffer) + const identifier = bchjs.HDNode.toIdentifier(node) + assert.equal(identifier.toString("hex"), hash160.toString("hex")) + }) + }) + }) +}) diff --git a/test/unit/ipfs.js b/test/unit/ipfs.js new file mode 100644 index 0000000..3c6ea3d --- /dev/null +++ b/test/unit/ipfs.js @@ -0,0 +1,443 @@ +/* + Unit tests for the IPFS Class. +*/ + +const assert = require("chai").assert +const sinon = require("sinon") +const BCHJS = require("../../src/bch-js") +let bchjs + +const mockData = require("./fixtures/ipfs-mock") + +describe(`#IPFS`, () => { + let sandbox + + beforeEach(() => { + sandbox = sinon.createSandbox() + + bchjs = new BCHJS() + }) + + afterEach(() => sandbox.restore()) + + describe("#initUppy", () => { + it("should initialize uppy", () => { + bchjs.IPFS.initUppy() + }) + }) + + describe("#createFileModelServer", () => { + it("should throw an error if file does not exist", async () => { + try { + const path = "/non-existant-file" + + await bchjs.IPFS.createFileModelServer(path) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include(err.message, `Could not find this file`) + } + }) + + it("should create a new file model", async () => { + const path = `${__dirname}/ipfs.js` + + sandbox + .stub(bchjs.IPFS.axios, "post") + .resolves({ data: mockData.mockNewFileModel }) + + const result = await bchjs.IPFS.createFileModelServer(path) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "hostingCostBCH") + assert.property(result, "hostingCostUSD") + assert.property(result, "file") + + assert.property(result.file, "payloadLink") + assert.property(result.file, "hasBeenPaid") + assert.property(result.file, "_id") + assert.property(result.file, "schemaVersion") + assert.property(result.file, "size") + assert.property(result.file, "fileName") + assert.property(result.file, "fileExtension") + assert.property(result.file, "createdTimestamp") + assert.property(result.file, "hostingCost") + assert.property(result.file, "walletIndex") + assert.property(result.file, "bchAddr") + }) + }) + + describe("#uploadFileServer", () => { + it("should throw an error if file does not exist", async () => { + try { + const path = "/non-existant-file" + + await bchjs.IPFS.uploadFileServer(path) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include(err.message, `Could not find this file`) + } + }) + + it("should throw an error if modelId is not included", async () => { + try { + const path = `${__dirname}/ipfs.js` + + await bchjs.IPFS.uploadFileServer(path) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include(err.message, `Must include a file model ID`) + } + }) + + it("Should throw error if the file was not uploaded", async () => { + try { + const mock = { + successful: [], + failed: [ + { + id: "file id" + } + ] + } + sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mock) + + const path = `${__dirname}/ipfs.js` + await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52") + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(err) + assert.include(err.message, `The file could not be uploaded`) + } + }) + + it("should return file object if the file is uploaded", async () => { + try { + sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mockData.uploadData) + + const path = `${__dirname}/ipfs.js` + const result = await bchjs.IPFS.uploadFileServer( + path, + "5ec562319bfacc745e8d8a52" + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "schemaVersion") + assert.property(result, "size") + assert.property(result, "fileId") + assert.property(result, "fileName") + assert.property(result, "fileExtension") + } catch (err) { + //console.log(err) + assert.equal(true, false, "Unexpected result") + } + }) + }) + + describe("#getStatus", () => { + it("should throw an error if modelId is not included", async () => { + try { + await bchjs.IPFS.getStatus() + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include(err.message, `Must include a file model ID`) + } + }) + + it("should get data on an unpaid file", async () => { + const modelId = "5ec7392c2acfe57aa62e945a" + + sandbox + .stub(bchjs.IPFS.axios, "get") + .resolves({ data: mockData.unpaidFileData }) + + const result = await bchjs.IPFS.getStatus(modelId) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "hasBeenPaid") + assert.property(result, "satCost") + assert.property(result, "bchAddr") + assert.property(result, "ipfsHash") + assert.property(result, "fileId") + assert.property(result, "fileName") + }) + + it("should get data on an unpaid file", async () => { + const modelId = "5ec7392c2acfe57aa62e945a" + + sandbox + .stub(bchjs.IPFS.axios, "get") + .resolves({ data: mockData.paidFileData }) + + const result = await bchjs.IPFS.getStatus(modelId) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "hasBeenPaid") + assert.property(result, "satCost") + assert.property(result, "bchAddr") + assert.property(result, "ipfsHash") + assert.property(result, "fileId") + assert.property(result, "fileName") + }) + }) + + describe("#createFileModelWeb", () => { + it("should throw an error if file is undefined", async () => { + try { + let file + + await bchjs.IPFS.createFileModelWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include(err.message, `File is required`) + } + }) + it("should throw an error if file is empty", async () => { + try { + const file = {} + + await bchjs.IPFS.createFileModelWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include( + err.message, + `File should have the property 'name' of string type` + ) + } + }) + it("should throw an error if 'name' property is not included", async () => { + try { + const file = { + size: 5000 + } + + await bchjs.IPFS.createFileModelWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include( + err.message, + `File should have the property 'name' of string type` + ) + } + }) + + it("should throw an error if 'size' property is not included", async () => { + try { + const file = { + name: "ipfs.js" + } + + await bchjs.IPFS.createFileModelWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include( + err.message, + `File should have the property 'size' of number type` + ) + } + }) + + it("should create a new file model", async () => { + const file = { + name: "ipfs.js", + size: 5000, + type: "text/plain" + } + sandbox + .stub(bchjs.IPFS.axios, "post") + .resolves({ data: mockData.mockNewFileModel }) + + const result = await bchjs.IPFS.createFileModelWeb(file) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "hostingCostBCH") + assert.property(result, "hostingCostUSD") + assert.property(result, "file") + + assert.property(result.file, "payloadLink") + assert.property(result.file, "hasBeenPaid") + assert.property(result.file, "_id") + assert.property(result.file, "schemaVersion") + assert.property(result.file, "size") + assert.property(result.file, "fileName") + assert.property(result.file, "fileExtension") + assert.property(result.file, "createdTimestamp") + assert.property(result.file, "hostingCost") + assert.property(result.file, "walletIndex") + assert.property(result.file, "bchAddr") + }) + }) + + describe("#uploadFileWeb", () => { + it("should throw an error if file is undefined", async () => { + try { + let file + + await bchjs.IPFS.uploadFileWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include(err.message, `File is required`) + } + }) + it("should throw an error if file is empty", async () => { + try { + const file = {} + + await bchjs.IPFS.uploadFileWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include( + err.message, + `File should have the property 'name' of string type` + ) + } + }) + it("should throw an error if 'name' property is not included", async () => { + try { + const file = { + size: 5000, + type: "text/plain" + } + + await bchjs.IPFS.uploadFileWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include( + err.message, + `File should have the property 'name' of string type` + ) + } + }) + it("should throw an error if 'size' property is not included", async () => { + try { + const file = { + name: "ipfs.js", + type: "text/plain" + } + + await bchjs.IPFS.uploadFileWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include( + err.message, + `File should have the property 'size' of number type` + ) + } + }) + it("should throw an error if 'type' property is not included", async () => { + try { + const file = { + name: "ipfs.js", + size: 5000 + } + + await bchjs.IPFS.uploadFileWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include( + err.message, + `File should have the property 'type' of string type` + ) + } + }) + it("should throw an error if modelId is not included", async () => { + try { + const file = { + name: "ipfs.js", + size: 5000, + type: "text/plain" + } + await bchjs.IPFS.uploadFileWeb(file) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(`err.message: ${err.message}`) + assert.include(err.message, `Must include a file model ID`) + } + }) + + it("Should throw error if the file was not uploaded", async () => { + try { + const mock = { + successful: [], + failed: [ + { + id: "file id" + } + ] + } + sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mock) + + const file = { + name: "ipfs.js", + size: 5000, + type: "text/plain" + } + await bchjs.IPFS.uploadFileWeb(file, "5ec562319bfacc745e8d8a52") + + assert.equal(true, false, "Unexpected result") + } catch (err) { + //console.log(err) + assert.include(err.message, `The file could not be uploaded`) + } + }) + + it("should return file object if the file is uploaded", async () => { + try { + sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mockData.uploadData) + + const file = { + name: "ipfs.js", + size: 5000, + type: "text/plain" + } + const result = await bchjs.IPFS.uploadFileWeb( + file, + "5ec562319bfacc745e8d8a52" + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "schemaVersion") + assert.property(result, "size") + assert.property(result, "fileId") + assert.property(result, "fileName") + assert.property(result, "fileExtension") + } catch (err) { + //console.log(err) + assert.equal(true, false, "Unexpected result") + } + }) + }) +}) diff --git a/test/unit/mining.js b/test/unit/mining.js new file mode 100644 index 0000000..2079ed0 --- /dev/null +++ b/test/unit/mining.js @@ -0,0 +1,111 @@ +// Public npm libraries +const assert = require("assert") +const axios = require("axios") +const sinon = require("sinon") + +// Unit under test (uut) +const BCHJS = require("../../src/bch-js") +let bchjs + +describe("#Mining", () => { + beforeEach(() => { + bchjs = new BCHJS() + }) + + describe("#getBlockTemplate", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should get block template", done => { + const data = { + data: + "01000000017f6305e3b0b05f5b57a82f4e6d4187e148bbe56a947208390e488bad36472368000000006a47304402203b0079ff5b896187feb02e2679c87ac2fb8d483b60e0721ed33601e2c0eecc700220590f8a0e1a51b53b368294861fd5fc99db3a6607d0f4e543f6217108e208c1834121024c93c841d7f576584ffbf513b7abd8283e6562669905f6554f788fce4cc67a34ffffffff0228100000000000001976a914af78709a76abc8a28e568c9210c8247dd10cff2c88ac22020000000000001976a914f339927678803f451b41400737e7dc83c6a8682188ac00000000", + txid: + "7f462d71c649a0d8cfbaa2d20d8ff86677966b308f0ac9906ee015bf4453f97a", + hash: + "7f462d71c649a0d8cfbaa2d20d8ff86677966b308f0ac9906ee015bf4453f97a", + depends: [], + fee: 226, + sigops: 2 + } + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Mining.getBlockTemplate("") + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getMiningInfo", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should get mining info", done => { + const data = { + blocks: 527816, + currentblocksize: 89408, + currentblocktx: 156, + difficulty: 568757800682.7649, + blockprioritypercentage: 5, + errors: "", + networkhashps: 4347259225696976000, + pooledtx: 184, + chain: "main" + } + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Mining.getMiningInfo() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) + + describe("#getNetworkHashps", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should get network hashps", done => { + const data = 3586365937646890000 + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Mining.getNetworkHashps() + .then(result => { + assert.equal(data, result) + }) + .then(done, done) + }) + }) + + describe("#submitBlock", () => { + // TODO finish + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should TODO", done => { + const data = {} + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "post").returns(resolved) + + bchjs.Mining.submitBlock() + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) +}) diff --git a/test/unit/mnemonic.js b/test/unit/mnemonic.js new file mode 100644 index 0000000..2028653 --- /dev/null +++ b/test/unit/mnemonic.js @@ -0,0 +1,340 @@ +const fixtures = require("./fixtures/mnemonic.json") +const assert = require("assert") +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() + +describe("#Mnemonic", () => { + describe("#generate", () => { + it("should generate a 12 word mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(128) + assert.equal(mnemonic.split(" ").length, 12) + }) + + it("should generate a 15 word mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(160) + assert.equal(mnemonic.split(" ").length, 15) + }) + + it("should generate a 18 word mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(192) + assert.equal(mnemonic.split(" ").length, 18) + }) + + it("should generate an 21 word mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(224) + assert.equal(mnemonic.split(" ").length, 21) + }) + + it("should generate an 24 word mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(256) + assert.equal(mnemonic.split(" ").length, 24) + }) + + it("should generate an 24 word italian mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate( + 256, + bchjs.Mnemonic.wordLists().italian + ) + assert.equal(mnemonic.split(" ").length, 24) + }) + }) + + describe("#fromEntropy", () => { + it("should generate a 12 word mnemonic from 16 bytes of entropy", () => { + const rand = bchjs.Crypto.randomBytes(16) + const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) + assert.equal(mnemonic.split(" ").length, 12) + }) + + it("should generate a 15 word mnemonic from 20 bytes of entropy", () => { + const rand = bchjs.Crypto.randomBytes(20) + const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) + assert.equal(mnemonic.split(" ").length, 15) + }) + + it("should generate an 18 word mnemonic from 24 bytes of entropy", () => { + const rand = bchjs.Crypto.randomBytes(24) + const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) + assert.equal(mnemonic.split(" ").length, 18) + }) + + it("should generate an 21 word mnemonic from 28 bytes of entropy", () => { + const rand = bchjs.Crypto.randomBytes(28) + const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) + assert.equal(mnemonic.split(" ").length, 21) + }) + + it("should generate an 24 word mnemonic from 32 bytes of entropy", () => { + const rand = bchjs.Crypto.randomBytes(32) + const mnemonic = bchjs.Mnemonic.fromEntropy(rand.toString("hex")) + assert.equal(mnemonic.split(" ").length, 24) + }) + + it("should generate an 24 french word mnemonic 32 bytes of entropy", () => { + const rand = bchjs.Crypto.randomBytes(32) + const mnemonic = bchjs.Mnemonic.fromEntropy( + rand.toString("hex"), + bchjs.Mnemonic.wordLists().french + ) + assert.equal(mnemonic.split(" ").length, 24) + }) + + fixtures.fromEntropy.forEach(entropy => { + const mnemonic = bchjs.Mnemonic.fromEntropy(entropy.entropy) + it(`should convert ${entropy.entropy} to ${entropy.mnemonic}`, () => { + assert.equal(mnemonic, entropy.mnemonic) + }) + }) + }) + + describe("#toEntropy", () => { + it("should turn a 12 word mnemonic to entropy", () => { + const mnemonic = bchjs.Mnemonic.generate(128) + const entropy = bchjs.Mnemonic.toEntropy(mnemonic) + assert.equal(entropy.length, 16) + }) + + it("should turn a 15 word mnemonic to entropy", () => { + const mnemonic = bchjs.Mnemonic.generate(160) + const entropy = bchjs.Mnemonic.toEntropy(mnemonic) + assert.equal(entropy.length, 20) + }) + + it("should turn a 18 word mnemonic to entropy", () => { + const mnemonic = bchjs.Mnemonic.generate(192) + const entropy = bchjs.Mnemonic.toEntropy(mnemonic) + assert.equal(entropy.length, 24) + }) + + it("should turn a 21 word mnemonic to entropy", () => { + const mnemonic = bchjs.Mnemonic.generate(224) + const entropy = bchjs.Mnemonic.toEntropy(mnemonic) + assert.equal(entropy.length, 28) + }) + + it("should turn a 24 word mnemonic to entropy", () => { + const mnemonic = bchjs.Mnemonic.generate(256) + const entropy = bchjs.Mnemonic.toEntropy(mnemonic) + assert.equal(entropy.length, 32) + }) + + it("should turn a 24 word spanish mnemonic to entropy", () => { + const mnemonic = bchjs.Mnemonic.generate( + 256, + bchjs.Mnemonic.wordLists().spanish + ) + const entropy = bchjs.Mnemonic.toEntropy( + mnemonic, + bchjs.Mnemonic.wordLists().spanish + ) + assert.equal(entropy.length, 32) + }) + + fixtures.fromEntropy.forEach(fixture => { + const entropy = bchjs.Mnemonic.toEntropy(fixture.mnemonic) + it(`should convert ${fixture.mnemonic} to ${fixture.entropy}`, () => { + assert.equal(entropy.toString("hex"), fixture.entropy) + }) + }) + }) + + describe("#validate", () => { + it("fails for a mnemonic that is too short", () => { + assert.equal( + bchjs.Mnemonic.validate( + "mixed winner", + bchjs.Mnemonic.wordLists().english + ), + "Invalid mnemonic" + ) + }) + + it("fails for a mnemonic that is too long", () => { + assert.equal( + bchjs.Mnemonic.validate( + "mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake mixed winner decide drift danger together twice planet impose asthma catch require select mask awkward spy relief front work solar pitch economy render cake", + bchjs.Mnemonic.wordLists().english + ), + "Invalid mnemonic" + ) + }) + + it("fails if mnemonic words are not in the word list", () => { + assert.equal( + bchjs.Mnemonic.validate( + "failsauce one two three four five six seven eight nine ten eleven", + bchjs.Mnemonic.wordLists().english + ), + "failsauce is not in wordlist, did you mean balance?" + ) + }) + + it("validate a 128 bit mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(128) + assert.equal( + bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), + "Valid mnemonic" + ) + }) + + it("validate a 160 bit mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(160) + assert.equal( + bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), + "Valid mnemonic" + ) + }) + + it("validate a 192 bit mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(192) + assert.equal( + bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), + "Valid mnemonic" + ) + }) + + it("validate a 224 bit mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(224) + assert.equal( + bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), + "Valid mnemonic" + ) + }) + + it("validate a 256 bit mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate(256) + assert.equal( + bchjs.Mnemonic.validate(mnemonic, bchjs.Mnemonic.wordLists().english), + "Valid mnemonic" + ) + }) + + it("validate a 256 bit chinese simplified mnemonic", () => { + const mnemonic = bchjs.Mnemonic.generate( + 256, + bchjs.Mnemonic.wordLists().chinese_simplified + ) + assert.equal( + bchjs.Mnemonic.validate( + mnemonic, + bchjs.Mnemonic.wordLists().chinese_simplified + ), + "Valid mnemonic" + ) + }) + }) + + describe("#toSeed", () => { + it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 128 bit mnemonic", async () => { + const mnemonic = bchjs.Mnemonic.generate(128) + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") + assert.equal(rootSeedBuffer.byteLength, 64) + }) + + it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 160 bit mnemonic", async () => { + const mnemonic = bchjs.Mnemonic.generate(160) + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") + assert.equal(rootSeedBuffer.byteLength, 64) + }) + + it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 192 bit mnemonic", async () => { + const mnemonic = bchjs.Mnemonic.generate(192) + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") + assert.equal(rootSeedBuffer.byteLength, 64) + }) + + it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 224 bit mnemonic", async () => { + const mnemonic = bchjs.Mnemonic.generate(224) + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") + assert.equal(rootSeedBuffer.byteLength, 64) + }) + + it("should create 512 bit / 64 byte HMAC-SHA512 root seed from a 256 bit mnemonic", async () => { + const mnemonic = bchjs.Mnemonic.generate(256) + const rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic, "") + assert.equal(rootSeedBuffer.byteLength, 64) + }) + }) + + describe("#wordLists", () => { + it("return a list of 2048 english words", () => { + assert.equal(bchjs.Mnemonic.wordLists().english.length, 2048) + }) + + it("return a list of 2048 japanese words", () => { + assert.equal(bchjs.Mnemonic.wordLists().japanese.length, 2048) + }) + + it("return a list of 2048 chinese simplified words", () => { + assert.equal(bchjs.Mnemonic.wordLists().chinese_simplified.length, 2048) + }) + + it("return a list of 2048 chinese traditional words", () => { + assert.equal(bchjs.Mnemonic.wordLists().chinese_traditional.length, 2048) + }) + + it("return a list of 2048 french words", () => { + assert.equal(bchjs.Mnemonic.wordLists().french.length, 2048) + }) + + it("return a list of 2048 italian words", () => { + assert.equal(bchjs.Mnemonic.wordLists().italian.length, 2048) + }) + + it("return a list of 2048 korean words", () => { + assert.equal(bchjs.Mnemonic.wordLists().korean.length, 2048) + }) + + it("return a list of 2048 spanish words", () => { + assert.equal(bchjs.Mnemonic.wordLists().spanish.length, 2048) + }) + }) + + describe("#toKeypairs", async () => { + fixtures.toKeypairs.forEach(async (fixture, i) => { + const keypairs = await bchjs.Mnemonic.toKeypairs(fixture.mnemonic, 5) + keypairs.forEach((keypair, j) => { + it(`Generate keypair from mnemonic`, () => { + assert.equal( + keypair.privateKeyWIF, + fixtures.toKeypairs[i].output[j].privateKeyWIF + ) + // assert.equal( + // keypair.address, + // fixtures.toKeypairs[i].output[j].address + // ) + }) + }) + + const regtestKeypairs = await bchjs.Mnemonic.toKeypairs( + fixture.mnemonic, + 5, + true + ) + regtestKeypairs.forEach((keypair, j) => { + it(`Generate keypair from mnemonic`, () => { + assert.equal( + keypair.privateKeyWIF, + fixtures.toKeypairs[i].output[j].privateKeyWIFRegTest + ) + // assert.equal( + // keypair.address, + // fixtures.toKeypairs[i].output[j].regtestAddress + // ) + }) + }) + }) + }) + + describe("#findNearestWord", () => { + fixtures.findNearestWord.forEach((fixture, i) => { + const word = bchjs.Mnemonic.findNearestWord( + fixture.word, + bchjs.Mnemonic.wordLists()[fixture.language] + ) + it(`find word ${fixture.foundWord} near ${fixture.word} in ${fixture.language}`, () => { + assert.equal(word, fixture.foundWord) + }) + }) + }) +}) diff --git a/test/unit/ninsight.js b/test/unit/ninsight.js new file mode 100644 index 0000000..9670586 --- /dev/null +++ b/test/unit/ninsight.js @@ -0,0 +1,255 @@ +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/ninsight-mock") + +describe(`#Ninsight`, () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + describe(`#utxo`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.Ninsight.utxo(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 GET utxos for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.utxo }) + + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Ninsight.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "utxos") + assert.property(result, "legacyAddress") + assert.property(result, "cashAddress") + assert.property(result, "slpAddress") + assert.property(result, "scriptPubKey") + assert.property(result, "asm") + + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "txid") + assert.property(result.utxos[0], "vout") + assert.property(result.utxos[0], "amount") + assert.property(result.utxos[0], "satoshis") + assert.property(result.utxos[0], "height") + assert.property(result.utxos[0], "confirmations") + }) + + it(`should POST utxo details for an array of addresses`, async () => { + // Mock the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.utxoPost }) + + const addr = [ + "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7", + "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr" + ] + + const result = await bchjs.Ninsight.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isArray(result[0].utxos) + + assert.property(result[0], "utxos") + assert.property(result[0], "legacyAddress") + assert.property(result[0], "cashAddress") + assert.property(result[0], "slpAddress") + assert.property(result[0], "scriptPubKey") + assert.property(result[0], "asm") + }) + }) + + describe("#unconfirmed", () => { + it("should throw an error for improper input", async () => { + try { + const addr = 12345 + + await bchjs.Ninsight.unconfirmed(addr) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.include( + err.message, + "Input address must be a string or array of strings." + ) + } + }) + + it(`should POST utxos for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmed }) + + const addr = "bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5" + + const result = await bchjs.Ninsight.unconfirmed(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "utxos") + assert.property(result, "legacyAddress") + assert.property(result, "cashAddress") + assert.property(result, "slpAddress") + assert.property(result, "scriptPubKey") + + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "txid") + assert.property(result.utxos[0], "vout") + assert.property(result.utxos[0], "amount") + assert.property(result.utxos[0], "satoshis") + assert.property(result.utxos[0], "confirmations") + assert.property(result.utxos[0], "ts") + }) + + it(`should POST utxo details for an array of addresses`, async () => { + // Mock the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmedPost }) + + const addr = [ + "bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5", + "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr" + ] + + const result = await bchjs.Ninsight.unconfirmed(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isArray(result[0].utxos) + + assert.property(result[0], "utxos") + assert.property(result[0], "legacyAddress") + assert.property(result[0], "cashAddress") + assert.property(result[0], "slpAddress") + assert.property(result[0], "scriptPubKey") + }) + }) + + describe(`#transactions`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.Ninsight.transactions(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 transaction history for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.transactionsPost }) + + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Ninsight.transactions(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.property(result[0], "cashAddress") + assert.property(result[0], "legacyAddress") + assert.property(result[0], "txs") + assert.isArray(result[0].txs) + assert.property(result[0].txs[0], "txid") + assert.property(result[0].txs[0], "vin") + assert.property(result[0].txs[0], "vout") + }) + it(`should POST transaction history for an array of addresses`, async () => { + // Mock the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.transactionsPost }) + + const addr = [ + "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7", + "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr" + ] + + const result = await bchjs.Ninsight.transactions(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.property(result[0], "cashAddress") + assert.property(result[0], "legacyAddress") + assert.property(result[0], "txs") + assert.isArray(result[0].txs) + assert.property(result[0].txs[0], "txid") + }) + }) + describe(`#txDetails`, () => { + it(`should throw an error for improper input`, async () => { + try { + const txid = 12345 + + await bchjs.Ninsight.txDetails(txid) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include( + err.message, + `Transaction ID must be a string or array of strings.` + ) + } + }) + it(`should POST transaction details for a single TxID`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.detailsPost }) + + const txid = "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" + + const result = await bchjs.Ninsight.txDetails(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.property(result[0], "txid") + assert.property(result[0], "version") + assert.property(result[0], "locktime") + assert.property(result[0], "vin") + assert.property(result[0], "vout") + assert.property(result[0], "blockhash") + assert.property(result[0], "blockheight") + assert.property(result[0], "confirmations") + assert.property(result[0], "time") + assert.property(result[0], "blocktime") + assert.property(result[0], "isCoinBase") + assert.property(result[0], "valueOut") + assert.property(result[0], "size") + }) + it(`should POST transaction details for an array of TxIDs`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.detailsPost }) + + const txid = [ + "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33", + "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" + ] + + const result = await bchjs.Ninsight.txDetails(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.property(result[0], "txid") + assert.property(result[0], "vin") + assert.property(result[0], "vout") + }) + }) +}) diff --git a/test/unit/openbazaar.js b/test/unit/openbazaar.js new file mode 100644 index 0000000..d663fb0 --- /dev/null +++ b/test/unit/openbazaar.js @@ -0,0 +1,129 @@ +const chai = require("chai") +const assert = chai.assert +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() +const axios = require("axios") +const sinon = require("sinon") + +const mockData = require("./fixtures/openbazaar-mock") + +describe(`#OpenBazaar`, () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + describe(`#Balance`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.OpenBazaar.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 GET balance for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.balance }) + + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.OpenBazaar.balance(addr) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, [ + "page", + "totalPages", + "itemsOnPage", + "addrStr", + "balance", + "totalReceived", + "totalSent", + "unconfirmedBalance", + "unconfirmedTxApperances", + "txApperances", + "transactions" + ]) + assert.isArray(result.transactions) + }) + }) + + describe(`#utxo`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.OpenBazaar.utxo(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 GET utxos for a single address`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) + + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.OpenBazaar.utxo(addr) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAllKeys(result[0], [ + "txid", + "vout", + "amount", + "height", + "confirmations", + "satoshis" + ]) + }) + }) + + describe(`#tx`, () => { + it(`should throw an error for improper input`, async () => { + try { + const txid = 12345 + + await bchjs.OpenBazaar.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 GET tx details for a single txid`, async () => { + // Stub the network call. + sandbox.stub(axios, "get").resolves({ data: mockData.tx }) + + const txid = + "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7" + + const result = await bchjs.OpenBazaar.tx(txid) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, [ + "txid", + "version", + "vin", + "vout", + "blockhash", + "blockheight", + "confirmations", + "blocktime", + "valueOut", + "valueIn", + "fees", + "hex" + ]) + assert.isArray(result.vin) + assert.isArray(result.vout) + }) + }) +}) diff --git a/test/unit/raw-tranactions.js b/test/unit/raw-tranactions.js new file mode 100644 index 0000000..a2f41fa --- /dev/null +++ b/test/unit/raw-tranactions.js @@ -0,0 +1,135 @@ +/* + TODO: + -Create a mocking library of data to compare unit and integration tests. +*/ + +// Public npm libraries +const assert = require("assert") +const axios = require("axios") +const sinon = require("sinon") +// const nock = require("nock") // HTTP mocking + +// Unit under test (uut) +const BCHJS = require("../../src/bch-js") +// const bchjs = new BCHJS() +let bchjs + +// Used for debugging +// const util = require("util") +// util.inspect.defaultOptions = { depth: 1 } + +describe("#RawTransactions", () => { + beforeEach(() => { + bchjs = new BCHJS() + }) + + describe("#decodeRawTransaction", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should decode raw transaction", done => { + const data = { + txid: + "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a", + hash: + "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a", + size: 10, + version: 2, + locktime: 0, + vin: [], + vout: [] + } + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.RawTransactions.decodeRawTransaction("02000000000000000000") + .then(result => { + assert.equal(data, result) + }) + .then(done, done) + }) + }) + + describe("#decodeScript", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should decode script", async () => { + const data = { + asm: "OP_RETURN 5361746f736869204e616b616d6f746f", + type: "nulldata", + p2sh: "bitcoincash:prswx5965nfumux9qng5kj8hw603vcne7q08t8c6jp" + } + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + const result = await bchjs.RawTransactions.decodeScript( + "6a105361746f736869204e616b616d6f746f" + ) + //console.log(`result: ${util.inspect(result)}`) + + assert.deepEqual(data, result) + }) + }) + + describe("#getRawTransaction", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should get raw transaction", done => { + const data = + "020000000160d663961c63c7f0a07f22ec07b8f55b3935bfdbed8b1d8454916e8932fbf109010000006b4830450221008479fab4cfdcb111833d250a43f98ac26d43272b7a29cb1b9a0491eae5c44b3502203448b17253632395c29a7d62058bbfe93efb20fc8636ba6837002d464195aec04121029123258f7cdcd45b864066bcaa9b71f24d5ed1fa1dd36eaf107d8432b5014658ffffffff016d180000000000001976a91479d3297d1823149f4ec61df31d19f2fad5390c0288ac00000000" + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.RawTransactions.getRawTransaction( + "808d617eccaad4f1397fe07a06ec5ed15a0821cf22a3e0931c0c92aef9e572b6" + ) + .then(result => { + assert.equal(data, result) + }) + .then(done, done) + }) + }) + + describe("#sendRawTransaction", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should send single raw transaction", async () => { + const data = "Error: transaction already in block chain" + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + const result = await bchjs.RawTransactions.sendRawTransaction( + "020000000160d663961c63c7f0a07f22ec07b8f55b3935bfdbed8b1d8454916e8932fbf109010000006b4830450221008479fab4cfdcb111833d250a43f98ac26d43272b7a29cb1b9a0491eae5c44b3502203448b17253632395c29a7d62058bbfe93efb20fc8636ba6837002d464195aec04121029123258f7cdcd45b864066bcaa9b71f24d5ed1fa1dd36eaf107d8432b5014658ffffffff016d180000000000001976a91479d3297d1823149f4ec61df31d19f2fad5390c0288ac00000000" + ) + + assert.equal(data, result) + }) + + // it("should send an array of raw transactions", async () => { + // const data = "Error: transaction already in block chain" + // + // // Mock the http call to rest.bitcoin.com + // // nock(`${bchjs.RawTransactions.restURL}`) + // // .post(uri => uri.includes(`/`)) + // // .reply(200, { data: data }) + // sandbox.stub(bchjs, "axios").resolves({ data: data }) + // + // const result = await bchjs.RawTransactions.sendRawTransaction([ + // "020000000160d663961c63c7f0a07f22ec07b8f55b3935bfdbed8b1d8454916e8932fbf109010000006b4830450221008479fab4cfdcb111833d250a43f98ac26d43272b7a29cb1b9a0491eae5c44b3502203448b17253632395c29a7d62058bbfe93efb20fc8636ba6837002d464195aec04121029123258f7cdcd45b864066bcaa9b71f24d5ed1fa1dd36eaf107d8432b5014658ffffffff016d180000000000001976a91479d3297d1823149f4ec61df31d19f2fad5390c0288ac00000000" + // ]) + // + // assert.equal(data, result.data) + // }) + }) +}) diff --git a/test/unit/scripts.js b/test/unit/scripts.js new file mode 100644 index 0000000..7095ba7 --- /dev/null +++ b/test/unit/scripts.js @@ -0,0 +1,487 @@ +// Public npm libraries +const assert = require("assert") +const Buffer = require("safe-buffer").Buffer + +// Mocks +const fixtures = require("./fixtures/script.json") + +// Unit under test (uut) +const BCHJS = require("../../src/bch-js") +let bchjs + +describe("#Script", () => { + beforeEach(() => { + bchjs = new BCHJS() + }) + + describe("#decode", () => { + describe("P2PKH scriptSig", () => { + fixtures.decodeScriptSig.forEach(fixture => { + it(`should decode scriptSig buffer`, () => { + const decodedScriptSig = bchjs.Script.decode( + Buffer.from(fixture.scriptSigHex, "hex") + ) + assert.equal(typeof decodedScriptSig, "object") + }) + + it(`should decode scriptSig buffer to cash address ${fixture.cashAddress}`, () => { + const decodedScriptSig = bchjs.Script.decode( + Buffer.from(fixture.scriptSigHex, "hex") + ) + const address = bchjs.HDNode.toCashAddress( + bchjs.ECPair.fromPublicKey(decodedScriptSig[1]) + ) + assert.equal(address, fixture.cashAddress) + }) + + it(`should decode scriptSig buffer to legacy address ${fixture.legacyAddress}`, () => { + const decodedScriptSig = bchjs.Script.decode( + Buffer.from(fixture.scriptSigHex, "hex") + ) + const address = bchjs.HDNode.toLegacyAddress( + bchjs.ECPair.fromPublicKey(decodedScriptSig[1]) + ) + assert.equal(address, fixture.legacyAddress) + }) + }) + }) + + describe("P2PKH scriptPubKey", () => { + fixtures.decodeScriptPubKey.forEach(fixture => { + it(`should decode scriptSig buffer`, () => { + const decodedScriptPubKey = bchjs.Script.decode( + Buffer.from(fixture.scriptPubKeyHex, "hex") + ) + assert.equal(decodedScriptPubKey.length, 5) + }) + + it(`should match hashed pubKey ${fixture.pubKeyHex}`, () => { + const decodedScriptPubKey = bchjs.Script.decode( + Buffer.from(fixture.scriptPubKeyHex, "hex") + ) + const data = Buffer.from(fixture.pubKeyHex, "hex") + const hash160 = bchjs.Crypto.hash160(data).toString("hex") + assert.equal(decodedScriptPubKey[2].toString("hex"), hash160) + }) + }) + }) + }) + + describe("#encode", () => { + describe("P2PKH scriptSig", () => { + fixtures.encodeScriptSig.forEach(fixture => { + it(`should encode scriptSig chunks to buffer`, () => { + const arr = [ + Buffer.from(fixture.scriptSigChunks[0], "hex"), + Buffer.from(fixture.scriptSigChunks[1], "hex") + ] + const encodedScriptSig = bchjs.Script.encode(arr) + assert.equal(typeof encodedScriptSig, "object") + }) + }) + }) + + describe("P2PKH scriptPubKey", () => { + fixtures.encodeScriptPubKey.forEach(fixture => { + it(`should encode scriptPubKey buffer`, () => { + const decodedScriptPubKey = bchjs.Script.decode( + Buffer.from(fixture.scriptPubKeyHex, "hex") + ) + const compiledScriptPubKey = bchjs.Script.encode(decodedScriptPubKey) + assert.equal( + compiledScriptPubKey.toString("hex"), + fixture.scriptPubKeyHex + ) + }) + }) + }) + + describe("Encode SLP SEND OP_RETURN properly", () => { + it("should correctly compile OP_RETURN SLP SEND transaction", () => { + const scriptArr = [ + bchjs.Script.opcodes.OP_RETURN, + Buffer.from("534c5000", "hex"), + Buffer.from("01", "hex"), + Buffer.from(`SEND`), + Buffer.from( + "73db55368981e4878440637e448d4abe7f661be5c3efdcbcb63bd86a01a76b5a", + "hex" + ), + Buffer.from("00000001", "hex") + ] + + const data = bchjs.Script.encode2(scriptArr) + + // convert data to a hex string + let str = "" + for (let i = 0; i < data.length; i++) { + let hex = Number(data[i]).toString(16) + + // zero pad when its a single digit. + hex = `0${hex}` + hex = hex.slice(-2) + //console.log(`hex: ${hex}`) + + str += hex + } + console.log(`Hex string: ${str}`) + + //console.log(`scriptArr: ${JSON.stringify(data,null,2)}`) + + const correctStr = + "6a04534c500001010453454e442073db55368981e4878440637e448d4abe7f661be5c3efdcbcb63bd86a01a76b5a0400000001" + + assert.equal(str, correctStr) + }) + }) + }) + + describe("#toASM", () => { + describe("P2PKH scriptSig", () => { + fixtures.scriptSigToASM.forEach(fixture => { + it(`should encode scriptSig buffer to ${fixture.asm}`, () => { + const arr = [ + Buffer.from(fixture.scriptSigChunks[0], "hex"), + Buffer.from(fixture.scriptSigChunks[1], "hex") + ] + const compiledScriptSig = bchjs.Script.encode(arr) + const asm = bchjs.Script.toASM(compiledScriptSig) + assert.equal(asm, fixture.asm) + }) + }) + }) + + describe("P2PKH scriptPubKey", () => { + fixtures.scriptPubKeyToASM.forEach(fixture => { + it(`should compile scriptPubKey buffer to ${fixture.asm}`, () => { + const asm = bchjs.Script.toASM( + Buffer.from(fixture.scriptPubKeyHex, "hex") + ) + assert.equal(asm, fixture.asm) + }) + }) + }) + }) + + describe("#fromASM", () => { + describe("P2PKH scriptSig", () => { + fixtures.scriptSigFromASM.forEach(fixture => { + it(`should decode scriptSig asm to buffer`, () => { + const buf = bchjs.Script.fromASM(fixture.asm) + assert.equal(typeof buf, "object") + }) + }) + }) + + describe("P2PKH scriptPubKey", () => { + fixtures.scriptPubKeyFromASM.forEach(fixture => { + it(`should decode scriptPubKey asm to buffer`, () => { + const buf = bchjs.Script.fromASM(fixture.asm) + assert.equal(typeof buf, "object") + }) + }) + }) + }) + + describe("#OPCodes", () => { + for (const opcode in fixtures.opcodes) { + it(`should have OP Code ${opcode}`, () => { + assert.equal(bchjs.Script.opcodes[opcode], fixtures.opcodes[opcode]) + }) + } + }) + + describe("#classifyInput", () => { + fixtures.classifyInput.forEach(fixture => { + it(`should classify input type ${fixture.type}`, () => { + const type = bchjs.Script.classifyInput( + bchjs.Script.fromASM(fixture.script) + ) + assert.equal(type, fixture.type) + }) + }) + }) + + describe("#classifyOutput", () => { + fixtures.classifyOutput.forEach(fixture => { + it(`should classify ouput type ${fixture.type}`, () => { + const type = bchjs.Script.classifyOutput( + bchjs.Script.fromASM(fixture.script) + ) + assert.equal(type, fixture.type) + }) + }) + }) + + describe("#nullDataTemplate", () => { + fixtures.nullDataTemplate.forEach(fixture => { + it(`should encode nulldata output`, () => { + const buf = bchjs.Script.nullData.output.encode( + Buffer.from(`${fixture.data}`, "ascii") + ) + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode nulldata output`, () => { + const buf = bchjs.Script.nullData.output.decode( + Buffer.from(`${fixture.hex}`, "hex") + ) + assert.equal(buf.toString("ascii"), fixture.data) + }) + + it(`should confirm correctly formatted nulldata output`, () => { + const buf = bchjs.Script.nullData.output.encode( + Buffer.from(`${fixture.data}`, "ascii") + ) + const valid = bchjs.Script.nullData.output.check(buf) + assert.equal(valid, true) + }) + }) + }) + + describe("#pubKeyTemplate", () => { + describe("#pubKeyInputTemplate", () => { + fixtures.pubKeyInputTemplate.forEach(fixture => { + it(`should encode pubKey input`, () => { + const buf = bchjs.Script.pubKey.input.encode( + Buffer.from(fixture.signature, "hex") + ) + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode pubKey input`, () => { + const buf = bchjs.Script.pubKey.input.decode( + Buffer.from(fixture.hex, "hex") + ) + assert.equal(buf.toString("hex"), fixture.signature) + }) + + it(`should confirm correctly formatted pubKeyHash input`, () => { + const buf = bchjs.Script.pubKey.input.encode( + Buffer.from(fixture.signature, "hex") + ) + const valid = bchjs.Script.pubKey.input.check(buf) + assert.equal(valid, true) + }) + }) + }) + + describe("#pubKeyOutputTemplate", () => { + fixtures.pubKeyOutputTemplate.forEach(fixture => { + it(`should encode pubKey output`, () => { + const buf = bchjs.Script.pubKey.output.encode( + Buffer.from(fixture.pubKey, "hex") + ) + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode pubKey output`, () => { + const buf = bchjs.Script.pubKey.output.decode( + Buffer.from(`${fixture.hex}`, "hex") + ) + assert.equal(buf.toString("hex"), fixture.pubKey) + }) + + it(`should confirm correctly formatted pubKey output`, () => { + const buf = bchjs.Script.pubKey.output.encode( + Buffer.from(fixture.pubKey, "hex") + ) + const valid = bchjs.Script.pubKey.output.check(buf) + assert.equal(valid, true) + }) + }) + }) + }) + + describe("#pubKeyHashTemplate", () => { + describe("#pubKeyHashInputTemplate", () => { + fixtures.pubKeyHashInputTemplate.forEach(fixture => { + it(`should encode pubKeyHash input`, () => { + const buf = bchjs.Script.pubKeyHash.input.encode( + Buffer.from(fixture.signature, "hex"), + Buffer.from(fixture.pubKey, "hex") + ) + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode pubKeyHash input signature`, () => { + const buf = bchjs.Script.pubKeyHash.input.decode( + Buffer.from(fixture.hex, "hex") + ) + assert.equal(buf.signature.toString("hex"), fixture.signature) + }) + + it(`should decode pubKeyHash input pubkey`, () => { + const buf = bchjs.Script.pubKeyHash.input.decode( + Buffer.from(fixture.hex, "hex") + ) + assert.equal(buf.pubKey.toString("hex"), fixture.pubKey) + }) + + it(`should confirm correctly formatted pubKeyHash input`, () => { + const buf = bchjs.Script.pubKeyHash.input.encode( + Buffer.from(fixture.signature, "hex"), + Buffer.from(fixture.pubKey, "hex") + ) + const valid = bchjs.Script.pubKeyHash.input.check(buf) + assert.equal(valid, true) + }) + }) + }) + + describe("#pubKeyHashOutputTemplate", () => { + it("should exercise pubKeyHashOutputTemplate", () => { + fixtures.pubKeyHashOutputTemplate.forEach(fixture => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const identifier = bchjs.HDNode.toIdentifier(node) + it(`should encode pubKeyHash output`, () => { + const buf = bchjs.Script.pubKeyHash.output.encode(identifier) + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode pubKeyHash output`, () => { + const buf = bchjs.Script.pubKeyHash.output.decode( + Buffer.from(`${fixture.hex}`, "hex") + ) + assert.equal(buf.toString("hex"), identifier.toString("hex")) + }) + + it(`should confirm correctly formatted pubKeyHash output`, () => { + const buf = bchjs.Script.pubKeyHash.output.encode(identifier) + const valid = bchjs.Script.pubKeyHash.output.check(buf) + assert.equal(valid, true) + }) + }) + }) + }) + }) + + describe("#multisigTemplate", () => { + describe("#multisigInputTemplate", () => { + fixtures.multisigInputTemplate.forEach(fixture => { + it(`should encode multisig input`, () => { + const signatures = fixture.signatures.map(signature => + signature + ? Buffer.from(signature, "hex") + : bchjs.Script.opcodes.OP_0 + ) + + const buf = bchjs.Script.multisig.input.encode(signatures) + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode multisig input`, () => { + const buf = bchjs.Script.multisig.input.decode( + Buffer.from(fixture.hex, "hex") + ) + assert.equal(buf[0].toString("hex"), fixture.signatures[0]) + }) + + it(`should confirm correctly formatted multisig input`, () => { + const signatures = fixture.signatures.map(signature => + signature + ? Buffer.from(signature, "hex") + : bchjs.Script.opcodes.OP_0 + ) + + const buf = bchjs.Script.multisig.input.encode(signatures) + const valid = bchjs.Script.multisig.input.check(buf) + assert.equal(valid, true) + }) + }) + }) + + describe("#multisigOutputTemplate", () => { + fixtures.multisigOutputTemplate.forEach(fixture => { + it(`should encode multisig output`, () => { + const pubKeys = fixture.pubKeys.map(p => Buffer.from(p, "hex")) + const m = pubKeys.length + const buf = bchjs.Script.multisig.output.encode(m, pubKeys) + + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode multisig output`, () => { + const output = bchjs.Script.multisig.output.decode( + Buffer.from(`${fixture.hex}`, "hex") + ) + assert.equal(output.m, fixture.pubKeys.length) + }) + + it(`should confirm correctly formatted multisig output`, () => { + const pubKeys = fixture.pubKeys.map(p => Buffer.from(p, "hex")) + const m = pubKeys.length + const buf = bchjs.Script.multisig.output.encode(m, pubKeys) + const valid = bchjs.Script.multisig.output.check(buf) + assert.equal(valid, true) + }) + }) + }) + }) + + describe("#scriptHashTemplate", () => { + describe("#scriptHashInputTemplate", () => { + fixtures.scriptHashInputTemplate.forEach(fixture => { + it(`should encode scriptHash input`, () => { + const buf = bchjs.Script.scriptHash.input.encode( + bchjs.Script.fromASM(fixture.redeemScriptSig), + bchjs.Script.fromASM(fixture.redeemScript) + ) + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode scriptHash input`, () => { + const redeemScriptSig = bchjs.Script.fromASM(fixture.redeemScriptSig) + const redeemScript = bchjs.Script.fromASM(fixture.redeemScript) + assert.deepEqual( + bchjs.Script.scriptHash.input.decode( + Buffer.from(fixture.hex, "hex") + ), + { + redeemScriptSig: redeemScriptSig, + redeemScript: redeemScript + } + ) + }) + + it(`should confirm correctly formatted scriptHash input`, () => { + const buf = bchjs.Script.scriptHash.input.encode( + bchjs.Script.fromASM(fixture.redeemScriptSig), + bchjs.Script.fromASM(fixture.redeemScript) + ) + const valid = bchjs.Script.scriptHash.input.check(buf) + assert.equal(valid, true) + }) + }) + }) + + describe("#scriptHashOutputTemplate", () => { + fixtures.scriptHashOutputTemplate.forEach(fixture => { + it(`should encode scriptHash output`, () => { + const redeemScript = bchjs.Script.fromASM(fixture.output) + const scriptHash = bchjs.Crypto.hash160(redeemScript) + const buf = bchjs.Script.scriptHash.output.encode(scriptHash) + + assert.equal(buf.toString("hex"), fixture.hex) + }) + + it(`should decode scriptHash output`, () => { + const redeemScript = bchjs.Script.fromASM(fixture.output) + const scriptHash = bchjs.Crypto.hash160(redeemScript) + const buf = bchjs.Script.scriptHash.output.decode( + Buffer.from(`${fixture.hex}`, "hex") + ) + assert.deepEqual(buf, scriptHash) + }) + + it(`should confirm correctly formatted scriptHash output`, () => { + const redeemScript = bchjs.Script.fromASM(fixture.output) + const scriptHash = bchjs.Crypto.hash160(redeemScript) + const buf = bchjs.Script.scriptHash.output.encode(scriptHash) + const valid = bchjs.Script.scriptHash.output.check(buf) + assert.equal(valid, true) + }) + }) + }) + }) +}) diff --git a/test/unit/slp-address.js b/test/unit/slp-address.js new file mode 100644 index 0000000..ef0ed07 --- /dev/null +++ b/test/unit/slp-address.js @@ -0,0 +1,846 @@ +const assert = require("assert") + +const BCHJS = require("../../src/bch-js") +// const SLP = require("../../src/slp/slp") +// const SLP = new slp({ restURL: "http://fakeurl.com/" }) +let slp + +const fixtures = require("./fixtures/slp/address.json") +//const axios = require("axios") +//const sinon = require("sinon") + +function flatten(arrays) { + return [].concat.apply([], arrays) +} + +const LEGACY_MAINNET_ADDRESSES = flatten([ + fixtures.mainnet.legacyP2PKH, + fixtures.mainnet.legacyP2SH +]) + +const CASH_MAINNET_ADDRESSES = flatten([ + fixtures.mainnet.cashAddressP2PKH, + fixtures.mainnet.cashAddressP2SH +]) + +const SLP_MAINNET_ADDRESSES = flatten([ + fixtures.mainnet.slpAddressP2PKH, + fixtures.mainnet.slpAddressP2SH +]) + +const LEGACY_TESTNET_ADDRESSES = flatten([ + fixtures.testnet.legacyP2PKH, + fixtures.testnet.legacyP2SH +]) + +const CASH_TESTNET_ADDRESSES = flatten([ + fixtures.testnet.cashAddressP2PKH, + fixtures.testnet.cashAddressP2SH +]) + +const SLP_TESTNET_ADDRESSES = flatten([ + fixtures.testnet.slpAddressP2PKH, + fixtures.testnet.slpAddressP2SH +]) + +const MAINNET_P2PKH_ADDRESSES = flatten([ + fixtures.mainnet.legacyP2PKH, + fixtures.mainnet.cashAddressP2PKH, + fixtures.mainnet.slpAddressP2PKH +]) + +const TESTNET_P2PKH_ADDRESSES = flatten([ + fixtures.testnet.legacyP2PKH, + fixtures.testnet.cashAddressP2PKH, + fixtures.testnet.slpAddressP2PKH +]) + +const MAINNET_P2SH_ADDRESSES = flatten([ + fixtures.mainnet.legacyP2SH, + fixtures.mainnet.cashAddressP2SH, + fixtures.mainnet.slpAddressP2SH +]) + +const TESTNET_P2SH_ADDRESSES = flatten([ + fixtures.testnet.legacyP2SH, + fixtures.testnet.cashAddressP2SH, + fixtures.testnet.slpAddressP2SH +]) +/* +const CASH_MAINNET_ADDRESSES_NO_PREFIX = CASH_MAINNET_ADDRESSES.map(address => { + const parts = address.split(":") + return parts[1] +}) + +const CASH_TESTNET_ADDRESSES_NO_PREFIX = CASH_TESTNET_ADDRESSES.map(address => { + const parts = address.split(":") + return parts[1] +}) + +const SLP_MAINNET_ADDRESSES_NO_PREFIX = SLP_MAINNET_ADDRESSES.map(address => { + const parts = address.split(":") + return parts[1] +}) + +const SLP_TESTNET_ADDRESSES_NO_PREFIX = SLP_TESTNET_ADDRESSES.map(address => { + const parts = address.split(":") + return parts[1] +}) +*/ +describe("#SLP Address", () => { + beforeEach(() => { + const bchjs = new BCHJS() + // console.log(`bchjs.restURL: ${bchjs.restURL}`) + slp = bchjs.SLP + }) + + describe("#mainnet", () => { + describe("#toLegacyAddress", () => { + it("should convert mainnet legacy address format to itself correctly", () => { + assert.deepEqual( + LEGACY_MAINNET_ADDRESSES.map(address => + slp.Address.toLegacyAddress(address) + ), + LEGACY_MAINNET_ADDRESSES + ) + }) + + it(`should convert cashAddr to legacyAddr`, async () => { + assert.deepEqual( + CASH_MAINNET_ADDRESSES.map(address => + slp.Address.toLegacyAddress(address) + ), + LEGACY_MAINNET_ADDRESSES + ) + }) + + it(`should convert slpAddr to legacyAddr`, async () => { + assert.deepEqual( + SLP_MAINNET_ADDRESSES.map(address => + slp.Address.toLegacyAddress(address) + ), + LEGACY_MAINNET_ADDRESSES + ) + }) + }) + + describe("#toCashAddress", () => { + it("should convert mainnet cash address format to itself correctly", () => { + assert.deepEqual( + CASH_MAINNET_ADDRESSES.map(address => + slp.Address.toCashAddress(address) + ), + CASH_MAINNET_ADDRESSES + ) + }) + + it(`should convert legacyAddr to cashAddr`, async () => { + assert.deepEqual( + LEGACY_MAINNET_ADDRESSES.map(address => + slp.Address.toCashAddress(address) + ), + CASH_MAINNET_ADDRESSES + ) + }) + + it(`should convert slpAddr to cashAddr`, async () => { + assert.deepEqual( + SLP_MAINNET_ADDRESSES.map(address => + slp.Address.toCashAddress(address) + ), + CASH_MAINNET_ADDRESSES + ) + }) + }) + + describe("#toSLPAddress", () => { + it("should convert mainnet slp address format to itself correctly", () => { + assert.deepEqual( + SLP_MAINNET_ADDRESSES.map(address => + slp.Address.toSLPAddress(address) + ), + SLP_MAINNET_ADDRESSES + ) + }) + + it(`should convert legacyAddr to slpAddr`, async () => { + assert.deepEqual( + LEGACY_MAINNET_ADDRESSES.map(address => + slp.Address.toSLPAddress(address) + ), + SLP_MAINNET_ADDRESSES + ) + }) + + it(`should convert cashAddr to slpAddr`, async () => { + assert.deepEqual( + CASH_MAINNET_ADDRESSES.map(address => + slp.Address.toSLPAddress(address) + ), + SLP_MAINNET_ADDRESSES + ) + }) + }) + + describe("#isLegacyAddress", () => { + describe("is legacy addr", () => { + LEGACY_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a legacy address`, () => { + const isLegacyaddr = slp.Address.isLegacyAddress(address) + assert.equal(isLegacyaddr, true) + }) + }) + }) + + describe("cashaddr is not legacy addr", () => { + CASH_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a legacy address`, () => { + const isLegacyaddr = slp.Address.isLegacyAddress(address) + assert.equal(isLegacyaddr, false) + }) + }) + }) + + describe("slpaddr is not legacy addr", () => { + SLP_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a legacy address`, () => { + const isLegacyaddr = slp.Address.isLegacyAddress(address) + assert.equal(isLegacyaddr, false) + }) + }) + }) + }) + + describe("#isCashAddress", () => { + describe("is cashaddr", () => { + CASH_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a cashaddr address`, () => { + const isCashaddr = slp.Address.isCashAddress(address) + assert.equal(isCashaddr, true) + }) + }) + }) + + describe("legacy is not cash addr", () => { + LEGACY_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a cash address`, () => { + const isCashaddr = slp.Address.isCashAddress(address) + assert.equal(isCashaddr, false) + }) + }) + }) + + describe("slpaddr is not cash addr", () => { + SLP_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a cash address`, () => { + const isCashaddr = slp.Address.isCashAddress(address) + assert.equal(isCashaddr, false) + }) + }) + }) + }) + + describe("#isSLPAddress", () => { + describe("is slpaddr", () => { + SLP_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is an slp address`, () => { + const isSLPaddr = slp.Address.isSLPAddress(address) + assert.equal(isSLPaddr, true) + }) + }) + }) + + describe("legacy is not slp addr", () => { + LEGACY_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not an slp address`, () => { + const isSLPaddr = slp.Address.isSLPAddress(address) + assert.equal(isSLPaddr, false) + }) + }) + }) + + describe("cash is not slp addr", () => { + CASH_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not an slp address`, () => { + const isSLPaddr = slp.Address.isSLPAddress(address) + assert.equal(isSLPaddr, false) + }) + }) + }) + }) + + describe("#isMainnetAddress", () => { + describe("mainnet legacy addr", () => { + LEGACY_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a mainnet address`, () => { + const isMainnetaddr = slp.Address.isMainnetAddress(address) + assert.equal(isMainnetaddr, true) + }) + }) + }) + + describe("mainnet cash addr", () => { + CASH_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a mainnet address`, () => { + const isMainnetaddr = slp.Address.isMainnetAddress(address) + assert.equal(isMainnetaddr, true) + }) + }) + }) + + describe("mainnet slp addr", () => { + SLP_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a mainnet address`, () => { + const isMainnetaddr = slp.Address.isMainnetAddress(address) + assert.equal(isMainnetaddr, true) + }) + }) + }) + + describe("testnet legacy addr", () => { + LEGACY_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a mainnet address`, () => { + const isMainnetaddr = slp.Address.isMainnetAddress(address) + assert.equal(isMainnetaddr, false) + }) + }) + }) + + describe("testnet cash addr", () => { + CASH_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a mainnet address`, () => { + const isMainnetaddr = slp.Address.isMainnetAddress(address) + assert.equal(isMainnetaddr, false) + }) + }) + }) + + describe("testnet slp addr", () => { + SLP_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a mainnet address`, () => { + const isMainnetaddr = slp.Address.isMainnetAddress(address) + assert.equal(isMainnetaddr, false) + }) + }) + }) + }) + + describe("#isP2PKHAddress", () => { + describe("mainnet legacy addr", () => { + MAINNET_P2PKH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a P2PKH address`, () => { + const isP2PKHaddr = slp.Address.isP2PKHAddress(address) + assert.equal(isP2PKHaddr, true) + }) + }) + }) + }) + + describe("#isP2SHAddress", () => { + describe("mainnet legacy addr", () => { + MAINNET_P2SH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a P2SH address`, () => { + const isP2SHaddr = slp.Address.isP2SHAddress(address) + assert.equal(isP2SHaddr, true) + }) + }) + }) + }) + + describe("#detectAddressFormat", () => { + LEGACY_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a legacy address`, () => { + const isLegacy = slp.Address.detectAddressFormat(address) + assert.equal(isLegacy, "legacy") + }) + }) + + CASH_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a cash address`, () => { + const isCashaddr = slp.Address.detectAddressFormat(address) + assert.equal(isCashaddr, "cashaddr") + }) + }) + + SLP_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is an slp address`, () => { + const isSlpaddr = slp.Address.detectAddressFormat(address) + assert.equal(isSlpaddr, "slpaddr") + }) + }) + }) + + describe("#detectAddressNetwork", () => { + LEGACY_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a mainnet address`, () => { + const isMainnet = slp.Address.detectAddressNetwork(address) + assert.equal(isMainnet, "mainnet") + }) + }) + + CASH_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a mainnet address`, () => { + const isMainnet = slp.Address.detectAddressNetwork(address) + assert.equal(isMainnet, "mainnet") + }) + }) + + SLP_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a mainnet address`, () => { + const isMainnet = slp.Address.detectAddressNetwork(address) + assert.equal(isMainnet, "mainnet") + }) + }) + }) + + describe("#detectAddressType", () => { + MAINNET_P2PKH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a p2pkh address`, () => { + const isp2pkh = slp.Address.detectAddressType(address) + assert.equal(isp2pkh, "p2pkh") + }) + }) + + MAINNET_P2SH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a p2sh address`, () => { + const isp2sh = slp.Address.detectAddressType(address) + assert.equal(isp2sh, "p2sh") + }) + }) + }) + }) + + describe("#testnet", () => { + describe("#toLegacyAddress", () => { + it("should convert testnet legacy address format to itself correctly", () => { + assert.deepEqual( + LEGACY_TESTNET_ADDRESSES.map(address => + slp.Address.toLegacyAddress(address) + ), + LEGACY_TESTNET_ADDRESSES + ) + }) + + it(`should convert cashAddr to legacyAddr`, async () => { + assert.deepEqual( + CASH_TESTNET_ADDRESSES.map(address => + slp.Address.toLegacyAddress(address) + ), + LEGACY_TESTNET_ADDRESSES + ) + }) + + it(`should convert slpAddr to legacyAddr`, async () => { + assert.deepEqual( + SLP_TESTNET_ADDRESSES.map(address => + slp.Address.toLegacyAddress(address) + ), + LEGACY_TESTNET_ADDRESSES + ) + }) + }) + + describe("#toCashAddress", () => { + it("should convert testnet cash address format to itself correctly", () => { + assert.deepEqual( + CASH_TESTNET_ADDRESSES.map(address => + slp.Address.toCashAddress(address) + ), + CASH_TESTNET_ADDRESSES + ) + }) + + it(`should convert legacyAddr to cashAddr`, async () => { + assert.deepEqual( + LEGACY_TESTNET_ADDRESSES.map(address => + slp.Address.toCashAddress(address) + ), + CASH_TESTNET_ADDRESSES + ) + }) + + it(`should convert slpAddr to cashAddr`, async () => { + assert.deepEqual( + SLP_TESTNET_ADDRESSES.map(address => + slp.Address.toCashAddress(address) + ), + CASH_TESTNET_ADDRESSES + ) + }) + }) + + describe("#toSLPAddress", () => { + it("should convert testnet slp address format to itself correctly", () => { + assert.deepEqual( + SLP_TESTNET_ADDRESSES.map(address => + slp.Address.toSLPAddress(address) + ), + SLP_TESTNET_ADDRESSES + ) + }) + + it(`should convert legacyAddr to slpAddr`, async () => { + assert.deepEqual( + LEGACY_TESTNET_ADDRESSES.map(address => + slp.Address.toSLPAddress(address) + ), + SLP_TESTNET_ADDRESSES + ) + }) + + it(`should convert cashAddr to slpAddr`, async () => { + assert.deepEqual( + CASH_TESTNET_ADDRESSES.map(address => + slp.Address.toSLPAddress(address) + ), + SLP_TESTNET_ADDRESSES + ) + }) + }) + + describe("#isLegacyAddress", () => { + describe("is legacy addr", () => { + LEGACY_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a legacy address`, () => { + const isLegacyaddr = slp.Address.isLegacyAddress(address) + assert.equal(isLegacyaddr, true) + }) + }) + }) + + describe("cashaddr is not legacy addr", () => { + CASH_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a legacy address`, () => { + const isLegacyaddr = slp.Address.isLegacyAddress(address) + assert.equal(isLegacyaddr, false) + }) + }) + }) + + describe("slpaddr is not legacy addr", () => { + SLP_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a legacy address`, () => { + const isLegacyaddr = slp.Address.isLegacyAddress(address) + assert.equal(isLegacyaddr, false) + }) + }) + }) + }) + + describe("#isCashAddress", () => { + describe("is cashaddr", () => { + CASH_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a cashaddr address`, () => { + const isCashaddr = slp.Address.isCashAddress(address) + assert.equal(isCashaddr, true) + }) + }) + }) + + describe("legacy is not cash addr", () => { + LEGACY_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a cash address`, () => { + const isCashaddr = slp.Address.isCashAddress(address) + assert.equal(isCashaddr, false) + }) + }) + }) + + describe("slpaddr is not cash addr", () => { + SLP_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a cash address`, () => { + const isCashaddr = slp.Address.isCashAddress(address) + assert.equal(isCashaddr, false) + }) + }) + }) + }) + + describe("#isSLPAddress", () => { + describe("is slpaddr", () => { + SLP_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is an slp address`, () => { + const isSLPaddr = slp.Address.isSLPAddress(address) + assert.equal(isSLPaddr, true) + }) + }) + }) + + describe("legacy is not slp addr", () => { + LEGACY_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not an slp address`, () => { + const isSLPaddr = slp.Address.isSLPAddress(address) + assert.equal(isSLPaddr, false) + }) + }) + }) + + describe("cash is not slp addr", () => { + CASH_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not an slp address`, () => { + const isSLPaddr = slp.Address.isSLPAddress(address) + assert.equal(isSLPaddr, false) + }) + }) + }) + }) + + describe("#isTestnetAddress", () => { + describe("testnet legacy addr", () => { + LEGACY_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnetaddr = slp.Address.isTestnetAddress(address) + assert.equal(isTestnetaddr, true) + }) + }) + }) + + describe("testnet cash addr", () => { + CASH_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnetaddr = slp.Address.isTestnetAddress(address) + assert.equal(isTestnetaddr, true) + }) + }) + }) + + describe("testnet slp addr", () => { + SLP_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnetaddr = slp.Address.isTestnetAddress(address) + assert.equal(isTestnetaddr, true) + }) + }) + }) + + describe("mainnet legacy addr", () => { + LEGACY_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a testnet address`, () => { + const isTestnetaddr = slp.Address.isTestnetAddress(address) + assert.equal(isTestnetaddr, false) + }) + }) + }) + + describe("mainnet cash addr", () => { + CASH_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a testnet address`, () => { + const isTestnetaddr = slp.Address.isTestnetAddress(address) + assert.equal(isTestnetaddr, false) + }) + }) + }) + + describe("mainnet slp addr", () => { + SLP_MAINNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is not a testnet address`, () => { + const isTestnetaddr = slp.Address.isTestnetAddress(address) + assert.equal(isTestnetaddr, false) + }) + }) + }) + }) + + describe("#isP2PKHAddress", () => { + describe("testnet legacy addr", () => { + TESTNET_P2PKH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a P2PKH address`, () => { + const isP2PKHaddr = slp.Address.isP2PKHAddress(address) + assert.equal(isP2PKHaddr, true) + }) + }) + }) + }) + + describe("#isP2SHAddress", () => { + describe("testnet legacy addr", () => { + TESTNET_P2SH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a P2SH address`, () => { + const isP2SHaddr = slp.Address.isP2SHAddress(address) + assert.equal(isP2SHaddr, true) + }) + }) + }) + }) + + describe("#detectAddressFormat", () => { + LEGACY_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a legacy address`, () => { + const isLegacy = slp.Address.detectAddressFormat(address) + assert.equal(isLegacy, "legacy") + }) + }) + + CASH_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a cash address`, () => { + const isCashaddr = slp.Address.detectAddressFormat(address) + assert.equal(isCashaddr, "cashaddr") + }) + }) + + SLP_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is an slp address`, () => { + const isSlpaddr = slp.Address.detectAddressFormat(address) + assert.equal(isSlpaddr, "slpaddr") + }) + }) + }) + + describe("#detectAddressNetwork", () => { + LEGACY_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnet = slp.Address.detectAddressNetwork(address) + assert.equal(isTestnet, "testnet") + }) + }) + + CASH_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnet = slp.Address.detectAddressNetwork(address) + assert.equal(isTestnet, "testnet") + }) + }) + + SLP_TESTNET_ADDRESSES.forEach(address => { + it(`should detect ${address} is a testnet address`, () => { + const isTestnet = slp.Address.detectAddressNetwork(address) + assert.equal(isTestnet, "testnet") + }) + }) + }) + + describe("#detectAddressType", () => { + TESTNET_P2PKH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a p2pkh address`, () => { + const isp2pkh = slp.Address.detectAddressType(address) + assert.equal(isp2pkh, "p2pkh") + }) + }) + }) + + describe("#detectAddressType", () => { + TESTNET_P2SH_ADDRESSES.forEach(address => { + it(`should detect ${address} is a p2sh address`, () => { + const isp2sh = slp.Address.detectAddressType(address) + assert.equal(isp2sh, "p2sh") + }) + }) + }) + }) +}) +/* +describe("#details", () => { + let sandbox + beforeEach(() => (sandbox = sinon.sandbox.create())) + afterEach(() => sandbox.restore()) + + it("should get details", done => { + const data = { + balance: 0.00000546, + balanceSat: 546, + totalReceived: 0.00426132, + totalReceivedSat: 426132, + totalSent: 0.00425586, + totalSentSat: 425586, + unconfirmedBalance: 0, + unconfirmedBalanceSat: 0, + unconfirmedTxApperances: 0, + txApperances: 3, + transactions: [ + "902fe6ed7a19570c032b3ba4c4d7af92804662b486a15c8ca2d284166c658dd4", + "467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5", + "c1d9f3490e96a1fe4f77195067f1ab12c787f79b39d107424e0c4c810098e11b" + ], + legacyAddress: "1NM2ozrXVSnMRm66ua6aGeXgMsU7yqwqLS", + cashAddress: "bitcoincash:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3g4hl47n9y", + slpAddress: "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6", + currentPage: 0, + pagesTotal: 1 + } + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + slp.Address.details( + "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6" + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) +}) + +describe("#utxo", () => { + let sandbox + beforeEach(() => (sandbox = sinon.sandbox.create())) + afterEach(() => sandbox.restore()) + + it("should get utxo", done => { + const data = { + utxos: [ + { + txid: + "467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5", + vout: 1, + amount: 0.00000546, + satoshis: 546, + height: 568215, + confirmations: 24 + } + ], + legacyAddress: "1NM2ozrXVSnMRm66ua6aGeXgMsU7yqwqLS", + cashAddress: "bitcoincash:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3g4hl47n9y", + scriptPubKey: "76a914ea2478cbb9f44100b547cf264d34155dce044b8a88ac" + } + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + slp.Address.utxo("simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6") + .then(result => { + assert.deepEqual( + "467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5", + result.utxos[0].txid + ) + }) + .then(done, done) + }) +}) + +describe("#unconfirmed", () => { + let sandbox + beforeEach(() => (sandbox = sinon.sandbox.create())) + afterEach(() => sandbox.restore()) + + it("should get unconfirmed transactions", done => { + const data = { + utxos: [], + legacyAddress: "1NM2ozrXVSnMRm66ua6aGeXgMsU7yqwqLS", + cashAddress: "bitcoincash:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3g4hl47n9y", + slpAddress: "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6", + scriptPubKey: "76a914ea2478cbb9f44100b547cf264d34155dce044b8a88ac" + } + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + slp.Address.unconfirmed( + "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6" + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) +}) +*/ + +// (async () => { +// try { +// const details = await slp.Address.unconfirmed( +// "simpleledger:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3gev5wtnm6" +// ) +// console.log(details) +// } catch (error) { +// console.error(error) +// } +// })() diff --git a/test/unit/slp-ecpair.js b/test/unit/slp-ecpair.js new file mode 100644 index 0000000..5024520 --- /dev/null +++ b/test/unit/slp-ecpair.js @@ -0,0 +1,26 @@ +const assert = require("assert") + +const fixtures = require("./fixtures/slp/ecpair.json") + +const BCHJS = require("../../src/bch-js") +let slp + +// const SLP = require("../../src/slp/slp") +// const slp = new SLP({ restURL: "http://fakeurl.com/" }) + +describe("#SLP ECPair", () => { + beforeEach(() => { + const bchjs = new BCHJS() + slp = bchjs.SLP + }) + + describe("#toSLPAddress", () => { + it(`should return slp address for ecpair`, async () => { + fixtures.wif.forEach((wif, index) => { + const ecpair = slp.ECPair.fromWIF(wif) + const slpAddr = slp.ECPair.toSLPAddress(ecpair) + assert.equal(slpAddr, fixtures.address[index]) + }) + }) + }) +}) diff --git a/test/unit/slp-nft1.js b/test/unit/slp-nft1.js new file mode 100644 index 0000000..9e9640b --- /dev/null +++ b/test/unit/slp-nft1.js @@ -0,0 +1,264 @@ +/* + Unit tests for the TokenType1 library. +*/ + +const assert = require("chai").assert +const nock = require("nock") // http call mocking +const sinon = require("sinon") +// const axios = require("axios") + +// Default to unit tests unless some other value for TEST is passed. +if (!process.env.TEST) process.env.TEST = "unit" +// const SERVER = bchjs.restURL + +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() + +// Mock data used for unit tests +// const mockData = require("./fixtures/slp/mock-utils") + +// Default to unit tests unless some other value for TEST is passed. +if (!process.env.TEST) process.env.TEST = "unit" + +describe("#SLP NFT1", () => { + let sandbox + + beforeEach(() => { + // Activate nock if it's inactive. + if (!nock.isActive()) nock.activate() + + sandbox = sinon.createSandbox() + }) + + afterEach(() => { + // Clean up HTTP mocks. + nock.cleanAll() // clear interceptor list. + nock.restore() + + sandbox.restore() + }) + + describe("#newNFTGroupOpReturn", () => { + it("should generate new NFT Group OP_RETURN code", () => { + const configObj = { + name: "SLP Test Token", + ticker: "SLPTEST", + documentUrl: "https://bchjs.cash", + documentHash: "", + initialQty: 5 + } + + const result = bchjs.SLP.NFT1.newNFTGroupOpReturn(configObj) + // console.log(`result: `, result) + + assert.equal(Buffer.isBuffer(result), true) + }) + }) + + describe("#mintNFTGroupOpReturn", () => { + it("should generate NFT Group Mint OP_RETURN code", () => { + const tokenUtxoData = [ + { + txid: + "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76", + vout: 2, + value: "546", + height: 638207, + confirmations: 63, + satoshis: 546, + utxoType: "minting-baton", + transactionType: "mint", + tokenId: + "680967b3f6fe080dbca8dbd370665bd29742e3490db24e2f28d08b424511807e", + tokenType: 129, + tokenTicker: "NFTP", + tokenName: "NFT Parent", + tokenDocumentUrl: "FullStack.cash", + tokenDocumentHash: "", + decimals: 0, + mintBatonVout: 2, + isValid: true + }, + { + txid: + "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76", + vout: 1, + value: "546", + height: 638207, + confirmations: 63, + satoshis: 546, + utxoType: "token", + tokenQty: 10, + transactionType: "mint", + tokenId: + "680967b3f6fe080dbca8dbd370665bd29742e3490db24e2f28d08b424511807e", + tokenType: 129, + tokenTicker: "NFTP", + tokenName: "NFT Parent", + tokenDocumentUrl: "FullStack.cash", + tokenDocumentHash: "", + decimals: 0, + mintBatonVout: 2, + isValid: true + } + ] + + const result = bchjs.SLP.NFT1.mintNFTGroupOpReturn(tokenUtxoData, 10) + // console.log(`result: `, result) + + assert.equal(Buffer.isBuffer(result), true) + }) + }) + + describe("#generateNFTChildOpReturn", () => { + it("should generate NFT Genesis OP_RETURN code", () => { + const configObj = { + name: "SLP Test Token", + ticker: "SLPTEST", + documentUrl: "https://bchjs.cash", + documentHash: "" + } + + const result = bchjs.SLP.NFT1.generateNFTChildGenesisOpReturn(configObj) + // console.log(`result: `, result) + + assert.equal(Buffer.isBuffer(result), true) + }) + }) + + describe("#generateNFTChildSendOpReturn", () => { + it("should generate send OP_RETURN code for no change", () => { + // Mock UTXO. + const tokenUtxos = [ + { + txid: + "81955624a8eb7011769ff5faa607f78f84b0f8152b9145e7db8b1e521c895ee3", + vout: 1, + value: "546", + height: 638273, + confirmations: 42, + satoshis: 546, + utxoType: "token", + tokenQty: 1, + tokenId: + "81955624a8eb7011769ff5faa607f78f84b0f8152b9145e7db8b1e521c895ee3", + tokenTicker: "NFTC", + tokenName: "NFT Child", + tokenDocumentUrl: "https://FullStack.cash", + tokenDocumentHash: "", + decimals: 0, + tokenType: 129, + isValid: true + } + ] + + const result = bchjs.SLP.NFT1.generateNFTChildSendOpReturn(tokenUtxos, 1) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["script", "outputs"]) + assert.isNumber(result.outputs) + }) + + it("should generate send OP_RETURN code with change", () => { + // Mock UTXO. + const tokenUtxos = [ + { + txid: + "81955624a8eb7011769ff5faa607f78f84b0f8152b9145e7db8b1e521c895ee3", + vout: 1, + value: "546", + height: 638273, + confirmations: 42, + satoshis: 546, + utxoType: "token", + tokenQty: 4, + tokenId: + "81955624a8eb7011769ff5faa607f78f84b0f8152b9145e7db8b1e521c895ee3", + tokenTicker: "NFTC", + tokenName: "NFT Child", + tokenDocumentUrl: "https://FullStack.cash", + tokenDocumentHash: "", + decimals: 0, + tokenType: 129, + isValid: true + } + ] + + const result = bchjs.SLP.NFT1.generateNFTChildSendOpReturn(tokenUtxos, 1) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["script", "outputs"]) + assert.isNumber(result.outputs) + }) + }) + + describe("#generateNFTGroupSendOpReturn", () => { + it("should generate send OP_RETURN with change", () => { + // Mock UTXO. + const tokenUtxos = [ + { + txid: + "35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919", + vout: 1, + value: "546", + height: 638207, + confirmations: 3, + satoshis: 546, + utxoType: "token", + tokenQty: 10, + transactionType: "mint", + tokenId: + "eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981", + tokenType: 129, + tokenTicker: "NFTTT", + tokenName: "NFT Test Token", + tokenDocumentUrl: "https://FullStack.cash", + tokenDocumentHash: "", + decimals: 0, + mintBatonVout: 2, + isValid: true + } + ] + + const result = bchjs.SLP.NFT1.generateNFTGroupSendOpReturn(tokenUtxos, 1) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["script", "outputs"]) + assert.isNumber(result.outputs) + }) + + it("should generate send OP_RETURN with no change", () => { + // Mock UTXO. + const tokenUtxos = [ + { + txid: + "35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919", + vout: 1, + value: "546", + height: 638207, + confirmations: 3, + satoshis: 546, + utxoType: "token", + tokenQty: 10, + transactionType: "mint", + tokenId: + "eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981", + tokenType: 129, + tokenTicker: "NFTTT", + tokenName: "NFT Test Token", + tokenDocumentUrl: "https://FullStack.cash", + tokenDocumentHash: "", + decimals: 0, + mintBatonVout: 2, + isValid: true + } + ] + + const result = bchjs.SLP.NFT1.generateNFTGroupSendOpReturn(tokenUtxos, 10) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["script", "outputs"]) + assert.isNumber(result.outputs) + }) + }) +}) diff --git a/test/unit/slp-tokentype1.js b/test/unit/slp-tokentype1.js new file mode 100644 index 0000000..23fa94f --- /dev/null +++ b/test/unit/slp-tokentype1.js @@ -0,0 +1,319 @@ +/* + Unit tests for the TokenType1 library. +*/ + +const assert = require("chai").assert +const nock = require("nock") // http call mocking +const sinon = require("sinon") +// const axios = require("axios") + +// Default to unit tests unless some other value for TEST is passed. +if (!process.env.TEST) process.env.TEST = "unit" +// const SERVER = bchjs.restURL + +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() + +// Mock data used for unit tests +// const mockData = require("./fixtures/slp/mock-utils") + +// Default to unit tests unless some other value for TEST is passed. +if (!process.env.TEST) process.env.TEST = "unit" + +describe("#SLP TokenType1", () => { + let sandbox + + beforeEach(() => { + // Activate nock if it's inactive. + if (!nock.isActive()) nock.activate() + + sandbox = sinon.createSandbox() + }) + + afterEach(() => { + // Clean up HTTP mocks. + nock.cleanAll() // clear interceptor list. + nock.restore() + + sandbox.restore() + }) + + describe("#generateSendOpReturn", () => { + it("should generate send OP_RETURN code", () => { + // Mock UTXO. + const tokenUtxos = [ + { + txid: + "a8eb788b8ddda6faea00e6e2756624b8feb97655363d0400dd66839ea619d36e", + vout: 2, + value: "546", + confirmations: 0, + satoshis: 546, + utxoType: "token", + transactionType: "send", + tokenId: + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", + tokenTicker: "TOK-CH", + tokenName: "TokyoCash", + tokenDocumentUrl: "", + tokenDocumentHash: "", + decimals: 8, + tokenQty: 7 + } + ] + + const result = bchjs.SLP.TokenType1.generateSendOpReturn(tokenUtxos, 1) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["script", "outputs"]) + assert.isNumber(result.outputs) + }) + }) + + describe("#generateBurnOpReturn", () => { + it("should generate burn OP_RETURN code", () => { + // Mock UTXO. + const tokenUtxos = [ + { + txid: + "a8eb788b8ddda6faea00e6e2756624b8feb97655363d0400dd66839ea619d36e", + vout: 2, + value: "546", + confirmations: 0, + satoshis: 546, + utxoType: "token", + transactionType: "send", + tokenId: + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", + tokenTicker: "TOK-CH", + tokenName: "TokyoCash", + tokenDocumentUrl: "", + tokenDocumentHash: "", + decimals: 8, + tokenQty: 7 + } + ] + + const result = bchjs.SLP.TokenType1.generateBurnOpReturn(tokenUtxos, 1) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // console.log(`result: `, result) + + assert.equal(Buffer.isBuffer(result), true) + }) + }) + + describe("#generateGenesisOpReturn", () => { + it("should generate genesis OP_RETURN code", () => { + const configObj = { + name: "SLP Test Token", + ticker: "SLPTEST", + documentUrl: "https://bchjs.cash", + documentHash: "", + decimals: 8, + initialQty: 10 + } + + const result = bchjs.SLP.TokenType1.generateGenesisOpReturn(configObj) + // console.log(`result: `, result) + + assert.equal(Buffer.isBuffer(result), true) + }) + + it("should work if user does not specify doc hash", () => { + const configObj = { + name: "SLP Test Token", + ticker: "SLPTEST", + documentUrl: "https://bchjs.cash", + decimals: 8, + initialQty: 10 + } + + const result = bchjs.SLP.TokenType1.generateGenesisOpReturn(configObj) + // console.log(`result: `, result) + + assert.equal(Buffer.isBuffer(result), true) + }) + }) + + describe("#generateMintOpReturn", () => { + it("should throw error if tokenUtxos is not an array.", () => { + try { + bchjs.SLP.TokenType1.generateMintOpReturn({}, 100) + + assert.equal(true, false, "Unexpected result.") + } catch (err) { + assert.include( + err.message, + `tokenUtxos must be an array`, + "Expected error message." + ) + } + }) + + it("should throw error if minting baton is not in UTXOs.", () => { + try { + const utxos = [ + { + txid: + "ccc6d336399e26d98afcd3821b41fb1535cd50f57063ed7593eaed5108659606", + vout: 1, + value: "546", + height: 637618, + confirmations: 239, + satoshis: 546, + utxoType: "token", + tokenQty: 100, + tokenId: + "ccc6d336399e26d98afcd3821b41fb1535cd50f57063ed7593eaed5108659606", + tokenTicker: "SLPTEST", + tokenName: "SLP Test Token", + tokenDocumentUrl: "https://FullStack.cash", + tokenDocumentHash: "", + decimals: 8, + isValid: true + } + ] + + bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) + + assert.equal(true, false, "Unexpected result.") + } catch (err) { + assert.include( + err.message, + `Minting baton could not be found in tokenUtxos array`, + "Expected error message." + ) + } + }) + + it("should throw error if tokenId is not included in minting-baton UTXO.", () => { + try { + const utxos = [ + { + txid: + "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", + vout: 2, + value: "546", + height: 637625, + confirmations: 207, + satoshis: 546, + utxoType: "minting-baton", + tokenTicker: "SLPTEST", + tokenName: "SLP Test Token", + tokenDocumentUrl: "https://FullStack.cash", + tokenDocumentHash: "", + decimals: 8, + isValid: true + } + ] + + bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) + + assert.equal(true, false, "Unexpected result.") + } catch (err) { + assert.include( + err.message, + `tokenId property not found in mint-baton UTXO`, + "Expected error message." + ) + } + }) + + it("should throw error if decimals is not included in minting-baton UTXO.", () => { + try { + const utxos = [ + { + txid: + "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", + vout: 2, + value: "546", + height: 637625, + confirmations: 207, + satoshis: 546, + utxoType: "minting-baton", + tokenId: + "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", + tokenTicker: "SLPTEST", + tokenName: "SLP Test Token", + tokenDocumentUrl: "https://FullStack.cash", + tokenDocumentHash: "", + isValid: true + } + ] + + bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) + + assert.equal(true, false, "Unexpected result.") + } catch (err) { + assert.include( + err.message, + `decimals property not found in mint-baton UTXO`, + "Expected error message." + ) + } + }) + + it("should generate genesis OP_RETURN code", () => { + tokenUtxo = [ + { + txid: + "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", + vout: 2, + value: "546", + height: 637625, + confirmations: 207, + satoshis: 546, + utxoType: "minting-baton", + tokenId: + "9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136", + tokenTicker: "SLPTEST", + tokenName: "SLP Test Token", + tokenDocumentUrl: "https://FullStack.cash", + tokenDocumentHash: "", + decimals: 8, + isValid: true + } + ] + + const result = bchjs.SLP.TokenType1.generateMintOpReturn(tokenUtxo, 100) + // console.log(`result: `, result) + + assert.equal(Buffer.isBuffer(result), true) + }) + }) + + describe("#getHexOpReturn", () => { + it("should return OP_RETURN object ", async () => { + const tokenUtxos = [ + { + tokenId: + "0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1", + decimals: 0, + tokenQty: 2 + } + ] + + const sendQty = 1.5 + + sandbox.stub(bchjs.SLP.TokenType1.axios, "post").resolves({ + data: { + script: + "6a04534c500001010453454e44200a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1080000000000000001080000000000000000", + outputs: 2 + } + }) + + const result = await bchjs.SLP.TokenType1.getHexOpReturn( + tokenUtxos, + sendQty + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "script") + assert.isString(result.script) + + assert.property(result, "outputs") + assert.isNumber(result.outputs) + }) + }) +}) diff --git a/test/unit/transaction-builder.js b/test/unit/transaction-builder.js new file mode 100644 index 0000000..aa5f29f --- /dev/null +++ b/test/unit/transaction-builder.js @@ -0,0 +1,2039 @@ +const fixtures = require("./fixtures/transaction-builder.json") +const assert = require("assert") +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() +const Buffer = require("safe-buffer").Buffer + +describe("#TransactionBuilder", () => { + describe("#hashTypes", () => { + const transactionBuilder = new bchjs.TransactionBuilder("mainnet") + fixtures.hashTypes.forEach(fixture => { + it(`should match hash type`, () => { + assert.equal( + fixture[Object.keys(fixture)[0]], + transactionBuilder.hashTypes[Object.keys(fixture)[0]] + ) + }) + }) + }) + + describe("#P2PK", () => { + describe("#toOne", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2pk.toOne.mainnet.forEach(fixture => { + it(`should create 1-to-1 P2PK transaction on mainnet`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder() + const originalAmount = fixture.amount + const txid = fixture.txHash + const pubKey = bchjs.HDNode.toPublicKey(node) + const buf = bchjs.Script.pubKey.output.encode(pubKey) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf, sendAmount) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.scripts.p2pk.toOne.testnet.forEach(fixture => { + it(`should create 1-to-1 P2PK transaction on testnet`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const originalAmount = fixture.amount + const txid = fixture.txHash + const pubKey = bchjs.HDNode.toPublicKey(node) + const buf = bchjs.Script.pubKey.output.encode(pubKey) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf, sendAmount) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + }) + + describe("#toMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2pk.toMany.mainnet.forEach(fixture => { + it(`should create 1-to-many P2PK transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const transactionBuilder = new bchjs.TransactionBuilder() + const originalAmount = fixture.amount + const txid = fixture.txHash + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) + const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) + const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf2, Math.floor(sendAmount / 2)) + transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) + const keyPair = bchjs.HDNode.toKeyPair(node1) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.scripts.p2pk.toMany.testnet.forEach(fixture => { + it(`should create 1-to-many P2PK transaction on testnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const originalAmount = fixture.amount + const txid = fixture.txHash + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) + const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) + const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf2, Math.floor(sendAmount / 2)) + transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) + const keyPair = bchjs.HDNode.toKeyPair(node1) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + }) + + describe("#manyToMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2pk.manyToMany.mainnet.forEach(fixture => { + it(`should create many-to-many P2PK transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) + const transactionBuilder = new bchjs.TransactionBuilder() + const originalAmount = fixture.amount + const txid = fixture.txHash + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const pubKey4 = bchjs.HDNode.toPublicKey(node4) + const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) + const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) + const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) + const buf4 = bchjs.Script.pubKey.output.encode(pubKey4) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + transactionBuilder.addInput( + txid, + 1, + transactionBuilder.DEFAULT_SEQUENCE, + buf2 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) + transactionBuilder.addOutput(buf4, Math.floor(sendAmount / 2)) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.scripts.p2pk.manyToMany.testnet.forEach(fixture => { + it(`should create many-to-many P2PK transaction on testnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const originalAmount = fixture.amount + const txid = fixture.txHash + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const pubKey4 = bchjs.HDNode.toPublicKey(node4) + const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) + const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) + const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) + const buf4 = bchjs.Script.pubKey.output.encode(pubKey4) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + transactionBuilder.addInput( + txid, + 1, + transactionBuilder.DEFAULT_SEQUENCE, + buf2 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) + transactionBuilder.addOutput(buf4, Math.floor(sendAmount / 2)) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + }) + + describe("#fromMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2pk.fromMany.mainnet.forEach(fixture => { + it(`should create many-to-1 P2PK transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const transactionBuilder = new bchjs.TransactionBuilder() + const originalAmount = fixture.amount + const txid = fixture.txHash + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) + const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) + const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + transactionBuilder.addInput( + txid, + 1, + transactionBuilder.DEFAULT_SEQUENCE, + buf2 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf3, sendAmount) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.scripts.p2pk.fromMany.testnet.forEach(fixture => { + it(`should create many-to-1 P2PK transaction on testnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const originalAmount = fixture.amount + const txid = fixture.txHash + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const buf1 = bchjs.Script.pubKey.output.encode(pubKey1) + const buf2 = bchjs.Script.pubKey.output.encode(pubKey2) + const buf3 = bchjs.Script.pubKey.output.encode(pubKey3) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + transactionBuilder.addInput( + txid, + 1, + transactionBuilder.DEFAULT_SEQUENCE, + buf2 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf3, sendAmount) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + }) + }) + + describe("#P2PKH", () => { + describe("#toOne", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2pkh.toOne.mainnet.forEach(fixture => { + it(`should create 1-to-1 P2PKH transaction on mainnet`, () => { + const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder() + const keyPair = bchjs.HDNode.toKeyPair(hdnode) + const txHash = fixture.txHash + // original amount of satoshis in vin + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, fixture.vout) + // get byte count to calculate fee. paying 1 sat/byte + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 1 } + ) + // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + const sendAmount = originalAmount - byteCount + // add output w/ address and amount to send + let redeemScript + transactionBuilder.addOutput(fixture.outputs[0], sendAmount) + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + + // build tx + const tx = transactionBuilder.build() + // output rawhex + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.scripts.p2pkh.toOne.testnet.forEach(fixture => { + it(`should create 1-to-1 P2PKH transaction on testnet`, () => { + const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv, "testnet") + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const keyPair = bchjs.HDNode.toKeyPair(hdnode) + const txHash = fixture.txHash + // original amount of satoshis in vin + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, fixture.vout) + // get byte count to calculate fee. paying 1 sat/byte + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 1 } + ) + // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + const sendAmount = originalAmount - byteCount * 15 + // add output w/ address and amount to send + let redeemScript + transactionBuilder.addOutput(fixture.outputs[0], sendAmount) + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + + // build tx + const tx = transactionBuilder.build() + // output rawhex + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + /* + describe("#RegTest", () => { + fixtures.scripts.p2pkh.toOne.regtest.forEach(fixture => { + it(`should create 1-to-1 P2PKH transaction on regtest`, () => { + const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder("regtest") + const keyPair = bchjs.HDNode.toKeyPair(hdnode) + const txHash = fixture.txHash + // original amount of satoshis in vin + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, fixture.vout) + // get byte count to calculate fee. paying 1 sat/byte + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 1 } + ) + // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + const sendAmount = originalAmount - byteCount * 15 + // add output w/ address and amount to send + let redeemScript + transactionBuilder.addOutput(fixture.outputs[0], sendAmount) + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + + // build tx + const tx = transactionBuilder.build() + // output rawhex + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + */ + }) + + describe("#toMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2pkh.toMany.mainnet.forEach(fixture => { + it(`should create 1-to-2 P2PKH transaction on mainnet`, () => { + const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder() + const keyPair = bchjs.HDNode.toKeyPair(hdnode) + const txHash = fixture.txHash + // original amount of satoshis in vin + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, fixture.vout) + // get byte count to calculate fee. paying 1 sat/byte + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 2 } + ) + // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + const sendAmount = originalAmount - byteCount + // add output w/ address and amount to send + transactionBuilder.addOutput( + fixture.outputs[0], + Math.floor(sendAmount / 2) + ) + transactionBuilder.addOutput( + fixture.outputs[1], + Math.floor(sendAmount / 2) + ) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + // build tx + const tx = transactionBuilder.build() + // output rawhex + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.scripts.p2pkh.toMany.testnet.forEach(fixture => { + // TODO pass in tesnet network config + it(`should create 1-to-2 P2PKH transaction on testnet`, () => { + const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const keyPair = bchjs.HDNode.toKeyPair(hdnode) + const txHash = fixture.txHash + // original amount of satoshis in vin + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, fixture.vout) + // get byte count to calculate fee. paying 1 sat/byte + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 2 } + ) + // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + const sendAmount = originalAmount - byteCount * 15 + // add output w/ address and amount to send + transactionBuilder.addOutput( + fixture.outputs[0], + Math.floor(sendAmount / 2) + ) + transactionBuilder.addOutput( + fixture.outputs[1], + Math.floor(sendAmount / 2) + ) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + // build tx + const tx = transactionBuilder.build() + // output rawhex + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + /* + describe("#RegTest", () => { + fixtures.scripts.p2pkh.toMany.regtest.forEach(fixture => { + // TODO pass in tesnet network config + it(`should create 1-to-2 P2PKH transaction on regtest`, () => { + const hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder("regtest") + const keyPair = bchjs.HDNode.toKeyPair(hdnode) + const txHash = fixture.txHash + // original amount of satoshis in vin + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, fixture.vout) + // get byte count to calculate fee. paying 1 sat/byte + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 2 } + ) + // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + const sendAmount = originalAmount - byteCount * 15 + // add output w/ address and amount to send + transactionBuilder.addOutput( + fixture.outputs[0], + Math.floor(sendAmount / 2) + ) + transactionBuilder.addOutput( + fixture.outputs[1], + Math.floor(sendAmount / 2) + ) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + // build tx + const tx = transactionBuilder.build() + // output rawhex + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + })*/ + }) + + describe("#manyToMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2pkh.manyToMany.mainnet.forEach(fixture => { + it(`should create 2-to-2 P2PKH transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txHash = fixture.txHash + const originalAmount = fixture.amounts[0] + fixture.amounts[1] + transactionBuilder.addInput(txHash, 0) + transactionBuilder.addInput(txHash, 1) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 2 }, + { P2PKH: 2 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput( + fixture.outputs[0], + Math.floor(sendAmount / 2) + ) + transactionBuilder.addOutput( + fixture.outputs[1], + Math.floor(sendAmount / 2) + ) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[0] + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[1] + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.scripts.p2pkh.manyToMany.testnet.forEach(fixture => { + it(`should create 2-to-2 P2PKH transaction on testnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const txHash = fixture.txHash + const originalAmount = fixture.amounts[0] + fixture.amounts[1] + transactionBuilder.addInput(txHash, 0) + transactionBuilder.addInput(txHash, 1) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 2 }, + { P2PKH: 2 } + ) + const sendAmount = originalAmount - byteCount * 15 + transactionBuilder.addOutput( + fixture.outputs[0], + Math.floor(sendAmount / 2) + ) + transactionBuilder.addOutput( + fixture.outputs[1], + Math.floor(sendAmount / 2) + ) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[0] + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[1] + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + /* + describe("#RegTest", () => { + fixtures.scripts.p2pkh.manyToMany.regtest.forEach(fixture => { + it(`should create 2-to-2 P2PKH transaction on regtest`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const transactionBuilder = new bchjs.TransactionBuilder("regtest") + const txHash = fixture.txHash + const originalAmount = fixture.amounts[0] + fixture.amounts[1] + transactionBuilder.addInput(txHash, 0) + transactionBuilder.addInput(txHash, 1) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 2 }, + { P2PKH: 2 } + ) + const sendAmount = originalAmount - byteCount * 15 + transactionBuilder.addOutput( + fixture.outputs[0], + Math.floor(sendAmount / 2) + ) + transactionBuilder.addOutput( + fixture.outputs[1], + Math.floor(sendAmount / 2) + ) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[0] + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[1] + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + */ + }) + + describe("#fromMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2pkh.fromMany.mainnet.forEach(fixture => { + it(`should create 2-to-1 P2PKH transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txHash = fixture.txHash + const originalAmount = fixture.amounts[0] + fixture.amounts[1] + transactionBuilder.addInput(txHash, 0) + transactionBuilder.addInput(txHash, 1) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 2 }, + { P2PKH: 1 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(fixture.outputs[0], sendAmount) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[0] + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[1] + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.scripts.p2pkh.fromMany.testnet.forEach(fixture => { + it(`should create 2-to-1 P2PKH transaction on testnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const txHash = fixture.txHash + const originalAmount = fixture.amounts[0] + fixture.amounts[1] + transactionBuilder.addInput(txHash, 0) + transactionBuilder.addInput(txHash, 1) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 2 }, + { P2PKH: 1 } + ) + const sendAmount = originalAmount - byteCount * 15 + transactionBuilder.addOutput(fixture.outputs[0], sendAmount) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[0] + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[1] + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + /* + describe("#RegTest", () => { + fixtures.scripts.p2pkh.fromMany.regtest.forEach(fixture => { + it(`should create 2-to-1 P2PKH transaction on regtest`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const transactionBuilder = new bchjs.TransactionBuilder("regtest") + const txHash = fixture.txHash + const originalAmount = fixture.amounts[0] + fixture.amounts[1] + transactionBuilder.addInput(txHash, 0) + transactionBuilder.addInput(txHash, 1) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 2 }, + { P2PKH: 1 } + ) + const sendAmount = originalAmount - byteCount * 15 + transactionBuilder.addOutput(fixture.outputs[0], sendAmount) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + let redeemScript + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[0] + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amounts[1] + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + */ + }) + }) + + describe("#op_return", () => { + describe("#Mainnet", () => { + fixtures.nulldata.mainnet.forEach(fixture => { + it(`should create transaction w/ OP_RETURN output on mainnet`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder() + const txHash = fixture.txHash + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, 0) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 5 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(fixture.output, sendAmount) + const data = fixture.data + const buf = bchjs.Script.nullData.output.encode( + Buffer.from(data, "ascii") + ) + transactionBuilder.addOutput(buf, 0) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.nulldata.testnet.forEach(fixture => { + it(`should create transaction w/ OP_RETURN output on testnet`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const txHash = fixture.txHash + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, 0) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 5 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(fixture.output, sendAmount) + const data = fixture.data + const buf = bchjs.Script.nullData.output.encode( + Buffer.from(data, "ascii") + ) + transactionBuilder.addOutput(buf, 0) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + /* + describe("#RegTest", () => { + fixtures.nulldata.regtest.forEach(fixture => { + it(`should create transaction w/ OP_RETURN output on regtest`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder("regtest") + const txHash = fixture.txHash + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, 0) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 5 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(fixture.output, sendAmount) + const data = fixture.data + const buf = bchjs.Script.nullData.output.encode( + Buffer.from(data, "ascii") + ) + transactionBuilder.addOutput(buf, 0) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + */ + }) + + describe("#P2MS", () => { + describe("#toOne", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2ms.toOne.mainnet.forEach(fixture => { + it(`should create 1-to-1 1-of-2 P2MS transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txid = fixture.txHash + const originalAmount = fixture.amount + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const buf1 = bchjs.Script.multisig.output.encode(1, [ + pubKey1, + pubKey2 + ]) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const pubKey4 = bchjs.HDNode.toPublicKey(node4) + const buf2 = bchjs.Script.multisig.output.encode(1, [ + pubKey3, + pubKey4 + ]) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(buf2, sendAmount) + let redeemScript + const keyPair = bchjs.HDNode.toKeyPair(node1) + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + // describe('#Testnet', () => { + // fixtures.scripts.p2ms.toOne.testnet.forEach((fixture) => { + // it(`should create 1-to-1 P2MS transaction on testnet`, () => { + // let hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv, 'testnet'); + // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); + // let keyPair = bchjs.HDNode.toKeyPair(hdnode); + // let txHash = fixture.txHash; + // // original amount of satoshis in vin + // let originalAmount = fixture.amount; + // transactionBuilder.addInput(txHash, fixture.vout); + // // get byte count to calculate fee. paying 1 sat/byte + // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 1 }); + // // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + // let sendAmount = originalAmount - (byteCount * 15); + // // add output w/ address and amount to send + // let redeemScript + // transactionBuilder.addOutput(fixture.outputs[0], sendAmount); + // transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount); + // + // // build tx + // let tx = transactionBuilder.build(); + // // output rawhex + // let hex = tx.toHex(); + // assert.equal(hex, fixture.hex); + // }); + // }); + // }); + }) + + describe("#toMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2ms.toMany.mainnet.forEach(fixture => { + it(`should create 1-to-2 P2MS transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) + const node5 = bchjs.HDNode.fromXPriv(fixture.xprivs[4]) + const node6 = bchjs.HDNode.fromXPriv(fixture.xprivs[5]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txid = fixture.txHash + const originalAmount = fixture.amount + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const buf1 = bchjs.Script.multisig.output.encode(1, [ + pubKey1, + pubKey2 + ]) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const pubKey4 = bchjs.HDNode.toPublicKey(node4) + const buf2 = bchjs.Script.multisig.output.encode(1, [ + pubKey3, + pubKey4 + ]) + transactionBuilder.addOutput(buf2, Math.floor(sendAmount / 2)) + const pubKey5 = bchjs.HDNode.toPublicKey(node5) + const pubKey6 = bchjs.HDNode.toPublicKey(node6) + const buf3 = bchjs.Script.multisig.output.encode(1, [ + pubKey5, + pubKey6 + ]) + transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) + let redeemScript + const keyPair = bchjs.HDNode.toKeyPair(node1) + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + // describe('#Testnet', () => { + // fixtures.scripts.p2ms.toMany.testnet.forEach((fixture) => { + // // TODO pass in tesnet network config + // it(`should create 1-to-2 P2MS transaction on testnet`, () => { + // let hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv); + // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); + // let keyPair = bchjs.HDNode.toKeyPair(hdnode); + // let txHash = fixture.txHash; + // // original amount of satoshis in vin + // let originalAmount = fixture.amount; + // transactionBuilder.addInput(txHash, fixture.vout); + // // get byte count to calculate fee. paying 1 sat/byte + // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 2 }); + // // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + // let sendAmount = originalAmount - (byteCount * 15); + // // add output w/ address and amount to send + // transactionBuilder.addOutput(fixture.outputs[0], Math.floor(sendAmount / 2)); + // transactionBuilder.addOutput(fixture.outputs[1], Math.floor(sendAmount / 2)); + // let redeemScript + // transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount); + // // build tx + // let tx = transactionBuilder.build(); + // // output rawhex + // let hex = tx.toHex(); + // assert.equal(hex, fixture.hex); + // }); + // }); + // }); + }) + + describe("#manyToMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2ms.manyToMany.mainnet.forEach(fixture => { + it(`should create 2-to-2 P2MS transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) + const node5 = bchjs.HDNode.fromXPriv(fixture.xprivs[4]) + const node6 = bchjs.HDNode.fromXPriv(fixture.xprivs[5]) + const node7 = bchjs.HDNode.fromXPriv(fixture.xprivs[6]) + const node8 = bchjs.HDNode.fromXPriv(fixture.xprivs[7]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txid = fixture.txHash + const originalAmount = fixture.amount + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const buf1 = bchjs.Script.multisig.output.encode(1, [ + pubKey1, + pubKey2 + ]) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const pubKey4 = bchjs.HDNode.toPublicKey(node4) + const buf2 = bchjs.Script.multisig.output.encode(1, [ + pubKey3, + pubKey4 + ]) + transactionBuilder.addInput( + txid, + 1, + transactionBuilder.DEFAULT_SEQUENCE, + buf2 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + const pubKey5 = bchjs.HDNode.toPublicKey(node5) + const pubKey6 = bchjs.HDNode.toPublicKey(node6) + const buf3 = bchjs.Script.multisig.output.encode(1, [ + pubKey5, + pubKey6 + ]) + transactionBuilder.addOutput(buf3, Math.floor(sendAmount / 2)) + const pubKey7 = bchjs.HDNode.toPublicKey(node7) + const pubKey8 = bchjs.HDNode.toPublicKey(node8) + const buf4 = bchjs.Script.multisig.output.encode(1, [ + pubKey7, + pubKey8 + ]) + transactionBuilder.addOutput(buf4, Math.floor(sendAmount / 2)) + let redeemScript + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node3) + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + // describe('#Testnet', () => { + // fixtures.scripts.p2ms.manyToMany.testnet.forEach((fixture) => { + // it(`should create 2-to-2 P2MS transaction on testnet`, () => { + // let node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]); + // let node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]); + // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); + // let txHash = fixture.txHash; + // let originalAmount = fixture.amounts[0] + fixture.amounts[1]; + // transactionBuilder.addInput(txHash, 0); + // transactionBuilder.addInput(txHash, 1); + // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 2 }); + // let sendAmount = originalAmount - (byteCount * 15); + // transactionBuilder.addOutput(fixture.outputs[0], Math.floor(sendAmount / 2)); + // transactionBuilder.addOutput(fixture.outputs[1], Math.floor(sendAmount / 2)); + // let keyPair1 = bchjs.HDNode.toKeyPair(node1); + // let keyPair2 = bchjs.HDNode.toKeyPair(node2); + // let redeemScript; + // transactionBuilder.sign(0, keyPair1, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[0]); + // transactionBuilder.sign(1, keyPair2, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[1]); + // let tx = transactionBuilder.build(); + // let hex = tx.toHex(); + // assert.equal(hex, fixture.hex); + // }); + // }); + // }); + }) + + describe("#fromMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2ms.fromMany.mainnet.forEach(fixture => { + it(`should create 2-to-1 P2MS transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) + const node5 = bchjs.HDNode.fromXPriv(fixture.xprivs[4]) + const node6 = bchjs.HDNode.fromXPriv(fixture.xprivs[5]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txid = fixture.txHash + const originalAmount = fixture.amount + const pubKey1 = bchjs.HDNode.toPublicKey(node1) + const pubKey2 = bchjs.HDNode.toPublicKey(node2) + const buf1 = bchjs.Script.multisig.output.encode(1, [ + pubKey1, + pubKey2 + ]) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + buf1 + ) + const pubKey3 = bchjs.HDNode.toPublicKey(node3) + const pubKey4 = bchjs.HDNode.toPublicKey(node4) + const buf2 = bchjs.Script.multisig.output.encode(1, [ + pubKey3, + pubKey4 + ]) + transactionBuilder.addInput( + txid, + 1, + transactionBuilder.DEFAULT_SEQUENCE, + buf2 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 3 } + ) + const sendAmount = originalAmount - byteCount + const pubKey5 = bchjs.HDNode.toPublicKey(node5) + const pubKey6 = bchjs.HDNode.toPublicKey(node6) + const buf3 = bchjs.Script.multisig.output.encode(1, [ + pubKey5, + pubKey6 + ]) + transactionBuilder.addOutput(buf3, sendAmount) + let redeemScript + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node3) + transactionBuilder.sign( + 0, + keyPair1, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + transactionBuilder.sign( + 1, + keyPair2, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + // describe('#Testnet', () => { + // fixtures.scripts.p2ms.fromMany.testnet.forEach((fixture) => { + // it(`should create 2-to-1 P2MS transaction on testnet`, () => { + // let node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]); + // let node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]); + // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); + // let txHash = fixture.txHash; + // let originalAmount = fixture.amounts[0] + fixture.amounts[1]; + // transactionBuilder.addInput(txHash, 0); + // transactionBuilder.addInput(txHash, 1); + // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 1 }); + // let sendAmount = originalAmount - (byteCount * 15); + // transactionBuilder.addOutput(fixture.outputs[0], sendAmount); + // let keyPair1 = bchjs.HDNode.toKeyPair(node1); + // let keyPair2 = bchjs.HDNode.toKeyPair(node2); + // let redeemScript; + // transactionBuilder.sign(0, keyPair1, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[0]); + // transactionBuilder.sign(1, keyPair2, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[1]); + // let tx = transactionBuilder.build(); + // let hex = tx.toHex(); + // assert.equal(hex, fixture.hex); + // }); + // }); + // }); + }) + }) + + describe("#P2SH", () => { + describe("#toOne", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2sh.toOne.mainnet.forEach(fixture => { + it(`should create 1-to-1 P2SH transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txid = fixture.txHash + const originalAmount = fixture.amount + const identifier1 = bchjs.HDNode.toIdentifier(node1) + const buf1 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier1, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash1 = bchjs.Crypto.hash160(buf1) + const data1 = bchjs.Script.scriptHash.output.encode(scriptHash1) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + data1 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 2 }, + { P2PKH: 1 } + ) + const sendAmount = originalAmount - byteCount + const identifier2 = bchjs.HDNode.toIdentifier(node2) + const buf2 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier2, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + + const scriptHash2 = bchjs.Crypto.hash160(buf2) + const data2 = bchjs.Script.scriptHash.output.encode(scriptHash2) + transactionBuilder.addOutput(data2, sendAmount) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + transactionBuilder.sign( + 0, + keyPair1, + buf1, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + // describe('#Testnet', () => { + // fixtures.scripts.p2sh.toOne.testnet.forEach((fixture) => { + // it(`should create 1-to-1 P2SH transaction on testnet`, () => { + // let hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv, 'testnet'); + // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); + // let keyPair = bchjs.HDNode.toKeyPair(hdnode); + // let txHash = fixture.txHash; + // // original amount of satoshis in vin + // let originalAmount = fixture.amount; + // transactionBuilder.addInput(txHash, fixture.vout); + // // get byte count to calculate fee. paying 1 sat/byte + // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 1 }); + // // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + // let sendAmount = originalAmount - (byteCount * 15); + // // add output w/ address and amount to send + // let redeemScript + // transactionBuilder.addOutput(fixture.outputs[0], sendAmount); + // transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount); + // + // // build tx + // let tx = transactionBuilder.build(); + // // output rawhex + // let hex = tx.toHex(); + // assert.equal(hex, fixture.hex); + // }); + // }); + // }); + }) + + describe("#toMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2sh.toMany.mainnet.forEach(fixture => { + it(`should create 1-to-2 P2SH transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txid = fixture.txHash + const originalAmount = fixture.amount + const identifier1 = bchjs.HDNode.toIdentifier(node1) + const buf1 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier1, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash1 = bchjs.Crypto.hash160(buf1) + const data1 = bchjs.Script.scriptHash.output.encode(scriptHash1) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + data1 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 2 } + ) + const sendAmount = originalAmount - byteCount + const identifier2 = bchjs.HDNode.toIdentifier(node2) + const buf2 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier2, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash2 = bchjs.Crypto.hash160(buf2) + const data2 = bchjs.Script.scriptHash.output.encode(scriptHash2) + transactionBuilder.addOutput(data2, Math.floor(sendAmount / 2)) + const identifier3 = bchjs.HDNode.toIdentifier(node3) + const buf3 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier3, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash3 = bchjs.Crypto.hash160(buf3) + const data3 = bchjs.Script.scriptHash.output.encode(scriptHash3) + transactionBuilder.addOutput(data3, Math.floor(sendAmount / 2)) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + transactionBuilder.sign( + 0, + keyPair1, + buf1, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + // describe('#Testnet', () => { + // fixtures.scripts.p2sh.toMany.testnet.forEach((fixture) => { + // // TODO pass in tesnet network config + // it(`should create 1-to-2 P2SH transaction on testnet`, () => { + // let hdnode = bchjs.HDNode.fromXPriv(fixture.xpriv); + // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); + // let keyPair = bchjs.HDNode.toKeyPair(hdnode); + // let txHash = fixture.txHash; + // // original amount of satoshis in vin + // let originalAmount = fixture.amount; + // transactionBuilder.addInput(txHash, fixture.vout); + // // get byte count to calculate fee. paying 1 sat/byte + // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 2 }); + // // amount to send to receiver. It's the original amount - 1 sat/byte for tx size + // let sendAmount = originalAmount - (byteCount * 15); + // // add output w/ address and amount to send + // transactionBuilder.addOutput(fixture.outputs[0], Math.floor(sendAmount / 2)); + // transactionBuilder.addOutput(fixture.outputs[1], Math.floor(sendAmount / 2)); + // let redeemScript + // transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount); + // // build tx + // let tx = transactionBuilder.build(); + // // output rawhex + // let hex = tx.toHex(); + // assert.equal(hex, fixture.hex); + // }); + // }); + // }); + }) + + describe("#manyToMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2sh.manyToMany.mainnet.forEach(fixture => { + it(`should create 2-to-2 P2SH transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const node4 = bchjs.HDNode.fromXPriv(fixture.xprivs[3]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txid = fixture.txHash + const originalAmount = fixture.amount + const identifier1 = bchjs.HDNode.toIdentifier(node1) + const buf1 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier1, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash1 = bchjs.Crypto.hash160(buf1) + const data1 = bchjs.Script.scriptHash.output.encode(scriptHash1) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + data1 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 5 }, + { P2PKH: 5 } + ) + const sendAmount = originalAmount - byteCount + const identifier2 = bchjs.HDNode.toIdentifier(node2) + const buf2 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier2, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash2 = bchjs.Crypto.hash160(buf2) + const data2 = bchjs.Script.scriptHash.output.encode(scriptHash2) + transactionBuilder.addInput( + txid, + 1, + transactionBuilder.DEFAULT_SEQUENCE, + data2 + ) + const identifier3 = bchjs.HDNode.toIdentifier(node3) + const buf3 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier3, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash3 = bchjs.Crypto.hash160(buf3) + const data3 = bchjs.Script.scriptHash.output.encode(scriptHash3) + transactionBuilder.addOutput(data3, Math.floor(sendAmount / 2)) + const identifier4 = bchjs.HDNode.toIdentifier(node4) + const buf4 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier4, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash4 = bchjs.Crypto.hash160(buf4) + const data4 = bchjs.Script.scriptHash.output.encode(scriptHash4) + transactionBuilder.addOutput(data4, Math.floor(sendAmount / 2)) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + transactionBuilder.sign( + 0, + keyPair1, + buf1, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + transactionBuilder.sign( + 1, + keyPair2, + buf2, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + // describe('#Testnet', () => { + // fixtures.scripts.p2sh.manyToMany.testnet.forEach((fixture) => { + // it(`should create 2-to-2 P2SH transaction on testnet`, () => { + // let node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]); + // let node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]); + // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); + // let txHash = fixture.txHash; + // let originalAmount = fixture.amounts[0] + fixture.amounts[1]; + // transactionBuilder.addInput(txHash, 0); + // transactionBuilder.addInput(txHash, 1); + // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 2 }); + // let sendAmount = originalAmount - (byteCount * 15); + // transactionBuilder.addOutput(fixture.outputs[0], Math.floor(sendAmount / 2)); + // transactionBuilder.addOutput(fixture.outputs[1], Math.floor(sendAmount / 2)); + // let keyPair1 = bchjs.HDNode.toKeyPair(node1); + // let keyPair2 = bchjs.HDNode.toKeyPair(node2); + // let redeemScript; + // transactionBuilder.sign(0, keyPair1, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[0]); + // transactionBuilder.sign(1, keyPair2, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[1]); + // let tx = transactionBuilder.build(); + // let hex = tx.toHex(); + // assert.equal(hex, fixture.hex); + // }); + // }); + // }); + }) + + describe("#fromMany", () => { + describe("#Mainnet", () => { + fixtures.scripts.p2sh.fromMany.mainnet.forEach(fixture => { + it(`should create 2-to-1 P2SH transaction on mainnet`, () => { + const node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]) + const node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]) + const node3 = bchjs.HDNode.fromXPriv(fixture.xprivs[2]) + const transactionBuilder = new bchjs.TransactionBuilder() + const txid = fixture.txHash + const originalAmount = fixture.amount + const identifier1 = bchjs.HDNode.toIdentifier(node1) + const buf1 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier1, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash1 = bchjs.Crypto.hash160(buf1) + const data1 = bchjs.Script.scriptHash.output.encode(scriptHash1) + transactionBuilder.addInput( + txid, + 0, + transactionBuilder.DEFAULT_SEQUENCE, + data1 + ) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 3 }, + { P2PKH: 2 } + ) + const sendAmount = originalAmount - byteCount + const identifier2 = bchjs.HDNode.toIdentifier(node2) + const buf2 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier2, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash2 = bchjs.Crypto.hash160(buf2) + const data2 = bchjs.Script.scriptHash.output.encode(scriptHash2) + transactionBuilder.addInput( + txid, + 1, + transactionBuilder.DEFAULT_SEQUENCE, + data2 + ) + const identifier3 = bchjs.HDNode.toIdentifier(node3) + const buf3 = bchjs.Script.encode([ + bchjs.Script.opcodes.OP_DUP, + bchjs.Script.opcodes.OP_HASH160, + identifier3, + bchjs.Script.opcodes.OP_EQUALVERIFY, + bchjs.Script.opcodes.OP_CHECKSIG + ]) + const scriptHash3 = bchjs.Crypto.hash160(buf3) + const data3 = bchjs.Script.scriptHash.output.encode(scriptHash3) + transactionBuilder.addOutput(data3, sendAmount) + const keyPair1 = bchjs.HDNode.toKeyPair(node1) + const keyPair2 = bchjs.HDNode.toKeyPair(node2) + transactionBuilder.sign( + 0, + keyPair1, + buf1, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + transactionBuilder.sign( + 1, + keyPair2, + buf2, + transactionBuilder.hashTypes.SIGHASH_ALL, + originalAmount / 2 + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + // describe('#Testnet', () => { + // fixtures.scripts.p2sh.fromMany.testnet.forEach((fixture) => { + // it(`should create 2-to-1 P2SH transaction on testnet`, () => { + // let node1 = bchjs.HDNode.fromXPriv(fixture.xprivs[0]); + // let node2 = bchjs.HDNode.fromXPriv(fixture.xprivs[1]); + // let transactionBuilder = new bchjs.TransactionBuilder('testnet'); + // let txHash = fixture.txHash; + // let originalAmount = fixture.amounts[0] + fixture.amounts[1]; + // transactionBuilder.addInput(txHash, 0); + // transactionBuilder.addInput(txHash, 1); + // let byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 1 }); + // let sendAmount = originalAmount - (byteCount * 15); + // transactionBuilder.addOutput(fixture.outputs[0], sendAmount); + // let keyPair1 = bchjs.HDNode.toKeyPair(node1); + // let keyPair2 = bchjs.HDNode.toKeyPair(node2); + // let redeemScript; + // transactionBuilder.sign(0, keyPair1, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[0]); + // transactionBuilder.sign(1, keyPair2, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, fixture.amounts[1]); + // let tx = transactionBuilder.build(); + // let hex = tx.toHex(); + // assert.equal(hex, fixture.hex); + // }); + // }); + // }); + }) + }) + + describe("#op_return", () => { + describe("#Mainnet", () => { + fixtures.nulldata.mainnet.forEach(fixture => { + it(`should create transaction w/ OP_RETURN output on mainnet`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder() + const txHash = fixture.txHash + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, 0) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 5 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(fixture.output, sendAmount) + const data = fixture.data + const buf = bchjs.Script.nullData.output.encode( + Buffer.from(data, "ascii") + ) + transactionBuilder.addOutput(buf, 0) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + + describe("#Testnet", () => { + fixtures.nulldata.testnet.forEach(fixture => { + it(`should create transaction w/ OP_RETURN output on testnet`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder("testnet") + const txHash = fixture.txHash + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, 0) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 5 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(fixture.output, sendAmount) + const data = fixture.data + const buf = bchjs.Script.nullData.output.encode( + Buffer.from(data, "ascii") + ) + transactionBuilder.addOutput(buf, 0) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + /* + describe("#RegTest", () => { + fixtures.nulldata.regtest.forEach(fixture => { + it(`should create transaction w/ OP_RETURN output on regtest`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder("regtest") + const txHash = fixture.txHash + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, 0) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 5 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(fixture.output, sendAmount) + const data = fixture.data + const buf = bchjs.Script.nullData.output.encode( + Buffer.from(data, "ascii") + ) + transactionBuilder.addOutput(buf, 0) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) +*/ + }) + + describe("#bip66", () => { + fixtures.bip66.forEach(fixture => { + it(`should bip66 encode as ${fixture.DER}`, () => { + const transactionBuilder = new bchjs.TransactionBuilder() + const r = Buffer.from(fixture.r, "hex") + const s = Buffer.from(fixture.s, "hex") + const DER = transactionBuilder.bip66.encode(r, s) + assert.equal(DER.toString("hex"), fixture.DER) + }) + }) + + fixtures.bip66.forEach(fixture => { + it(`should bip66 decode ${fixture.DER}`, () => { + const transactionBuilder = new bchjs.TransactionBuilder() + const buffer = Buffer.from(fixture.DER, "hex") + const signature = transactionBuilder.bip66.decode(buffer) + assert.equal(signature.r.toString("hex"), fixture.r) + assert.equal(signature.s.toString("hex"), fixture.s) + }) + }) + + fixtures.bip66.forEach(fixture => { + it(`should bip66 check ${fixture.DER}`, () => { + const transactionBuilder = new bchjs.TransactionBuilder() + const buffer = Buffer.from(fixture.DER, "hex") + assert.equal(transactionBuilder.bip66.check(buffer), true) + }) + }) + }) + + describe("#bip68", () => { + fixtures.bip68.encode.forEach(fixture => { + it(`should bip68 encode as ${fixture.result}`, () => { + const transactionBuilder = new bchjs.TransactionBuilder() + const obj = {} + obj[fixture.type] = fixture.value + const encode = transactionBuilder.bip68.encode(obj) + assert.equal(encode, fixture.result) + }) + }) + + fixtures.bip68.decode.forEach(fixture => { + it(`should bip68 decode ${fixture.result}`, () => { + const transactionBuilder = new bchjs.TransactionBuilder() + const obj = {} + const decode = transactionBuilder.bip68.decode(fixture.result) + assert.equal(Object.keys(decode)[0], fixture.type) + assert.deepEqual(decode[Object.keys(decode)[0]], fixture.value) + }) + }) + }) + + describe("#LockTime", () => { + describe("#Mainnet", () => { + fixtures.locktime.mainnet.forEach(fixture => { + it(`should create transaction with nLockTime on mainnet`, () => { + const node = bchjs.HDNode.fromXPriv(fixture.xpriv) + const transactionBuilder = new bchjs.TransactionBuilder() + + const txHash = fixture.txHash + const originalAmount = fixture.amount + transactionBuilder.addInput(txHash, 0, 1) + const byteCount = bchjs.BitcoinCash.getByteCount( + { P2PKH: 1 }, + { P2PKH: 1 } + ) + const sendAmount = originalAmount - byteCount + transactionBuilder.addOutput(fixture.output, sendAmount) + const lockTime = fixture.lockTime + transactionBuilder.setLockTime(lockTime) + const keyPair = bchjs.HDNode.toKeyPair(node) + let redeemScript + transactionBuilder.sign( + 0, + keyPair, + redeemScript, + transactionBuilder.hashTypes.SIGHASH_ALL, + fixture.amount + ) + const tx = transactionBuilder.build() + const hex = tx.toHex() + assert.equal(hex, fixture.hex) + }) + }) + }) + }) +}) diff --git a/test/unit/util.js b/test/unit/util.js new file mode 100644 index 0000000..7a17955 --- /dev/null +++ b/test/unit/util.js @@ -0,0 +1,35 @@ +const assert = require("assert") +const axios = require("axios") +const BCHJS = require("../../src/bch-js") +const bchjs = new BCHJS() +const sinon = require("sinon") + +describe("#Util", () => { + describe("#validateAddress", () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + it("should validate address", done => { + const data = { + isvalid: true, + address: "bitcoincash:qpz7qtkuyhrsz4qmnnrvf8gz9zd0u9v7eqsewyk4w5", + scriptPubKey: "76a91445e02edc25c701541b9cc6c49d02289afe159ec888ac", + ismine: false, + iswatchonly: false, + isscript: false + } + + const resolved = new Promise(r => r({ data: data })) + sandbox.stub(axios, "get").returns(resolved) + + bchjs.Util.validateAddress( + "bitcoincash:qpz7qtkuyhrsz4qmnnrvf8gz9zd0u9v7eqsewyk4w5" + ) + .then(result => { + assert.deepEqual(data, result) + }) + .then(done, done) + }) + }) +}) From e97a8e7cf500fbec863fe53c376cda8b772be0ea Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 15:41:39 -0800 Subject: [PATCH 6/9] fix(slp utils): refactoring unit tests --- package-lock.json | 118 +++++++++++++++++++++++------------------ package.json | 1 + src/slp/utils.js | 1 + test/unit/address.js | 3 +- test/unit/slp-utils.js | 108 ++++++++++++++----------------------- 5 files changed, 111 insertions(+), 120 deletions(-) diff --git a/package-lock.json b/package-lock.json index 48e2189..75bd26c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@psf/bch-js", - "version": "3.4.0", + "version": "4.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4445,6 +4445,12 @@ "integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=", "dev": true }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", @@ -5253,9 +5259,9 @@ } }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "node-mocks-http": { @@ -5301,9 +5307,9 @@ "dev": true }, "npm": { - "version": "6.14.7", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.7.tgz", - "integrity": "sha512-swhsdpNpyXg4GbM6LpOQ6qaloQuIKizZ+Zh6JPXJQc59ka49100Js0WvZx594iaKSoFgkFq2s8uXFHS3/Xy2WQ==", + "version": "6.14.9", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.9.tgz", + "integrity": "sha512-yHi1+i9LyAZF1gAmgyYtVk+HdABlLy94PMIDoK1TRKWvmFQAt5z3bodqVwKvzY0s6dLqQPVsRLiwhJfNtiHeCg==", "dev": true, "requires": { "JSONStream": "^1.3.5", @@ -5370,7 +5376,7 @@ "lodash.uniq": "~4.5.0", "lodash.without": "~4.4.0", "lru-cache": "^5.1.1", - "meant": "~1.0.1", + "meant": "^1.0.2", "mississippi": "^3.0.0", "mkdirp": "^0.5.5", "move-concurrently": "^1.0.1", @@ -5385,8 +5391,8 @@ "npm-packlist": "^1.4.8", "npm-pick-manifest": "^3.0.2", "npm-profile": "^4.0.4", - "npm-registry-fetch": "^4.0.5", - "npm-user-validate": "~1.0.0", + "npm-registry-fetch": "^4.0.7", + "npm-user-validate": "^1.0.1", "npmlog": "~4.1.2", "once": "~1.4.0", "opener": "^1.5.1", @@ -5461,17 +5467,6 @@ "humanize-ms": "^1.2.1" } }, - "ajv": { - "version": "5.5.2", - "bundled": true, - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, "ansi-align": { "version": "2.0.0", "bundled": true, @@ -5802,11 +5797,6 @@ "mkdirp": "~0.5.0" } }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true - }, "code-point-at": { "version": "1.1.0", "bundled": true, @@ -5898,11 +5888,11 @@ } }, "configstore": { - "version": "3.1.2", + "version": "3.1.5", "bundled": true, "dev": true, "requires": { - "dot-prop": "^4.1.0", + "dot-prop": "^4.2.1", "graceful-fs": "^4.1.2", "make-dir": "^1.0.0", "unique-string": "^1.0.0", @@ -6078,7 +6068,7 @@ } }, "dot-prop": { - "version": "4.2.0", + "version": "4.2.1", "bundled": true, "dev": true, "requires": { @@ -6255,11 +6245,6 @@ "bundled": true, "dev": true }, - "fast-deep-equal": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, "fast-json-stable-stringify": { "version": "2.0.0", "bundled": true, @@ -6580,12 +6565,35 @@ "dev": true }, "har-validator": { - "version": "5.1.0", + "version": "5.1.5", "bundled": true, "dev": true, "requires": { - "ajv": "^5.3.0", + "ajv": "^6.12.3", "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true, + "dev": true + } } }, "has": { @@ -6872,11 +6880,6 @@ "bundled": true, "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "bundled": true, - "dev": true - }, "json-stringify-safe": { "version": "5.0.1", "bundled": true, @@ -7224,7 +7227,7 @@ } }, "meant": { - "version": "1.0.1", + "version": "1.0.2", "bundled": true, "dev": true }, @@ -7249,6 +7252,11 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.5", + "bundled": true, + "dev": true + }, "minizlib": { "version": "1.3.3", "bundled": true, @@ -7485,7 +7493,7 @@ } }, "npm-registry-fetch": { - "version": "4.0.5", + "version": "4.0.7", "bundled": true, "dev": true, "requires": { @@ -7514,7 +7522,7 @@ } }, "npm-user-validate": { - "version": "1.0.0", + "version": "1.0.1", "bundled": true, "dev": true }, @@ -7859,13 +7867,6 @@ "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "bundled": true, - "dev": true - } } }, "read": { @@ -8525,6 +8526,21 @@ "xdg-basedir": "^3.0.0" } }, + "uri-js": { + "version": "4.4.0", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "bundled": true, + "dev": true + } + } + }, "url-parse-lax": { "version": "1.0.0", "bundled": true, diff --git a/package.json b/package.json index e0d0142..de0f794 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "eslint-config-prettier": "^6.0.0", "eslint-plugin-node": "^9.1.0", "eslint-plugin-prettier": "^3.1.0", + "lodash.clonedeep": "^4.5.0", "mocha": "^6.1.4", "nock": "^10.0.6", "node-mocks-http": "^1.7.0", diff --git a/src/slp/utils.js b/src/slp/utils.js index 3f50ddc..7688c56 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -196,6 +196,7 @@ class Utils { async list(id) { let path let method + if (!id) { method = "get" path = `${this.restURL}slp/list` diff --git a/test/unit/address.js b/test/unit/address.js index 57beb97..1f61c29 100644 --- a/test/unit/address.js +++ b/test/unit/address.js @@ -338,7 +338,7 @@ describe("#address.js", () => { }) }) }) - /* + describe("address format detection", () => { describe("#isLegacyAddress", () => { describe("is legacy", () => { @@ -932,5 +932,4 @@ describe("#address.js", () => { }) }) }) - */ }) diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 3f5dd4c..2baf628 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -1,9 +1,7 @@ // Public npm libraries -const assert = require("assert") -const assert2 = require("chai").assert -// const nock = require("nock") // http call mocking +const assert = require("chai").assert const sinon = require("sinon") -// const axios = require("axios") +const cloneDeep = require("lodash.clonedeep") // Unit under test const SLP = require("../../src/slp/slp") @@ -15,7 +13,8 @@ const REST_URL = process.env.RESTURL : "https://bchn.fullstack.cash/v4/" // Mock data used for unit tests -const mockData = require("./fixtures/slp/mock-utils") +const mockDataLib = require("./fixtures/slp/mock-utils") +let mockData // Default to unit tests unless some other value for TEST is passed. if (!process.env.TEST) process.env.TEST = "unit" @@ -27,6 +26,8 @@ describe("#SLP Utils", () => { // Activate nock if it's inactive. // if (!nock.isActive()) nock.activate() + mockData = cloneDeep(mockDataLib) + sandbox = sinon.createSandbox() uut = new SLP({ @@ -45,69 +46,42 @@ describe("#SLP Utils", () => { }) describe("#list", () => { - it("should do something", () => { - console.log("hello world") - }) - // - // it(`should list single SLP token by id`, async () => { - // // Mock the call to the REST API - // // if (process.env.TEST === "unit") { - // // nock(SERVER) - // // .get(uri => uri.includes("/")) - // // .reply(200, mockData.mockToken) - // // } - // - // const tokenId = - // "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84" - // - // console.log(`uut.restURL: ${uut.restURL}`) - // - // const list = await uut.Utils.list(tokenId) - // console.log(`list: ${JSON.stringify(list, null, 2)}`) - // - // // assert.equal(list.id, tokenId) - // }) + it(`should list single SLP token by id`, async () => { + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.mockToken }) - // it(`should list multiple SLP tokens by array of ids`, async () => { - // // Mock the call to the REST API - // if (process.env.TEST === "unit") { - // nock(SERVER) - // .post(uri => uri.includes("/")) - // .reply(200, mockData.mockTokens) - // } - // - // const tokenIds = [ - // "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84", - // "b3f4f132dc3b9c8c96316346993a8d54d729715147b7b11aa6c8cd909e955313" - // ] - // - // const list = await uut.Utils.list(tokenIds) - // // console.log(`list: ${JSON.stringify(list, null, 2)}`) - // - // assert2.hasAllKeys(list[0], [ - // "blockCreated", - // "blockLastActiveMint", - // "blockLastActiveSend", - // "circulatingSupply", - // "containsBaton", - // "decimals", - // "documentHash", - // "documentUri", - // "id", - // "initialTokenQty", - // "mintingBatonStatus", - // "name", - // "symbol", - // "timestamp", - // "timestampUnix", - // "totalBurned", - // "totalMinted", - // "txnsSinceGenesis", - // "validAddresses", - // "versionType" - // ]) - // assert.equal(list[0].id, tokenIds[0]) - // }) + const tokenId = + "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84" + + const list = await uut.Utils.list(tokenId) + // console.log(`list: ${JSON.stringify(list, null, 2)}`) + + assert.equal(list.id, tokenId) + assert.property(list, "decimals") + assert.property(list, "symbol") + assert.property(list, "documentUri") + assert.property(list, "name") + }) + + it(`should list multiple SLP tokens by array of ids`, async () => { + // Mock the call to the REST API + sandbox + .stub(uut.Utils.axios, "post") + .resolves({ data: mockData.mockList }) + + const tokenIds = [ + "4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84", + "8fc284dcbc922f7bb7e2a443dc3af792f52923bba403fcf67ca028c88e89da0e" + ] + + const list = await uut.Utils.list(tokenIds) + // console.log(`list: ${JSON.stringify(list, null, 2)}`) + + assert.isArray(list) + assert.property(list[0], "symbol") + assert.property(list[1], "symbol") + }) }) /* describe("#balancesForAddress", () => { From 1375601a81a67bdf1fd12e12c9f42df0e95edefa Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 17:41:43 -0800 Subject: [PATCH 7/9] Restored all unit tests --- src/slp/utils.js | 19 +- test/unit/slp-utils.js | 611 ++++++++++++++++++++--------------------- 2 files changed, 304 insertions(+), 326 deletions(-) diff --git a/src/slp/utils.js b/src/slp/utils.js index 7688c56..dffd5b7 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -373,9 +373,9 @@ class Utils { */ // Retrieve token balances for a given tokenId. async balancesForToken(tokenId) { - const path = `${this.restURL}slp/balancesForToken/${tokenId}` - try { + const path = `${this.restURL}slp/balancesForToken/${tokenId}` + const response = await _this.axios.get(path, _this.axiosOptions) return response.data } catch (error) { @@ -703,10 +703,11 @@ class Utils { * } */ async tokenStats(tokenId) { - const path = `${this.restURL}slp/tokenStats/${tokenId}` - try { + const path = `${this.restURL}slp/tokenStats/${tokenId}` + const response = await _this.axios.get(path, _this.axiosOptions) + return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data @@ -773,10 +774,11 @@ class Utils { */ // Retrieve token transactions for a given tokenId and address. async transactions(tokenId, address) { - const path = `${this.restURL}slp/transactions/${tokenId}/${address}` - try { + const path = `${this.restURL}slp/transactions/${tokenId}/${address}` + const response = await _this.axios.get(path, _this.axiosOptions) + return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data @@ -812,10 +814,11 @@ class Utils { * } */ async burnTotal(transactionId) { - const path = `${this.restURL}slp/burnTotal/${transactionId}` - try { + const path = `${this.restURL}slp/burnTotal/${transactionId}` + const response = await _this.axios.get(path, _this.axiosOptions) + return response.data } catch (error) { if (error.response && error.response.data) throw error.response.data diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 2baf628..f55707a 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -83,7 +83,7 @@ describe("#SLP Utils", () => { assert.property(list[1], "symbol") }) }) - /* + describe("#balancesForAddress", () => { it(`should throw an error if input is not a string or array of strings`, async () => { try { @@ -91,10 +91,10 @@ describe("#SLP Utils", () => { await uut.Utils.balancesForAddress(address) - assert2.equal(true, false, "Uh oh. Code path should not end here.") + assert.equal(true, false, "Uh oh. Code path should not end here.") } catch (err) { //console.log(`Error: `, err) - assert2.include( + assert.include( err.message, `Input address must be a string or array of strings` ) @@ -117,8 +117,8 @@ describe("#SLP Utils", () => { ) // console.log(`balances: ${JSON.stringify(balances, null, 2)}`) - assert2.isArray(balances) - assert2.hasAllKeys(balances[0], [ + assert.isArray(balances) + assert.hasAllKeys(balances[0], [ "tokenId", "balanceString", "balance", @@ -143,9 +143,9 @@ describe("#SLP Utils", () => { const balances = await uut.Utils.balancesForAddress(addresses) //console.log(`balances: ${JSON.stringify(balances, null, 2)}`) - assert2.isArray(balances) - assert2.isArray(balances[0]) - assert2.hasAllKeys(balances[0][0], [ + assert.isArray(balances) + assert.isArray(balances[0]) + assert.hasAllKeys(balances[0][0], [ "tokenId", "balanceString", "balance", @@ -158,11 +158,9 @@ describe("#SLP Utils", () => { describe("#validateTxid", () => { it(`should validate slp txid`, async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") { - nock(SERVER) - .post(uri => uri.includes("/")) - .reply(200, mockData.mockIsValidTxid) - } + sandbox + .stub(uut.Utils.axios, "post") + .resolves({ data: mockData.mockIsValidTxid }) const isValid = await uut.Utils.validateTxid( "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb" @@ -180,32 +178,30 @@ describe("#SLP Utils", () => { describe("#balancesForToken", () => { it(`should retrieve token balances for a given tokenId`, async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") { - nock(SERVER) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockBalancesForToken) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.mockBalancesForToken }) const balances = await uut.Utils.balancesForToken( "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb" ) - assert2.hasAnyKeys(balances[0], ["tokenBalance", "slpAddress"]) + + assert.hasAnyKeys(balances[0], ["tokenBalance", "slpAddress"]) }) }) describe("#tokenStats", () => { it(`should retrieve stats for a given tokenId`, async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") { - nock(SERVER) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockTokenStats) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.mockTokenStats }) const tokenStats = await uut.Utils.tokenStats( "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb" ) - assert2.hasAnyKeys(tokenStats, [ + + assert.hasAnyKeys(tokenStats, [ "circulatingSupply", "decimals", "documentUri", @@ -225,35 +221,32 @@ describe("#SLP Utils", () => { describe("#transactions", () => { it(`should retrieve transactions for a given tokenId and address`, async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") { - nock(SERVER) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockTransactions) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.mockTransactions }) const transactions = await uut.Utils.transactions( "495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a", "simpleledger:qrhvcy5xlegs858fjqf8ssl6a4f7wpstaqnt0wauwu" ) - assert2.hasAnyKeys(transactions[0], ["txid", "tokenDetails"]) + + assert.hasAnyKeys(transactions[0], ["txid", "tokenDetails"]) }) }) describe("#burnTotal", () => { it(`should retrieve input, output and burn totals`, async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") { - nock(SERVER) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockBurnTotal) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.mockBurnTotal }) const burnTotal = await uut.Utils.burnTotal( "c7078a6c7400518a513a0bde1f4158cf740d08d3b5bfb19aa7b6657e2f4160de" ) //console.log(`burnTotal: ${JSON.stringify(burnTotal, null, 2)}`) - assert2.hasAnyKeys(burnTotal, [ + assert.hasAnyKeys(burnTotal, [ "transactionId", "inputTotal", "outputTotal", @@ -269,62 +262,56 @@ describe("#SLP Utils", () => { await uut.Utils.decodeOpReturn(txid) - assert2.equal(true, false, "Unexpected result.") + assert.equal(true, false, "Unexpected result.") } catch (err) { //console.log(`err: ${util.inspect(err)}`) - assert2.include(err.message, `txid string must be included`) + assert.include(err.message, `txid string must be included`) } }) it("should throw an error for non-SLP transaction", async () => { try { // Mock the call to the REST API - if (process.env.TEST === "unit") { - sandbox - .stub(uut.Utils.axios, "get") - .resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn }) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn }) const txid = "3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec" await uut.Utils.decodeOpReturn(txid) - assert2.equal(true, false, "Unexpected result.") + assert.equal(true, false, "Unexpected result.") } catch (err) { // console.log(`err: ${util.inspect(err)}`) - assert2.include(err.message, `scriptpubkey not op_return`) + assert.include(err.message, `scriptpubkey not op_return`) } }) it("should throw an error for non-SLP transaction with OP_RETURN", async () => { try { // Mock the call to the REST API - if (process.env.TEST === "unit") { - sandbox - .stub(uut.Utils.axios, "get") - .resolves({ data: mockData.nonSLPTxDetailsWithOpReturn }) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.nonSLPTxDetailsWithOpReturn }) const txid = "2ff74c48a5d657cf45f699601990bffbbe7a2a516d5480674cbf6c6a4497908f" await uut.Utils.decodeOpReturn(txid) - assert2.equal(true, false, "Unexpected result.") + assert.equal(true, false, "Unexpected result.") } catch (err) { // console.log(`err: ${util.inspect(err)}`) - assert2.include(err.message, `SLP not in first chunk`) + assert.include(err.message, `SLP not in first chunk`) } }) it("should decode a genesis transaction", async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") { - sandbox - .stub(uut.Utils.axios, "get") - .resolves({ data: mockData.txDetailsSLPGenesis }) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.txDetailsSLPGenesis }) const txid = "bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90" @@ -332,7 +319,7 @@ describe("#SLP Utils", () => { const result = await uut.Utils.decodeOpReturn(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.hasAllKeys(result, [ + assert.hasAllKeys(result, [ "tokenType", "txType", "tokenId", @@ -348,11 +335,9 @@ describe("#SLP Utils", () => { it("should decode a mint transaction", async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") { - sandbox - .stub(uut.Utils.axios, "get") - .resolves({ data: mockData.txDetailsSLPMint }) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.txDetailsSLPMint }) const txid = "65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4" @@ -360,7 +345,7 @@ describe("#SLP Utils", () => { const result = await uut.Utils.decodeOpReturn(txid) //console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.hasAllKeys(result, [ + assert.hasAllKeys(result, [ "tokenType", "txType", "tokenId", @@ -371,11 +356,9 @@ describe("#SLP Utils", () => { it("should decode a send transaction", async () => { // Mock the call to the REST API - if (process.env.TEST === "unit") { - sandbox - .stub(uut.Utils.axios, "get") - .resolves({ data: mockData.txDetailsSLPSend }) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.txDetailsSLPSend }) const txid = "4f922565af664b6fdf0a1ba3924487344be721b3d8815c62cafc8a51e04a8afa" @@ -383,16 +366,14 @@ describe("#SLP Utils", () => { const result = await uut.Utils.decodeOpReturn(txid) //console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.hasAllKeys(result, ["tokenType", "txType", "tokenId", "amounts"]) + assert.hasAllKeys(result, ["tokenType", "txType", "tokenId", "amounts"]) }) it("should properly decode a Genesis transaction with no minting baton", async () => { // Mock the call to the REST API. - if (process.env.TEST === "unit") { - sandbox - .stub(uut.Utils.axios, "get") - .resolves({ data: mockData.txDetailsSLPGenesisNoBaton }) - } + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.txDetailsSLPGenesisNoBaton }) const txid = "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" @@ -400,16 +381,14 @@ describe("#SLP Utils", () => { const data = await uut.Utils.decodeOpReturn(txid) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.equal(data.mintBatonVout, 0) + assert.equal(data.mintBatonVout, 0) }) it("should decode a send transaction with alternate encoding", async () => { - // Mock the call to rest.bitcoin.com - if (process.env.TEST === "unit") { - sandbox - .stub(uut.Utils.axios, "get") - .resolves({ data: mockData.txDetailsSLPSendAlt }) - } + // Mock the call to the REST API + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.txDetailsSLPSendAlt }) const txid = "d94357179775425ebc59c93173bd6dc9854095f090a2eb9dcfe9797398bc8eae" @@ -417,7 +396,7 @@ describe("#SLP Utils", () => { const data = await uut.Utils.decodeOpReturn(txid) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.hasAnyKeys(data, [ + assert.hasAnyKeys(data, [ "transactionType", "txType", "tokenId", @@ -430,12 +409,10 @@ describe("#SLP Utils", () => { // decodeOpReturn2() method using the slp-parser library. it("should throw error for invalid SLP transaction", async () => { try { - // Mock the call to rest.bitcoin.com - if (process.env.TEST === "unit") { - sandbox - .stub(uut.Utils.axios, "get") - .resolves({ data: mockData.mockInvalidSlpSend }) - } + // Mock the call to the REST API + sandbox + .stub(uut.Utils.axios, "get") + .resolves({ data: mockData.mockInvalidSlpSend }) const txid = "a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415" @@ -444,7 +421,7 @@ describe("#SLP Utils", () => { // console.log(`result: ${JSON.stringify(result, null, 2)}`) } catch (err) { // console.log(`err: `, err) - assert2.include(err.message, "amount string size not 8 bytes") + assert.include(err.message, "amount string size not 8 bytes") } }) @@ -460,20 +437,20 @@ describe("#SLP Utils", () => { const data = await uut.Utils.decodeOpReturn(txid) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.property(data, "tokenType") - assert2.property(data, "txType") - assert2.property(data, "ticker") - assert2.property(data, "name") - assert2.property(data, "tokenId") - assert2.property(data, "documentUri") - assert2.property(data, "documentHash") - assert2.property(data, "decimals") - assert2.property(data, "mintBatonVout") - assert2.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") - assert2.equal(data.tokenType, 129) - assert2.equal(data.mintBatonVout, 2) - assert2.equal(data.qty, 1) + assert.equal(data.tokenType, 129) + assert.equal(data.mintBatonVout, 2) + assert.equal(data.qty, 1) }) // it("should decode a NFT Child transaction", async () => { @@ -488,20 +465,20 @@ describe("#SLP Utils", () => { // const data = await uut.Utils.decodeOpReturn(txid) // console.log(`data: ${JSON.stringify(data, null, 2)}`) // - // // assert2.property(data, "tokenType") - // // assert2.property(data, "txType") - // // assert2.property(data, "ticker") - // // assert2.property(data, "name") - // // assert2.property(data, "tokenId") - // // assert2.property(data, "documentUri") - // // assert2.property(data, "documentHash") - // // assert2.property(data, "decimals") - // // assert2.property(data, "mintBatonVout") - // // assert2.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") // // - // // assert2.equal(data.tokenType, 129) - // // assert2.equal(data.mintBatonVout, 2) - // // assert2.equal(data.qty, 1) + // // assert.equal(data.tokenType, 129) + // // assert.equal(data.mintBatonVout, 2) + // // assert.equal(data.qty, 1) // }) }) @@ -510,9 +487,9 @@ describe("#SLP Utils", () => { try { await uut.Utils.tokenUtxoDetails("test") - assert2.equal(true, false, "Unexpected result.") + assert.equal(true, false, "Unexpected result.") } catch (err) { - assert2.include( + assert.include( err.message, `Input must be an array`, "Expected error message." @@ -544,9 +521,9 @@ describe("#SLP Utils", () => { await uut.Utils.tokenUtxoDetails(utxos) - assert2.equal(true, false, "Unexpected result.") + assert.equal(true, false, "Unexpected result.") } catch (err) { - assert2.include( + assert.include( err.message, `utxo 1 does not have a satoshis or value property`, "Expected error message." @@ -577,9 +554,9 @@ describe("#SLP Utils", () => { await uut.Utils.tokenUtxoDetails(utxos) - assert2.equal(true, false, "Unexpected result.") + assert.equal(true, false, "Unexpected result.") } catch (err) { - assert2.include( + assert.include( err.message, `utxo 1 does not have a txid or tx_hash property`, "Expected error message." @@ -639,31 +616,31 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - // assert2.equal(data[0], false, "Change UTXO marked as false.") - assert2.property(data[0], "txid") - assert2.property(data[0], "vout") - assert2.property(data[0], "amount") - assert2.property(data[0], "satoshis") - assert2.property(data[0], "height") - assert2.property(data[0], "confirmations") - assert2.property(data[0], "isValid") - assert2.equal(data[0].isValid, false) + // assert.equal(data[0], false, "Change UTXO marked as false.") + assert.property(data[0], "txid") + assert.property(data[0], "vout") + assert.property(data[0], "amount") + assert.property(data[0], "satoshis") + assert.property(data[0], "height") + assert.property(data[0], "confirmations") + assert.property(data[0], "isValid") + assert.equal(data[0].isValid, false) - assert2.property(data[1], "txid") - assert2.property(data[1], "vout") - assert2.property(data[1], "amount") - assert2.property(data[1], "satoshis") - assert2.property(data[1], "height") - assert2.property(data[1], "confirmations") - assert2.property(data[1], "utxoType") - assert2.property(data[1], "tokenId") - assert2.property(data[1], "tokenTicker") - assert2.property(data[1], "tokenName") - assert2.property(data[1], "tokenDocumentUrl") - assert2.property(data[1], "tokenDocumentHash") - assert2.property(data[1], "decimals") - assert2.property(data[1], "isValid") - assert2.equal(data[1].isValid, true) + assert.property(data[1], "txid") + assert.property(data[1], "vout") + assert.property(data[1], "amount") + assert.property(data[1], "satoshis") + assert.property(data[1], "height") + assert.property(data[1], "confirmations") + assert.property(data[1], "utxoType") + assert.property(data[1], "tokenId") + assert.property(data[1], "tokenTicker") + assert.property(data[1], "tokenName") + assert.property(data[1], "tokenDocumentUrl") + assert.property(data[1], "tokenDocumentHash") + assert.property(data[1], "decimals") + assert.property(data[1], "isValid") + assert.equal(data[1].isValid, true) }) it("should return details for a MINT token utxo", async () => { @@ -720,23 +697,23 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.property(data[0], "txid") - assert2.property(data[0], "vout") - assert2.property(data[0], "amount") - assert2.property(data[0], "satoshis") - assert2.property(data[0], "height") - assert2.property(data[0], "confirmations") - assert2.property(data[0], "utxoType") - assert2.property(data[0], "transactionType") - assert2.property(data[0], "tokenId") - assert2.property(data[0], "tokenTicker") - assert2.property(data[0], "tokenName") - assert2.property(data[0], "tokenDocumentUrl") - assert2.property(data[0], "tokenDocumentHash") - assert2.property(data[0], "decimals") - assert2.property(data[0], "mintBatonVout") - assert2.property(data[0], "tokenQty") - assert2.property(data[0], "isValid") + assert.property(data[0], "txid") + assert.property(data[0], "vout") + assert.property(data[0], "amount") + assert.property(data[0], "satoshis") + assert.property(data[0], "height") + assert.property(data[0], "confirmations") + assert.property(data[0], "utxoType") + assert.property(data[0], "transactionType") + assert.property(data[0], "tokenId") + assert.property(data[0], "tokenTicker") + assert.property(data[0], "tokenName") + assert.property(data[0], "tokenDocumentUrl") + assert.property(data[0], "tokenDocumentHash") + assert.property(data[0], "decimals") + assert.property(data[0], "mintBatonVout") + assert.property(data[0], "tokenQty") + assert.property(data[0], "isValid") assert.equal(data[0].isValid, true) }) @@ -792,21 +769,21 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.property(data[0], "txid") - assert2.property(data[0], "vout") - assert2.property(data[0], "amount") - assert2.property(data[0], "satoshis") - assert2.property(data[0], "height") - assert2.property(data[0], "confirmations") - assert2.property(data[0], "utxoType") - assert2.property(data[0], "tokenId") - assert2.property(data[0], "tokenTicker") - assert2.property(data[0], "tokenName") - assert2.property(data[0], "tokenDocumentUrl") - assert2.property(data[0], "tokenDocumentHash") - assert2.property(data[0], "decimals") - assert2.property(data[0], "tokenQty") - assert2.property(data[0], "isValid") + assert.property(data[0], "txid") + assert.property(data[0], "vout") + assert.property(data[0], "amount") + assert.property(data[0], "satoshis") + assert.property(data[0], "height") + assert.property(data[0], "confirmations") + assert.property(data[0], "utxoType") + assert.property(data[0], "tokenId") + assert.property(data[0], "tokenTicker") + assert.property(data[0], "tokenName") + assert.property(data[0], "tokenDocumentUrl") + assert.property(data[0], "tokenDocumentHash") + assert.property(data[0], "decimals") + assert.property(data[0], "tokenQty") + assert.property(data[0], "isValid") assert.equal(data[0].isValid, true) }) @@ -873,17 +850,17 @@ describe("#SLP Utils", () => { const result = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.isArray(result) - assert2.equal(result.length, 2) + assert.isArray(result) + assert.equal(result.length, 2) - assert2.property(result[0], "txid") - assert2.property(result[0], "vout") - assert2.property(result[0], "value") - assert2.property(result[0], "satoshis") - assert2.property(result[0], "height") - assert2.property(result[0], "confirmations") - assert2.property(result[0], "isValid") - assert2.equal(result[0].isValid, false) + assert.property(result[0], "txid") + assert.property(result[0], "vout") + assert.property(result[0], "value") + assert.property(result[0], "satoshis") + assert.property(result[0], "height") + assert.property(result[0], "confirmations") + assert.property(result[0], "isValid") + assert.equal(result[0].isValid, false) assert.equal(result[1].isValid, true) assert.equal(result[1].utxoType, "token") @@ -953,19 +930,19 @@ describe("#SLP Utils", () => { const result = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.isArray(result) - assert2.equal(result.length, 2) + assert.isArray(result) + assert.equal(result.length, 2) - assert2.property(result[0], "txid") - assert2.property(result[0], "vout") - assert2.property(result[0], "amount") - assert2.property(result[0], "satoshis") - assert2.property(result[0], "height") - assert2.property(result[0], "confirmations") - assert2.property(result[0], "isValid") - assert2.equal(result[0].isValid, false) + assert.property(result[0], "txid") + assert.property(result[0], "vout") + assert.property(result[0], "amount") + assert.property(result[0], "satoshis") + assert.property(result[0], "height") + assert.property(result[0], "confirmations") + assert.property(result[0], "isValid") + assert.equal(result[0].isValid, false) - assert2.equal(result[1].isValid, true) + assert.equal(result[1].isValid, true) assert.equal(result[1].utxoType, "token") assert.equal(result[1].transactionType, "send") }) @@ -1001,23 +978,23 @@ describe("#SLP Utils", () => { const result = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.isArray(result) + assert.isArray(result) - assert2.property(result[0], "txid") - assert2.property(result[0], "vout") - assert2.property(result[0], "amount") - assert2.property(result[0], "satoshis") - assert2.property(result[0], "confirmations") - assert2.property(result[0], "isValid") - assert2.equal(result[0].isValid, false) + assert.property(result[0], "txid") + assert.property(result[0], "vout") + assert.property(result[0], "amount") + assert.property(result[0], "satoshis") + assert.property(result[0], "confirmations") + assert.property(result[0], "isValid") + assert.equal(result[0].isValid, false) - assert2.property(result[1], "txid") - assert2.property(result[1], "vout") - assert2.property(result[1], "amount") - assert2.property(result[1], "satoshis") - assert2.property(result[1], "confirmations") - assert2.property(result[1], "isValid") - assert2.equal(result[1].isValid, false) + assert.property(result[1], "txid") + assert.property(result[1], "vout") + assert.property(result[1], "amount") + assert.property(result[1], "satoshis") + assert.property(result[1], "confirmations") + assert.property(result[1], "isValid") + assert.equal(result[1].isValid, false) }) it("should decode a Genesis transaction", async () => { @@ -1078,18 +1055,18 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.isArray(data) + assert.isArray(data) - assert2.equal(data[0].utxoType, "token") - assert2.equal(data[0].tokenQty, 100) - assert2.equal(data[0].isValid, true) - assert2.equal(data[0].tokenType, 1) + assert.equal(data[0].utxoType, "token") + assert.equal(data[0].tokenQty, 100) + assert.equal(data[0].isValid, true) + assert.equal(data[0].tokenType, 1) - assert2.equal(data[1].utxoType, "minting-baton") - assert2.equal(data[1].isValid, true) - assert2.equal(data[1].tokenType, 1) + assert.equal(data[1].utxoType, "minting-baton") + assert.equal(data[1].isValid, true) + assert.equal(data[1].tokenType, 1) - assert2.equal(data[2].isValid, false) + assert.equal(data[2].isValid, false) }) it("should decode a Mint transaction", async () => { @@ -1176,18 +1153,18 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.isArray(data) + assert.isArray(data) - assert2.equal(data[0].utxoType, "token") - assert2.equal(data[0].tokenQty, 100) - assert2.equal(data[0].isValid, true) - assert2.equal(data[0].tokenType, 1) + assert.equal(data[0].utxoType, "token") + assert.equal(data[0].tokenQty, 100) + assert.equal(data[0].isValid, true) + assert.equal(data[0].tokenType, 1) - assert2.equal(data[1].utxoType, "minting-baton") - assert2.equal(data[1].isValid, true) - assert2.equal(data[1].tokenType, 1) + assert.equal(data[1].utxoType, "minting-baton") + assert.equal(data[1].isValid, true) + assert.equal(data[1].tokenType, 1) - assert2.equal(data[2].isValid, false) + assert.equal(data[2].isValid, false) }) it("should decode a NFT Group Genesis transaction", async () => { @@ -1270,17 +1247,17 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.isArray(data) + assert.isArray(data) - assert2.equal(data[0].isValid, false) + assert.equal(data[0].isValid, false) - assert2.equal(data[1].utxoType, "minting-baton") - assert2.equal(data[1].isValid, true) - assert2.equal(data[1].tokenType, 129) + assert.equal(data[1].utxoType, "minting-baton") + assert.equal(data[1].isValid, true) + assert.equal(data[1].tokenType, 129) - assert2.equal(data[2].utxoType, "token") - assert2.equal(data[2].tokenType, 129) - assert2.equal(data[1].isValid, true) + assert.equal(data[2].utxoType, "token") + assert.equal(data[2].tokenType, 129) + assert.equal(data[1].isValid, true) }) it("should decode a NFT Group Mint transaction", async () => { @@ -1372,17 +1349,17 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.isArray(data) + assert.isArray(data) - assert2.equal(data[0].isValid, false) + assert.equal(data[0].isValid, false) - assert2.equal(data[1].utxoType, "minting-baton") - assert2.equal(data[1].tokenType, 129) - assert2.equal(data[1].isValid, true) + assert.equal(data[1].utxoType, "minting-baton") + assert.equal(data[1].tokenType, 129) + assert.equal(data[1].isValid, true) - assert2.equal(data[2].utxoType, "token") - assert2.equal(data[2].tokenType, 129) - assert2.equal(data[2].isValid, true) + assert.equal(data[2].utxoType, "token") + assert.equal(data[2].tokenType, 129) + assert.equal(data[2].isValid, true) }) it("should decode a NFT Child Genesis transaction", async () => { @@ -1442,13 +1419,13 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.isArray(data) + assert.isArray(data) - assert2.equal(data[0].utxoType, "token") - assert2.equal(data[0].tokenType, 65) - assert2.equal(data[0].isValid, true) + assert.equal(data[0].utxoType, "token") + assert.equal(data[0].tokenType, 65) + assert.equal(data[0].isValid, true) - assert2.equal(data[1].isValid, false) + assert.equal(data[1].isValid, false) }) it("should decode an NFT Child Send transaction", async () => { @@ -1518,14 +1495,14 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.isArray(data) + assert.isArray(data) - assert2.equal(data[0].utxoType, "token") - assert2.equal(data[0].transactionType, "send") - assert2.equal(data[0].tokenType, 65) - assert2.equal(data[0].isValid, true) + assert.equal(data[0].utxoType, "token") + assert.equal(data[0].transactionType, "send") + assert.equal(data[0].tokenType, 65) + assert.equal(data[0].isValid, true) - assert2.equal(data[1].isValid, false) + assert.equal(data[1].isValid, false) }) it("should decode an NFT Group Send transaction", async () => { @@ -1611,19 +1588,19 @@ describe("#SLP Utils", () => { const data = await uut.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.isArray(data) + assert.isArray(data) - assert2.equal(data[0].utxoType, "token") - assert2.equal(data[0].transactionType, "send") - assert2.equal(data[0].tokenType, 129) - assert2.equal(data[0].isValid, true) + assert.equal(data[0].utxoType, "token") + assert.equal(data[0].transactionType, "send") + assert.equal(data[0].tokenType, 129) + assert.equal(data[0].isValid, true) - assert2.equal(data[1].utxoType, "token") - assert2.equal(data[1].transactionType, "send") - assert2.equal(data[1].tokenType, 129) - assert2.equal(data[1].isValid, true) + assert.equal(data[1].utxoType, "token") + assert.equal(data[1].transactionType, "send") + assert.equal(data[1].tokenType, 129) + assert.equal(data[1].isValid, true) - assert2.equal(data[2].isValid, false) + assert.equal(data[2].isValid, false) }) it("should return null value when 429 recieved", async () => { @@ -1704,7 +1681,7 @@ describe("#SLP Utils", () => { try { await uut.Utils.txDetails() } catch (err) { - assert2.include( + assert.include( err.message, `txid string must be included`, "Expected error message." @@ -1730,7 +1707,7 @@ describe("#SLP Utils", () => { assert.fail("Unexpected result") } catch (err) { // console.log(`err: `, err) - assert2.include(err.error, `TXID not found`, "Expected error message.") + assert.include(err.error, `TXID not found`, "Expected error message.") } }) @@ -1747,7 +1724,7 @@ describe("#SLP Utils", () => { const result = await uut.Utils.txDetails(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.hasAnyKeys(result, [ + assert.hasAnyKeys(result, [ "txid", "version", "locktime", @@ -1775,10 +1752,10 @@ describe("#SLP Utils", () => { await uut.Utils.hydrateUtxos(utxos) - assert2.equal(true, false, "Uh oh. Code path should not end here.") + assert.equal(true, false, "Uh oh. Code path should not end here.") } catch (err) { // console.log(`Error: `, err) - assert2.include(err.message, `Input must be an array.`) + assert.include(err.message, `Input must be an array.`) } }) }) @@ -1792,10 +1769,10 @@ describe("#SLP Utils", () => { await uut.Utils.validateTxid2(txid) - assert2.equal(true, false, "Unexpected result") + assert.equal(true, false, "Unexpected result") } catch (err) { // console.log("err: ", err) - assert2.include(err.message, "txid must be 64 character string") + assert.include(err.message, "txid must be 64 character string") } }) @@ -1806,10 +1783,10 @@ describe("#SLP Utils", () => { await uut.Utils.validateTxid2(txid) - assert2.equal(true, false, "Unexpected result") + assert.equal(true, false, "Unexpected result") } catch (err) { // console.log("err: ", err) - assert2.include(err.message, "txid must be 64 character string") + assert.include(err.message, "txid must be 64 character string") } }) @@ -1829,11 +1806,11 @@ describe("#SLP Utils", () => { const result = await uut.Utils.validateTxid2(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.property(result, "txid") - assert2.equal(result.txid, txid) + assert.property(result, "txid") + assert.equal(result.txid, txid) - assert2.property(result, "isValid") - assert2.equal(result.isValid, false) + assert.property(result, "isValid") + assert.equal(result.isValid, false) }) it("should validate a known valid TXID", async () => { @@ -1852,11 +1829,11 @@ describe("#SLP Utils", () => { const result = await uut.Utils.validateTxid2(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.property(result, "txid") - assert2.equal(result.txid, txid) + assert.property(result, "txid") + assert.equal(result.txid, txid) - assert2.property(result, "isValid") - assert2.equal(result.isValid, true) + assert.property(result, "isValid") + assert.equal(result.isValid, true) }) // slp-validate can take a long time. bch-api cuts it off if it fails to @@ -1874,10 +1851,10 @@ describe("#SLP Utils", () => { await uut.Utils.validateTxid2(txid) - assert2.equal(true, false, "Unexpected result") + assert.equal(true, false, "Unexpected result") } catch (err) { // console.log("err: ", err) - assert2.include(err.message, "slp-validate timed out") + assert.include(err.message, "slp-validate timed out") } }) }) @@ -1895,13 +1872,13 @@ describe("#SLP Utils", () => { const result = await uut.Utils.validateTxid(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.isArray(result) + assert.isArray(result) - assert2.property(result[0], "txid") - assert2.equal(result[0].txid, txid) + assert.property(result[0], "txid") + assert.equal(result[0].txid, txid) - assert2.property(result[0], "valid") - assert2.equal(result[0].valid, null) + assert.property(result[0], "valid") + assert.equal(result[0].valid, null) }) /* it("should handle a mix of valid, invalid, and non-SLP txs", async () => { @@ -1920,7 +1897,6 @@ describe("#SLP Utils", () => { console.log(`result: ${JSON.stringify(result, null, 2)}`) }) */ - /* }) describe("#getWhitelist", () => { @@ -1931,9 +1907,9 @@ describe("#SLP Utils", () => { const result = await uut.Utils.getWhitelist() - assert2.isArray(result) - assert2.property(result[0], "name") - assert2.property(result[1], "tokenId") + assert.isArray(result) + assert.property(result[0], "name") + assert.property(result[1], "tokenId") }) it("catches and throws an error", async () => { @@ -1945,10 +1921,10 @@ describe("#SLP Utils", () => { await uut.Utils.getWhitelist() - assert2.fail("Unexpected result") + assert.fail("Unexpected result") } catch (err) { console.log("err: ", err) - assert2.include(err.error, "Network error") + assert.include(err.error, "Network error") } }) }) @@ -1966,13 +1942,13 @@ describe("#SLP Utils", () => { const result = await uut.Utils.validateTxid3(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.isArray(result) + assert.isArray(result) - assert2.property(result[0], "txid") - assert2.equal(result[0].txid, txid) + assert.property(result[0], "txid") + assert.equal(result[0].txid, txid) - assert2.property(result[0], "valid") - assert2.equal(result[0].valid, null) + assert.property(result[0], "valid") + assert.equal(result[0].valid, null) }) it("should handle an array with a single element", async () => { @@ -1986,13 +1962,13 @@ describe("#SLP Utils", () => { const result = await uut.Utils.validateTxid3(txid) - assert2.isArray(result) + assert.isArray(result) - assert2.property(result[0], "txid") - assert2.equal(result[0].txid, txid) + assert.property(result[0], "txid") + assert.equal(result[0].txid, txid) - assert2.property(result[0], "valid") - assert2.equal(result[0].valid, true) + assert.property(result[0], "valid") + assert.equal(result[0].valid, true) }) it("should handle an single string input", async () => { @@ -2005,13 +1981,13 @@ describe("#SLP Utils", () => { const result = await uut.Utils.validateTxid3(txid) - assert2.isArray(result) + assert.isArray(result) - assert2.property(result[0], "txid") - assert2.equal(result[0].txid, txid) + assert.property(result[0], "txid") + assert.equal(result[0].txid, txid) - assert2.property(result[0], "valid") - assert2.equal(result[0].valid, true) + assert.property(result[0], "valid") + assert.equal(result[0].valid, true) }) it("catches and throws an error", async () => { @@ -2023,12 +1999,11 @@ describe("#SLP Utils", () => { await uut.Utils.validateTxid3() - assert2.equal(true, false, "Unexpected result") + assert.equal(true, false, "Unexpected result") } catch (err) { // console.log("err: ", err) - assert2.include(err.error, "Network error") + assert.include(err.error, "Network error") } }) }) - */ }) From 1f01e6ae47f293e244cd60d5f34ee1aa68db5e9f Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 18:40:14 -0800 Subject: [PATCH 8/9] fix(tokenUtxoDetails): Catching and invalidating malformed SLP OP_RETURNs --- package.json | 2 +- src/slp/utils.js | 16 ++++++++++++---- test/unit/slp-utils.js | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index de0f794..8bb0ba7 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "test:temp": "RESTURL=https://bchn.fullstack.cash/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/bchn/", "test:temp2": "RESTURL=https://abc.fullstack.cash/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/", "test:temp3": "ISBCHN=true RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/bchn/", - "test:temp4": "mocha --trace-warnings --unhandled-rejections=strict -g '#SLP Utils' test/unit/", + "test:temp4": "mocha --timeout=30000 -g '#tokenUtxoDetails' test/unit/", "coverage": "nyc report --reporter=text-lcov | coveralls", "coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/", "docs": "./node_modules/.bin/apidoc -i src/ -o docs" diff --git a/src/slp/utils.js b/src/slp/utils.js index dffd5b7..a6aea56 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -1111,9 +1111,9 @@ class Utils { let slpData = false try { slpData = await this.decodeOpReturn(utxo.txid, decodeOpReturnCache) - // console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`) + console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`) } catch (err) { - // console.log(`error from decodeOpReturn(${utxo.txid}): `, err) + console.log(`error from decodeOpReturn(${utxo.txid}): `, err) // An error will be thrown if the txid is not SLP. // If error is for some other reason, like a 429 error, mark utxo as 'null' @@ -1121,9 +1121,13 @@ class Utils { if ( !err.message || (err.message.indexOf("scriptpubkey not op_return") === -1 && - err.message.indexOf("lokad id") === -1) + err.message.indexOf("lokad id") === -1 && + err.message.indexOf("trailing data") === -1) ) { - // console.log(`error from decodeOpReturn(${utxo.txid}): `, err) + console.log( + `unknown error from decodeOpReturn(). Marking as 'null'`, + err + ) utxo.isValid = null outAry.push(utxo) @@ -1132,8 +1136,10 @@ class Utils { // an SLP UTXO. // Mark as false and continue the loop. } else { + console.log("marking as invalid") utxo.isValid = false outAry.push(utxo) + console.log(`outAry: ${JSON.stringify(outAry, null, 2)}`) } // Halt the execution of the loop and increase to the next index. @@ -1336,6 +1342,8 @@ class Utils { } } + console.log(`pt2 outAry: ${JSON.stringify(outAry, null, 2)}`) + return outAry } catch (error) { if (error.response && error.response.data) throw error.response.data diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index f55707a..7f32c10 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -1673,7 +1673,43 @@ describe("#SLP Utils", () => { assert.equal(data[0].isValid, false) }) + it("should invalidate a malformed SLP OP_RETURN", async () => { + sandbox + .stub(uut.Utils, "decodeOpReturn") + .rejects(new Error("trailing data")) + + const utxos = [ + // Malformed SLP tx + { + note: "Malformed SLP tx", + tx_hash: + "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", + tx_pos: 1, + value: 546 + } + ] + + const data = await uut.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + assert.equal(data[0].isValid, false) + }) + + // it("should validate against the whitelist SLPDB when regular SLPDB returns null", async () => { + // const utxos = [ + // // Malformed SLP tx + // { + // note: "Malformed SLP tx", + // tx_hash: + // "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", + // tx_pos: 1, + // value: 546 + // } + // ] // + // const data = await uut.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + // }) }) describe("#txDetails", () => { From d1847d2ecadf0bf61c563e99d77fabab38be0dc8 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 19:18:47 -0800 Subject: [PATCH 9/9] feat(tokenUtxoDetails()): Using fallback SLP validators --- src/slp/utils.js | 49 +++++++++------ test/unit/fixtures/slp/mock-utils.js | 30 ++++++++- test/unit/slp-utils.js | 91 +++++++++++++++++++++++----- 3 files changed, 137 insertions(+), 33 deletions(-) diff --git a/src/slp/utils.js b/src/slp/utils.js index a6aea56..9f55d32 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -1,3 +1,4 @@ +// Public npm libraries const axios = require("axios") const slpParser = require("slp-parser") @@ -1111,9 +1112,9 @@ class Utils { let slpData = false try { slpData = await this.decodeOpReturn(utxo.txid, decodeOpReturnCache) - console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`) + // console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`) } catch (err) { - console.log(`error from decodeOpReturn(${utxo.txid}): `, err) + // console.log(`error from decodeOpReturn(${utxo.txid}): `, err) // An error will be thrown if the txid is not SLP. // If error is for some other reason, like a 429 error, mark utxo as 'null' @@ -1124,10 +1125,10 @@ class Utils { err.message.indexOf("lokad id") === -1 && err.message.indexOf("trailing data") === -1) ) { - console.log( - `unknown error from decodeOpReturn(). Marking as 'null'`, - err - ) + // console.log( + // `unknown error from decodeOpReturn(). Marking as 'null'`, + // err + // ) utxo.isValid = null outAry.push(utxo) @@ -1136,10 +1137,9 @@ class Utils { // an SLP UTXO. // Mark as false and continue the loop. } else { - console.log("marking as invalid") + // console.log("marking as invalid") utxo.isValid = false outAry.push(utxo) - console.log(`outAry: ${JSON.stringify(outAry, null, 2)}`) } // Halt the execution of the loop and increase to the next index. @@ -1285,7 +1285,8 @@ class Utils { // validate the TXID with SLPDB. if (outAry[i].tokenType) { // Only execute this block if the current UTXO has a 'tokenType' - // property. i.e. it has been hydrated with SLP information. + // property. i.e. it has been successfully hydrated with SLP + // information. // If the value has been cached, use the cached version first. let isValid = cachedTxValidation[utxo.txid] @@ -1302,7 +1303,13 @@ class Utils { // If still null, check the whitelist SLPDB if (isValid === null) { - console.log(`outAry[${i}]: ${JSON.stringify(outAry[i], null, 2)}`) + // console.log( + // `checking against whitelist SLPDB. outAry[${i}]: ${JSON.stringify( + // outAry[i], + // null, + // 2 + // )}` + // ) // Figure out if the token UTXO is in the whitelist. let utxoInWhitelist = false @@ -1318,6 +1325,15 @@ class Utils { // if the general SLPDB is not. if (utxoInWhitelist) { isValid = await this.validateTxid3(utxo.txid) + + // console.log( + // `whitelist-SLPDB for ${utxo.txid}: ${JSON.stringify( + // isValid, + // null, + // 2 + // )}` + // ) + isValid = isValid[0].valid // Save the result to the local cache. @@ -1328,22 +1344,21 @@ class Utils { // If still null, as a last resort, check it against slp-validate if (isValid === null) { isValid = await this.validateTxid2(utxo.txid) - console.log( - `slp-validate isValid: ${JSON.stringify(isValid, null, 2)}` - ) - isValid = isValid.valid + // console.log( + // `slp-validate isValid: ${JSON.stringify(isValid, null, 2)}` + // ) + + isValid = isValid.isValid // Save the result to the local cache. cachedTxValidation[utxo.txid] = isValid } - console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`) + // console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`) outAry[i].isValid = isValid } } - console.log(`pt2 outAry: ${JSON.stringify(outAry, null, 2)}`) - return outAry } catch (error) { if (error.response && error.response.data) throw error.response.data diff --git a/test/unit/fixtures/slp/mock-utils.js b/test/unit/fixtures/slp/mock-utils.js index b0d0034..af499b0 100644 --- a/test/unit/fixtures/slp/mock-utils.js +++ b/test/unit/fixtures/slp/mock-utils.js @@ -1163,6 +1163,33 @@ const mockValidateTxidArray = [ } ] +const whitelist = [ + { + name: "USDH", + tokenId: "c4b0d62156b3fa5c8f3436079b5394f7edc1bef5dc1cd2f9d0c4d46f82cca479" + }, + { + name: "SPICE", + tokenId: "4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf" + }, + { + name: "PSF", + tokenId: "38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0" + }, + { + name: "TROUT", + tokenId: "a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2" + }, + { + name: "PSFTEST", + tokenId: "d0ef4de95b78222bfee2326ab11382f4439aa0855936e2fe6ac129a8d778baa0" + }, + { + name: "TOK-CH", + tokenId: "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" + } +] + module.exports = { mockList, mockToken, @@ -1190,5 +1217,6 @@ module.exports = { mockInvalidSlpSend, mockWhitelist, mockValidateTxid3Valid, - mockValidateTxid3Invalid + mockValidateTxid3Invalid, + whitelist } diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 7f32c10..c562fd7 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -1695,21 +1695,82 @@ describe("#SLP Utils", () => { assert.equal(data[0].isValid, false) }) - // it("should validate against the whitelist SLPDB when regular SLPDB returns null", async () => { - // const utxos = [ - // // Malformed SLP tx - // { - // note: "Malformed SLP tx", - // tx_hash: - // "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", - // tx_pos: 1, - // value: 546 - // } - // ] - // - // const data = await uut.Utils.tokenUtxoDetails(utxos) - // console.log(`data: ${JSON.stringify(data, null, 2)}`) - // }) + it("should use backup validators when regular SLPDB returns null", async () => { + // Mock the call to REST API + // Stub the calls to decodeOpReturn. + sandbox + .stub(uut.Utils, "decodeOpReturn") + .onCall(0) + .resolves({ + tokenType: 1, + txType: "SEND", + tokenId: + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", + amounts: ["200000000", "99887500000000"] + }) + .onCall(1) + .resolves({ + tokenType: 1, + txType: "GENESIS", + ticker: "TOK-CH", + name: "TokyoCash", + tokenId: + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", + documentUri: "", + documentHash: "", + decimals: 8, + mintBatonVout: 0, + qty: "2100000000000000" + }) + + // Force default SLPDB to return 'null', which should trigger usage of the + // backup whitelist-SLPDB. + sandbox.stub(uut.Utils, "validateTxid").resolves([ + { + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + valid: null + } + ]) + + // Force the token UTXO to be in the whitelist. + uut.Utils.whitelist = mockData.whitelist + + // Mock the response from the whitelist-SLPDB. + sandbox.stub(uut.Utils, "validateTxid3").resolves([ + { + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + valid: null + } + ]) + + // Mock the response from slp-api + sandbox.stub(uut.Utils, "validateTxid2").resolves({ + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + isValid: true, + msg: "" + }) + + const utxos = [ + { + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + vout: 1, + amount: 0.00000546, + satoshis: 546, + height: 596089, + confirmations: 748 + } + ] + + const data = await uut.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + // The UTXO should have been validated by the third validation source. + assert.equal(data[0].isValid, true) + }) }) describe("#txDetails", () => {