fix(tokenUtxoDetails2): Added with unit and integration tests. Uses new decodeOpReturn2()

This commit is contained in:
Chris Troutner
2020-05-28 10:11:05 -07:00
parent b528a69c58
commit e36d99ca66
3 changed files with 881 additions and 14 deletions
+212 -14
View File
@@ -635,6 +635,218 @@ describe(`#SLP`, () => {
})
})
describe("#tokenUtxoDetails2", () => {
// // 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 () => {
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 bchjs.SLP.Utils.tokenUtxoDetails2(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0], false, "Change UTXO marked as 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], "tokenType")
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
const utxos = [
{
txid:
"cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 600297,
confirmations: 76
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails2(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 () => {
const utxos = [
{
txid:
"fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails2(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 () => {
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 bchjs.SLP.Utils.tokenUtxoDetails2(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 2)
assert.equal(result[0], false)
assert.equal(result[1].isValid, true)
})
it("should handle problematic utxos", async () => {
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 bchjs.SLP.Utils.tokenUtxoDetails2(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 2)
assert.equal(result[0], false)
assert.equal(result[1].isValid, true)
})
it("should return false for BCH-only UTXOs", async () => {
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 data = await bchjs.SLP.Utils.tokenUtxoDetails2(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data)
assert.equal(false, data[0])
assert.equal(false, data[1])
})
})
describe("#balancesForAddress", () => {
it(`should throw an error if input is not a string or array of strings`, async () => {
try {
@@ -653,13 +865,6 @@ describe(`#SLP`, () => {
})
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
// Mock the call to rest.bitcoin.com
if (process.env.TEST === "unit") {
sandbox
.stub(axios, "get")
.resolves({ data: mockData.balancesForAddress })
}
const balances = await bchjs.SLP.Utils.balancesForAddress(
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9"
)
@@ -681,13 +886,6 @@ describe(`#SLP`, () => {
"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57"
]
// Mock the call to rest.bitcoin.com
if (process.env.TEST === "unit") {
sandbox
.stub(axios, "post")
.resolves({ data: mockData.balancesForAddresses })
}
const balances = await bchjs.SLP.Utils.balancesForAddress(addresses)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
+473
View File
@@ -1521,6 +1521,479 @@ describe("#SLP Utils", () => {
})
})
describe("#tokenUtxoDetails2", () => {
it("should throw error if input is not an array.", async () => {
try {
await slp.Utils.tokenUtxoDetails2("test")
assert2.equal(true, false, "Unexpected result.")
} catch (err) {
assert2.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 slp.Utils.tokenUtxoDetails2(utxos)
assert2.equal(true, false, "Unexpected result.")
} catch (err) {
assert2.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 slp.Utils.tokenUtxoDetails2(utxos)
assert2.equal(true, false, "Unexpected result.")
} catch (err) {
assert2.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(slp.Utils, "validateTxid").resolves([
{
txid:
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
valid: true
}
])
// Stub the calls to decodeOpReturn.
sandbox.stub(slp.Utils, "decodeOpReturn2").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 slp.Utils.tokenUtxoDetails2(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert2.equal(data[0], false, "Change UTXO marked as 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], "tokenType")
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)
})
it("should return details for a MINT token utxo", async () => {
// Mock the call to REST API
// Stub the calls to decodeOpReturn.
sandbox
.stub(slp.Utils, "decodeOpReturn2")
.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(slp.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 slp.Utils.tokenUtxoDetails2(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.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(slp.Utils, "decodeOpReturn2")
.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(slp.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 slp.Utils.tokenUtxoDetails2(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.equal(data[0].isValid, true)
})
it("should handle BCH and SLP utxos in the same TX", async () => {
// Mock external dependencies.
sandbox
.stub(slp.Utils, "validateTxid")
.resolves(mockData.mockDualValidation)
sandbox
.stub(slp.Utils, "decodeOpReturn2")
.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 slp.Utils.tokenUtxoDetails2(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.isArray(result)
assert2.equal(result.length, 2)
assert2.equal(result[0], false)
assert.equal(result[1].isValid, true)
})
it("should handle problematic utxos", async () => {
// Mock external dependencies.
// Stub the calls to decodeOpReturn.
sandbox
.stub(slp.Utils, "decodeOpReturn2")
.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(slp.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 slp.Utils.tokenUtxoDetails2(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.isArray(result)
assert2.equal(result.length, 2)
assert2.equal(result[0], false)
assert2.equal(result[1].isValid, true)
})
it("should return false for BCH-only UTXOs", async () => {
// Mock live network calls
if (process.env.TEST === "unit") {
sandbox
.stub(slp.Utils, "decodeOpReturn2")
.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 data = await slp.Utils.tokenUtxoDetails2(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert2.isArray(data)
assert2.equal(false, data[0])
assert2.equal(false, data[1])
})
})
describe("#txDetails", () => {
it("should throw an error if txid is not included", async () => {
try {