URI: 
       tMerge branch '2.0' - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 7267579fe0792fc7ff65724d1756520d943bc66e
   DIR parent bb859c244b219479be2ea0980ff552bd9df362e4
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Thu, 27 Feb 2014 10:41:22 +0100
       
       Merge branch '2.0'
       
       Diffstat:
         M electrum                            |     125 +++++++++++++++++++------------
         M gui/qt/installwizard.py             |      23 +++++++----------------
         M gui/qt/main_window.py               |      15 +++++++++++++++
         M lib/__init__.py                     |       2 +-
         M lib/bitcoin.py                      |     218 +++++++++++++++++++++++++++----
         M lib/commands.py                     |      73 ++++++++++++++++++++++++++-----
         M lib/interface.py                    |      16 ++++++++++++++++
         M lib/network.py                      |     114 +++++++++++++++++--------------
         M lib/version.py                      |       6 +++---
         M lib/wallet.py                       |     358 +++++++++++++++++++------------
         D lib/wallet_factory.py               |      11 -----------
         M scripts/block_headers               |       2 +-
         M scripts/peers                       |      82 +++++++++++++++++++++++++++----
         M scripts/servers                     |       4 ++--
       
       14 files changed, 734 insertions(+), 315 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -89,6 +89,7 @@ def arg_parser():
            parser.add_option("-G", "--gap", dest="gap_limit", default=None, help="gap limit")
            parser.add_option("-W", "--password", dest="password", default=None, help="set password for usage with commands (currently only implemented for create command, do not use it for longrunning gui session since the password is visible in /proc)")
            parser.add_option("-1", "--oneserver", action="store_true", dest="oneserver", default=False, help="connect to one server only")
       +    parser.add_option("--bip32", action="store_true", dest="bip32", default=False, help="bip32")
            return parser
        
        
       t@@ -105,15 +106,34 @@ def print_help_cb(self, opt, value, parser):
        
        
        def run_command(cmd, password=None, args=[]):
       +    import xmlrpclib, socket
            cmd_runner = Commands(wallet, network)
       -    func = getattr(cmd_runner, cmd)
       +    func = getattr(cmd_runner, cmd.name)
            cmd_runner.password = password
       +
       +    if cmd.requires_network and not options.offline:
       +        cmd_runner.network = xmlrpclib.ServerProxy('http://localhost:8000')
       +        if wallet:
       +            wallet.start_threads(cmd_runner.network)
       +            wallet.update()
       +    else:
       +        cmd_runner.network = None
       +
            try:
                result = func(*args[1:])
       +    except socket.error:
       +        print "Daemon not running"
       +        sys.exit(1)
            except Exception:
                traceback.print_exc(file=sys.stdout)
                sys.exit(1)
        
       +
       +    if cmd.requires_network and not options.offline:
       +        if wallet:
       +            wallet.stop_threads()
       +
       +
            if type(result) == str:
                util.print_msg(result)
            elif result is not None:
       t@@ -191,15 +211,6 @@ if __name__ == '__main__':
            # instanciate wallet for command-line
            storage = WalletStorage(config)
        
       -    if cmd.requires_wallet:
       -        wallet = Wallet(storage)
       -    else:
       -        wallet = None
       -
       -    if cmd.name not in ['create', 'restore'] and cmd.requires_wallet and not storage.file_exists:
       -        print_msg("Error: Wallet file not found.")
       -        print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
       -        sys.exit(0)
        
            if cmd.name in ['create', 'restore']:
                if storage.file_exists:
       t@@ -215,35 +226,26 @@ if __name__ == '__main__':
                if not config.get('server'):
                    config.set_key('server', pick_random_server())
        
       -        fee = options.tx_fee if options.tx_fee else raw_input("fee (default:%s):" % (str(Decimal(wallet.fee)/100000000)))
       -        gap = options.gap_limit if options.gap_limit else raw_input("gap limit (default 5):")
       -
       -        if fee:
       -            wallet.set_fee(float(fee)*100000000)
       -        if gap:
       -            wallet.change_gap_limit(int(gap))
       +        #fee = options.tx_fee if options.tx_fee else raw_input("fee (default:%s):" % (str(Decimal(wallet.fee)/100000000)))
       +        #gap = options.gap_limit if options.gap_limit else raw_input("gap limit (default 5):")
       +        #if fee:
       +        #    wallet.set_fee(float(fee)*100000000)
       +        #if gap:
       +        #    wallet.change_gap_limit(int(gap))
        
                if cmd.name == 'restore':
                    import getpass
                    seed = getpass.getpass(prompt="seed:", stream=None) if options.concealed else raw_input("seed:")
       -            try:
       -                seed.decode('hex')
       -            except Exception:
       -                print_error("Warning: Not hex, trying decode.")
       -                seed = mnemonic_decode(seed.split(' '))
       -            if not seed:
       -                sys.exit("Error: No seed")
       -
       -            wallet.init_seed(str(seed))
       +            wallet = Wallet.from_seed(str(seed),storage)
       +            if not wallet:
       +                sys.exit("Error: Invalid seed")
                    wallet.save_seed(password)
                    if not options.offline:
                        network = Network(config)
                        network.start()
                        wallet.start_threads(network)
       -
                        print_msg("Recovering wallet...")
                        wallet.restore(lambda x: x)
       -
                        if wallet.is_found():
                            print_msg("Recovery successful")
                        else:
       t@@ -253,6 +255,7 @@ if __name__ == '__main__':
                        print_msg("Warning: This wallet was restored offline. It may contain more addresses than displayed.")
        
                else:
       +            wallet = Wallet(storage)
                    wallet.init_seed(None)
                    wallet.save_seed(password)
                    wallet.synchronize()
       t@@ -264,6 +267,19 @@ if __name__ == '__main__':
                # terminate
                sys.exit(0)
        
       +
       +    if cmd.name not in ['create', 'restore'] and cmd.requires_wallet and not storage.file_exists:
       +        print_msg("Error: Wallet file not found.")
       +        print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
       +        sys.exit(0)
       +
       +
       +    if cmd.requires_wallet:
       +        wallet = Wallet(storage)
       +    else:
       +        wallet = None
       +
       +
            # important warning
            if cmd.name in ['dumpprivkey', 'dumpprivkeys']:
                print_msg("WARNING: ALL your private keys are secret.")
       t@@ -345,19 +361,37 @@ if __name__ == '__main__':
                    print_msg("Warning: Final argument was reconstructed from several arguments:", repr(message))
                    args = args[0:cmd.min_args] + [message]
        
       -    # open session
       -    if cmd.requires_network and not options.offline:
       -        network = Network(config)
       -        if not network.start(wait=True):
       -            print_msg("Not connected, aborting.")
       -            sys.exit(1)
       -        print_error("Connected to " + network.interface.connection_msg)
        
       -        if wallet:
       -            wallet.start_threads(network)
       -            wallet.update()
       -    else:
       -        network = None
       +    if cmd.name == 'daemon' and args[1] == 'start':
       +        pid = os.fork()
       +        if (pid == 0): # The first child.
       +            os.chdir("/")
       +            os.setsid()
       +            os.umask(0)
       +            pid2 = os.fork()
       +            if (pid2 == 0):  # Second child
       +                from SimpleXMLRPCServer import SimpleXMLRPCServer
       +                # start the daemon
       +                network = Network(config)
       +                if not network.start(wait=True):
       +                    print_msg("Not connected, aborting.")
       +                    sys.exit(1)
       +                print_msg("Connected to " + network.interface.connection_msg)
       +                server = SimpleXMLRPCServer(('localhost',8000), allow_none=True, logRequests=False)
       +                server.register_function(network.synchronous_get, 'synchronous_get')
       +                server.register_function(network.get_servers, 'get_servers')
       +                server.register_function(network.main_server, 'main_server')
       +                server.register_function(network.send, 'send')
       +                server.register_function(network.subscribe, 'subscribe')
       +                server.register_function(network.is_connected, 'is_connected')
       +                server.register_function(network.is_up_to_date, 'is_up_to_date')
       +                server.register_function(lambda: setattr(server,'running', False), 'stop')
       +                server.running = True
       +                while server.running:
       +                    server.handle_request()
       +                print_msg("Daemon stopped")
       +
       +        sys.exit(0)
        
            # run the command
            if cmd.name == 'deseed':
       t@@ -394,11 +428,8 @@ if __name__ == '__main__':
                wallet.update_password(password, new_password)
        
            else:
       -        run_command(cmd.name, password, args)
       +        run_command(cmd, password, args)
        
       -    if network:
       -        if wallet:
       -            wallet.stop_threads()
       -        network.stop()
       -        time.sleep(0.1)
       -        sys.exit(0)
       +
       +    time.sleep(0.1)
       +    sys.exit(0)
   DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
       t@@ -3,7 +3,7 @@ from PyQt4.QtCore import *
        import PyQt4.QtCore as QtCore
        
        from electrum.i18n import _
       -from electrum import Wallet, mnemonic
       +from electrum import Wallet
        
        from seed_dialog import SeedDialog
        from network_dialog import NetworkDialog
       t@@ -259,13 +259,13 @@ class InstallWizard(QDialog):
                if not action: 
                    return
        
       -        wallet = Wallet(self.storage)
       -        gap = self.config.get('gap_limit', 5)
       -        if gap != 5:
       -            wallet.gap_limit = gap
       -            wallet.storage.put('gap_limit', gap, True)
       +        #gap = self.config.get('gap_limit', 5)
       +        #if gap != 5:
       +        #    wallet.gap_limit = gap
       +        #    wallet.storage.put('gap_limit', gap, True)
        
                if action == 'create':
       +            wallet = Wallet(self.storage)
                    wallet.init_seed(None)
                    if not self.show_seed(wallet):
                        return
       t@@ -277,23 +277,14 @@ class InstallWizard(QDialog):
                        wallet.synchronize()  # generate first addresses offline
                    self.waiting_dialog(create)
        
       -
                elif action == 'restore':
                    seed = self.seed_dialog()
                    if not seed:
                        return
       -            try:
       -                wallet.init_seed(seed)
       -            except Exception:
       -                import traceback
       -                traceback.print_exc(file=sys.stdout)
       -                QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
       -                return
       -
       +            wallet = Wallet.from_seed(seed, self.storage)
                    ok, old_password, password = self.password_dialog(wallet)
                    wallet.save_seed(password)
        
       -
                elif action == 'watching':
                    mpk = self.mpk_dialog()
                    if not mpk:
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -426,6 +426,9 @@ class ElectrumWindow(QMainWindow):
                raw_transaction_text = raw_transaction_menu.addAction(_("&From text"))
                raw_transaction_text.triggered.connect(self.do_process_from_text)
        
       +        raw_transaction_text = raw_transaction_menu.addAction(_("&From the blockchain"))
       +        raw_transaction_text.triggered.connect(self.do_process_from_txid)
       +
        
                help_menu = menubar.addMenu(_("&Help"))
                show_about = help_menu.addAction(_("&About"))
       t@@ -1887,6 +1890,18 @@ class ElectrumWindow(QMainWindow):
                if tx:
                    self.show_transaction(tx)
        
       +    def do_process_from_txid(self):
       +        from electrum import transaction
       +        txid, ok = QInputDialog.getText(self, _('Lookup transaction'), _('Transaction ID') + ':')
       +        if ok and txid:
       +            r = self.network.synchronous_get([ ('blockchain.transaction.get',[str(txid)]) ])[0]
       +            if r:
       +                tx = transaction.Transaction(r)
       +                if tx:
       +                    self.show_transaction(tx)
       +                else:
       +                    self.show_message("unknown transaction")
       +
            def do_process_from_csvReader(self, csvReader):
                outputs = []
                try:
   DIR diff --git a/lib/__init__.py b/lib/__init__.py
       t@@ -1,7 +1,7 @@
        from version import ELECTRUM_VERSION
        from util import format_satoshis, print_msg, print_json, print_error, set_verbosity
        from wallet import WalletSynchronizer, WalletStorage
       -from wallet_factory import WalletFactory as Wallet
       +from wallet import Wallet
        from verifier import TxVerifier
        from network import Network, DEFAULT_SERVERS, DEFAULT_PORTS, pick_random_server
        from interface import Interface
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -53,14 +53,25 @@ def op_push(i):
            
        
        
       +def sha256(x):
       +    return hashlib.sha256(x).digest()
       +
        def Hash(x):
            if type(x) is unicode: x=x.encode('utf-8')
       -    return hashlib.sha256(hashlib.sha256(x).digest()).digest()
       +    return sha256(sha256(x))
       +
        hash_encode = lambda x: x[::-1].encode('hex')
        hash_decode = lambda x: x.decode('hex')[::-1]
       -
        hmac_sha_512 = lambda x,y: hmac.new(x, y, hashlib.sha512).digest()
       -mnemonic_hash = lambda x: hmac_sha_512("Bitcoin mnemonic", x).encode('hex')
       +
       +def mnemonic_to_seed(mnemonic, passphrase):
       +    from pbkdf2 import PBKDF2
       +    import hmac
       +    PBKDF2_ROUNDS = 2048
       +    return PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations = PBKDF2_ROUNDS, macmodule = hmac, digestmodule = hashlib.sha512).read(64)
       +
       +from version import SEED_PREFIX
       +is_seed = lambda x: hmac_sha_512("Seed version", x).encode('hex')[0:2].startswith(SEED_PREFIX)
        
        # pywallet openssl private key implementation
        
       t@@ -115,11 +126,11 @@ def i2o_ECPublicKey(pubkey, compressed=False):
        def hash_160(public_key):
            try:
                md = hashlib.new('ripemd160')
       -        md.update(hashlib.sha256(public_key).digest())
       +        md.update(sha256(public_key))
                return md.digest()
            except Exception:
                import ripemd
       -        md = ripemd.new(hashlib.sha256(public_key).digest())
       +        md = ripemd.new(sha256(public_key))
                return md.digest()
        
        
       t@@ -137,15 +148,6 @@ def bc_address_to_hash_160(addr):
            bytes = b58decode(addr, 25)
            return ord(bytes[0]), bytes[1:21]
        
       -def encode_point(pubkey, compressed=False):
       -    order = generator_secp256k1.order()
       -    p = pubkey.pubkey.point
       -    x_str = ecdsa.util.number_to_string(p.x(), order)
       -    y_str = ecdsa.util.number_to_string(p.y(), order)
       -    if compressed:
       -        return chr(2 + (p.y() & 1)) + x_str
       -    else:
       -        return chr(4) + pubkey.to_string() #x_str + y_str
        
        __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
        __b58base = len(__b58chars)
       t@@ -233,8 +235,7 @@ def regenerate_key(sec):
            if not b:
                return False
            b = b[0:32]
       -    secret = int('0x' + b.encode('hex'), 16)
       -    return EC_KEY(secret)
       +    return EC_KEY(b)
        
        def GetPubKey(pubkey, compressed=False):
            return i2o_ECPublicKey(pubkey, compressed)
       t@@ -282,13 +283,14 @@ try:
        except Exception:
            print "cannot import ecdsa.curve_secp256k1. You probably need to upgrade ecdsa.\nTry: sudo pip install --upgrade ecdsa"
            exit()
       +
        from ecdsa.curves import SECP256k1
       +from ecdsa.ellipticcurve import Point
        from ecdsa.util import string_to_number, number_to_string
        
        def msg_magic(message):
            varint = var_int(len(message))
            encoded_varint = "".join([chr(int(varint[i:i+2], 16)) for i in xrange(0, len(varint), 2)])
       -
            return "\x18Bitcoin Signed Message:\n" + encoded_varint + message
        
        
       t@@ -301,9 +303,66 @@ def verify_message(address, signature, message):
                return False
        
        
       +def chunks(l, n):
       +    return [l[i:i+n] for i in xrange(0, len(l), n)]
       +
       +
       +def ECC_YfromX(x,curved=curve_secp256k1, odd=True):
       +    _p = curved.p()
       +    _a = curved.a()
       +    _b = curved.b()
       +    for offset in range(128):
       +        Mx = x + offset
       +        My2 = pow(Mx, 3, _p) + _a * pow(Mx, 2, _p) + _b % _p
       +        My = pow(My2, (_p+1)/4, _p )
       +
       +        if curved.contains_point(Mx,My):
       +            if odd == bool(My&1):
       +                return [My,offset]
       +            return [_p-My,offset]
       +    raise Exception('ECC_YfromX: No Y found')
       +
       +def private_header(msg,v):
       +    assert v<1, "Can't write version %d private header"%v
       +    r = ''
       +    if v==0:
       +        r += ('%08x'%len(msg)).decode('hex')
       +        r += sha256(msg)[:2]
       +    return ('%02x'%v).decode('hex') + ('%04x'%len(r)).decode('hex') + r
       +
       +def public_header(pubkey,v):
       +    assert v<1, "Can't write version %d public header"%v
       +    r = ''
       +    if v==0:
       +        r = sha256(pubkey)[:2]
       +    return '\x6a\x6a' + ('%02x'%v).decode('hex') + ('%04x'%len(r)).decode('hex') + r
       +
       +
       +def negative_point(P):
       +    return Point( P.curve(), P.x(), -P.y(), P.order() )
       +
       +
       +def point_to_ser(P, comp=True ):
       +    if comp:
       +        return ( ('%02x'%(2+(P.y()&1)))+('%064x'%P.x()) ).decode('hex')
       +    return ( '04'+('%064x'%P.x())+('%064x'%P.y()) ).decode('hex')
       +
       +
       +def ser_to_point(Aser):
       +    curve = curve_secp256k1
       +    generator = generator_secp256k1
       +    _r  = generator.order()
       +    assert Aser[0] in ['\x02','\x03','\x04']
       +    if Aser[0] == '\x04':
       +        return Point( curve, str_to_long(Aser[1:33]), str_to_long(Aser[33:]), _r )
       +    Mx = string_to_number(Aser[1:])
       +    return Point( curve, Mx, ECC_YfromX(Mx, curve, Aser[0]=='\x03')[0], _r )
       +
       +
        
        class EC_KEY(object):
       -    def __init__( self, secret ):
       +    def __init__( self, k ):
       +        secret = string_to_number(k)
                self.pubkey = ecdsa.ecdsa.Public_key( generator_secp256k1, generator_secp256k1 * secret )
                self.privkey = ecdsa.ecdsa.Private_key( self.pubkey, secret )
                self.secret = secret
       t@@ -323,10 +382,11 @@ class EC_KEY(object):
                else:
                    raise Exception("error: cannot sign message")
        
       +
            @classmethod
            def verify_message(self, address, signature, message):
                """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """
       -        from ecdsa import numbertheory, ellipticcurve, util
       +        from ecdsa import numbertheory, util
                import msqr
                curve = curve_secp256k1
                G = generator_secp256k1
       t@@ -352,7 +412,7 @@ class EC_KEY(object):
                beta = msqr.modular_sqrt(alpha, curve.p())
                y = beta if (beta - recid) % 2 == 0 else curve.p() - beta
                # 1.4 the constructor checks that nR is at infinity
       -        R = ellipticcurve.Point(curve, x, y, order)
       +        R = Point(curve, x, y, order)
                # 1.5 compute e from message:
                h = Hash( msg_magic(message) )
                e = string_to_number(h)
       t@@ -364,11 +424,94 @@ class EC_KEY(object):
                # check that Q is the public key
                public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
                # check that we get the original signing address
       -        addr = public_key_to_bc_address( encode_point(public_key, compressed) )
       +        addr = public_key_to_bc_address( point_to_ser(public_key.pubkey.point, compressed) )
                if address != addr:
                    raise Exception("Bad signature")
        
        
       +    # ecdsa encryption/decryption methods
       +    # credits: jackjack, https://github.com/jackjack-jj/jeeq
       +
       +    @classmethod
       +    def encrypt_message(self, message, pubkey):
       +        generator = generator_secp256k1
       +        curved = curve_secp256k1
       +        r = ''
       +        msg = private_header(message,0) + message
       +        msg = msg + ('\x00'*( 32-(len(msg)%32) ))
       +        msgs = chunks(msg,32)
       +
       +        _r  = generator.order()
       +        str_to_long = string_to_number
       +
       +        P = generator
       +        if len(pubkey)==33: #compressed
       +            pk = Point( curve_secp256k1, str_to_long(pubkey[1:33]), ECC_YfromX(str_to_long(pubkey[1:33]), curve_secp256k1, pubkey[0]=='\x03')[0], _r )
       +        else:
       +            pk = Point( curve_secp256k1, str_to_long(pubkey[1:33]), str_to_long(pubkey[33:65]), _r )
       +
       +        for i in range(len(msgs)):
       +            n = ecdsa.util.randrange( pow(2,256) )
       +            Mx = str_to_long(msgs[i])
       +            My, xoffset = ECC_YfromX(Mx, curved)
       +            M = Point( curved, Mx+xoffset, My, _r )
       +            T = P*n
       +            U = pk*n + M
       +            toadd = point_to_ser(T) + point_to_ser(U)
       +            toadd = chr(ord(toadd[0])-2 + 2*xoffset) + toadd[1:]
       +            r += toadd
       +
       +        return base64.b64encode(public_header(pubkey,0) + r)
       +
       +
       +    def decrypt_message(self, enc):
       +        G = generator_secp256k1
       +        curved = curve_secp256k1
       +        pvk = self.secret
       +        pubkeys = [point_to_ser(G*pvk,True), point_to_ser(G*pvk,False)]
       +        enc = base64.b64decode(enc)
       +        str_to_long = string_to_number
       +
       +        assert enc[:2]=='\x6a\x6a'
       +
       +        phv = str_to_long(enc[2])
       +        assert phv==0, "Can't read version %d public header"%phv
       +        hs = str_to_long(enc[3:5])
       +        public_header=enc[5:5+hs]
       +        checksum_pubkey=public_header[:2]
       +        address=filter(lambda x:sha256(x)[:2]==checksum_pubkey, pubkeys)
       +        assert len(address)>0, 'Bad private key'
       +        address=address[0]
       +        enc=enc[5+hs:]
       +        r = ''
       +        for Tser,User in map(lambda x:[x[:33],x[33:]], chunks(enc,66)):
       +            ots = ord(Tser[0])
       +            xoffset = ots>>1
       +            Tser = chr(2+(ots&1))+Tser[1:]
       +            T = ser_to_point(Tser)
       +            U = ser_to_point(User)
       +            V = T*pvk
       +            Mcalc = U + negative_point(V)
       +            r += ('%064x'%(Mcalc.x()-xoffset)).decode('hex')
       +
       +        pvhv = str_to_long(r[0])
       +        assert pvhv==0, "Can't read version %d private header"%pvhv
       +        phs = str_to_long(r[1:3])
       +        private_header = r[3:3+phs]
       +        size = str_to_long(private_header[:4])
       +        checksum = private_header[4:6]
       +        r = r[3+phs:]
       +
       +        msg = r[:size]
       +        hashmsg = sha256(msg)[:2]
       +        checksumok = hashmsg==checksum
       +
       +        return [msg, checksumok, address]
       +
       +
       +
       +
       +
        ###################################### BIP32 ##############################
        
        random_seed = lambda n: "%032x"%ecdsa.util.randrange( pow(2,n) )
       t@@ -408,7 +551,7 @@ def CKD(k, c, n):
            import hmac
            from ecdsa.util import string_to_number, number_to_string
            order = generator_secp256k1.order()
       -    keypair = EC_KEY(string_to_number(k))
       +    keypair = EC_KEY(k)
            K = GetPubKey(keypair.pubkey,True)
        
            if n & BIP32_PRIME: # We want to make a "secret" address that can't be determined from K
       t@@ -531,8 +674,35 @@ def test_bip32(seed, sequence):
        
                
        
       +def test_crypto():
       +
       +    G = generator_secp256k1
       +    _r  = G.order()
       +    pvk = ecdsa.util.randrange( pow(2,256) ) %_r
       +
       +    Pub = pvk*G
       +    pubkey_c = point_to_ser(Pub,True)
       +    pubkey_u = point_to_ser(Pub,False)
       +    addr_c = public_key_to_bc_address(pubkey_c)
       +    addr_u = public_key_to_bc_address(pubkey_u)
       +
       +    print "Private key            ", '%064x'%pvk
       +    print "Compressed public key  ", pubkey_c.encode('hex')
       +    print "Uncompressed public key", pubkey_u.encode('hex')
       +
       +    message = "Chancellor on brink of second bailout for banks"
       +    enc = EC_KEY.encrypt_message(message,pubkey_c)
       +    eck = EC_KEY(number_to_string(pvk,_r))
       +    dec = eck.decrypt_message(enc)
       +    print "decrypted", dec
       +
       +    signature = eck.sign_message(message, True, addr_c)
       +    print signature
       +    EC_KEY.verify_message(addr_c, signature, message)
       +
        
        if __name__ == '__main__':
       -    test_bip32("000102030405060708090a0b0c0d0e0f", "0'/1/2'/2/1000000000")
       -    test_bip32("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542","0/2147483647'/1/2147483646'/2")
       +    test_crypto()
       +    #test_bip32("000102030405060708090a0b0c0d0e0f", "0'/1/2'/2/1000000000")
       +    #test_bip32("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542","0/2147483647'/1/2147483646'/2")
        
   DIR diff --git a/lib/commands.py b/lib/commands.py
       t@@ -67,9 +67,9 @@ register_command('dumpprivkeys',         0, 0, False, True,  True,  'dump all pr
        register_command('freeze',               1, 1, False, True,  True,  'Freeze the funds at one of your wallet\'s addresses', 'freeze <address>')
        register_command('getbalance',           0, 1, True,  True,  False, 'Return the balance of your wallet, or of one account in your wallet', 'getbalance [<account>]')
        register_command('getservers',           0, 0, True,  False, False, 'Return the list of available servers')
       -register_command('getversion',           0, 0, False,  False,  False, 'Return the version of your client', 'getversion')
       -register_command('getaddressbalance',    1, 1, True,  True,  False, 'Return the balance of an address', 'getaddressbalance <address>')
       -register_command('getaddresshistory',    1, 1, True,  True,  False, 'Return the transaction history of a wallet address', 'getaddresshistory <address>')
       +register_command('getversion',           0, 0, False, False, False, 'Return the version of your client', 'getversion')
       +register_command('getaddressbalance',    1, 1, True,  False, False, 'Return the balance of an address', 'getaddressbalance <address>')
       +register_command('getaddresshistory',    1, 1, True,  False, False, 'Return the transaction history of a wallet address', 'getaddresshistory <address>')
        register_command('getconfig',            1, 1, False, False, False, 'Return a configuration variable', 'getconfig <name>')
        register_command('getpubkeys',           1, 1, False, True,  False, 'Return the public keys for a wallet address', 'getpubkeys <bitcoin address>')
        register_command('getrawtransaction',    1, 1, True,  False, False, 'Retrieve a transaction', 'getrawtransaction <txhash>')
       t@@ -79,7 +79,8 @@ register_command('help',                 0, 1, False, False, False, 'Prints this
        register_command('history',              0, 0, True,  True,  False, 'Returns the transaction history of your wallet')
        register_command('importprivkey',        1, 1, False, True,  True,  'Import a private key', 'importprivkey <privatekey>')
        register_command('listaddresses',        2, 2, False, True,  False, 'Returns your list of addresses.', '', listaddr_options)
       -register_command('listunspent',          0, 0, True,  True,  False, 'Returns the list of unspent inputs in your wallet.')
       +register_command('listunspent',          0, 0, True,  False, False, 'Returns the list of unspent inputs in your wallet.')
       +register_command('getaddressunspent',    1, 1, True,  False, False, 'Returns the list of unspent inputs in your wallet.')
        register_command('mktx',                 5, 5, False, True,  True,  'Create a signed transaction', 'mktx <recipient> <amount> [label]', payto_options)
        register_command('mksendmanytx',         4, 4, False, True,  True,  'Create a signed transaction', mksendmany_syntax, payto_options)
        register_command('payto',                5, 5, True,  True,  True,  'Create and broadcast a transaction.', payto_syntax, payto_options)
       t@@ -95,6 +96,12 @@ register_command('unfreeze',             1, 1, False, True,  False, 'Unfreeze th
        register_command('validateaddress',      1, 1, False, False, False, 'Check that the address is valid', 'validateaddress <address>')
        register_command('verifymessage',        3,-1, False, False, False, 'Verifies a signature', verifymessage_syntax)
        
       +register_command('encrypt',              2,-1, False, False, False, 'encrypt a message with pubkey','encrypt <pubkey> <message>')
       +register_command('decrypt',              2,-1, False, False, False, 'decrypt a message with privkey','decrypt <privkey> <message>')
       +register_command('daemon',               1, 1, True, False, False, 'start/stop daemon')
       +register_command('getproof',             1, 1, True, False, False, 'get merkle proof', 'getproof <address>')
       +register_command('getunspentaddress',    2, 2, True, False, False, 'get the address of an unspent','getunspentaddress <txid> <pos>')
       +
        
        
        
       t@@ -106,6 +113,7 @@ class Commands:
                self._callback = callback
                self.password = None
        
       +
            def _run(self, method, args, password_getter):
                cmd = known_commands[method]
                if cmd.requires_password and self.wallet.use_encryption:
       t@@ -117,11 +125,22 @@ class Commands:
                    apply(self._callback, ())
                return result
        
       +
            def getaddresshistory(self, addr):
       -        assert self.wallet.is_mine(addr)
       -        h = self.wallet.get_history(addr)
       -        if h is None: h = self.network.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0]
       -        return h
       +        return self.network.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0]
       +
       +
       +    def daemon(self, arg):
       +        if arg=='stop':
       +            return self.network.stop()
       +        elif arg=='status':
       +            return { 
       +                'server':self.network.main_server(), 
       +                'connected':self.network.is_connected()
       +            }
       +        else:
       +            return "unknown command \"%s\""% arg
       +
        
            def listunspent(self):
                import copy
       t@@ -129,6 +148,15 @@ class Commands:
                for i in l: i["value"] = str(Decimal(i["value"])/100000000)
                return l
        
       +
       +    def getaddressunspent(self, addr):
       +        return self.network.synchronous_get([ ('blockchain.address.listunspent',[addr]) ])[0]
       +
       +
       +    def getunspentaddress(self, txid, num):
       +        return self.network.synchronous_get([ ('blockchain.utxo.get_address',[txid, num]) ])[0]
       +
       +
            def createrawtransaction(self, inputs, outputs):
                # convert to own format
                for i in inputs:
       t@@ -199,9 +227,14 @@ class Commands:
                return out
        
            def getaddressbalance(self, addr):
       -        c, u = self.wallet.get_addr_balance(addr)
       -        out = { "confirmed": str(Decimal(c)/100000000) }
       -        if u: out["unconfirmed"] = str(Decimal(u)/100000000)
       +        b = self.network.synchronous_get([ ('blockchain.address.get_balance',[addr]) ])[0]
       +        return str(Decimal(b)/100000000)
       +
       +    def getproof(self, addr):
       +        p = self.network.synchronous_get([ ('blockchain.address.get_proof',[addr]) ])[0]
       +        out = []
       +        for i,s in p:
       +            out.append(i)
                return out
        
            def getservers(self):
       t@@ -350,11 +383,27 @@ class Commands:
                    if cmd.options: print_msg("options:\n" + cmd.options)
                return None
        
       +
            def getrawtransaction(self, tx_hash):
       +        import transaction
                if self.wallet:
                    tx = self.wallet.transactions.get(tx_hash)
                    if tx:
                        return tx
       -        return self.network.retrieve_transaction(tx_hash)
       +
       +        r = self.network.synchronous_get([ ('blockchain.transaction.get',[tx_hash]) ])[0]
       +        if r:
       +            return transaction.Transaction(r)
       +        else:
       +            return "unknown transaction"
       +
       +    def encrypt(self, pubkey, message):
       +        return EC_KEY.encrypt_message(message, pubkey.decode('hex'))
       +
       +    def decrypt(self, secret, message):
       +        ec = regenerate_key(secret)
       +        decrypted = ec.decrypt_message(message)
       +        return decrypted[0]
       +
        
        
   DIR diff --git a/lib/interface.py b/lib/interface.py
       t@@ -598,6 +598,22 @@ class Interface(threading.Thread):
                self.queue.put(self)
        
        
       +    def synchronous_get(self, requests, timeout=100000000):
       +        queue = Queue.Queue()
       +        ids = self.send(requests, lambda i,r: queue.put(r))
       +        id2 = ids[:]
       +        res = {}
       +        while ids:
       +            r = queue.get(True, timeout)
       +            _id = r.get('id')
       +            if _id in ids:
       +                ids.remove(_id)
       +                res[_id] = r.get('result')
       +        out = []
       +        for _id in id2:
       +            out.append(res[_id])
       +        return out
       +
        
        if __name__ == "__main__":
        
   DIR diff --git a/lib/network.py b/lib/network.py
       t@@ -22,6 +22,37 @@ DEFAULT_SERVERS = {
        }
        
        
       +def parse_servers(result):
       +    """ parse servers list into dict format"""
       +    from version import PROTOCOL_VERSION
       +    servers = {}
       +    for item in result:
       +        host = item[1]
       +        out = {}
       +        version = None
       +        pruning_level = '-'
       +        if len(item) > 2:
       +            for v in item[2]:
       +                if re.match("[stgh]\d*", v):
       +                    protocol, port = v[0], v[1:]
       +                    if port == '': port = DEFAULT_PORTS[protocol]
       +                    out[protocol] = port
       +                elif re.match("v(.?)+", v):
       +                    version = v[1:]
       +                elif re.match("p\d*", v):
       +                    pruning_level = v[1:]
       +                if pruning_level == '': pruning_level = '0'
       +        try: 
       +            is_recent = float(version)>=float(PROTOCOL_VERSION)
       +        except Exception:
       +            is_recent = False
       +
       +        if out and is_recent:
       +            out['pruning'] = pruning_level
       +            servers[host] = out
       +
       +    return servers
       +
        
        
        def filter_protocol(servers, p):
       t@@ -66,6 +97,8 @@ class Network(threading.Thread):
                self.interface = None
                self.proxy = self.config.get('proxy')
                self.heights = {}
       +        self.merkle_roots = {}
       +        self.utxo_roots = {}
                self.server_lag = 0
        
                dir_path = os.path.join( self.config.path, 'certs')
       t@@ -82,6 +115,14 @@ class Network(threading.Thread):
                return self.interface and self.interface.is_connected
        
        
       +    def is_up_to_date(self):
       +        return self.interface.is_up_to_date()
       +
       +
       +    def main_server(self):
       +        return self.interface.server
       +
       +
            def send_subscriptions(self):
                for cb, sub in self.subscriptions.items():
                    self.interface.send(sub, cb)
       t@@ -327,6 +368,8 @@ class Network(threading.Thread):
                if not result: return
                height = result.get('block_height')
                self.heights[i.server] = height
       +        self.merkle_roots[i.server] = result.get('merkle_root')
       +        self.utxo_roots[i.server] = result.get('utxo_root')
                # notify blockchain about the new height
                self.blockchain.queue.put((i,result))
        
       t@@ -341,7 +384,7 @@ class Network(threading.Thread):
        
            def on_peers(self, i, r):
                if not r: return
       -        self.irc_servers = self.parse_servers(r.get('result'))
       +        self.irc_servers = parse_servers(r.get('result'))
                self.trigger_callback('peers')
        
            def on_banner(self, i, r):
       t@@ -356,59 +399,26 @@ class Network(threading.Thread):
        
            
            def synchronous_get(self, requests, timeout=100000000):
       -        queue = Queue.Queue()
       -        ids = self.interface.send(requests, lambda i,r: queue.put(r))
       -        id2 = ids[:]
       -        res = {}
       -        while ids:
       -            r = queue.get(True, timeout)
       -            _id = r.get('id')
       -            if _id in ids:
       -                ids.remove(_id)
       -                res[_id] = r.get('result')
       -        out = []
       -        for _id in id2:
       -            out.append(res[_id])
       -        return out
       +        return self.interface.synchronous_get(requests)
       +
       +
       +
       +    #def retrieve_transaction(self, tx_hash, tx_height=0):
       +    #    import transaction
       +    #    r = self.synchronous_get([ ('blockchain.transaction.get',[tx_hash, tx_height]) ])[0]
       +    #    if r:
       +    #        return transaction.Transaction(r)
       +
       +
       +
        
        
       -    def retrieve_transaction(self, tx_hash, tx_height=0):
       -        import transaction
       -        r = self.synchronous_get([ ('blockchain.transaction.get',[tx_hash, tx_height]) ])[0]
       -        if r:
       -            return transaction.Transaction(r)
       -
       -
       -    def parse_servers(self, result):
       -        """ parse servers list into dict format"""
       -        from version import PROTOCOL_VERSION
       -        servers = {}
       -        for item in result:
       -            host = item[1]
       -            out = {}
       -            version = None
       -            pruning_level = '-'
       -            if len(item) > 2:
       -                for v in item[2]:
       -                    if re.match("[stgh]\d*", v):
       -                        protocol, port = v[0], v[1:]
       -                        if port == '': port = DEFAULT_PORTS[protocol]
       -                        out[protocol] = port
       -                    elif re.match("v(.?)+", v):
       -                        version = v[1:]
       -                    elif re.match("p\d*", v):
       -                        pruning_level = v[1:]
       -                    if pruning_level == '': pruning_level = '0'
       -            try: 
       -                is_recent = float(version)>=float(PROTOCOL_VERSION)
       -            except Exception:
       -                is_recent = False
       -
       -            if out and is_recent:
       -                out['pruning'] = pruning_level
       -                servers[host] = out
       -
       -        return servers
       +class NetworkProxy:
       +    # interface to the network object. 
       +    # handle subscriptions and callbacks
       +    # the network object can be jsonrpc server 
       +    def __init__(self, network):
       +        self.network = network
        
        
        
   DIR diff --git a/lib/version.py b/lib/version.py
       t@@ -1,5 +1,5 @@
        ELECTRUM_VERSION = "1.9.8"  # version of the client package
       -PROTOCOL_VERSION = '0.6'    # protocol version requested
       -SEED_VERSION     = 4        # bump this every time the seed generation is modified
       +PROTOCOL_VERSION = '0.9'    # protocol version requested
       +NEW_SEED_VERSION = 6        # bip32 wallets
       +OLD_SEED_VERSION = 4        # old electrum deterministic generation
        SEED_PREFIX      = '01'     # the hash of the mnemonic seed must begin with this
       -
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -73,6 +73,7 @@ class WalletStorage:
        
            def __init__(self, config):
                self.lock = threading.Lock()
       +        self.config = config
                self.data = {}
                self.file_exists = False
                self.path = self.init_path(config)
       t@@ -151,7 +152,10 @@ class WalletStorage:
                    os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
        
        
       -class Wallet:
       +
       +    
       +
       +class NewWallet:
        
            def __init__(self, storage):
        
       t@@ -160,7 +164,7 @@ class Wallet:
                self.gap_limit_for_change = 3 # constant
        
                # saved fields
       -        self.seed_version          = storage.get('seed_version', SEED_VERSION)
       +        self.seed_version          = storage.get('seed_version', NEW_SEED_VERSION)
        
                self.gap_limit             = storage.get('gap_limit', 5)
                self.use_change            = storage.get('use_change',True)
       t@@ -180,12 +184,6 @@ class Wallet:
        
                self.next_addresses = storage.get('next_addresses',{})
        
       -        if self.seed_version not in [4, 6]:
       -            msg = "This wallet seed is not supported."
       -            if self.seed_version in [5]:
       -                msg += "\nTo open this wallet, try 'git checkout seed_v%d'"%self.seed_version
       -                print msg
       -                sys.exit(1)
        
                # This attribute is set when wallet.start_threads is called.
                self.synchronizer = None
       t@@ -290,42 +288,27 @@ class Wallet:
                    # we keep only 13 words, that's approximately 139 bits of entropy
                    words = mnemonic.mn_encode(s)[0:13] 
                    seed = ' '.join(words)
       -            if mnemonic_hash(seed).startswith(SEED_PREFIX): 
       -                break  # this removes 12 bits of entropy 
       +            if is_seed(seed):
       +                break  # this will remove 8 bits of entropy
                    nonce += 1
        
                return seed
        
        
            def init_seed(self, seed):
       -        import mnemonic
       +        import mnemonic, unicodedata
                
                if self.seed: 
                    raise Exception("a seed exists")
        
       -        if not seed:
       -            self.seed = random_seed(128)
       -            self.seed_version = 4
       -            return
       +        self.seed_version = NEW_SEED_VERSION
        
       -        # check if seed is hexadecimal
       -        seed = seed.strip()
       -        try:
       -            assert seed
       -            seed.decode('hex')
       -            self.seed_version = 4
       -            self.seed = str(seed)
       +        if not seed:
       +            self.seed = self.make_seed()
                    return
       -        except Exception:
       -            pass
        
       -        words = seed.split()
       -        self.seed_version = 4
       -        self.seed = mnemonic.mn_decode(words)
       +        self.seed = unicodedata.normalize('NFC', unicode(seed.strip()))
        
       -        if not self.seed:
       -            raise Exception("Invalid seed")
       -        
                    
        
            def save_seed(self, password):
       t@@ -338,41 +321,26 @@ class Wallet:
                self.create_accounts(password)
        
        
       -    def create_watching_only_wallet(self, params):
       -        K0, c0 = params
       -        if not K0:
       -            return
       -
       -        if not c0:
       -            self.seed_version = 4
       -            self.storage.put('seed_version', self.seed_version, True)
       -            self.create_old_account(K0)
       -            return
       -
       -        cK0 = ""
       +    def create_watching_only_wallet(self, K0, c0):
       +        cK0 = "" #FIXME
                self.master_public_keys = {
                    "m/0'/": (c0, K0, cK0),
                    }
                self.storage.put('master_public_keys', self.master_public_keys, True)
                self.storage.put('seed_version', self.seed_version, True)
       -        self.create_account('1','Main account')
       +        self.create_account('1of1','Main account')
        
        
            def create_accounts(self, password):
                seed = pw_decode(self.seed, password)
       -
       -        if self.seed_version == 4:
       -            mpk = OldAccount.mpk_from_seed(seed)
       -            self.create_old_account(mpk)
       -        else:
       -            # create default account
       -            self.create_master_keys('1', password)
       -            self.create_account('1','Main account')
       +        # create default account
       +        self.create_master_keys('1of1', password)
       +        self.create_account('1of1','Main account')
        
        
            def create_master_keys(self, account_type, password):
       -        master_k, master_c, master_K, master_cK = bip32_init(self.get_seed(None))
       -        if account_type == '1':
       +        master_k, master_c, master_K, master_cK = bip32_init(self.get_seed(password))
       +        if account_type == '1of1':
                    k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
                    self.master_public_keys["m/0'/"] = (c0, K0, cK0)
                    self.master_private_keys["m/0'/"] = pw_encode(k0, password)
       t@@ -398,7 +366,7 @@ class Wallet:
                self.storage.put('master_private_keys', self.master_private_keys, True)
        
            def has_master_public_keys(self, account_type):
       -        if account_type == '1':
       +        if account_type == '1of1':
                    return "m/0'/" in self.master_public_keys
                elif account_type == '2of2':
                    return set(["m/1'/", "m/2'/"]) <= set(self.master_public_keys.keys())
       t@@ -421,17 +389,18 @@ class Wallet:
        
            def deseed_branch(self, k):
                # check that parent has no seed
       -        assert self.seed == ''
       +        # assert self.seed == ''
                self.master_private_keys.pop(k)
                self.storage.put('master_private_keys', self.master_private_keys, True)
        
       +
            def is_watching_only(self):
                return (self.seed == '') and (self.master_private_keys == {})
        
        
        
            def account_id(self, account_type, i):
       -        if account_type == '1':
       +        if account_type == '1of1':
                    return "m/0'/%d"%i
                elif account_type == '2of2':
                    return "m/1'/%d & m/2'/%d"%(i,i)
       t@@ -451,7 +420,7 @@ class Wallet:
                return i
        
        
       -    def new_account_address(self, account_type = '1'):
       +    def new_account_address(self, account_type = '1of1'):
                i = self.num_accounts(account_type)
                k = self.account_id(account_type,i)
        
       t@@ -465,12 +434,12 @@ class Wallet:
                return k, addr
        
        
       -    def next_account(self, account_type = '1'):
       +    def next_account(self, account_type = '1of1'):
        
                i = self.num_accounts(account_type)
                account_id = self.account_id(account_type,i)
        
       -        if account_type is '1':
       +        if account_type is '1of1':
                    master_c0, master_K0, _ = self.master_public_keys["m/0'/"]
                    c0, K0, cK0 = bip32_public_derivation(master_c0.decode('hex'), master_K0.decode('hex'), "m/0'/", "m/0'/%d"%i)
                    account = BIP32_Account({ 'c':c0, 'K':K0, 'cK':cK0 })
       t@@ -514,7 +483,7 @@ class Wallet:
        
        
        
       -    def create_account(self, account_type = '1', name = None):
       +    def create_account(self, account_type = '1of1', name = None):
                k, account = self.next_account(account_type)
                if k in self.pending_accounts:
                    self.pending_accounts.pop(k)
       t@@ -526,12 +495,6 @@ class Wallet:
                    self.set_label(k, name)
        
        
       -    def create_old_account(self, mpk):
       -        self.storage.put('master_public_key', mpk, True)
       -        self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
       -        self.save_accounts()
       -
       -
            def save_accounts(self):
                d = {}
                for k, v in self.accounts.items():
       t@@ -596,14 +559,13 @@ class Wallet:
                return s[0] == 1
        
            def get_master_public_key(self):
       -        if self.seed_version == 4:
       -            return self.storage.get("master_public_key")
       -        else:
       -            c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
       -            return repr((c, K))
       +        c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
       +        return repr((c, K))
        
            def get_master_private_key(self, account, password):
       -        master_k = pw_decode( self.master_private_keys[account], password)
       +        k = self.master_private_keys.get(account)
       +        if not k: return
       +        master_k = pw_decode( k, password)
                master_c, master_K, master_Kc = self.master_public_keys[account]
                try:
                    K, Kc = get_pubkeys_from_secret(master_k.decode('hex'))
       t@@ -675,25 +637,14 @@ class Wallet:
                return '&'.join(dd)
        
        
       -
            def get_seed(self, password):
                s = pw_decode(self.seed, password)
       -        if self.seed_version == 4:
       -            seed = s
       -            self.accounts[0].check_seed(seed)
       -        else:
       -            seed = mnemonic_hash(s)
       +        seed = mnemonic_to_seed(s,'').encode('hex')
                return seed
       -        
        
       -    def get_mnemonic(self, password):
       -        import mnemonic
       -        s = pw_decode(self.seed, password)
       -        if self.seed_version == 4:
       -            return ' '.join(mnemonic.mn_encode(s))
       -        else:
       -            return s
        
       +    def get_mnemonic(self, password):
       +        return pw_decode(self.seed, password)
                
        
            def get_private_key(self, address, password):
       t@@ -746,23 +697,6 @@ class Wallet:
                for txin in tx.inputs:
                    keyid = txin.get('KeyID')
                    if keyid:
       -
       -                if self.seed_version == 4:
       -                    m = re.match("old\(([0-9a-f]+),(\d+),(\d+)", keyid)
       -                    if not m: continue
       -                    mpk = m.group(1)
       -                    if mpk != self.storage.get('master_public_key'): continue 
       -                    for_change = int(m.group(2))
       -                    num = int(m.group(3))
       -                    account = self.accounts[0]
       -                    addr = account.get_address(for_change, num)
       -                    txin['address'] = addr # fixme: side effect
       -                    pk = account.get_private_key(seed, (for_change, num))
       -                    pubkey = public_key_from_private_key(pk)
       -                    keypairs[pubkey] = pk
       -                    continue
       -
       -
                        roots = []
                        for s in keyid.split('&'):
                            m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
       t@@ -921,7 +855,7 @@ class Wallet:
        
        
            def create_pending_accounts(self):
       -        for account_type in ['1','2of2','2of3']:
       +        for account_type in ['1of1','2of2','2of3']:
                    if not self.has_master_public_keys(account_type):
                        continue
                    k, a = self.new_account_address(account_type)
       t@@ -1050,29 +984,23 @@ class Wallet:
        
        
            def get_account_name(self, k):
       -        if k == 0:
       -            if self.seed_version == 4: 
       -                name = 'Main account'
       +        default = "Unnamed account"
       +        m = re.match("m/0'/(\d+)", k)
       +        if m:
       +            num = m.group(1)
       +            if num == '0':
       +                default = "Main account"
                    else:
       -                name = 'Old account'
       -        else:
       -            default = "Unnamed account"
       -            m = re.match("m/0'/(\d+)", k)
       -            if m:
       -                num = m.group(1)
       -                if num == '0':
       -                    default = "Main account"
       -                else:
       -                    default = "Account %s"%num
       +                default = "Account %s"%num
                            
       -            m = re.match("m/1'/(\d+) & m/2'/(\d+)", k)
       -            if m:
       -                num = m.group(1)
       -                default = "2of2 account %s"%num
       -            name = self.labels.get(k, default)
       -
       +        m = re.match("m/1'/(\d+) & m/2'/(\d+)", k)
       +        if m:
       +            num = m.group(1)
       +            default = "2of2 account %s"%num
       +        name = self.labels.get(k, default)
                return name
        
       +
            def get_account_names(self):
                accounts = {}
                for k, account in self.accounts.items():
       t@@ -1081,6 +1009,7 @@ class Wallet:
                    accounts[-1] = 'Imported keys'
                return accounts
        
       +
            def get_account_addresses(self, a, include_change=True):
                if a is None:
                    o = self.addresses(True)
       t@@ -1537,7 +1466,7 @@ class Wallet:
            def start_threads(self, network):
                from verifier import TxVerifier
                self.network = network
       -        if self.network:
       +        if self.network is not None:
                    self.verifier = TxVerifier(self.network, self.storage)
                    self.verifier.start()
                    self.set_verifier(self.verifier)
       t@@ -1622,12 +1551,12 @@ class WalletSynchronizer(threading.Thread):
                    if not self.network.is_connected():
                        self.network.wait_until_connected()
                        
       -            self.run_interface(self.network.interface)
       +            self.run_interface()
        
        
       -    def run_interface(self, interface):
       +    def run_interface(self):
        
       -        print_error("synchronizer: connected to", interface.server)
       +        print_error("synchronizer: connected to", self.network.main_server())
        
                requested_tx = []
                missing_tx = []
       t@@ -1657,12 +1586,12 @@ class WalletSynchronizer(threading.Thread):
                    # request missing transactions
                    for tx_hash, tx_height in missing_tx:
                        if (tx_hash, tx_height) not in requested_tx:
       -                    interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r))
       +                    self.network.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r))
                            requested_tx.append( (tx_hash, tx_height) )
                    missing_tx = []
        
                    # detect if situation has changed
       -            if interface.is_up_to_date() and self.queue.empty():
       +            if self.network.is_up_to_date() and self.queue.empty():
                        if not self.wallet.is_up_to_date():
                            self.wallet.set_up_to_date(True)
                            self.was_updated = True
       t@@ -1672,7 +1601,7 @@ class WalletSynchronizer(threading.Thread):
                            self.was_updated = True
        
                    if self.was_updated:
       -                self.wallet.network.trigger_callback('updated')
       +                self.network.trigger_callback('updated')
                        self.was_updated = False
        
                    # 2. get a response
       t@@ -1681,8 +1610,9 @@ class WalletSynchronizer(threading.Thread):
                    except Queue.Empty:
                        continue
        
       -            if interface != self.network.interface:
       -                break
       +            # see if it changed
       +            #if interface != self.network.interface:
       +            #    break
                    
                    if not r:
                        continue
       t@@ -1700,7 +1630,7 @@ class WalletSynchronizer(threading.Thread):
                        addr = params[0]
                        if self.wallet.get_status(self.wallet.get_history(addr)) != result:
                            if requested_histories.get(addr) is None:
       -                        interface.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r))
       +                        self.network.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r))
                                requested_histories[addr] = result
        
                    elif method == 'blockchain.address.get_history':
       t@@ -1750,7 +1680,163 @@ class WalletSynchronizer(threading.Thread):
                        print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
        
                    if self.was_updated and not requested_tx:
       -                self.wallet.network.trigger_callback('updated')
       -                self.wallet.network.trigger_callback("new_transaction") # Updated gets called too many times from other places as well; if we use that signal we get the notification three times
       -
       +                self.network.trigger_callback('updated')
       +                # Updated gets called too many times from other places as well; if we use that signal we get the notification three times
       +                self.network.trigger_callback("new_transaction") 
                        self.was_updated = False
       +
       +
       +
       +
       +class OldWallet(NewWallet):
       +
       +    def init_seed(self, seed):
       +        import mnemonic
       +        
       +        if self.seed: 
       +            raise Exception("a seed exists")
       +
       +        if not seed:
       +            seed = random_seed(128)
       +
       +        self.seed_version = OLD_SEED_VERSION
       +
       +        # see if seed was entered as hex
       +        seed = seed.strip()
       +        try:
       +            assert seed
       +            seed.decode('hex')
       +            self.seed = str(seed)
       +            return
       +        except Exception:
       +            pass
       +
       +        words = seed.split()
       +        try:
       +            mnemonic.mn_decode(words)
       +        except Exception:
       +            raise
       +
       +        self.seed = mnemonic.mn_decode(words)
       +
       +        if not self.seed:
       +            raise Exception("Invalid seed")
       +            
       +
       +
       +    def get_master_public_key(self):
       +        return self.storage.get("master_public_key")
       +
       +    def create_accounts(self, password):
       +        seed = pw_decode(self.seed, password)
       +        mpk = OldAccount.mpk_from_seed(seed)
       +        self.create_account(mpk)
       +
       +    def create_account(self, mpk):
       +        self.storage.put('master_public_key', mpk, True)
       +        self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
       +        self.save_accounts()
       +
       +    def create_watching_only_wallet(self, K0):
       +        self.seed_version = OLD_SEED_VERSION
       +        self.storage.put('seed_version', self.seed_version, True)
       +        self.create_account(K0)
       +
       +    def get_seed(self, password):
       +        seed = pw_decode(self.seed, password)
       +        self.accounts[0].check_seed(seed)
       +        return seed
       +
       +    def get_mnemonic(self, password):
       +        import mnemonic
       +        s = pw_decode(self.seed, password)
       +        return ' '.join(mnemonic.mn_encode(s))
       +
       +
       +    def add_keypairs_from_KeyID(self, tx, keypairs, password):
       +        # first check the provided password
       +        seed = self.get_seed(password)
       +        for txin in tx.inputs:
       +            keyid = txin.get('KeyID')
       +            if keyid:
       +                m = re.match("old\(([0-9a-f]+),(\d+),(\d+)", keyid)
       +                if not m: continue
       +                mpk = m.group(1)
       +                if mpk != self.storage.get('master_public_key'): continue 
       +                for_change = int(m.group(2))
       +                num = int(m.group(3))
       +                account = self.accounts[0]
       +                addr = account.get_address(for_change, num)
       +                txin['address'] = addr # fixme: side effect
       +                pk = account.get_private_key(seed, (for_change, num))
       +                pubkey = public_key_from_private_key(pk)
       +                keypairs[pubkey] = pk
       +
       +
       +    def get_account_name(self, k):
       +        assert k == 0
       +        return 'Main account'
       +
       +
       +
       +
       +# former WalletFactory
       +class Wallet(object):
       +
       +    def __new__(self, storage):
       +        config = storage.config
       +        if config.get('bitkey', False):
       +            # if user requested support for Bitkey device,
       +            # import Bitkey driver
       +            from wallet_bitkey import WalletBitkey
       +            return WalletBitkey(config)
       +
       +        if not storage.file_exists:
       +            seed_version = NEW_SEED_VERSION if config.get('bip32') is True else OLD_SEED_VERSION
       +        else:
       +            seed_version = storage.get('seed_version')
       +
       +        if seed_version == OLD_SEED_VERSION:
       +            return OldWallet(storage)
       +        elif seed_version == NEW_SEED_VERSION:
       +            return NewWallet(storage)
       +        else:
       +            msg = "This wallet seed is not supported."
       +            if seed_version in [5]:
       +                msg += "\nTo open this wallet, try 'git checkout seed_v%d'"%seed_version
       +            print msg
       +            sys.exit(1)
       +
       +
       +
       +
       +    @classmethod
       +    def from_seed(self, seed, storage):
       +        import mnemonic
       +        if not seed:
       +            return 
       +
       +        words = seed.strip().split()
       +        try:
       +            mnemonic.mn_decode(words)
       +            uses_electrum_words = True
       +        except Exception:
       +            uses_electrum_words = False
       +
       +        try:
       +            seed.decode('hex')
       +            is_hex = True
       +        except Exception:
       +            is_hex = False
       +         
       +        if is_hex or (uses_electrum_words and len(words) != 13):
       +            print "old style wallet", len(words), words
       +            w = OldWallet(storage)
       +            w.init_seed(seed) #hex
       +        else:
       +            #assert is_seed(seed)
       +            w = Wallet(storage)
       +            w.init_seed(seed)
       +
       +
       +        return w
   DIR diff --git a/lib/wallet_factory.py b/lib/wallet_factory.py
       t@@ -1,11 +0,0 @@
       -class WalletFactory(object):
       -    def __new__(cls, config):
       -        if config.get('bitkey', False):
       -            # if user requested support for Bitkey device,
       -            # import Bitkey driver
       -            from wallet_bitkey import WalletBitkey
       -            return WalletBitkey(config)
       -        
       -        # Load standard wallet
       -        from wallet import Wallet
       -        return Wallet(config)
   DIR diff --git a/scripts/block_headers b/scripts/block_headers
       t@@ -5,7 +5,7 @@
        import time, electrum
        
        # 1. start the interface and wait for connection
       -interface = electrum.Interface('electrum.no-ip.org:50002:s')
       +interface = electrum.Interface('ecdsa.net:50002:s')
        interface.start(wait = True)
        if not interface.is_connected:
            print "not connected"
   DIR diff --git a/scripts/peers b/scripts/peers
       t@@ -1,17 +1,79 @@
        #!/usr/bin/env python
        
       -import time, electrum
       +import time, electrum, Queue
       +from electrum import Interface, SimpleConfig
       +from electrum.network import filter_protocol, parse_servers
       +from collections import defaultdict
        
       -electrum.set_verbosity(False)     # default is True
       -network = electrum.Network({'auto_cycle':True})
       -network.register_callback('peers',lambda: electrum.print_json(network.irc_servers.keys()))
       +# 1. start interface and wait for connection
       +interface = electrum.Interface('ecdsa.net:50002:s')
       +interface.start(wait = True)
       +if not interface.is_connected:
       +    print "not connected"
       +    exit()
        
       -if not network.start(wait=True):
       -    print "Not connected"
       -    exit(1)
       +# 2. get list of peers
       +q = Queue.Queue()
       +interface.send([('server.peers.subscribe',[])], lambda i,x: q.put(x))
       +r = q.get(timeout=10000)
       +peers = parse_servers(r.get('result'))
       +peers = filter_protocol(peers,'s')
        
       -print "Connected to", network.interface.server
       -while not network.irc_servers:
       -    time.sleep(1)
       +# start interfaces
       +config = SimpleConfig()
       +interfaces = map ( lambda server: Interface(server, config), peers )
       +results_queue = Queue.Queue()
       +reached_servers = []
       +for i in interfaces: i.start(q)
        
       +while peers:
       +    i = q.get(timeout=1)
       +    peers.remove(i.server)
       +    if i.is_connected:
       +        i.send([('blockchain.headers.subscribe',[])], lambda i,x: results_queue.put((i,x)))
       +        reached_servers.append(i.server)
       +
       +def analyze(results):
       +    out = {}
       +    dd = {}
       +    for k, v in results.items():
       +        height = v.get('block_height')
       +        merkle = v.get('merkle_root')
       +        utxo = v.get('utxo_root')
       +        d = dd.get(merkle, defaultdict(int))
       +        d[utxo] += 1
       +        dd[merkle] = d
       +
       +    refs = {}
       +    for merkle, d in dd.items():
       +        v = d.values()
       +        m = max(v)
       +        ref = d.keys()[v.index(m)]
       +        refs[merkle] = ref, m
       +
       +    for k, v in results.items():
       +        height = v.get('block_height')
       +        merkle = v.get('merkle_root')
       +        utxo = v.get('utxo_root')
       +        ref_utxo, num = refs.get(merkle)
       +
       +        if ref_utxo != utxo and num > 1:
       +            out[k] = height, merkle, utxo
       +
       +    return out
       +
       +
       +results = {}
       +while reached_servers:
       +    i, r = results_queue.get(timeout=10000)
       +    results[i.server] = r.get('result')
       +    reached_servers.remove(i.server)
       +
       +electrum.print_json(results)
       +out = analyze(results)
       +if out:
       +    print "faulty servers:"
       +    electrum.print_json(out)
       +else:
       +    print "ok"
        
   DIR diff --git a/scripts/servers b/scripts/servers
       t@@ -22,7 +22,7 @@ while servers:
            i = q.get(timeout=1000)
            servers.remove(i.server)
            if i.is_connected:
       -        i.send([('blockchain.numblocks.subscribe',[])], lambda i,x: results_queue.put((i,x)))
       +        i.send([('blockchain.headers.subscribe',[])], lambda i,x: results_queue.put((i,x)))
                reached_servers.append(i.server)
                i.status = "ok"
            else:
       t@@ -32,7 +32,7 @@ d = defaultdict(int)
        
        while reached_servers:
            i, r = results_queue.get(timeout=1000)
       -    i.blocks = r.get('result')
       +    i.blocks = r.get('result').get('block_height')
            d[i.blocks] += 1
            reached_servers.remove(i.server)