2015-08-20 17:27:15 +02:00
|
|
|
import binascii
|
|
|
|
import logging
|
|
|
|
from lbrynet.core.cryptoutils import get_lbry_hash_obj
|
|
|
|
from lbrynet.cryptstream.CryptBlob import CryptBlobInfo
|
|
|
|
from twisted.internet import defer
|
2015-09-01 23:49:26 +02:00
|
|
|
from lbrynet.core.Error import DuplicateStreamHashError, InvalidStreamDescriptorError
|
2015-08-20 17:27:15 +02:00
|
|
|
|
|
|
|
|
2015-09-08 21:42:56 +02:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
LBRYFileStreamType = "lbryfile"
|
|
|
|
|
|
|
|
|
|
|
|
def save_sd_info(stream_info_manager, sd_info, ignore_duplicate=False):
|
2015-09-08 21:42:56 +02:00
|
|
|
log.debug("Saving info for %s", str(sd_info['stream_name']))
|
2015-08-20 17:27:15 +02:00
|
|
|
hex_stream_name = sd_info['stream_name']
|
|
|
|
key = sd_info['key']
|
|
|
|
stream_hash = sd_info['stream_hash']
|
|
|
|
raw_blobs = sd_info['blobs']
|
|
|
|
suggested_file_name = sd_info['suggested_file_name']
|
|
|
|
crypt_blobs = []
|
|
|
|
for blob in raw_blobs:
|
|
|
|
length = blob['length']
|
|
|
|
if length != 0:
|
|
|
|
blob_hash = blob['blob_hash']
|
|
|
|
else:
|
|
|
|
blob_hash = None
|
|
|
|
blob_num = blob['blob_num']
|
|
|
|
iv = blob['iv']
|
|
|
|
crypt_blobs.append(CryptBlobInfo(blob_hash, blob_num, length, iv))
|
2015-09-08 21:42:56 +02:00
|
|
|
log.debug("Trying to save stream info for %s", str(hex_stream_name))
|
2015-08-20 17:27:15 +02:00
|
|
|
d = stream_info_manager.save_stream(stream_hash, hex_stream_name, key,
|
|
|
|
suggested_file_name, crypt_blobs)
|
|
|
|
|
|
|
|
def check_if_duplicate(err):
|
|
|
|
if ignore_duplicate is True:
|
|
|
|
err.trap(DuplicateStreamHashError)
|
|
|
|
|
|
|
|
d.addErrback(check_if_duplicate)
|
|
|
|
|
|
|
|
d.addCallback(lambda _: stream_hash)
|
|
|
|
return d
|
|
|
|
|
|
|
|
|
|
|
|
def get_sd_info(stream_info_manager, stream_hash, include_blobs):
|
|
|
|
d = stream_info_manager.get_stream_info(stream_hash)
|
|
|
|
|
|
|
|
def format_info(stream_info):
|
|
|
|
fields = {}
|
|
|
|
fields['stream_type'] = LBRYFileStreamType
|
|
|
|
fields['stream_name'] = stream_info[1]
|
|
|
|
fields['key'] = stream_info[0]
|
|
|
|
fields['suggested_file_name'] = stream_info[2]
|
|
|
|
fields['stream_hash'] = stream_hash
|
|
|
|
|
|
|
|
def format_blobs(blobs):
|
|
|
|
formatted_blobs = []
|
|
|
|
for blob_hash, blob_num, iv, length in blobs:
|
|
|
|
blob = {}
|
|
|
|
if length != 0:
|
|
|
|
blob['blob_hash'] = blob_hash
|
|
|
|
blob['blob_num'] = blob_num
|
|
|
|
blob['iv'] = iv
|
|
|
|
blob['length'] = length
|
|
|
|
formatted_blobs.append(blob)
|
|
|
|
fields['blobs'] = formatted_blobs
|
|
|
|
return fields
|
|
|
|
|
|
|
|
if include_blobs is True:
|
|
|
|
d = stream_info_manager.get_blobs_for_stream(stream_hash)
|
|
|
|
else:
|
|
|
|
d = defer.succeed([])
|
|
|
|
d.addCallback(format_blobs)
|
|
|
|
return d
|
|
|
|
|
|
|
|
d.addCallback(format_info)
|
|
|
|
return d
|
|
|
|
|
|
|
|
|
|
|
|
class LBRYFileStreamDescriptorValidator(object):
|
|
|
|
def __init__(self, raw_info):
|
|
|
|
self.raw_info = raw_info
|
|
|
|
|
|
|
|
def validate(self):
|
2015-09-08 21:42:56 +02:00
|
|
|
log.debug("Trying to validate stream descriptor for %s", str(self.raw_info['stream_name']))
|
2015-08-20 17:27:15 +02:00
|
|
|
try:
|
|
|
|
hex_stream_name = self.raw_info['stream_name']
|
|
|
|
key = self.raw_info['key']
|
|
|
|
hex_suggested_file_name = self.raw_info['suggested_file_name']
|
|
|
|
stream_hash = self.raw_info['stream_hash']
|
|
|
|
blobs = self.raw_info['blobs']
|
|
|
|
except KeyError as e:
|
2015-09-01 23:49:26 +02:00
|
|
|
raise InvalidStreamDescriptorError("Missing '%s'" % (e.args[0]))
|
2015-08-20 17:27:15 +02:00
|
|
|
for c in hex_suggested_file_name:
|
|
|
|
if c not in '0123456789abcdef':
|
2015-09-01 23:49:26 +02:00
|
|
|
raise InvalidStreamDescriptorError("Suggested file name is not a hex-encoded string")
|
2015-08-20 17:27:15 +02:00
|
|
|
h = get_lbry_hash_obj()
|
|
|
|
h.update(hex_stream_name)
|
|
|
|
h.update(key)
|
|
|
|
h.update(hex_suggested_file_name)
|
|
|
|
|
|
|
|
def get_blob_hashsum(b):
|
|
|
|
length = b['length']
|
|
|
|
if length != 0:
|
|
|
|
blob_hash = b['blob_hash']
|
|
|
|
else:
|
|
|
|
blob_hash = None
|
|
|
|
blob_num = b['blob_num']
|
|
|
|
iv = b['iv']
|
|
|
|
blob_hashsum = get_lbry_hash_obj()
|
|
|
|
if length != 0:
|
|
|
|
blob_hashsum.update(blob_hash)
|
|
|
|
blob_hashsum.update(str(blob_num))
|
|
|
|
blob_hashsum.update(iv)
|
|
|
|
blob_hashsum.update(str(length))
|
|
|
|
return blob_hashsum.digest()
|
|
|
|
|
|
|
|
blobs_hashsum = get_lbry_hash_obj()
|
|
|
|
for blob in blobs:
|
|
|
|
blobs_hashsum.update(get_blob_hashsum(blob))
|
|
|
|
if blobs[-1]['length'] != 0:
|
2015-09-01 23:49:26 +02:00
|
|
|
raise InvalidStreamDescriptorError("Does not end with a zero-length blob.")
|
2015-08-20 17:27:15 +02:00
|
|
|
h.update(blobs_hashsum.digest())
|
|
|
|
if h.hexdigest() != stream_hash:
|
2015-09-01 23:49:26 +02:00
|
|
|
raise InvalidStreamDescriptorError("Stream hash does not match stream metadata")
|
2015-09-22 04:06:04 +02:00
|
|
|
log.debug("It is validated")
|
2015-08-20 17:27:15 +02:00
|
|
|
return defer.succeed(True)
|
|
|
|
|
|
|
|
def info_to_show(self):
|
|
|
|
info = []
|
|
|
|
info.append(("stream_name", binascii.unhexlify(self.raw_info.get("stream_name"))))
|
|
|
|
size_so_far = 0
|
|
|
|
for blob_info in self.raw_info.get("blobs", []):
|
|
|
|
size_so_far += int(blob_info['length'])
|
2015-09-20 06:49:29 +02:00
|
|
|
info.append(("stream_size", str(self.get_length_of_stream())))
|
2015-08-20 17:27:15 +02:00
|
|
|
suggested_file_name = self.raw_info.get("suggested_file_name", None)
|
|
|
|
if suggested_file_name is not None:
|
|
|
|
suggested_file_name = binascii.unhexlify(suggested_file_name)
|
|
|
|
info.append(("suggested_file_name", suggested_file_name))
|
2015-09-20 06:49:29 +02:00
|
|
|
return info
|
|
|
|
|
|
|
|
def get_length_of_stream(self):
|
|
|
|
size_so_far = 0
|
|
|
|
for blob_info in self.raw_info.get("blobs", []):
|
|
|
|
size_so_far += int(blob_info['length'])
|
|
|
|
return size_so_far
|