Exemplo n.º 1
0
    def __init__(self, data: bytes, *, add_parents: bool = False, propagate: bool = False,
                 timeout: Optional[float] = None):
        """Init TxJob.

        add_parents: Add parents before mining tx.
        propagate: Propagate tx to the full node after it is mined.
        timeout: Mining timeout.
        """
        self._tx: BaseTransaction = tx_or_block_from_bytes(data)

        self.uuid: bytes = self._tx.get_funds_hash()
        self.add_parents: bool = add_parents
        self.propagate: bool = propagate
        self.timeout: Optional[float] = timeout

        self.expected_queue_time: float = 0
        self.expected_mining_time: float = 0

        self.status: JobStatus = JobStatus.PENDING
        self.message: str = ''
        self.created_at: float = txstratum.time.time()
        self.submitted_at: Optional[float] = None
        self.total_time: Optional[float] = None
        self.nonce: Optional[bytes] = None
        self.timestamp: Optional[int] = None

        self._timeout_timer: Optional[TimerHandle] = None
        self._cleanup_timer: Optional[TimerHandle] = None
Exemplo n.º 2
0
 async def test_submit_job_invalid_non_standard_script(self):
     tx_bytes = update_timestamp(TX1_DATA)
     tx = tx_or_block_from_bytes(tx_bytes)
     tx.outputs[0].script = b'x' * MAX_OUTPUT_SCRIPT_SIZE
     tx_bytes = bytes(tx)
     tx_hex = tx_bytes.hex()
     resp = await self.client.request('POST',
                                      '/submit-job',
                                      json={'tx': tx_hex})
     data = await resp.json()
     self.assertEqual(400, resp.status)
     self.assertEqual({'error': 'txout-non-standard-script'}, data)
Exemplo n.º 3
0
 async def test_submit_job_invalid_tx_weight(self):
     tx_bytes = update_timestamp(TX1_DATA)
     tx = tx_or_block_from_bytes(tx_bytes)
     tx.weight = MAX_TX_WEIGHT + 0.1
     tx_bytes = bytes(tx)
     tx_hex = tx_bytes.hex()
     resp = await self.client.request('POST',
                                      '/submit-job',
                                      json={'tx': tx_hex})
     data = await resp.json()
     self.assertEqual(400, resp.status)
     self.assertEqual({'error': 'tx-weight-is-too-high'}, data)
Exemplo n.º 4
0
def get_timestamp(tx_bytes: bytes) -> int:
    """Get timestamp of a serialized tx."""
    tx = tx_or_block_from_bytes(tx_bytes)
    return tx.timestamp
Exemplo n.º 5
0
def update_timestamp(tx_bytes: bytes, *, delta: int = 0) -> bytes:
    """Update timestamp to current timestamp."""
    tx = tx_or_block_from_bytes(tx_bytes)
    tx.timestamp = int(txstratum.time.time()) + delta
    return bytes(tx)
Exemplo n.º 6
0
    async def submit_job(self, request: web.Request) -> web.Response:
        """Submit a new tx job to the manager.

        Method: POST
        Format: json
        Params:
        - tx: str, hex dump of the transaction
        - propagate: bool, propagate tx to Hathor’s full node after it is solved
        - add_parents: bool, add parents before resolving the tx
        """
        try:
            data = await request.json()
        except json.decoder.JSONDecodeError:
            return web.json_response({'error': 'cannot-decode-json'},
                                     status=400)
        if not isinstance(data, dict):
            return web.json_response({'error': 'json-must-be-an-object'},
                                     status=400)
        tx_hex = data.get('tx')
        if not tx_hex:
            return web.json_response({'error': 'missing-tx'}, status=400)
        try:
            tx_bytes = bytes.fromhex(tx_hex)
            tx = tx_or_block_from_bytes(tx_bytes)
        except (ValueError, TxValidationError):
            return web.json_response({'error': 'invalid-tx'}, status=400)

        if not isinstance(tx, (Transaction, TokenCreationTransaction)):
            return web.json_response({'error': 'invalid-tx'}, status=400)

        if tx.weight > self.max_tx_weight:
            self.log.debug('tx-weight-is-too-high', data=data)
            return web.json_response({'error': 'tx-weight-is-too-high'},
                                     status=400)

        for txout in tx.outputs:
            if len(txout.script) > self.max_output_script_size:
                self.log.debug('txout-script-is-too-big', data=data)
                return web.json_response({'error': 'txout-script-is-too-big'},
                                         status=400)
            if self.only_standard_script:
                p2pkh = P2PKH.parse_script(txout.script)
                if p2pkh is None:
                    return web.json_response(
                        {'error': 'txout-non-standard-script'}, status=400)

        now = txstratum.time.time()
        if abs(tx.timestamp - now) > self.max_timestamp_delta:
            if self.fix_invalid_timestamp:
                tx.timestamp = int(now)
                tx_bytes = bytes(tx)
            else:
                return web.json_response({'error': 'tx-timestamp-invalid'},
                                         status=400)

        if 'timeout' not in data:
            timeout = self.tx_timeout
        else:
            try:
                timeout = min(self.tx_timeout, float(data['timeout']))
            except ValueError:
                return web.json_response({'error': 'invalid-timeout'},
                                         status=400)

            if timeout <= 0:
                return web.json_response({'error': 'invalid-timeout'},
                                         status=400)

        add_parents = data.get('add_parents', False)
        propagate = data.get('propagate', False)

        job = TxJob(tx_bytes,
                    add_parents=add_parents,
                    propagate=propagate,
                    timeout=timeout)
        success = self.manager.add_job(job)
        if not success:
            return web.json_response({'error': 'job-already-exists'},
                                     status=400)
        return web.json_response(job.to_dict())