예제 #1
0
    def render_POST(self, request):
        """ POST request /mining/
            Expects a parameter 'block_bytes' that is the block in bytes
            Create the block object from the bytes and propagate it

            :rtype: bytes
        """
        try:
            post_data = json.loads(request.content.read().decode('utf-8'))
            block_bytes_str = post_data['block_bytes']
            block_bytes = base64.b64decode(block_bytes_str)
            block = Block.create_from_struct(block_bytes,
                                             storage=self.manager.tx_storage)
        except (AttributeError, KeyError, ValueError, json.JSONDecodeError,
                binascii.Error, struct.error):
            # XXX ideally, we should catch each error separately and send an specific error
            # message, but we only return 0 or 1 on the API
            # AttributeError, json.JSONDecodeError: empty data or error decoding json
            # KeyError: missing 'block_bytes' on post_data
            # ValueError, struct.error: raised in create_block_from_struct
            # binascii.Error: incorrect base64 data
            return b'0'

        ret = self.manager.submit_block(block)
        if ret:
            return b'1'
        return b'0'
예제 #2
0
    def test_post(self):
        response_get = yield self.web.get('mining')
        data_get = response_get.json_value()
        block_bytes_str = data_get.get('block_bytes')

        block_bytes = base64.b64decode(block_bytes_str)
        block = Block.create_from_struct(block_bytes)
        block.weight = 4
        block.resolve()

        block_bytes = bytes(block)
        block_bytes_str = base64.b64encode(block_bytes).decode('ascii')

        response_post = yield self.web.post('mining',
                                            {'block_bytes': block_bytes_str})
        self.assertEqual(response_post.written[0], b'1')

        block.weight = 100
        block_bytes = bytes(block)
        block_bytes_str = base64.b64encode(block_bytes).decode('ascii')

        response_post = yield self.web.post('mining',
                                            {'block_bytes': block_bytes_str})
        # Probability 2^(100 - 256) of failing
        self.assertEqual(response_post.written[0], b'0')
예제 #3
0
def resolve_block_bytes(block_bytes):
    """ From block bytes we create a block and resolve pow
        Return block bytes with hash and nonce after pow
        :rtype: bytes
    """
    from hathor.transaction import Block
    block_bytes = base64.b64decode(block_bytes)
    block = Block.create_from_struct(block_bytes)
    block.resolve()
    return block.get_struct()
예제 #4
0
    def render_POST(self, request):
        """ POST request /mining/
            Expects a parameter 'block_bytes' that is the block in bytes
            Create the block object from the bytes and propagate it

            :rtype: bytes
        """
        post_data = json.loads(request.content.read().decode('utf-8'))
        block_bytes_str = post_data['block_bytes']
        block_bytes = base64.b64decode(block_bytes_str)
        block = Block.create_from_struct(block_bytes,
                                         storage=self.manager.tx_storage)
        ret = self.manager.propagate_tx(block)
        if ret:
            return b'1'
        return b'0'
예제 #5
0
    def test_post_invalid_data(self):
        response_get = yield self.web.get('mining')
        data_get = response_get.json_value()
        block_bytes_str = data_get.get('block_bytes')

        block_bytes = base64.b64decode(block_bytes_str)
        block = Block.create_from_struct(block_bytes)
        block.weight = 4
        block.resolve()

        block_bytes = bytes(block)
        block_bytes_str = base64.b64encode(block_bytes).decode('ascii')

        # missing post data
        response_post = yield self.web.post('mining')
        self.assertEqual(response_post.written[0], b'0')

        # invalid block bytes
        response_post = yield self.web.post('mining', {'block_bytes': base64.b64encode(b'aaa').decode('ascii')})
        self.assertEqual(response_post.written[0], b'0')

        # invalid base64
        response_post = yield self.web.post('mining', {'block_bytes': 'YWFha'})
        self.assertEqual(response_post.written[0], b'0')
예제 #6
0
def execute(args: Namespace) -> None:
    from requests.exceptions import ConnectionError

    from hathor.transaction import Block
    from hathor.transaction.exceptions import HathorError

    print('Hathor CPU Miner v1.0.0')
    print('URL: {}'.format(args.url))

    if args.init_delay:
        print('Init delay {} seconds'.format(args.init_delay))
        time.sleep(args.init_delay)

    signal.signal(signal.SIGINT, signal_handler)

    sleep_seconds = 0
    if args.sleep:
        sleep_seconds = args.sleep

    total = 0
    conn_retries = 0
    q_in: Queue[Tuple[Block, int, int, int]]
    q_out: Queue[Block]
    q_in, q_out = Queue(), Queue()
    while True:
        print('Requesting mining information...')
        try:
            response = requests.get(args.url)
        except ConnectionError as e:
            print('Error connecting to server: {}'.format(args.url))
            print(e)
            if conn_retries >= _MAX_CONN_RETRIES:
                print('Too many connection failures, giving up.')
                sys.exit(1)
            else:
                conn_retries += 1
                print('Waiting {} seconds to try again ({} of {})...'.format(
                    _SLEEP_ON_ERROR_SECONDS, conn_retries, _MAX_CONN_RETRIES))
                time.sleep(_SLEEP_ON_ERROR_SECONDS)
                continue
        else:
            conn_retries = 0

        if response.status_code == 503:
            print('Node still syncing. Waiting {} seconds to try again...'.
                  format(_SLEEP_ON_ERROR_SECONDS))
            time.sleep(_SLEEP_ON_ERROR_SECONDS)
            continue

        try:
            data = response.json()
        except JSONDecodeError as e:
            print('Error reading response from server: {}'.format(response))
            print(e)
            print('Waiting {} seconds to try again...'.format(
                _SLEEP_ON_ERROR_SECONDS))
            time.sleep(_SLEEP_ON_ERROR_SECONDS)
            continue
        block_bytes = base64.b64decode(data['block_bytes'])
        block = Block.create_from_struct(block_bytes)
        assert block.hash is not None
        assert isinstance(block, Block)
        print('Mining block with weight {}'.format(block.weight))

        p = Process(target=worker, args=(q_in, q_out))
        p.start()
        q_in.put((block, 0, 2**32, sleep_seconds))
        p.join()

        block = q_out.get()
        block.update_hash()
        assert block.hash is not None
        print('[{}] New block found: {} (nonce={}, weight={})'.format(
            datetime.datetime.now(), block.hash.hex(), block.nonce,
            block.weight))

        try:
            block.verify_without_storage()
        except HathorError:
            print(
                '[{}] ERROR: Block has not been pushed because it is not valid.'
                .format(datetime.datetime.now()))
        else:
            block_bytes = block.get_struct()
            response = requests.post(
                args.url,
                json={
                    'block_bytes':
                    base64.b64encode(block_bytes).decode('utf-8')
                })
            if not response.ok:
                print(
                    '[{}] ERROR: Block has been rejected. Unknown exception.'.
                    format(datetime.datetime.now()))

            if response.ok and response.text != '1':
                print('[{}] ERROR: Block has been rejected.'.format(
                    datetime.datetime.now()))

        print('')

        total += 1
        if args.count and total == args.count:
            break