From 272c1ad8295bc5e4bb6809ab17474e9839b08377 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 17 Apr 2022 12:14:06 -0600 Subject: [PATCH 1/2] Parse P2PK and P2MS addresses --- client/src/components/SearchBar.svelte | 26 ++++++++++++++++++- .../src/components/TransactionOverlay.svelte | 12 ++++++--- client/src/utils/encodings.js | 18 +++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/client/src/components/SearchBar.svelte b/client/src/components/SearchBar.svelte index 3c9b541..869f8f6 100644 --- a/client/src/components/SearchBar.svelte +++ b/client/src/components/SearchBar.svelte @@ -4,7 +4,7 @@ import { fade } from 'svelte/transition' import { flip } from 'svelte/animate' import Icon from './Icon.svelte' 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 TxIcon from '../assets/icon/cil-arrow-circle-right.svg' import { fly } from 'svelte/transition' @@ -22,6 +22,10 @@ $: { } } +function clearInput () { + query = null +} + async function searchSubmit (e) { e.preventDefault() @@ -58,6 +62,22 @@ async function searchSubmit (e) { max-width: 600px; margin: 0 1em; + .clear-button { + position: absolute; + right: 0; + bottom: .3em; + margin: 0; + color: var(--palette-bad); + font-size: 1.2em; + cursor: pointer; + opacity: 1; + transition: opacity 300ms; + + &.disabled { + opacity: 0; + } + } + .input-icon { font-size: 24px; margin: 0 10px; @@ -130,6 +150,7 @@ async function searchSubmit (e) { margin: 0; color: var(--input-color); width: 100%; + padding-right: 1.5em; &.disabled { color: var(--palette-e); @@ -143,6 +164,9 @@ async function searchSubmit (e) {
+
+ +
-

{$detailTx.outputs.length} output{$detailTx.outputs.length > 1 ? 's' : ''} {#if $detailTx.fee != null}+ fee{/if}

+

{$detailTx.outputs.length} output{$detailTx.outputs.length > 1 ? 's' : ''} {#if $detailTx.fee}+ fee{/if}

{#each outputs as output}
clickItem(output)}>

{output.address.slice(0,-6)}{output.address.slice(-6)}

diff --git a/client/src/utils/encodings.js b/client/src/utils/encodings.js index ccd1b8b..d31d4e0 100644 --- a/client/src/utils/encodings.js +++ b/client/src/utils/encodings.js @@ -48,6 +48,24 @@ export function SPKToAddress (spk) { } else if (spk.startsWith('6a')) { // 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' } } From b5bcaf23774af8d66b14eb6c8b7e42e677eaa23e Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 18 Apr 2022 10:25:00 -0600 Subject: [PATCH 2/2] Support searching mempool txs, error handling --- client/src/components/SearchBar.svelte | 69 +++++++++++++++++-- client/src/components/SearchTab.svelte | 4 +- .../components/util/LoadingAnimation.svelte | 1 + client/src/controllers/TxController.js | 3 - client/src/utils/search.js | 56 +++++++++------ server/lib/router.ex | 5 +- 6 files changed, 104 insertions(+), 34 deletions(-) diff --git a/client/src/components/SearchBar.svelte b/client/src/components/SearchBar.svelte index 869f8f6..471ca46 100644 --- a/client/src/components/SearchBar.svelte +++ b/client/src/components/SearchBar.svelte @@ -7,12 +7,23 @@ import SearchIcon from '../assets/icon/cil-search.svg' import CrossIcon from '../assets/icon/cil-x-circle.svg' import AddressIcon from '../assets/icon/cil-wallet.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 { matchQuery, searchTx, searchBlock } from '../utils/search.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 matchedQuery +let errorMessage $: { if (query) { @@ -20,31 +31,48 @@ $: { } else { 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) { e.preventDefault() - if (matchedQuery) { + if (matchedQuery && matchedQuery.query !== 'address') { $loading++ + let searchErr switch(matchedQuery.query) { case 'txid': - await searchTx(matchedQuery.txid) + searchErr = await searchTx(matchedQuery.txid) break; case 'input': - await searchTx(matchedQuery.txid, matchedQuery.input, null) + searchErr = await searchTx(matchedQuery.txid, matchedQuery.input, null) break; case 'output': - await searchTx(matchedQuery.txid, null, matchedQuery.output) + searchErr = await searchTx(matchedQuery.txid, null, matchedQuery.output) break; } + if (searchErr != null) handleSearchError(searchErr) $loading-- + } else { + errorMessage = 'enter a transaction id, block hash or block height' } return false @@ -65,7 +93,7 @@ async function searchSubmit (e) { .clear-button { position: absolute; right: 0; - bottom: .3em; + bottom: .4em; margin: 0; color: var(--palette-bad); font-size: 1.2em; @@ -135,10 +163,28 @@ async function searchSubmit (e) { 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 { - .underline.active { + .search-input:active, .search-input:focus { + & ~ .underline.active { width: 100%; } } @@ -150,6 +196,7 @@ async function searchSubmit (e) { margin: 0; color: var(--input-color); width: 100%; + padding-left: 1.5em; padding-right: 1.5em; &.disabled { @@ -170,6 +217,14 @@ async function searchSubmit (e) {