mirror of
https://github.com/Retropex/bitfeed.git
synced 2025-05-12 19:20:46 +02:00
Merge pull request #42 from bitfeed-project/address-types
Address types
This commit is contained in:
commit
30b7c8d42f
@ -4,15 +4,26 @@ import { fade } from 'svelte/transition'
|
|||||||
import { flip } from 'svelte/animate'
|
import { flip } from 'svelte/animate'
|
||||||
import Icon from './Icon.svelte'
|
import Icon from './Icon.svelte'
|
||||||
import SearchIcon from '../assets/icon/cil-search.svg'
|
import SearchIcon from '../assets/icon/cil-search.svg'
|
||||||
import CrossIcon from '../assets/icon/cil-x.svg'
|
import CrossIcon from '../assets/icon/cil-x-circle.svg'
|
||||||
import AddressIcon from '../assets/icon/cil-wallet.svg'
|
import AddressIcon from '../assets/icon/cil-wallet.svg'
|
||||||
import TxIcon from '../assets/icon/cil-arrow-circle-right.svg'
|
import TxIcon from '../assets/icon/cil-arrow-circle-right.svg'
|
||||||
|
import BlockIcon from '../assets/icon/grid-icon.svg'
|
||||||
import { fly } from 'svelte/transition'
|
import { fly } from 'svelte/transition'
|
||||||
import { matchQuery, searchTx, searchBlock } from '../utils/search.js'
|
import { matchQuery, searchTx, searchBlock } from '../utils/search.js'
|
||||||
import { selectedTx, detailTx, overlay, loading } from '../stores.js'
|
import { selectedTx, detailTx, overlay, loading } from '../stores.js'
|
||||||
|
|
||||||
|
const queryIcons = {
|
||||||
|
txid: TxIcon,
|
||||||
|
input: TxIcon,
|
||||||
|
output: TxIcon,
|
||||||
|
// address: AddressIcon,
|
||||||
|
blockhash: BlockIcon,
|
||||||
|
blockheight: BlockIcon,
|
||||||
|
}
|
||||||
|
|
||||||
let query
|
let query
|
||||||
let matchedQuery
|
let matchedQuery
|
||||||
|
let errorMessage
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (query) {
|
if (query) {
|
||||||
@ -20,27 +31,48 @@ $: {
|
|||||||
} else {
|
} else {
|
||||||
matchedQuery = null
|
matchedQuery = null
|
||||||
}
|
}
|
||||||
|
errorMessage = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearInput () {
|
||||||
|
query = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearchError (err) {
|
||||||
|
switch (err) {
|
||||||
|
case '404':
|
||||||
|
if (matchedQuery && matchedQuery.label) {
|
||||||
|
errorMessage = `${matchedQuery.label} not found`
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorMessage = 'server error'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchSubmit (e) {
|
async function searchSubmit (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (matchedQuery) {
|
if (matchedQuery && matchedQuery.query !== 'address') {
|
||||||
$loading++
|
$loading++
|
||||||
|
let searchErr
|
||||||
switch(matchedQuery.query) {
|
switch(matchedQuery.query) {
|
||||||
case 'txid':
|
case 'txid':
|
||||||
await searchTx(matchedQuery.txid)
|
searchErr = await searchTx(matchedQuery.txid)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'input':
|
case 'input':
|
||||||
await searchTx(matchedQuery.txid, matchedQuery.input, null)
|
searchErr = await searchTx(matchedQuery.txid, matchedQuery.input, null)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'output':
|
case 'output':
|
||||||
await searchTx(matchedQuery.txid, null, matchedQuery.output)
|
searchErr = await searchTx(matchedQuery.txid, null, matchedQuery.output)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (searchErr != null) handleSearchError(searchErr)
|
||||||
$loading--
|
$loading--
|
||||||
|
} else {
|
||||||
|
errorMessage = 'enter a transaction id, block hash or block height'
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@ -58,6 +90,22 @@ async function searchSubmit (e) {
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
margin: 0 1em;
|
margin: 0 1em;
|
||||||
|
|
||||||
|
.clear-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: .4em;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--palette-bad);
|
||||||
|
font-size: 1.2em;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 300ms;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.input-icon {
|
.input-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
@ -115,10 +163,28 @@ async function searchSubmit (e) {
|
|||||||
transition: width 300ms;
|
transition: width 300ms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-msg {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: var(--palette-bad);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-icon.query-type {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: .4em;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--palette-x);
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover, &:active, &:focus {
|
.search-input:active, .search-input:focus {
|
||||||
.underline.active {
|
& ~ .underline.active {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,6 +196,8 @@ async function searchSubmit (e) {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--input-color);
|
color: var(--input-color);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding-left: 1.5em;
|
||||||
|
padding-right: 1.5em;
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
color: var(--palette-e);
|
color: var(--palette-e);
|
||||||
@ -143,9 +211,20 @@ async function searchSubmit (e) {
|
|||||||
<div class="input-wrapper" transition:fly={{ y: -25 }}>
|
<div class="input-wrapper" transition:fly={{ y: -25 }}>
|
||||||
<form class="search-form" action="" on:submit={searchSubmit}>
|
<form class="search-form" action="" on:submit={searchSubmit}>
|
||||||
<input class="search-input" type="text" bind:value={query} placeholder="Enter a txid">
|
<input class="search-input" type="text" bind:value={query} placeholder="Enter a txid">
|
||||||
|
<div class="clear-button" class:disabled={query == null || query === ''} on:click={clearInput} title="Clear">
|
||||||
|
<Icon icon={CrossIcon}/>
|
||||||
|
</div>
|
||||||
<div class="underline" />
|
<div class="underline" />
|
||||||
<div class="underline active" />
|
<div class="underline active" />
|
||||||
<button type="submit" class="search-submit" />
|
<button type="submit" class="search-submit" />
|
||||||
|
{#if matchedQuery && matchedQuery.query && queryIcons[matchedQuery.query]}
|
||||||
|
<div class="input-icon query-type" transition:fade={{ duration: 300 }} title={matchedQuery.label}>
|
||||||
|
<Icon icon={queryIcons[matchedQuery.query]} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if errorMessage }
|
||||||
|
<p class="error-msg" transition:fade={{ duration: 300 }}>{ errorMessage }</p>
|
||||||
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
<div class="input-icon search icon-button" on:click={searchSubmit} title="Search">
|
<div class="input-icon search icon-button" on:click={searchSubmit} title="Search">
|
||||||
<Icon icon={SearchIcon}/>
|
<Icon icon={SearchIcon}/>
|
||||||
|
@ -42,6 +42,7 @@ setNextColor()
|
|||||||
$: {
|
$: {
|
||||||
if ($newHighlightQuery) {
|
if ($newHighlightQuery) {
|
||||||
matchedQuery = matchQuery($newHighlightQuery)
|
matchedQuery = matchQuery($newHighlightQuery)
|
||||||
|
if (matchedQuery && (matchedQuery.query === 'blockhash' || matchedQuery.query === 'blockheight')) matchedQuery = null
|
||||||
if (matchedQuery) {
|
if (matchedQuery) {
|
||||||
matchedQuery.colorIndex = queryColorIndex
|
matchedQuery.colorIndex = queryColorIndex
|
||||||
matchedQuery.color = highlightColors[queryColorIndex]
|
matchedQuery.color = highlightColors[queryColorIndex]
|
||||||
@ -61,6 +62,7 @@ $: {
|
|||||||
$: {
|
$: {
|
||||||
if (query) {
|
if (query) {
|
||||||
matchedQuery = matchQuery(query.trim())
|
matchedQuery = matchQuery(query.trim())
|
||||||
|
if (matchedQuery && (matchedQuery.query === 'blockhash' || matchedQuery.query === 'blockheight')) matchedQuery = null
|
||||||
if (matchedQuery) {
|
if (matchedQuery) {
|
||||||
matchedQuery.colorIndex = queryColorIndex
|
matchedQuery.colorIndex = queryColorIndex
|
||||||
matchedQuery.color = highlightColors[queryColorIndex]
|
matchedQuery.color = highlightColors[queryColorIndex]
|
||||||
@ -113,7 +115,7 @@ function clearUsedColor (colorIndex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function add () {
|
async function add () {
|
||||||
if (matchedQuery && !$highlightingFull) {
|
if (matchedQuery && matchedQuery.query !== 'blockhash' && matchedQuery.query !== 'blockheight' && !$highlightingFull) {
|
||||||
watchlist.push({
|
watchlist.push({
|
||||||
...matchedQuery
|
...matchedQuery
|
||||||
})
|
})
|
||||||
|
@ -11,6 +11,7 @@ import { searchTx } from '../utils/search.js'
|
|||||||
|
|
||||||
function onClose () {
|
function onClose () {
|
||||||
$detailTx = null
|
$detailTx = null
|
||||||
|
$highlightInOut = null
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatBTC (sats) {
|
function formatBTC (sats) {
|
||||||
@ -69,9 +70,12 @@ function expandAddresses(items, truncate) {
|
|||||||
if (item.script_pub_key) {
|
if (item.script_pub_key) {
|
||||||
address = SPKToAddress(item.script_pub_key) || "unknown"
|
address = SPKToAddress(item.script_pub_key) || "unknown"
|
||||||
if (address === 'OP_RETURN') {
|
if (address === 'OP_RETURN') {
|
||||||
title = item.script_pub_key.substring(2).match(/../g).reduce((parsed, hexChar) => {
|
title = item.script_pub_key.substring(4).match(/../g).reduce((parsed, hexChar) => {
|
||||||
return parsed + String.fromCharCode(parseInt(hexChar, 16))
|
return parsed + String.fromCharCode(parseInt(hexChar, 16))
|
||||||
}, "")
|
}, "")
|
||||||
|
} else if (address === 'P2PK') {
|
||||||
|
if (item.script_pub_key.length === 70) title = 'compressed pubkey: ' + item.script_pub_key.substring(2,68)
|
||||||
|
else title = 'pubkey: ' + item.script_pub_key.substring(2,132)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@ -113,7 +117,7 @@ $: {
|
|||||||
}
|
}
|
||||||
} else inputs = []
|
} else inputs = []
|
||||||
if ($detailTx && $detailTx.outputs) {
|
if ($detailTx && $detailTx.outputs) {
|
||||||
if ($detailTx.isCoinbase || !$detailTx.is_inflated || $detailTx.fee == null) {
|
if ($detailTx.isCoinbase || !$detailTx.is_inflated || !$detailTx.fee) {
|
||||||
outputs = expandAddresses($detailTx.outputs, truncate)
|
outputs = expandAddresses($detailTx.outputs, truncate)
|
||||||
} else {
|
} else {
|
||||||
outputs = [{address: 'fee', value: $detailTx.fee, fee: true}, ...expandAddresses($detailTx.outputs, truncate)]
|
outputs = [{address: 'fee', value: $detailTx.fee, fee: true}, ...expandAddresses($detailTx.outputs, truncate)]
|
||||||
@ -580,7 +584,7 @@ async function clickItem (item) {
|
|||||||
<p class="header">{$detailTx.inputs.length} input{$detailTx.inputs.length > 1 ? 's' : ''}</p>
|
<p class="header">{$detailTx.inputs.length} input{$detailTx.inputs.length > 1 ? 's' : ''}</p>
|
||||||
{#each inputs as input}
|
{#each inputs as input}
|
||||||
<div class="entry clickable" on:click={() => clickItem(input)}>
|
<div class="entry clickable" on:click={() => clickItem(input)}>
|
||||||
<p class="address" title={input.address}><span class="truncatable">{input.address.slice(0,-6)}</span><span class="suffix">{input.address.slice(-6)}</span></p>
|
<p class="address" title={input.title || input.address}><span class="truncatable">{input.address.slice(0,-6)}</span><span class="suffix">{input.address.slice(-6)}</span></p>
|
||||||
<p class="amount">{ input.value == null ? '???' : formatBTC(input.value) }</p>
|
<p class="amount">{ input.value == null ? '???' : formatBTC(input.value) }</p>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@ -613,7 +617,7 @@ async function clickItem (item) {
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="column outputs">
|
<div class="column outputs">
|
||||||
<p class="header">{$detailTx.outputs.length} output{$detailTx.outputs.length > 1 ? 's' : ''} {#if $detailTx.fee != null}+ fee{/if}</p>
|
<p class="header">{$detailTx.outputs.length} output{$detailTx.outputs.length > 1 ? 's' : ''} {#if $detailTx.fee}+ fee{/if}</p>
|
||||||
{#each outputs as output}
|
{#each outputs as output}
|
||||||
<div class="entry" class:clickable={output.rest || output.spend} class:unspent={!output.spend && !output.fee} class:highlight={highlight.out != null && highlight.out === output.index} on:click={() => clickItem(output)}>
|
<div class="entry" class:clickable={output.rest || output.spend} class:unspent={!output.spend && !output.fee} class:highlight={highlight.out != null && highlight.out === output.index} on:click={() => clickItem(output)}>
|
||||||
<p class="address" title={output.title || output.address}><span class="truncatable">{output.address.slice(0,-6)}</span><span class="suffix">{output.address.slice(-6)}</span></p>
|
<p class="address" title={output.title || output.address}><span class="truncatable">{output.address.slice(0,-6)}</span><span class="suffix">{output.address.slice(-6)}</span></p>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: solid 2px var(--palette-x);
|
border: solid 2px var(--palette-x);
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
.sizer {
|
.sizer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -106,7 +106,6 @@ export default class TxController {
|
|||||||
|
|
||||||
dropTx (txid) {
|
dropTx (txid) {
|
||||||
if (this.txs[txid] && this.poolScene.drop(txid)) {
|
if (this.txs[txid] && this.poolScene.drop(txid)) {
|
||||||
console.log('dropping tx', txid)
|
|
||||||
this.txs[txid].view.update({
|
this.txs[txid].view.update({
|
||||||
display: {
|
display: {
|
||||||
position: {
|
position: {
|
||||||
@ -123,8 +122,6 @@ export default class TxController {
|
|||||||
this.destroyTx(txid)
|
this.destroyTx(txid)
|
||||||
}, 2000)
|
}, 2000)
|
||||||
// this.poolScene.layoutAll()
|
// this.poolScene.layoutAll()
|
||||||
} else {
|
|
||||||
console.log('dropped unknown tx', txid)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,24 @@ export function SPKToAddress (spk) {
|
|||||||
} else if (spk.startsWith('6a')) {
|
} else if (spk.startsWith('6a')) {
|
||||||
// OP_RETURN
|
// OP_RETURN
|
||||||
return 'OP_RETURN'
|
return 'OP_RETURN'
|
||||||
|
} else if (spk.length == 134 && spk.startsWith('41') && spk.endsWith('ac')) {
|
||||||
|
// uncompressed p2pk
|
||||||
|
return 'P2PK'
|
||||||
|
} else if (spk.length == 70 && spk.startsWith('21') && spk.endsWith('ac')) {
|
||||||
|
// compressed p2pk
|
||||||
|
return 'P2PK'
|
||||||
|
} else if (spk.endsWith('51ae') && spk.startsWith('51')) {
|
||||||
|
// possible p2ms (raw multisig)
|
||||||
|
return '1-of-1 P2MS'
|
||||||
|
} else if (spk.endsWith('52ae')) {
|
||||||
|
// possible p2ms (raw multisig)
|
||||||
|
if (spk.startsWith(51)) return '1-of-2 P2MS'
|
||||||
|
if (spk.startsWith(52)) return '2-of-2 P2MS'
|
||||||
|
} else if (spk.endsWith('53ae')) {
|
||||||
|
// possible p2ms (raw multisig)
|
||||||
|
if (spk.startsWith(51)) return '1-of-3 P2MS'
|
||||||
|
if (spk.startsWith(52)) return '2-of-3 P2MS'
|
||||||
|
if (spk.startsWith(53)) return '3-of-3 P2MS'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,20 +14,22 @@ function matchQuery (query) {
|
|||||||
const asInt = parseInt(q)
|
const asInt = parseInt(q)
|
||||||
// Remember to update the bounds in
|
// Remember to update the bounds in
|
||||||
if (!isNaN(asInt) && asInt >= 0 && `${asInt}` === q) {
|
if (!isNaN(asInt) && asInt >= 0 && `${asInt}` === q) {
|
||||||
return null /*{
|
return {
|
||||||
query: 'blockheight',
|
query: 'blockheight',
|
||||||
|
label: 'block height',
|
||||||
height: asInt,
|
height: asInt,
|
||||||
value: asInt
|
value: asInt
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looks like a block hash?
|
// Looks like a block hash?
|
||||||
if (/^0{8}[a-f0-9]{56}$/.test(q)) {
|
if (/^0{8}[a-f0-9]{56}$/.test(q)) {
|
||||||
return null /* {
|
return {
|
||||||
query: 'blockhash',
|
query: 'blockhash',
|
||||||
|
label: 'block hash',
|
||||||
hash: query,
|
hash: query,
|
||||||
value: query,
|
value: query,
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looks like a transaction input?
|
// Looks like a transaction input?
|
||||||
@ -35,6 +37,7 @@ function matchQuery (query) {
|
|||||||
const parts = q.split(':')
|
const parts = q.split(':')
|
||||||
return {
|
return {
|
||||||
query: 'input',
|
query: 'input',
|
||||||
|
label: 'transaction input',
|
||||||
txid: parts[1],
|
txid: parts[1],
|
||||||
output: parts[0],
|
output: parts[0],
|
||||||
value: q
|
value: q
|
||||||
@ -46,6 +49,7 @@ function matchQuery (query) {
|
|||||||
const parts = q.split(':')
|
const parts = q.split(':')
|
||||||
return {
|
return {
|
||||||
query: 'output',
|
query: 'output',
|
||||||
|
label: 'transaction output',
|
||||||
txid: parts[0],
|
txid: parts[0],
|
||||||
output: parts[1],
|
output: parts[1],
|
||||||
value: q
|
value: q
|
||||||
@ -56,6 +60,7 @@ function matchQuery (query) {
|
|||||||
if (/^[a-f0-9]{64}$/.test(q)) {
|
if (/^[a-f0-9]{64}$/.test(q)) {
|
||||||
return {
|
return {
|
||||||
query: 'txid',
|
query: 'txid',
|
||||||
|
label: 'transaction',
|
||||||
txid: q,
|
txid: q,
|
||||||
value: q
|
value: q
|
||||||
}
|
}
|
||||||
@ -72,6 +77,7 @@ function matchQuery (query) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
query: 'address',
|
query: 'address',
|
||||||
|
label: 'address',
|
||||||
encoding: 'base58',
|
encoding: 'base58',
|
||||||
addressType,
|
addressType,
|
||||||
address: query,
|
address: query,
|
||||||
@ -93,6 +99,7 @@ function matchQuery (query) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
query: 'address',
|
query: 'address',
|
||||||
|
label: 'address',
|
||||||
encoding: 'bech32',
|
encoding: 'bech32',
|
||||||
addressType,
|
addressType,
|
||||||
address: query,
|
address: query,
|
||||||
@ -108,30 +115,37 @@ export {matchQuery as matchQuery}
|
|||||||
|
|
||||||
async function fetchTx (txid) {
|
async function fetchTx (txid) {
|
||||||
if (!txid) return
|
if (!txid) return
|
||||||
try {
|
const response = await fetch(`${api.uri}/api/tx/${txid}`, {
|
||||||
const response = await fetch(`${api.uri}/api/tx/${txid}`, {
|
method: 'GET'
|
||||||
method: 'GET'
|
})
|
||||||
})
|
if (!response) throw new Error('null response')
|
||||||
|
if (response.status == 200) {
|
||||||
const result = await response.json()
|
const result = await response.json()
|
||||||
const txData = result.tx
|
const txData = result.tx
|
||||||
txData.block = { height: result.blockheight, hash: result.blockhash, time: result.time * 1000 }
|
if (result.blockheight != null && result.blockhash != null) {
|
||||||
|
txData.block = { height: result.blockheight, hash: result.blockhash, time: result.time * 1000 }
|
||||||
|
}
|
||||||
return new BitcoinTx(txData, null, (txData.inputs && txData.inputs[0] && txData.inputs[0].prev_txid === "0000000000000000000000000000000000000000000000000000000000000000"))
|
return new BitcoinTx(txData, null, (txData.inputs && txData.inputs[0] && txData.inputs[0].prev_txid === "0000000000000000000000000000000000000000000000000000000000000000"))
|
||||||
} catch (err) {
|
} else {
|
||||||
console.log("failed to fetch tx ", txid)
|
throw new Error(response.status)
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchTx(txid, input, output) {
|
export async function searchTx(txid, input, output) {
|
||||||
const searchResult = await fetchTx(txid)
|
try {
|
||||||
if (searchResult) {
|
const searchResult = await fetchTx(txid)
|
||||||
selectedTx.set(searchResult)
|
if (searchResult) {
|
||||||
detailTx.set(searchResult)
|
selectedTx.set(searchResult)
|
||||||
overlay.set('tx')
|
detailTx.set(searchResult)
|
||||||
if (input != null || output != null) highlightInOut.set({txid, input, output})
|
overlay.set('tx')
|
||||||
return true
|
if (input != null || output != null) highlightInOut.set({txid, input, output})
|
||||||
} else {
|
return null
|
||||||
return false
|
} else {
|
||||||
|
return '500'
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('error fetching tx ', err)
|
||||||
|
return err.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,8 +55,9 @@ defmodule BitcoinStream.Router do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp get_tx(txid) do
|
defp get_tx(txid) do
|
||||||
with {:ok, 200, %{"hex" => hex, "blockhash" => blockhash}} <- RPC.request(:rpc, "getrawtransaction", [txid, true]),
|
with {:ok, 200, verbosetx} <- RPC.request(:rpc, "getrawtransaction", [txid, true]),
|
||||||
{:ok, 200, %{"height" => height, "time" => time}} <- RPC.request(:rpc, "getblockheader", [blockhash, true]),
|
%{"hex" => hex, "blockhash" => blockhash} <- Map.merge(%{"blockhash" => nil}, verbosetx),
|
||||||
|
{:ok, 200, %{"height" => height, "time" => time}} <- (if blockhash != nil do RPC.request(:rpc, "getblockheader", [blockhash, true]) else {:ok, 200, %{"height" => nil, "time" => nil}} end),
|
||||||
rawtx <- Base.decode16!(hex, case: :lower),
|
rawtx <- Base.decode16!(hex, case: :lower),
|
||||||
{:ok, txn } <- BitcoinTx.decode(rawtx),
|
{:ok, txn } <- BitcoinTx.decode(rawtx),
|
||||||
inflated_txn <- BitcoinTx.inflate(txn, false),
|
inflated_txn <- BitcoinTx.inflate(txn, false),
|
||||||
|
Loading…
Reference in New Issue
Block a user