async def get_outpoints_info(request): log = request.app["log"] log.info("POST %s" % str(request.rel_url)) addresses = dict() status = 500 response = { "error_code": INTERNAL_SERVER_ERROR, "message": "internal server error", "details": "" } try: try: await request.post() data = await request.json() outpoints = [] if len(data) > 100: raise APIException(PARAMETER_ERROR, "only 100 outpoints allowed") """ connector_utxo connector_unconfirmed_utxo connector_unconfirmed_stxo """ for outpoint in data: try: o, a = outpoint.split(":") tx_id = s2rh(o) if len(tx_id) != 32: raise Exception() outpoints.append((tx_id, int(a), b"".join( (tx_id, int_to_bytes(int(a)))))) except: raise APIException(PARAMETER_ERROR, "invalid outpoint %s" % outpoint) except: raise APIException(JSON_DECODE_ERROR, "invalid outpoints list") response = await outpoints_info(outpoints, request.app) status = 200 except APIException as err: status = err.status response = { "error_code": err.err_code, "message": err.message, "details": err.details } except Exception as err: if request.app["debug"]: log.error(str(traceback.format_exc())) else: log.error(str(err)) finally: return web.json_response(response, dumps=json.dumps, status=status)
async def get_block_transactions_list(request): log = request.app["log"] log.info("GET %s" % str(request.rel_url)) status = 500 response = {"error_code": INTERNAL_SERVER_ERROR, "message": "internal server error", "details": ""} parameters = request.rel_url.query try: try: limit = int(parameters["limit"]) if not (limit > 0 and limit <= request.app["get_block_tx_page_limit"]): raise Exception() except: limit = request.app["get_block_tx_page_limit"] try: page = int(parameters["page"]) if page <= 0: raise Exception() except: page = 1 try: order = "asc" if parameters["order"] == "asc" else "desc" except: order = "asc" pointer = request.match_info['block_pointer'] if len(pointer) < 12: try: pointer = int(pointer) except: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") elif len(pointer) == 64: try: pointer = s2rh(pointer) except: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") else: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") response = await block_transaction_id_list(pointer, limit, page, order, request.app) status = 200 except APIException as err: status = err.status response = {"error_code": err.err_code, "message": err.message, "details": err.details} except Exception as err: if request.app["debug"]: log.error(str(traceback.format_exc())) else: log.error(str(err)) finally: return web.json_response(response, dumps=json.dumps, status = status)
async def get_block_headers(request): log = request.app["log"] log.info("GET %s" % str(request.rel_url)) status = 500 response = { "error_code": INTERNAL_SERVER_ERROR, "message": "internal server error", "details": "" } try: pointer = request.match_info['block_pointer'] if len(pointer) < 12: try: pointer = int(pointer) except: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") elif len(pointer) == 64: try: pointer = s2rh(pointer) except: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") else: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") try: count = request.match_info['count'] except: count = 2000 try: count = int(count) if count < 1 and count > 2000: count = 2000 except: raise APIException(PARAMETER_ERROR, "invalid count parameter") response = await block_headers(pointer, count, request.app) status = 200 except APIException as err: status = err.status response = { "error_code": err.err_code, "message": err.message, "details": err.details } except Exception as err: if request.app["debug"]: log.error(str(traceback.format_exc())) else: log.error(str(err)) finally: return web.json_response(response, dumps=json.dumps, status=status)
async def get_transaction_by_pointer(request): log = request.app["log"] log.info("GET %s" % str(request.rel_url)) pointer = request.match_info['tx_pointer'] status = 500 response = {"error_code": INTERNAL_SERVER_ERROR, "message": "internal server error", "details": ""} option_raw_tx = False try: if request.rel_url.query['raw_tx'] in ('True','1'): option_raw_tx = True except: pass try: if len(pointer) == 64: try: pointer = s2rh(pointer) except: raise APIException(PARAMETER_ERROR, "invalid transaction hash") else: try: k = pointer.split(":") b = int(k[0]) t = int(k[1]) if b < 0: raise Exception() if t < 0: raise Exception() pointer = (b << 39) + (t << 20) except: raise APIException(PARAMETER_ERROR, "invalid transaction pointer") response = await tx_by_pointer_opt_tx(pointer, option_raw_tx, request.app) status = 200 except APIException as err: status = err.status response = {"error_code": err.err_code, "message": err.message, "details": err.details } except Exception as err: if request.app["debug"]: log.error(str(traceback.format_exc())) else: log.error(str(err)) finally: return web.json_response(response, dumps=json.dumps, status = status)
async def get_block_data_by_pointer(request, pointer=None): log = request.app["log"] log.info("GET %s" % str(request.rel_url)) status = 500 response = {"error_code": INTERNAL_SERVER_ERROR, "message": "internal server error", "details": ""} parameters = request.rel_url.query try: if parameters["statistics"] in ('1', 'True'): stat = True else: stat = False except: stat = False try: if pointer is None: pointer = request.match_info['block_pointer'] if len(pointer) < 12: try: pointer = int(pointer) except: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") elif len(pointer) == 64: try: pointer = s2rh(pointer) except: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") else: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") response = await block_data_by_pointer(pointer, stat, request.app) status = 200 except APIException as err: status = err.status response = {"error_code": err.err_code, "message": err.message, "details": err.details} except Exception as err: if request.app["debug"]: log.error(str(traceback.format_exc())) else: log.error(str(err)) finally: return web.json_response(response, dumps=json.dumps, status = status)
async def get_block_filters_batch_headers(request): log = request.app["log"] log.info("GET %s" % str(request.rel_url)) status = 500 response = { "error_code": INTERNAL_SERVER_ERROR, "message": "internal server error", "details": "" } filter_type = int(request.match_info['filter_type']) start_height = int(request.match_info['start_height']) stop_hash = request.match_info['stop_hash'] try: try: stop_hash = s2rh(stop_hash) if len(stop_hash) != 32: raise Exception() except: raise APIException(INVALID_BLOCK_POINTER, "invalid stop hash") if filter_type < 1: raise APIException(PARAMETER_ERROR, "invalid filter_type") if start_height < 0: raise APIException(PARAMETER_ERROR, "invalid start_height") response = await block_filters_batch_headers(filter_type, start_height, stop_hash, log, request.app) status = 200 except APIException as err: status = err.status response = { "error_code": err.err_code, "message": err.message, "details": err.details } except Exception as err: if request.app["debug"]: log.error(str(traceback.format_exc())) else: log.error(str(err)) finally: return web.json_response(response, dumps=json.dumps, status=status)
async def _new_block(self, block): self.block_dependency_tx = 0 """ 0 Check if block already exist in db 1 Check parent block in db: If no parent get last block height from db if last block height >= recent block height this is orphan ignore it else: remove top block from db and ask block with hrecent block height -1 return 2 add all transactions from block to db ask full block from node parse txs and add to db in case not exist 3 call before add block handler^ if this handler rise exception block adding filed 4 add block to db and commit 5 after block add handelr 6 ask next block """ if not self.active or not self.active_block.done(): return if block is None: self.sync = False self.log.debug('Block synchronization completed') return self.active_block = asyncio.Future() binary_block_hash = unhexlify(block["hash"]) binary_previousblock_hash = \ unhexlify(block["previousblockhash"]) \ if "previousblockhash" in block else None block_height = int(block["height"]) next_block_height = block_height + 1 self.log.info("New block %s %s" % (block_height, block["hash"])) bt = q = tm() try: async with self._db_pool.acquire() as con: # blockchain position check block_exist = self.block_cache.get(binary_block_hash) if block_exist is not None: self.log.info("block already exist in db %s" % block["hash"]) return # Get parent from db if binary_previousblock_hash is not None: parent_height = self.block_cache.get( binary_previousblock_hash) else: parent_height = None # self.log.warning("parent height %s" % parent_height) if parent_height is None: # have no mount point in local chain # self.log.warning("last local height %s" % self.last_block_height) if self.last_block_height is not None: if self.last_block_height >= block_height: self.log.critical( "bitcoin node out of sync block %s" % block["hash"]) return if self.last_block_height + 1 == block_height: if self.orphan_handler: tq = tm() await self.orphan_handler( self.last_block_height, con) self.log.info("orphan handler %s [%s]" % (self.last_block_height, tm(tq))) tq = tm() await remove_orphan(self, con) self.log.info("remove orphan %s [%s]" % (self.last_block_height + 1, tm(tq))) next_block_height -= 2 if next_block_height > self.last_block_height: next_block_height = self.last_block_height + 1 if self.sync and next_block_height >= self.sync: if self.sync_requested: next_block_height = self.last_block_height + 1 else: self.sync = next_block_height return else: if self.start_block is not None and block[ "height"] != self.start_block: self.log.info("Start from block %s" % self.start_block) next_block_height = self.start_block return else: if self.last_block_height + 1 != block_height: if self.orphan_handler: tq = tm() await self.orphan_handler(self.last_block_height, con) self.log.info("orphan handler %s [%s]" % (self.last_block_height, tm(tq))) await remove_orphan(self, con) next_block_height -= 1 self.log.debug("requested %s" % next_block_height) return self.log.debug("blockchain position check [%s]" % tm(q)) # add all block transactions q = tm() binary_tx_hash_list = [unhexlify(t)[::-1] for t in block["tx"]] if block["height"] in (91842, 91880): # BIP30 Fix self.tx_cache.pop( s2rh( "d5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599" )) self.tx_cache.pop( s2rh( "e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468" )) tx_id_list, missed = await get_tx_id_list( self, binary_tx_hash_list, con) if len(tx_id_list) + len(missed) != len(block["tx"]): raise Exception("tx count mismatch") self.await_tx_id_list = tx_id_list if self.before_block_handler: sn = await self.before_block_handler( block, missed, self.sync_tx_lock, self.node_last_block, con) if sn and missed: self.await_tx_id_list = self.await_tx_id_list + [ 0 for i in range(len(missed)) ] missed = [] cq = tm() missed = [rh2s(t) for t in missed] self.log.info("Transactions already exist: %s missed %s [%s]" % (len(tx_id_list), len(missed), tm(q))) if missed: self.log.debug("Request missed transactions") self.missed_tx_list = list(missed) self.await_tx_list = missed self.await_tx_future = dict() for i in missed: self.await_tx_future[unhexlify(i) [::-1]] = asyncio.Future() self.block_txs_request = asyncio.Future() if len(missed) == len(block["tx"]): self.loop.create_task( self._get_missed(block["hash"], block["time"], block["height"])) else: self.loop.create_task( self._get_missed(False, block["time"], block["height"])) try: await asyncio.wait_for(self.block_txs_request, timeout=self.block_timeout) except asyncio.CancelledError: # refresh rpc connection session await self.rpc.close() self.rpc = aiojsonrpc.rpc(self.rpc_url, self.loop, timeout=self.rpc_timeout) raise RuntimeError("block transaction request timeout") if len(block["tx"]) != len(self.await_tx_id_list): self.log.error("get block transactions failed") self.log.error(str(self.await_tx_id_list)) raise Exception("get block transactions failed") tx_count = len(self.await_tx_id_list) self.total_received_tx += tx_count self.total_received_tx_time += tm(q) rate = round(self.total_received_tx / self.total_received_tx_time) self.log.info( "Transactions received: %s [%s] rate tx/s ->> %s <<" % (tx_count, tm(cq), rate)) async with con.transaction(): if self.block_received_handler: await self.block_received_handler(block, con) # insert new block await insert_new_block(self, binary_block_hash, block["height"], binary_previousblock_hash, block["time"], con) if not self.external_dublicate_filter: self.loop.create_task( update_block_height(self, block["height"], list(self.await_tx_id_list))) if self.sync == block["height"]: self.sync += 1 next_block_height = self.sync # after block added handler if self.block_handler: await self.block_handler(block, con) self.last_block_height = block["height"] self.block_cache.set(binary_block_hash, block["height"]) except Exception as err: if self.await_tx_list: self.await_tx_list = [] self.log.error(str(traceback.format_exc())) self.log.error("new block error %s" % str(err)) next_block_height = None finally: self.active_block.set_result(True) self.log.debug("block processing completed") if next_block_height is not None: self.sync_requested = True self.loop.create_task( self.get_block_by_height(next_block_height)) self.log.info( "%s block [%s tx/ %s size] (dp %s) processing time %s cache [%s/%s]" % (block["height"], len(block["tx"]), block["size"] / 1000000, self.block_dependency_tx, tm(bt), len(self.block_hashes_preload._store), len(self.block_preload._store)))
async def get_block_transactions(request): log = request.app["log"] log.info("GET %s" % str(request.rel_url)) status = 500 response = {"error_code": INTERNAL_SERVER_ERROR, "message": "internal server error", "details": ""} parameters = request.rel_url.query try: limit = int(parameters["limit"]) if not (limit >= 0): raise Exception() except: limit = 50 try: mode = parameters['mode'] except: mode = "brief" if mode not in ["brief", "verbose"]: mode = "brief" try: page = int(parameters["page"]) if page <= 0: raise Exception() except: page = 1 if limit == 0 or limit > request.app["get_block_tx_page_limit"]: if limit == 0: page = 1 limit = request.app["get_block_tx_page_limit"] if limit * page > 2 ** 19 - 1: limit = 50 page = 1 try: order = "asc" if parameters["order"] == "asc" else "desc" except: order = "asc" try: pointer = request.match_info['block_pointer'] if len(pointer) < 12: try: pointer = int(pointer) except: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") elif len(pointer) == 64: try: pointer = s2rh(pointer) except: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") else: raise APIException(INVALID_BLOCK_POINTER, "invalid block pointer") option_raw_tx = False try: if request.rel_url.query['raw_tx'] in ('True', '1'): option_raw_tx = True except: pass response = await block_transactions(pointer, option_raw_tx, limit, page, order, mode, request.app) status = 200 except APIException as err: status = err.status response = {"error_code": err.err_code, "message": err.message, "details": err.details} except Exception as err: if request.app["debug"]: log.error(str(traceback.format_exc())) else: log.error(str(err)) finally: return web.json_response(response, dumps=json.dumps, status = status)