tendermint-demo/cli/query.py

107 lines
4.1 KiB
Python
Raw Normal View History

2018-06-22 13:10:12 +05:00
#!/usr/bin/python
2018-06-22 00:08:53 +05:00
import sys, urllib, json, datetime, time, hashlib, sha3
2018-06-22 13:10:12 +05:00
from common_parse_utils import read_json, get_sync_info, get_max_height
2018-06-22 00:08:53 +05:00
2018-06-22 15:44:01 +05:00
CMD_FAST_PUT = "fastput"
CMD_PUT = "put"
2018-06-22 00:08:53 +05:00
CMD_RUN = "run"
CMD_GET_QUERY = "get"
CMD_LS_QUERY = "ls"
2018-06-22 15:44:01 +05:00
ALL_COMMANDS = {CMD_GET_QUERY, CMD_PUT, CMD_RUN, CMD_FAST_PUT, CMD_LS_QUERY}
2018-06-22 00:08:53 +05:00
def verify_merkle_proof(result, proof, app_hash):
parts = proof.split(", ")
parts_len = len(parts)
for index in range(parts_len, -1, -1):
low_string = parts[index] if index < parts_len else result
low_hash = hashlib.sha3_256(low_string).hexdigest()
high_hashes = parts[index - 1].split(" ") if index > 0 else [app_hash.lower()]
if not any(low_hash in s for s in high_hashes):
return False
return True
def checked_abci_query(tmaddress, height, command, query, tentative_info):
2018-06-22 13:10:12 +05:00
if get_max_height(tmaddress) < height + 1:
2018-06-22 00:08:53 +05:00
return (height, None, None, None, False, "Cannot verify tentative '%s'! Height is not verifiable" % (info or ""))
2018-06-22 13:10:12 +05:00
app_hash = read_json('%s/block?height=%d' % (tmaddress, height + 1))["result"]["block"]["header"]["app_hash"]
response = read_json('%s/abci_query?height=%d&data="%s:%s"' % (tmaddress, height, command, query))["result"]["response"]
2018-06-22 00:08:53 +05:00
(result, proof) = (
response["value"].decode('base64') if "value" in response else None,
response["proof"].decode('base64') if "proof" in response else None
)
if result is None:
2018-06-22 13:10:12 +05:00
return (height, result, proof, app_hash, False, "Result is empty")
2018-06-22 00:08:53 +05:00
elif tentative_info is not None and result != tentative_info:
2018-06-22 13:10:12 +05:00
return (height, result, proof, app_hash, False, "Verified result '%s' doesn't match tentative '%s'!" % (result, info))
2018-06-22 00:08:53 +05:00
elif proof is None:
2018-06-22 13:10:12 +05:00
return (height, result, proof, app_hash, False, "No proof")
elif not verify_merkle_proof(result, proof, app_hash) :
return (height, result, proof, app_hash, False, "Proof is invalid")
2018-06-22 00:08:53 +05:00
else:
2018-06-22 13:10:12 +05:00
return (height, result, proof, app_hash, True, "")
2018-06-22 00:08:53 +05:00
2018-06-22 15:44:01 +05:00
def print_response(attribute, value, always=False):
need_print = always or "v" in flags
if need_print:
print attribute.upper() + ":", (8 - len(attribute)) * " ", value
2018-06-22 00:08:53 +05:00
def print_checked_abci_query(tmaddress, height, command, query, tentative_info):
2018-06-22 13:10:12 +05:00
(height, result, proof, app_hash, success, message) = checked_abci_query(tmaddress, height, command, query, tentative_info)
2018-06-22 15:44:01 +05:00
print_response("height", height)
print_response("app_hash", app_hash or "NOT_READY")
print_response("proof", (proof or "NO_PROOF").upper())
print_response("result", result or "EMPTY", True)
2018-06-22 00:08:53 +05:00
if success:
print "OK"
else:
2018-06-22 15:44:01 +05:00
print_response("bad", message, True)
2018-06-22 00:08:53 +05:00
def latest_provable_height(tmaddress):
2018-06-22 13:10:12 +05:00
return get_sync_info(tmaddress)["latest_block_height"] - 1
2018-06-22 00:08:53 +05:00
def wait_for_height(tmaddress, height):
for w in range(0, 5):
2018-06-22 13:10:12 +05:00
if get_max_height(tmaddress) >= height:
2018-06-22 00:08:53 +05:00
break
time.sleep(1)
2018-06-22 15:44:01 +05:00
2018-06-22 00:08:53 +05:00
2018-06-22 15:44:01 +05:00
num_args = len(sys.argv)
if num_args < 4 or not sys.argv[2] in ALL_COMMANDS:
print "usage: python query.py host:port <command> [flags] arg"
print "<command> is one of:", ", ".join(ALL_COMMANDS)
2018-06-22 00:08:53 +05:00
sys.exit()
tmaddress = sys.argv[1]
command = sys.argv[2]
2018-06-22 15:44:01 +05:00
flags = "".join(sys.argv[3:(num_args - 1)])
arg = sys.argv[num_args - 1]
if command in {CMD_FAST_PUT, CMD_PUT, CMD_RUN}:
2018-06-22 00:08:53 +05:00
if command == CMD_RUN:
query_key = "optarg"
tx = query_key + "=" + arg
else:
tx = arg
query_key = tx.split("=")[0]
2018-06-22 13:10:12 +05:00
response = read_json(tmaddress + '/broadcast_tx_commit?tx="' + tx + '"')
2018-06-22 00:08:53 +05:00
if "error" in response:
2018-06-22 15:44:01 +05:00
print_response("error", response["error"]["data"], True)
2018-06-22 00:08:53 +05:00
else:
height = response["result"]["height"]
if response["result"].get("deliver_tx", {}).get("code", "0") != "0":
2018-06-22 15:44:01 +05:00
print_response("height", height)
print_response("bad", log or "NO_MESSAGE", True)
2018-06-22 00:08:53 +05:00
else:
info = response["result"].get("deliver_tx", {}).get("info")
2018-06-22 15:44:01 +05:00
if command in {CMD_PUT, CMD_RUN} and info is not None:
2018-06-22 00:08:53 +05:00
wait_for_height(tmaddress, height + 1)
print_checked_abci_query(tmaddress, height, "get", query_key, info)
else:
2018-06-22 15:44:01 +05:00
print_response("height", height)
print_response("info", info or "EMPTY")
2018-06-22 00:08:53 +05:00
print "OK"
elif command in {CMD_GET_QUERY, CMD_LS_QUERY}:
height = latest_provable_height(tmaddress)
2018-06-22 15:44:01 +05:00
print_checked_abci_query(tmaddress, height, command, arg, None)