From d344ee0474426c7fe79e959dcce61c925cbeb8a5 Mon Sep 17 00:00:00 2001 From: Kirill Fomichev Date: Sat, 12 Dec 2015 15:43:07 +0300 Subject: [PATCH] Small blockchain changes --- lib/blockchain.py | 78 +++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/lib/blockchain.py b/lib/blockchain.py index 4b71a20cd..d8cd42d6b 100644 --- a/lib/blockchain.py +++ b/lib/blockchain.py @@ -28,7 +28,7 @@ class Blockchain(util.PrintError): def __init__(self, config, network): self.config = config self.network = network - self.headers_url = 'https://headers.electrum.org/blockchain_headers' + self.headers_url = "https://headers.electrum.org/blockchain_headers" self.local_height = 0 self.set_local_height() @@ -40,42 +40,42 @@ class Blockchain(util.PrintError): self.set_local_height() self.print_error("%d blocks" % self.local_height) - def verify_headers(self, header, prev_header, bits, target): + def verify_header(self, header, prev_header, bits, target): prev_hash = self.hash_header(prev_header) - assert prev_hash == header.get('prev_block_hash'), "prev hash mismatch: %s vs %s"% (prev_hash, header.get('prev_block_hash')) + assert prev_hash == header.get('prev_block_hash'), "prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')) assert bits == header.get('bits'), "bits mismatch: %s vs %s" % (bits, header.get('bits')) _hash = self.hash_header(header) - assert int('0x'+_hash, 16) <= target, "insufficient proof of work: %s vs target %s" % (int('0x'+_hash, 16), target) + assert int('0x' + _hash, 16) <= target, "insufficient proof of work: %s vs target %s" % (int('0x' + _hash, 16), target) def verify_chain(self, chain): first_header = chain[0] - prev_header = self.read_header(first_header.get('block_height') -1) + prev_header = self.read_header(first_header.get('block_height') - 1) for header in chain: height = header.get('block_height') - bits, target = self.get_target(height/2016, chain) - self.verify_headers(header, prev_header, bits, target) + bits, target = self.get_target(height / 2016, chain) + self.verify_header(header, prev_header, bits, target) prev_header = header def verify_chunk(self, index, hexdata): data = hexdata.decode('hex') - num = len(data)/80 - prev_header = None if index == 0 else self.read_header(index*2016-1) + num = len(data) / 80 + prev_header = None + if index != 0: + prev_header = self.read_header(index*2016 - 1) bits, target = self.get_target(index) for i in range(num): - raw_header = data[i*80:(i+1)*80] + raw_header = data[i*80:(i+1) * 80] header = self.deserialize_header(raw_header) - self.verify_headers(header, prev_header, bits, target) + self.verify_header(header, prev_header, bits, target) prev_header = header - self.save_chunk(index, data) - self.print_error("validated chunk %d" % index) def serialize_header(self, res): - s = int_to_hex(res.get('version'),4) \ + s = int_to_hex(res.get('version'), 4) \ + rev_hex(res.get('prev_block_hash')) \ + rev_hex(res.get('merkle_root')) \ - + int_to_hex(int(res.get('timestamp')),4) \ - + int_to_hex(int(res.get('bits')),4) \ - + int_to_hex(int(res.get('nonce')),4) + + int_to_hex(int(res.get('timestamp')), 4) \ + + int_to_hex(int(res.get('bits')), 4) \ + + int_to_hex(int(res.get('nonce')), 4) return s def deserialize_header(self, s): @@ -90,7 +90,9 @@ class Blockchain(util.PrintError): return h def hash_header(self, header): - return "0"*64 if header is None else rev_hex(Hash(self.serialize_header(header).decode('hex')).encode('hex')) + if header is None: + return '0' * 64 + return hash_encode(Hash(self.serialize_header(header).decode('hex'))) def path(self): return os.path.join(self.config.path, 'blockchain_headers') @@ -102,17 +104,17 @@ class Blockchain(util.PrintError): try: import urllib, socket socket.setdefaulttimeout(30) - self.print_error("downloading ", self.headers_url ) + self.print_error("downloading ", self.headers_url) urllib.urlretrieve(self.headers_url, filename) self.print_error("done.") except Exception: - self.print_error( "download failed. creating file", filename ) - open(filename,'wb+').close() + self.print_error("download failed. creating file", filename) + open(filename, 'wb+').close() def save_chunk(self, index, chunk): filename = self.path() - f = open(filename,'rb+') - f.seek(index*2016*80) + f = open(filename, 'rb+') + f.seek(index * 2016 * 80) h = f.write(chunk) f.close() self.set_local_height() @@ -122,8 +124,8 @@ class Blockchain(util.PrintError): assert len(data) == 80 height = header.get('block_height') filename = self.path() - f = open(filename,'rb+') - f.seek(height*80) + f = open(filename, 'rb+') + f.seek(height * 80) h = f.write(data) f.close() self.set_local_height() @@ -138,8 +140,8 @@ class Blockchain(util.PrintError): def read_header(self, block_height): name = self.path() if os.path.exists(name): - f = open(name,'rb') - f.seek(block_height*80) + f = open(name, 'rb') + f.seek(block_height * 80) h = f.read(80) f.close() if len(h) == 80: @@ -149,11 +151,11 @@ class Blockchain(util.PrintError): def get_target(self, index, chain=None): if index == 0: return 0x1d00ffff, MAX_TARGET - first = self.read_header((index-1)*2016) - last = self.read_header(index*2016-1) + first = self.read_header((index-1) * 2016) + last = self.read_header(index*2016 - 1) if last is None: for h in chain: - if h.get('block_height') == index*2016-1: + if h.get('block_height') == index*2016 - 1: last = h assert last is not None # bits to target @@ -162,23 +164,23 @@ class Blockchain(util.PrintError): assert bitsN >= 0x03 and bitsN <= 0x1d, "First part of bits should be in [0x03, 0x1d]" bitsBase = bits & 0xffffff assert bitsBase >= 0x8000 and bitsBase <= 0x7fffff, "Second part of bits should be in [0x8000, 0x7fffff]" - target = bitsBase << (8*(bitsN-3)) + target = bitsBase << (8 * (bitsN-3)) # new target nActualTimespan = last.get('timestamp') - first.get('timestamp') - nTargetTimespan = 14*24*60*60 - nActualTimespan = max(nActualTimespan, nTargetTimespan/4) - nActualTimespan = min(nActualTimespan, nTargetTimespan*4) - new_target = min(MAX_TARGET, (target * nActualTimespan)/nTargetTimespan) + nTargetTimespan = 14 * 24 * 60 * 60 + nActualTimespan = max(nActualTimespan, nTargetTimespan / 4) + nActualTimespan = min(nActualTimespan, nTargetTimespan * 4) + new_target = min(MAX_TARGET, (target*nActualTimespan) / nTargetTimespan) # convert new target to bits c = ("%064x" % new_target)[2:] while c[:2] == '00' and len(c) > 6: c = c[2:] - bitsN, bitsBase = len(c)/2, int('0x'+c[:6], 16) + bitsN, bitsBase = len(c) / 2, int('0x' + c[:6], 16) if bitsBase >= 0x800000: bitsN += 1 bitsBase >>= 8 new_bits = bitsN << 24 | bitsBase - return new_bits, bitsBase << (8*(bitsN-3)) + return new_bits, bitsBase << (8 * (bitsN-3)) def connect_header(self, chain, header): '''Builds a header chain until it connects. Returns True if it has @@ -213,6 +215,8 @@ class Blockchain(util.PrintError): def connect_chunk(self, idx, chunk): try: self.verify_chunk(idx, chunk) + self.print_error("validated chunk %d" % index) + self.save_chunk(index, data) return idx + 1 except BaseException as e: self.print_error('verify_chunk failed', str(e))