Skip to content

Commit 2fa0730

Browse files
committed
Merge branch '202510_protocol_1_6'
This implements the remainder of the protocol 1.6 changes, and it bumps PROTOCOL_MAX to "1.6". Some of protocol 1.6 was merged previously, see e.g.: - #288 - #302 - kyuupichan/electrumx#1001 - #325 ref spesmilo/electrum-protocol#6 ref #317
2 parents 04d517a + 8b00d24 commit 2fa0730

2 files changed

Lines changed: 64 additions & 22 deletions

File tree

src/electrumx/server/mempool.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,15 @@ async def potential_spends(self, hashX):
416416
return result
417417

418418
async def transaction_summaries(self, hashX):
419-
'''Return a list of MemPoolTxSummary objects for the hashX.'''
419+
'''Return a list of MemPoolTxSummary objects for the hashX,
420+
sorted as expected by protocol methods.
421+
'''
420422
result = []
421423
for tx_hash in self.hashXs.get(hashX, ()):
422424
tx = self.txs[tx_hash]
423425
has_ui = any(hash in self.txs for hash, idx in tx.prevouts)
424426
result.append(MemPoolTxSummary(tx_hash, tx.fee, has_ui))
427+
result.sort(key=lambda x: (x.has_unconfirmed_inputs, x.hash[::-1]))
425428
return result
426429

427430
async def unordered_UTXOs(self, hashX):

src/electrumx/server/session.py

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,7 @@ class ElectrumX(SessionBase):
10671067

10681068
PROTOCOL_MIN = (1, 4)
10691069
# consider bumping Coin.MIN_REQUIRED_DAEMON_VERSION too when releasing a new protocol version
1070-
PROTOCOL_MAX = (1, 4, 3)
1070+
PROTOCOL_MAX = (1, 6, 0)
10711071

10721072
def __init__(self, *args, **kwargs):
10731073
super().__init__(*args, **kwargs)
@@ -1208,7 +1208,7 @@ async def address_status(self, hashX):
12081208
12091209
Status is a hex string, but must be None if there is no history.
12101210
'''
1211-
# Note history is ordered and mempool unordered in electrum-server
1211+
# Note both confirmed history and mempool history are ordered
12121212
# For mempool, height is -1 if it has unconfirmed inputs, otherwise 0
12131213
db_history, cost = await self.session_mgr.limited_history(hashX)
12141214
mempool = await self.mempool.transaction_summaries(hashX)
@@ -1278,7 +1278,7 @@ async def scripthash_get_balance(self, scripthash):
12781278
return await self.get_balance(hashX)
12791279

12801280
async def unconfirmed_history(self, hashX):
1281-
# Note unconfirmed history is unordered in electrum-server
1281+
# Note both confirmed history and mempool history are ordered
12821282
# height is -1 if it has unconfirmed inputs, otherwise 0
12831283
result = [{'tx_hash': hash_to_hex_str(tx.hash),
12841284
'height': -tx.has_unconfirmed_inputs,
@@ -1288,7 +1288,7 @@ async def unconfirmed_history(self, hashX):
12881288
return result
12891289

12901290
async def confirmed_and_unconfirmed_history(self, hashX):
1291-
# Note history is ordered but unconfirmed is unordered in e-s
1291+
# Note both confirmed history and mempool history are ordered
12921292
history, cost = await self.session_mgr.limited_history(hashX)
12931293
self.bump_cost(cost)
12941294
conf = [{'tx_hash': hash_to_hex_str(tx_hash), 'height': height}
@@ -1357,6 +1357,8 @@ async def block_headers(self, start_height, count, cp_height=0):
13571357
start_height and count must be non-negative integers. At most
13581358
MAX_CHUNK_SIZE headers will be returned.
13591359
'''
1360+
if self.protocol_tuple >= (1, 6):
1361+
return await self.block_headers_array(start_height, count, cp_height)
13601362
start_height = non_negative_integer(start_height)
13611363
count = non_negative_integer(count)
13621364
cp_height = non_negative_integer(cp_height)
@@ -1373,6 +1375,38 @@ async def block_headers(self, start_height, count, cp_height=0):
13731375
self.bump_cost(cost)
13741376
return result
13751377

1378+
async def block_headers_array(self, start_height, count, cp_height=0):
1379+
'''Return block headers in an array for the main chain;
1380+
starting at start_height.
1381+
start_height and count must be non-negative integers. At most
1382+
MAX_CHUNK_SIZE headers will be returned.
1383+
'''
1384+
start_height = non_negative_integer(start_height)
1385+
count = non_negative_integer(count)
1386+
cp_height = non_negative_integer(cp_height)
1387+
cost = count / 50
1388+
1389+
max_size = self.MAX_CHUNK_SIZE
1390+
count = min(count, max_size)
1391+
headers, count = await self.db.read_headers(start_height, count)
1392+
result = {'count': count, 'max': max_size, 'headers': []}
1393+
if count and cp_height:
1394+
cost += 1.0
1395+
last_height = start_height + count - 1
1396+
result.update(await self._merkle_proof(cp_height, last_height))
1397+
1398+
cursor = 0
1399+
height = 0
1400+
while cursor < len(headers):
1401+
next_cursor = self.db.header_offset(height + 1)
1402+
header = headers[cursor:next_cursor]
1403+
result['headers'].append(header.hex())
1404+
cursor = next_cursor
1405+
height += 1
1406+
1407+
self.bump_cost(cost)
1408+
return result
1409+
13761410
def is_tor(self):
13771411
'''Try to detect if the connection is to a tor hidden service we are
13781412
running.'''
@@ -1960,36 +1994,41 @@ async def block_header(self, height, cp_height=0):
19601994
return result
19611995

19621996
# Covered by a checkpoint; truncate AuxPoW data
1963-
result['header'] = self.truncate_auxpow(result['header'], height)
1997+
result['header'] = self.truncate_auxpow_single(result['header'])
19641998
return result
19651999

19662000
async def block_headers(self, start_height, count, cp_height=0):
1967-
result = await super().block_headers(start_height, count, cp_height)
1968-
19692001
# Older protocol versions don't truncate AuxPoW
19702002
if self.protocol_tuple < (1, 4, 1):
1971-
return result
2003+
return await super().block_headers(start_height, count, cp_height)
19722004

19732005
# Not covered by a checkpoint; return full AuxPoW data
19742006
if cp_height == 0:
1975-
return result
2007+
return await super().block_headers(start_height, count, cp_height)
2008+
2009+
result = await super().block_headers_array(start_height, count, cp_height)
19762010

19772011
# Covered by a checkpoint; truncate AuxPoW data
1978-
result['hex'] = self.truncate_auxpow(result['hex'], start_height)
1979-
return result
2012+
result['headers'] = self.truncate_auxpow_headers(result['headers'])
19802013

1981-
def truncate_auxpow(self, headers_full_hex, start_height):
1982-
height = start_height
1983-
headers_full = util.hex_to_bytes(headers_full_hex)
1984-
cursor = 0
1985-
headers = bytearray()
2014+
# Return headers in array form
2015+
if self.protocol_tuple >= (1, 6):
2016+
return result
19862017

1987-
while cursor < len(headers_full):
1988-
headers += headers_full[cursor:cursor+self.coin.TRUNCATED_HEADER_SIZE]
1989-
cursor += self.db.dynamic_header_len(height)
1990-
height += 1
2018+
# Return headers in concatenated form
2019+
result['hex'] = ''.join(result['headers'])
2020+
del result['headers']
2021+
return result
2022+
2023+
def truncate_auxpow_headers(self, headers):
2024+
result = []
2025+
for header in headers:
2026+
result.append(self.truncate_auxpow_single(header))
2027+
return result
19912028

1992-
return headers.hex()
2029+
def truncate_auxpow_single(self, header: str):
2030+
# 2 hex chars per byte
2031+
return header[:2*self.coin.TRUNCATED_HEADER_SIZE]
19932032

19942033

19952034
class NameIndexElectrumX(ElectrumX):

0 commit comments

Comments
 (0)