def send_inventory(self, serialized_transaction) -> msg_getdata: message = msg_inv() inventory = CInv() inventory.type = MSG_TX hash_transaction = Hash(serialized_transaction) inventory.hash = hash_transaction message.inv.append(inventory) timeout = time() + NODE_COMMUNICATION_TIMEOUT while time() < timeout: node = self.connect() if node is None: self.reset_connection() continue if not self.send_message(message): self.terminate(node) continue messages = self.capture_messages([ msg_getdata, ]) if not messages: self.terminate(node) continue logger.info('[%s] Node responded correctly.', node) return messages[0]
def test_getdata_message_with_two_blocks(self): cblock1 = CBlock() block1 = Block(cblock1, BlockOrigin.private) block1.cblock = cblock1 cInv1 = CInv() cInv1.type = networking.inv_typemap['Block'] cInv1.hash = cblock1.GetHash() cblock2 = CBlock() block2 = Block(cblock2, BlockOrigin.private) block2.cblock = cblock2 cInv2 = CInv() cInv2.type = networking.inv_typemap['Block'] cInv2.hash = cblock2.GetHash() message = messages.msg_getdata() message.inv = [cInv1, cInv2] self.chain.blocks = { cblock1.GetHash(): block1, cblock2.GetHash(): block2 } self.networking.getdata_message(self.public_connection1, message) self.assertTrue(self.public_connection1.send.called) self.assertEqual(self.public_connection1.send.call_count, 2)
def test_getdata_message_without_tx(self): message = messages.msg_getdata() inv = CInv() inv.type = networking.inv_typemap['TX'] inv.hash = 'hash1' message.inv = [inv] self.networking.get_tx = MagicMock() self.networking.get_tx.return_value = None self.networking.getdata_message(self.public_connection1, message) self.assertFalse(self.private_connection.send.called) self.assertFalse(self.public_connection1.send.called) self.assertFalse(self.public_connection2.send.called)
def send_transaction(self): import createTransaction tx = createTransaction.make_self_transaction() inv_msg = msg_inv() tx_inv = CInv() tx_inv.type = 1 tx_inv.hash = tx.GetHash() inv_msg.inv.append(tx_inv) self.my_socket.send(inv_msg.to_bytes()) iterations = 0 while self._process_message(transaction=tx) != "donedone" and iterations < 50: pass if iterations > 45: print("stuck on send? tid: ", threading.get_ident(), '\n\ttarget: ', self.link) iterations += 1 # print("leaving send_transaction()") return tx_inv.hash
def test_getdata_message_cblock_not_available(self): cblock = CBlock() hash_ = cblock.GetHash() block = Block(cblock, BlockOrigin.private) message = messages.msg_getdata() cInv = CInv() cInv.type = networking.inv_typemap['Block'] cInv.hash = hash_ message.inv = [cInv] self.chain.blocks = {hash_: block} self.networking.deferred_block_requests = {} self.networking.getdata_message(self.public_connection1, message) self.assertFalse(self.public_connection1.called) self.assertIn(hash_, self.networking.deferred_block_requests) self.assertIn(self.public_connection1.host[0], self.networking.deferred_block_requests[hash_])
def test_getdata_message_with_block(self): cblock = CBlock() block = Block(cblock, BlockOrigin.private) block.cblock = cblock message = messages.msg_getdata() cInv = CInv() cInv.type = networking.inv_typemap['Block'] cInv.hash = cblock.GetHash() message.inv = [cInv] self.chain.blocks = {cblock.GetHash(): block} self.networking.getdata_message(self.public_connection1, message) self.assertTrue(self.public_connection1.send.called) self.assertEqual(self.public_connection1.send.call_args[0][0], 'block') self.assertEqual(self.public_connection1.send.call_args[0][1].block, cblock)
def broadcast_tx(self, tx): """ Sends the tx to half our peers and waits for half of the remainder to announce it via inv packets before calling back. """ def on_peer_anncounce(txid): self.subscriptions[txhash]["announced"] += 1 if self.subscriptions[txhash]["announced"] >= self.subscriptions[ txhash]["ann_threshold"]: if self.subscriptions[txid]["timeout"].active(): self.subscriptions[txid]["timeout"].cancel() self.subscriptions[txid]["deferred"].callback(True) d = defer.Deferred() transaction = CTransaction.stream_deserialize(BytesIO(unhexlify(tx))) txhash = transaction.GetHash() self.inventory[txhash] = transaction cinv = CInv() cinv.type = 1 cinv.hash = txhash inv_packet = msg_inv() inv_packet.inv.append(cinv) self.bloom_filter.insert(txhash) self.subscriptions[txhash] = { "announced": 0, "ann_threshold": len(self.peers) / 4, "callback": on_peer_anncounce, "confirmations": 0, "in_blocks": [], "deferred": d, "timeout": reactor.callLater(10, d.callback, False) } for peer in self.peers[len(self.peers) / 2:]: peer.protocol.load_filter() for peer in self.peers[:len(self.peers) / 2]: peer.protocol.send_message(inv_packet) return d
def do_connect(client_addr, server_addr, nonce): global INV_SEND_TS, g_ver_ts, g_getaddr_send_ts with socket.socket() as sock: sock.connect((server_addr, PORT)) with sock.makefile(mode='rwb') as fake_fil: send_pkt(fake_fil, version_pkt(client_addr, server_addr, nonce)) remote_ver = recv_pkt(fake_fil) print(f'nonce: {remote_ver.nNonce}') send_pkt(fake_fil, msg_verack()) i = 0 try: while True: if i == 3: g_getaddr_send_ts = time.time() print("sending getaddr!") send_pkt(fake_fil, msg_getaddr()) elif i == 5: print(f"sending inv for {b2lx(FAKE_TX_HASH)}!") pkt = msg_inv() inv = CInv() inv.type = 1 # TX inv.hash = FAKE_TX_HASH pkt.inv.append(inv) INV_SEND_TS = time.time() send_pkt(fake_fil, pkt) elif i == 4: print(f"sending marker ips") pkt = msg_addr() for fake_ip in gen_fake_ips(RUN_ID, 78, 0xf): addr = CAddress() addr.ip = fake_ip addr.port = 9 # discard addr.nTime = g_ver_ts addr.nServices = 1 # NODE_NETWORK, otherwise they won't always accept the addr pkt.addrs.append(addr) send_pkt(fake_fil, pkt) recv_pkt(fake_fil) i += 1 except KeyboardInterrupt: pass return remote_ver.nNonce
def broadcast_tx(self, tx): """ Sends the tx to half our peers and waits for half of the remainder to announce it via inv packets before calling back. """ def on_peer_anncounce(txid): self.subscriptions[txhash]["announced"] += 1 if self.subscriptions[txhash]["announced"] >= self.subscriptions[txhash]["ann_threshold"]: if self.subscriptions[txid]["timeout"].active(): self.subscriptions[txid]["timeout"].cancel() self.subscriptions[txid]["deferred"].callback(True) d = defer.Deferred() transaction = CTransaction.stream_deserialize(BytesIO(unhexlify(tx))) txhash = transaction.GetHash() self.inventory[txhash] = transaction cinv = CInv() cinv.type = 1 cinv.hash = txhash inv_packet = msg_inv() inv_packet.inv.append(cinv) self.bloom_filter.insert(txhash) self.subscriptions[txhash] = { "announced": 0, "ann_threshold": len(self.peers)/4, "callback": on_peer_anncounce, "confirmations": 0, "in_blocks": [], "deferred": d, "timeout": reactor.callLater(10, d.callback, False) } for peer in self.peers[len(self.peers)/2:]: peer.protocol.load_filter() for peer in self.peers[:len(self.peers)/2]: peer.protocol.send_message(inv_packet) return d
def send_getblocks(self, timecheck=True): if not self.getblocks_ok: return now = time.time() # if timecheck and (now - self.last_getblocks) < 1: # return self.last_getblocks = now our_height = self.chaindb.getheight() if our_height < 0: gd = msg_getdata(self.ver_send) inv = CInv() inv.type = 2 inv.hash = bitcoin.params.GENESIS_BLOCK.GetHash() gd.inv.append(inv) self.send_message(gd) elif our_height < self.remote_height: gb = msg_getblocks(self.ver_send) if our_height >= 0: gb.locator.vHave = self.chaindb.getlocator() self.send_message(gb)
def send_getdata(self): start_block_hash = self.parameters_store.get_last_block_analized() block_hashes = self.block_chain.get_next_n_blocks_hashes( start_block_hash, 5000) if len(block_hashes) == 0: self.stop_client = True print("Analysis ended!") self.data_request = len(block_hashes) invs = [] for block_hash in block_hashes: inv = CInv() ''' MSG_FILTERED_BLOCK Indicates the reply should be a merkleblock message rather than a block message; this only works if a bloom filter has been set. ''' inv.type = 3 inv.hash = lx(block_hash) invs.append(inv) msg = msg_getdata() msg.inv = invs self.send_message(msg)
def test_serialization(self): inv = CInv() inv.type = 123 inv.hash = b"0" * 32 stream = _BytesIO() inv.stream_serialize(stream) serialized = _BytesIO(stream.getvalue()) deserialized = CInv.stream_deserialize(serialized) self.assertEqual(deserialized, inv)
def send_inventory(self, serialized_transaction) -> Optional[msg_getdata]: ''' Sends inventory message with given serialized transaction to connected node. Returns: msg_getdata, None: get data request or None if something went wrong Note: This method is used by `broadcast_transaction` to inform connected node about existence of the new transaction. ''' message = msg_inv() inventory = CInv() inventory.type = MSG_TX hash_transaction = Hash(serialized_transaction) inventory.hash = hash_transaction message.inv.append(inventory) timeout = time() + NODE_COMMUNICATION_TIMEOUT while time() < timeout: node = self.connect() if node is None: self.reset_connection() continue if not self.send_message(message): self.terminate(node) continue messages = self.capture_messages([ msg_getdata, ]) if not messages: self.terminate(node) continue logger.info('[%s] Node responded correctly.', node) return messages[0]
def test_getdata_message_with_unknown_hashes(self): message = messages.msg_getdata() cInv1 = CInv() cInv1.type = networking.inv_typemap['Block'] cInv1.hash = 'hash1' cInv2 = CInv() cInv2.type = networking.inv_typemap['Block'] cInv2.hash = 'hash2' message.inv = [cInv1, cInv2] self.chain.blocks = {} self.networking.getdata_message(self.public_connection1, message) self.assertFalse(self.public_connection1.send.called)
def send_getblocks(self, timecheck=True): if not self.getblocks_ok: return now = time.time() # if timecheck and (now - self.last_getblocks) < 1: # return self.last_getblocks = now our_height = self.chaindb.getheight() if our_height < 0: gd = msg_getdata(self.ver_send) inv = CInv() inv.type = 2 inv.hash = lx( 'cf7938a048f1442dd34f87ce56d3e25455b22a44f676325f1ae8c7a33d0731c7' ) gd.inv.append(inv) self.send_message(gd) elif our_height < self.remote_height: gb = msg_getblocks(self.ver_send) if our_height >= 0: gb.locator.vHave = self.chaindb.getlocator() self.send_message(gb)
def dataReceived(self, data): self.buffer += data header = MsgHeader.from_bytes(self.buffer) if len(self.buffer) < header.msglen + 24: return try: stream = BytesIO(self.buffer) m = MsgSerializable.stream_deserialize(stream) self.buffer = stream.read() if m.command == "verack": self.timeouts["verack"].cancel() del self.timeouts["verack"] if "version" not in self.timeouts: self.on_handshake_complete() elif m.command == "version": self.version = m if m.nVersion < 70001 or m.nServices != 1: self.transport.loseConnection() self.timeouts["version"].cancel() del self.timeouts["version"] msg_verack().stream_serialize(self.transport) if self.blockchain is not None: self.to_download = self.version.nStartingHeight - self.blockchain.get_height() if "verack" not in self.timeouts: self.on_handshake_complete() elif m.command == "getdata": for item in m.inv: if item.hash in self.inventory and item.type == 1: transaction = msg_tx() transaction.tx = self.inventory[item.hash] transaction.stream_serialize(self.transport) elif m.command == "inv": for item in m.inv: # This is either an announcement of tx we broadcast ourselves or a tx we have already downloaded. # In either case we only need to callback here. if item.type == 1 and item.hash in self.subscriptions: self.subscriptions[item.hash]["callback"](item.hash) # This is the first time we are seeing this txid. Let's download it and check to see if it sends # coins to any addresses in our subscriptions. elif item.type == 1 and item.hash not in self.inventory: self.timeouts[item.hash] = reactor.callLater(5, self.response_timeout, item.hash) cinv = CInv() cinv.type = 1 cinv.hash = item.hash getdata_packet = msg_getdata() getdata_packet.inv.append(cinv) getdata_packet.stream_serialize(self.transport) # The peer announced a new block. Unlike txs, we should download it, even if we've previously # downloaded it from another peer, to make sure it doesn't contain any txs we didn't know about. elif item.type == 2 or item.type == 3: if self.state == State.DOWNLOADING: self.download_tracker[0] += 1 cinv = CInv() cinv.type = 3 cinv.hash = item.hash getdata_packet = msg_getdata() getdata_packet.inv.append(cinv) getdata_packet.stream_serialize(self.transport) if self.state != State.DOWNLOADING: self.log.debug("Peer %s:%s announced new %s %s" % (self.transport.getPeer().host, self.transport.getPeer().port, CInv.typemap[item.type], b2lx(item.hash))) elif m.command == "tx": if m.tx.GetHash() in self.timeouts: self.timeouts[m.tx.GetHash()].cancel() for out in m.tx.vout: try: addr = str(CBitcoinAddress.from_scriptPubKey(out.scriptPubKey)) except Exception: addr = None if addr in self.subscriptions: if m.tx.GetHash() not in self.subscriptions: # It's possible the first time we are hearing about this tx is following block # inclusion. If this is the case, let's make sure we include the correct number # of confirmations. in_blocks = self.inventory[m.tx.GetHash()] if m.tx.GetHash() in self.inventory else [] confirms = [] if len(in_blocks) > 0: for block in in_blocks: confirms.append(self.blockchain.get_confirmations(block)) self.subscriptions[m.tx.GetHash()] = { "announced": 0, "ann_threshold": self.subscriptions[addr][0], "confirmations": max(confirms) if len(confirms) > 0 else 0, "last_confirmation": 0, "callback": self.subscriptions[addr][1], "in_blocks": in_blocks, "tx": m.tx } self.subscriptions[addr][1](m.tx.GetHash()) if m.tx.GetHash() in self.inventory: del self.inventory[m.tx.GetHash()] elif m.command == "merkleblock": if self.blockchain is not None: self.blockchain.process_block(m.block) if self.state != State.DOWNLOADING: self.blockchain.save() # check for block inclusion of subscribed txs for match in m.block.get_matched_txs(): if match in self.subscriptions: self.subscriptions[match]["in_blocks"].append(m.block.GetHash()) else: # stick the hash here in case this is the first we are hearing about this tx. # when the tx comes over the wire after this block, we will append this hash. self.inventory[match] = [m.block.GetHash()] # run through subscriptions and callback with updated confirmations for txid in self.subscriptions: try: confirms = [] for block in self.subscriptions[txid]["in_blocks"]: confirms.append(self.blockchain.get_confirmations(block)) self.subscriptions[txid]["confirmations"] = max(confirms) self.subscriptions[txid]["callback"](txid) except Exception: pass # If we are in the middle of an initial chain download, let's check to see if we have # either reached the end of the download or if we need to loop back around and make # another get_blocks call. if self.state == State.DOWNLOADING: self.download_count += 1 percent = int((self.download_count / float(self.to_download))*100) if self.download_listener is not None: self.download_listener.progress(percent, self.download_count) self.download_listener.on_block_downloaded((self.transport.getPeer().host, self.transport.getPeer().port), header, self.to_download - self.download_count + 1) if percent == 100: if self.download_listener is not None: self.download_listener.download_complete() self.log.info("Chain download 100% complete") self.download_tracker[1] += 1 # We've downloaded every block in the inv packet and still have more to go. if (self.download_tracker[0] == self.download_tracker[1] and self.blockchain.get_height() < self.version.nStartingHeight): if self.timeouts["download"].active(): self.timeouts["download"].cancel() self.download_blocks(self.callbacks["download"]) # We've downloaded everything so let's callback to the client. elif self.blockchain.get_height() >= self.version.nStartingHeight: self.blockchain.save() self.state = State.CONNECTED self.callbacks["download"]() if self.timeouts["download"].active(): self.timeouts["download"].cancel() elif m.command == "headers": if self.timeouts["download"].active(): self.timeouts["download"].cancel() for header in m.headers: # If this node sent a block with no parent then disconnect from it and callback # on client.check_for_more_blocks. if self.blockchain.process_block(header) is None: self.blockchain.save() self.callbacks["download"]() self.transport.loseConnection() return self.download_count += 1 percent = int((self.download_count / float(self.to_download))*100) if self.download_listener is not None: self.download_listener.progress(percent, self.download_count) self.download_listener.on_block_downloaded((self.transport.getPeer().host, self.transport.getPeer().port), header, self.to_download - self.download_count + 1) if percent == 100: if self.download_listener is not None: self.download_listener.download_complete() self.log.info("Chain download 100% complete") # The headers message only comes in batches of 500 blocks. If we still have more blocks to download # loop back around and call get_headers again. if self.blockchain.get_height() < self.version.nStartingHeight: self.download_blocks(self.callbacks["download"]) else: self.blockchain.save() self.callbacks["download"]() self.state = State.CONNECTED elif m.command == "ping": msg_pong(nonce=m.nonce).stream_serialize(self.transport) else: self.log.debug("Received message %s from %s:%s" % (m.command, self.transport.getPeer().host, self.transport.getPeer().port)) if len(self.buffer) >= 24: self.dataReceived("") except Exception: traceback.print_exc()
def dataReceived(self, data): self.buffer += data # if self.buffer.length >= sizeof(message header) # messageHeader := MessageHeader.deserialize(self.buffer) # if messageHeader.payloadLength > someBigNumber # throw DropConnection # if self.buffer.length < messageHeader.payloadLength # return try: m = MsgSerializable.from_bytes(self.buffer) self.buffer = "" if m.command == "verack": self.timeouts["verack"].cancel() del self.timeouts["verack"] if "version" not in self.timeouts: self.on_handshake_complete() elif m.command == "version": self.version = m if m.nVersion < 70001: self.transport.loseConnection() self.timeouts["version"].cancel() del self.timeouts["version"] msg_verack().stream_serialize(self.transport) if "verack" not in self.timeouts: self.on_handshake_complete() elif m.command == "getdata": for item in m.inv: if item.hash in self.inventory and item.type == 1: transaction = msg_tx() transaction.tx = self.inventory[item.hash] transaction.stream_serialize(self.transport) elif m.command == "inv": for item in m.inv: # callback tx if item.type == 1 and item.hash in self.subscriptions: self.subscriptions[item.hash]["callback"](item.hash) # download tx and check subscription elif item.type == 1 and item.hash not in self.inventory: self.timeouts[item.hash] = reactor.callLater(5, self.response_timeout, item.hash) cinv = CInv() cinv.type = 1 cinv.hash = item.hash getdata_packet = msg_getdata() getdata_packet.inv.append(cinv) getdata_packet.stream_serialize(self.transport) # download block elif item.type == 2 or item.type == 3: cinv = CInv() cinv.type = 3 cinv.hash = item.hash getdata_packet = msg_getdata() getdata_packet.inv.append(cinv) getdata_packet.stream_serialize(self.transport) print "Peer %s:%s announced new %s %s" % (self.transport.getPeer().host, self.transport.getPeer().port, CInv.typemap[item.type], b2lx(item.hash)) elif m.command == "tx": if m.tx.GetHash() in self.timeouts: self.timeouts[m.tx.GetHash()].cancel() for out in m.tx.vout: addr = str(CBitcoinAddress.from_scriptPubKey(out.scriptPubKey)) if addr in self.subscriptions: if m.tx.GetHash() not in self.subscriptions: self.subscriptions[m.tx.GetHash()] = { "announced": 0, "ann_threshold": self.subscriptions[addr][0], "confirmations": 0, "callback": self.subscriptions[addr][1], "in_blocks": self.inventory[m.tx.GetHash()] if m.tx.GetHash() in self.inventory else [], "tx": m.tx } self.subscriptions[addr][1](m.tx.GetHash()) if m.tx.GetHash() in self.inventory: del self.inventory[m.tx.GetHash()] elif m.command == "merkleblock": self.blockchain.process_block(m.block) if self.blockchain is not None: # check for block inclusion of subscribed txs for match in m.block.get_matched_txs(): if match in self.subscriptions: self.subscriptions[match]["in_blocks"].append(m.block.GetHash()) else: # stick the hash here in case this is a tx we missed on broadcast. # when the tx comes over the wire after this block, we will append this hash. self.inventory[match] = [m.block.GetHash()] # run through subscriptions and callback with updated confirmations for txid in self.subscriptions: if len(txid) == 32: confirms = [] for block in self.subscriptions[txid]["in_blocks"]: confirms.append(self.blockchain.get_confirmations(block)) self.subscriptions[txid]["confirmations"] = max(confirms) self.subscriptions[txid]["callback"](txid) elif m.command == "headers": self.timeouts["download"].cancel() to_download = self.version.nStartingHeight - self.blockchain.get_height() i = 1 for header in m.headers: self.blockchain.process_block(header) if i % 50 == 0 or int((i / float(to_download))*100) == 100: print "Chain download %s%% complete" % int((i / float(to_download))*100) i += 1 if self.blockchain.get_height() < self.version.nStartingHeight: self.download_blocks(self.callbacks["download"]) elif self.callbacks["download"]: self.callbacks["download"]() else: print "Received message %s from %s:%s" % (m.command, self.transport.getPeer().host, self.transport.getPeer().port) except Exception: pass
def run(self): try: self.s.connect((self.addr.ip, self.addr.port)) version_pkt(self.addr.ip, self.addr.port).stream_serialize(self.s) except Exception as e: print(e) print("{}, {}: Version handshake failed with {}:{}".format(datetime.datetime.now(), self.name, self.addr.ip, self.addr.port)) self._stop_request.set() # Make sure we dont send an addr or inv straight away self.lastaddr = time.time() self.lastinv = time.time() while not self._stop_request.is_set(): # Send at most one of these three if time.time() - self.lastping > PING_INTERVAL: msg_ping(random.getrandbits(64)).stream_serialize(self.s) self.lastping = time.time() elif time.time() - self.lastaddr > ADDR_INTERVAL: out = msg_addr() # Grab 10 random addresses with address_lock: random_addresses = random.sample(addresses, min(10, len(addresses))) for address in random_addresses: caddr = CAddress() # Lie a bit caddr.nTime = int(time.time()) - random.randrange(300) caddr.nServices = address.services caddr.port = address.port caddr.ip = address.ip out.addrs.append(caddr) out.stream_serialize(self.s) self.lastaddr = time.time() elif time.time() - self.lastinv > INV_INTERVAL: out = msg_inv() out_inv = CInv() out_inv.type = BLOCK_TYPE out_inv.hash = guess_latest_block() out.inv = [out_inv] out.stream_serialize(self.s) self.lastinv = time.time() try: msg = MsgSerializable.stream_deserialize(self.s) t = time.time() if isinstance(msg, msg_version): msg_verack().stream_serialize(self.s) elif isinstance(msg, msg_verack): print("{}, {}: Version handshake complete".format(datetime.datetime.now(), self.name)) elif isinstance(msg, msg_ping): result = push({ "me": { "ip": my_ipv4, "port": my_port }, "time": t, "type": "ping", "peer": { "ip": self.addr.ip, "port": self.addr.port }, "last": { "ping": self.lastping, "inv": self.lastinv, "addr": self.lastaddr }, "raw": base64.b64encode(msg.to_bytes()).decode('utf-8'), "data": msg.nonce }) msg_pong(nonce=msg.nonce).stream_serialize(self.s) elif isinstance(msg, msg_pong): result = push({ "me": { "ip": my_ipv4, "port": my_port }, "time": t, "type": "pong", "peer": { "ip": self.addr.ip, "port": self.addr.port }, "last": { "ping": self.lastping, "inv": self.lastinv, "addr": self.lastaddr }, "raw": base64.b64encode(msg.to_bytes()).decode('utf-8'), "data": msg.nonce }) elif isinstance(msg, msg_getheaders): pass elif isinstance(msg, msg_alert): pass elif isinstance(msg, msg_inv): if any(item.type == BLOCK_TYPE for item in msg.inv): result = push({ "me": { "ip": my_ipv4, "port": my_port }, "time": t, "type": "inv", "peer": { "ip": self.addr.ip, "port": self.addr.port }, "last": { "ping": self.lastping, "inv": self.lastinv, "addr": self.lastaddr }, "raw": base64.b64encode(msg.to_bytes()).decode('utf-8'), "data": [ { "type": "block" if item.type == BLOCK_TYPE else "tx", "hash": b2lx(item.hash) } for item in msg.inv ] }) for inv in msg.inv: if inv.type == BLOCK_TYPE: append_latest_block(inv.hash) elif isinstance(msg, msg_addr): discover_new_addresses(msg.addrs) else: print("{}, {}: Unhandled message type: {}".format(datetime.datetime.now(), self.name, msg.command.decode('utf-8'))) except socket.timeout: continue except SerializationTruncationError: print("{}, {}: **************** Socket closed. ****************".format(datetime.datetime.now(), self.name)) break self.s.close() print("{}, {}: Stopped.".format(datetime.datetime.now(), self.name))