explorer.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. #!/usr/bin/env python3
  2. from datetime import datetime, timezone
  3. from quart import Quart, render_template, abort, g
  4. from rpc_client import JsonRpcPool, JsonRpcError
  5. app = Quart(__name__)
  6. app.config.update(
  7. RPC_HOST="127.0.0.1",
  8. RPC_PORT="22222",
  9. RPC_MIN_CONNECTIONS=5,
  10. RPC_MAX_CONNECTIONS=50,
  11. NETWORK="testnet",
  12. )
  13. # Global pool
  14. rpc: JsonRpcPool = None
  15. @app.before_serving
  16. async def startup():
  17. global rpc
  18. rpc = JsonRpcPool(
  19. host=app.config["RPC_HOST"],
  20. port=app.config["RPC_PORT"],
  21. min_connections=app.config["RPC_MIN_CONNECTIONS"],
  22. max_connections=app.config["RPC_MAX_CONNECTIONS"],
  23. )
  24. await rpc.start()
  25. app.logger.info(f"RPC pool started: {app.config['RPC_HOST']}:{app.config['RPC_PORT']}")
  26. @app.after_serving
  27. async def shutdown():
  28. await rpc.close()
  29. app.logger.info("RPC pool closed")
  30. @app.errorhandler(JsonRpcError)
  31. async def handle_rpc_error(error: JsonRpcError):
  32. app.logger.error(f"RPC Error: {error.code} - {error.message}")
  33. if error.code == -32600:
  34. abort(404)
  35. return await render_template("error.html", error=error.message), 500
  36. @app.errorhandler(ConnectionError)
  37. async def handle_connection_error(error):
  38. app.logger.error(f"RPC Connection Error: {error}")
  39. return await render_template("error.html", error="Service temporarily unavailable"), 503
  40. @app.route("/")
  41. async def index():
  42. current_difficulty = await rpc.call("current_difficulty", params=[])
  43. current_height = await rpc.call("current_height", params=[])
  44. latest_blocks = await rpc.call("latest_blocks", params=[20])
  45. # TODO: hashrate
  46. # TODO: emission
  47. # TODO: mempool_txs = await rpc.call("mempool", params=[])
  48. for block in latest_blocks:
  49. dt = datetime.fromtimestamp(block["timestamp"], tz=timezone.utc)
  50. block["timestamp"] = dt.strftime("%B %d, %Y at %I:%M %p UTC")
  51. return await render_template(
  52. "index.html",
  53. network=app.config["NETWORK"],
  54. current_difficulty=current_difficulty[0],
  55. current_height=current_height,
  56. #mempool_txs=mempool_txs,
  57. #mempool_txs_len=len(mempool_txs),
  58. latest_blocks=latest_blocks,
  59. )
  60. @app.route("/block/<int:block_height>")
  61. async def get_block_by_height(block_height: int):
  62. current_difficulty = await rpc.call("current_difficulty", params=[])
  63. current_height = await rpc.call("current_height", params=[])
  64. block = await rpc.call("get_block", params=[block_height])
  65. dt = datetime.fromtimestamp(block["timestamp"], tz=timezone.utc)
  66. block["timestamp"] = dt.strftime("%B %d, %Y at %I:%M %p UTC")
  67. block["n_txs"] = len(block["txs"])
  68. return await render_template(
  69. "block.html",
  70. network=app.config["NETWORK"],
  71. current_difficulty=current_difficulty[0],
  72. current_height=current_height,
  73. block=block,
  74. )
  75. @app.route("/tx/<tx_hash>")
  76. async def get_tx_by_hash(tx_hash: str):
  77. current_difficulty = await rpc.call("current_difficulty", params=[])
  78. current_height = await rpc.call("current_height", params=[])
  79. tx = await rpc.call("get_tx", params=[tx_hash])
  80. return await render_template(
  81. "tx.html",
  82. network=app.config["NETWORK"],
  83. current_difficulty=current_difficulty[0],
  84. current_height=current_height,
  85. tx=tx,
  86. )
  87. if __name__ == "__main__":
  88. app.run(host="127.0.0.1", port=5000, debug=True)