self.wallet.was_updated = False
-encode = lambda x: x[::-1].encode('hex')
-decode = lambda x: x.decode('hex')[::-1]
-from bitcoin import Hash, rev_hex, int_to_hex
-
-class WalletVerifier(threading.Thread):
-
- def __init__(self, wallet, config):
- threading.Thread.__init__(self)
- self.daemon = True
- self.config = config
- self.wallet = wallet
- self.interface = self.wallet.interface
- self.interface.register_channel('verifier')
- self.validated = config.get('verified_tx',[])
- self.merkle_roots = config.get('merkle_roots',{})
- self.headers = config.get('block_headers',{})
- self.lock = threading.Lock()
- self.saved = True
-
- def run(self):
- requested = []
-
- while True:
- txlist = self.wallet.get_tx_hashes()
-
- for tx in txlist:
- if tx not in self.validated:
- if tx not in requested:
- requested.append(tx)
- self.request_merkle(tx)
- self.saved = False
- break
-
- try:
- r = self.interface.get_response('verifier',timeout=1)
- except Queue.Empty:
- if len(self.validated) == len(txlist) and not self.saved:
- print "saving verified transactions"
- self.config.set_key('verified_tx', self.validated, True)
- self.saved = True
- continue
-
- # 3. handle response
- method = r['method']
- params = r['params']
- result = r['result']
-
- if method == 'blockchain.transaction.get_merkle':
- tx_hash = params[0]
- tx_height = result.get('block_height')
- self.merkle_roots[tx_hash] = self.hash_merkle_root(result['merkle'], tx_hash)
- # if we already have the header, check merkle root directly
- header = self.headers.get(tx_height)
- if header:
- self.validated.append(tx_hash)
- assert header.get('merkle_root') == self.merkle_roots[tx_hash]
- self.request_headers(tx_height)
-
- elif method == 'blockchain.block.get_header':
- self.validate_header(result)
-
-
- def request_merkle(self, tx_hash):
- self.interface.send([ ('blockchain.transaction.get_merkle',[tx_hash]) ], 'verifier')
-
-
- def request_headers(self, tx_height, delta=10):
- headers_requests = []
- for height in range(tx_height-delta,tx_height+delta): # we might can request blocks that do not exist yet
- if height not in self.headers:
- headers_requests.append( ('blockchain.block.get_header',[height]) )
- self.interface.send(headers_requests,'verifier')
-
-
- def validate_header(self, header):
- """ if there is a previous or a next block in the list, check the hash"""
- 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)
- assert prev_hash == header.get('prev_block_hash')
- if next_header:
- _hash = self.hash_header(header)
- assert _hash == next_header.get('prev_block_hash')
-
- # check if there are transactions at that height
- for tx_hash in self.wallet.get_transactions_at_height(height):
- if tx_hash in self.validated: continue
- # check if we already have the merkle root
- merkle_root = self.merkle_roots.get(tx_hash)
- if merkle_root:
- self.validated.append(tx_hash)
- assert header.get('merkle_root') == merkle_root
-
- def hash_header(self, res):
- header = 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)
- return rev_hex(Hash(header.decode('hex')).encode('hex'))
-
- def hash_merkle_root(self, merkle_s, target_hash):
- h = decode(target_hash)
- for item in merkle_s:
- is_left = item[0] == 'L'
- h = Hash( h + decode(item[1:]) ) if is_left else Hash( decode(item[1:]) + h )
- return encode(h)