mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-21 16:51:59 -07:00
Merge pull request #176 from Permissionless-Software-Foundation/ct-unstable
Improvements to Transaction.get()
This commit is contained in:
@@ -319,6 +319,9 @@ class RawTransactions {
|
|||||||
const inputTxid = vin.txid
|
const inputTxid = vin.txid
|
||||||
const inputVout = vin.vout
|
const inputVout = vin.vout
|
||||||
|
|
||||||
|
// TODO: Coinbase TXs have no input transaction. Figure out how to
|
||||||
|
// handle this corner case.
|
||||||
|
|
||||||
// Get the TX details for the input, in order to retrieve the address of
|
// Get the TX details for the input, in order to retrieve the address of
|
||||||
// the sender.
|
// the sender.
|
||||||
const txDetailsParent = await this.getRawTransaction(inputTxid, true)
|
const txDetailsParent = await this.getRawTransaction(inputTxid, true)
|
||||||
|
|||||||
+1
-1
@@ -921,7 +921,7 @@ class Utils {
|
|||||||
* const txid =
|
* const txid =
|
||||||
* "266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1"
|
* "266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1"
|
||||||
*
|
*
|
||||||
* const data = await slp.Utils.decodeOpReturn2(txid)
|
* const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||||
*
|
*
|
||||||
* console.log(`Decoded OP_RETURN data: ${JSON.stringify(data,null,2)}`)
|
* console.log(`Decoded OP_RETURN data: ${JSON.stringify(data,null,2)}`)
|
||||||
* } catch (error) {
|
* } catch (error) {
|
||||||
|
|||||||
+49
-10
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
const RawTransaction = require('./raw-transactions')
|
const RawTransaction = require('./raw-transactions')
|
||||||
const SlpUtils = require('./slp/utils')
|
const SlpUtils = require('./slp/utils')
|
||||||
|
const BigNumber = require('bignumber.js')
|
||||||
|
|
||||||
class Transaction {
|
class Transaction {
|
||||||
constructor (config) {
|
constructor (config) {
|
||||||
@@ -75,9 +76,18 @@ class Transaction {
|
|||||||
// Add the token quantity to each output.
|
// Add the token quantity to each output.
|
||||||
for (let i = 0; i < outTokenData.amounts.length; i++) {
|
for (let i = 0; i < outTokenData.amounts.length; i++) {
|
||||||
const rawQty = outTokenData.amounts[i]
|
const rawQty = outTokenData.amounts[i]
|
||||||
const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
|
// const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
|
||||||
|
|
||||||
txDetails.vout[i + 1].tokenQty = realQty
|
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||||
|
// floating point number.
|
||||||
|
let realQty = new BigNumber(rawQty).dividedBy(
|
||||||
|
10 ** parseInt(txDetails.tokenDecimals)
|
||||||
|
)
|
||||||
|
realQty = realQty.toString()
|
||||||
|
// realQty = parseFloat(realQty)
|
||||||
|
|
||||||
|
txDetails.vout[i + 1].tokenQtyStr = realQty
|
||||||
|
txDetails.vout[i + 1].tokenQty = parseFloat(realQty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tokenQty = null to any outputs that don't have a value.
|
// Add tokenQty = null to any outputs that don't have a value.
|
||||||
@@ -101,17 +111,46 @@ class Transaction {
|
|||||||
// `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
|
// `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
|
||||||
// )
|
// )
|
||||||
|
|
||||||
// Get the appropriate vout token amount. This may throw an error,
|
let tokenQty = 0
|
||||||
// which means this Vin is not actually a token UTXO, it was just
|
if (inTokenData.txType === 'SEND') {
|
||||||
// associated with a previous token TX.
|
// Get the appropriate vout token amount. This may throw an error,
|
||||||
const tokenQty = inTokenData.amounts[thisVin.vout - 1]
|
// which means this Vin is not actually a token UTXO, it was just
|
||||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
// associated with a previous token TX.
|
||||||
|
tokenQty = inTokenData.amounts[thisVin.vout - 1]
|
||||||
|
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||||
|
|
||||||
|
//
|
||||||
|
} else if (inTokenData.txType === 'GENESIS') {
|
||||||
|
// Only vout[1] of a Genesis transaction represents the tokens.
|
||||||
|
// Any other outputs in that transaction are normal BCH UTXOs.
|
||||||
|
if (thisVin.vout === 1) {
|
||||||
|
tokenQty = inTokenData.qty
|
||||||
|
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
'Unexpected code path in Transaction.get(). Is this a MINT transaction?'
|
||||||
|
)
|
||||||
|
console.log(inTokenData)
|
||||||
|
throw new Error('Unexpected code path')
|
||||||
|
}
|
||||||
|
|
||||||
if (tokenQty) {
|
if (tokenQty) {
|
||||||
const realQty =
|
// const realQty =
|
||||||
Number(tokenQty) / Math.pow(10, txDetails.tokenDecimals)
|
// Number(tokenQty) / Math.pow(10, txDetails.tokenDecimals)
|
||||||
|
|
||||||
thisVin.tokenQty = realQty
|
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||||
|
// floating point number.
|
||||||
|
let realQty = new BigNumber(tokenQty).dividedBy(
|
||||||
|
10 ** parseInt(txDetails.tokenDecimals)
|
||||||
|
)
|
||||||
|
realQty = realQty.toString()
|
||||||
|
// realQty = parseFloat(realQty)
|
||||||
|
|
||||||
|
thisVin.tokenQtyStr = realQty
|
||||||
|
thisVin.tokenQty = parseFloat(realQty)
|
||||||
// txDetails.vin[i].tokenQty = tokenQty
|
// txDetails.vin[i].tokenQty = tokenQty
|
||||||
} else {
|
} else {
|
||||||
thisVin.tokenQty = null
|
thisVin.tokenQty = null
|
||||||
|
|||||||
@@ -175,10 +175,124 @@ const mockOpReturnData03 = {
|
|||||||
amounts: ['1000000000', '99883400000000']
|
amounts: ['1000000000', '99883400000000']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const genesisTestInputTx = {
|
||||||
|
txid: '874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e',
|
||||||
|
hash: '874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e',
|
||||||
|
version: 1,
|
||||||
|
size: 480,
|
||||||
|
locktime: 543408,
|
||||||
|
vin: [
|
||||||
|
{
|
||||||
|
txid: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
|
||||||
|
vout: 1,
|
||||||
|
scriptSig: {
|
||||||
|
asm:
|
||||||
|
'30440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f55[ALL|FORKID] 028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11',
|
||||||
|
hex:
|
||||||
|
'4730440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f554121028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11'
|
||||||
|
},
|
||||||
|
sequence: 4294967294,
|
||||||
|
address: 'bitcoincash:qp2jesd06k8ycj4wvkpl9lcwaemtr04f5yphjsa07v',
|
||||||
|
value: 0.00000546
|
||||||
|
},
|
||||||
|
{
|
||||||
|
txid: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
|
||||||
|
vout: 3,
|
||||||
|
scriptSig: {
|
||||||
|
asm:
|
||||||
|
'3045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519[ALL|FORKID] 02cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bc',
|
||||||
|
hex:
|
||||||
|
'483045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519412102cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bc'
|
||||||
|
},
|
||||||
|
sequence: 4294967294,
|
||||||
|
address: 'bitcoincash:qppj3euc36x5u6twr5cxrrea2rca53vsfu3dxwr86j',
|
||||||
|
value: 0.00172192
|
||||||
|
}
|
||||||
|
],
|
||||||
|
vout: [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
n: 0,
|
||||||
|
scriptPubKey: {
|
||||||
|
asm:
|
||||||
|
'OP_RETURN 5262419 1 1145980243 323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35 00000000004c4b40 00000000004c4b40',
|
||||||
|
hex:
|
||||||
|
'6a04534c500001010453454e4420323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d350800000000004c4b400800000000004c4b40',
|
||||||
|
type: 'nulldata'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0.00000546,
|
||||||
|
n: 1,
|
||||||
|
scriptPubKey: {
|
||||||
|
asm:
|
||||||
|
'OP_DUP OP_HASH160 0a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e0 OP_EQUALVERIFY OP_CHECKSIG',
|
||||||
|
hex: '76a9140a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e088ac',
|
||||||
|
reqSigs: 1,
|
||||||
|
type: 'pubkeyhash',
|
||||||
|
addresses: ['bitcoincash:qq98fnuup7eldhtzc067ang76mss29pguqh7qv9eac']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0.00000546,
|
||||||
|
n: 2,
|
||||||
|
scriptPubKey: {
|
||||||
|
asm:
|
||||||
|
'OP_DUP OP_HASH160 d4548261e1be0de7e50b7511597799ec4af2b173 OP_EQUALVERIFY OP_CHECKSIG',
|
||||||
|
hex: '76a914d4548261e1be0de7e50b7511597799ec4af2b17388ac',
|
||||||
|
reqSigs: 1,
|
||||||
|
type: 'pubkeyhash',
|
||||||
|
addresses: ['bitcoincash:qr29fqnpuxlqmel9pd63zkthn8ky4u43wv0v7pg5mn']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0.00171165,
|
||||||
|
n: 3,
|
||||||
|
scriptPubKey: {
|
||||||
|
asm:
|
||||||
|
'OP_DUP OP_HASH160 d4548261e1be0de7e50b7511597799ec4af2b173 OP_EQUALVERIFY OP_CHECKSIG',
|
||||||
|
hex: '76a914d4548261e1be0de7e50b7511597799ec4af2b17388ac',
|
||||||
|
reqSigs: 1,
|
||||||
|
type: 'pubkeyhash',
|
||||||
|
addresses: ['bitcoincash:qr29fqnpuxlqmel9pd63zkthn8ky4u43wv0v7pg5mn']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
hex:
|
||||||
|
'0100000002359dab0d9c8ddc788b14c0b51493d195c9fbd9f2203d091663350bae351e3a32010000006a4730440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f554121028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11feffffff359dab0d9c8ddc788b14c0b51493d195c9fbd9f2203d091663350bae351e3a32030000006b483045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519412102cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bcfeffffff040000000000000000406a04534c500001010453454e4420323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d350800000000004c4b400800000000004c4b4022020000000000001976a9140a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e088ac22020000000000001976a914d4548261e1be0de7e50b7511597799ec4af2b17388ac9d9c0200000000001976a914d4548261e1be0de7e50b7511597799ec4af2b17388acb04a0800',
|
||||||
|
blockhash: '000000000000000000292a9c6150fce48e2edd8df346948494fe6249e6e7f63b',
|
||||||
|
confirmations: 163095,
|
||||||
|
time: 1534271330,
|
||||||
|
blocktime: 1534271330
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisTestOpReturnData01 = {
|
||||||
|
tokenType: 1,
|
||||||
|
txType: 'SEND',
|
||||||
|
tokenId: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
|
||||||
|
amounts: ['5000000', '5000000']
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisTestOpReturnData02 = {
|
||||||
|
tokenType: 1,
|
||||||
|
txType: 'GENESIS',
|
||||||
|
ticker: '',
|
||||||
|
name: '',
|
||||||
|
tokenId: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
|
||||||
|
documentUri: '',
|
||||||
|
documentHash: '',
|
||||||
|
decimals: 0,
|
||||||
|
mintBatonVout: 2,
|
||||||
|
qty: '10000000'
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
nonSlpTxDetails,
|
nonSlpTxDetails,
|
||||||
slpTxDetails,
|
slpTxDetails,
|
||||||
mockOpReturnData01,
|
mockOpReturnData01,
|
||||||
mockOpReturnData02,
|
mockOpReturnData02,
|
||||||
mockOpReturnData03
|
mockOpReturnData03,
|
||||||
|
genesisTestInputTx,
|
||||||
|
genesisTestOpReturnData01,
|
||||||
|
genesisTestOpReturnData02
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,5 +115,54 @@ describe('#TransactionLib', () => {
|
|||||||
assert.include(err.message, 'test error')
|
assert.include(err.message, 'test error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// This test case was created in response to a bug. When the input TX
|
||||||
|
// was a Genesis SLP transaction, the inputs of the transaction were not
|
||||||
|
// being hydrated properly.
|
||||||
|
it('should get input details when input is a genesis tx', async () => {
|
||||||
|
// Mock dependencies
|
||||||
|
sandbox
|
||||||
|
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||||
|
.resolves(mockData.genesisTestInputTx)
|
||||||
|
sandbox
|
||||||
|
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||||
|
.onCall(0)
|
||||||
|
.resolves(mockData.genesisTestOpReturnData01)
|
||||||
|
.onCall(1)
|
||||||
|
.resolves(mockData.genesisTestOpReturnData02)
|
||||||
|
.onCall(2)
|
||||||
|
.resolves(mockData.genesisTestOpReturnData02)
|
||||||
|
.onCall(3)
|
||||||
|
.resolves(mockData.genesisTestOpReturnData02)
|
||||||
|
sandbox
|
||||||
|
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
|
||||||
|
.resolves(true)
|
||||||
|
|
||||||
|
const txid =
|
||||||
|
'874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e'
|
||||||
|
|
||||||
|
const result = await bchjs.Transaction.get(txid)
|
||||||
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||||
|
|
||||||
|
// Assert that there are stanardized properties.
|
||||||
|
assert.property(result, 'txid')
|
||||||
|
assert.property(result, 'vin')
|
||||||
|
assert.property(result, 'vout')
|
||||||
|
assert.property(result.vout[0], 'value')
|
||||||
|
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||||
|
|
||||||
|
// Assert that added properties exist.
|
||||||
|
assert.property(result.vout[0], 'tokenQty')
|
||||||
|
assert.equal(result.vout[0].tokenQty, null)
|
||||||
|
assert.property(result.vin[0], 'address')
|
||||||
|
assert.property(result.vin[0], 'value')
|
||||||
|
assert.property(result.vin[0], 'tokenQty')
|
||||||
|
assert.property(result, 'isValidSLPTx')
|
||||||
|
assert.equal(result.isValidSLPTx, true)
|
||||||
|
|
||||||
|
// Assert inputs values unique to a Genesis input have the proper values.
|
||||||
|
assert.equal(result.vin[0].tokenQty, 10000000)
|
||||||
|
assert.equal(result.vin[1].tokenQty, null)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user