mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-09-06 18:14:26 -05:00
* Transaction status, and fixes * Close websocket on ConnectionCloseError, and fix wallet bug * More validation and better error messages * Uncomment lines * Fix tests
260 lines
9.4 KiB
Python
Executable File
260 lines
9.4 KiB
Python
Executable File
import asyncio
|
|
import argparse
|
|
import sys
|
|
import aiohttp
|
|
import time
|
|
from time import struct_time, localtime
|
|
|
|
from typing import List, Optional
|
|
|
|
from src.server.connection import NodeType
|
|
from src.types.header_block import HeaderBlock
|
|
from src.rpc.rpc_client import RpcClient
|
|
from src.util.byte_types import hexstr_to_bytes
|
|
|
|
|
|
def str2bool(v: str) -> bool:
|
|
if isinstance(v, bool):
|
|
return v
|
|
if v.lower() in ("yes", "true", "t", "y", "1"):
|
|
return True
|
|
elif v.lower() in ("no", "false", "f", "n", "0"):
|
|
return False
|
|
else:
|
|
raise argparse.ArgumentTypeError("Boolean value expected.")
|
|
|
|
|
|
async def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Manage a Chia Full Node from the command line.",
|
|
epilog="You can combine -s and -c. Try 'watch -n 10 python -m script.cli -s -c' if you have 'watch' installed.",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-b",
|
|
"--block_header_hash",
|
|
help="Look up a block by block header hash string.",
|
|
type=str,
|
|
default="",
|
|
)
|
|
parser.add_argument(
|
|
"-s",
|
|
"--state",
|
|
help="Show the current state of the blockchain.",
|
|
type=str2bool,
|
|
nargs="?",
|
|
const=True,
|
|
default=False,
|
|
)
|
|
parser.add_argument(
|
|
"-c",
|
|
"--connections",
|
|
help="List nodes connected to this Full Node.",
|
|
type=str2bool,
|
|
nargs="?",
|
|
const=True,
|
|
default=False,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-p",
|
|
"--rpc-port",
|
|
help=f"Set the port where the Full Node is hosting the RPC interface. See the rpc_port "
|
|
f"under full_node in config.yaml. Defaults to 8555",
|
|
type=int,
|
|
default=8555,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-e",
|
|
"--exit-node",
|
|
help="Shut down the running Full Node",
|
|
nargs="?",
|
|
const=True,
|
|
default=False,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-a",
|
|
"--add-connection",
|
|
help="Connect to another Full Node by ip:port",
|
|
type=str,
|
|
default="",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-r",
|
|
"--remove-connection",
|
|
help="Remove a Node by the first 10 characters of NodeID",
|
|
type=str,
|
|
default="",
|
|
)
|
|
|
|
args = parser.parse_args(args=None if sys.argv[1:] else ["--help"])
|
|
|
|
# print(args)
|
|
try:
|
|
client = await RpcClient.create(args.rpc_port)
|
|
|
|
# print (dir(client))
|
|
# TODO: Add other rpc calls
|
|
# TODO: pretty print response
|
|
if args.state:
|
|
blockchain_state = await client.get_blockchain_state()
|
|
lca_block = blockchain_state["lca"]
|
|
tips = blockchain_state["tips"]
|
|
difficulty = blockchain_state["difficulty"]
|
|
ips = blockchain_state["ips"]
|
|
sync_mode = blockchain_state["sync_mode"]
|
|
total_iters = lca_block.data.total_iters
|
|
num_blocks: int = 10
|
|
|
|
if sync_mode:
|
|
sync_max_block = await client.get_heaviest_block_seen()
|
|
# print (max_block)
|
|
print(
|
|
"Current Blockchain Status. Full Node Syncing to",
|
|
sync_max_block.data.height,
|
|
)
|
|
else:
|
|
print("Current Blockchain Status. Full Node Synced")
|
|
print("Current least common ancestor ", lca_block.header_hash)
|
|
# print ("LCA time",time.ctime(lca_block.data.timestamp),"LCA height:",lca_block.height)
|
|
lca_time = struct_time(localtime(lca_block.data.timestamp))
|
|
print(
|
|
"LCA time",
|
|
time.strftime("%a %b %d %Y %T %Z", lca_time),
|
|
"LCA height:",
|
|
lca_block.height,
|
|
)
|
|
print("Heights of tips: " + str([h.height for h in tips]))
|
|
print(f"Current difficulty: {difficulty}")
|
|
print(f"Current VDF iterations per second: {ips:.0f}")
|
|
# print("LCA data:\n", lca_block.data)
|
|
print("Total iterations since genesis:", total_iters)
|
|
print("")
|
|
heads: List[HeaderBlock] = tips
|
|
added_blocks: List[HeaderBlock] = []
|
|
while len(added_blocks) < num_blocks and len(heads) > 0:
|
|
heads = sorted(heads, key=lambda b: b.height, reverse=True)
|
|
max_block = heads[0]
|
|
if max_block not in added_blocks:
|
|
added_blocks.append(max_block)
|
|
heads.remove(max_block)
|
|
prev: Optional[HeaderBlock] = await client.get_header(
|
|
max_block.prev_header_hash
|
|
)
|
|
if prev is not None:
|
|
heads.append(prev)
|
|
|
|
latest_blocks_labels = []
|
|
for i, b in enumerate(added_blocks):
|
|
latest_blocks_labels.append(
|
|
f"{b.height}:{b.header_hash}"
|
|
f" {'LCA' if b.header_hash == lca_block.header_hash else ''}"
|
|
f" {'TIP' if b.header_hash in [h.header_hash for h in tips] else ''}"
|
|
)
|
|
for i in range(len(latest_blocks_labels)):
|
|
if i < 2:
|
|
print(latest_blocks_labels[i])
|
|
elif i == 2:
|
|
print(
|
|
latest_blocks_labels[i],
|
|
"\n",
|
|
" -----",
|
|
)
|
|
else:
|
|
print("", latest_blocks_labels[i])
|
|
# if called together with other arguments, leave a blank line
|
|
if args.connections:
|
|
print("")
|
|
if args.connections:
|
|
connections = await client.get_connections()
|
|
print("Connections")
|
|
print(
|
|
f"Type IP Ports NodeID Last Connect"
|
|
f" MB Up|Dwn"
|
|
)
|
|
for con in connections:
|
|
last_connect_tuple = struct_time(localtime(con["last_message_time"]))
|
|
# last_connect = time.ctime(con['last_message_time'])
|
|
last_connect = time.strftime("%b %d %T", last_connect_tuple)
|
|
mb_down = con["bytes_read"] / 1024
|
|
mb_up = con["bytes_written"] / 1024
|
|
# print (last_connect)
|
|
con_str = (
|
|
f"{NodeType(con['type']).name:9} {con['peer_host']:39} "
|
|
f"{con['peer_port']:5}/{con['peer_server_port']:<5}"
|
|
f"{con['node_id'].hex()[:10]}... "
|
|
f"{last_connect} "
|
|
f"{mb_down:7.1f}|{mb_up:<7.1f}"
|
|
)
|
|
print(con_str)
|
|
# if called together with other arguments, leave a blank line
|
|
if args.state:
|
|
print("")
|
|
if args.exit_node:
|
|
node_stop = await client.stop_node()
|
|
print(node_stop, "Node stopped.")
|
|
if args.add_connection:
|
|
if ":" not in args.add_connection:
|
|
print(
|
|
"Enter a valid IP and port in the following format: 10.5.4.3:8000"
|
|
)
|
|
else:
|
|
ip, port = (
|
|
":".join(args.add_connection.split(":")[:-1]),
|
|
args.add_connection.split(":")[-1],
|
|
)
|
|
print(f"Connecting to {ip}, {port}")
|
|
try:
|
|
await client.open_connection(ip, int(port))
|
|
except BaseException:
|
|
# TODO: catch right exception
|
|
print(f"Failed to connect to {ip}:{port}")
|
|
if args.remove_connection:
|
|
result_txt = ""
|
|
if len(args.remove_connection) != 10:
|
|
result_txt = "Invalid NodeID"
|
|
else:
|
|
connections = await client.get_connections()
|
|
for con in connections:
|
|
if args.remove_connection == con["node_id"].hex()[:10]:
|
|
print(
|
|
"Attempting to disconnect", "NodeID", args.remove_connection
|
|
)
|
|
try:
|
|
await client.close_connection(con["node_id"])
|
|
except BaseException:
|
|
result_txt = (
|
|
f"Failed to disconnect NodeID {args.remove_connection}"
|
|
)
|
|
else:
|
|
result_txt = f"NodeID {args.remove_connection}... {NodeType(con['type']).name} "
|
|
f"{con['peer_host']} disconnected."
|
|
elif result_txt == "":
|
|
result_txt = f"NodeID {args.remove_connection}... not found."
|
|
print(result_txt)
|
|
elif args.block_header_hash != "":
|
|
block = await client.get_block(hexstr_to_bytes(args.block_header_hash))
|
|
# print(dir(block))
|
|
if block is not None:
|
|
print("Block header:")
|
|
print(block.header)
|
|
block_time = struct_time(localtime(block.header.data.timestamp))
|
|
print("Block time:", time.strftime("%a %b %d %Y %T %Z", block_time))
|
|
else:
|
|
print("Block hash", args.block_header_hash, "not found.")
|
|
|
|
except Exception as e:
|
|
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
|
print(f"Connection error. Check if full node is running at {args.rpc_port}")
|
|
else:
|
|
print(f"Exception {e}")
|
|
|
|
client.close()
|
|
await client.await_closed()
|
|
|
|
|
|
asyncio.run(main())
|