mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 00:52:01 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37109cd7ef | ||
|
|
83d523260d | ||
|
|
8768a810c7 | ||
|
|
8832bfc8df | ||
|
|
a5208110b9 | ||
|
|
99ad7cd87f | ||
|
|
68ae084fb0 | ||
|
|
b0b0e17abc | ||
|
|
a54306e382 | ||
|
|
1860232fa6 | ||
|
|
d8437cf3fb |
+2
-2
@@ -16,8 +16,8 @@
|
||||
"test:integration:local": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration",
|
||||
"test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
||||
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet",
|
||||
"test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#sort0ConfTxs' test/integration/",
|
||||
"test:temp2": "mocha --timeout=30000 -g '#sortConfTxs' test/unit/",
|
||||
"test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#getTxDataSlp' test/integration/",
|
||||
"test:temp2": "mocha --timeout=30000 -g '#getTxData' 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",
|
||||
|
||||
+4
-4
@@ -651,8 +651,8 @@ class ElectrumX {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Electrumx.sort0ConfTxs() sort0ConfTxs()
|
||||
* @apiName ElectrumX sort0ConfTxs
|
||||
* @api Electrumx.sortAllTxs() sortAllTxs()
|
||||
* @apiName ElectrumX sortAllTxs
|
||||
* @apiGroup ElectrumX
|
||||
* @apiDescription Sort the output of Electrum.transactions() by block height.
|
||||
*
|
||||
@@ -666,7 +666,7 @@ class ElectrumX {
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* const txs = await bchjs.Electrumx.transactions('bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v')
|
||||
* const sortedTxs = await bchjs.Electrumx.sort0ConfTxs(txs.transactions, 'ASCENDING')
|
||||
* const sortedTxs = await bchjs.Electrumx.sortAllTxs(txs.transactions, 'ASCENDING')
|
||||
* console.log(sortedTxs)
|
||||
* })()
|
||||
*
|
||||
@@ -682,7 +682,7 @@ class ElectrumX {
|
||||
* // ]
|
||||
*/
|
||||
// Substitute zero-conf txs with the current block-height + 1
|
||||
async sort0ConfTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
async sortAllTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// Calculate the height of the next block
|
||||
const nextBlock = (await this.blockchain.getBlockCount()) + 1
|
||||
|
||||
+22
-22
@@ -19,7 +19,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* bchjs.HDNode.fromSeed(seedBuffer);
|
||||
*
|
||||
@@ -28,7 +28,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* bchjs.HDNode.fromSeed(seedBuffer);
|
||||
*/
|
||||
@@ -54,7 +54,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to legacy address
|
||||
@@ -66,7 +66,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
@@ -88,7 +88,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
@@ -100,7 +100,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
@@ -121,7 +121,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.SLP.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
@@ -133,7 +133,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.SLP.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
@@ -156,7 +156,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to WIF
|
||||
@@ -168,7 +168,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to WIF
|
||||
@@ -190,7 +190,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to extended public key
|
||||
@@ -202,7 +202,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to extended public key
|
||||
@@ -224,7 +224,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to extended private key
|
||||
@@ -236,7 +236,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to extended private key
|
||||
@@ -258,7 +258,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create root seed buffer from mnemonic
|
||||
* let rootSeed= bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let rootSeed= await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from root seed
|
||||
* let hdNode = bchjs.HDNode.fromSeed(rootSeed);
|
||||
* // create public key buffer from HDNode
|
||||
@@ -269,7 +269,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // create public key buffer from HDNode
|
||||
@@ -290,7 +290,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create root seed buffer from mnemonic
|
||||
* let rootSeed= bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let rootSeed= await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from root seed
|
||||
* let hdNode = bchjs.HDNode.fromSeed(rootSeed);
|
||||
* // create public key buffer from HDNode
|
||||
@@ -302,7 +302,7 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // create public key buffer from HDNode
|
||||
@@ -370,7 +370,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // derive hardened child HDNode
|
||||
@@ -391,7 +391,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // derive unhardened child HDNode
|
||||
@@ -412,7 +412,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // derive hardened child HDNode
|
||||
@@ -615,7 +615,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create root seed buffer
|
||||
* let rootSeedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create master hd node
|
||||
* let masterHDNode = bchjs.HDNode.fromSeed(rootSeedBuffer);
|
||||
* // derive child node
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const axios = require('axios')
|
||||
|
||||
const SlpUtils = require('./slp/utils')
|
||||
|
||||
let _this
|
||||
|
||||
class RawTransactions {
|
||||
@@ -27,6 +29,9 @@ class RawTransactions {
|
||||
// Encapsulate dependencies
|
||||
this.axios = axios
|
||||
|
||||
// Dependencies
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
@@ -289,6 +294,180 @@ class RawTransactions {
|
||||
}
|
||||
}
|
||||
|
||||
// Given verbose transaction details, this function retrieves the transaction
|
||||
// data for the inputs (the parent transactions). It returns an array of
|
||||
// objects. Each object corresponds to a transaction input, and contains
|
||||
// the address that generated that input UTXO.
|
||||
//
|
||||
// Assumes a single TX. Does not yet work with an array of TXs.
|
||||
// This function returns an array of objects, each object if formated as follows:
|
||||
// {
|
||||
// vin: 0, // The position of the input for the given txid
|
||||
// address: bitcoincash:qzhrpmu7nruyfcemeanqh5leuqcnf6zkjq4qm9nqh0
|
||||
// }
|
||||
async _getInputAddrs (txDetails) {
|
||||
try {
|
||||
const retArray = [] // Return array
|
||||
|
||||
for (let i = 0; i < txDetails.vin.length; i++) {
|
||||
// The first input represents the sender of the BCH or tokens.
|
||||
const vin = txDetails.vin[i]
|
||||
const inputTxid = vin.txid
|
||||
const inputVout = vin.vout
|
||||
|
||||
// Get the TX details for the input, in order to retrieve the address of
|
||||
// the sender.
|
||||
const txDetailsParent = await this.getRawTransaction(inputTxid, true)
|
||||
// console.log(
|
||||
// `txDetailsParent: ${JSON.stringify(txDetailsParent, null, 2)}`
|
||||
// )
|
||||
|
||||
// The vout from the previous tx that represents the sender.
|
||||
const voutSender = txDetailsParent.vout[inputVout]
|
||||
|
||||
retArray.push({
|
||||
vin: i,
|
||||
address: voutSender.scriptPubKey.addresses[0]
|
||||
})
|
||||
}
|
||||
|
||||
return retArray
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api RawTransactions.getTxData() getTxData()
|
||||
* @apiName getTxData
|
||||
* @apiGroup RawTransactions
|
||||
* @apiDescription
|
||||
* Returns an object of transaction data, including addresses for input UTXOs.
|
||||
*
|
||||
* This function is equivalent to running `getRawTransaction (txid, true)`,
|
||||
* execept the `vin` array will be populated with an `address` property that
|
||||
* contains the `bitcoincash:` address of the sender for each input.
|
||||
*
|
||||
* This function will only work with a single txid. It does not yet support an
|
||||
* array of TXIDs.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.RawTransactions.getTxData("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||
* console.log(txData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*/
|
||||
// Equivalent to running: async getRawTransaction (txid, verbose = true)
|
||||
// Only handles a single TXID (not arrays).
|
||||
// Appends the BCH address to the inputs of the transaction.
|
||||
async getTxData (txid) {
|
||||
try {
|
||||
if (typeof txid !== 'string') {
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
}
|
||||
|
||||
// Get the TX details for the transaction under consideration.
|
||||
const txDetails = await this.getRawTransaction(txid, true)
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
const inAddrs = await this._getInputAddrs(txDetails)
|
||||
// console.log(`inAddrs: ${JSON.stringify(inAddrs, null, 2)}`)
|
||||
|
||||
// Add the input address to the transaction data.
|
||||
for (let i = 0; i < inAddrs.length; i++) {
|
||||
txDetails.vin[i].address = inAddrs[i].address
|
||||
}
|
||||
|
||||
return txDetails
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps getTxData(), but also appends SLP token information to each input
|
||||
// and output of the transaction. This is a very API-heavy call.
|
||||
// Returns false if the txid is not an SLP transaction.
|
||||
//
|
||||
// Warning! This is a prototype function and the output can change at any time
|
||||
// without reflecting a change in the semantic version. DO NOT USE IN PRODUCTION.
|
||||
async getTxDataSlp (txid) {
|
||||
try {
|
||||
if (typeof txid !== 'string') {
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
}
|
||||
|
||||
const txDetails = await this.getTxData(txid)
|
||||
|
||||
// First get the token information for the output. If that fails, then
|
||||
// this is not an SLP transaction, and this method can return false.
|
||||
let outTokenData
|
||||
try {
|
||||
outTokenData = await this.slpUtils.decodeOpReturn(txid)
|
||||
// console.log(`outTokenData: ${JSON.stringify(outTokenData, null, 2)}`)
|
||||
|
||||
// Add token information to the tx details object.
|
||||
txDetails.tokenTxType = outTokenData.txType
|
||||
txDetails.tokenId = outTokenData.tokenId
|
||||
|
||||
// Add the token quantity to each output.
|
||||
for (let i = 0; i < outTokenData.amounts.length; i++) {
|
||||
txDetails.vout[i + 1].tokenQty = outTokenData.amounts[i]
|
||||
}
|
||||
|
||||
// Loop through each input and retrieve the token data.
|
||||
for (let i = 0; i < txDetails.vin.length; i++) {
|
||||
const thisVin = txDetails.vin[i]
|
||||
|
||||
try {
|
||||
// If decodeOpReturn() throws an error, then this input is not
|
||||
// from an SLP transaction and can be ignored.
|
||||
const inTokenData = await this.slpUtils.decodeOpReturn(thisVin.txid)
|
||||
// console.log(
|
||||
// `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
|
||||
// )
|
||||
|
||||
// Get the appropriate vout token amount. This may throw an error,
|
||||
// which means this Vin is not actually a token UTXO, it was just
|
||||
// associated with a previous token TX.
|
||||
const tokenQty = inTokenData.amounts[thisVin.vout - 1]
|
||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||
|
||||
if (tokenQty) {
|
||||
thisVin.tokenQty = tokenQty
|
||||
// txDetails.vin[i].tokenQty = tokenQty
|
||||
} else {
|
||||
thisVin.tokenQty = null
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log('catch 2: ', err)
|
||||
// If decodeOpReturn() throws an error, then this input is not
|
||||
// from an SLP transaction and can be ignored.
|
||||
// thisVin.tokenQty = null
|
||||
thisVin.tokenQty = null
|
||||
continue
|
||||
}
|
||||
// console.log(
|
||||
// `2: txDetails.vin[i]: ${JSON.stringify(txDetails.vin[i], null, 2)}`
|
||||
// )
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log('catch 1: ', err)
|
||||
return false
|
||||
}
|
||||
|
||||
return txDetails
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api RawTransactions.sendRawTransaction() sendRawTransaction()
|
||||
* @apiName sendRawTransaction
|
||||
|
||||
+4
-7
@@ -883,18 +883,14 @@ class Utils {
|
||||
* @api SLP.Utils.decodeOpReturn() decodeOpReturn()
|
||||
* @apiName decodeOpReturn
|
||||
* @apiGroup SLP Utils
|
||||
* @apiDescription Retrieves transactions data from a txid and decodes the SLP OP_RETURN data.
|
||||
* @apiDescription
|
||||
* Retrieves transactions data from a txid and decodes the SLP OP_RETURN data.
|
||||
*
|
||||
* Similar to decodeOpReturn(), except decodeOpReturn2() uses the slp-parser
|
||||
* library maintained by JT Freeman. Outputs have slightly different format.
|
||||
* Throws an error if given a non-SLP txid.
|
||||
*
|
||||
* If optional associative array parameter cache is used, will cache and
|
||||
* reuse responses for the same input.
|
||||
*
|
||||
* Throws an error if given a non-SLP txid.
|
||||
*
|
||||
* In a future version of bch-js, this method will replace the origonal
|
||||
* decodeOpReturn() method.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*
|
||||
@@ -921,6 +917,7 @@ class Utils {
|
||||
* "99883300000000"
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
*/
|
||||
// Reimplementation of decodeOpReturn() using slp-parser.
|
||||
async decodeOpReturn (txid, cache = null) {
|
||||
|
||||
@@ -369,12 +369,12 @@ describe('#ElectrumX', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#sort0ConfTxs', () => {
|
||||
describe('#sortAllTxs', () => {
|
||||
it('should GET transaction history for a single address', async () => {
|
||||
const addr = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
|
||||
const txs = await bchjs.Electrumx.transactions(addr)
|
||||
const sortedTransactions = await bchjs.Electrumx.sort0ConfTxs(
|
||||
const sortedTransactions = await bchjs.Electrumx.sortAllTxs(
|
||||
txs.transactions
|
||||
)
|
||||
// console.log(
|
||||
|
||||
@@ -230,7 +230,7 @@ describe('#rawtransaction', () => {
|
||||
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', () => {
|
||||
describe('#sendRawTransaction', () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const dataMock =
|
||||
@@ -238,9 +238,9 @@ describe('#rawtransaction', () => {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(data)
|
||||
await bchjs.RawTransactions.sendRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ['error'])
|
||||
@@ -248,6 +248,58 @@ describe('#rawtransaction', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#_getInputAddrs', () => {
|
||||
it('should return an array of input addresses', async () => {
|
||||
// const txid = '32233db13f2ae6d82b6262f335643dccf09fc0bcfcef4bc3fbe023355f02e112'
|
||||
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
const txDetails = await bchjs.RawTransactions.getRawTransaction(txid, true)
|
||||
|
||||
const result = await bchjs.RawTransactions._getInputAddrs(txDetails)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.equal(result.length, 1)
|
||||
assert.property(result[0], 'vin')
|
||||
assert.property(result[0], 'address')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTxData', () => {
|
||||
it('should return tx data with input addresses', async () => {
|
||||
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result.vin[0], 'address')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTxDataSlp', () => {
|
||||
it('should return tx data with SLP information', async () => {
|
||||
const txid = 'b438855cfcab64516b44097d7212df9cdb99226c8d7c7ab504d35fcfd834cb5b'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxDataSlp(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'tokenQty')
|
||||
|
||||
assert.equal(result.vin[0].tokenQty, null)
|
||||
assert.equal(result.vin[1].tokenQty, '100000000000')
|
||||
})
|
||||
|
||||
it('should handle non-slp tx', async () => {
|
||||
const txid = '04a6aca328af2445327015895b8da9766093b1989b52e477559759eb8072fc0a'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxDataSlp(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
|
||||
@@ -456,12 +456,12 @@ describe('#ElectrumX', () => {
|
||||
})
|
||||
|
||||
// These tests use mocked data that contains unconfirmed transactions.
|
||||
describe('#sort0ConfTxs', () => {
|
||||
describe('#sortAllTxs', () => {
|
||||
it('should sort in ascending', async () => {
|
||||
// Stub network calls
|
||||
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
|
||||
|
||||
const result = await bchjs.Electrumx.sort0ConfTxs(mockData.txHistoryWithUnconfirmed.transactions, 'ASCENDING')
|
||||
const result = await bchjs.Electrumx.sortAllTxs(mockData.txHistoryWithUnconfirmed.transactions, 'ASCENDING')
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isBelow(result[0].height, result[1].height)
|
||||
@@ -471,7 +471,7 @@ describe('#ElectrumX', () => {
|
||||
// Stub network calls
|
||||
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
|
||||
|
||||
const result = await bchjs.Electrumx.sort0ConfTxs(mockData.txHistoryWithUnconfirmed.transactions)
|
||||
const result = await bchjs.Electrumx.sortAllTxs(mockData.txHistoryWithUnconfirmed.transactions)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(result[1].height, result[2].height)
|
||||
@@ -482,7 +482,7 @@ describe('#ElectrumX', () => {
|
||||
// Stub network calls
|
||||
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
|
||||
|
||||
await bchjs.Electrumx.sort0ConfTxs('abc')
|
||||
await bchjs.Electrumx.sortAllTxs('abc')
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Mocking data for the RawTransactions unit tests.
|
||||
*/
|
||||
|
||||
const mockTx = {
|
||||
txid: '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3',
|
||||
vin: [
|
||||
{
|
||||
txid: '8a6b3b70569270f0bdbf68fd12a410aef8f7bf044bc88ab02386a1572024b2bd',
|
||||
vout: 0,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3044022035c42f5b10d412445c5ecc5feea42c7f885c433669306c699da0f687216c61d5022018c81cd0ea68101cf3cbe0af67165fca1ce3d667be69d0c9329f0679bbee6ba0[ALL|FORKID] 030152eb20beaa692daaa1a27596dcc98cc06ccbc6eec23d6182a08c7bdaa29ea9',
|
||||
hex:
|
||||
'473044022035c42f5b10d412445c5ecc5feea42c7f885c433669306c699da0f687216c61d5022018c81cd0ea68101cf3cbe0af67165fca1ce3d667be69d0c9329f0679bbee6ba04121030152eb20beaa692daaa1a27596dcc98cc06ccbc6eec23d6182a08c7bdaa29ea9'
|
||||
},
|
||||
sequence: 4294967295
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const mockParentTx1 = {
|
||||
vout: [
|
||||
{
|
||||
value: 0.04199959,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 d5258a73b5c8f94c939d7fe96f78ce97906083be OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914d5258a73b5c8f94c939d7fe96f78ce97906083be88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qr2jtznnkhy0jnynn4l7jmmce6teqcyrhc8herhlgt']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 1.66296161,
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 5e95c308c25c74c64c5ffe44a60a4d9b35743e90 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a9145e95c308c25c74c64c5ffe44a60a4d9b35743e9088ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qp0ftscgcfw8f3jvtllyffs2fkdn2ap7jqreakn4ye']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const mockGetInputAddrsOutput = [
|
||||
{
|
||||
vin: 0,
|
||||
address: 'bitcoincash:qr2jtznnkhy0jnynn4l7jmmce6teqcyrhc8herhlgt'
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
mockTx,
|
||||
mockParentTx1,
|
||||
mockGetInputAddrsOutput
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('assert')
|
||||
const assert2 = require('chai').assert
|
||||
const axios = require('axios')
|
||||
const sinon = require('sinon')
|
||||
// const nock = require("nock") // HTTP mocking
|
||||
@@ -18,16 +19,19 @@ let bchjs
|
||||
// const util = require("util")
|
||||
// util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const mockData = require('./fixtures/rawtransaction-mock')
|
||||
|
||||
describe('#RawTransactions', () => {
|
||||
let sandbox
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe('#decodeRawTransaction', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#decodeRawTransaction', () => {
|
||||
it('should decode raw transaction', done => {
|
||||
const data = {
|
||||
txid:
|
||||
@@ -132,4 +136,82 @@ describe('#RawTransactions', () => {
|
||||
// assert.strictEqual(data, result.data)
|
||||
// })
|
||||
})
|
||||
|
||||
describe('#_getInputAddrs', () => {
|
||||
it('should return an array of input addresses', async () => {
|
||||
sandbox
|
||||
.stub(bchjs.RawTransactions, 'getRawTransaction')
|
||||
.resolves(mockData.mockParentTx1)
|
||||
|
||||
const result = await bchjs.RawTransactions._getInputAddrs(mockData.mockTx)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert2.isArray(result)
|
||||
assert2.equal(result.length, 1)
|
||||
assert2.property(result[0], 'vin')
|
||||
assert2.property(result[0], 'address')
|
||||
})
|
||||
|
||||
it('should catch and throw and error', async () => {
|
||||
try {
|
||||
sandbox
|
||||
.stub(bchjs.RawTransactions, 'getRawTransaction')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
await bchjs.RawTransactions._getInputAddrs(mockData.mockTx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
assert2.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTxData', () => {
|
||||
it('should return tx data with input addresses', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(bchjs.RawTransactions, 'getRawTransaction').resolves(mockData.mockTx)
|
||||
sandbox.stub(bchjs.RawTransactions, '_getInputAddrs').resolves(mockData.mockGetInputAddrsOutput)
|
||||
|
||||
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert2.property(result.vin[0], 'address')
|
||||
})
|
||||
|
||||
it('should throw an error for a non-txid input', async () => {
|
||||
try {
|
||||
await bchjs.RawTransactions.getTxData(1234)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
assert2.include(err.message, 'Input must be a string or array of strings')
|
||||
}
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
// Force a network error.
|
||||
sandbox
|
||||
.stub(bchjs.RawTransactions, 'getRawTransaction')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
await bchjs.RawTransactions.getTxData(txid)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
assert2.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user