X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=lib%2Fblockchain.py;h=aa4880c44b791b7f514d4dc4f5bfad30bb5ef8a6;hb=HEAD;hp=ff4524b22a261e534cbf821f0458bfe3bf07bb0f;hpb=e462ef48cdcbf71cef126ec473988a69fd306bbb;p=electrum-nvc.git diff --git a/lib/blockchain.py b/lib/blockchain.py index ff4524b..aa4880c 100644 --- a/lib/blockchain.py +++ b/lib/blockchain.py @@ -21,6 +21,11 @@ import threading, time, Queue, os, sys, shutil from util import user_dir, appdata_dir, print_error from bitcoin import * +try: + from ltc_scrypt import getPoWHash +except ImportError: + print_msg("Warning: scrypt not available, using fallback") + from scrypt import scrypt_1024_1_1_80 as getPoWHash class Blockchain(threading.Thread): @@ -30,15 +35,17 @@ class Blockchain(threading.Thread): self.config = config self.network = network self.lock = threading.Lock() - self.height = 0 self.local_height = 0 self.running = False - self.headers_url = 'http://headers.electrum.org/blockchain_headers' + self.headers_url = 'http://novacoin.su/static/blockchain_headers' self.set_local_height() self.queue = Queue.Queue() - self.servers_height = {} + def height(self): + return self.local_height + + def stop(self): with self.lock: self.running = False @@ -68,42 +75,47 @@ class Blockchain(threading.Thread): if not header: continue height = header.get('block_height') - self.servers_height[i.server] = height + + if height <= self.local_height: + continue if height > self.local_height + 50: - self.get_chunks(i, header, height) - self.network.trigger_callback('updated') + if not self.get_and_verify_chunks(i, header, height): + continue if height > self.local_height: # get missing parts from interface (until it connects to my chain) chain = self.get_chain( i, header ) # skip that server if the result is not consistent - if not chain: continue + if not chain: + print_error('e') + continue # verify the chain if self.verify_chain( chain ): print_error("height:", height, i.server) for header in chain: self.save_header(header) - self.height = height else: print_error("error", i.server) # todo: dismiss that server + continue - self.network.trigger_callback('updated') - h = self.servers_height.get(self.network.interface.server) - if h is not None and h < height - 1: - print_error( "Server is lagging", height, h) - if self.config.get('auto_cycle'): - self.network.set_server(i.server) - else: - self.network.interface.stop() + self.network.new_blockchain_height(height, i) - + def calculate_target(self, bits): + # convert to bignum + MM = 256*256*256 + a = bits%MM + if a < 0x8000: + a *= 256 + return (a) * pow(2, 8 * (bits/MM - 3)) + + def verify_chain(self, chain): first_header = chain[0] @@ -114,13 +126,13 @@ class Blockchain(threading.Thread): height = header.get('block_height') prev_hash = self.hash_header(prev_header) - bits, target = self.get_target(height/2016) - _hash = self.hash_header(header) + header_hash = self.hash_header(header) + try: assert prev_hash == header.get('prev_block_hash') - assert bits == header.get('bits') - assert eval('0x'+_hash) < target - except: + if header.get('nonce') != 0: + assert int('0x'+_hash,16) < self.calculate_target(header.get('bits')) + except Exception: return False prev_header = header @@ -139,55 +151,24 @@ class Blockchain(threading.Thread): else: prev_header = self.read_header(index*2016-1) if prev_header is None: raise - previous_hash = self.hash_header(prev_header) - - bits, target = self.get_target(index) + _hash = self.hash_header(prev_header) for i in range(num): height = index*2016 + i raw_header = data[i*80:(i+1)*80] header = self.header_from_string(raw_header) - _hash = self.hash_header(header) + header_hash = self.hash_header(header) + assert previous_hash == header.get('prev_block_hash') - assert bits == header.get('bits') - assert eval('0x'+_hash) < target + if header.get('nonce') != 0: + assert int('0x'+header_hash,16) < calculate_target(header.get('bits')) previous_header = header - previous_hash = _hash + previous_hash = header_hash self.save_chunk(index, data) print_error("validated chunk %d"%height) - - def verify_header(self, header): - # add header to the blockchain file - # if there is a reorg, push it in a stack - - height = header.get('block_height') - - prev_header = self.read_header(height -1) - if not prev_header: - # return False to request previous header - return False - - prev_hash = self.hash_header(prev_header) - bits, target = self.get_target(height/2016) - _hash = self.hash_header(header) - try: - assert prev_hash == header.get('prev_block_hash') - assert bits == header.get('bits') - assert eval('0x'+_hash) < target - except: - # this can be caused by a reorg. - print_error("verify header failed"+ repr(header)) - verifier.undo_verifications() - - # return False to request previous header. - return False - - self.save_header(header) - print_error("verify header:", _hash, height) - return True def header_to_string(self, res): @@ -201,7 +182,7 @@ class Blockchain(threading.Thread): def header_from_string(self, s): - hex_to_int = lambda s: eval('0x' + s[::-1].encode('hex')) + hex_to_int = lambda s: int('0x' + s[::-1].encode('hex'), 16) h = {} h['version'] = hex_to_int(s[0:4]) h['prev_block_hash'] = hash_encode(s[4:36]) @@ -212,7 +193,7 @@ class Blockchain(threading.Thread): return h def hash_header(self, header): - return rev_hex(Hash(self.header_to_string(header).decode('hex')).encode('hex')) + return rev_hex(getPoWHash(self.header_to_string(header).decode('hex')).encode('hex')) def path(self): return os.path.join( self.config.path, 'blockchain_headers') @@ -227,7 +208,8 @@ class Blockchain(threading.Thread): socket.setdefaulttimeout(30) print_error("downloading ", self.headers_url ) urllib.urlretrieve(self.headers_url, filename) - except: + print_error("done.") + except Exception: print_error( "download failed. creating file", filename ) open(filename,'wb+').close() @@ -257,7 +239,6 @@ class Blockchain(threading.Thread): h = os.path.getsize(name)/80 - 1 if self.local_height != h: self.local_height = h - self.height = self.local_height def read_header(self, block_height): @@ -271,47 +252,6 @@ class Blockchain(threading.Thread): h = self.header_from_string(h) return h - - def get_target(self, index): - - max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 - if index == 0: return 0x1d00ffff, max_target - - first = self.read_header((index-1)*2016) - last = self.read_header(index*2016-1) - - nActualTimespan = last.get('timestamp') - first.get('timestamp') - nTargetTimespan = 14*24*60*60 - nActualTimespan = max(nActualTimespan, nTargetTimespan/4) - nActualTimespan = min(nActualTimespan, nTargetTimespan*4) - - bits = last.get('bits') - # convert to bignum - MM = 256*256*256 - a = bits%MM - if a < 0x8000: - a *= 256 - target = (a) * pow(2, 8 * (bits/MM - 3)) - - # new target - new_target = min( max_target, (target * nActualTimespan)/nTargetTimespan ) - - # convert it to bits - c = ("%064X"%new_target)[2:] - i = 31 - while c[0:2]=="00": - c = c[2:] - i -= 1 - - c = eval('0x'+c[0:6]) - if c > 0x800000: - c /= 256 - i += 1 - - new_bits = c + MM * i - return new_bits, new_target - - def request_header(self, i, h, queue): print_error("requesting header %d from %s"%(h, i.server)) i.send([ ('blockchain.block.get_header',[h])], lambda i,r: queue.put((i,r))) @@ -378,39 +318,25 @@ class Blockchain(threading.Thread): return chain - def get_chunks(self, i, header, height): - requested_chunks = [] + def get_and_verify_chunks(self, i, header, height): + queue = Queue.Queue() min_index = (self.local_height + 1)/2016 max_index = (height + 1)/2016 - for n in range(min_index, max_index + 1): - print_error( "requesting chunk", n ) - i.send([ ('blockchain.block.get_chunk',[n])], lambda i,r:queue.put(r)) - requested_chunks.append(n) - break - - while requested_chunks: - try: - r = queue.get(timeout=1) - except Queue.Empty: + n = min_index + while n < max_index + 1: + print_error( "Requesting chunk:", n ) + r = i.synchronous_get([ ('blockchain.block.get_chunk',[n])])[0] + if not r: continue - if not r: continue - - if r.get('error'): - print_error('Verifier received an error:', r) - continue - - # 3. handle response - params = r['params'] - result = r['result'] - - index = params[0] - self.verify_chunk(index, result) - requested_chunks.remove(index) - - - - - + try: + self.verify_chunk(n, r) + n = n + 1 + except Exception: + print_error('Verify chunk failed!') + n = n - 1 + if n < 0: + return False + return True