def send_inv(self, blocks): private_block_invs = [] public_block_invs = [] for block in blocks: inv = hash_to_inv('Block', block.hash()) if block.block_origin is BlockOrigin.private: public_block_invs.append(inv) logging.debug("{} to be send to public".format(block.hash_repr())) else: private_block_invs.append(inv) logging.debug("{} to be send to private".format(block.hash_repr())) if len(private_block_invs) > 0: private_connection = self.get_private_connection() if private_connection is not None: msg = messages.msg_inv() msg.inv = private_block_invs private_connection.send('inv', msg) logging.info('{} block invs send to private'.format(len(private_block_invs))) else: logging.warning('there is no connection to private (ip={})'.format(self.private_ip)) if len(public_block_invs) > 0: msg = messages.msg_inv() msg.inv = public_block_invs i = 0 for connection in self.get_current_public_connection(): connection.send('inv', msg) i += 1 logging.info('{} block invs send to {} public connections' .format(len(public_block_invs), i))
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 getblocks(self, message): blkmeta = self.chaindb.locate(message.locator) height = blkmeta.height top_height = self.getheight() end_height = height + 500 if end_height > top_height: end_height = top_height msg = messages.msg_inv() while height <= end_height: hash = long(self.chaindb.height[str(height)]) if hash == message.hashstop: break inv = net.CInv() inv.type = messages.MSG_BLOCK inv.hash = hash msg.inv.append(inv) height += 1 if len(msg.inv) > 0: self.send_message(msg) if height <= top_height: self.hash_continue = msg.inv[-1].hash
def test_inv_message_msg_tx_known(self): inv = net.CInv() inv.type = networking.inv_typemap['TX'] inv.hash = 'hash1' msg = messages.msg_inv() msg.inv = [inv] self.networking.txs = {inv.hash: 'saved_transaction'} self.networking.inv_message(self.private_connection, msg) 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 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 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 getdata_block(self, blkhash): block = self.chaindb.getblock(blkhash) if block is None: return msg = messages.msg_block() msg.block = block self.send_message(msg) if blkhash == self.hash_continue: self.hash_continue = None inv = net.CInv() inv.type = messages.MSG_BLOCK inv.hash = self.chaindb.gettophash() msg = messages.msg_inv() msg.inv.append(inv) self.send_message(msg)
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 got_message(self, message): gevent.sleep() if self.last_sent + 30 * 60 < time.time(): self.send_message(messages.msg_ping(self.ver_send)) self.log.debug("recv %s" % repr(message)) if message.command == "version": self.ver_send = min(self.params.PROTO_VERSION, message.nVersion) if self.ver_send < self.params.MIN_PROTO_VERSION: self.log.info("Obsolete version %d, closing" % (self.ver_send,)) self.handle_close() return if (self.ver_send >= self.params.NOBLKS_VERSION_START and self.ver_send <= self.params.NOBLKS_VERSION_END): self.getblocks_ok = False self.remote_height = message.nStartingHeight self.send_message(messages.msg_verack(self.ver_send)) if self.ver_send >= self.params.CADDR_TIME_VERSION: self.send_message(messages.msg_getaddr(self.ver_send)) self.request_latest() self.client_version = message.strSubVer elif message.command == "verack": self.ver_recv = self.ver_send if self.ver_send >= self.params.MEMPOOL_GD_VERSION: self.send_message(messages.msg_mempool()) elif message.command == "ping": if self.ver_send > self.params.BIP0031_VERSION: self.send_message(messages.msg_pong(self.ver_send)) elif message.command == "addr": self.peermgr.new_addrs(message.addrs) elif message.command == "inv": # special message sent to kick getblocks if (len(message.inv) == 1 and message.inv[0].type == messages.MSG_BLOCK and self.chaindb.haveblock(message.inv[0].hash, True)): self.request_latest(False) return want = messages.msg_getdata(self.ver_send) for i in message.inv: if i.type == 1: want.inv.append(i) elif i.type == 2: want.inv.append(i) if len(want.inv): self.send_message(want) elif message.command == "tx": if self.chaindb.tx_is_orphan(message.tx): self.log.info( "MemPool: Ignoring orphan TX {}" .format(message.tx.GetHash().encode('hex'))) elif not self.chaindb.tx_signed(message.tx, None, True): self.log.info( "MemPool: Ignoring failed-sig TX {}" .format(message.tx.GetHash().encode('hex'))) else: self.mempool.add(message.tx) elif message.command == "block": self.chaindb.putblock(message.block) self.last_block_rx = time.time() elif message.command == "headers": self.chaindb.putblock(message.block) self.last_block_rx = time.time() elif message.command == "getdata": self.getdata(message) elif message.command == "getblocks": self.getblocks(message) elif message.command == "getheaders": self.getheaders(message) elif message.command == "getaddr": msg = messages.msg_addr() msg.addrs = self.peermgr.random_addrs() self.send_message(msg) elif message.command == "mempool": msg = messages.msg_inv() for k in self.mempool.pool.iterkeys(): inv = net.CInv() inv.type = messages.MSG_TX inv.hash = k msg.inv.append(inv) if len(msg.inv) == 50000: break self.send_message(msg) # if we haven't seen a 'block' message in a little while, # and we're still not caught up, send another getblocks last_blkmsg = time.time() - self.last_block_rx if last_blkmsg > 5: self.request_latest()
def got_message(self, message): gevent.sleep() if self.last_sent + 30 * 60 < time.time(): self.send_message(messages.msg_ping(self.ver_send)) self.log.debug("recv %s" % repr(message)) if message.command == "version": self.ver_send = min(self.params.PROTO_VERSION, message.nVersion) if self.ver_send < self.params.MIN_PROTO_VERSION: self.log.info("Obsolete version %d, closing" % (self.ver_send, )) self.handle_close() return if (self.ver_send >= self.params.NOBLKS_VERSION_START and self.ver_send <= self.params.NOBLKS_VERSION_END): self.getblocks_ok = False self.remote_height = message.nStartingHeight self.send_message(messages.msg_verack(self.ver_send)) if self.ver_send >= self.params.CADDR_TIME_VERSION: self.send_message(messages.msg_getaddr(self.ver_send)) self.request_latest() self.client_version = message.strSubVer elif message.command == "verack": self.ver_recv = self.ver_send if self.ver_send >= self.params.MEMPOOL_GD_VERSION: self.send_message(messages.msg_mempool()) elif message.command == "ping": if self.ver_send > self.params.BIP0031_VERSION: self.send_message(messages.msg_pong(self.ver_send)) elif message.command == "addr": self.peermgr.new_addrs(message.addrs) elif message.command == "inv": # special message sent to kick getblocks if (len(message.inv) == 1 and message.inv[0].type == messages.MSG_BLOCK and self.chaindb.haveblock(message.inv[0].hash, True)): self.request_latest(False) return want = messages.msg_getdata(self.ver_send) for i in message.inv: if i.type == 1: want.inv.append(i) elif i.type == 2: want.inv.append(i) if len(want.inv): self.send_message(want) elif message.command == "tx": if self.chaindb.tx_is_orphan(message.tx): self.log.info("MemPool: Ignoring orphan TX {}".format( message.tx.GetHash().encode('hex'))) elif not self.chaindb.tx_signed(message.tx, None, True): self.log.info("MemPool: Ignoring failed-sig TX {}".format( message.tx.GetHash().encode('hex'))) else: self.mempool.add(message.tx) elif message.command == "block": self.chaindb.putblock(message.block) self.last_block_rx = time.time() elif message.command == "headers": self.chaindb.putblock(message.block) self.last_block_rx = time.time() elif message.command == "getdata": self.getdata(message) elif message.command == "getblocks": self.getblocks(message) elif message.command == "getheaders": self.getheaders(message) elif message.command == "getaddr": msg = messages.msg_addr() msg.addrs = self.peermgr.random_addrs() self.send_message(msg) elif message.command == "mempool": msg = messages.msg_inv() for k in self.mempool.pool.iterkeys(): inv = net.CInv() inv.type = messages.MSG_TX inv.hash = k msg.inv.append(inv) if len(msg.inv) == 50000: break self.send_message(msg) # if we haven't seen a 'block' message in a little while, # and we're still not caught up, send another getblocks last_blkmsg = time.time() - self.last_block_rx if last_blkmsg > 5: self.request_latest()
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))