add history error

This commit is contained in:
dni
2026-07-13 09:09:42 +02:00
parent d0bc63d114
commit 2941157658
5 changed files with 104 additions and 27 deletions
+63 -17
View File
@@ -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}")
+1
View File
@@ -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'
} }
+10
View File
@@ -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
+29 -10
View File
@@ -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>
+1
View File
@@ -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):