1
0

DecodeRawTx.vue 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177
  1. <script setup>
  2. import numeral from 'numeral'
  3. import { useSystemStore } from '@/stores/system'
  4. const System = useSystemStore()
  5. /**
  6. * Reverse Bytes
  7. *
  8. * Reverses the order (endianness) of the hex string.
  9. *
  10. * @param {String} _bytes A (hex) string.
  11. */
  12. const reverseBytes = (_bytes) => {
  13. if (typeof _bytes === 'undefined' || !_bytes || _bytes.length === 0) {
  14. return _bytes
  15. }
  16. // if (!Array.isArray(_bytes)) {
  17. // // return _bytes
  18. // // NOTE: We presume this is a HEX string.
  19. // return _bytes.match(/[a-fA-F0-9]{2}/g).reverse().join('')
  20. // }
  21. /* Reverse bytes. */
  22. // source: https://stackoverflow.com/a/29017642/514914
  23. return _bytes.match(/[a-fA-F0-9]{2}/g).reverse().join('')
  24. }
  25. // const rawTxHex = ref(null)
  26. // const rawTxHex = ref('0200000001abadc7de402c7a7b84b7fb2eff5d0c8c94d661d010637a4fb5b8c11c55e6f435000000006441f8c401531747cb2cfb133a37b0817a942cbc72cbe259aec83a5d2ee8d6977a535a2050d4f2dbc53560673fbdfc17f7ef6af14e390964847ed5d4e7a487d29c094121024c750792d20e799f91cad7ebd8e67ae5e6638be213fcec8b4874a010d070db4900000000016e040000000000001976a9141d048fbba6307f595356910bca64fa0e86ca7de788ac00000000') // BCH (1 in / 1 out)
  27. // const rawTxHex = ref('0200000002b8d95713b4a6c742f55326b2fa0bbfc7810630f7e293b9e172b67058cdcb3487010000006441bc71d8a42d1f9bf8137bcea2834770d6a1b31c5afdf7f8d4bbbb8815fcca0037b55c7fa78ae1f62e87e3ac953ab9241691ae3f8b09f4edfa402590e64d1451964121024c750792d20e799f91cad7ebd8e67ae5e6638be213fcec8b4874a010d070db490000000080d2fec073c4d7e8b539c1cd8e30a23f9b29b6d7b47e4a3b639401592194e2f50100000064413023ef0bf09b203b105a679aa337de7135c96a2361a52e28bd8981c78f4f238e5f57c995ce9982f1e1d1954f1f9d9fe90e4d5a1c8065b321e3f4bbf38d9835de4121024c750792d20e799f91cad7ebd8e67ae5e6638be213fcec8b4874a010d070db4900000000010c090000000000001976a9141d048fbba6307f595356910bca64fa0e86ca7de788ac00000000') // BCH (2 in / 1 out)
  28. // const rawTxHex = ref('020000000148957a304661f6426cb13bb364d558fa6dbed2202e4cd5bcbfa208a75de1022a040000006b483045022100c4d265fc9b7b61c944a917c7efc6d378754f525448f433e64dfcb1b07c9d78c902207db08c00603dde7b07488f90f760e228f9295fcdf9afeecc34ed40cdd6e5630441210350a165309f0f0761ed321890e033bc4af5e842cf38507e9862d602e448233fdeffffffff020a1f6d00000000001976a914658fb06e86f3c3ac21c6fbf50e18dfc0747b2dbd88aceacfe300000000001976a9143cc2e4b8446d150efdc420babb77ef69422e06fb88ac00000000') // BCH_ECSDA (1 in / 2 out)
  29. // const rawTxHex = ref('01000000016377d5d92f7244deddab0c3cf0268d2005eee238491fa6c8cb40e43b0e98245e010000006441f7f9f9ca507031b26c972fc23ab9b052843f7c3b3ee4e29acdd4dc09dbc03addf9f07e069d52009f5ded90a741c5223b90b20e39ebf9af37b0ca3ef1010959c0412102ff08fda3ea73d50eb7c52f82b55d7883e1843c7e6c80ba0daf08032f2e88692efeffffff020000000000000000866a4c834243482069732061626f757420676976696e672070656f706c65207468652066726565646f6d20746f206d616b65207468656972206f776e2063686f696365732c20746f20707572737565207468656972206f776e2068617070696e6573732c20686f7765766572207468657920696e646976696475616c6c7920736565206669742e3a9598000000000017a914785ca29645c56f51ff2581dd29e812764fe79636871ae60800') // BCH_P2SH (1 in / 2 out)
  30. const rawTxHex = ref('000100c80e241c69107c397ad81730541aba81d0528d14309b2a516f6e415c484c6b9864222103cbe16ecb57d7a173ef5d46692daf38f366e2939b655817335fbfcd8a4edc41c0405e29f9533df564bc308d188e7e12a3f41954e24ba1d7a994c8d0060ea819a70c2af63d74b995f9aace3daf965df314df94b437cb662eff12861e2fd3d6974e1afeffffff39050000000000000101580200000000000017005114aa53676557ac11d41f0c66caade565cac78ca4f8605b0300') // NEXA
  31. const chainid = computed(() => {
  32. /* Validate hex value. */
  33. if (
  34. typeof rawTxHex.value === 'undefined' ||
  35. rawTxHex.value === null ||
  36. rawTxHex.value === ''
  37. ) {
  38. /* Return null. */
  39. return null
  40. }
  41. /* Test for Bitcoin Cash. */
  42. if (rawTxHex.value.slice(0, 8) === '01000000' || rawTxHex.value.slice(0, 8) === '02000000') {
  43. return 'BCH'
  44. }
  45. /* Test for Nexa. */
  46. if (rawTxHex.value.slice(0, 2) === '00') {
  47. return 'NEXA'
  48. }
  49. return null
  50. })
  51. /**
  52. * Transaction Version (Parser)
  53. *
  54. * Will parse the transaction version.
  55. */
  56. const txVersion = computed(() => {
  57. /* Validate hex value. */
  58. if (
  59. typeof rawTxHex.value === 'undefined'
  60. || rawTxHex.value === null
  61. || rawTxHex.value === ''
  62. ) {
  63. /* Return null. */
  64. return null
  65. }
  66. /* Initialize version. */
  67. let version = null
  68. /* Validate hex length. */
  69. // NOTE: Allow up to 4 bytes for version number.
  70. if (rawTxHex.value.length >= 8) {
  71. if (chainid.value === 'BCH') {
  72. /* Parse (transaction) version. */
  73. version = rawTxHex.value.slice(0, 8) // BCH
  74. }
  75. if (chainid.value === 'NEXA') {
  76. /* Parse (transaction) version. */
  77. version = rawTxHex.value.slice(0, 2) // NEXA
  78. }
  79. }
  80. /* Return (transaction) version. */
  81. return version
  82. })
  83. const txInputsCount = computed(() => {
  84. /* Validate hex value. */
  85. if (!chainid) return null
  86. let startPos = 0
  87. let endPos = 0
  88. let decimal = null
  89. let value = null
  90. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  91. if (chainid.value === 'BCH') {
  92. startPos = 8
  93. endPos = 10
  94. /* Parse tx input count. */
  95. value = rawTxHex.value.slice(startPos, endPos)
  96. }
  97. if (chainid.value === 'NEXA') {
  98. startPos = 2
  99. endPos = 4
  100. /* Parse tx input count. */
  101. value = rawTxHex.value.slice(startPos, endPos)
  102. // FIXME Add support for variable integer.
  103. }
  104. }
  105. /* Calulcate decimal value. */
  106. decimal = parseInt(value, 16)
  107. /* Return tx input count. */
  108. return {
  109. startPos,
  110. endPos,
  111. value,
  112. decimal,
  113. }
  114. })
  115. const txInputs = computed(() => {
  116. /* Validate hex value. */
  117. if (!chainid) return null
  118. let startPos = 0
  119. let endPos = 0
  120. let value = null
  121. const inputs = []
  122. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  123. for (let i = 0; i < txInputsCount.value.decimal; i++) {
  124. if (chainid.value === 'BCH') {
  125. // TBD
  126. }
  127. if (chainid.value === 'NEXA') {
  128. startPos = txInputsCount.value.endPos
  129. let authLen = rawTxHex.value.slice(startPos + 64 + 2, startPos + 64 + 2 + 2)
  130. console.log('AUTH LEN', authLen)
  131. endPos = startPos + 64 + 2 + 2 + (parseInt(authLen, 16) * 2) + 8 + 16
  132. /* Parse input. */
  133. value = rawTxHex.value.slice(startPos, endPos)
  134. }
  135. inputs.push({
  136. startPos,
  137. endPos,
  138. value,
  139. })
  140. }
  141. }
  142. /* Return inputs. */
  143. return inputs
  144. })
  145. const txOutpoints = computed(() => {
  146. /* Validate hex value. */
  147. if (!chainid) return null
  148. let startPos = 0
  149. let endPos = 0
  150. let value = null
  151. let outpoints = []
  152. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  153. if (chainid.value === 'BCH') {
  154. // TBD
  155. }
  156. if (chainid.value === 'NEXA') {
  157. txInputs.value.forEach(_input => {
  158. console.log('INPUT', _input)
  159. startPos = 2
  160. endPos = startPos + 64
  161. value = _input.value.slice(startPos, endPos)
  162. outpoints.push({
  163. startPos,
  164. endPos,
  165. value,
  166. reversed: reverseBytes(value),
  167. })
  168. })
  169. }
  170. }
  171. /* Return tx input count. */
  172. return outpoints
  173. })
  174. const txOutpointsAmounts = computed(() => {
  175. /* Validate hex value. */
  176. if (!chainid) return null
  177. let startPos = 0
  178. let endPos = 0
  179. let value = null
  180. let amounts = []
  181. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  182. if (chainid.value === 'BCH') {
  183. // TBD
  184. }
  185. if (chainid.value === 'NEXA') {
  186. txInputs.value.forEach(_input => {
  187. // console.log('INPUT', _input)
  188. // startPos = 2
  189. // endPos = startPos + 64
  190. value = _input.value.slice(-16)
  191. let nex
  192. let reversed
  193. let satoshis
  194. let usd
  195. reversed = reverseBytes(value)
  196. satoshis = parseInt(reversed, 16)
  197. nex = satoshis / 100.0
  198. if (System.quotes.NEXA) {
  199. usd = numeral(nex * System.quotes.NEXA.price).format('$0,0.00[000000]')
  200. }
  201. amounts.push({
  202. startPos,
  203. endPos,
  204. value,
  205. reversed,
  206. satoshis,
  207. nex,
  208. usd,
  209. })
  210. })
  211. }
  212. }
  213. /* Return tx input count. */
  214. return amounts
  215. })
  216. const txOutpointIndices = computed(() => {
  217. /* Validate hex value. */
  218. if (!chainid) return null
  219. let startPos = 0
  220. let endPos = 0
  221. let value = null
  222. let outpointIndices = []
  223. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  224. if (chainid.value === 'BCH') {
  225. startPos = 74
  226. endPos = 82
  227. value = rawTxHex.value.slice(startPos, endPos)
  228. /* Parse authorization block. */
  229. outpointIndices.push({
  230. startPos,
  231. endPos,
  232. value,
  233. })
  234. startPos = 356
  235. endPos = 364
  236. value = rawTxHex.value.slice(startPos, endPos)
  237. /* Parse authorization block. */
  238. outpointIndices.push({
  239. startPos,
  240. endPos,
  241. value,
  242. })
  243. }
  244. if (chainid.value === 'NEXA') {
  245. /* Parse tx outpoint index. */
  246. // outpointIndex = rawTxHex.value.slice(4, 6)
  247. outpointIndices.push({
  248. startPos,
  249. endPos,
  250. value,
  251. })
  252. }
  253. }
  254. /* Return tx input count. */
  255. return outpointIndices
  256. })
  257. const txOutpoint = computed(() => {
  258. /* Validate hex value. */
  259. if (!chainid || chainid !== 'NEXA') return null
  260. let outpoint = null
  261. let reversed = null
  262. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  263. /* Parse tx id. */
  264. outpoint = rawTxHex.value.slice(6, 70)
  265. /* Reverse endianness. */
  266. reversed = reverseBytes(outpoint)
  267. }
  268. /* Return (reversed) tx id. */
  269. return reversed
  270. })
  271. const txInputBlocks = computed(() => {
  272. /* Validate hex value. */
  273. if (!chainid) return null
  274. let startPos = 0
  275. let endPos = 0
  276. let authLen = 0
  277. let authBlocks = []
  278. if (chainid.value === 'BCH') {
  279. startPos = 82
  280. endPos = startPos + 2
  281. authLen = parseInt(rawTxHex.value.slice(startPos, endPos), 16) * 2
  282. startPos = endPos
  283. endPos = startPos + authLen
  284. /* Parse authorization block. */
  285. authBlocks.push({
  286. startPos,
  287. endPos,
  288. value: rawTxHex.value.slice(startPos, endPos),
  289. })
  290. /* Parse # of remaining inputs. */
  291. const numInputsRemaining = txInputsCount.decimal - 1
  292. /* Handle remaining inputs. */
  293. for (let i = 0; i < numInputsRemaining; i++) {
  294. startPos = endPos + 8 + 64 + 8 // sequence + txid + vout
  295. endPos = startPos + 2
  296. authLen = parseInt(rawTxHex.value.slice(startPos, endPos), 16) * 2
  297. startPos = endPos
  298. endPos = startPos + authLen
  299. authBlocks.push({
  300. startPos,
  301. endPos,
  302. value: rawTxHex.value.slice(startPos, endPos),
  303. })
  304. }
  305. }
  306. if (chainid.value === 'NEXA') {
  307. startPos = 82
  308. endPos = startPos + 2
  309. authLen = parseInt(rawTxHex.value.slice(startPos, endPos), 16) * 2
  310. startPos = endPos
  311. endPos = startPos + authLen
  312. /* Parse authorization block. */
  313. authBlocks.push({
  314. startPos,
  315. endPos,
  316. value: rawTxHex.value.slice(startPos, endPos),
  317. })
  318. /* Parse # of remaining inputs. */
  319. const numInputsRemaining = txInputsCount.decimal - 1
  320. /* Handle remaining inputs. */
  321. for (let i = 0; i < numInputsRemaining; i++) {
  322. startPos = endPos + 8 + 64 + 8 // sequence + txid + vout
  323. endPos = startPos + 2
  324. authLen = parseInt(rawTxHex.value.slice(startPos, endPos), 16) * 2
  325. startPos = endPos
  326. endPos = startPos + authLen
  327. authBlocks.push({
  328. startPos,
  329. endPos,
  330. value: rawTxHex.value.slice(startPos, endPos),
  331. })
  332. }
  333. }
  334. // console.log('AUTH BLOCKS', authBlocks)
  335. /* Return authorization block length. */
  336. return authBlocks
  337. })
  338. const txSigBlocks = computed(() => {
  339. /* Validate hex value. */
  340. if (!chainid) return null
  341. let startPos = 0
  342. let endPos = 0
  343. let value = null
  344. let sigBlockLengths = []
  345. if (chainid.value === 'BCH') {
  346. for (let i = 0; i < txInputBlocks.value.length; i++) {
  347. startPos = txInputBlocks.value[i].startPos
  348. endPos = startPos + 2
  349. value = rawTxHex.value.slice(startPos, endPos)
  350. /* Parse signature block. */
  351. sigBlockLengths.push({
  352. startPos,
  353. endPos,
  354. value,
  355. })
  356. }
  357. }
  358. console.log('sigBlockLengths', sigBlockLengths)
  359. /* Return signature block length. */
  360. return sigBlockLengths
  361. })
  362. const txInputScriptBytes = computed(() => {
  363. return null
  364. /* Validate hex value. */
  365. if (!chainid) return null
  366. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  367. /* Parse tx outpoint index. */
  368. const txInputScriptBytes = rawTxHex.value.slice(76, 142)
  369. /* Return tx input count. */
  370. return txInputScriptBytes
  371. } else {
  372. /* Return null. */
  373. return null
  374. }
  375. })
  376. const txSignatures = computed(() => {
  377. /* Validate hex value. */
  378. if (!chainid) return null
  379. let startPos = 0
  380. let endPos = 0
  381. let value = null
  382. let signatures = []
  383. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  384. if (chainid.value === 'BCH') {
  385. txSigBlocks.value.forEach(_block => {
  386. const sigLen = parseInt(_block.value, 16) * 2
  387. startPos = _block.startPos + 2
  388. endPos = startPos + sigLen
  389. /* Parse tx outpoint index. */
  390. value = rawTxHex.value.slice(startPos, endPos)
  391. signatures.push({
  392. startPos,
  393. endPos,
  394. tiny: `${value.slice(0, 16)} ... ${value.slice(-16)}`, // for mobile
  395. abbr: `${value.slice(0, 32)} ... ${value.slice(-32)}`, // for desktop
  396. value,
  397. })
  398. })
  399. }
  400. if (chainid.value === 'NEXA') {
  401. const sigLen = parseInt(txSigBlocks.value, 16) * 2
  402. startPos = 144
  403. endPos = startPos + sigLen
  404. /* Parse tx outpoint index. */
  405. value = rawTxHex.value.slice(startPos, endPos)
  406. }
  407. }
  408. /* Return tx input count. */
  409. return signatures
  410. })
  411. const txPubkeys = computed(() => {
  412. /* Validate hex value. */
  413. if (!chainid) return null
  414. let startPos = 0
  415. let endPos = 0
  416. let value = null
  417. let pubkeys = []
  418. let pubkeyLen = 0
  419. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  420. if (chainid.value === 'BCH') {
  421. txSignatures.value.forEach(_sig => {
  422. pubkeyLen = rawTxHex.value.slice(_sig.endPos, _sig.endPos + 2)
  423. // console.log('PUBKEY LEN', pubkeyLen)
  424. startPos = _sig.endPos + 2
  425. endPos = startPos + (parseInt(pubkeyLen, 16) * 2)
  426. /* Parse tx outpoint index. */
  427. // FIXME: Calculate the txInputScriptBytes (length).
  428. value = rawTxHex.value.slice(startPos, endPos)
  429. pubkeys.push({
  430. startPos,
  431. endPos,
  432. value,
  433. })
  434. })
  435. }
  436. if (chainid.value === 'NEXA') {
  437. startPos = 144
  438. endPos = 272
  439. /* Parse tx outpoint index. */
  440. // FIXME: Calculate the txInputScriptBytes (length).
  441. value = rawTxHex.value.slice(startPos, endPos)
  442. }
  443. }
  444. /* Return tx input count. */
  445. return pubkeys
  446. })
  447. const txSequences = computed(() => {
  448. /* Validate hex value. */
  449. if (!chainid) return null
  450. let startPos = 0
  451. let endPos = 0
  452. let value = null
  453. let sequences = []
  454. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  455. if (chainid.value === 'BCH') {
  456. txPubkeys.value.forEach(_pubkey => {
  457. startPos = _pubkey.endPos
  458. endPos = startPos + 8
  459. /* Parse tx outpoint index. */
  460. // FIXME: Calculate the txInputScriptBytes (length).
  461. value = rawTxHex.value.slice(startPos, endPos)
  462. sequences.push({
  463. startPos,
  464. endPos,
  465. value,
  466. })
  467. })
  468. }
  469. if (chainid.value === 'NEXA') {
  470. return
  471. startPos = txPubkeys.value.endPos
  472. endPos = startPos + 8
  473. /* Parse tx outpoint index. */
  474. value = rawTxHex.value.slice(startPos, endPos)
  475. }
  476. }
  477. /* Return tx input count. */
  478. return sequences
  479. })
  480. const txOutputCount = computed(() => {
  481. /* Validate hex value. */
  482. if (!chainid) return null
  483. let startPos = 0
  484. let endPos = 0
  485. let value = null
  486. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  487. if (chainid.value === 'BCH') {
  488. startPos = txSequences.value[txSequences.value.length - 1].endPos
  489. endPos = startPos + 2
  490. /* Parse tx outpoint index. */
  491. value = rawTxHex.value.slice(startPos, endPos)
  492. }
  493. if (chainid.value === 'NEXA') {
  494. return
  495. startPos = txSequences.value[txSequences.value.length - 1].endPos
  496. endPos = startPos + 2
  497. /* Parse tx outpoint index. */
  498. value = rawTxHex.value.slice(startPos, endPos)
  499. }
  500. }
  501. /* Return tx input count. */
  502. return {
  503. startPos,
  504. endPos,
  505. value,
  506. }
  507. })
  508. const txOutputValues = computed(() => {
  509. /* Validate hex value. */
  510. if (!chainid) return null
  511. let startPos = 0
  512. let endPos = 0
  513. let value = null
  514. let outputValues = []
  515. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  516. if (chainid.value === 'BCH') {
  517. let numOutputs = parseInt(txOutputCount.value.value, 16)
  518. // console.log('NUM OUTPUTS', numOutputs)
  519. for (let i = 0; i < numOutputs; i++) {
  520. if (startPos === 0) {
  521. startPos = txOutputCount.value.endPos
  522. } else {
  523. startPos = endPos
  524. endPos = startPos +2
  525. let scriptLen = rawTxHex.value.slice(startPos, endPos)
  526. console.log('scriptLen', scriptLen)
  527. // startPos = endPos + 2 + 50 // FIXME: Add support for P2PKT
  528. startPos = endPos + (parseInt(scriptLen, 16) * 2)
  529. }
  530. endPos = startPos + 16
  531. /* Parse tx outpoint index. */
  532. value = rawTxHex.value.slice(startPos, endPos)
  533. let bch
  534. let reversed
  535. let satoshis
  536. let usd
  537. reversed = reverseBytes(value)
  538. satoshis = parseInt(reversed, 16)
  539. bch = satoshis / 100000000.0
  540. if (System.quotes.BCH) {
  541. usd = numeral(bch * System.quotes.BCH.price).format('$0,0.00[00]')
  542. }
  543. outputValues.push({
  544. startPos,
  545. endPos,
  546. value,
  547. reversed,
  548. satoshis,
  549. bch,
  550. usd,
  551. })
  552. }
  553. }
  554. if (chainid.value === 'NEXA') {
  555. return
  556. startPos = txOutputCount.value.endPos
  557. endPos = startPos + 16
  558. /* Parse tx outpoint index. */
  559. value = rawTxHex.value.slice(startPos, endPos)
  560. }
  561. }
  562. /* Return tx input count. */
  563. return outputValues
  564. })
  565. const txOutputScriptBytes = computed(() => {
  566. /* Validate hex value. */
  567. if (!chainid) return null
  568. let startPos = 0
  569. let endPos = 0
  570. let value = null
  571. let outputScriptBytesLengths = []
  572. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  573. if (chainid.value === 'BCH') {
  574. let numOutputs = parseInt(txOutputCount.value.value, 16)
  575. console.log('NUM OUTPUTS', numOutputs)
  576. console.log('txOutputValues', txOutputValues.value)
  577. for (let i = 0; i < numOutputs; i++) {
  578. startPos = txOutputValues.value[i].endPos
  579. endPos = startPos + 2
  580. const keyBlock = rawTxHex.value.slice(startPos, endPos)
  581. console.log('KEY BLOCK', keyBlock)
  582. const keyLen = parseInt(keyBlock, 16) * 2
  583. startPos = startPos + 2
  584. endPos = startPos + keyLen
  585. /* Parse tx outpoint index. */
  586. value = rawTxHex.value.slice(startPos, endPos)
  587. if (value.startsWith('6a4c')) {
  588. value = `OP_RETURN ${value.slice(6)}`
  589. }
  590. outputScriptBytesLengths.push({
  591. len: keyBlock,
  592. startPos,
  593. endPos,
  594. value,
  595. })
  596. }
  597. }
  598. if (chainid.value === 'NEXA') {
  599. return
  600. startPos = txSequences.value[0].endPos
  601. endPos = startPos + 2
  602. /* Parse tx outpoint index. */
  603. value = rawTxHex.value.slice(startPos, endPos)
  604. }
  605. }
  606. /* Return tx input count. */
  607. return outputScriptBytesLengths
  608. })
  609. // const txPubKeyScripts = computed (() => {
  610. // /* Validate hex value. */
  611. // if (!chainid) return null
  612. // let startPos = 0
  613. // let endPos = 0
  614. // let value = null
  615. // let pubKeyScripts = []
  616. // return null
  617. // if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  618. // if (chainid.value === 'BCH') {
  619. // startPos = txOutputValues.value.endPos
  620. // endPos = startPos + 2
  621. // const keyBlock = rawTxHex.value.slice(startPos, endPos)
  622. // console.log('KEY BLOCK', keyBlock)
  623. // const keyLen = parseInt(keyBlock, 16) * 2
  624. // startPos = startPos + 2
  625. // endPos = startPos + keyLen
  626. // /* Parse tx outpoint index. */
  627. // value = rawTxHex.value.slice(startPos, endPos)
  628. // pubKeyScripts.push({
  629. // startPos,
  630. // endPos,
  631. // value,
  632. // })
  633. // }
  634. // if (chainid.value === 'NEXA') {
  635. // startPos = txSequence.value.endPos
  636. // endPos = startPos + 2
  637. // /* Parse tx outpoint index. */
  638. // value = rawTxHex.value.slice(startPos, endPos)
  639. // }
  640. // }
  641. // /* Return tx input count. */
  642. // return pubKeyScripts
  643. // })
  644. const txLockTime = computed(() => {
  645. /* Validate hex value. */
  646. if (!chainid) return null
  647. let startPos = 0
  648. let endPos = 0
  649. let decimal = null
  650. let reversed = null
  651. let value = null
  652. if (typeof rawTxHex.value !== 'undefined' && rawTxHex.value !== '') {
  653. if (chainid.value === 'BCH') {
  654. startPos = txOutputScriptBytes.value[txOutputScriptBytes.value.length - 1].endPos
  655. endPos = startPos + 8
  656. /* Parse tx outpoint index. */
  657. value = rawTxHex.value.slice(startPos, endPos)
  658. }
  659. if (chainid.value === 'NEXA') {
  660. return
  661. startPos = txOutputScriptBytes.value[0].endPos
  662. endPos = startPos + 8
  663. /* Parse tx outpoint index. */
  664. value = rawTxHex.value.slice(startPos, endPos)
  665. }
  666. /* Reverse the bytes. */
  667. reversed = reverseBytes(value)
  668. /* Convert to decimal value. */
  669. decimal = parseInt(reversed, 16)
  670. }
  671. /* Return tx input count. */
  672. return {
  673. startPos,
  674. endPos,
  675. decimal,
  676. reversed,
  677. value,
  678. }
  679. })
  680. const myHandler = (_event) => {
  681. console.log('HANDLER', _event)
  682. }
  683. </script>
  684. <template>
  685. <main class="flex flex-col space-y-5">
  686. <div v-if="chainid === 'BCH'" class="">
  687. <h2 class="text-3xl text-yellow-700 font-medium">
  688. Bitcoin Cash Transaction
  689. </h2>
  690. </div>
  691. <div v-if="chainid === 'NEXA'" class="">
  692. <h2 class="text-3xl text-yellow-700 font-medium">
  693. Nexa Transaction
  694. </h2>
  695. </div>
  696. <textarea
  697. class="w-full h-32 px-2 py-1 block border-4 border-yellow-400 bg-yellow-50 text-xs text-yellow-900 rounded-lg"
  698. placeholder="Paste raw hex code here"
  699. v-model="rawTxHex"
  700. ></textarea>
  701. <button @click="rawTxHex = ''" class="w-fit my-5 px-3 py-2 bg-yellow-200 border-2 border-yellow-400 rounded-lg hover:bg-yellow-300">
  702. Clear Data
  703. </button>
  704. <section v-if="txVersion" class="w-fit mt-2 px-3 py-1 bg-gradient-to-r from-yellow-50 to-yellow-100 border border-yellow-500 rounded shadow">
  705. <h2 class="text-2xl font-medium">
  706. Version
  707. </h2>
  708. <h3 class="text-xl text-rose-500 font-medium">
  709. {{txVersion}}
  710. </h3>
  711. <small v-if="chainid === 'BCH'" class="text-muted">
  712. Version number is the 1st 4 bytes of the transaction data in Big-endian (BE) format.
  713. </small>
  714. <small v-if="chainid === 'NEXA'" class="text-muted">
  715. Version number is the 1st byte of the transaction data.
  716. </small>
  717. </section>
  718. <section v-if="txInputsCount" class="w-fit mt-2 px-3 py-1 bg-gradient-to-r from-yellow-50 to-yellow-100 border border-yellow-500 rounded shadow">
  719. <h2 class="text-2xl font-medium">
  720. Input Count
  721. </h2>
  722. <h3 class="text-xl text-rose-500 font-medium font-mono">
  723. {{txInputsCount.value}}
  724. </h3>
  725. </section>
  726. <section v-for="(input, index) of txInputBlocks" :key="input.value" class="ml-10 px-3 py-3 bg-gradient-to-r from-gray-100 to-gray-200 border-2 border-gray-400 rounded shadow">
  727. <pre class="border-4 border-green-500">
  728. {{ txInputs }}
  729. </pre>
  730. <h2 class="text-xl text-gray-500 font-medium tracking-widest">
  731. Input # {{index + 1}}
  732. </h2>
  733. <section v-if="txOutpoints" class="w-fit mt-2 px-3 py-1 bg-gradient-to-r from-yellow-50 to-yellow-100 border border-yellow-500 rounded shadow">
  734. <h2 class="text-2xl font-medium">
  735. Unspent Transaction Output ID
  736. </h2>
  737. <small class="block text-muted">
  738. <a :href="'https://blockchair.com/bitcoin-cash/transaction/' + txOutpoints[index].reversed" target="_blank" class="block font-mono text-base text-blue-500 font-medium hover:underline">
  739. {{txOutpoints[index].reversed}}
  740. </a>
  741. </small>
  742. <small class="block text-xs italic">
  743. NOTE: Endianness has been reversed.
  744. </small>
  745. </section>
  746. <section v-for="(outpoint, index) of txOutpointsAmounts" :key="outpoint.value" class="ml-10 px-3 py-3 bg-gradient-to-r from-gray-100 to-gray-200 border-2 border-gray-400 rounded shadow">
  747. <h2 class="text-xl text-yellow-900 font-medium">
  748. Output Value
  749. </h2>
  750. <h3 class="text-xl text-rose-500 font-medium font-mono">
  751. {{outpoint.value}}
  752. </h3>
  753. <small class=" text-yellow-700">
  754. An <span class="font-medium">8-byte (64-bit)</span> number in Big-endian (BE) format.
  755. </small>
  756. <section class="w-fit my-1 px-3 py-1 text-yellow-400 bg-gradient-to-r from-yellow-800 to-yellow-700 border border-yellow-500 rounded shadow">
  757. <div v-if="outpoint.satoshis" class="grid grid-cols-2 gap-2">
  758. <span class="font-mono text-yellow-100 font-medium text-right">
  759. {{outpoint.satoshis}}
  760. </span>
  761. <span class="text-sm">
  762. satoshis
  763. </span>
  764. </div>
  765. <div v-if="outpoint.nex" class="grid grid-cols-2 gap-2">
  766. <span class="font-mono text-yellow-100 font-medium text-right">
  767. {{outpoint.nex}}
  768. </span>
  769. <span class="text-sm">
  770. NEX
  771. </span>
  772. </div>
  773. <div v-if="outpoint.usd" class="grid grid-cols-2 gap-2">
  774. <span class="font-mono text-yellow-100 font-medium text-right">
  775. {{outpoint.usd}}
  776. </span>
  777. <span class="text-sm">
  778. USD
  779. <small class="text-xs text-yellow-500">
  780. {{numeral(System.quotes.NEXA.price).format('$0,0.00[0000]')}}
  781. </small>
  782. </span>
  783. </div>
  784. </section>
  785. <section v-if="txOutputScriptBytes">
  786. <h2 class="text-xl text-yellow-900 font-medium">
  787. Script Bytes
  788. </h2>
  789. <h3 class="text-xl text-rose-500 font-medium font-mono">
  790. {{txOutputScriptBytes[index].value}}
  791. </h3>
  792. </section>
  793. <section v-if="txPubKeyScripts">
  794. <h2 class="text-2xl font-medium">
  795. PubKey Script / Template
  796. </h2>
  797. <h3 class="text-xl text-rose-500 font-medium font-mono">
  798. {{txPubKeyScripts[index].value}}
  799. </h3>
  800. </section>
  801. </section>
  802. <!-- <section v-if="txOutpointIndices" class="w-fit mt-2 px-3 py-1 bg-gradient-to-r from-yellow-50 to-yellow-100 border border-yellow-500 rounded shadow">
  803. <h2 v-if="chainid === 'NEXA'" class="text-2xl font-medium">
  804. Outpoint Index
  805. </h2>
  806. <h2 v-if="chainid === 'BCH'" class="text-2xl font-medium">
  807. Vout Index
  808. </h2>
  809. <h3 class="text-xl text-rose-500 font-medium font-mono">
  810. {{txOutpointIndices[index].value}}
  811. </h3>
  812. </section> -->
  813. <!-- <section v-if="txInputBlocks">
  814. <h2 class="text-2xl font-medium" @contextmenu.prevent="myHandler($event)">
  815. Authorization Block
  816. </h2>
  817. <div>
  818. <h3 class="text-xs text-rose-500 font-medium font-mono">
  819. {{txInputBlocks[index].value}}
  820. <small class="">[ {{txInputBlocks[index].value.length / 2}} bytes ]</small>
  821. </h3>
  822. <section class="mt-2 px-3 py-1 bg-yellow-100 border border-yellow-500 rounded">
  823. <h3 class="text-xl text-rose-500 font-medium font-mono">
  824. {{txSigBlocks[index].value}}
  825. <small class="text-xs">[ {{parseInt(txSigBlocks[index].value, 16)}} bytes ]</small>
  826. <small v-if="txSigBlocks[index].value === '41'" class="ml-2 text-xs">Schnorr Signature</small>
  827. <small v-else class="ml-2 text-xs">ECDSA Signature</small>
  828. </h3>
  829. <small class="text-xs text-rose-500 font-medium font-mono">
  830. {{txSignatures[index].abbr}}
  831. </small>
  832. <h3 class="text-sm text-yellow-700 font-medium">
  833. Sig Hash Type ⇒ {{txSignatures[index].value.slice(-2)}}
  834. <small v-if="txSignatures[index].value.slice(-2) === '41'" class="text-xs">
  835. [ ALL | FORKID ]
  836. </small>
  837. <small v-if="txSignatures[index].value.slice(-2) === 'c1'" class="text-xs">
  838. [ ALL | ANYONECANPAY | FORKID ]
  839. </small>
  840. </h3>
  841. <h3 class="text-sm text-rose-500 font-medium font-mono">
  842. {{txPubkeys[index].value}}
  843. </h3>
  844. <h3 class="text-sm text-rose-500 font-medium font-mono">
  845. {{txSequences[index].value}}
  846. <small class="text-muted text-xs">
  847. (<code class="mx-1 text-rose-500 font-medium">MAXINT - 1??</code>)
  848. </small>
  849. </h3>
  850. </section>
  851. </div>
  852. </section> -->
  853. </section>
  854. <!-- <section v-if="txOutpoint">
  855. <h2 class="text-2xl font-medium">
  856. Outpoint
  857. </h2>
  858. <h3 class="text-xl text-rose-500 font-medium font-mono">
  859. {{txOutpoint}}
  860. </h3>
  861. </section> -->
  862. <!-- <section v-if="txOutputCount.value" class="w-fit mt-2 px-3 py-1 bg-gradient-to-r from-yellow-50 to-yellow-100 border border-yellow-500 rounded shadow">
  863. <h2 class="text-2xl font-medium">
  864. Output Count
  865. </h2>
  866. <h3 class="text-xl text-rose-500 font-medium font-mono">
  867. {{txOutputCount.value}}
  868. </h3>
  869. </section> -->
  870. <!-- <section v-for="(output, index) of txOutputValues" :key="output.value" class="ml-10 px-3 py-3 bg-gradient-to-r from-gray-100 to-gray-200 border-2 border-gray-400 rounded shadow">
  871. <h2 class="text-xl text-yellow-900 font-medium">
  872. Output Value
  873. </h2>
  874. <h3 class="text-xl text-rose-500 font-medium font-mono">
  875. {{output.value}}
  876. </h3>
  877. <small class=" text-yellow-700">
  878. An <span class="font-medium">8-byte (64-bit)</span> number in Big-endian (BE) format.
  879. </small>
  880. <section class="w-fit my-1 px-3 py-1 text-yellow-400 bg-gradient-to-r from-yellow-800 to-yellow-700 border border-yellow-500 rounded shadow">
  881. <div v-if="output.satoshis" class="grid grid-cols-2 gap-2">
  882. <span class="font-mono text-yellow-100 font-medium text-right">
  883. {{output.satoshis}}
  884. </span>
  885. <span class="text-sm">
  886. satoshis
  887. </span>
  888. </div>
  889. <div v-if="output.bch" class="grid grid-cols-2 gap-2">
  890. <span class="font-mono text-yellow-100 font-medium text-right">
  891. {{output.bch}}
  892. </span>
  893. <span class="text-sm">
  894. BCH
  895. </span>
  896. </div>
  897. <div v-if="output.usd" class="grid grid-cols-2 gap-2">
  898. <span class="font-mono text-yellow-100 font-medium text-right">
  899. {{output.usd}}
  900. </span>
  901. <span class="text-sm">
  902. USD
  903. <small class="text-xs text-yellow-500">
  904. {{numeral(System.quotes.BCH.price).format('$0,0.00')}}
  905. </small>
  906. </span>
  907. </div>
  908. </section>
  909. <section v-if="txOutputScriptBytes">
  910. <h2 class="text-xl text-yellow-900 font-medium">
  911. Script Bytes
  912. </h2>
  913. <h3 class="text-xl text-rose-500 font-medium font-mono">
  914. {{txOutputScriptBytes[index].value}}
  915. </h3>
  916. </section>
  917. <section v-if="txPubKeyScripts">
  918. <h2 class="text-2xl font-medium">
  919. PubKey Script / Template
  920. </h2>
  921. <h3 class="text-xl text-rose-500 font-medium font-mono">
  922. {{txPubKeyScripts[index].value}}
  923. </h3>
  924. </section>
  925. </section> -->
  926. <!-- <section v-if="txLockTime.value" class="w-fit mt-2 px-3 py-1 bg-gradient-to-r from-yellow-50 to-yellow-100 border border-yellow-500 rounded shadow">
  927. <h2 class="text-2xl font-medium">
  928. Lock Time
  929. </h2>
  930. <h3 class="text-xl text-rose-500 font-medium font-mono">
  931. {{txLockTime.value}} [ {{txLockTime.decimal}} ]
  932. </h3>
  933. </section> -->
  934. </main>
  935. </template>