Skip to content

Commit 95ffc2b

Browse files
committed
Fixes
- Qt six import was broken - variable in Qt support interface rename incomplete - don't put memoryview in an objectProcessorQueue - inventory data corruption fix
1 parent 8a3f1e5 commit 95ffc2b

6 files changed

Lines changed: 43 additions & 26 deletions

File tree

src/bitmessageqt/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@
5656
import bitmessage_icons_rc # noqa:F401 pylint: disable=unused-import
5757
import helper_sent
5858

59-
from six.moves import iteritems, itervalues, range as xrange
60-
from six import text_type
59+
from six import iteritems, itervalues, text_type
6160

6261
try:
6362
from plugins.plugin import get_plugin, get_plugins

src/bitmessageqt/support.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def createSupportMessage(myapp):
141141
if paths.frozen:
142142
frozen = paths.frozen
143143
portablemode = "True" if state.appdata == paths.lookupExeFolder() else "False"
144-
cpow = "True" if proofofwork.bmpow else "False"
144+
cpow = "True" if proofofwork.BMPOW else "False"
145145
openclpow = str(
146146
config.safeGet('bitmessagesettings', 'opencl')
147147
) if openclEnabled() else "None"

src/class_objectProcessor.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,19 @@ def checkackdata(data):
141141
# bypass nonce and time, retain object type/version/stream + body
142142
readPosition = 16
143143

144-
if data[readPosition:] in state.ackdataForWhichImWatching:
144+
# data may be a memoryview, which is not hashable and thus
145+
# cannot be used as a dictionary key; convert the slice to bytes.
146+
ackcheckdata = bytes(data[readPosition:])
147+
148+
if ackcheckdata in state.ackdataForWhichImWatching:
145149
logger.info('This object is an acknowledgement bound for me.')
146-
del state.ackdataForWhichImWatching[data[readPosition:]]
150+
del state.ackdataForWhichImWatching[ackcheckdata]
147151
sqlExecute(
148152
"UPDATE sent SET status='ackreceived', lastactiontime=?"
149-
" WHERE ackdata=?", int(time.time()), data[readPosition:])
153+
" WHERE ackdata=?", int(time.time()), ackcheckdata)
150154
queues.UISignalQueue.put((
151155
'updateSentItemStatusByAckdata', (
152-
data[readPosition:],
156+
ackcheckdata,
153157
_translate(
154158
"MainWindow",
155159
"Acknowledgement of the message received %1"
@@ -208,7 +212,9 @@ def processgetpubkey(data):
208212

209213
myAddress = ''
210214
if requestedAddressVersionNumber <= 3:
211-
requestedHash = data[readPosition:readPosition + 20]
215+
# data may be a memoryview; convert slices to bytes so they
216+
# are hashable and can be used as dictionary keys.
217+
requestedHash = bytes(data[readPosition:readPosition + 20])
212218
if len(requestedHash) != 20:
213219
return logger.debug(
214220
'The length of the requested hash is not 20 bytes.'
@@ -220,7 +226,8 @@ def processgetpubkey(data):
220226
if requestedHash in shared.myAddressesByHash:
221227
myAddress = shared.myAddressesByHash[requestedHash]
222228
elif requestedAddressVersionNumber >= 4:
223-
requestedTag = data[readPosition:readPosition + 32]
229+
# data may be a memoryview; convert to bytes for hashability.
230+
requestedTag = bytes(data[readPosition:readPosition + 32])
224231
if len(requestedTag) != 32:
225232
return logger.debug(
226233
'The length of the requested tag is not 32 bytes.'
@@ -413,7 +420,8 @@ def processpubkey(self, data):
413420
'(within processpubkey) payloadLength less than 350.'
414421
' Sanity check failed.')
415422

416-
tag = data[readPosition:readPosition + 32]
423+
# data may be a memoryview; convert to bytes for hashability.
424+
tag = bytes(data[readPosition:readPosition + 32])
417425
if tag not in state.neededPubkeys:
418426
return logger.info(
419427
'We don\'t need this v4 pubkey. We didn\'t ask for it.')
@@ -807,7 +815,8 @@ def processbroadcast(self, data):
807815
' v4 broadcast: %s seconds.',
808816
time.time() - messageProcessingStartTime)
809817
elif broadcastVersion == 5:
810-
embeddedTag = data[readPosition:readPosition + 32]
818+
# data may be a memoryview; convert to bytes for hashability.
819+
embeddedTag = bytes(data[readPosition:readPosition + 32])
811820
readPosition += 32
812821
if embeddedTag not in shared.MyECSubscriptionCryptorObjects:
813822
logger.debug('We\'re not interested in this broadcast.')

src/network/bmproto.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ def bm_command_object(self):
441441
try:
442442
self.object.checkObjectByType()
443443
objectProcessorQueue.put((
444-
self.object.objectType, memoryview(self.object.data)))
444+
self.object.objectType, bytes(self.object.data)))
445445
except BMObjectInvalidError:
446446
self.stopDownloadingObject(self.object.inventoryHash, True)
447447
else:
@@ -457,8 +457,8 @@ def bm_command_object(self):
457457

458458
state.Inventory[self.object.inventoryHash] = (
459459
self.object.objectType, self.object.streamNumber,
460-
memoryview(self.payload[objectOffset:]), self.object.expiresTime,
461-
memoryview(self.object.tag)
460+
bytes(self.payload[objectOffset:]), self.object.expiresTime,
461+
bytes(self.object.tag)
462462
)
463463
self.handleReceivedObject(
464464
self.object.streamNumber, self.object.inventoryHash)

src/storage/sqlite.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ def flush(self):
112112
sqlite3.Binary(objectHash),
113113
value.type,
114114
value.stream,
115-
sqlite3.Binary(bytes(value.payload)),
115+
sqlite3.Binary(value.payload),
116116
value.expires,
117-
sqlite3.Binary(bytes(value.tag)))
117+
sqlite3.Binary(value.tag))
118118
self._inventory.clear()
119119

120120
def clean(self):

src/tests/test_inventory_flush.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,44 +84,53 @@ def _make_hash(seed):
8484
"""Return a 32-byte hash derived from *seed*."""
8585
return (b'\x00' * 31 + bytes([seed & 0xFF]))[-32:]
8686

87-
def _flush_and_check(self, obj_hash):
87+
def _flush_and_check(self, obj_hash, expected_payload=None):
8888
"""
89-
Flush the inventory to the database, clear the _objects lookup
90-
cache so that __contains__ is forced to hit sqlite, then verify
91-
the hash is found via the normal inventory API.
89+
Flush the inventory to the database, clear both in-memory
90+
caches so that __contains__ and __getitem__ are forced to
91+
hit sqlite, then verify the hash is found and (optionally)
92+
that the payload content survived the round-trip.
9293
"""
9394
self.inventory.flush()
9495
self.inventory._objects.clear()
9596
self.assertIn(obj_hash, self.inventory)
97+
if expected_payload is not None:
98+
value = self.inventory[obj_hash]
99+
self.assertEqual(
100+
bytes(value.payload), expected_payload,
101+
"Payload content corrupted after flush")
96102

97103
# -- test cases -------------------------------------------------------
98104

99105
def test_flush_with_bytes_payload(self):
100106
"""Baseline: payload and tag are plain bytes."""
101107
h = self._make_hash(1)
108+
payload = b'\x80\x01' + os.urandom(64)
102109
self.inventory[h] = (
103-
2, 1, b'\x80\x01' + os.urandom(64),
110+
2, 1, payload,
104111
int(time.time()) + 3600, b'\xff' * 32)
105-
self._flush_and_check(h)
112+
self._flush_and_check(h, payload)
106113

107114
def test_flush_with_memoryview_payload(self):
108115
"""
109116
Reproduce the production crash: payload and tag as memoryview
110117
cause 'Error binding parameter 3 - probably unsupported type.'
111118
"""
112119
h = self._make_hash(2)
120+
payload = b'\x80\x02' + os.urandom(64)
113121
self.inventory[h] = (
114-
2, 1, memoryview(b'\x80\x02' + os.urandom(64)),
122+
2, 1, memoryview(payload),
115123
int(time.time()) + 3600, memoryview(b'\xee' * 32))
116-
self._flush_and_check(h)
124+
self._flush_and_check(h, payload)
117125

118126
def test_flush_with_bytearray_payload(self):
119127
"""bytearray is another bytes-like type that could trip sqlite3."""
120128
h = self._make_hash(3)
129+
payload = b'\x80\x03' + os.urandom(64)
121130
self.inventory[h] = (
122-
2, 1, bytearray(b'\x80\x03' + os.urandom(64)),
131+
2, 1, bytearray(payload),
123132
int(time.time()) + 3600, bytearray(b'\xdd' * 32))
124-
self._flush_and_check(h)
133+
self._flush_and_check(h, payload)
125134

126135
def test_flush_with_empty_tag(self):
127136
"""Empty tag (b'') must not break the INSERT."""

0 commit comments

Comments
 (0)