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'
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')
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()
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'
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')
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