Example #1
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]
    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)
Example #4
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
    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)
Example #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
Example #8
0
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
Example #9
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
Example #10
0
    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)
Example #12
0
    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)
Example #13
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]
    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)
Example #15
0
    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)
Example #16
0
    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()
Example #17
0
    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
Example #18
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))