2018-06-28 15:39:04 +03:00
|
|
|
#
|
|
|
|
# Copyright 2018 Fluence Labs Limited
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
#
|
|
|
|
|
2018-06-25 17:16:14 +03:00
|
|
|
#!/usr/bin/python2.7
|
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-25 17:16:14 +03:00
|
|
|
|
|
|
|
CMD_PUT_TX = "put"
|
2018-06-22 00:08:53 +05:00
|
|
|
CMD_GET_QUERY = "get"
|
|
|
|
CMD_LS_QUERY = "ls"
|
2018-06-27 12:55:49 +03:00
|
|
|
ALL_COMMANDS = {CMD_PUT_TX, CMD_GET_QUERY, 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:
|
2018-06-27 12:55:49 +03:00
|
|
|
print attribute + ":", (8 - len(attribute)) * " ", value
|
2018-06-22 15:44:01 +05:00
|
|
|
|
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-27 12:55:49 +03:00
|
|
|
app_hash_to_show = "0x" + app_hash[0:6] if app_hash != "" else "--------"
|
2018-06-22 15:44:01 +05:00
|
|
|
print_response("height", height)
|
2018-06-27 12:55:49 +03:00
|
|
|
print_response("app_hash", app_hash_to_show or "NOT_READY")
|
|
|
|
# print_response("proof", (proof or "NO_PROOF").upper())
|
2018-06-25 17:16:14 +03:00
|
|
|
print result or "???"
|
|
|
|
if not success:
|
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
|
|
|
|
2018-06-22 18:29:32 +05:00
|
|
|
def wait_for_height(tmaddress, height, seconds_to_wait = 5):
|
|
|
|
wait_step = 0.1
|
|
|
|
for w in range(0, int(seconds_to_wait / wait_step)):
|
2018-06-22 13:10:12 +05:00
|
|
|
if get_max_height(tmaddress) >= height:
|
2018-06-22 00:08:53 +05:00
|
|
|
break
|
2018-06-22 18:29:32 +05:00
|
|
|
time.sleep(wait_step)
|
2018-06-22 18:22:27 +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]
|
2018-06-27 12:55:49 +03:00
|
|
|
if command in {CMD_PUT_TX}:
|
|
|
|
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-27 12:55:49 +03:00
|
|
|
print_response("height", height)
|
|
|
|
# print_response("info", info or "EMPTY") TODO: what is info?
|
2018-06-22 00:08:53 +05:00
|
|
|
elif command in {CMD_GET_QUERY, CMD_LS_QUERY}:
|
|
|
|
height = latest_provable_height(tmaddress)
|
2018-06-25 17:16:14 +03:00
|
|
|
print_checked_abci_query(tmaddress, height, command, arg, None)
|