Beispiel #1
0
 def _test_block_cleanup(self):
     block_msg = self._get_sample_block(self._get_file_path())
     transactions = block_msg.txns()[:]
     block_hash = typing.cast(BtcObjectHash, block_msg.block_hash())
     random.shuffle(transactions)
     short_len = int(len(transactions) * 0.9)
     transactions_short = transactions[:short_len]
     unknown_transactions = transactions[short_len:]
     transaction_hashes = []
     for idx, tx in enumerate(transactions_short):
         tx_hash = BtcObjectHash(buf=crypto.double_sha256(tx),
                                 length=btc_constants.BTC_SHA_HASH_LEN)
         transaction_hashes.append(tx_hash)
         self.transaction_service.set_transaction_contents(tx_hash, tx)
         self.transaction_service.assign_short_id(tx_hash, idx + 1)
     for idx, tx in enumerate(unknown_transactions):
         tx_hash = BtcObjectHash(buf=crypto.double_sha256(tx),
                                 length=btc_constants.BTC_SHA_HASH_LEN)
         transaction_hashes.append(tx_hash)
         if idx % 2 == 0:
             self.transaction_service.set_transaction_contents(tx_hash, tx)
     self.cleanup_service._block_hash_marked_for_cleanup.add(block_hash)
     self.cleanup_service.clean_block_transactions(block_msg,
                                                   self.transaction_service)
     self.assertEqual(0, self.transaction_service._total_tx_contents_size)
     for tx_hash in transaction_hashes:
         self.assertFalse(
             self.transaction_service.has_transaction_contents(tx_hash))
Beispiel #2
0
    def start_transaction_recovery(
            self,
            unknown_sids: Iterable[int],
            unknown_hashes: Iterable[Sha256Hash],
            block_hash: Sha256Hash,
            connection: Optional[AbstractRelayConnection] = None) -> None:
        all_unknown_sids = []
        all_unknown_sids.extend(unknown_sids)
        tx_service = self._node.get_tx_service()

        # retrieving sids of txs with unknown contents
        for tx_hash in unknown_hashes:
            transaction_key = tx_service.get_transaction_key(tx_hash)
            tx_sid = tx_service.get_short_id_by_key(transaction_key)
            all_unknown_sids.append(tx_sid)

        if not self._node.opts.request_recovery:
            if connection is not None:
                network_num = connection.network_num
            else:
                network_num = self._node.network_num
            # log recovery started to match with recovery completing
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_STARTED,
                network_num=network_num,
                txs_count=len(all_unknown_sids),
                more_info="recovery from relay is disabled",
            )
            return

        get_txs_message = GetTxsMessage(short_ids=all_unknown_sids)
        self._node.broadcast(
            get_txs_message,
            connection_types=(ConnectionType.RELAY_TRANSACTION, ))

        if connection is not None:
            tx_stats.add_txs_by_short_ids_event(
                all_unknown_sids,
                TransactionStatEventType.
                TX_UNKNOWN_SHORT_IDS_REQUESTED_BY_GATEWAY_FROM_RELAY,
                network_num=self._node.network_num,
                peers=[connection],
                block_hash=convert.bytes_to_hex(block_hash.binary))
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_STARTED,
                network_num=connection.network_num,
                txs_count=len(all_unknown_sids),
                request_hash=convert.bytes_to_hex(
                    crypto.double_sha256(get_txs_message.rawbytes())))
        else:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_REPEATED,
                network_num=self._node.network_num,
                txs_count=len(all_unknown_sids),
                request_hash=convert.bytes_to_hex(
                    crypto.double_sha256(get_txs_message.rawbytes())))
    def test_msg_broadcast_duplicate_block_with_different_short_id(self):
        # test scenario when first received block is compressed with unknown short ids,
        # but second received block is compressed with known short ids
        ont_block = self.ont_block()
        block_hash = ont_block.block_hash()
        transactions = self.bx_transactions()

        unknown_sid_transaction_service = ExtensionTransactionService(MockNode(
            gateway_helpers.get_gateway_opts(8999)), 0)
        for i, transaction in enumerate(transactions):
            unknown_sid_transaction_service.assign_short_id(transaction.tx_hash(), i)
            unknown_sid_transaction_service.set_transaction_contents(transaction.tx_hash(), transaction.tx_val())

        unknown_short_id_block = bytes(
            self.gateway_node.message_converter.block_to_bx_block(
                ont_block, unknown_sid_transaction_service, True, self.gateway_node.network.min_tx_age_seconds
            )[0]
        )
        unknown_key, unknown_cipher = symmetric_encrypt(unknown_short_id_block)
        unknown_block_hash = crypto.double_sha256(unknown_cipher)
        unknown_message = BroadcastMessage(Sha256Hash(unknown_block_hash), self.TEST_NETWORK_NUM, "",
                                           BroadcastMessageType.BLOCK, False, bytearray(unknown_short_id_block))
        unknown_key_message = KeyMessage(Sha256Hash(unknown_block_hash), self.TEST_NETWORK_NUM, "", unknown_key)

        local_transaction_service = self.gateway_node.get_tx_service()
        for i, transaction in enumerate(transactions):
            local_transaction_service.assign_short_id(transaction.tx_hash(), i + 20)
            local_transaction_service.set_transaction_contents(transaction.tx_hash(), transaction.tx_val())

        known_short_id_block = bytes(
            self.gateway_node.message_converter.block_to_bx_block(
                ont_block, local_transaction_service, True, self.gateway_node.network.min_tx_age_seconds
            )[0]
        )
        known_key, known_cipher = symmetric_encrypt(known_short_id_block)
        known_block_hash = crypto.double_sha256(known_cipher)
        known_message = BroadcastMessage(Sha256Hash(known_block_hash), self.TEST_NETWORK_NUM, "",
                                         BroadcastMessageType.BLOCK, False, bytearray(known_short_id_block))
        known_key_message = KeyMessage(Sha256Hash(known_block_hash), self.TEST_NETWORK_NUM, "", known_key)

        self.sut.msg_broadcast(unknown_message)
        self.sut.msg_key(unknown_key_message)

        self.assertEqual(1, len(self.gateway_node.block_queuing_service))
        self.assertEqual(True, self.gateway_node.block_queuing_service._blocks_waiting_for_recovery[block_hash])
        self.assertEqual(1, len(self.gateway_node.block_recovery_service._block_hash_to_bx_block_hashes))
        self.assertNotIn(block_hash, self.gateway_node.blocks_seen.contents)

        self.sut.msg_broadcast(known_message)
        self.sut.msg_key(known_key_message)
        self.gateway_node_sut.msg_get_data(GetDataOntMessage(self.magic, InventoryOntType.MSG_BLOCK.value, block_hash))

        self.gateway_node.broadcast.assert_called()
        self.assertEqual(0, len(self.gateway_node.block_queuing_service))
        self.assertEqual(0, len(self.gateway_node.block_recovery_service._block_hash_to_bx_block_hashes))
        self.assertIn(block_hash, self.gateway_node.blocks_seen.contents)
Beispiel #4
0
    def setUp(self):
        super().setUp()

        self.node1.alarm_queue = AlarmQueue()
        self.node2.alarm_queue = AlarmQueue()

        self.network_num = 1
        self.magic = 12345
        self.version = 23456
        self.prev_block_hash = bytearray(crypto.double_sha256(b"123"))
        self.prev_block = BtcObjectHash(self.prev_block_hash,
                                        length=crypto.SHA256_HASH_LEN)
        self.merkle_root_hash = bytearray(crypto.double_sha256(b"234"))
        self.merkle_root = BtcObjectHash(self.merkle_root_hash,
                                         length=crypto.SHA256_HASH_LEN)
        self.bits = 2
        self.nonce = 3

        opts = self.gateway_1_opts()
        if opts.use_extensions:
            helpers.set_extensions_parallelism()
        self.btc_message_converter = btc_message_converter_factory.create_btc_message_converter(
            self.magic, opts)

        self.btc_transactions = [
            TxBtcMessage(self.magic, self.version, [], [], i)
            for i in range(self.TRANSACTIONS_COUNT)
        ]
        self.btc_transactions_for_block = [
            tx_btc_message.rawbytes()[btc_constants.BTC_HDR_COMMON_OFF:]
            for tx_btc_message in self.btc_transactions
        ]
        self.transactions = [
            self.btc_message_converter.tx_to_bx_txs(tx_btc_message,
                                                    self.network_num)[0][0]
            for tx_btc_message in self.btc_transactions
        ]
        self.transactions_with_short_ids = [
            TxMessage(tx_message.tx_hash(), tx_message.network_num(), "",
                      i + 1, tx_message.tx_val())
            for i, tx_message in enumerate(self.transactions)
        ]
        self.transactions_with_no_content = [
            TxMessage(tx_message.tx_hash(), tx_message.network_num(), "",
                      i + 1) for i, tx_message in enumerate(self.transactions)
        ]
        self.transactions_by_short_id = {
            tx_message.short_id(): tx_message
            for tx_message in self.transactions_with_short_ids
        }
        self.block = BlockBtcMessage(self.magic, self.version,
                                     self.prev_block, self.merkle_root,
                                     int(time.time()), self.bits, self.nonce,
                                     self.btc_transactions_for_block)
 def bdn_tx_to_bx_tx(
         self,
         raw_tx: Union[bytes, bytearray, memoryview],
         network_num: int,
         quota_type: Optional[QuotaType] = None
 ) -> TxMessage:
     return TxMessage(Sha256Hash(crypto.double_sha256(raw_tx)), network_num, tx_val=raw_tx)
    def add_block(self, bx_block: memoryview, block_hash: Sha256Hash,
                  unknown_tx_sids: List[int],
                  unknown_tx_hashes: List[Sha256Hash]):
        """
        Adds a block that needs to recovery. Tracks unknown short ids and contents as they come in.
        :param bx_block: bytearray representation of compressed block
        :param block_hash: original ObjectHash of block
        :param unknown_tx_sids: list of unknown short ids
        :param unknown_tx_hashes: list of unknown tx ObjectHashes
        """
        logger.trace(
            "Recovering block with {} unknown short ids and {} contents: {}",
            len(unknown_tx_sids), len(unknown_tx_hashes), block_hash)
        bx_block_hash = Sha256Hash(crypto.double_sha256(bx_block))

        self._bx_block_hash_to_block[bx_block_hash] = bx_block
        self._bx_block_hash_to_block_hash[bx_block_hash] = block_hash
        self._bx_block_hash_to_sids[bx_block_hash] = set(unknown_tx_sids)
        self._bx_block_hash_to_tx_hashes[bx_block_hash] = set(
            unknown_tx_hashes)

        self._block_hash_to_bx_block_hashes[block_hash].add(bx_block_hash)
        for sid in unknown_tx_sids:
            self._sid_to_bx_block_hashes[sid].add(bx_block_hash)
        for tx_hash in unknown_tx_hashes:
            self._tx_hash_to_bx_block_hashes[tx_hash].add(bx_block_hash)

        self._blocks_expiration_queue.add(bx_block_hash)
        self._schedule_cleanup()
Beispiel #7
0
    def test_bdn_stats_block_new_from_bdn_ignore_from_node(self):
        block_msg = NewBlockEthProtocolMessage(
            None,
            _block_with_timestamp(time.time() + 1 - (
                self.node.opts.blockchain_ignore_block_interval_count *
                self.node.opts.blockchain_block_interval)), 10)

        internal_new_block_msg = InternalEthBlockInfo.from_new_block_msg(
            block_msg)
        msg_bytes, block_info = self.node.message_converter.block_to_bx_block(
            internal_new_block_msg, self.node._tx_service, True,
            self.node.network.min_tx_age_seconds)
        msg_hash = Sha256Hash(crypto.double_sha256(msg_bytes))
        broadcast_msg = BroadcastMessage(message_hash=msg_hash,
                                         network_num=1,
                                         is_encrypted=False,
                                         blob=msg_bytes)
        self.relay_connection.msg_broadcast(broadcast_msg)

        block_msg.serialize()
        self.block_blockchain_connection_protocol.msg_block(block_msg)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        for stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.values(
        ):
            self.assertEqual(1, stats.new_blocks_received_from_bdn)
            self.assertEqual(0, stats.new_blocks_received_from_blockchain_node)
Beispiel #8
0
    def test_msg_broadcast_wait_for_key(self):
        ont_block = self.ont_block()
        bx_block = self.bx_block(ont_block)

        key, ciphertext = symmetric_encrypt(bx_block)
        block_hash = crypto.double_sha256(ciphertext)
        broadcast_message = BroadcastMessage(Sha256Hash(block_hash),
                                             self.TEST_NETWORK_NUM, "",
                                             BroadcastMessageType.BLOCK, True,
                                             ciphertext)

        self.sut.msg_broadcast(broadcast_message)

        # handle duplicate messages
        self.sut.msg_broadcast(broadcast_message)
        self.sut.msg_broadcast(broadcast_message)

        self.assertEqual(3, len(self.gateway_node.broadcast.call_args_list))
        for call, conn_type in self.gateway_node.broadcast.call_args_list:
            msg, conn = call
            self.assertTrue(isinstance(msg, BlockReceivedMessage))
        self.gateway_node.broadcast.reset_mock()

        self.assertEqual(1, len(self.gateway_node.in_progress_blocks))

        key_message = KeyMessage(Sha256Hash(block_hash), self.TEST_NETWORK_NUM,
                                 "", key)
        self.sut.msg_key(key_message)

        self._assert_block_sent(ont_block)
Beispiel #9
0
def get_block_info(
        bx_block: memoryview,
        block_hash: Sha256Hash,
        short_ids: List[int],
        decompress_start_datetime: datetime,
        decompress_start_timestamp: float,
        total_tx_count: Optional[int] = None,
        btc_block_msg: Optional[BlockBtcMessage] = None
) -> BlockInfo:
    if btc_block_msg is not None:
        bx_block_hash = convert.bytes_to_hex(crypto.double_sha256(bx_block))
        compressed_size = len(bx_block)
        prev_block_hash = convert.bytes_to_hex(btc_block_msg.prev_block_hash().binary)
        btc_block_len = len(btc_block_msg.rawbytes())
        compression_rate = 100 - float(compressed_size) / btc_block_len * 100
    else:
        bx_block_hash = None
        compressed_size = None
        prev_block_hash = None
        btc_block_len = None
        compression_rate = None
    return BlockInfo(
        block_hash,
        short_ids,
        decompress_start_datetime,
        datetime.utcnow(),
        (time.time() - decompress_start_timestamp) * 1000,
        total_tx_count,
        bx_block_hash,
        prev_block_hash,
        btc_block_len,
        compressed_size,
        compression_rate
    )
Beispiel #10
0
    def _propagate_block_to_gateway_peers(self, cipher_hash, bx_block):
        """
        Propagates unencrypted bx_block to all gateway peers for encryption and sending to bloXroute.
        Also sends keys to bloXroute in case this was user error (e.g. no gateway peers).
        Called after a timeout. This invalidates all future bx_block receipts.
        """
        bx_block_hash = crypto.double_sha256(bx_block)
        hex_bx_block_hash = convert.bytes_to_hex(bx_block_hash)

        logger.debug("Did not receive enough receipts for: {}. Propagating compressed block to other gateways: {}",
                     cipher_hash, hex_bx_block_hash)
        self._send_key(cipher_hash)

        request = BlockPropagationRequestMessage(bx_block)
        conns = self._node.broadcast(request, None, connection_types=(ConnectionType.GATEWAY,))
        block_stats.add_block_event_by_block_hash(cipher_hash,
                                                  BlockStatEventType.ENC_BLOCK_PROPAGATION_NEEDED,
                                                  network_num=self._node.network_num,
                                                  compressed_block_hash=hex_bx_block_hash,
                                                  peers=conns,
                                                  more_info="Peers: {}, {} receipts".format(
                                                      stats_format.connections(conns),
                                                      self._receipt_tracker[cipher_hash]))

        del self._receipt_tracker[cipher_hash]
        del self._alarms[cipher_hash]
        return constants.CANCEL_ALARMS
Beispiel #11
0
    def __init__(self,
                 magic: Optional[int] = None,
                 command: Optional[bytes] = None,
                 payload_len: Optional[int] = None,
                 buf: Optional[bytearray] = None):
        self.buf = buf
        # pyre-fixme[6]: Expected `Union[bytearray, bytes, memoryview]` for 1st
        #  param but got `Optional[bytearray]`.
        self._memoryview = memoryview(buf)

        # pyre-fixme[6]: Expected `int` for 1st param but got `Optional[int]`.
        checksum = crypto.double_sha256(
            self._memoryview[ont_constants.ONT_HDR_COMMON_OFF:payload_len +
                             ont_constants.ONT_HDR_COMMON_OFF])

        off = 0
        # pyre-fixme[6]: Expected `Union[array.array[typing.Any], bytearray,
        #  memoryview, mmap.mmap]` for 2nd param but got `Optional[bytearray]`.
        struct.pack_into("<L12sL", buf, off, magic, command, payload_len)
        off += ont_constants.ONT_HEADER_MINUS_CHECKSUM
        # pyre-fixme[16]: `Optional` has no attribute `__setitem__`.
        buf[off:off + ont_constants.ONT_MSG_CHECKSUM_LEN] = checksum[0:4]

        self._magic = magic
        self._command = command
        self._payload_len = payload_len
        self._payload = None
        self._checksum = None
    def test_get_txs_block_recovery_encrypted(self):
        block: BlockOntMessage = self.ont_block()
        transactions: List[TxOntMessage] = self.ont_transactions(block)

        # assign short ids that the local connection won't know about until it gets the txs message
        remote_transaction_service = ExtensionTransactionService(
            MockNode(gateway_helpers.get_gateway_opts(8999)), 0)
        short_id_mapping = {}
        for i, transaction in enumerate(transactions):
            tx_hash = transaction.tx_hash()

            remote_transaction_service.assign_short_id(tx_hash, i + 1)
            remote_transaction_service.set_transaction_contents(
                tx_hash, transaction.rawbytes())
            short_id_mapping[tx_hash] = TransactionInfo(
                tx_hash, transaction.rawbytes(), i + 1)

        bx_block = bytes(
            self.gateway_node.message_converter.block_to_bx_block(
                block, remote_transaction_service)[0])

        self.gateway_node.block_recovery_service.add_block = \
            MagicMock(wraps=self.gateway_node.block_recovery_service.add_block)
        self.gateway_node.send_msg_to_node = MagicMock()
        self.gateway_node.broadcast = MagicMock()

        key, ciphertext = symmetric_encrypt(bx_block)
        block_hash = crypto.double_sha256(ciphertext)
        key_message = KeyMessage(Sha256Hash(block_hash), DEFAULT_NETWORK_NUM,
                                 "", key)
        broadcast_message = BroadcastMessage(Sha256Hash(block_hash),
                                             DEFAULT_NETWORK_NUM, "",
                                             BroadcastMessageType.BLOCK, True,
                                             ciphertext)

        self.sut.msg_broadcast(broadcast_message)

        self.gateway_node.broadcast.reset_mock()
        self.sut.msg_key(key_message)

        self.gateway_node.block_recovery_service.add_block.assert_called_once()
        self.assertEqual(2, self.gateway_node.broadcast.call_count)

        recovery_broadcast = self.gateway_node.broadcast.call_args_list[0]
        ((gettxs_message, ), recovery_kwargs) = recovery_broadcast
        self.assertIsInstance(gettxs_message, GetTxsMessage)
        self.assertIn(ConnectionType.RELAY_TRANSACTION,
                      recovery_kwargs["connection_types"])

        key_broadcast = self.gateway_node.broadcast.call_args_list[1]
        ((key_message, _conn), recovery_kwargs) = key_broadcast
        self.assertIsInstance(key_message, KeyMessage)
        self.assertIn(ConnectionType.GATEWAY,
                      recovery_kwargs["connection_types"])

        txs = [tx for tx in short_id_mapping.values()]
        txs_message = TxsMessage(txs=txs)
        self.sut.msg_txs(txs_message)

        self._assert_block_sent(block)
Beispiel #13
0
def get_txid(
        buffer: Union[memoryview, bytearray]) -> Tuple[OntObjectHash, int]:
    if not isinstance(buffer, memoryview):
        buffer = memoryview(buffer)
    off = 1
    txtype = buffer[off:off + 1]
    off += 1 + 40
    # Deploy type
    if txtype == ont_common_constants.ONT_TX_DEPLOY_TYPE_INDICATOR_AS_BYTEARRAY:
        buffer_length, size = ont_varint_to_int(buffer, off)
        off += buffer_length + size
        off += 1
        buffer_length, size = ont_varint_to_int(buffer, off)
        off += buffer_length + size
        buffer_length, size = ont_varint_to_int(buffer, off)
        off += buffer_length + size
        buffer_length, size = ont_varint_to_int(buffer, off)
        off += buffer_length + size
        buffer_length, size = ont_varint_to_int(buffer, off)
        off += buffer_length + size
        buffer_length, size = ont_varint_to_int(buffer, off)
        off += buffer_length + size
    # Invoke type
    elif txtype == ont_common_constants.ONT_TX_INVOKE_TYPE_INDICATOR_AS_BYTEARRAY:
        buffer_length, size = ont_varint_to_int(buffer, off)
        off += buffer_length + size

    _, size = ont_varint_to_int(buffer, off)
    off += size

    return OntObjectHash(buf=crypto.double_sha256(buffer[:off]),
                         length=ont_common_constants.ONT_HASH_LEN), off
Beispiel #14
0
    def test_msg_key_wait_for_broadcast(self):
        ont_block = self.ont_block()
        bx_block = self.bx_block(ont_block)

        key, ciphertext = symmetric_encrypt(bx_block)
        block_hash = crypto.double_sha256(ciphertext)

        self.gateway_node.broadcast.assert_not_called()

        key_message = KeyMessage(Sha256Hash(block_hash), self.TEST_NETWORK_NUM,
                                 "", key)
        self.sut.msg_key(key_message)

        # handle duplicate broadcasts
        self.sut.msg_key(key_message)
        self.sut.msg_key(key_message)

        self.assertEqual(1, len(self.gateway_node.in_progress_blocks))

        broadcast_message = BroadcastMessage(Sha256Hash(block_hash),
                                             self.TEST_NETWORK_NUM, "",
                                             BroadcastMessageType.BLOCK, True,
                                             ciphertext)
        self.sut.msg_broadcast(broadcast_message)

        self._assert_block_sent(ont_block)
    def block_to_bx_block(
            self, block_msg, tx_service
    ) -> Tuple[memoryview, BlockInfo]:
        """
        Compresses a Bitcoin block's transactions and packs it into a bloXroute block.
        """
        compress_start_datetime = datetime.utcnow()
        compress_start_timestamp = time.time()
        size = 0
        buf = deque()
        short_ids = []
        header = block_msg.header()
        size += len(header)
        buf.append(header)

        for tx in block_msg.txns():

            tx_hash = btc_common_utils.get_txid(tx)
            short_id = tx_service.get_short_id(tx_hash)
            if short_id == constants.NULL_TX_SID:
                buf.append(tx)
                size += len(tx)
            else:
                short_ids.append(short_id)
                buf.append(btc_constants.BTC_SHORT_ID_INDICATOR_AS_BYTEARRAY)
                size += 1

        serialized_short_ids = compact_block_short_ids_serializer.serialize_short_ids_into_bytes(short_ids)
        buf.append(serialized_short_ids)
        size += constants.UL_ULL_SIZE_IN_BYTES
        offset_buf = struct.pack("<Q", size)
        buf.appendleft(offset_buf)
        size += len(serialized_short_ids)

        block = bytearray(size)
        off = 0
        for blob in buf:
            next_off = off + len(blob)
            block[off:next_off] = blob
            off = next_off

        prev_block_hash = convert.bytes_to_hex(block_msg.prev_block_hash().binary)
        bx_block_hash = convert.bytes_to_hex(crypto.double_sha256(block))
        original_size = len(block_msg.rawbytes())

        block_info = BlockInfo(
            block_msg.block_hash(),
            short_ids,
            compress_start_datetime,
            datetime.utcnow(),
            (time.time() - compress_start_timestamp) * 1000,
            block_msg.txn_count(),
            bx_block_hash,
            prev_block_hash,
            original_size,
            size,
            100 - float(size) / original_size * 100
        )
        return memoryview(block), block_info
    def block_to_bx_block(
            self, block_msg, tx_service, enable_block_compression: bool,
            min_tx_age_seconds: float) -> Tuple[memoryview, BlockInfo]:
        """
        Compresses a Bitcoin block's transactions and packs it into a bloXroute block.
        """
        compress_start_datetime = datetime.utcnow()
        compress_start_timestamp = time.time()
        size = 0
        buf = deque()
        short_ids = []
        original_size = len(block_msg.rawbytes())
        ignored_sids = []

        header = block_msg.header()
        size += len(header)
        buf.append(header)

        max_timestamp_for_compression = time.time() - min_tx_age_seconds

        for tx in block_msg.txns():
            tx_hash = btc_common_utils.get_txid(tx)
            transaction_key = tx_service.get_transaction_key(tx_hash)
            short_id = tx_service.get_short_id_by_key(transaction_key)

            short_id_assign_time = 0
            if short_id != constants.NULL_TX_SID:
                short_id_assign_time = tx_service.get_short_id_assign_time(
                    short_id)

            if short_id == constants.NULL_TX_SID or \
                    not enable_block_compression or \
                    short_id_assign_time > max_timestamp_for_compression:
                if short_id != constants.NULL_TX_SIDS:
                    ignored_sids.append(short_id)
                buf.append(tx)
                size += len(tx)
            else:
                short_ids.append(short_id)
                buf.append(btc_constants.BTC_SHORT_ID_INDICATOR_AS_BYTEARRAY)
                size += 1

        block = finalize_block_bytes(buf, size, short_ids)

        prev_block_hash = convert.bytes_to_hex(
            block_msg.prev_block_hash().binary)
        bx_block_hash = convert.bytes_to_hex(crypto.double_sha256(block))

        block_info = BlockInfo(block_msg.block_hash(), short_ids,
                               compress_start_datetime, datetime.utcnow(),
                               (time.time() - compress_start_timestamp) * 1000,
                               block_msg.txn_count(), bx_block_hash,
                               prev_block_hash, original_size, size,
                               100 - float(size) / original_size * 100,
                               ignored_sids)

        return memoryview(block), block_info
    def msg_headers(self, msg: HeadersOntMessage):
        if not self.node.should_process_block_hash():
            return

        header = msg.headers()[-1]
        raw_hash = crypto.double_sha256(header)
        reply = GetDataOntMessage(self.magic, InventoryOntType.MSG_BLOCK.value,
                                  OntObjectHash(buf=raw_hash, length=ont_constants.ONT_HASH_LEN))
        self.connection.enqueue_msg(reply)
Beispiel #18
0
 def encrypt_and_add_payload(self, payload):
     """
     Encrypts payload, computing a hash and storing it along with the key for later release.
     If encryption is disabled for dev, store ciphertext identical to hash_key.
     """
     key, ciphertext = symmetric_encrypt(bytes(payload))
     hash_key = crypto.double_sha256(ciphertext)
     self._add(hash_key, key, ciphertext, payload)
     return ciphertext, hash_key
 def bdn_tx_to_bx_tx(
         self,
         raw_tx: Union[bytes, bytearray, memoryview],
         network_num: int,
         transaction_flag: Optional[TransactionFlag] = None) -> TxMessage:
     return TxMessage(Sha256Hash(crypto.double_sha256(raw_tx)),
                      network_num,
                      tx_val=raw_tx,
                      transaction_flag=transaction_flag)
class BtcConnectionProtocolTest(AbstractTestCase):
    HASH = BtcObjectHash(binary=crypto.double_sha256(b"123"))

    def setUp(self):
        opts = gateway_helpers.get_gateway_opts(8000,
                                                include_default_btc_args=True)
        if opts.use_extensions:
            helpers.set_extensions_parallelism()
        self.node = MockGatewayNode(opts)
        self.node.block_processing_service = MagicMock()

        self.connection = MagicMock()
        gateway_helpers.add_blockchain_peer(self.node, self.connection)
        self.connection.node = self.node
        self.connection.peer_ip = LOCALHOST
        self.connection.peer_port = 8001
        self.connection.network_num = 2
        self.connection.endpoint = IpEndpoint(self.connection.peer_ip,
                                              self.connection.peer_port)
        self.node.blockchain_peers.add(
            BlockchainPeerInfo(self.connection.peer_ip,
                               self.connection.peer_port))
        gateway_bdn_performance_stats_service.set_node(self.node)

        self.sut = BtcBaseConnectionProtocol(self.connection)

    def test_msg_block_success(self):
        block_timestamp = int(
            time.time()
        ) + 1 - self.node.opts.blockchain_ignore_block_interval_count * self.node.opts.blockchain_block_interval
        txns = [
            TxBtcMessage(0, 0, [], [], i).rawbytes()[BTC_HDR_COMMON_OFF:]
            for i in range(10)
        ]
        message = BlockBtcMessage(0, 0, self.HASH, self.HASH, block_timestamp,
                                  0, 0, txns)

        self.sut.msg_block(message)
        self.node.block_processing_service.queue_block_for_processing.assert_called_once(
        )

    def test_msg_block_too_old(self):
        block_timestamp = int(
            time.time()
        ) - 1 - self.node.opts.blockchain_ignore_block_interval_count * self.node.opts.blockchain_block_interval
        txns = [
            TxBtcMessage(0, 0, [], [], i).rawbytes()[BTC_HDR_COMMON_OFF:]
            for i in range(10)
        ]
        message = BlockBtcMessage(0, 0, self.HASH, self.HASH, 0,
                                  block_timestamp, 0, txns)

        self.sut.msg_block(message)
        self.node.block_processing_service.queue_block_for_processing.assert_not_called(
        )
 def test_get_connection_protocol_version__wrong_message(self):
     wrong_message = BroadcastMessage(message_hash=Sha256Hash(
         crypto.double_sha256(b"hello")),
                                      network_num=1,
                                      source_id="",
                                      blob=bytearray(1))
     input_buffer = InputBuffer()
     input_buffer.add_bytes(wrong_message.rawbytes())
     self.assertEqual(
         3,
         self.version_manager.get_connection_protocol_version(input_buffer))
    def start_transaction_recovery(
            self,
            unknown_sids: Iterable[int],
            unknown_hashes: Iterable[Sha256Hash],
            block_hash: Sha256Hash,
            connection: Optional[AbstractRelayConnection] = None):
        all_unknown_sids = []
        all_unknown_sids.extend(unknown_sids)
        tx_service = self._node.get_tx_service()

        # retrieving sids of txs with unknown contents
        for tx_hash in unknown_hashes:
            tx_sid = tx_service.get_short_id(tx_hash)
            all_unknown_sids.append(tx_sid)

        get_txs_message = GetTxsMessage(short_ids=all_unknown_sids)
        self._node.broadcast(
            get_txs_message,
            connection_types=[ConnectionType.RELAY_TRANSACTION])

        if connection is not None:
            tx_stats.add_txs_by_short_ids_event(
                all_unknown_sids,
                TransactionStatEventType.
                TX_UNKNOWN_SHORT_IDS_REQUESTED_BY_GATEWAY_FROM_RELAY,
                network_num=self._node.network_num,
                peer=connection.peer_desc,
                block_hash=convert.bytes_to_hex(block_hash.binary))
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_STARTED,
                network_num=connection.network_num,
                request_hash=convert.bytes_to_hex(
                    crypto.double_sha256(get_txs_message.rawbytes())))
        else:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_REPEATED,
                network_num=self._node.network_num,
                request_hash=convert.bytes_to_hex(
                    crypto.double_sha256(get_txs_message.rawbytes())))
Beispiel #23
0
    def test_msg_broadcast_encrypted(self, mock_handle_decrypted_block):
        msg_bytes = helpers.generate_bytearray(50)
        msg_hash = Sha256Hash(crypto.double_sha256(msg_bytes))

        broadcast_msg = BroadcastMessage(message_hash=msg_hash, network_num=1, is_encrypted=True, blob=msg_bytes)

        self.connection.msg_broadcast(broadcast_msg)

        self.assertTrue(self.node.in_progress_blocks.has_ciphertext_for_hash(msg_hash))
        self.assertEqual(1, len(self.node.broadcast_messages))
        self.assertIsInstance(self.node.broadcast_messages[0][0], BlockReceivedMessage)
        mock_handle_decrypted_block.assert_not_called()
Beispiel #24
0
 def bdn_tx_to_bx_tx(
     self,
     raw_tx: Union[bytes, bytearray, memoryview],
     network_num: int,
     transaction_flag: Optional[TransactionFlag] = None,
     account_id: str = common_constants.DECODED_EMPTY_ACCOUNT_ID
 ) -> TxMessage:
     return TxMessage(Sha256Hash(crypto.double_sha256(raw_tx)),
                      network_num,
                      tx_val=raw_tx,
                      transaction_flag=transaction_flag,
                      account_id=account_id)
 def msg_block_propagation_request(self, msg):
     bx_block = msg.blob()
     bx_block_hash = crypto.double_sha256(bx_block)
     block_stats.add_block_event_by_block_hash(
         bx_block_hash,
         BlockStatEventType.BX_BLOCK_PROPAGATION_REQUESTED_BY_PEER,
         network_num=self.network_num,
         peers=[self],
     )
     self.node.neutrality_service.propagate_block_to_network(bx_block,
                                                             self,
                                                             from_peer=True)
Beispiel #26
0
    def test_msg_broadcast_unencrypted(self, mock_handle_decrypted_block):
        msg_bytes = helpers.generate_bytearray(50)
        msg_hash = Sha256Hash(crypto.double_sha256(msg_bytes))

        broadcast_msg = BroadcastMessage(message_hash=msg_hash, network_num=1, is_encrypted=False, blob=msg_bytes)

        self.connection.msg_broadcast(broadcast_msg)

        self.assertFalse(self.node.in_progress_blocks.has_ciphertext_for_hash(msg_hash))
        self.assertEqual(0, len(self.node.broadcast_messages))

        mock_handle_decrypted_block.assert_called_once()
class OntConnectionProtocolTest(AbstractTestCase):
    HASH = OntObjectHash(binary=crypto.double_sha256(b"123"))

    def setUp(self):
        opts = gateway_helpers.get_gateway_opts(8000,
                                                include_default_ont_args=True)
        if opts.use_extensions:
            helpers.set_extensions_parallelism()
        self.node = MockOntGatewayNode(opts)
        self.node.block_processing_service = MagicMock()

        self.connection = MagicMock()
        gateway_helpers.add_blockchain_peer(self.node, self.connection)

        self.connection.node = self.node
        self.connection.peer_ip = LOCALHOST
        self.connection.peer_port = 8001
        self.connection.network_num = 2
        self.connection.endpoint = IpEndpoint(self.connection.peer_ip,
                                              self.connection.peer_port)
        self.node.blockchain_peers.add(
            BlockchainPeerInfo(self.connection.peer_ip,
                               self.connection.peer_port))
        gateway_bdn_performance_stats_service.set_node(self.node)

        self.sut = OntBaseConnectionProtocol(self.connection)

    def test_msg_block_success(self):
        block_timestamp = int(time.time()) + 1 - \
                          self.node.opts.blockchain_ignore_block_interval_count * \
                          self.node.opts.blockchain_block_interval
        message = BlockOntMessage(0, 0, self.HASH, self.HASH,
                                  self.HASH, block_timestamp, 0, 0, bytes(10),
                                  bytes(20), [bytes(33)] * 5, [bytes(2)] * 3,
                                  [bytes(32)] * 5, self.HASH)

        self.sut.msg_block(message)
        self.node.block_processing_service.queue_block_for_processing.assert_called_once(
        )

    def test_msg_block_too_old(self):
        block_timestamp = int(time.time()) - 1 - \
                          self.node.opts.blockchain_ignore_block_interval_count * \
                          self.node.opts.blockchain_block_interval
        message = BlockOntMessage(0, 0, self.HASH, self.HASH,
                                  self.HASH, block_timestamp, 0, 0, bytes(10),
                                  bytes(20), [bytes(33)] * 5, [bytes(2)] * 3,
                                  [bytes(32)] * 5, self.HASH)

        self.sut.msg_block(message)
        self.node.block_processing_service.queue_block_for_processing.assert_not_called(
        )
    def clean_block_transactions(
            self, block_msg: BlockBtcMessage,
            transaction_service: TransactionService) -> None:
        block_short_ids = []
        block_unknown_tx_hashes = []
        start_time = time.time()

        short_ids_count = 0
        unknown_tx_hashes_count = 0
        transactions_processed = 0

        tx_hash_to_contents_len_before_cleanup = transaction_service.get_tx_hash_to_contents_len(
        )
        short_id_count_before_cleanup = transaction_service.get_short_id_count(
        )

        for tx in block_msg.txns():
            tx_hash = BtcObjectHash(buf=crypto.double_sha256(tx),
                                    length=BTC_SHA_HASH_LEN)
            short_ids = transaction_service.remove_transaction_by_tx_hash(
                tx_hash, force=True)
            if short_ids is None:
                unknown_tx_hashes_count += 1
                block_unknown_tx_hashes.append(tx_hash)
            else:
                short_ids_count += len(short_ids)
                block_short_ids.extend(short_ids)
            transactions_processed += 1
        block_hash = block_msg.block_hash()
        transaction_service.on_block_cleaned_up(block_hash)
        end_time = time.time()
        duration = end_time - start_time
        tx_hash_to_contents_len_after_cleanup = transaction_service.get_tx_hash_to_contents_len(
        )
        short_id_count_after_cleanup = transaction_service.get_short_id_count()

        logger.debug(
            "Finished cleaning up block {}. Processed {} hashes, {} of which were unknown, and cleaned up {} "
            "short ids. Took {:.3f}s.", block_hash, transactions_processed,
            unknown_tx_hashes_count, short_ids_count, duration)

        transaction_service.log_block_transaction_cleanup_stats(
            block_hash, block_msg.txn_count(),
            tx_hash_to_contents_len_before_cleanup,
            tx_hash_to_contents_len_after_cleanup,
            short_id_count_before_cleanup, short_id_count_after_cleanup)

        self._block_hash_marked_for_cleanup.discard(block_hash)
        self.node.post_block_cleanup_tasks(
            block_hash=block_hash,
            short_ids=block_short_ids,
            unknown_tx_hashes=block_unknown_tx_hashes)
Beispiel #29
0
def btc_block(timestamp=None, real_block=None):
    if real_block is not None:
        return BlockBtcMessage(buf=bytearray(convert.hex_to_bytes(real_block)))
    if timestamp is None:
        timestamp = int(time.time())

    magic = 12345
    version = 23456
    prev_block_hash = bytearray(crypto.double_sha256(b"123"))
    prev_block = BtcObjectHash(prev_block_hash, length=SHA256_HASH_LEN)
    merkle_root_hash = bytearray(crypto.double_sha256(b"234"))
    merkle_root = BtcObjectHash(merkle_root_hash, length=SHA256_HASH_LEN)
    bits = 2
    nonce = 3

    txns = [
        TxBtcMessage(magic, version, [], [], i).rawbytes()[BTC_HDR_COMMON_OFF:]
        for i in range(10)
    ]

    return BlockBtcMessage(magic, version, prev_block, merkle_root, timestamp,
                           bits, nonce, txns)
Beispiel #30
0
 def block_hash(self) -> OntObjectHash:
     if self._hash_val is None:
         if self._header_offset is None:
             self.parse_message()
         assert self._header_offset is not None
         header = self._memoryview[ont_constants.ONT_HDR_COMMON_OFF:self.
                                   _header_offset]
         raw_hash = crypto.double_sha256(header)
         self._hash_val = OntObjectHash(buf=raw_hash,
                                        length=ont_constants.ONT_HASH_LEN)
     hash_val = self._hash_val
     assert isinstance(hash_val, OntObjectHash)
     return hash_val