mirror of
https://github.com/LBRYFoundation/lbry-sdk.git
synced 2025-09-01 09:45:13 +00:00
start a new module for isolating resolve related code before we can refactor it
This commit is contained in:
parent
036663ae62
commit
9e89b3e40e
3 changed files with 302 additions and 188 deletions
98
lbrynet/wallet/claim_proofs.py
Normal file
98
lbrynet/wallet/claim_proofs.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import binascii
|
||||||
|
|
||||||
|
from lbryschema.hashing import sha256
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidProofError(Exception): pass
|
||||||
|
|
||||||
|
|
||||||
|
def height_to_vch(n):
|
||||||
|
r = [0 for i in range(8)]
|
||||||
|
r[4] = n >> 24
|
||||||
|
r[5] = n >> 16
|
||||||
|
r[6] = n >> 8
|
||||||
|
r[7] = n % 256
|
||||||
|
# need to reset each value mod 256 because for values like 67784
|
||||||
|
# 67784 >> 8 = 264, which is obviously larger then the maximum
|
||||||
|
# value input into chr()
|
||||||
|
return ''.join([chr(x % 256) for x in r])
|
||||||
|
|
||||||
|
|
||||||
|
def get_hash_for_outpoint(txhash, nOut, nHeightOfLastTakeover):
|
||||||
|
txhash_hash = Hash(txhash)
|
||||||
|
nOut_hash = Hash(str(nOut))
|
||||||
|
height_of_last_takeover_hash = Hash(height_to_vch(nHeightOfLastTakeover))
|
||||||
|
outPointHash = Hash(txhash_hash + nOut_hash + height_of_last_takeover_hash)
|
||||||
|
return outPointHash
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyPep8
|
||||||
|
def verify_proof(proof, rootHash, name):
|
||||||
|
previous_computed_hash = None
|
||||||
|
reverse_computed_name = ''
|
||||||
|
verified_value = False
|
||||||
|
for i, node in enumerate(proof['nodes'][::-1]):
|
||||||
|
found_child_in_chain = False
|
||||||
|
to_hash = ''
|
||||||
|
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']
|
||||||
|
to_hash += chr(child['character'])
|
||||||
|
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
|
||||||
|
reverse_computed_name += chr(child['character'])
|
||||||
|
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']))
|
||||||
|
if not isinstance(proof['nOut'], (long, int)):
|
||||||
|
raise InvalidProofError("nOut was invalid: {}".format(proof['nOut']))
|
||||||
|
if not isinstance(proof['last takeover height'], (long, int)):
|
||||||
|
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]
|
||||||
|
|
||||||
|
previous_computed_hash = Hash(to_hash)
|
||||||
|
|
||||||
|
if previous_computed_hash != binascii.unhexlify(rootHash)[::-1]:
|
||||||
|
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")
|
||||||
|
if 'txhash' in proof and 'nOut' in proof:
|
||||||
|
if name != reverse_computed_name[::-1]:
|
||||||
|
raise InvalidProofError("name did not match proof")
|
||||||
|
if not name.startswith(reverse_computed_name[::-1]):
|
||||||
|
raise InvalidProofError("name fragment does not match proof")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def Hash(x):
|
||||||
|
if type(x) is unicode:
|
||||||
|
x = x.encode('utf-8')
|
||||||
|
return sha256(sha256(x))
|
|
@ -1,15 +1,14 @@
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from ecdsa import BadSignatureError
|
|
||||||
from six import int2byte
|
from six import int2byte
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from lbrynet.core.Error import UnknownNameError, UnknownClaimID, UnknownURI, UnknownOutpoint
|
from lbrynet.core.Error import UnknownNameError, UnknownClaimID, UnknownURI
|
||||||
from lbryschema.address import is_address
|
from .resolve import format_amount_value, _get_permanent_url, validate_claim_signature_and_get_channel_name
|
||||||
from lbryschema.claim import ClaimDict
|
from .resolve import _verify_proof, _handle_claim_result
|
||||||
from lbryschema.decode import smart_decode
|
from lbryschema.decode import smart_decode
|
||||||
from lbryschema.error import URIParseError, DecodeError
|
from lbryschema.error import URIParseError, DecodeError
|
||||||
from lbryschema.uri import parse_lbry_uri
|
from lbryschema.uri import parse_lbry_uri
|
||||||
|
@ -20,7 +19,6 @@ from torba.util import int_to_hex, rev_hex, hash_encode
|
||||||
from .account import Account
|
from .account import Account
|
||||||
from .network import Network
|
from .network import Network
|
||||||
from .database import WalletDatabase
|
from .database import WalletDatabase
|
||||||
from .claim_proofs import verify_proof, InvalidProofError
|
|
||||||
from .transaction import Transaction
|
from .transaction import Transaction
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,9 +182,10 @@ class MainNetLedger(BaseLedger):
|
||||||
height = certificate_response['height']
|
height = certificate_response['height']
|
||||||
depth = self.headers.height - height
|
depth = self.headers.height - height
|
||||||
certificate_result = _verify_proof(self, parsed_uri.name,
|
certificate_result = _verify_proof(self, parsed_uri.name,
|
||||||
claim_trie_root,
|
claim_trie_root,
|
||||||
certificate_response,
|
certificate_response,
|
||||||
height, depth)
|
height, depth,
|
||||||
|
transaction_class=self.transaction_class)
|
||||||
result['certificate'] = self.parse_and_validate_claim_result(certificate_result,
|
result['certificate'] = self.parse_and_validate_claim_result(certificate_result,
|
||||||
raw=raw)
|
raw=raw)
|
||||||
elif certificate_resolution_type == "claim_id":
|
elif certificate_resolution_type == "claim_id":
|
||||||
|
@ -433,183 +432,3 @@ class RegTestLedger(MainNetLedger):
|
||||||
genesis_hash = '6e3fcf1299d4ec5d79c3a4c91d624a4acf9e2e173d95a1a0504f677669687556'
|
genesis_hash = '6e3fcf1299d4ec5d79c3a4c91d624a4acf9e2e173d95a1a0504f677669687556'
|
||||||
genesis_bits = 0x207fffff
|
genesis_bits = 0x207fffff
|
||||||
target_timespan = 1
|
target_timespan = 1
|
||||||
|
|
||||||
# Format amount to be decimal encoded string
|
|
||||||
# Format value to be hex encoded string
|
|
||||||
# TODO: refactor. Came from lbryum, there could be another part of torba doing it
|
|
||||||
def format_amount_value(obj):
|
|
||||||
COIN = 100000000
|
|
||||||
if isinstance(obj, dict):
|
|
||||||
for k, v in obj.iteritems():
|
|
||||||
if k == 'amount' or k == 'effective_amount':
|
|
||||||
if not isinstance(obj[k], float):
|
|
||||||
obj[k] = float(obj[k]) / float(COIN)
|
|
||||||
elif k == 'supports' and isinstance(v, list):
|
|
||||||
obj[k] = [{'txid': txid, 'nout': nout, 'amount': float(amount) / float(COIN)}
|
|
||||||
for (txid, nout, amount) in v]
|
|
||||||
elif isinstance(v, (list, dict)):
|
|
||||||
obj[k] = format_amount_value(v)
|
|
||||||
elif isinstance(obj, list):
|
|
||||||
obj = [format_amount_value(o) for o in obj]
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def _get_permanent_url(claim_result):
|
|
||||||
if claim_result.get('has_signature') and claim_result.get('channel_name'):
|
|
||||||
return "{0}#{1}/{2}".format(
|
|
||||||
claim_result['channel_name'],
|
|
||||||
claim_result['value']['publisherSignature']['certificateId'],
|
|
||||||
claim_result['name']
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return "{0}#{1}".format(
|
|
||||||
claim_result['name'],
|
|
||||||
claim_result['claim_id']
|
|
||||||
)
|
|
||||||
def _verify_proof(ledger, name, claim_trie_root, result, height, depth):
|
|
||||||
"""
|
|
||||||
Verify proof for name claim
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _build_response(name, value, claim_id, txid, n, amount, effective_amount,
|
|
||||||
claim_sequence, claim_address, supports):
|
|
||||||
r = {
|
|
||||||
'name': name,
|
|
||||||
'value': value.encode('hex'),
|
|
||||||
'claim_id': claim_id,
|
|
||||||
'txid': txid,
|
|
||||||
'nout': n,
|
|
||||||
'amount': amount,
|
|
||||||
'effective_amount': effective_amount,
|
|
||||||
'height': height,
|
|
||||||
'depth': depth,
|
|
||||||
'claim_sequence': claim_sequence,
|
|
||||||
'address': claim_address,
|
|
||||||
'supports': supports
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
|
|
||||||
def _parse_proof_result(name, result):
|
|
||||||
support_amount = sum([amt for (stxid, snout, amt) in result['supports']])
|
|
||||||
supports = result['supports']
|
|
||||||
if 'txhash' in result['proof'] and 'nOut' in result['proof']:
|
|
||||||
if 'transaction' in result:
|
|
||||||
tx = Transaction(raw=unhexlify(result['transaction']))
|
|
||||||
nOut = result['proof']['nOut']
|
|
||||||
if result['proof']['txhash'] == tx.hex_id:
|
|
||||||
if 0 <= nOut < len(tx.outputs):
|
|
||||||
claim_output = tx.outputs[nOut]
|
|
||||||
effective_amount = claim_output.amount + support_amount
|
|
||||||
claim_address = ledger.hash160_to_address(claim_output.script.values['pubkey_hash'])
|
|
||||||
claim_id = result['claim_id']
|
|
||||||
claim_sequence = result['claim_sequence']
|
|
||||||
claim_script = claim_output.script
|
|
||||||
decoded_name, decoded_value = claim_script.values['claim_name'], claim_script.values['claim']
|
|
||||||
if decoded_name == name:
|
|
||||||
return _build_response(name, decoded_value, claim_id,
|
|
||||||
tx.hex_id, nOut, claim_output.amount,
|
|
||||||
effective_amount, claim_sequence,
|
|
||||||
claim_address, supports)
|
|
||||||
return {'error': 'name in proof did not match requested name'}
|
|
||||||
outputs = len(tx['outputs'])
|
|
||||||
return {'error': 'invalid nOut: %d (let(outputs): %d' % (nOut, outputs)}
|
|
||||||
return {'error': "computed txid did not match given transaction: %s vs %s" %
|
|
||||||
(tx.hex_id, result['proof']['txhash'])
|
|
||||||
}
|
|
||||||
return {'error': "didn't receive a transaction with the proof"}
|
|
||||||
return {'error': 'name is not claimed'}
|
|
||||||
|
|
||||||
if 'proof' in result:
|
|
||||||
try:
|
|
||||||
verify_proof(result['proof'], claim_trie_root, name)
|
|
||||||
except InvalidProofError:
|
|
||||||
return {'error': "Proof was invalid"}
|
|
||||||
return _parse_proof_result(name, result)
|
|
||||||
else:
|
|
||||||
return {'error': "proof not in result"}
|
|
||||||
|
|
||||||
|
|
||||||
def validate_claim_signature_and_get_channel_name(claim, certificate_claim,
|
|
||||||
claim_address, decoded_certificate=None):
|
|
||||||
if not certificate_claim:
|
|
||||||
return False, None
|
|
||||||
certificate = decoded_certificate or smart_decode(certificate_claim['value'])
|
|
||||||
if not isinstance(certificate, ClaimDict):
|
|
||||||
raise TypeError("Certificate is not a ClaimDict: %s" % str(type(certificate)))
|
|
||||||
if _validate_signed_claim(claim, claim_address, certificate):
|
|
||||||
return True, certificate_claim['name']
|
|
||||||
return False, None
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_signed_claim(claim, claim_address, certificate):
|
|
||||||
if not claim.has_signature:
|
|
||||||
raise Exception("Claim is not signed")
|
|
||||||
if not is_address(claim_address):
|
|
||||||
raise Exception("Not given a valid claim address")
|
|
||||||
try:
|
|
||||||
if claim.validate_signature(claim_address, certificate.protobuf):
|
|
||||||
return True
|
|
||||||
except BadSignatureError:
|
|
||||||
# print_msg("Signature for %s is invalid" % claim_id)
|
|
||||||
return False
|
|
||||||
except Exception as err:
|
|
||||||
log.error("Signature for %s is invalid, reason: %s - %s", claim_address,
|
|
||||||
str(type(err)), err)
|
|
||||||
return False
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: The following came from code handling lbryum results. Now that it's all in one place a refactor should unify it.
|
|
||||||
def _decode_claim_result(claim):
|
|
||||||
if 'has_signature' in claim and claim['has_signature']:
|
|
||||||
if not claim['signature_is_valid']:
|
|
||||||
log.warning("lbry://%s#%s has an invalid signature",
|
|
||||||
claim['name'], claim['claim_id'])
|
|
||||||
try:
|
|
||||||
decoded = smart_decode(claim['value'])
|
|
||||||
claim_dict = decoded.claim_dict
|
|
||||||
claim['value'] = claim_dict
|
|
||||||
claim['hex'] = decoded.serialized.encode('hex')
|
|
||||||
except DecodeError:
|
|
||||||
claim['hex'] = claim['value']
|
|
||||||
claim['value'] = None
|
|
||||||
claim['error'] = "Failed to decode value"
|
|
||||||
return claim
|
|
||||||
|
|
||||||
def _handle_claim_result(results):
|
|
||||||
if not results:
|
|
||||||
#TODO: cannot determine what name we searched for here
|
|
||||||
# we should fix lbryum commands that return None
|
|
||||||
raise UnknownNameError("")
|
|
||||||
|
|
||||||
if 'error' in results:
|
|
||||||
if results['error'] in ['name is not claimed', 'claim not found']:
|
|
||||||
if 'claim_id' in results:
|
|
||||||
raise UnknownClaimID(results['claim_id'])
|
|
||||||
elif 'name' in results:
|
|
||||||
raise UnknownNameError(results['name'])
|
|
||||||
elif 'uri' in results:
|
|
||||||
raise UnknownURI(results['uri'])
|
|
||||||
elif 'outpoint' in results:
|
|
||||||
raise UnknownOutpoint(results['outpoint'])
|
|
||||||
raise Exception(results['error'])
|
|
||||||
|
|
||||||
# case where return value is {'certificate':{'txid', 'value',...},...}
|
|
||||||
if 'certificate' in results:
|
|
||||||
results['certificate'] = _decode_claim_result(results['certificate'])
|
|
||||||
|
|
||||||
# case where return value is {'claim':{'txid','value',...},...}
|
|
||||||
if 'claim' in results:
|
|
||||||
results['claim'] = _decode_claim_result(results['claim'])
|
|
||||||
|
|
||||||
# case where return value is {'txid','value',...}
|
|
||||||
# returned by queries that are not name resolve related
|
|
||||||
# (getclaimbyoutpoint, getclaimbyid, getclaimsfromtx)
|
|
||||||
elif 'value' in results:
|
|
||||||
results = _decode_claim_result(results)
|
|
||||||
|
|
||||||
# case where there is no 'certificate', 'value', or 'claim' key
|
|
||||||
elif 'certificate' not in results:
|
|
||||||
msg = 'result in unexpected format:{}'.format(results)
|
|
||||||
assert False, msg
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
197
lbrynet/wallet/resolve.py
Normal file
197
lbrynet/wallet/resolve.py
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ecdsa import BadSignatureError
|
||||||
|
from binascii import unhexlify
|
||||||
|
|
||||||
|
from lbrynet.core.Error import UnknownNameError, UnknownClaimID, UnknownURI, UnknownOutpoint
|
||||||
|
from lbryschema.address import is_address
|
||||||
|
from lbryschema.claim import ClaimDict
|
||||||
|
from lbryschema.decode import smart_decode
|
||||||
|
from lbryschema.error import DecodeError
|
||||||
|
|
||||||
|
from .claim_proofs import verify_proof, InvalidProofError
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Format amount to be decimal encoded string
|
||||||
|
# Format value to be hex encoded string
|
||||||
|
# TODO: refactor. Came from lbryum, there could be another part of torba doing it
|
||||||
|
def format_amount_value(obj):
|
||||||
|
COIN = 100000000
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
for k, v in obj.iteritems():
|
||||||
|
if k == 'amount' or k == 'effective_amount':
|
||||||
|
if not isinstance(obj[k], float):
|
||||||
|
obj[k] = float(obj[k]) / float(COIN)
|
||||||
|
elif k == 'supports' and isinstance(v, list):
|
||||||
|
obj[k] = [{'txid': txid, 'nout': nout, 'amount': float(amount) / float(COIN)}
|
||||||
|
for (txid, nout, amount) in v]
|
||||||
|
elif isinstance(v, (list, dict)):
|
||||||
|
obj[k] = format_amount_value(v)
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
obj = [format_amount_value(o) for o in obj]
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def _get_permanent_url(claim_result):
|
||||||
|
if claim_result.get('has_signature') and claim_result.get('channel_name'):
|
||||||
|
return "{0}#{1}/{2}".format(
|
||||||
|
claim_result['channel_name'],
|
||||||
|
claim_result['value']['publisherSignature']['certificateId'],
|
||||||
|
claim_result['name']
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return "{0}#{1}".format(
|
||||||
|
claim_result['name'],
|
||||||
|
claim_result['claim_id']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _verify_proof(ledger, name, claim_trie_root, result, height, depth, transaction_class):
|
||||||
|
"""
|
||||||
|
Verify proof for name claim
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _build_response(name, value, claim_id, txid, n, amount, effective_amount,
|
||||||
|
claim_sequence, claim_address, supports):
|
||||||
|
r = {
|
||||||
|
'name': name,
|
||||||
|
'value': value.encode('hex'),
|
||||||
|
'claim_id': claim_id,
|
||||||
|
'txid': txid,
|
||||||
|
'nout': n,
|
||||||
|
'amount': amount,
|
||||||
|
'effective_amount': effective_amount,
|
||||||
|
'height': height,
|
||||||
|
'depth': depth,
|
||||||
|
'claim_sequence': claim_sequence,
|
||||||
|
'address': claim_address,
|
||||||
|
'supports': supports
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
|
||||||
|
def _parse_proof_result(name, result):
|
||||||
|
support_amount = sum([amt for (stxid, snout, amt) in result['supports']])
|
||||||
|
supports = result['supports']
|
||||||
|
if 'txhash' in result['proof'] and 'nOut' in result['proof']:
|
||||||
|
if 'transaction' in result:
|
||||||
|
tx = transaction_class(raw=unhexlify(result['transaction']))
|
||||||
|
nOut = result['proof']['nOut']
|
||||||
|
if result['proof']['txhash'] == tx.hex_id:
|
||||||
|
if 0 <= nOut < len(tx.outputs):
|
||||||
|
claim_output = tx.outputs[nOut]
|
||||||
|
effective_amount = claim_output.amount + support_amount
|
||||||
|
claim_address = ledger.hash160_to_address(claim_output.script.values['pubkey_hash'])
|
||||||
|
claim_id = result['claim_id']
|
||||||
|
claim_sequence = result['claim_sequence']
|
||||||
|
claim_script = claim_output.script
|
||||||
|
decoded_name, decoded_value = claim_script.values['claim_name'], claim_script.values['claim']
|
||||||
|
if decoded_name == name:
|
||||||
|
return _build_response(name, decoded_value, claim_id,
|
||||||
|
tx.hex_id, nOut, claim_output.amount,
|
||||||
|
effective_amount, claim_sequence,
|
||||||
|
claim_address, supports)
|
||||||
|
return {'error': 'name in proof did not match requested name'}
|
||||||
|
outputs = len(tx['outputs'])
|
||||||
|
return {'error': 'invalid nOut: %d (let(outputs): %d' % (nOut, outputs)}
|
||||||
|
return {'error': "computed txid did not match given transaction: %s vs %s" %
|
||||||
|
(tx.hex_id, result['proof']['txhash'])
|
||||||
|
}
|
||||||
|
return {'error': "didn't receive a transaction with the proof"}
|
||||||
|
return {'error': 'name is not claimed'}
|
||||||
|
|
||||||
|
if 'proof' in result:
|
||||||
|
try:
|
||||||
|
verify_proof(result['proof'], claim_trie_root, name)
|
||||||
|
except InvalidProofError:
|
||||||
|
return {'error': "Proof was invalid"}
|
||||||
|
return _parse_proof_result(name, result)
|
||||||
|
else:
|
||||||
|
return {'error': "proof not in result"}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_claim_signature_and_get_channel_name(claim, certificate_claim,
|
||||||
|
claim_address, decoded_certificate=None):
|
||||||
|
if not certificate_claim:
|
||||||
|
return False, None
|
||||||
|
certificate = decoded_certificate or smart_decode(certificate_claim['value'])
|
||||||
|
if not isinstance(certificate, ClaimDict):
|
||||||
|
raise TypeError("Certificate is not a ClaimDict: %s" % str(type(certificate)))
|
||||||
|
if _validate_signed_claim(claim, claim_address, certificate):
|
||||||
|
return True, certificate_claim['name']
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_signed_claim(claim, claim_address, certificate):
|
||||||
|
if not claim.has_signature:
|
||||||
|
raise Exception("Claim is not signed")
|
||||||
|
if not is_address(claim_address):
|
||||||
|
raise Exception("Not given a valid claim address")
|
||||||
|
try:
|
||||||
|
if claim.validate_signature(claim_address, certificate.protobuf):
|
||||||
|
return True
|
||||||
|
except BadSignatureError:
|
||||||
|
# print_msg("Signature for %s is invalid" % claim_id)
|
||||||
|
return False
|
||||||
|
except Exception as err:
|
||||||
|
log.error("Signature for %s is invalid, reason: %s - %s", claim_address,
|
||||||
|
str(type(err)), err)
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: The following came from code handling lbryum results. Now that it's all in one place a refactor should unify it.
|
||||||
|
def _decode_claim_result(claim):
|
||||||
|
if 'has_signature' in claim and claim['has_signature']:
|
||||||
|
if not claim['signature_is_valid']:
|
||||||
|
log.warning("lbry://%s#%s has an invalid signature",
|
||||||
|
claim['name'], claim['claim_id'])
|
||||||
|
try:
|
||||||
|
decoded = smart_decode(claim['value'])
|
||||||
|
claim_dict = decoded.claim_dict
|
||||||
|
claim['value'] = claim_dict
|
||||||
|
claim['hex'] = decoded.serialized.encode('hex')
|
||||||
|
except DecodeError:
|
||||||
|
claim['hex'] = claim['value']
|
||||||
|
claim['value'] = None
|
||||||
|
claim['error'] = "Failed to decode value"
|
||||||
|
return claim
|
||||||
|
|
||||||
|
def _handle_claim_result(results):
|
||||||
|
if not results:
|
||||||
|
#TODO: cannot determine what name we searched for here
|
||||||
|
# we should fix lbryum commands that return None
|
||||||
|
raise UnknownNameError("")
|
||||||
|
|
||||||
|
if 'error' in results:
|
||||||
|
if results['error'] in ['name is not claimed', 'claim not found']:
|
||||||
|
if 'claim_id' in results:
|
||||||
|
raise UnknownClaimID(results['claim_id'])
|
||||||
|
elif 'name' in results:
|
||||||
|
raise UnknownNameError(results['name'])
|
||||||
|
elif 'uri' in results:
|
||||||
|
raise UnknownURI(results['uri'])
|
||||||
|
elif 'outpoint' in results:
|
||||||
|
raise UnknownOutpoint(results['outpoint'])
|
||||||
|
raise Exception(results['error'])
|
||||||
|
|
||||||
|
# case where return value is {'certificate':{'txid', 'value',...},...}
|
||||||
|
if 'certificate' in results:
|
||||||
|
results['certificate'] = _decode_claim_result(results['certificate'])
|
||||||
|
|
||||||
|
# case where return value is {'claim':{'txid','value',...},...}
|
||||||
|
if 'claim' in results:
|
||||||
|
results['claim'] = _decode_claim_result(results['claim'])
|
||||||
|
|
||||||
|
# case where return value is {'txid','value',...}
|
||||||
|
# returned by queries that are not name resolve related
|
||||||
|
# (getclaimbyoutpoint, getclaimbyid, getclaimsfromtx)
|
||||||
|
elif 'value' in results:
|
||||||
|
results = _decode_claim_result(results)
|
||||||
|
|
||||||
|
# case where there is no 'certificate', 'value', or 'claim' key
|
||||||
|
elif 'certificate' not in results:
|
||||||
|
msg = 'result in unexpected format:{}'.format(results)
|
||||||
|
assert False, msg
|
||||||
|
|
||||||
|
return results
|
Loading…
Add table
Reference in a new issue