simple payment verification: check targets, use block headers file.
[electrum-nvc.git] / lib / wallet.py
index 355a187..c3f5984 100644 (file)
@@ -29,6 +29,7 @@ import random
 import aes
 import ecdsa
 import Queue
+import time
 
 from ecdsa.util import string_to_number, number_to_string
 from util import print_error, user_dir, format_satoshis
@@ -831,7 +832,7 @@ class WalletSynchronizer(threading.Thread):
         self.interface = self.wallet.interface
         self.interface.register_channel('synchronizer')
         self.wallet.interface.register_callback('connected', self.wallet.init_up_to_date)
-
+        self.wallet.interface.register_callback('connected', lambda: self.interface.send([('server.banner',[])],'synchronizer') )
 
     def synchronize_wallet(self):
         new_addresses = self.wallet.synchronize()
@@ -859,8 +860,14 @@ class WalletSynchronizer(threading.Thread):
 
     def run(self):
 
-        # subscriptions
+        # wait until we are connected, in case the user is not connected
+        while not self.interface.is_connected:
+            time.sleep(1)
+        
+        # request banner, because 'connected' event happens before this thread is started
         self.interface.send([('server.banner',[])],'synchronizer')
+
+        # subscriptions
         self.interface.send([('blockchain.numblocks.subscribe',[])], 'synchronizer')
         self.interface.send([('server.peers.subscribe',[])],'synchronizer')
         self.subscribe_to_addresses(self.wallet.all_addresses())
@@ -933,109 +940,3 @@ class WalletSynchronizer(threading.Thread):
                 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.wallet = wallet
-        self.interface = self.wallet.interface
-        self.interface.register_channel('verifier')
-        self.validated = []
-        self.merkle_roots = {}
-        self.headers = {}
-        self.lock = threading.Lock()
-
-    def run(self):
-        requested = []
-
-        while True:
-            txlist = self.wallet.get_tx_hashes()
-            for tx in txlist:
-                if tx not in requested:
-                    requested.append(tx)
-                    self.request_merkle(tx)
-                    break
-            try:
-                r = self.interface.get_response('verifier',timeout=1)
-            except Queue.Empty:
-                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)