tree checksum vpatch file split hunks

all signers:

antecedents: 9981-replay-notice 9982-getdata 9979-presence

press order:

genesis
9992-handle-edge-cases-add-feedback
9991-improved-logging
9990-keep-ephemeral-ports-open
9989-show-wot-nicks
9988-hash-dedup
9987-embargoing
9986-rebroadcast-simple-hearsay-and-more
9985-single-thread
9984-unbork-at-command
9983-knobs
9982-getdata
9981-replay-notice
9980-preserve-bounces
9979-presence
9978-bugfixes

patch:

- 431C84D2FA991B4C43090296E80765E1460288DE0FDB2D81ACF73A18977ACC16164111F210AF83488A80B92FA35D307ECAC26E59C860BBC12CD59A5C2A3E4C00
+ E349F34EFFDC0C0F717424902644C1643775B1F604E8356A0FFCAEF980E2E15D6576836A081B87CB9FA1F6FB99ADA553F687851B6FFCC0F83E1BA86C81727AEB
blatta/Makefile
(1 . 4)(1 . 4)
5 VERSION := 9979
6 VERSION := 9978
7
8 DISTFILES = Makefile blatta README.txt lib migrations tests test_net_configs start_test_net.sh
9
- 4446F38DF71CAB018B132851C12E5DF188949618983D81EAA098C41276CF5B8A96D9FEDF55EE3D5EF73412B3D5F3690CA0EA890ADCFD61867171A569D7C6E605
+ 5B3D8B0A91F1E8716B600AEE2966C7456798DCBEAB55AF77AAC6D5F73F9BEF501929C6AC426D973498C2D3C4BEE65C47F93CEA5742E32CA127BC308449AE707A
blatta/lib/client.py
(533 . 6)(533 . 9)
14 def pest_reply(self, msg):
15 self.message(":Pest NOTICE %s :%s" % (self.server.channel_name, msg))
16
17 def pest_dm_reply(self, speaker, msg):
18 self.message(":%s NOTICE %s :%s" % (speaker, self.nickname, msg))
19
20 def send_join(self, handle):
21 self.message(":%s JOIN %s" % (handle, self.server.channel_name))
22
(543 . 7)(546 . 7)
24 self.message(":%s AWAY :No recent messages" % (handle))
25
26 def send_back(self, handle):
27 self.message(":%s AWAY" % (handle))
28 self.message(":%s AWAY :" % (handle))
29
30 def reply_403(self, channeyl):
31 self.reply("403 %s %s :No such channel" % (self.nickname, channel))
- B2BFEE16A02F0AD104FBE59958BE80D27FC59C50695D25B4C008C28ABA4438C00706C3181C473C7A74BD85F21A681059B694B3B792F7C234D3478B346307EB7C
+ B6619F974D8EFC25C60EB441BE028EC57EB693EFB6E97188590572213D8061145EDE293212D0CBC9112CA5C5AFE4D278D7F6D10B12938663BC2E6D7DD0AC6EE0
blatta/lib/direct.py
(41 . 18)(41 . 16)
36 self.log_outgoing(target_peer)
37
38 def retry(self, requesting_peer):
39 target_peer = self.state.get_peer_by_handle(self.handle)
40
41 if target_peer == None:
42 logging.debug("Aborting message: unknown handle: %s" % self.handle)
43 if requesting_peer == None:
44 logging.debug("Aborting message: unknown peer: %s" % requesting_peer.handles[0])
45 return
46
47 if not target_peer.get_key():
48 logging.debug("No key for peer associated with %s" % self.handle)
49 if not requesting_peer.get_key():
50 logging.debug("No key for peer associated with %s" % requesting_peer.handles[0])
51 return
52
53 # TODO: Figure out how to verify that the requester was the original intended recipient
54 signed_packet_bytes = self.pack(target_peer, self.command, self.bounces, self.message_bytes)
55 target_peer.send(signed_packet_bytes)
56 self.log_outgoing(target_peer)
57 signed_packet_bytes = self.pack(requesting_peer, self.command, self.bounces, self.message_bytes)
58 requesting_peer.send(signed_packet_bytes)
59 self.log_outgoing(requesting_peer)
60
- 9F5B2F1661FAB5FCC0A29D99094099C7A40ACAA3736475E277177ED97036DB710CCC0C4A608BE22759A2F0493CB28BB62574B055A83C71205998A2E0EB8721DD
+ 76FCE797467FEC82AC2D8908F6769B043C7FD14BAE6E9ACFB3B4D2660A61725CFF6CE2E9A2EF21EA6F7A27F6A75D6CFB9D19013F058CFC32C5D86BC771EC86ED
blatta/lib/long_buffer.py
(18 . 10)(18 . 10)
65
66 command, message_bytes = self.state.get_message(message_hash)
67 if message_bytes:
68 if command == DIRECT:
69 return Direct({message_bytes: message_bytes}, self.state)
70 elif command == BROADCAST:
71 return Broadcast({'message_bytes': message_bytes}, self.state)
72 if command == DIRECT:
73 return Direct({'message_bytes': message_bytes}, self.state)
74 elif command == BROADCAST:
75 return Broadcast({'message_bytes': message_bytes}, self.state)
76
77 def expunge_expired(self):
78 for message in self.buffer.values():
- 9D70F920B8F62BCBC311319DFE98461EBD73E787CC1B97005B2F807F76F4FE99FB45A6650A04A149A854C8C547E87C77D00AFC39426A6756BCD5D7E5D8881221
+ B74685019898622685BD7F422BF5D7157AF5624EEDA40C5B976A3530E1E189B26E7CC38E5FCCD49C9A78ACD268C0154DD87F1014A6708AFBC0C184DE61D7E053
blatta/lib/message.py
(54 . 11)(54 . 13)
83 self.state = state
84 self.prefix = None
85 self.fork_status = None
86 # target peer handle
87 self.handle = message.get("handle")
88 self.peer = message.get("peer")
89 self.body = message.get("body")
90 self.timestamp = message.get("timestamp")
91 self.command = message.get("command")
92 # source peer handle
93 self.speaker = message.get("speaker")
94 self.bounces = message.get("bounces")
95 self.self_chain = message.get("self_chain")
(164 . 7)(166 . 6)
97
98 return cls._evaluate_message(long_buffer, order_buffer, message)
99
100
101 @classmethod
102 def _evaluate_message(cls, long_buffer, order_buffer, message):
103 # if we're expecting this message as a GETDATA response, skip the timestamp check
(231 . 16)(232 . 10)
105
106 @classmethod
107 def _evaluate_direct(cls, long_buffer, order_buffer, message):
108 # if this is the first direct message from a station, we needn't check for antecedents
109 if message['self_chain'] == EMPTY_CHAIN:
110 return message
111
112 # no need to check net_chain for a direct message
113 if not (long_buffer.has(message['self_chain'])):
114 if not order_buffer.has(message['message_hash']):
115 order_buffer.add(message)
116 message['error_code'] = OUT_OF_ORDER_SELF
117 return message
118 message['error_code'] = OUT_OF_ORDER_SELF
119 return message
120 return message
121
122 @classmethod
(267 . 15)(262 . 6)
124 return int_ts, self_chain, net_chain, speaker, body
125
126 @classmethod
127 def _unpack_last_valid_message(cls, last_message_info):
128 if last_message_info.get('message_bytes'):
129 return cls._unpack_message(
130 last_message_info['message_bytes']
131 )[4]
132 else:
133 return "<no prior message received>"
134
135 @classmethod
136 def _pad(cls, text, size):
137 return text.ljust(size, "\x00")
138
- 388113D46790BA9828275D8F1B20CEF6DCA63976D43192EFA8D4332B3360FA8CB93A50A026EB35A66BA129BEA7E61FC6973491072F6B5385D4EC83C11CCD2DC7
+ 204D40B495A9D2C5BD82BFA20AA738CDF3DD8A3F19F5D2AB1F0D5809ECB5E4C3B36A712D761566A8CA9DEE93911754445C73CA2B0A976CB78A950F5A2981ED49
blatta/lib/order_buffer.py
(1 . 3)(1 . 5)
143 import binascii
144 import logging
145 import time
146 from broadcast import Broadcast
147 from direct import Direct
(14 . 6)(16 . 7)
149 ts = time.time()
150 if message['command'] == BROADCAST:
151 m = Broadcast(message, self.state)
152 assert(m.message_hash == message['message_hash'])
153 elif message['command'] == DIRECT:
154 m = Direct(message, self.state)
155 else:
(33 . 25)(36 . 13)
157 return True
158 return False
159
160 def has(self, message_hash):
161 for value in self.buffer.values():
162 for message in value:
163 if message_hash == message.message_hash:
164 return True
165 return False
166
167 def dequeue_and_order_mature_messages(self):
168 current_time = time.time()
169 sorted_messages = sorted(self.buffer.keys())
170 mature_messages = []
171 for timestamp in sorted_messages:
172 if timestamp < current_time - int(self.state.get_knob('order_buffer_expiration_seconds')):
173 if isinstance(self.buffer[timestamp], list):
174 if len(self.buffer[timestamp]) > 0:
175 for message in self.buffer[timestamp]:
176 mature_messages.append(message)
177 del self.buffer[timestamp]
178 else:
179 mature_messages.append(self.buffer[timestamp])
180 del self.buffer[timestamp]
181 for message in self.buffer[timestamp]:
182 mature_messages.append(message)
183 del self.buffer[timestamp]
184 return sorted(mature_messages, key=lambda m: m.timestamp)
- CA409E06CB9491B00378C24BB0A189ED752C01A46B3D8406F403F7E7D378D4A62F91A1ADA8EAB3FFDF374E4A62EA3A5EDF3EDB48CD5500BBDE30F3E8153C209F
+ 78ED8993826A75EAB1B2D638F6A61BD6AED1727786F1687F909E41F95379F4556574F4B363B525C5A2D6987340D6E67FAE17E9F6DB92D9900F2430B428E45778
blatta/lib/state.py
(19 . 10)(19 . 11)
190 'embargo_interval_seconds': 1,
191 'rubbish_interval_seconds': 10,
192 'nick': '',
193 'order_buffer_check_seconds': 5 * 60,
194 'order_buffer_expiration_seconds': 5 * 60,
195 'order_buffer_check_seconds': 180,
196 'order_buffer_expiration_seconds': 120,
197 'short_buffer_expiration_seconds': 1,
198 'short_buffer_check_interval_seconds': 1,
199 'getdata_requests_expiration_seconds': 10,
200 'peer_offline_interval_seconds': 60,
201 'peer_away_interval_seconds': 10 * 60,
202 'presence_check_seconds': 5,
(183 . 6)(184 . 8)
204 h = cursor.execute("select handle from handles where handle_id=?",
205 (handle_id,)).fetchone()[0]
206 if updated_at_utc:
207 if '.' not in updated_at_utc:
208 updated_at_utc = updated_at_utc + '.0'
209 dt_format = '%Y-%m-%d %H:%M:%S.%f'
210 dt_utc = datetime.datetime.strptime(updated_at_utc, dt_format)
211 dt_local = self.utc_to_local(dt_utc)
(383 . 7)(386 . 11)
213 def handle_is_online(self, handle):
214 # last rubbish message from peer associated with handle is
215 # sufficiently recent
216 at = self.get_at(handle)[0]
217 try:
218 at = self.get_at(handle)[0]
219 except IndexError:
220 return False
221
222 if at["active_at_unixtime"] > time.time() - int(self.get_knob("peer_offline_interval_seconds")):
223 return True
224 else:
- A82E5B4C782B1484C844FD7843441BF0220BD18A796963B836C23DBC00A0F4031677D7699EC86A6A4B892B194871869E3CF55F481A5D978559E235869A7C3417
+ 79D6E16967ECED23171091D8CEFD19A20BECA845A68698BE54F75EF48F869C2DFD80D452BA3DC4ABABBE76A5007381D309E627EEC61F2D83AFBC513B543880AB
blatta/lib/station.py
(1 . 6)(1 . 6)
229 import time
230
231 VERSION = 9979
232 VERSION = 9978
233 STATUS_ONLINE = 0
234 STATUS_AWAY = 1
235
(36 . 6)(36 . 7)
237 self.short_buffer = ShortBuffer(self.state)
238 self.long_buffer = LongBuffer(self.state)
239 self.order_buffer = OrderBuffer(self.state)
240 self.getdata_requests = {}
241 self.server = Server(cmd_line_options, self)
242 self.handlers = {
243 DIRECT: self.handle_direct,
(44 . 6)(45 . 7)
245 IGNORE: self.handle_ignore
246 }
247 self.presence = {}
248 self.last_delivered = 0
249
250 def start(self):
251 self.server.start()
(106 . 7)(108 . 6)
253 def handle_direct(self, message):
254 message.log_incoming(message.peer)
255 self.deliver(message)
256 self.long_buffer.intern(message)
257 self.conditionally_update_at(message, message.metadata["address"])
258
259 def handle_broadcast(self, message):
(119 . 7)(120 . 6)
261 if self.short_buffer.has(message.message_hash):
262 self.short_buffer.drop(message.message_hash)
263 self.deliver(message)
264 self.long_buffer.intern(message)
265 self.state.update_net_chain(message.message_hash)
266 self.rebroadcast(message)
267 else:
(166 . 7)(166 . 6)
269 message.prefix = "%s[%d]" % (message.speaker, len(message_with_stats['closest_peers']))
270
271 self.deliver(message)
272 self.long_buffer.intern(message)
273 self.state.update_net_chain(message.message_hash)
274 message.reporting_peers = message_with_stats['reporting_peers']
275 self.rebroadcast(message)
(226 . 10)(225 . 18)
277 port = packet_info[1]
278 logging.debug(
279 "[%s:%d] -> message received out of order: %s" % (address, port, binascii.hexlify(message['message_hash'])))
280 if not self.order_buffer.has(message['message_hash']):
281 for chain in broken_chains:
282 for chain in broken_chains:
283 if not self.getdata_requests.get(message[chain]):
284 GetData(message, chain, self.state).send()
285 self.getdata_requests[message[chain]] = time.time()
286 self.order_buffer.add(message)
287 self.clean_getdata_requests()
288
289 def clean_getdata_requests(self):
290 for message_hash in self.getdata_requests.keys():
291 if (self.getdata_requests.get(message_hash) <
292 time.time() - self.state.get_knob('getdata_requests_expiration_seconds')):
293 del self.getdata_requests[message_hash]
294
295 def deliver(self, message):
296 # it's possible that these messages are from an order buffer
(238 . 18)(245 . 24)
298 if self.long_buffer.has(message.message_hash):
299 return
300
301
302 # send to the irc client
303 if self.client:
304 # emit a replay warning if this message is a getdata response and older than the last
305 # displayed message
306 if message.get_data_response:
307 if message.timestamp < self.state.get_latest_message_timestamp():
308 if message.timestamp < self.last_delivered:
309 warning = time.strftime(
310 "Replay: %Y-%m-%d %H:%M:%S:",
311 time.localtime(message.timestamp)
312 )
313 self.client.pest_reply(warning)
314 if message.command == BROADCAST:
315 self.client.pest_reply(warning)
316 else:
317 self.client.pest_dm_reply(message.speaker, warning)
318 self.client.message_from_station(message)
319 self.last_delivered = message.timestamp
320 self.long_buffer.intern(message)
321
322 # we only update the address table if the speaker is same as peer
323 def conditionally_update_at(self, message, address):
- B48397DE933C7423E2E5C881F18C0E30FF858F31D3DE6EF4C6074EA512AFA85069C9FD0CAF537C2CC41CC4CA8D69FDFE1AE1FD6F7AF9211CCDE47C8311A70776
+ C50220A20C89E9C56B3083422835887C8C5346354F2A10A117E873F3287A3F44E6F9ACE31E46045B149A0A310A77C86E4506E682CCD944D2B88C2C484336F10B
blatta/tests/test_getdata.py
(1 . 5)(1 . 4)
328 import unittest
329 import logging
330
331 import time
332 from mock import Mock
(9 . 7)(8 . 6)
334 from lib.order_buffer import OrderBuffer
335 from lib.state import State
336 from lib.direct import Direct
337 from lib.broadcast import Broadcast
338 import helper
339
340 class TestGetData(unittest.TestCase):
-
+ D4056F2557395E2D4F1367500F9A9462F7C518648B025C887C376243BFF75E70E59B2F341AEBE3064DBA038C4F9E4C09E77F870B90CAB8F3545DB9A000BE4D90
blatta/tests/test_order_buffer.py
(0 . 0)(1 . 32)
345 import unittest
346
347 import time
348 from mock import Mock
349 from lib.commands import BROADCAST
350 from lib.message import Message
351 from lib.getdata import GetData
352 from lib.long_buffer import LongBuffer
353 from lib.order_buffer import OrderBuffer
354 from lib.state import State
355 from lib.direct import Direct
356 import helper
357
358 class TestOrderBuffer(unittest.TestCase):
359 def setUp(self):
360 helper.setup()
361 self.socket = Mock()
362 self.state = State(self.socket)
363 self.state.set_knob('nick', 'alice')
364
365 def test_add(self):
366 m1 = {
367 'command': BROADCAST,
368 'body': "m1",
369 'speaker': "bob",
370 'handle': "alice",
371 'self_chain': 'abc',
372 'net_chain': 'def',
373 'message_hash': '123'
374 }
375 ob = OrderBuffer(self.state)
376 ob.add(m1)
- F6EB911B7A54EC1EF368E1EED8202AA2E385086F3D5509C08EB8BB78D216FF2408B7409B5F4F368D46EA98475C4AFB3A67A094EDE9109278AD0CB7EA93459F4B
+ 90668D46C39A64258B943CF8FF176E84B0B98EB662B8E6193B717F6582BFEAE981698754FF6F4AA7EFDA8F672DED907EE0680A5F3C175E3848718C712A200F2D
blatta/tests/test_station.py
(1 . 4)(1 . 5)
381 # https://stackoverflow.com/questions/1896918/running-unittest-with-typical-test-directory-structure
382 import time
383 import unittest
384 import logging
385 from mock import Mock
(75 . 6)(76 . 16)
387 def tearDown(self):
388 pass
389
390 def test_clean_getdata_requests_clears_expired_hashes(self):
391 self.station.getdata_requests["abc"] = time.time() - 15
392 self.station.clean_getdata_requests()
393 self.assertEqual(len(self.station.getdata_requests), 0)
394
395 def test_clean_getdata_requests_retains_valid_hashes(self):
396 self.station.getdata_requests["abc"] = time.time()
397 self.station.clean_getdata_requests()
398 self.assertEqual(len(self.station.getdata_requests), 1)
399
400 def test_embargo_bounce_ordering(self):
401 self.skipTest("the tested code has been re-implemented")
402 peer1 = Mock()