From c228acd28a8067c6cb0eaa6a670e81dcbb3d0629 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 3 May 2021 18:53:41 -0700 Subject: [PATCH] fix(integration tests): Adding tests for bchn with no whitelist SLPDB --- .../chains/bchn-no-wl-slpdb/rawtransaction.js | 71 +++++ .../chains/bchn-no-wl-slpdb/slp.js | 273 ++++++++++++++++++ .../bchn-no-wl-slpdb/utxo-integration.js | 105 +++++++ 3 files changed, 449 insertions(+) create mode 100644 test/integration/chains/bchn-no-wl-slpdb/rawtransaction.js create mode 100644 test/integration/chains/bchn-no-wl-slpdb/slp.js create mode 100644 test/integration/chains/bchn-no-wl-slpdb/utxo-integration.js diff --git a/test/integration/chains/bchn-no-wl-slpdb/rawtransaction.js b/test/integration/chains/bchn-no-wl-slpdb/rawtransaction.js new file mode 100644 index 0000000..c2fe0d1 --- /dev/null +++ b/test/integration/chains/bchn-no-wl-slpdb/rawtransaction.js @@ -0,0 +1,71 @@ +/* + Integration tests for the bchjs. Only covers calls made to + rest.bitcoin.com. + + TODO +*/ + +const chai = require('chai') +const assert = chai.assert +const BCHJS = require('../../../../src/bch-js') +const bchjs = new BCHJS() + +// Inspect utility used for debugging. +const util = require('util') +util.inspect.defaultOptions = { + showHidden: true, + colors: true, + depth: 3 +} + +describe('#rawtransaction', () => { + beforeEach(async () => { + if (process.env.IS_USING_FREE_TIER) await sleep(3000) + }) + + /* + Testing sentRawTransaction isn't really possible with an integration test, + as the endpoint really needs an e2e test to be properly tested. The tests + below expect error messages returned from the server, but at least test + that the server is responding on those endpoints, and responds consistently. + */ + describe('sendRawTransaction', () => { + it('should send a single transaction hex', async () => { + try { + const hex = + '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000' + + await bchjs.RawTransactions.sendRawTransaction(hex) + // console.log(`result ${JSON.stringify(result, null, 2)}`) + + assert.equal(true, false, 'Unexpected result!') + } catch (err) { + // console.log(`err: ${util.inspect(err)}`) + + assert.hasAllKeys(err, ['error']) + assert.include(err.error, 'Missing inputs') + } + }) + + it('should send an array of tx hexes', async () => { + try { + const hexes = [ + '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000', + '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000' + ] + + const result = await bchjs.RawTransactions.sendRawTransaction(hexes) + console.log(`result ${JSON.stringify(result, null, 2)}`) + } catch (err) { + // console.log(`err: ${util.inspect(err)}`) + + assert.hasAllKeys(err, ['error']) + assert.include(err.error, 'Missing inputs') + } + }) + }) +}) + +function sleep (ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} diff --git a/test/integration/chains/bchn-no-wl-slpdb/slp.js b/test/integration/chains/bchn-no-wl-slpdb/slp.js new file mode 100644 index 0000000..6ce75a2 --- /dev/null +++ b/test/integration/chains/bchn-no-wl-slpdb/slp.js @@ -0,0 +1,273 @@ +/* + Integration tests for the bchjs covering SLP tokens. + These tests are specific to the ABC chain. +*/ + +const chai = require('chai') +const assert = chai.assert + +const BCHJS = require('../../../../src/bch-js') +let bchjs + +// Inspect utility used for debugging. +const util = require('util') +util.inspect.defaultOptions = { + showHidden: true, + colors: true, + depth: 1 +} + +describe('#SLP', () => { + // before(() => { + // console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`) + // console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`) + // }) + + beforeEach(async () => { + // Introduce a delay so that the BVT doesn't trip the rate limits. + if (process.env.IS_USING_FREE_TIER) await sleep(3000) + + bchjs = new BCHJS() + }) + + describe('#util', () => { + describe('#decodeOpReturn', () => { + it('should decode a NFT Child transaction', async () => { + const txid = + 'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b' + + const data = await bchjs.SLP.Utils.decodeOpReturn(txid) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + assert.property(data, 'tokenType') + assert.property(data, 'txType') + assert.property(data, 'ticker') + assert.property(data, 'name') + assert.property(data, 'tokenId') + assert.property(data, 'documentUri') + assert.property(data, 'documentHash') + assert.property(data, 'decimals') + assert.property(data, 'mintBatonVout') + assert.property(data, 'qty') + + assert.equal(data.tokenType, 65) + assert.equal(data.mintBatonVout, 0) + assert.equal(data.qty, '1') + }) + }) + + describe('#tokenUtxoDetails', () => { + it('should handle a range of UTXO types', async () => { + const utxos = [ + // Malformed SLP tx + { + note: 'Malformed SLP tx', + tx_hash: + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a', + tx_pos: 1, + value: 546 + }, + // Normal TX (non-SLP) + { + note: 'Normal TX (non-SLP)', + tx_hash: + '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0', + tx_pos: 0, + value: 400000 + }, + // Valid PSF SLP tx + { + note: 'Valid PSF SLP tx', + tx_hash: + 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd', + tx_pos: 1, + value: 546 + }, + // Valid SLP token not in whitelist + { + note: 'Valid SLP token not in whitelist', + tx_hash: + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488', + tx_pos: 1, + value: 546 + }, + // Token send on BCHN network. + { + note: 'Token send on BCHN network', + tx_hash: + '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019', + tx_pos: 1, + value: 546 + }, + // Token send on ABC network. + { + note: 'Token send on ABC network', + tx_hash: + '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d', + tx_pos: 1, + value: 546 + }, + // Known invalid SLP token send of PSF tokens. + { + note: 'Known invalid SLP token send of PSF tokens', + tx_hash: + '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74', + tx_pos: 1, + value: 546 + } + ] + + const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + // Malformed SLP tx + assert.equal(data[0].tx_hash, utxos[0].tx_hash) + assert.equal(data[0].isValid, false) + + // 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('#validateTxid3', () => { + it('should invalidate a known invalid TXID', async () => { + const txid = + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a' + + const result = await bchjs.SLP.Utils.validateTxid3(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + + assert.property(result[0], 'txid') + assert.equal(result[0].txid, txid) + + assert.property(result[0], 'valid') + assert.equal(result[0].valid, null) + }) + + it('should validate a known valid TXID', async () => { + const txid = + 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd' + + const result = await bchjs.SLP.Utils.validateTxid3(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + + assert.property(result[0], 'txid') + assert.equal(result[0].txid, txid) + + assert.property(result[0], 'valid') + assert.equal(result[0].valid, true) + }) + + it('should handle a mix of valid, invalid, and non-SLP txs', async () => { + const txids = [ + // Malformed SLP tx + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a', + // Normal TX (non-SLP) + '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0', + // Valid PSF SLP tx + 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd', + // Valid SLP token not in whitelist + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488', + // Unprocessed SLP TX + '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019' + ] + + const result = await bchjs.SLP.Utils.validateTxid3(txids) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + // True in validateTxid() (no whitelist SLPDB) but null in validateTxid3() + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, true) + + // Note: This should change from null to true once SLPDB finishes indexing. + assert.equal(result[4].txid, txids[4]) + // True when no whitelist SLPDB is used. + assert.equal(result[4].valid, true) + }) + }) + + describe('#validateTxid', () => { + it('should handle a mix of valid, invalid, and non-SLP txs', async () => { + const txids = [ + // Malformed SLP tx + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a', + // Normal TX (non-SLP) + '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0', + // Valid PSF SLP tx + 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd', + // Valid SLP token not in whitelist + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488', + // Token send on BCHN network. + '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019', + // Token send on ABC network. + '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d' + ] + + const result = await bchjs.SLP.Utils.validateTxid(txids) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + // True in validateTxid() but null in validateTxid3() + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, true) + + assert.equal(result[4].txid, txids[4]) + assert.equal(result[4].valid, true) + + assert.equal(result[5].txid, txids[5]) + assert.equal(result[5].valid, null) + }) + }) + }) +}) + +// Promise-based sleep function +function sleep (ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} diff --git a/test/integration/chains/bchn-no-wl-slpdb/utxo-integration.js b/test/integration/chains/bchn-no-wl-slpdb/utxo-integration.js new file mode 100644 index 0000000..b0dfc0d --- /dev/null +++ b/test/integration/chains/bchn-no-wl-slpdb/utxo-integration.js @@ -0,0 +1,105 @@ +/* + Integration tests for the utxo.js library. +*/ + +const assert = require('chai').assert + +const BCHJS = require('../../../../src/bch-js') +const bchjs = new BCHJS() + +describe('#UTXO', () => { + beforeEach(async () => { + // sandbox = sinon.createSandbox() + + if (process.env.IS_USING_FREE_TIER) await sleep(3000) + }) + + describe('#get', () => { + it('should get hydrated and filtered UTXOs for an address', async () => { + // const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9' + const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9' + + const result = await bchjs.Utxo.get(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.property(result[0], 'address') + assert.property(result[0], 'bchUtxos') + assert.property(result[0], 'nullUtxos') + assert.property(result[0], 'slpUtxos') + assert.isArray(result[0].bchUtxos) + assert.isArray(result[0].nullUtxos) + }) + + it('should handle an array of addresses', async () => { + const addr = [ + 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9', + 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9' + ] + + const result = await bchjs.Utxo.get(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.property(result[0], 'address') + assert.property(result[0], 'bchUtxos') + assert.property(result[0], 'nullUtxos') + assert.property(result[0], 'slpUtxos') + assert.isArray(result[0].bchUtxos) + assert.isArray(result[0].nullUtxos) + }) + + it('should handle NFTs and minting batons', async () => { + const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj' + + const result = await bchjs.Utxo.get(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.property(result[0], 'address') + assert.property(result[0], 'bchUtxos') + assert.property(result[0], 'nullUtxos') + assert.property(result[0], 'slpUtxos') + assert.isArray(result[0].bchUtxos) + assert.isArray(result[0].nullUtxos) + + assert.isArray(result[0].slpUtxos.type1.mintBatons) + assert.isArray(result[0].slpUtxos.type1.tokens) + assert.isArray(result[0].slpUtxos.nft.groupMintBatons) + assert.isArray(result[0].slpUtxos.nft.groupTokens) + assert.isArray(result[0].slpUtxos.nft.tokens) + }) + }) + + describe('#findBiggestUtxo', () => { + it('should sort UTXOs from Electrumx', async () => { + const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z' + + const electrumxUtxos = await bchjs.Electrumx.utxo(addr) + // console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`) + + const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, 'satoshis') + assert.equal(result.satoshis, 800) + }) + + it('should sort UTXOs from Utxos.get()', async () => { + const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z' + + const utxos = await bchjs.Utxo.get(addr) + // console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`) + + const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, 'satoshis') + assert.equal(result.satoshis, 800) + }) + }) +}) + +function sleep (ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +}