Пример #1
0
    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))
Пример #2
0
    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]
Пример #3
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
Пример #4
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
Пример #5
0
    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)
Пример #6
0
	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
Пример #7
0
    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
Пример #8
0
    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
Пример #9
0
    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)
Пример #10
0
    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)
Пример #11
0
    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]
Пример #12
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()
Пример #13
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()
Пример #14
0
    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))