2018-08-09 03:19:15 +02:00
|
|
|
import struct
|
2018-07-12 03:31:50 +02:00
|
|
|
import binascii
|
2018-11-04 07:24:41 +01:00
|
|
|
from torba.client.hash import double_sha256
|
2018-07-12 03:31:50 +02:00
|
|
|
|
|
|
|
|
2018-07-15 07:20:44 +02:00
|
|
|
class InvalidProofError(Exception):
|
|
|
|
pass
|
2018-07-12 03:31:50 +02:00
|
|
|
|
|
|
|
|
2018-08-09 03:19:15 +02:00
|
|
|
def get_hash_for_outpoint(txhash, nout, height_of_last_takeover):
|
|
|
|
return double_sha256(
|
|
|
|
double_sha256(txhash) +
|
|
|
|
double_sha256(str(nout).encode()) +
|
|
|
|
double_sha256(struct.pack('>Q', height_of_last_takeover))
|
|
|
|
)
|
2018-07-12 03:31:50 +02:00
|
|
|
|
|
|
|
|
|
|
|
# noinspection PyPep8
|
2018-11-23 05:24:21 +01:00
|
|
|
def verify_proof(proof, root_hash, name):
|
2018-07-12 03:31:50 +02:00
|
|
|
previous_computed_hash = None
|
2018-07-15 21:23:31 +02:00
|
|
|
reverse_computed_name = ''
|
2018-07-12 03:31:50 +02:00
|
|
|
verified_value = False
|
|
|
|
for i, node in enumerate(proof['nodes'][::-1]):
|
|
|
|
found_child_in_chain = False
|
2018-07-15 05:02:19 +02:00
|
|
|
to_hash = b''
|
2018-07-12 03:31:50 +02:00
|
|
|
previous_child_character = None
|
|
|
|
for child in node['children']:
|
|
|
|
if child['character'] < 0 or child['character'] > 255:
|
|
|
|
raise InvalidProofError("child character not int between 0 and 255")
|
|
|
|
if previous_child_character:
|
|
|
|
if previous_child_character >= child['character']:
|
|
|
|
raise InvalidProofError("children not in increasing order")
|
|
|
|
previous_child_character = child['character']
|
2018-11-23 05:24:21 +01:00
|
|
|
to_hash += bytes((child['character'],))
|
2018-07-12 03:31:50 +02:00
|
|
|
if 'nodeHash' in child:
|
|
|
|
if len(child['nodeHash']) != 64:
|
|
|
|
raise InvalidProofError("invalid child nodeHash")
|
|
|
|
to_hash += binascii.unhexlify(child['nodeHash'])[::-1]
|
|
|
|
else:
|
|
|
|
if previous_computed_hash is None:
|
|
|
|
raise InvalidProofError("previous computed hash is None")
|
|
|
|
if found_child_in_chain is True:
|
|
|
|
raise InvalidProofError("already found the next child in the chain")
|
|
|
|
found_child_in_chain = True
|
2018-07-15 21:23:31 +02:00
|
|
|
reverse_computed_name += chr(child['character'])
|
2018-07-12 03:31:50 +02:00
|
|
|
to_hash += previous_computed_hash
|
|
|
|
|
|
|
|
if not found_child_in_chain:
|
|
|
|
if i != 0:
|
|
|
|
raise InvalidProofError("did not find the alleged child")
|
|
|
|
if i == 0 and 'txhash' in proof and 'nOut' in proof and 'last takeover height' in proof:
|
|
|
|
if len(proof['txhash']) != 64:
|
|
|
|
raise InvalidProofError("txhash was invalid: {}".format(proof['txhash']))
|
2018-10-18 19:01:13 +02:00
|
|
|
if not isinstance(proof['nOut'], int):
|
2018-07-12 03:31:50 +02:00
|
|
|
raise InvalidProofError("nOut was invalid: {}".format(proof['nOut']))
|
2018-10-18 19:01:18 +02:00
|
|
|
if not isinstance(proof['last takeover height'], int):
|
2018-07-12 03:31:50 +02:00
|
|
|
raise InvalidProofError(
|
|
|
|
'last takeover height was invalid: {}'.format(proof['last takeover height']))
|
|
|
|
to_hash += get_hash_for_outpoint(
|
|
|
|
binascii.unhexlify(proof['txhash'])[::-1],
|
|
|
|
proof['nOut'],
|
|
|
|
proof['last takeover height']
|
|
|
|
)
|
|
|
|
verified_value = True
|
|
|
|
elif 'valueHash' in node:
|
|
|
|
if len(node['valueHash']) != 64:
|
|
|
|
raise InvalidProofError("valueHash was invalid")
|
|
|
|
to_hash += binascii.unhexlify(node['valueHash'])[::-1]
|
|
|
|
|
2018-08-09 03:19:15 +02:00
|
|
|
previous_computed_hash = double_sha256(to_hash)
|
2018-07-12 03:31:50 +02:00
|
|
|
|
2018-11-23 05:24:21 +01:00
|
|
|
if previous_computed_hash != binascii.unhexlify(root_hash)[::-1]:
|
2018-07-12 03:31:50 +02:00
|
|
|
raise InvalidProofError("computed hash does not match roothash")
|
|
|
|
if 'txhash' in proof and 'nOut' in proof:
|
|
|
|
if not verified_value:
|
|
|
|
raise InvalidProofError("mismatch between proof claim and outcome")
|
2019-02-14 20:54:23 +01:00
|
|
|
target = reverse_computed_name[::-1].encode('ISO-8859-1').decode()
|
2018-07-12 03:31:50 +02:00
|
|
|
if 'txhash' in proof and 'nOut' in proof:
|
2019-02-14 20:54:23 +01:00
|
|
|
if name != target:
|
2018-07-12 03:31:50 +02:00
|
|
|
raise InvalidProofError("name did not match proof")
|
2019-02-14 20:54:23 +01:00
|
|
|
if not name.startswith(target):
|
2018-07-12 03:31:50 +02:00
|
|
|
raise InvalidProofError("name fragment does not match proof")
|
|
|
|
return True
|