From bf97eedee54eda5399d3066dc36c0fbf4247b0b7 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg Date: Fri, 14 Feb 2020 14:57:01 -0500 Subject: [PATCH] Add new name proof check algorithm --- lbry/wallet/claim_proofs.py | 12 ++++- scripts/check_claim_proof.py | 54 +++++++++++++++++++++ tests/unit/wallet/test_claim_proofs.py | 66 ++++++++++++++++++++++++-- 3 files changed, 127 insertions(+), 5 deletions(-) create mode 100755 scripts/check_claim_proof.py diff --git a/lbry/wallet/claim_proofs.py b/lbry/wallet/claim_proofs.py index 64692c24a..2db6e4dd8 100644 --- a/lbry/wallet/claim_proofs.py +++ b/lbry/wallet/claim_proofs.py @@ -15,8 +15,18 @@ def get_hash_for_outpoint(txhash, nout, height_of_last_takeover): ) +def is_proof_valid(claim, proof, root_hash) -> bool: + proof_hash = get_hash_for_outpoint(binascii.unhexlify(claim["txId"])[::-1], claim["n"], claim["lastTakeoverHeight"]) + for p in proof["pairs"]: + if p["odd"]: # odd = 1 = the missing hash is coming from the right side of the binary merkle trie + proof_hash = double_sha256(binascii.unhexlify(p["hash"])[::-1] + proof_hash) + else: # even = 0 = left side + proof_hash = double_sha256(proof_hash + binascii.unhexlify(p["hash"])[::-1]) + return root_hash == binascii.hexlify(proof_hash[::-1]).decode("ascii") + + # noinspection PyPep8 -def verify_proof(proof, root_hash, name): +def verify_proof_old(proof, root_hash, name): previous_computed_hash = None reverse_computed_name = '' verified_value = False diff --git a/scripts/check_claim_proof.py b/scripts/check_claim_proof.py new file mode 100755 index 000000000..e67040562 --- /dev/null +++ b/scripts/check_claim_proof.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import sys +import json +import subprocess +from binascii import hexlify, unhexlify +from os.path import basename +from lbry.crypto.hash import double_sha256 +from lbry.wallet.claim_proofs import get_hash_for_outpoint + + +def lbrycrd(cli_path: str, *args: str, decode: bool = True) -> str: + rsp = subprocess.check_output((cli_path,) + args) + return json.loads(rsp) if decode else rsp.strip() + + +def script(cli_path: str, name: str) -> None: + height = lbrycrd(cli_path, "getblockchaininfo")["blocks"] + block_hash = lbrycrd(cli_path, "getblockhash", str(height), decode=False) + claimtrie_root_hash = lbrycrd(cli_path, "getblock", block_hash)["nameclaimroot"] + + claim = lbrycrd(cli_path, "getclaimbybid", name, "0", block_hash) + proof = lbrycrd(cli_path, 'getclaimproofbybid', name, "0", block_hash) + + if not claim: + print(f"No claims for {name}") + return + + print(f"Checking name proof for {name} at block {height} (block hash {block_hash.decode('ascii')})") + print(f"Claimtrie root hash is {claimtrie_root_hash}") + + proof_hash = get_hash_for_outpoint(unhexlify(claim["txId"])[::-1], claim["n"], claim["lastTakeoverHeight"]) + for p in proof["pairs"]: + if p["odd"]: # odd = 1 = the missing hash is coming from the right side of the binary merkle trie + proof_hash = double_sha256(unhexlify(p["hash"])[::-1] + proof_hash) + else: # even = 0 = left side + proof_hash = double_sha256(proof_hash + unhexlify(p["hash"])[::-1]) + + proof_hex = hexlify(proof_hash[::-1]).decode('ascii') + print(f"Proof hash is {proof_hex}") + + if proof_hex == claimtrie_root_hash: + print("Proof is valid") + else: + print("INVALID PROOF") + + +if __name__ == '__main__': + if len(sys.argv) != 3: + print(f"Usage: {basename(sys.argv[0])} LBRYCRD_CLI_PATH NAME") + print("") + print(f"Example: {basename(sys.argv[0])} /usr/local/bin/lbrycrd-cli @lbry") + sys.exit(1) + script(sys.argv[1], sys.argv[2]) diff --git a/tests/unit/wallet/test_claim_proofs.py b/tests/unit/wallet/test_claim_proofs.py index e393043f0..f74cebf99 100644 --- a/tests/unit/wallet/test_claim_proofs.py +++ b/tests/unit/wallet/test_claim_proofs.py @@ -1,12 +1,70 @@ import unittest from binascii import hexlify, unhexlify -from lbry.wallet.claim_proofs import get_hash_for_outpoint, verify_proof +from lbry.wallet.claim_proofs import get_hash_for_outpoint, is_proof_valid, verify_proof_old from lbry.crypto.hash import double_sha256 -class ClaimProofsTestCase(unittest.TestCase): - def test_verify_proof(self): +class TestVerifyProof(unittest.TestCase): + def current_algo(self): + # nameclaimroot from block 716110 + claimtrie_root_hash = "09b4bb94d26e256111c176d744cc26e5dc13bc00f24c3b14bab414aaafd92ee3" + + # winning claim for 'lbry' at block 716110 + claim = { + "txId": "a2c060a316bd56b3bc8f3d9d6997bf03f66625cda51347465ba88ccfac9ef3d7", + "n": 0, + "lastTakeoverHeight": 608276, + } + + # proof for the above claim at block 716110 + proof = { + "pairs": [ + {"odd": False, "hash": "5685300f64d691f1e47115c0e5fe9690e3fa3128bbf5e940db0aedb6270aef8d"}, + {"odd": False, "hash": "64c90cc399288e2c4404cb30344ba1ce575e2598bef2e7971633f50898122cb8"}, + {"odd": False, "hash": "7b777bed2d14e443f2323f7a7b3cd5d1072419c3bb4113441706455dbd119981"}, + {"odd": False, "hash": "66fc7f9a0920e7f8ece40642b0d29493db2489ca9b2e78da33cbb56ca3104745"}, + {"odd": False, "hash": "5172bcdbed3c2b4be5761375538ff0773e53d894fc280c7061624c02bf7ab08d"}, + {"odd": False, "hash": "4e5692b23763c4e1d071cb2dd63270c2c63023f071cb31f37944f8657c104dc5"}, + {"odd": False, "hash": "1fa2806b0ea26a719bb972af27e68beee2548402752713d9d3b52c1688dd6222"}, + {"odd": True, "hash": "144ab44b808d36e98daf698ab07b7631a10bd7db5eef3c1d9d25291036a67463"}, + {"odd": False, "hash": "183789ee0dc723b3b3afcfe421b3ff723c99862520429e1cc7e3e7234c304fc5"}, + {"odd": False, "hash": "701c7848b9cb33a68f84c164b0bec18fc6e5dfd8b09041e3b66cb26501b2b8e6"}, + {"odd": False, "hash": "fdaefcbb0fcf0e12063ee2f32d391c9e5324707c9c62a35a81f90d0d09c8770e"}, + {"odd": True, "hash": "df6828f2c8c6168d91f4d0ea898ddef42d067403fe30425f9782c01d081db64e"}, + {"odd": False, "hash": "8549d8a2dd3e7130da5629ca3bb7f06667c1a7551146d37931c4fc9d33631ba8"}, + {"odd": False, "hash": "489c2eb0f85f96ecae8295822152311b2cfd49781bf88bfc41d2233dc4eb37f4"}, + {"odd": False, "hash": "48f49966410e85025f82a45252fa83e0c71047da5638bfb2f234c58d021ccdde"}, + {"odd": True, "hash": "2dc6c9095320a3a252c996fd0ef3a833cf3b0370be331d312a302b814d5c74d2"}, + {"odd": True, "hash": "291b0e110845b26af972c67bbb5e12d62f6eab10872bf17685dbf776446ce98d"}, + {"odd": True, "hash": "830ec7fb0c87757fc9dd5340cb84f0fc5b09bfddd2f1f8d26c87dbef39e4dc32"}, + {"odd": False, "hash": "c0bfef3ae77acb8cf6b80520e21db51b3f22992ae3c10330405a371393cab9e1"}, + {"odd": False, "hash": "0000000000000000000000000000000000000000000000000000000000000003"}, + {"odd": False, "hash": "9914a968e24e06559c06f970066aac9efa29a66285981f2af9979ce0490124ae"}, + {"odd": True, "hash": "99ab44076946395c6f4cabdec2686b5aa6e0bfac91d4cdcbc759a2231e80b017"}, + {"odd": True, "hash": "49666ed318e0f0101be64ecee4794e5ce016d4d737fad5d61c6a7e703c01b0b4"}, + {"odd": True, "hash": "cccb7ffc80c98caae3cdb61deb89e6ee04a864221c42ed4c8eab43a9b94f17a7"}, + {"odd": False, "hash": "b03cda27b4fb2cd5c112de17e359d83de24530a500173aec58a2e752a3968025"}, + {"odd": False, "hash": "041f4b3b8a16e2bd3bd674715b51ed9e59df27f40de8e7c55bda069c6cb6cc6f"}, + {"odd": False, "hash": "6c17e88bb3d2a0cae5454f07cb56d09e192ef5c351b0316b1f342674bbb3031d"}, + {"odd": False, "hash": "ce01ad2296363eb7e928cf034c3c0dfb9fb2a164f91bc2e70dc3ace21109b73b"}, + {"odd": True, "hash": "2b70d527fd4019e31e7dacbc42bcad2e46172d4290ff8d23090d666559479dbb"}, + {"odd": False, "hash": "27d5eb878fce2145c1903ba84f81cffa2d789c34fa784fbfe2ecae812aab6890"}, + {"odd": False, "hash": "665b34a7e1b2578f2418823f702901f01ece537c96fe7e4d5d7b357e8d90e8d5"}, + {"odd": True, "hash": "18268d3e1dd64a6aca06c688538c365e2e113a057d4a2dd0b629c20d79a74eb5"}, + {"odd": False, "hash": "3e03c59427a569ccb5f8fcd36b3b38da044af981d9996ec94769f1f5458f7a7e"}, + {"odd": True, "hash": "f6c9440b20ad6903c08c21b4b3e7073ac53d10a74bcf2b46081a9b8e00fb4eaf"}, + {"odd": False, "hash": "084b8a05b60968476269e7e95fad736a4c2fb333273ac631d57ef807c7528a2b"}, + {"odd": False, "hash": "0000000000000000000000000000000000000000000000000000000000000003"}, + ], + "txId": "a2c060a316bd56b3bc8f3d9d6997bf03f66625cda51347465ba88ccfac9ef3d7", + "n": 0, + "lastTakeoverHeight": 608276 + } + + self.assertTrue(is_proof_valid(claim, proof, claimtrie_root_hash)) + + def old_algo(self): claim1_name = 97 # 'a' claim1_txid = 'bd9fa7ffd57d810d4ce14de76beea29d847b8ac34e8e536802534ecb1ca43b68' claim1_outpoint = 0 @@ -39,5 +97,5 @@ class ClaimProofsTestCase(unittest.TestCase): {'children': []}, ] } - out = verify_proof(proof, hexlify(root_hash[::-1]), 'a') + out = verify_proof_old(proof, hexlify(root_hash[::-1]), 'a') self.assertTrue(out)