add history error
This commit is contained in:
@@ -63,15 +63,31 @@ async def _tx_status(
|
|||||||
c: ElectrumClient, txid: str, scripthash: str | None
|
c: ElectrumClient, txid: str, scripthash: str | None
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
if scripthash:
|
if scripthash:
|
||||||
history: list[HistoryEntry] = await c.get_history(scripthash)
|
try:
|
||||||
for entry in history:
|
history: list[HistoryEntry] = await c.get_history(scripthash)
|
||||||
if entry.tx_hash == txid:
|
for entry in history:
|
||||||
return {
|
if entry.tx_hash == txid:
|
||||||
"txid": txid,
|
return {
|
||||||
"confirmed": entry.height > 0,
|
"txid": txid,
|
||||||
"height": entry.height if entry.height > 0 else None,
|
"confirmed": entry.height > 0,
|
||||||
"fee": entry.fee,
|
"height": entry.height if entry.height > 0 else None,
|
||||||
}
|
"fee": entry.fee,
|
||||||
|
}
|
||||||
|
except ElectrumError:
|
||||||
|
# History too large; check mempool to determine confirmation status.
|
||||||
|
try:
|
||||||
|
mempool = await c.get_mempool(scripthash)
|
||||||
|
for entry in mempool:
|
||||||
|
if entry.tx_hash == txid:
|
||||||
|
return {
|
||||||
|
"txid": txid,
|
||||||
|
"confirmed": False,
|
||||||
|
"height": None,
|
||||||
|
"fee": entry.fee,
|
||||||
|
}
|
||||||
|
return {"txid": txid, "confirmed": True, "height": None, "fee": None}
|
||||||
|
except ElectrumError:
|
||||||
|
pass
|
||||||
return {"txid": txid, "confirmed": False, "height": None, "fee": None}
|
return {"txid": txid, "confirmed": False, "height": None, "fee": None}
|
||||||
|
|
||||||
|
|
||||||
@@ -145,11 +161,22 @@ async def api_address(address: str) -> AddressResponse:
|
|||||||
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=str(e)) from e
|
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=str(e)) from e
|
||||||
try:
|
try:
|
||||||
async with _client() as c:
|
async with _client() as c:
|
||||||
balance, history = await asyncio.gather(
|
balance_res, history_res = await asyncio.gather(
|
||||||
c.get_balance(scripthash),
|
c.get_balance(scripthash),
|
||||||
c.get_history(scripthash),
|
c.get_history(scripthash),
|
||||||
|
return_exceptions=True,
|
||||||
)
|
)
|
||||||
return AddressResponse(balance=balance, history=history)
|
if isinstance(balance_res, Exception):
|
||||||
|
raise HTTPException(
|
||||||
|
HTTPStatus.SERVICE_UNAVAILABLE, detail=str(balance_res)
|
||||||
|
)
|
||||||
|
history = [] if isinstance(history_res, Exception) else history_res
|
||||||
|
history_error = str(history_res) if isinstance(history_res, Exception) else None
|
||||||
|
return AddressResponse(
|
||||||
|
balance=balance_res, history=history, history_error=history_error
|
||||||
|
)
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
except ElectrumError as e:
|
except ElectrumError as e:
|
||||||
raise HTTPException(HTTPStatus.SERVICE_UNAVAILABLE, detail=str(e)) from e
|
raise HTTPException(HTTPStatus.SERVICE_UNAVAILABLE, detail=str(e)) from e
|
||||||
|
|
||||||
@@ -200,19 +227,38 @@ async def ws_address(websocket: WebSocket, address: str) -> None:
|
|||||||
try:
|
try:
|
||||||
async with _client() as c:
|
async with _client() as c:
|
||||||
await c.subscribe_scripthash(scripthash)
|
await c.subscribe_scripthash(scripthash)
|
||||||
balance, history = await asyncio.gather(
|
balance_res, history_res = await asyncio.gather(
|
||||||
c.get_balance(scripthash), c.get_history(scripthash)
|
c.get_balance(scripthash),
|
||||||
|
c.get_history(scripthash),
|
||||||
|
return_exceptions=True,
|
||||||
|
)
|
||||||
|
if isinstance(balance_res, Exception):
|
||||||
|
await websocket.send_json({"error": str(balance_res)})
|
||||||
|
return
|
||||||
|
history = [] if isinstance(history_res, Exception) else history_res
|
||||||
|
history_error = (
|
||||||
|
str(history_res) if isinstance(history_res, Exception) else None
|
||||||
|
)
|
||||||
|
resp = AddressResponse(
|
||||||
|
balance=balance_res, history=history, history_error=history_error
|
||||||
)
|
)
|
||||||
resp = AddressResponse(balance=balance, history=history)
|
|
||||||
await websocket.send_json(resp.dict())
|
await websocket.send_json(resp.dict())
|
||||||
|
|
||||||
async def on_address_change(params: list[Any]) -> None:
|
async def on_address_change(params: list[Any]) -> None:
|
||||||
try:
|
try:
|
||||||
bal, hist = await asyncio.gather(
|
bal_r, hist_r = await asyncio.gather(
|
||||||
c.get_balance(scripthash), c.get_history(scripthash)
|
c.get_balance(scripthash),
|
||||||
|
c.get_history(scripthash),
|
||||||
|
return_exceptions=True,
|
||||||
)
|
)
|
||||||
|
if isinstance(bal_r, Exception):
|
||||||
|
return
|
||||||
|
hist = [] if isinstance(hist_r, Exception) else hist_r
|
||||||
|
h_err = str(hist_r) if isinstance(hist_r, Exception) else None
|
||||||
await websocket.send_json(
|
await websocket.send_json(
|
||||||
AddressResponse(balance=bal, history=hist).dict()
|
AddressResponse(
|
||||||
|
balance=bal_r, history=hist, history_error=h_err
|
||||||
|
).dict()
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"ws_address send error: {e}")
|
logger.debug(f"ws_address send error: {e}")
|
||||||
|
|||||||
@@ -870,5 +870,6 @@ window.localisation.en = {
|
|||||||
confirmed: 'Confirmed',
|
confirmed: 'Confirmed',
|
||||||
unconfirmed: 'Unconfirmed',
|
unconfirmed: 'Unconfirmed',
|
||||||
no_transactions: 'No transactions found',
|
no_transactions: 'No transactions found',
|
||||||
|
history_unavailable: 'Transaction history unavailable (address has too many transactions)',
|
||||||
address: 'Address'
|
address: 'Address'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,16 @@ window.PageBlockExplorer = {
|
|||||||
this.fees = r.data
|
this.fees = r.data
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
|
clearResult() {
|
||||||
|
this.txResult = null
|
||||||
|
this.txStatus = null
|
||||||
|
this.addressResult = null
|
||||||
|
this.query = ''
|
||||||
|
if (this._searchWs) {
|
||||||
|
this._searchWs.close()
|
||||||
|
this._searchWs = null
|
||||||
|
}
|
||||||
|
},
|
||||||
async search() {
|
async search() {
|
||||||
const q = this.query.trim()
|
const q = this.query.trim()
|
||||||
if (!q) return
|
if (!q) return
|
||||||
|
|||||||
@@ -120,14 +120,17 @@
|
|||||||
<!-- Transaction result -->
|
<!-- Transaction result -->
|
||||||
<q-card v-if="txResult">
|
<q-card v-if="txResult">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="row items-center q-mb-sm q-gutter-sm">
|
<div class="row items-center justify-between q-mb-sm">
|
||||||
<div class="text-subtitle1" v-text="$t('transaction')"></div>
|
<div class="row items-center q-gutter-sm">
|
||||||
<q-badge
|
<div class="text-subtitle1" v-text="$t('transaction')"></div>
|
||||||
v-if="txStatus"
|
<q-badge
|
||||||
:color="txStatus.confirmed ? 'positive' : 'orange'"
|
v-if="txStatus"
|
||||||
:label="txStatus.confirmed ? $t('confirmed') : $t('unconfirmed')"
|
:color="txStatus.confirmed ? 'positive' : 'orange'"
|
||||||
></q-badge>
|
:label="txStatus.confirmed ? $t('confirmed') : $t('unconfirmed')"
|
||||||
<q-spinner v-if="!txStatus" size="1em" color="grey" />
|
></q-badge>
|
||||||
|
<q-spinner v-if="!txStatus" size="1em" color="grey" />
|
||||||
|
</div>
|
||||||
|
<q-btn flat round dense icon="close" @click="clearResult" />
|
||||||
</div>
|
</div>
|
||||||
<div class="q-mb-sm">
|
<div class="q-mb-sm">
|
||||||
<span class="text-caption text-grey">txid: </span>
|
<span class="text-caption text-grey">txid: </span>
|
||||||
@@ -200,6 +203,14 @@
|
|||||||
v-text="vout.scriptPubKey.address"
|
v-text="vout.scriptPubKey.address"
|
||||||
></a>
|
></a>
|
||||||
</template>
|
</template>
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
vout.scriptPubKey &&
|
||||||
|
vout.scriptPubKey.type === 'nulldata'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span class="text-grey">OP_RETURN</span>
|
||||||
|
</template>
|
||||||
<template v-else-if="vout.scriptPubKey">
|
<template v-else-if="vout.scriptPubKey">
|
||||||
<span v-text="vout.scriptPubKey.type"></span>
|
<span v-text="vout.scriptPubKey.type"></span>
|
||||||
</template>
|
</template>
|
||||||
@@ -218,7 +229,10 @@
|
|||||||
<!-- Address result -->
|
<!-- Address result -->
|
||||||
<q-card v-if="addressResult">
|
<q-card v-if="addressResult">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="text-subtitle1 q-mb-xs" v-text="$t('address')"></div>
|
<div class="row items-center justify-between q-mb-xs">
|
||||||
|
<div class="text-subtitle1" v-text="$t('address')"></div>
|
||||||
|
<q-btn flat round dense icon="close" @click="clearResult" />
|
||||||
|
</div>
|
||||||
<div class="text-caption q-mb-sm">
|
<div class="text-caption q-mb-sm">
|
||||||
<code class="be-wrap" v-text="currentAddress"></code>
|
<code class="be-wrap" v-text="currentAddress"></code>
|
||||||
</div>
|
</div>
|
||||||
@@ -286,7 +300,12 @@
|
|||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
<div
|
<div
|
||||||
v-if="addressResult.history.length === 0"
|
v-if="addressResult.history_error"
|
||||||
|
class="text-warning q-mt-sm text-caption"
|
||||||
|
v-text="$t('history_unavailable')"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-else-if="addressResult.history.length === 0"
|
||||||
class="text-grey q-mt-sm"
|
class="text-grey q-mt-sm"
|
||||||
v-text="$t('no_transactions')"
|
v-text="$t('no_transactions')"
|
||||||
></div>
|
></div>
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ class FeeResponse(BaseModel):
|
|||||||
class AddressResponse(BaseModel):
|
class AddressResponse(BaseModel):
|
||||||
balance: Balance
|
balance: Balance
|
||||||
history: list[HistoryEntry]
|
history: list[HistoryEntry]
|
||||||
|
history_error: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class BlockInfo(BaseModel):
|
class BlockInfo(BaseModel):
|
||||||
|
|||||||
Reference in New Issue
Block a user