Ejemplo n.º 1
0
    def handle_push_tx(self, params: Dict[str, Any]) -> bytes:
        try:
            tx_bytes = bytes.fromhex(params['hex_tx'])
            tx = tx_or_block_from_bytes(tx_bytes)
        except ValueError:
            data = {
                'success': False,
                'message': 'Invalid hexadecimal data',
                'can_force': False
            }
        except struct.error:
            data = {
                'success': False,
                'message':
                'This transaction is invalid. Try to decode it first to validate it.',
                'can_force': False
            }
        else:
            if tx.is_block:
                # It's a block and we can't push blocks
                data = {
                    'success': False,
                    'message':
                    'This transaction is invalid. A transaction must have at least one input',
                    'can_force': False
                }
            else:
                tx.storage = self.manager.tx_storage
                # If this tx is a double spending, don't even try to propagate in the network
                assert isinstance(tx, Transaction)
                is_double_spending = tx.is_double_spending()
                if is_double_spending:
                    data = {
                        'success': False,
                        'message':
                        'Invalid transaction. At least one of your inputs has already been spent.',
                        'can_force': False
                    }
                else:
                    success, message = tx.validate_tx_error()

                    if success or params['force']:
                        message = ''
                        try:
                            success = self.manager.propagate_tx(
                                tx, fails_silently=False)
                        except (InvalidNewTransaction, TxValidationError) as e:
                            success = False
                            message = str(e)
                        data = {'success': success, 'message': message}
                        if success:
                            data['tx'] = tx.to_json()
                    else:
                        data = {
                            'success': success,
                            'message': message,
                            'can_force': True
                        }

        return json_dumpb(data)
Ejemplo n.º 2
0
    def render_GET(self, request):
        """ Get request /decode_tx/ that returns the tx decoded, if success

            Expects 'hex_tx' as GET parameter

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        parsed = parse_get_arguments(request.args, ARGS)
        if not parsed['success']:
            return get_missing_params_msg(parsed['missing'])

        try:
            tx_bytes = bytes.fromhex(parsed['args']['hex_tx'])
            tx = tx_or_block_from_bytes(tx_bytes)
            tx.storage = self.manager.tx_storage
            data = get_tx_extra_data(tx)
        except ValueError:
            data = {'success': False, 'message': 'Invalid hexadecimal data'}
        except struct.error:
            data = {'success': False, 'message': 'Could not decode transaction'}

        return json.dumps(data, indent=4).encode('utf-8')
Ejemplo n.º 3
0
    def render_GET(self, request):
        """ Get request /decode_tx/ that returns the tx decoded, if success

            Expects 'hex_tx' as GET parameter

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        if b'hex_tx' in request.args:
            requested_decode = request.args[b'hex_tx'][0].decode('utf-8')
        else:
            return get_missing_params_msg('hex_tx')

        pattern = r'[a-fA-F\d]+'
        if re.match(pattern,
                    requested_decode) and len(requested_decode) % 2 == 0:
            tx_bytes = bytes.fromhex(requested_decode)

            try:
                tx = tx_or_block_from_bytes(tx_bytes)
                tx.storage = self.manager.tx_storage
                data = get_tx_extra_data(tx)
            except struct.error:
                data = {'success': False}

        else:
            data = {'success': False}
        return json.dumps(data, indent=4).encode('utf-8')
Ejemplo n.º 4
0
 def _load_transaction_from_filepath(self, filepath):
     try:
         with open(filepath, 'rb') as fp:
             tx_bytes = fp.read()
             tx = tx_or_block_from_bytes(tx_bytes)
             tx.storage = self
             tx.update_hash()
             return tx
     except FileNotFoundError:
         raise TransactionDoesNotExist
Ejemplo n.º 5
0
    def _load_from_bytes(self, tx_data: bytes,
                         meta_data: bytes) -> 'BaseTransaction':
        from hathor.transaction.base_transaction import tx_or_block_from_bytes
        from hathor.transaction.transaction_metadata import TransactionMetadata

        tx = tx_or_block_from_bytes(tx_data)
        tx._metadata = TransactionMetadata.create_from_json(
            json_loadb(meta_data))
        tx.storage = self
        return tx
Ejemplo n.º 6
0
    def render_POST(self, request: Request) -> Any:
        """ POST request for /thin_wallet/send_tokens/
            We expect 'tx_hex' as request args
            'tx_hex': serialized tx in hexadecimal
            We return success (bool)

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'POST')

        # Validating if we still have unused threads to solve the pow
        if len(self.manager.pow_thread_pool.working
               ) == settings.MAX_POW_THREADS:
            return self.return_POST(
                False,
                'The server is currently fully loaded to send tokens. Wait a moment and try again, please.'
            )

        post_data = json.loads(request.content.read().decode('utf-8'))
        tx_hex = post_data['tx_hex']

        try:
            tx = tx_or_block_from_bytes(bytes.fromhex(tx_hex))
        except struct.error:
            return self.return_POST(
                False, 'Error parsing hexdump to create the transaction')

        assert isinstance(tx, Transaction)
        # Set tx storage
        tx.storage = self.manager.tx_storage

        # If this tx is a double spending, don't even try to propagate in the network
        is_double_spending = tx.is_double_spending()
        if is_double_spending:
            data = {
                'success':
                False,
                'message':
                'Invalid transaction. At least one of your inputs has already been spent.'
            }
            return json.dumps(data, indent=4).encode('utf-8')

        request.should_stop_mining_thread = False

        if settings.SEND_TOKENS_STRATUM and self.manager.stratum_factory:
            self._render_POST_stratum(tx, request)
        else:
            self._render_POST(tx, request)

        request.notifyFinish().addErrback(self._responseFailed, request)

        from twisted.web.server import NOT_DONE_YET
        return NOT_DONE_YET
Ejemplo n.º 7
0
 def do_mining_submit(self,
                      hexdata: str,
                      optimistic: bool = False) -> Union[bool, Dict]:
     if not self.factory.manager.can_start_mining():
         self.log.warn('node syncing')
         return False
     tx = tx_or_block_from_bytes(bytes.fromhex(hexdata),
                                 storage=self.factory.manager.tx_storage)
     if not tx.is_block:
         self.log.warn('expected Block, received Transaction', data=hexdata)
         return False
     res = self.factory.manager.submit_block(tx)
     if res and optimistic:
         return self.manager.make_block_template(tx.hash).to_dict()
     return res
Ejemplo n.º 8
0
    def handle_data(self, payload: str) -> None:
        """ Handle a received DATA message.
        """
        if not payload:
            return
        data = base64.b64decode(payload)

        try:
            tx = tx_or_block_from_bytes(data)
        except struct.error:
            # Invalid data for tx decode
            return

        assert tx is not None
        assert tx.hash is not None

        self.log.debug('tx received from peer',
                       tx=tx.hash_hex,
                       peer=self.protocol.get_peer_id())

        if self.protocol.node.tx_storage.get_genesis(tx.hash):
            # We just got the data of a genesis tx/block. What should we do?
            # Will it reduce peer reputation score?
            return
        tx.storage = self.protocol.node.tx_storage
        assert tx.hash is not None

        key = self.get_data_key(tx.hash)
        deferred = self.deferred_by_key.pop(key, None)
        if deferred:
            # Adding to the DAG will be done after the downloader validates the correct order
            assert tx.timestamp is not None
            self.requested_data_arrived(tx.timestamp)
            deferred.callback(tx)
        elif self.manager.tx_storage.transaction_exists(tx.hash):
            # transaction already added to the storage, ignore it
            # XXX: maybe we could add a hash blacklist and punish peers propagating known bad txs
            return
        else:
            self.log.info('tx received in real time from peer',
                          tx=tx.hash_hex,
                          peer=self.protocol.get_peer_id())
            # If we have not requested the data, it is a new transaction being propagated
            # in the network, thus, we propagate it as well.
            result = self.manager.on_new_tx(tx,
                                            conn=self.protocol,
                                            propagate_to_peers=True)
            self.update_received_stats(tx, result)
Ejemplo n.º 9
0
    def render_POST(self, request):
        """ POST request for /submit_block/
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        data = json_loadb(request.content.read())

        tx = tx_or_block_from_bytes(bytes.fromhex(data['hexdata']), storage=self.manager.tx_storage)

        if not tx.is_block:
            self.log.debug('expected Block, received Transaction', data=data)
            raise APIError('Not a block')

        if not self.manager.can_start_mining():
            self.log.debug('cannot propagate Block, node syncing', data=data)
            raise APIError('Node syncing')

        res = self.manager.submit_block(tx)

        return json_dumpb({'result': res})
Ejemplo n.º 10
0
    def render_GET(self, request):
        """ GET request for /push_tx/
            Expects 'hex_tx' as args parameter that is the hex representation of the whole tx

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        requested_decode = request.args[b'hex_tx'][0].decode('utf-8')

        pattern = r'[a-fA-F\d]+'
        if re.match(pattern,
                    requested_decode) and len(requested_decode) % 2 == 0:
            tx_bytes = bytes.fromhex(requested_decode)

            try:
                tx = tx_or_block_from_bytes(tx_bytes)
            except struct.error:
                data = {
                    'success': False,
                    'message':
                    'This transaction is invalid. Try to decode it first to validate it.',
                    'can_force': False
                }
            else:
                if len(tx.inputs) == 0:
                    # It's a block and we can't push blocks
                    data = {
                        'success': False,
                        'message':
                        'This transaction is invalid. A transaction must have at least one input',
                        'can_force': False
                    }
                else:
                    tx.storage = self.manager.tx_storage
                    # If this tx is a double spending, don't even try to propagate in the network
                    is_double_spending = tx.is_double_spending()
                    if is_double_spending:
                        data = {
                            'success': False,
                            'message':
                            'Invalid transaction. At least one of your inputs has already been spent.',
                            'can_force': False
                        }
                    else:
                        success, message = tx.validate_tx_error()

                        force = b'force' in request.args and request.args[
                            b'force'][0].decode('utf-8') == 'true'
                        if success or force:
                            message = ''
                            try:
                                success = self.manager.propagate_tx(
                                    tx, fails_silently=False)
                            except (InvalidNewTransaction,
                                    TxValidationError) as e:
                                success = False
                                message = str(e)
                            data = {'success': success, 'message': message}
                        else:
                            data = {
                                'success': success,
                                'message': message,
                                'can_force': True
                            }
        else:
            data = {
                'success': False,
                'message':
                'This transaction is invalid. Try to decode it first to validate it.',
                'can_force': False
            }

        return json.dumps(data, indent=4).encode('utf-8')