Skip to content

Commit 42e9b38

Browse files
authored
Merge pull request #386 from SomberNight/202607_chainsyncerror
protocol 1.7: revert adding chaintip to responses
2 parents 6efe92a + be877be commit 42e9b38

1 file changed

Lines changed: 13 additions & 18 deletions

File tree

src/electrumx/server/session.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,6 @@ class BlockRef:
219219
height: int
220220
hash_rev: bytes
221221

222-
def to_protocol_json(self) -> tuple[int, str]:
223-
hash_hum = hash_to_hex_str(self.hash_rev)
224-
compressed_hash_hum = hash_hum.lstrip("0")
225-
return self.height, compressed_hash_hum
226-
227222

228223
@dataclass(slots=True)
229224
class SessionGroup:
@@ -892,7 +887,7 @@ def extra_cost(self, session: 'SessionBase') -> float:
892887

893888
def get_block_ref_for_chaintip(self) -> BlockRef:
894889
"""Returns a BlockRef for the current chaintip of the DB.
895-
Raises ChainSyncError if the DB height is not up-to-date with the daemon height.
890+
Raises ChainSyncError if the mempool is not up-to-date with the DB.
896891
"""
897892
# daemon_height = self.daemon.cached_height()
898893
bp_height = self.bp.height
@@ -909,9 +904,9 @@ def get_block_ref_for_chaintip(self) -> BlockRef:
909904
# the few that also query bitcoind need to implement their own logic to make sure
910905
# the state they present is consistent with e-x's current db state.
911906
if not (bp_height == db_height == notif_height):
912-
raise ChainSyncError(message="DB is lagging behind daemon")
907+
raise ChainSyncError(message="DB/mempool still catching up with each other")
913908
if not (bp_tip == db_tip == notif_tip):
914-
raise ChainSyncError(message="DB is lagging behind daemon")
909+
raise ChainSyncError(message="DB/mempool still catching up with each other")
915910
assert db_height >= 0
916911
assert db_tip is not None
917912
return BlockRef(
@@ -1035,7 +1030,7 @@ async def limited_history(self, hashX: bytes) -> tuple[Sequence[tuple[bytes, int
10351030
History is a sorted list of (txid_rev, height) tuples, or an RPCError.'''
10361031
# History DoS limit. Each element of history is about 99 bytes when encoded
10371032
# as JSON.
1038-
max_byte_size_of_history_list = self.env.max_send - 100 # deduct a bit for overhead ("chaintip" in proto 1.7, etc)
1033+
max_byte_size_of_history_list = self.env.max_send - 100 # deduct a bit for overhead
10391034
limit = max_byte_size_of_history_list // 99
10401035
assert limit > 0
10411036
cost = 0.1
@@ -1505,12 +1500,14 @@ async def _notify_inner(
15051500
method = 'blockchain.outpoint.subscribe'
15061501
txo_to_status = {} # type: dict[TxOutpoint, TXOSpendStatus]
15071502
for prevout in touched_outpoints:
1503+
# TODO taskgroup?
15081504
txo_to_status[prevout] = await self.txoutpoint_status_for_notif(*prevout) # can raise RPCError
15091505

15101506
# Check mempool TXOs - the status is a function of the confirmed state of
15111507
# other transactions. (this is to detect if height changed from -1 to 0)
15121508
mempool_txoutpoint_statuses = self.mempool_txoutpoint_statuses.copy()
15131509
for prevout, old_status in mempool_txoutpoint_statuses.items():
1510+
# TODO taskgroup?
15141511
status = await self.txoutpoint_status_for_notif(*prevout) # can raise RPCError
15151512
if status != old_status:
15161513
txo_to_status[prevout] = status
@@ -1781,10 +1778,14 @@ async def confirmed_and_unconfirmed_history(self, hashX: bytes) -> list[dict[str
17811778
for txid_rev, height in history]
17821779
unconf = await self.unconfirmed_history(hashX)
17831780
# note: the same tx could appear both in conf and unconf, if it was *just* mined.
1784-
# For protocol 1.7, this race is eliminated due to @retry_on_chain_sync_error(check_chaintip=True),
1785-
# which ensures (self.notified_height == db.db_height).
1786-
# Even for older protocol, _history_cache makes this race extremely unlikely: it could only happen
1781+
# - more complicated example: tx1 could have been just mined, while
1782+
# our not-caught-up-mempool still contains a conflicting tx2.
1783+
# we should avoid sending an inconsistent response...
1784+
# _history_cache makes this race unlikely: it could only happen
17871785
# if we *also* get a cache-miss (but the cache is only cleared when notified_height is updated).
1786+
# Conceptually the same issue affects other methods too, e.g. get_balance:
1787+
# in general the DB and the mempool might be out-of-sync with each other.
1788+
# This issue is eliminated if the caller is wrapped in @retry_on_chain_sync_error(check_chaintip=True).
17881789
return conf + unconf
17891790

17901791
@retry_on_chain_sync_error(check_chaintip=True)
@@ -1824,7 +1825,6 @@ async def phandle_scriptpubkey_get_balance(self, spk: str | Any) -> dict[str, An
18241825
scripthash = spk_to_scripthash(spk)
18251826
hashX = scripthash_to_hashX(scripthash)
18261827
d = await self.get_balance(hashX)
1827-
d["chaintip"] = self.session_mgr.get_block_ref_for_chaintip().to_protocol_json()
18281828
return d
18291829

18301830
@retry_on_chain_sync_error(check_chaintip=True)
@@ -1833,7 +1833,6 @@ async def phandle_scriptpubkey_get_history(self, spk: str) -> dict[str, Any]:
18331833
hashX = scripthash_to_hashX(scripthash)
18341834
d = dict()
18351835
d["history"] = await self.confirmed_and_unconfirmed_history(hashX)
1836-
d["chaintip"] = self.session_mgr.get_block_ref_for_chaintip().to_protocol_json()
18371836
return d
18381837

18391838
@retry_on_chain_sync_error(check_chaintip=True)
@@ -1842,7 +1841,6 @@ async def phandle_scriptpubkey_get_mempool(self, spk: str) -> dict[str, Any]:
18421841
hashX = scripthash_to_hashX(scripthash)
18431842
d = dict()
18441843
d["history"] = await self.unconfirmed_history(hashX)
1845-
d["chaintip"] = self.session_mgr.get_block_ref_for_chaintip().to_protocol_json()
18461844
return d
18471845

18481846
@retry_on_chain_sync_error(check_chaintip=True)
@@ -1851,7 +1849,6 @@ async def phandle_scriptpubkey_listunspent(self, spk: str) -> dict[str, Any]:
18511849
hashX = scripthash_to_hashX(scripthash)
18521850
d = dict()
18531851
d["utxos"] = await self.hashX_listunspent(hashX)
1854-
d["chaintip"] = self.session_mgr.get_block_ref_for_chaintip().to_protocol_json()
18551852
return d
18561853

18571854
@retry_on_chain_sync_error(check_chaintip=True)
@@ -1879,7 +1876,6 @@ async def phandle_txoutpoint_get_status(self, tx_hash: str | Any, txout_idx: int
18791876
if max(spend_status.funder_height or 0, spend_status.spender_height or 0) > db_chaintip.height:
18801877
raise ChainSyncError(message="DB is lagging behind daemon")
18811878
d = self._convert_txospendstatus_to_protocol_dict(spend_status)
1882-
d["chaintip"] = db_chaintip.to_protocol_json()
18831879
return d
18841880

18851881
@retry_on_chain_sync_error(check_chaintip=True)
@@ -1901,7 +1897,6 @@ async def phandle_txoutpoint_subscribe(self, tx_hash: str | Any, txout_idx: int
19011897
# sub to outpoint
19021898
self.txoutpoint_subs.add((txid_rev, txout_idx))
19031899
d = self._convert_txospendstatus_to_protocol_dict(spend_status)
1904-
d["chaintip"] = db_chaintip.to_protocol_json()
19051900
return d
19061901

19071902
async def phandle_txoutpoint_unsubscribe(self, tx_hash: str | Any, txout_idx: int | Any) -> bool:

0 commit comments

Comments
 (0)