def render_POST(self, request): """ POST request for /profiler/ We expect 'start' or 'stop' as request args and, in the case of stop, also an optional parameter 'filepath' 'start': bool to represent it should start the profiler 'stop': bool to represent it should stop the profiler 'filepath': str of the file path where to save the profiler file :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'POST') data_read = request.content.read() post_data = json.loads(data_read.decode('utf-8')) if data_read else {} ret = {'success': True} if 'start' in post_data: self.manager.start_profiler() elif 'stop' in post_data: if 'filepath' in post_data: filepath = post_data['filepath'] else: filepath = self.gen_dump_filename() self.manager.stop_profiler(save_to=filepath) ret['saved_to'] = filepath else: ret['success'] = False return json.dumps(ret, indent=4).encode('utf-8')
def render_GET(self, request): """ GET request for /wallet/history/ Expects 'page' and 'count' as request args 'page' is the pagination number 'count' is the number of elements in each page Returns a history array (can be 'SpentTx' or 'UnspentTx') and the total number of pages :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') page = int(request.args[b'page'][0]) count = int(request.args[b'count'][0]) history_tuple, total = self.manager.wallet.get_history(count, page) history = [] for obj in history_tuple: history_dict = obj.to_dict() history_dict['tx_id'] = history_dict['tx_id'] if 'from_tx_id' in history_dict: history_dict['from_tx_id'] = history_dict['from_tx_id'] history.append(history_dict) data = {'history': history, 'total_pages': math.ceil(total / count)} return json.dumps(data, indent=4).encode('utf-8')
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/address_history/ Expects 'addresses[]' as request args 'addresses[]' is an array of address Returns an array of WalletIndex for each address :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') wallet_index = self.manager.tx_storage.wallet_index if not wallet_index: request.setResponseCode(503) return json.dumps({'success': False}, indent=4).encode('utf-8') addresses = request.args[b'addresses[]'] history = [] seen: Set[bytes] = set() for address_to_decode in addresses: address = address_to_decode.decode('utf-8') for tx_hash in wallet_index.get_from_address(address): tx = self.manager.tx_storage.get_transaction(tx_hash) if tx_hash not in seen: seen.add(tx_hash) history.append(tx.to_json_extended()) data = {'history': history} return json.dumps(data, indent=4).encode('utf-8')
def _render_GET_thread(self, request: Request) -> bytes: """ GET request /graphviz/full.{format} Returns the rendered graph file """ set_cors(request, 'GET') tx_storage = self.manager.tx_storage graphviz = GraphvizVisualizer(tx_storage) if b'weight' in request.args: graphviz.show_weight = self.parse_bool_arg( request.args[b'weight'][0].decode('utf-8')) if b'acc_weight' in request.args: graphviz.show_acc_weight = self.parse_bool_arg( request.args[b'acc_weight'][0].decode('utf-8')) if b'verifications' in request.args: graphviz.include_verifications = self.parse_bool_arg( request.args[b'verifications'][0].decode('utf-8')) if b'funds' in request.args: graphviz.include_funds = self.parse_bool_arg( request.args[b'funds'][0].decode('utf-8')) if b'only_blocks' in request.args: graphviz.only_blocks = self.parse_bool_arg( request.args[b'only_blocks'][0].decode('utf-8')) dot = graphviz.dot(format=self.format.dot) request.setHeader(b'content-type', self.format.content_type) if self.format is FileFormat.DOT: return str(dot).encode('utf-8') return dot.pipe()
def _render_GET_thread(self, request: Request) -> bytes: """ GET request /graphviz/neighbours.{format} Returns the rendered graph file """ set_cors(request, 'GET') tx_storage = self.manager.tx_storage tx_hex = request.args[b'tx'][0].decode('utf-8') success, message = validate_tx_hash(tx_hex, tx_storage) if not success: return json.dumps({ 'success': False, 'message': message }, indent=4).encode('utf-8') graph_type = request.args[b'graph_type'][0].decode('utf-8') max_level = min(int(request.args[b'max_level'][0]), settings.MAX_GRAPH_LEVEL) tx = tx_storage.get_transaction(bytes.fromhex(tx_hex)) graphviz = GraphvizVisualizer(tx_storage) dot = graphviz.tx_neighborhood(tx, format=self.format.dot, max_level=max_level, graph_type=graph_type) request.setHeader(b'content-type', self.format.content_type) if self.format is FileFormat.DOT: return str(dot).encode('utf-8') return dot.pipe()
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/token/ Expects 'id' (hash) as GET parameter of the queried token :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.tx_storage.tokens_index: request.setResponseCode(503) return json.dumps({'success': False}).encode('utf-8') if b'id' in request.args: try: token_uid_str = request.args[b'id'][0].decode('utf-8') token_uid = bytes.fromhex(token_uid_str) except (ValueError, AttributeError): return json.dumps({ 'success': False, 'message': 'Invalid token id' }).encode('utf-8') data = self.get_one_token_data(token_uid) else: data = self.get_list_token_data() return json.dumps(data).encode('utf-8')
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')
def render_GET(self, request): """ Get request to /tips/ that return a list of tips hashes 'timestamp' is an optional parameter to be used in the get_tx_tips method :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') timestamp = None if b'timestamp' in request.args: try: timestamp = int(request.args[b'timestamp'][0]) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid timestamp parameter, expecting an integer' }).encode('utf-8') tx_tips = self.manager.tx_storage.get_tx_tips(timestamp) ret = {'success': True, 'tips': [tip.data.hex() for tip in tx_tips]} return json.dumps(ret).encode('utf-8')
def render_GET(self, request): """ GET request for /get_block_template/ """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') # params raw_address = request.args.get(b'address') if raw_address: address = decode_address(raw_address[0].decode()) else: address = b'' caps = set(map(lambda s: Capabilities(s.decode()), request.args.get(b'capabilities', []))) merged_mining = Capabilities.MERGED_MINING in caps if not self.manager.can_start_mining(): self.log.debug('cannot generate Block Template, node syncing') # XXX: HTTP 503 Service Unavailable is suitable for temporary server errors raise APIError('Node syncing', 503) # get block # XXX: miner can edit block data and output_script, so it's fine if address is None block = self.manager.generate_mining_block(address=address, merge_mined=merged_mining) # serialize data = block.to_json(include_metadata=True) data.pop('hash') data.pop('inputs') data.pop('nonce', None) data.pop('aux_pow', None) return json_dumpb(data)
def render_GET(self, request): """ Get request to /dashboard-tx/ that return a list of blocks and tx We expect two GET parameters: 'block' and 'tx' 'block': int that indicates de quantity of blocks I should return 'tx': int that indicates de quantity of tx I should return :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') # Get quantity for each block_count = int(request.args[b'block'][0]) tx_count = int(request.args[b'tx'][0]) # Restrict counts block_count = min(block_count, settings.MAX_DASHBOARD_COUNT) tx_count = min(tx_count, settings.MAX_DASHBOARD_COUNT) transactions, _ = self.manager.tx_storage.get_newest_txs( count=tx_count) serialized_tx = [tx.to_json_extended() for tx in transactions] blocks, _ = self.manager.tx_storage.get_newest_blocks( count=block_count) serialized_blocks = [block.to_json_extended() for block in blocks] data = { 'transactions': serialized_tx, 'blocks': serialized_blocks, } return json.dumps(data, indent=4).encode('utf-8')
def render_GET(self, request): """ GET request /getmininginfo/ :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.can_start_mining(): return json.dumps({ 'success': False, 'message': 'Node still syncing' }).encode('utf-8') # We can use any address. burn_address = bytes.fromhex( settings.P2PKH_VERSION_BYTE.hex() + 'acbfb94571417423c1ed66f706730c4aea516ac5762cccb8') block = self.manager.generate_mining_block(address=burn_address) height = block.calculate_height() - 1 difficulty = diff_from_weight(block.weight) parent = block.get_block_parent() hashrate = 2**(parent.weight - log(30, 2)) data = { 'hashrate': hashrate, 'difficulty': difficulty, 'blocks': height, 'success': True, } return json.dumps(data, indent=4).encode('utf-8')
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')
def render_GET(self, request): """ GET request for /wallet/address/ Expects a parameter 'new' (boolean) that says if we should create a new address Returns the address (new or old) :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if b'new' in request.args: new = request.args[b'new'][0].decode('utf-8') == 'true' else: new = False if new: # When user clicks 'Generate new address' we have to mark the old one # as used and return a new one but not mark the new as used # Because if the user refreshs the page we need to show the same self.manager.wallet.get_unused_address(mark_as_used=True) address = self.manager.wallet.get_unused_address(mark_as_used=False) data = { 'address': address, } return json.dumps(data, indent=4).encode('utf-8')
def render_POST(self, request: Request) -> bytes: """ POST request for /thin_wallet/address_history/ It has the same behaviour as the GET request but when using the GET we have a limit of addresses to put as query param, otherwise we end up reaching the HTTP content length limit """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'POST') if not self.manager.tx_storage.indexes.addresses: request.setResponseCode(503) return json.dumps({'success': False}, indent=4).encode('utf-8') raw_body_bytes = request.content.read() or b'' raw_body_str = raw_body_bytes.decode('utf-8') try: post_data = json.loads(raw_body_str) except json.JSONDecodeError: return get_missing_params_msg('invalid json') if 'addresses' not in post_data: return get_missing_params_msg('addresses') return self.get_address_history(post_data.get('addresses'), post_data.get('hash'))
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/token/ Expects 'id' (hash) as GET parameter of the queried token :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.tx_storage.tokens_index: request.setResponseCode(503) return json.dumps({'success': False}).encode('utf-8') if b'id' not in request.args: return get_missing_params_msg('id') try: token_uid_str = request.args[b'id'][0].decode('utf-8') token_uid = bytes.fromhex(token_uid_str) except (ValueError, AttributeError): return json.dumps({ 'success': False, 'message': 'Invalid token id' }).encode('utf-8') try: token_info = self.manager.tx_storage.tokens_index.get_token_info( token_uid) except KeyError: return json.dumps({ 'success': False, 'message': 'Unknown token' }).encode('utf-8') mint = [] melt = [] transactions_count = self.manager.tx_storage.tokens_index.get_transactions_count( token_uid) for tx_hash, index in token_info.mint: mint.append({'tx_id': tx_hash.hex(), 'index': index}) for tx_hash, index in token_info.melt: melt.append({'tx_id': tx_hash.hex(), 'index': index}) data = { 'name': token_info.name, 'symbol': token_info.symbol, 'success': True, 'mint': mint, 'melt': melt, 'total': token_info.total, 'transactions_count': transactions_count, } return json.dumps(data).encode('utf-8')
def render_GET(self, request): """ Get request to /dashboard-tx/ that return a list of blocks and tx We expect two GET parameters: 'block' and 'tx' 'block': int that indicates de quantity of blocks I should return 'tx': int that indicates de quantity of tx I should return :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']) # Get quantity for each try: block_count = int(parsed['args']['block']) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid parameter, cannot convert to int: block' }).encode('utf-8') try: tx_count = int(parsed['args']['tx']) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid parameter, cannot convert to int: tx' }).encode('utf-8') # Restrict counts block_count = min(block_count, settings.MAX_DASHBOARD_COUNT) tx_count = min(tx_count, settings.MAX_DASHBOARD_COUNT) transactions, _ = self.manager.tx_storage.get_newest_txs( count=tx_count) serialized_tx = [tx.to_json_extended() for tx in transactions] blocks, _ = self.manager.tx_storage.get_newest_blocks( count=block_count) serialized_blocks = [block.to_json_extended() for block in blocks] data = { 'success': True, 'transactions': serialized_tx, 'blocks': serialized_blocks, } return json.dumps(data, indent=4).encode('utf-8')
def render_GET(self, request): """ Get request /wallet/nano-contract/decode/ 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 = Transaction.create_from_struct(tx_bytes) except struct.error: data = {'success': False, 'message': 'Invalid transaction'} return json.dumps(data).encode('utf-8') outputs = [] nano_contract = None for _output in tx.outputs: _nano_contract = NanoContractMatchValues.parse_script( _output.script) if _nano_contract: nano_contract = _nano_contract.to_human_readable() nano_contract['value'] = _output.value continue else: outputs.append(_output.to_human_readable()) my_inputs, other_inputs = self.manager.wallet.separate_inputs( tx.inputs, self.manager.tx_storage) my_inputs = [_in.to_human_readable() for _in in my_inputs] other_inputs = [_in.to_human_readable() for _in in other_inputs] data = { 'success': True, 'nano_contract': nano_contract, 'outputs': outputs, 'my_inputs': my_inputs, 'other_inputs': other_inputs } else: data = {'success': False, 'message': 'Invalid transaction'} return json.dumps(data).encode('utf-8')
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
def render_GET(self, request): """ GET request for /wallet/balance/ Returns the int balance of the wallet :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') data = {'balance': self.manager.wallet.balance[settings.HATHOR_TOKEN_UID]._asdict()} return json.dumps(data, indent=4).encode('utf-8')
def render_POST(self, request): """ Creates a nano contract tx and returns it in hexadecimal format. Post data should be a json with the following items: values: List[{'address', 'value'}], with bet address and value fallback_address: if none of the addresses above is the winner, this address can execute the contract oracle_pubkey_hash: oracle's public key hashed oracle_data_id: oracle's id about this nano contract total_value: nano contract total value input_value: amount this wallet should stake in the nano contract :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'POST') try: data = json.loads(request.content.read().decode('utf-8')) except json.JSONDecodeError: return json.dumps({'success': False, 'message': 'Invalid format for post data'}).encode('utf-8') for param in PARAMS_POST: if param not in data: return get_missing_params_msg(param) try: decoded_params = self.decode_post_params(data) except ValueError as e: return json.dumps({'success': False, 'message': e.message}).encode('utf-8') nano_contract = NanoContractMatchValues( decoded_params.oracle_pubkey_hash, decoded_params.min_timestamp, decoded_params.oracle_data_id, decoded_params.value_dict, decoded_params.fallback_address ) tx_outputs = [] tx_outputs.append(TxOutput(decoded_params.total_value, nano_contract.create_output_script())) inputs, total_inputs_amount = self.manager.wallet.get_inputs_from_amount( decoded_params.input_value, self.manager.tx_storage ) change_tx = self.manager.wallet.handle_change_tx(total_inputs_amount, decoded_params.input_value) if change_tx: tx_outputs.append(TxOutput(change_tx.value, P2PKH.create_output_script(change_tx.address))) tx_inputs = [TxInput(txin.tx_id, txin.index, b'') for txin in inputs] tx = Transaction(inputs=tx_inputs, outputs=tx_outputs) ret = {'success': True, 'hex_tx': tx.get_struct().hex()} return json.dumps(ret).encode('utf-8')
def render_GET(self, request): """ GET request for /wallet/state/ Returns a boolean saying if the wallet is locked 'is_locked': True|False :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') data = {'is_locked': self.manager.wallet.is_locked(), 'type': self.manager.wallet.type.value} return json.dumps(data, indent=4).encode('utf-8')
def render_POST(self, request): """ Lock the wallet :return: Boolean if the user locked the wallet with success :rtype: string (json) Dict['success', bool] """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'POST') self.manager.wallet.lock() ret = {'success': True} return json.dumps(ret, indent=4).encode('utf-8')
def render_POST(self, request): """ Add p2p peers It expects a list of peers, in the format protocol://host:port (tcp://172.121.212.12:40403) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'POST') try: peers = json.loads(request.content.read().decode('utf-8')) except (json.JSONDecodeError, AttributeError): return json.dumps({ 'success': False, 'message': 'Invalid format for post data' }).encode('utf-8') if not isinstance(peers, list): return json.dumps({ 'success': False, 'message': 'Invalid format for post data. It was expected a list of strings.' }).encode('utf-8') known_peers = self.manager.connections.peer_storage.values() def already_connected(connection_string: str) -> bool: # determines if given connection string is already among connected or connecting peers endpoint_url = connection_string.replace('//', '') # ignore peers that we're already trying to connect if endpoint_url in self.manager.connections.iter_not_ready_endpoints( ): return True # remove peers we already know about for peer in known_peers: if connection_string in peer.entrypoints: return True return False filtered_peers = [ connection_string for connection_string in peers if not already_connected(connection_string) ] pd = BootstrapPeerDiscovery(filtered_peers) pd.discover_and_connect(self.manager.connections.connect_to) ret = {'success': True, 'peers': filtered_peers} return json.dumps(ret, indent=4).encode('utf-8')
def render_GET(self, request): """ Get request to /tips-histogram/ that return the number of tips between two timestamp We expect two GET parameters: 'begin' and 'end' 'begin': int that indicates the beginning of the interval 'end': int that indicates the end of the interval :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 json.dumps({ 'success': False, 'message': 'Missing parameter: {}'.format(parsed['missing']) }).encode('utf-8') args = parsed['args'] # Get quantity for each try: begin = int(args['begin']) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid parameter, cannot convert to int: begin' }).encode('utf-8') try: end = int(args['end']) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid parameter, cannot convert to int: end' }).encode('utf-8') v = [] for timestamp in range(begin, end + 1): tx_tips = self.manager.tx_storage.get_tx_tips(timestamp) v.append((timestamp, len(tx_tips))) return json.dumps({'success': True, 'tips': v}).encode('utf-8')
def render_GET(self, request): """ GET request for /miners/ Returns statistics about connected miners :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.stratum_factory: request.setResponseCode(503) return json.dumps({'success': False}, indent=4).encode('utf-8') return json.dumps(self.manager.stratum_factory.get_stats_resource()).encode('utf-8')
def render_POST(self, request): """ Post request /create_tx/ that returns an encoded tx, if valid Expects {"inputs":[{"tx_id": <hex encoded>, "index": <int>, "data": <optional base64 encoded>}], "outputs":[{"value": <int, 1.00 HTR = 100>, "token_uid": <optional omit for HTR, hex encoded>, "address" or "script"}]} as POST data """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'POST') body_content = json_loadb(request.content.read()) raw_inputs = body_content.get('inputs', []) raw_outputs = body_content.get('outputs', []) inputs = [TxInput.create_from_dict(i) for i in raw_inputs] tokens = [] outputs = [from_raw_output(i, tokens) for i in raw_outputs] timestamp = int(max(self.manager.tx_storage.latest_timestamp, self.manager.reactor.seconds())) parents = self.manager.get_new_tx_parents(timestamp) # this tx will have to be mined by tx-mining-server or equivalent tx = Transaction( timestamp=timestamp, inputs=inputs, outputs=outputs, parents=parents, storage=self.manager.tx_storage, ) fake_signed_tx = tx.clone() for tx_input in fake_signed_tx.inputs: # conservative estimate of the input data size to estimate a valid weight tx_input.data = b'\0' * 107 tx.weight = minimum_tx_weight(fake_signed_tx) tx.verify_unsigned_skip_pow() if tx.is_double_spending(): raise InvalidNewTransaction('At least one of your inputs has already been spent.') hex_data = bytes(tx).hex() data = tx.to_json() data.pop('hash', None) data.pop('nonce', None) return json_dumpb({ 'success': True, 'hex_data': hex_data, 'data': data, })
def render_GET(self, request): """ GET request for /websocket_stats/ that returns the stats from Websocket :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') data = { 'connections': len(self.websocket_factory.connections), 'subscribed_addresses': len(self.websocket_factory.address_connections), } return json.dumps(data, indent=4).encode('utf-8')
def render_GET(self, request): """ Get request to /tips/ that return a list of tips hashes 'timestamp' is an optional expected parameter to be used in the get_tx_tips method :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') timestamp = None if b'timestamp' in request.args: timestamp = int(request.args[b'timestamp'][0]) tx_tips = self.manager.tx_storage.get_tx_tips(timestamp) ret = [tip.data.hex() for tip in tx_tips] return json.dumps(ret).encode('utf-8')
def render_GET(self, request): """ Get request /transaction_acc_weight/ that returns the acc_weight data of a tx Expects 'id' (hash) as GET parameter of the tx we will return the data :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if b'id' not in request.args: return get_missing_params_msg('id') requested_hash = request.args[b'id'][0].decode('utf-8') data = self._render_GET_data(requested_hash) return json.dumps(data, indent=4).encode('utf-8')
def render_GET(self, request: 'Request') -> bytes: """ Get request /block_at_height/ that returns a block at height in parameter 'height': int, the height of block to get :rtype: string (json) """ assert self.manager.tx_storage.indexes is not None request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') # Height parameter is required parsed = parse_get_arguments(request.args, ['height']) if not parsed['success']: return get_missing_params_msg(parsed['missing']) args = parsed['args'] # Height parameter must be an integer try: height = int(args['height']) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid \'height\' parameter, expected an integer' }).encode('utf-8') # Get hash of the block with the height block_hash = self.manager.tx_storage.indexes.height.get(height) # If there is no block in the index with this height, block_hash will be None if block_hash is None: return json.dumps({ 'success': False, 'message': 'No block with height {}.'.format(height) }).encode('utf-8') block = self.manager.tx_storage.get_transaction(block_hash) data = {'success': True, 'block': block.to_json_extended()} return json.dumps(data, indent=4).encode('utf-8')