mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 09:02:01 -07:00
2180 lines
62 KiB
JavaScript
2180 lines
62 KiB
JavaScript
// Public npm libraries
|
|
const assert = require("chai").assert
|
|
const sinon = require("sinon")
|
|
const cloneDeep = require("lodash.clonedeep")
|
|
|
|
// Unit under test
|
|
const SLP = require("../../src/slp/slp")
|
|
let uut
|
|
let SERVER
|
|
|
|
const REST_URL = process.env.RESTURL
|
|
? process.env.RESTURL
|
|
: "https://bchn.fullstack.cash/v4/"
|
|
|
|
// Mock data used for unit tests
|
|
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"
|
|
|
|
describe("#SLP Utils", () => {
|
|
let sandbox
|
|
|
|
beforeEach(() => {
|
|
// Activate nock if it's inactive.
|
|
// if (!nock.isActive()) nock.activate()
|
|
|
|
mockData = cloneDeep(mockDataLib)
|
|
|
|
sandbox = sinon.createSandbox()
|
|
|
|
uut = new SLP({
|
|
restURL: REST_URL,
|
|
apiToken: process.env.BCHJSTOKEN
|
|
})
|
|
SERVER = uut.restURL
|
|
})
|
|
|
|
afterEach(() => {
|
|
// Clean up HTTP mocks.
|
|
// nock.cleanAll() // clear interceptor list.
|
|
// nock.restore()
|
|
|
|
sandbox.restore()
|
|
})
|
|
|
|
describe("#list", () => {
|
|
it(`should list single SLP token by id`, async () => {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.mockToken })
|
|
|
|
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", () => {
|
|
it(`should throw an error if input is not a string or array of strings`, async () => {
|
|
try {
|
|
const address = 1234
|
|
|
|
await uut.Utils.balancesForAddress(address)
|
|
|
|
assert.equal(true, false, "Uh oh. Code path should not end here.")
|
|
} catch (err) {
|
|
//console.log(`Error: `, err)
|
|
assert.include(
|
|
err.message,
|
|
`Input address must be a string or array of strings`
|
|
)
|
|
}
|
|
})
|
|
|
|
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
|
|
// Mock the call to the REST API
|
|
if (process.env.TEST === "unit") {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.balancesForAddress })
|
|
}
|
|
//sandbox
|
|
// .stub(uut.Utils, "balancesForAddress")
|
|
// .resolves(mockData.balancesForAddress)
|
|
|
|
const balances = await uut.Utils.balancesForAddress(
|
|
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9"
|
|
)
|
|
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
|
|
|
assert.isArray(balances)
|
|
assert.hasAllKeys(balances[0], [
|
|
"tokenId",
|
|
"balanceString",
|
|
"balance",
|
|
"decimalCount",
|
|
"slpAddress"
|
|
])
|
|
})
|
|
|
|
it(`should fetch balances for multiple addresses.`, async () => {
|
|
const addresses = [
|
|
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9",
|
|
"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57"
|
|
]
|
|
|
|
// Mock the call to the REST API
|
|
if (process.env.TEST === "unit") {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "post")
|
|
.resolves({ data: mockData.balancesForAddresses })
|
|
}
|
|
|
|
const balances = await uut.Utils.balancesForAddress(addresses)
|
|
//console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
|
|
|
assert.isArray(balances)
|
|
assert.isArray(balances[0])
|
|
assert.hasAllKeys(balances[0][0], [
|
|
"tokenId",
|
|
"balanceString",
|
|
"balance",
|
|
"decimalCount",
|
|
"slpAddress"
|
|
])
|
|
})
|
|
})
|
|
|
|
describe("#validateTxid", () => {
|
|
it(`should validate slp txid`, async () => {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "post")
|
|
.resolves({ data: mockData.mockIsValidTxid })
|
|
|
|
const isValid = await uut.Utils.validateTxid(
|
|
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
|
|
)
|
|
assert.deepEqual(isValid, [
|
|
{
|
|
txid:
|
|
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb",
|
|
valid: true
|
|
}
|
|
])
|
|
})
|
|
})
|
|
|
|
describe("#balancesForToken", () => {
|
|
it(`should retrieve token balances for a given tokenId`, async () => {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.mockBalancesForToken })
|
|
|
|
const balances = await uut.Utils.balancesForToken(
|
|
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
|
|
)
|
|
|
|
assert.hasAnyKeys(balances[0], ["tokenBalance", "slpAddress"])
|
|
})
|
|
})
|
|
|
|
describe("#tokenStats", () => {
|
|
it(`should retrieve stats for a given tokenId`, async () => {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.mockTokenStats })
|
|
|
|
const tokenStats = await uut.Utils.tokenStats(
|
|
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
|
|
)
|
|
|
|
assert.hasAnyKeys(tokenStats, [
|
|
"circulatingSupply",
|
|
"decimals",
|
|
"documentUri",
|
|
"name",
|
|
"satoshisLockedUp",
|
|
"symbol",
|
|
"tokenId",
|
|
"totalBurned",
|
|
"totalMinted",
|
|
"txnsSinceGenesis",
|
|
"validAddresses",
|
|
"validUtxos"
|
|
])
|
|
})
|
|
})
|
|
|
|
describe("#transactions", () => {
|
|
it(`should retrieve transactions for a given tokenId and address`, async () => {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.mockTransactions })
|
|
|
|
const transactions = await uut.Utils.transactions(
|
|
"495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a",
|
|
"simpleledger:qrhvcy5xlegs858fjqf8ssl6a4f7wpstaqnt0wauwu"
|
|
)
|
|
|
|
assert.hasAnyKeys(transactions[0], ["txid", "tokenDetails"])
|
|
})
|
|
})
|
|
|
|
describe("#burnTotal", () => {
|
|
it(`should retrieve input, output and burn totals`, async () => {
|
|
// Mock the call to the REST API
|
|
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)}`)
|
|
|
|
assert.hasAnyKeys(burnTotal, [
|
|
"transactionId",
|
|
"inputTotal",
|
|
"outputTotal",
|
|
"burnTotal"
|
|
])
|
|
})
|
|
})
|
|
|
|
describe("#decodeOpReturn", () => {
|
|
it("should throw an error for a non-string input", async () => {
|
|
try {
|
|
const txid = 53423 // Not a string.
|
|
|
|
await uut.Utils.decodeOpReturn(txid)
|
|
|
|
assert.equal(true, false, "Unexpected result.")
|
|
} catch (err) {
|
|
//console.log(`err: ${util.inspect(err)}`)
|
|
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
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn })
|
|
|
|
const txid =
|
|
"3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec"
|
|
|
|
await uut.Utils.decodeOpReturn(txid)
|
|
|
|
assert.equal(true, false, "Unexpected result.")
|
|
} catch (err) {
|
|
// console.log(`err: ${util.inspect(err)}`)
|
|
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
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.nonSLPTxDetailsWithOpReturn })
|
|
|
|
const txid =
|
|
"2ff74c48a5d657cf45f699601990bffbbe7a2a516d5480674cbf6c6a4497908f"
|
|
|
|
await uut.Utils.decodeOpReturn(txid)
|
|
|
|
assert.equal(true, false, "Unexpected result.")
|
|
} catch (err) {
|
|
// console.log(`err: ${util.inspect(err)}`)
|
|
assert.include(err.message, `SLP not in first chunk`)
|
|
}
|
|
})
|
|
|
|
it("should decode a genesis transaction", async () => {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.txDetailsSLPGenesis })
|
|
|
|
const txid =
|
|
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90"
|
|
|
|
const result = await uut.Utils.decodeOpReturn(txid)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.hasAllKeys(result, [
|
|
"tokenType",
|
|
"txType",
|
|
"tokenId",
|
|
"ticker",
|
|
"name",
|
|
"documentUri",
|
|
"documentHash",
|
|
"decimals",
|
|
"mintBatonVout",
|
|
"qty"
|
|
])
|
|
})
|
|
|
|
it("should decode a mint transaction", async () => {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.txDetailsSLPMint })
|
|
|
|
const txid =
|
|
"65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4"
|
|
|
|
const result = await uut.Utils.decodeOpReturn(txid)
|
|
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.hasAllKeys(result, [
|
|
"tokenType",
|
|
"txType",
|
|
"tokenId",
|
|
"mintBatonVout",
|
|
"qty"
|
|
])
|
|
})
|
|
|
|
it("should decode a send transaction", async () => {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.txDetailsSLPSend })
|
|
|
|
const txid =
|
|
"4f922565af664b6fdf0a1ba3924487344be721b3d8815c62cafc8a51e04a8afa"
|
|
|
|
const result = await uut.Utils.decodeOpReturn(txid)
|
|
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
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.
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.txDetailsSLPGenesisNoBaton })
|
|
|
|
const txid =
|
|
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
|
|
|
const data = await uut.Utils.decodeOpReturn(txid)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.equal(data.mintBatonVout, 0)
|
|
})
|
|
|
|
it("should decode a send transaction with alternate encoding", async () => {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.txDetailsSLPSendAlt })
|
|
|
|
const txid =
|
|
"d94357179775425ebc59c93173bd6dc9854095f090a2eb9dcfe9797398bc8eae"
|
|
|
|
const data = await uut.Utils.decodeOpReturn(txid)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.hasAnyKeys(data, [
|
|
"transactionType",
|
|
"txType",
|
|
"tokenId",
|
|
"amounts"
|
|
])
|
|
})
|
|
|
|
// Note: This TX is interpreted as valid by the original decodeOpReturn().
|
|
// Fixing this issue and related issues was the reason for creating the
|
|
// decodeOpReturn2() method using the slp-parser library.
|
|
it("should throw error for invalid SLP transaction", async () => {
|
|
try {
|
|
// Mock the call to the REST API
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.mockInvalidSlpSend })
|
|
|
|
const txid =
|
|
"a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415"
|
|
|
|
await uut.Utils.decodeOpReturn(txid)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
} catch (err) {
|
|
// console.log(`err: `, err)
|
|
assert.include(err.message, "amount string size not 8 bytes")
|
|
}
|
|
})
|
|
|
|
it("should decode a NFT Parent transaction", async () => {
|
|
// Mock the call to the REST API.
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.txDetailsSLPNftGenesis })
|
|
|
|
const txid =
|
|
"4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4"
|
|
|
|
const data = await uut.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, 129)
|
|
assert.equal(data.mintBatonVout, 2)
|
|
assert.equal(data.qty, 1)
|
|
})
|
|
|
|
// it("should decode a NFT Child transaction", async () => {
|
|
// // Mock the call to the REST API.
|
|
// // sandbox
|
|
// // .stub(uut.Utils.axios, "get")
|
|
// // .resolves({ data: mockData.txDetailsSLPNftGenesis })
|
|
//
|
|
// const txid =
|
|
// "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76"
|
|
//
|
|
// const data = await uut.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, 129)
|
|
// // assert.equal(data.mintBatonVout, 2)
|
|
// // assert.equal(data.qty, 1)
|
|
// })
|
|
})
|
|
|
|
describe("#tokenUtxoDetails", () => {
|
|
it("should throw error if input is not an array.", async () => {
|
|
try {
|
|
await uut.Utils.tokenUtxoDetails("test")
|
|
|
|
assert.equal(true, false, "Unexpected result.")
|
|
} catch (err) {
|
|
assert.include(
|
|
err.message,
|
|
`Input must be an array`,
|
|
"Expected error message."
|
|
)
|
|
}
|
|
})
|
|
|
|
it("should throw error if utxo does not have satoshis or value property.", async () => {
|
|
try {
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
|
vout: 3,
|
|
amount: 0.00002015,
|
|
satoshis: 2015,
|
|
height: 594892,
|
|
confirmations: 5
|
|
},
|
|
{
|
|
txid:
|
|
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
|
vout: 2,
|
|
amount: 0.00000546,
|
|
height: 594892,
|
|
confirmations: 5
|
|
}
|
|
]
|
|
|
|
await uut.Utils.tokenUtxoDetails(utxos)
|
|
|
|
assert.equal(true, false, "Unexpected result.")
|
|
} catch (err) {
|
|
assert.include(
|
|
err.message,
|
|
`utxo 1 does not have a satoshis or value property`,
|
|
"Expected error message."
|
|
)
|
|
}
|
|
})
|
|
|
|
it("should throw error if utxo does not have txid or tx_hash property.", async () => {
|
|
try {
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
|
vout: 3,
|
|
amount: 0.00002015,
|
|
satoshis: 2015,
|
|
height: 594892,
|
|
confirmations: 5
|
|
},
|
|
{
|
|
vout: 2,
|
|
amount: 0.00000546,
|
|
satoshis: 546,
|
|
height: 594892,
|
|
confirmations: 5
|
|
}
|
|
]
|
|
|
|
await uut.Utils.tokenUtxoDetails(utxos)
|
|
|
|
assert.equal(true, false, "Unexpected result.")
|
|
} catch (err) {
|
|
assert.include(
|
|
err.message,
|
|
`utxo 1 does not have a txid or tx_hash property`,
|
|
"Expected error message."
|
|
)
|
|
}
|
|
})
|
|
|
|
// // This captures an important corner-case. When an SLP token is created, the
|
|
// // change UTXO will contain the same SLP txid, but it is not an SLP UTXO.
|
|
it("should return details on minting baton from genesis transaction", async () => {
|
|
// Mock the call to REST API
|
|
// Stub the call to validateTxid
|
|
sandbox.stub(uut.Utils, "validateTxid").resolves([
|
|
{
|
|
txid:
|
|
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
|
valid: true
|
|
}
|
|
])
|
|
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox.stub(uut.Utils, "decodeOpReturn").resolves({
|
|
tokenType: 1,
|
|
txType: "GENESIS",
|
|
ticker: "SLPSDK",
|
|
name: "SLP SDK example using BITBOX",
|
|
tokenId:
|
|
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
|
documentUri: "developer.bitcoin.com",
|
|
documentHash: "",
|
|
decimals: 8,
|
|
mintBatonVout: 2,
|
|
qty: "50700000000"
|
|
})
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
|
vout: 3,
|
|
amount: 0.00002015,
|
|
satoshis: 2015,
|
|
height: 594892,
|
|
confirmations: 5
|
|
},
|
|
{
|
|
txid:
|
|
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
|
vout: 2,
|
|
amount: 0.00000546,
|
|
satoshis: 546,
|
|
height: 594892,
|
|
confirmations: 5
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
// 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)
|
|
|
|
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 () => {
|
|
// Mock the call to REST API
|
|
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.onCall(0)
|
|
.resolves({
|
|
tokenType: 1,
|
|
txType: "MINT",
|
|
tokenId:
|
|
"38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0",
|
|
mintBatonVout: 2,
|
|
qty: "1000000000000"
|
|
})
|
|
.onCall(1)
|
|
.resolves({
|
|
tokenType: 1,
|
|
txType: "GENESIS",
|
|
ticker: "PSF",
|
|
name: "Permissionless Software Foundation",
|
|
tokenId:
|
|
"38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0",
|
|
documentUri: "psfoundation.cash",
|
|
documentHash: "",
|
|
decimals: 8,
|
|
mintBatonVout: 2,
|
|
qty: "1988209163133"
|
|
})
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox.stub(uut.Utils, "validateTxid").resolves([
|
|
{
|
|
txid:
|
|
"cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76",
|
|
valid: true
|
|
}
|
|
])
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76",
|
|
vout: 1,
|
|
amount: 0.00000546,
|
|
satoshis: 546,
|
|
height: 600297,
|
|
confirmations: 76
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
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)
|
|
})
|
|
|
|
it("should return details for a simple SEND SLP token utxo", 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"
|
|
})
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox.stub(uut.Utils, "validateTxid").resolves([
|
|
{
|
|
txid:
|
|
"fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb",
|
|
valid: true
|
|
}
|
|
])
|
|
|
|
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)}`)
|
|
|
|
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)
|
|
})
|
|
|
|
it("should handle BCH and SLP utxos in the same TX", async () => {
|
|
// Mock external dependencies.
|
|
sandbox
|
|
.stub(uut.Utils, "validateTxid")
|
|
.resolves(mockData.mockDualValidation)
|
|
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.onCall(0)
|
|
.resolves({
|
|
tokenType: 1,
|
|
txType: "SEND",
|
|
tokenId:
|
|
"dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d",
|
|
amounts: ["1", "5"]
|
|
})
|
|
.onCall(1)
|
|
.resolves({
|
|
tokenType: 1,
|
|
txType: "SEND",
|
|
tokenId:
|
|
"dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d",
|
|
amounts: ["1", "5"]
|
|
})
|
|
.onCall(2)
|
|
.resolves({
|
|
tokenType: 1,
|
|
txType: "GENESIS",
|
|
ticker: "TAP",
|
|
name: "Thoughts and Prayers",
|
|
tokenId:
|
|
"dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d",
|
|
documentUri: "",
|
|
documentHash: "",
|
|
decimals: 0,
|
|
mintBatonVout: 2,
|
|
qty: "1000000"
|
|
})
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
|
vout: 3,
|
|
value: "6816",
|
|
height: 606848,
|
|
confirmations: 13,
|
|
satoshis: 6816
|
|
},
|
|
{
|
|
txid:
|
|
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
|
vout: 2,
|
|
value: "546",
|
|
height: 606848,
|
|
confirmations: 13,
|
|
satoshis: 546
|
|
}
|
|
]
|
|
|
|
const result = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.isArray(result)
|
|
assert.equal(result.length, 2)
|
|
|
|
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")
|
|
assert.equal(result[1].transactionType, "send")
|
|
})
|
|
|
|
it("should handle problematic utxos", async () => {
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.onCall(0)
|
|
.throws({ message: "scriptpubkey not op_return" })
|
|
.onCall(1)
|
|
.resolves({
|
|
tokenType: 1,
|
|
txType: "SEND",
|
|
tokenId:
|
|
"f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f",
|
|
amounts: ["5000000", "395010942"]
|
|
})
|
|
.onCall(2)
|
|
.resolves({
|
|
tokenType: 1,
|
|
txType: "GENESIS",
|
|
ticker: "AUDC",
|
|
name: "AUD Coin",
|
|
tokenId:
|
|
"f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f",
|
|
documentUri: "audcoino@gmail.com",
|
|
documentHash: "",
|
|
decimals: 6,
|
|
mintBatonVout: 0,
|
|
qty: "2000000000000000000"
|
|
})
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox.stub(uut.Utils, "validateTxid").resolves([
|
|
{
|
|
txid:
|
|
"67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19",
|
|
valid: true
|
|
}
|
|
])
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"0e3a217fc22612002031d317b4cecd9b692b66b52951a67b23c43041aefa3959",
|
|
vout: 0,
|
|
amount: 0.00018362,
|
|
satoshis: 18362,
|
|
height: 613483,
|
|
confirmations: 124
|
|
},
|
|
{
|
|
txid:
|
|
"67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19",
|
|
vout: 1,
|
|
amount: 0.00000546,
|
|
satoshis: 546,
|
|
height: 612075,
|
|
confirmations: 1532
|
|
}
|
|
]
|
|
|
|
const result = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.isArray(result)
|
|
assert.equal(result.length, 2)
|
|
|
|
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)
|
|
|
|
assert.equal(result[1].isValid, true)
|
|
assert.equal(result[1].utxoType, "token")
|
|
assert.equal(result[1].transactionType, "send")
|
|
})
|
|
|
|
it("should return isValid=false for BCH-only UTXOs", async () => {
|
|
// Mock live network calls
|
|
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.throws(new Error("scriptpubkey not op_return"))
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"a937f792c7c9eb23b4f344ce5c233d1ac0909217d0a504d71e6b1e4efb864a3b",
|
|
vout: 0,
|
|
amount: 0.00001,
|
|
satoshis: 1000,
|
|
confirmations: 0,
|
|
ts: 1578424704
|
|
},
|
|
{
|
|
txid:
|
|
"53fd141c2e999e080a5860887441a2c45e9cbe262027e2bd2ac998fc76e43c44",
|
|
vout: 0,
|
|
amount: 0.00001,
|
|
satoshis: 1000,
|
|
confirmations: 0,
|
|
ts: 1578424634
|
|
}
|
|
]
|
|
|
|
const result = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.isArray(result)
|
|
|
|
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)
|
|
|
|
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 () => {
|
|
const slpData = {
|
|
tokenType: 1,
|
|
txType: "GENESIS",
|
|
ticker: "SLPTEST",
|
|
name: "SLP Test Token",
|
|
tokenId:
|
|
"d2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954",
|
|
documentUri: "https://FullStack.cash",
|
|
documentHash: "",
|
|
decimals: 8,
|
|
mintBatonVout: 2,
|
|
qty: "10000000000"
|
|
}
|
|
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox.stub(uut.Utils, "decodeOpReturn").resolves(slpData)
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox.stub(uut.Utils, "validateTxid").resolves([
|
|
{
|
|
txid:
|
|
"d2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954",
|
|
valid: true
|
|
}
|
|
])
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"d2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954",
|
|
vout: 1,
|
|
value: "546",
|
|
confirmations: 0,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"d2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954",
|
|
vout: 2,
|
|
value: "546",
|
|
confirmations: 0,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"d2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954",
|
|
vout: 3,
|
|
value: "12178",
|
|
confirmations: 0,
|
|
satoshis: 12178
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.isArray(data)
|
|
|
|
assert.equal(data[0].utxoType, "token")
|
|
assert.equal(data[0].tokenQty, 100)
|
|
assert.equal(data[0].isValid, true)
|
|
assert.equal(data[0].tokenType, 1)
|
|
|
|
assert.equal(data[1].utxoType, "minting-baton")
|
|
assert.equal(data[1].isValid, true)
|
|
assert.equal(data[1].tokenType, 1)
|
|
|
|
assert.equal(data[2].isValid, false)
|
|
})
|
|
|
|
it("should decode a Mint transaction", async () => {
|
|
// Define stubbed data.
|
|
const slpData = {
|
|
tokenType: 1,
|
|
txType: "MINT",
|
|
tokenId:
|
|
"9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136",
|
|
mintBatonVout: 2,
|
|
qty: "10000000000"
|
|
}
|
|
|
|
const genesisData = {
|
|
tokenType: 1,
|
|
txType: "GENESIS",
|
|
ticker: "SLPTEST",
|
|
name: "SLP Test Token",
|
|
tokenId:
|
|
"9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136",
|
|
documentUri: "https://FullStack.cash",
|
|
documentHash: "",
|
|
decimals: 8,
|
|
mintBatonVout: 2,
|
|
qty: "10000000000"
|
|
}
|
|
|
|
const stubValid = [
|
|
{
|
|
txid:
|
|
"880587f01e3112e779c0fdf1b9b859c242a28e56ead85483eeedcaa52f051a04",
|
|
valid: true
|
|
}
|
|
]
|
|
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.resolves(slpData)
|
|
.onCall(1)
|
|
.resolves(genesisData)
|
|
.onCall(2)
|
|
.resolves(slpData)
|
|
.onCall(3)
|
|
.resolves(genesisData)
|
|
.onCall(4)
|
|
.resolves(slpData)
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox
|
|
.stub(uut.Utils, "validateTxid")
|
|
.resolves(stubValid)
|
|
.onCall(1)
|
|
.resolves(stubValid)
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"880587f01e3112e779c0fdf1b9b859c242a28e56ead85483eeedcaa52f051a04",
|
|
vout: 1,
|
|
value: "546",
|
|
confirmations: 0,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"880587f01e3112e779c0fdf1b9b859c242a28e56ead85483eeedcaa52f051a04",
|
|
vout: 2,
|
|
value: "546",
|
|
confirmations: 0,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"880587f01e3112e779c0fdf1b9b859c242a28e56ead85483eeedcaa52f051a04",
|
|
vout: 3,
|
|
value: "10552",
|
|
confirmations: 0,
|
|
satoshis: 10552
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.isArray(data)
|
|
|
|
assert.equal(data[0].utxoType, "token")
|
|
assert.equal(data[0].tokenQty, 100)
|
|
assert.equal(data[0].isValid, true)
|
|
assert.equal(data[0].tokenType, 1)
|
|
|
|
assert.equal(data[1].utxoType, "minting-baton")
|
|
assert.equal(data[1].isValid, true)
|
|
assert.equal(data[1].tokenType, 1)
|
|
|
|
assert.equal(data[2].isValid, false)
|
|
})
|
|
|
|
it("should decode a NFT Group Genesis transaction", async () => {
|
|
// Define stubbed data.
|
|
const slpData = {
|
|
tokenType: 129,
|
|
txType: "GENESIS",
|
|
ticker: "NFTTT",
|
|
name: "NFT Test Token",
|
|
tokenId:
|
|
"4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4",
|
|
documentUri: "https://FullStack.cash",
|
|
documentHash: "",
|
|
decimals: 0,
|
|
mintBatonVout: 2,
|
|
qty: "1"
|
|
}
|
|
|
|
const stubValid = [
|
|
{
|
|
txid:
|
|
"4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4",
|
|
valid: true
|
|
}
|
|
]
|
|
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.resolves(slpData)
|
|
.onCall(1)
|
|
.resolves(slpData)
|
|
.onCall(2)
|
|
.resolves(slpData)
|
|
.onCall(3)
|
|
.resolves(slpData)
|
|
.onCall(4)
|
|
.resolves(slpData)
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox
|
|
.stub(uut.Utils, "validateTxid")
|
|
.resolves(stubValid)
|
|
.onCall(1)
|
|
.resolves(stubValid)
|
|
.onCall(2)
|
|
.resolves(stubValid)
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4",
|
|
vout: 3,
|
|
value: "15620",
|
|
height: 638207,
|
|
confirmations: 3,
|
|
satoshis: 15620
|
|
},
|
|
{
|
|
txid:
|
|
"4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4",
|
|
vout: 2,
|
|
value: "546",
|
|
height: 638207,
|
|
confirmations: 3,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4",
|
|
vout: 1,
|
|
value: "546",
|
|
height: 638207,
|
|
confirmations: 3,
|
|
satoshis: 546
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.isArray(data)
|
|
|
|
assert.equal(data[0].isValid, false)
|
|
|
|
assert.equal(data[1].utxoType, "minting-baton")
|
|
assert.equal(data[1].isValid, true)
|
|
assert.equal(data[1].tokenType, 129)
|
|
|
|
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 () => {
|
|
// Define stubbed data.
|
|
const slpData = {
|
|
tokenType: 129,
|
|
txType: "MINT",
|
|
tokenId:
|
|
"eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981",
|
|
mintBatonVout: 2,
|
|
qty: "10"
|
|
}
|
|
|
|
const genesisData = {
|
|
tokenType: 129,
|
|
txType: "GENESIS",
|
|
ticker: "NFTTT",
|
|
name: "NFT Test Token",
|
|
tokenId:
|
|
"eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981",
|
|
documentUri: "https://FullStack.cash",
|
|
documentHash: "",
|
|
decimals: 0,
|
|
mintBatonVout: 2,
|
|
qty: "1"
|
|
}
|
|
|
|
const stubValid = [
|
|
{
|
|
txid:
|
|
"35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919",
|
|
valid: true
|
|
}
|
|
]
|
|
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.resolves(slpData)
|
|
.onCall(1)
|
|
.resolves(slpData)
|
|
.onCall(2)
|
|
.resolves(genesisData)
|
|
.onCall(3)
|
|
.resolves(slpData)
|
|
.onCall(4)
|
|
.resolves(genesisData)
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox
|
|
.stub(uut.Utils, "validateTxid")
|
|
.resolves(stubValid)
|
|
.onCall(1)
|
|
.resolves(stubValid)
|
|
.onCall(2)
|
|
.resolves(stubValid)
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919",
|
|
vout: 3,
|
|
value: "15620",
|
|
height: 638207,
|
|
confirmations: 3,
|
|
satoshis: 15620
|
|
},
|
|
{
|
|
txid:
|
|
"35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919",
|
|
vout: 2,
|
|
value: "546",
|
|
height: 638207,
|
|
confirmations: 3,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919",
|
|
vout: 1,
|
|
value: "546",
|
|
height: 638207,
|
|
confirmations: 3,
|
|
satoshis: 546
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.isArray(data)
|
|
|
|
assert.equal(data[0].isValid, false)
|
|
|
|
assert.equal(data[1].utxoType, "minting-baton")
|
|
assert.equal(data[1].tokenType, 129)
|
|
assert.equal(data[1].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 () => {
|
|
// Define stubbed data.
|
|
const slpData = {
|
|
tokenType: 65,
|
|
txType: "GENESIS",
|
|
ticker: "NFTC",
|
|
name: "NFT Child",
|
|
tokenId:
|
|
"9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683",
|
|
documentUri: "https://FullStack.cash",
|
|
documentHash: "",
|
|
decimals: 0,
|
|
mintBatonVout: 0,
|
|
qty: "1"
|
|
}
|
|
|
|
const stubValid = [
|
|
{
|
|
txid:
|
|
"9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683",
|
|
valid: true
|
|
}
|
|
]
|
|
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.resolves(slpData)
|
|
.onCall(1)
|
|
.resolves(slpData)
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox.stub(uut.Utils, "validateTxid").resolves(stubValid)
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683",
|
|
vout: 1,
|
|
value: "546",
|
|
confirmations: 0,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683",
|
|
vout: 2,
|
|
value: "13478",
|
|
confirmations: 0,
|
|
satoshis: 13478
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.isArray(data)
|
|
|
|
assert.equal(data[0].utxoType, "token")
|
|
assert.equal(data[0].tokenType, 65)
|
|
assert.equal(data[0].isValid, true)
|
|
|
|
assert.equal(data[1].isValid, false)
|
|
})
|
|
|
|
it("should decode an NFT Child Send transaction", async () => {
|
|
// Define stubbed data.
|
|
const slpData = {
|
|
tokenType: 65,
|
|
txType: "SEND",
|
|
tokenId:
|
|
"9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683",
|
|
amounts: ["1"]
|
|
}
|
|
|
|
const genesisData = {
|
|
tokenType: 65,
|
|
txType: "GENESIS",
|
|
ticker: "NFTC",
|
|
name: "NFT Child",
|
|
tokenId:
|
|
"9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683",
|
|
documentUri: "https://FullStack.cash",
|
|
documentHash: "",
|
|
decimals: 0,
|
|
mintBatonVout: 0,
|
|
qty: "1"
|
|
}
|
|
|
|
const stubValid = [
|
|
{
|
|
txid:
|
|
"6d68a7ffbb63ef851c43025f801a1d365cddda50b00741bca022c743d74cd61a",
|
|
valid: true
|
|
}
|
|
]
|
|
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.resolves(slpData)
|
|
.onCall(1)
|
|
.resolves(genesisData)
|
|
.onCall(2)
|
|
.resolves(slpData)
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox.stub(uut.Utils, "validateTxid").resolves(stubValid)
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"6d68a7ffbb63ef851c43025f801a1d365cddda50b00741bca022c743d74cd61a",
|
|
vout: 1,
|
|
value: "546",
|
|
confirmations: 0,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"6d68a7ffbb63ef851c43025f801a1d365cddda50b00741bca022c743d74cd61a",
|
|
vout: 2,
|
|
value: "12136",
|
|
confirmations: 0,
|
|
satoshis: 12136
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.isArray(data)
|
|
|
|
assert.equal(data[0].utxoType, "token")
|
|
assert.equal(data[0].transactionType, "send")
|
|
assert.equal(data[0].tokenType, 65)
|
|
assert.equal(data[0].isValid, true)
|
|
|
|
assert.equal(data[1].isValid, false)
|
|
})
|
|
|
|
it("should decode an NFT Group Send transaction", async () => {
|
|
// Define stubbed data.
|
|
const slpData = {
|
|
tokenType: 129,
|
|
txType: "SEND",
|
|
tokenId:
|
|
"eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981",
|
|
amounts: ["1", "8"]
|
|
}
|
|
|
|
const genesisData = {
|
|
tokenType: 129,
|
|
txType: "GENESIS",
|
|
ticker: "NFTTT",
|
|
name: "NFT Test Token",
|
|
tokenId:
|
|
"eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981",
|
|
documentUri: "https://FullStack.cash",
|
|
documentHash: "",
|
|
decimals: 0,
|
|
mintBatonVout: 2,
|
|
qty: "1"
|
|
}
|
|
|
|
const stubValid = [
|
|
{
|
|
txid:
|
|
"57cc47c265ce878679e95e2cec510d8a1a9840f5c62feb4743cc5947d57d9766",
|
|
valid: true
|
|
}
|
|
]
|
|
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.resolves(slpData)
|
|
.onCall(1)
|
|
.resolves(genesisData)
|
|
.onCall(2)
|
|
.resolves(slpData)
|
|
.onCall(3)
|
|
.resolves(genesisData)
|
|
.onCall(4)
|
|
.resolves(slpData)
|
|
|
|
// Stub the call to validateTxid
|
|
sandbox
|
|
.stub(uut.Utils, "validateTxid")
|
|
.resolves(stubValid)
|
|
.onCall(1)
|
|
.resolves(stubValid)
|
|
|
|
const utxos = [
|
|
{
|
|
txid:
|
|
"57cc47c265ce878679e95e2cec510d8a1a9840f5c62feb4743cc5947d57d9766",
|
|
vout: 1,
|
|
value: "546",
|
|
confirmations: 0,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"57cc47c265ce878679e95e2cec510d8a1a9840f5c62feb4743cc5947d57d9766",
|
|
vout: 2,
|
|
value: "546",
|
|
confirmations: 0,
|
|
satoshis: 546
|
|
},
|
|
{
|
|
txid:
|
|
"57cc47c265ce878679e95e2cec510d8a1a9840f5c62feb4743cc5947d57d9766",
|
|
vout: 3,
|
|
value: "10794",
|
|
confirmations: 0,
|
|
satoshis: 10794
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
assert.isArray(data)
|
|
|
|
assert.equal(data[0].utxoType, "token")
|
|
assert.equal(data[0].transactionType, "send")
|
|
assert.equal(data[0].tokenType, 129)
|
|
assert.equal(data[0].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)
|
|
|
|
assert.equal(data[2].isValid, false)
|
|
})
|
|
|
|
it("should return null value when 429 recieved", async () => {
|
|
const utxos = [
|
|
{
|
|
height: 654522,
|
|
tx_hash:
|
|
"072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
|
|
tx_pos: 0,
|
|
value: 6999,
|
|
satoshis: 6999,
|
|
txid:
|
|
"072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
|
|
vout: 0
|
|
},
|
|
{
|
|
height: 654522,
|
|
tx_hash:
|
|
"a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
|
|
tx_pos: 1,
|
|
value: 546,
|
|
satoshis: 546,
|
|
txid:
|
|
"a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
|
|
vout: 1
|
|
}
|
|
]
|
|
|
|
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 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
|
|
// due to a throttling issue.
|
|
assert.equal(data[0].isValid, null)
|
|
assert.equal(data[1].isValid, null)
|
|
})
|
|
|
|
// it("should handle a dust attack", async () => {
|
|
it("should handle dust attack UTXOs", async () => {
|
|
// Mock external dependencies.
|
|
// Stub the calls to decodeOpReturn.
|
|
sandbox
|
|
.stub(uut.Utils, "decodeOpReturn")
|
|
.rejects(new Error("lokad id wrong size"))
|
|
|
|
const utxos = [
|
|
{
|
|
height: 655965,
|
|
tx_hash:
|
|
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
|
|
tx_pos: 151,
|
|
value: 547,
|
|
satoshis: 547,
|
|
txid:
|
|
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
|
|
vout: 151,
|
|
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
|
|
hdIndex: 11
|
|
}
|
|
]
|
|
|
|
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
|
|
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 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)
|
|
})
|
|
|
|
it("should handle SLPDB returning null corner case", 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' corner case.
|
|
sandbox.stub(uut.Utils, "validateTxid").resolves([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", () => {
|
|
it("should throw an error if txid is not included", async () => {
|
|
try {
|
|
await uut.Utils.txDetails()
|
|
} catch (err) {
|
|
assert.include(
|
|
err.message,
|
|
`txid string must be included`,
|
|
"Expected error message."
|
|
)
|
|
}
|
|
})
|
|
|
|
it("should throw error for non-existent txid", async () => {
|
|
try {
|
|
// Mock the call to the REST API
|
|
if (process.env.TEST === "unit") {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
//.resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn })
|
|
.throws({ error: `TXID not found` })
|
|
}
|
|
|
|
const txid = `d284e71227ec89f714b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e`
|
|
|
|
await uut.Utils.txDetails(txid)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.fail("Unexpected result")
|
|
} catch (err) {
|
|
// console.log(`err: `, err)
|
|
assert.include(err.error, `TXID not found`, "Expected error message.")
|
|
}
|
|
})
|
|
|
|
it("should return details for an SLP txid", async () => {
|
|
// Mock the call to the REST API
|
|
if (process.env.TEST === "unit") {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.mockTxDetails })
|
|
}
|
|
|
|
const txid = `9dbaaafc48c49a21beabada8de632009288a2cd52eecefd0c00edcffca9955d0`
|
|
|
|
const result = await uut.Utils.txDetails(txid)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.hasAnyKeys(result, [
|
|
"txid",
|
|
"version",
|
|
"locktime",
|
|
"vin",
|
|
"vout",
|
|
"blockhash",
|
|
"blockheight",
|
|
"confirmations",
|
|
"time",
|
|
"blocktime",
|
|
"valueOut",
|
|
"size",
|
|
"valueIn",
|
|
"fees",
|
|
"tokenInfo",
|
|
"tokenIsValid"
|
|
])
|
|
})
|
|
})
|
|
|
|
describe("#hydrateUtxos", () => {
|
|
it("should throw an error if input is not an array", async () => {
|
|
try {
|
|
const utxos = 1234
|
|
|
|
await uut.Utils.hydrateUtxos(utxos)
|
|
|
|
assert.equal(true, false, "Uh oh. Code path should not end here.")
|
|
} catch (err) {
|
|
// console.log(`Error: `, err)
|
|
assert.include(err.message, `Input must be an array.`)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#validateTxid2", () => {
|
|
it("should throw an error if the input is an array", async () => {
|
|
try {
|
|
const txid = [
|
|
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
|
|
]
|
|
|
|
await uut.Utils.validateTxid2(txid)
|
|
|
|
assert.equal(true, false, "Unexpected result")
|
|
} catch (err) {
|
|
// console.log("err: ", err)
|
|
assert.include(err.message, "txid must be 64 character string")
|
|
}
|
|
})
|
|
|
|
it("should throw an error for a malformed txid", async () => {
|
|
try {
|
|
const txid =
|
|
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783"
|
|
|
|
await uut.Utils.validateTxid2(txid)
|
|
|
|
assert.equal(true, false, "Unexpected result")
|
|
} catch (err) {
|
|
// console.log("err: ", err)
|
|
assert.include(err.message, "txid must be 64 character string")
|
|
}
|
|
})
|
|
|
|
it("should invalidate a known invalid TXID", async () => {
|
|
const txid =
|
|
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
|
|
|
|
// Mock live network calls.
|
|
sandbox.stub(uut.Utils.axios, "get").resolves({
|
|
data: {
|
|
txid: txid,
|
|
isValid: false,
|
|
msg: ""
|
|
}
|
|
})
|
|
|
|
const result = await uut.Utils.validateTxid2(txid)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.property(result, "txid")
|
|
assert.equal(result.txid, txid)
|
|
|
|
assert.property(result, "isValid")
|
|
assert.equal(result.isValid, false)
|
|
})
|
|
|
|
it("should validate a known valid TXID", async () => {
|
|
const txid =
|
|
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
|
|
|
|
// Mock live network calls.
|
|
sandbox.stub(uut.Utils.axios, "get").resolves({
|
|
data: {
|
|
txid: txid,
|
|
isValid: true,
|
|
msg: ""
|
|
}
|
|
})
|
|
|
|
const result = await uut.Utils.validateTxid2(txid)
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
assert.property(result, "txid")
|
|
assert.equal(result.txid, txid)
|
|
|
|
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
|
|
// return is less than 10 seconds. This test case handle this situation.
|
|
it("should handle timeout errors", async () => {
|
|
try {
|
|
const txid =
|
|
"eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579"
|
|
|
|
// Mock live network calls.
|
|
sandbox.stub(uut.Utils.axios, "get").rejects({
|
|
error:
|
|
"Network error: Could not communicate with full node or other external service."
|
|
})
|
|
|
|
await uut.Utils.validateTxid2(txid)
|
|
|
|
assert.equal(true, false, "Unexpected result")
|
|
} catch (err) {
|
|
// console.log("err: ", err)
|
|
assert.include(err.message, "slp-validate timed out")
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#validateTxid", () => {
|
|
it("should invalidate a known invalid TXID", async () => {
|
|
const txid =
|
|
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
|
|
|
|
// Mock live network calls.
|
|
sandbox.stub(uut.Utils.axios, "post").resolves({
|
|
data: mockData.mockValidateTxid3Invalid
|
|
})
|
|
|
|
const result = await uut.Utils.validateTxid(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 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"
|
|
]
|
|
|
|
const result = await uut.Utils.validateTxid(txids)
|
|
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
})
|
|
*/
|
|
})
|
|
|
|
describe("#getWhitelist", () => {
|
|
it("should return the list", async () => {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.mockWhitelist })
|
|
|
|
const result = await uut.Utils.getWhitelist()
|
|
|
|
assert.isArray(result)
|
|
assert.property(result[0], "name")
|
|
assert.property(result[1], "tokenId")
|
|
})
|
|
|
|
it("catches and throws an error", async () => {
|
|
try {
|
|
sandbox.stub(uut.Utils.axios, "get").rejects({
|
|
error:
|
|
"Network error: Could not communicate with full node or other external service."
|
|
})
|
|
|
|
await uut.Utils.getWhitelist()
|
|
|
|
assert.fail("Unexpected result")
|
|
} catch (err) {
|
|
console.log("err: ", err)
|
|
assert.include(err.error, "Network error")
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#validateTxid3", () => {
|
|
it("should invalidate a known invalid TXID", async () => {
|
|
const txid =
|
|
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
|
|
|
|
// Mock live network calls.
|
|
sandbox.stub(uut.Utils.axios, "post").resolves({
|
|
data: mockData.mockValidateTxid3Invalid
|
|
})
|
|
|
|
const result = await uut.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 handle an array with a single element", async () => {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "post")
|
|
.resolves({ data: mockData.mockValidateTxid3Valid })
|
|
|
|
const txid = [
|
|
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd"
|
|
]
|
|
|
|
const result = await uut.Utils.validateTxid3(txid)
|
|
|
|
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 an single string input", async () => {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "post")
|
|
.resolves({ data: mockData.mockValidateTxid3Valid })
|
|
|
|
const txid =
|
|
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd"
|
|
|
|
const result = await uut.Utils.validateTxid3(txid)
|
|
|
|
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("catches and throws an error", async () => {
|
|
try {
|
|
sandbox.stub(uut.Utils.axios, "post").rejects({
|
|
error:
|
|
"Network error: Could not communicate with full node or other external service."
|
|
})
|
|
|
|
await uut.Utils.validateTxid3()
|
|
|
|
assert.equal(true, false, "Unexpected result")
|
|
} catch (err) {
|
|
// console.log("err: ", err)
|
|
assert.include(err.error, "Network error")
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#getStatus", () => {
|
|
it("should return the current block height of the SLPDB indexer", async () => {
|
|
sandbox
|
|
.stub(uut.Utils.axios, "get")
|
|
.resolves({ data: mockData.slpdbStatus })
|
|
|
|
const result = await uut.Utils.getStatus()
|
|
|
|
// console.log(`result: `, result)
|
|
|
|
assert.property(result, "bchBlockHeight")
|
|
assert.property(result, "slpProcessedBlockHeight")
|
|
})
|
|
})
|
|
})
|