rpc.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. # This file is part of DarkFi (https://dark.fi)
  2. #
  3. # Copyright (C) 2020-2024 Dyne.org foundation
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU Affero General Public License as
  7. # published by the Free Software Foundation, either version 3 of the
  8. # License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU Affero General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Affero General Public License
  16. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. import asyncio, json, random
  18. from flask import abort
  19. # DarkFi blockchain-explorer daemon JSON-RPC configuration
  20. URL = "127.0.0.1"
  21. PORT = 14567
  22. # Class representing the channel with the JSON-RPC server
  23. class Channel:
  24. def __init__(self, reader, writer):
  25. self.reader = reader
  26. self.writer = writer
  27. async def readline(self):
  28. if not (line := await self.reader.readline()):
  29. self.writer.close()
  30. return None
  31. # Strip the newline
  32. return line[:-1].decode()
  33. async def receive(self):
  34. if (plaintext := await self.readline()) is None:
  35. return None
  36. message = plaintext
  37. response = json.loads(message)
  38. return response
  39. async def send(self, obj):
  40. message = json.dumps(obj)
  41. data = message.encode()
  42. self.writer.write(data + b"\n")
  43. await self.writer.drain()
  44. # Create a Channel for given server.
  45. async def create_channel(server_name, port):
  46. try:
  47. reader, writer = await asyncio.open_connection(server_name, port)
  48. except ConnectionRefusedError:
  49. print(f"Error: Connection Refused to '{server_name}:{port}', Either because the daemon is down, is currently syncing or wrong url.")
  50. abort(500)
  51. channel = Channel(reader, writer)
  52. return channel
  53. # Execute a request towards the JSON-RPC server
  54. async def query(method, params):
  55. channel = await create_channel(URL, PORT)
  56. request = {
  57. "id": random.randint(0, 2**32),
  58. "method": method,
  59. "params": params,
  60. "jsonrpc": "2.0",
  61. }
  62. await channel.send(request)
  63. response = await channel.receive()
  64. # Closed connect returns None
  65. if response is None:
  66. print("error: connection with server was closed")
  67. abort(500)
  68. # Erroneous query is handled with not found
  69. if "error" in response:
  70. error = response["error"]
  71. errcode, errmsg = error["code"], error["message"]
  72. print(f"error: {errcode} - {errmsg}")
  73. abort(404)
  74. return response["result"]
  75. # Retrieve last n blocks from blockchain-explorer daemon
  76. async def get_last_n_blocks(n: str):
  77. return await query("blocks.get_last_n_blocks", [n])
  78. # Retrieve basic statistics from blockchain-explorer daemon
  79. async def get_basic_statistics():
  80. return await query("statistics.get_basic_statistics", [])
  81. # Retrieve the block information of given header hash from blockchain-explorer daemon
  82. async def get_block(header_hash: str):
  83. return await query("blocks.get_block_by_hash", [header_hash])
  84. # Retrieve the transactions of given block header hash from blockchain-explorer daemon
  85. async def get_block_transactions(header_hash: str):
  86. return await query("transactions.get_transactions_by_header_hash", [header_hash])
  87. # Retrieve the transaction information of given hash from blockchain-explorer daemon
  88. async def get_transaction(transaction_hash: str):
  89. return await query("transactions.get_transaction_by_hash", [transaction_hash])