electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit f91dde55dd8bf710feadcef7d0c806218efa43c3
parent b3b31fdc07517ea5af36b1eb37d595d5e0d2237f
Author: thomasv <thomasv@gitorious>
Date:   Thu, 25 Oct 2012 15:40:30 +0200

subscribe to / verify headers

Diffstat:
Mlib/interface.py | 4++++
Mlib/verifier.py | 111++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
2 files changed, 89 insertions(+), 26 deletions(-)

diff --git a/lib/interface.py b/lib/interface.py @@ -97,6 +97,10 @@ class Interface(threading.Thread): result = params[0] params = [] + elif method == 'blockchain.headers.subscribe': + result = params[0] + params = [] + elif method == 'blockchain.address.subscribe': addr = params[0] result = params[1] diff --git a/lib/verifier.py b/lib/verifier.py @@ -38,8 +38,14 @@ class WalletVerifier(threading.Thread): self.targets = config.get('targets',{}) # compute targets self.lock = threading.Lock() - #self.config.set_key('verified_tx', [], True) - #for i in range(70): self.get_target(i) + self.pending_headers = [] # headers that have not been verified + + self.height = 0 + self.local_height = 0 + self.set_local_numblocks() + + #prev_header = self.read_header(0) + #print prev_header #sys.exit() @@ -47,10 +53,14 @@ class WalletVerifier(threading.Thread): def run(self): requested_merkle = [] requested_chunks = [] + requested_headers = [] + + # subscribe to block headers + self.interface.send([ ('blockchain.headers.subscribe',[])], 'verifier') while True: # request missing chunks - max_index = self.wallet.blocks/2016 + max_index = (self.height+1)/2016 if not requested_chunks: for i in range(0, max_index + 1): # test if we can read the first header of the chunk @@ -60,8 +70,14 @@ class WalletVerifier(threading.Thread): requested_chunks.append(i) break - # todo: request missing blocks too - + # request missing headers + if not requested_chunks and self.local_height: + for i in range(self.local_height + 1, self.height + 1): + if i not in requested_headers: + print "requesting header", i + self.interface.send([ ('blockchain.block.get_header',[i])], 'verifier') + requested_headers.append(i) + # request missing tx merkle txlist = self.wallet.get_tx_hashes() for tx in txlist: @@ -92,8 +108,21 @@ class WalletVerifier(threading.Thread): self.verify_chunk(index, result) requested_chunks.remove(index) + elif method == 'blockchain.headers.subscribe': + self.height = result.get('block_height') + self.pending_headers.append(result) + elif method == 'blockchain.block.get_header': - self.verify_header(result) + height = result.get('block_height') + requested_headers.remove(height) + self.pending_headers.append(result) + + # process pending headers + # todo: sort them first + for header in self.pending_headers: + self.verify_header(header) + self.pending_headers = [] + def request_merkle(self, tx_hash): @@ -114,8 +143,8 @@ class WalletVerifier(threading.Thread): def verify_chunk(self, index, hexdata): data = hexdata.decode('hex') height = index*2016 - numblocks = len(data)/80 - print "validate_chunk", index, numblocks + num = len(data)/80 + print "validate_chunk", index, num if index == 0: previous_hash = ("0"*64) @@ -126,7 +155,7 @@ class WalletVerifier(threading.Thread): bits, target = self.get_target(index) - for i in range(numblocks): + for i in range(num): height = index*2016 + i raw_header = data[i*80:(i+1)*80] header = self.header_from_string(raw_header) @@ -141,24 +170,38 @@ class WalletVerifier(threading.Thread): self.save_chunk(index, data) - def validate_header(self, header): - """ if there is a previous or a next block in the list, check the hash""" + 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') - with self.lock: - self.headers[height] = header # detect conflicts - prev_header = next_header = None - if height-1 in self.headers: - prev_header = self.headers[height-1] - if height+1 in self.headers: - next_header = self.headers[height+1] - - if prev_header: - prev_hash = self.hash_header(prev_header) + + prev_header = self.read_header(height -1) + if not prev_header: + raise "no previous header", height + return + + #prev_hash = prev_header.get('block_height') + 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 + ok = True + except: + print "verify header failed", header + raise + # this could be caused by a reorg. request the previous header + ok = False + #request previous one + + if ok: self.save_header(header) - if next_header: - _hash = self.hash_header(header) - assert _hash == next_header.get('prev_block_hash') + print "verify header: ok", height + + def header_to_string(self, res): @@ -194,14 +237,12 @@ class WalletVerifier(threading.Thread): h = Hash( h + hash_decode(item[1:]) ) if is_left else Hash( hash_decode(item[1:]) + h ) return hash_encode(h) - def path(self): wdir = user_dir() if not os.path.exists( wdir ): wdir = os.path.dirname(self.config.path) return os.path.join( wdir, 'blockchain_headers') - def save_chunk(self, index, chunk): filename = self.path() if os.path.exists(filename): @@ -212,6 +253,24 @@ class WalletVerifier(threading.Thread): f.seek(index*2016*80) h = f.write(chunk) f.close() + self.set_local_numblocks() + + def save_header(self, header): + data = self.header_to_string(header).decode('hex') + assert len(data) == 80 + height = header.get('block_height') + filename = self.path() + f = open(filename,'rw+') + f.seek(height*80) + h = f.write(data) + f.close() + self.set_local_numblocks() + + def set_local_numblocks(self): + name = self.path() + if os.path.exists(name): + self.local_height = os.path.getsize(name)/80 - 1 + # print "local height", self.local_height, os.path.getsize(name)/80. def read_header(self, block_height):