URI: 
       tmoved python modules and ssl certs to other directories to make root less cluttered - electrum-personal-server - Maximally lightweight electrum server for a single user
  HTML git clone https://git.parazyd.org/electrum-personal-server
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
   DIR commit c18d6c9b136a10dc94869e744bb2544bdce5a72e
   DIR parent 2c8cd9aa12ae7d5344e9ecf2c518a642f7a0da6d
  HTML Author: chris-belcher <chris-belcher@users.noreply.github.com>
       Date:   Tue, 27 Mar 2018 20:23:17 +0100
       
       moved python modules and ssl certs to other directories to make root less cluttered
       
       Diffstat:
         R cert.crt -> certs/cert.crt          |       0 
         R cert.key -> certs/cert.key          |       0 
         D deterministicwallet.py              |     429 -------------------------------
         A electrumpersonalserver/determinist… |     429 +++++++++++++++++++++++++++++++
         R hashes.py -> electrumpersonalserve… |       0 
         R jsonrpc.py -> electrumpersonalserv… |       0 
         A electrumpersonalserver/merkleproof… |     271 +++++++++++++++++++++++++++++++
         A electrumpersonalserver/transaction… |     723 +++++++++++++++++++++++++++++++
         D merkleproof.py                      |     270 -------------------------------
         M rescan-script.py                    |       2 +-
         M server.py                           |      31 ++++++++-----------------------
         D transactionmonitor.py               |     705 -------------------------------
       
       12 files changed, 1432 insertions(+), 1428 deletions(-)
       ---
   DIR diff --git a/cert.crt b/certs/cert.crt
   DIR diff --git a/cert.key b/certs/cert.key
   DIR diff --git a/deterministicwallet.py b/deterministicwallet.py
       t@@ -1,429 +0,0 @@
       -
       -import bitcoin as btc
       -from hashes import bh2u, hash_160, bfh, sha256
       -
       -# the class hierarchy for deterministic wallets in this file:
       -# subclasses are written towards the right
       -# each class knows how to create the scriptPubKeys of that wallet
       -#
       -#                                       |-- SingleSigOldMnemonicWallet
       -#                                       |-- SingleSigP2PKHWallet
       -#                                       |-- SingleSigP2WPKHWallet
       -#                     SingleSigWallet --|
       -#                    /                  |-- SingleSigP2WPKH_P2SHWallet
       -# DeterministicWallet
       -#                    \                 |-- MultisigP2SHWallet
       -#                     MultisigWallet --|
       -#                                      |-- MultisigP2WSHWallet
       -#                                      |-- MultisigP2WSH_P2SHWallet
       -
       -#the wallet types are here
       -#https://github.com/spesmilo/electrum/blob/3.0.6/RELEASE-NOTES
       -#and
       -#https://github.com/spesmilo/electrum-docs/blob/master/xpub_version_bytes.rst
       -
       -def is_string_parsable_as_hex_int(s):
       -    try:
       -        int(s, 16)
       -        return True
       -    except:
       -        return False
       -
       -def parse_electrum_master_public_key(keydata, gaplimit):
       -    if keydata[:4] in ("xpub", "tpub"):
       -        wallet = SingleSigP2PKHWallet(keydata)
       -    elif keydata[:4] in ("zpub", "vpub"):
       -        wallet = SingleSigP2WPKHWallet(keydata)
       -    elif keydata[:4] in ("ypub", "upub"):
       -        wallet = SingleSigP2WPKH_P2SHWallet(keydata)
       -    elif keydata.find(" ") != -1: #multiple keys = multisig
       -        chunks = keydata.split(" ")
       -        try:
       -            m = int(chunks[0])
       -        except ValueError:
       -            raise ValueError("Unable to parse m in multisig key data: "
       -                + chunks[0])
       -        pubkeys = chunks[1:]
       -        if not all([pubkeys[0][:4] == pub[:4] for pub in pubkeys[1:]]):
       -            raise ValueError("inconsistent bip32 pubkey types")
       -        if pubkeys[0][:4] in ("xpub", "tpub"):
       -            wallet = MultisigP2SHWallet(m, pubkeys)
       -        elif pubkeys[0][:4] in ("Zpub", "Vpub"):
       -            wallet = MultisigP2WSHWallet(m, pubkeys)
       -        elif pubkeys[0][:4] in ("Ypub", "Upub"):
       -            wallet = MultisigP2WSH_P2SHWallet(m, pubkeys)
       -    elif is_string_parsable_as_hex_int(keydata) and len(keydata) == 128:
       -        wallet = SingleSigOldMnemonicWallet(keydata)
       -    else:
       -        raise ValueError("Unrecognized electrum mpk format: " + keydata[:4])
       -    wallet.gaplimit = gaplimit
       -    return wallet
       -
       -class DeterministicWallet(object):
       -    def __init__(self):
       -        self.gaplimit = 0
       -        self.next_index = [0, 0]
       -        self.scriptpubkey_index = {}
       -
       -    def get_new_scriptpubkeys(self, change, count):
       -        """Returns newly-generated addresses from this deterministic wallet"""
       -        return self.get_scriptpubkeys(change, self.next_index[change],
       -            count)
       -
       -    def get_scriptpubkeys(self, change, from_index, count):
       -        """Returns addresses from this deterministic wallet"""
       -        pass
       -
       -    #called in check_for_new_txes() when a new tx of ours arrives
       -    #to see if we need to import more addresses
       -    def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys):
       -        """Return None if they havent, or how many addresses to
       -           import if they have"""
       -        result = {}
       -        for spk in scriptpubkeys:
       -            if spk not in self.scriptpubkey_index:
       -                continue
       -            change, index = self.scriptpubkey_index[spk]
       -            distance_from_next = self.next_index[change] - index
       -            if distance_from_next > self.gaplimit:
       -                continue
       -            #need to import more
       -            if change in result:
       -                result[change] = max(result[change], self.gaplimit
       -                    - distance_from_next + 1)
       -            else:
       -                result[change] = self.gaplimit - distance_from_next + 1
       -        if len(result) > 0:
       -            return result
       -        else:
       -            return None
       -
       -    def rewind_one(self, change):
       -        """Go back one pubkey in a branch"""
       -        self.next_index[change] -= 1
       -
       -class SingleSigWallet(DeterministicWallet):
       -    def __init__(self, mpk):
       -        super(SingleSigWallet, self).__init__()
       -        self.branches = (btc.bip32_ckd(mpk, 0), btc.bip32_ckd(mpk, 1))
       -        #m/change/i
       -
       -    def pubkey_to_scriptpubkey(self, pubkey):
       -        raise RuntimeError()
       -
       -    def get_pubkey(self, change, index):
       -        return btc.bip32_extract_key(btc.bip32_ckd(self.branches[change],
       -            index))
       -
       -    def get_scriptpubkeys(self, change, from_index, count):
       -        result = []
       -        for index in range(from_index, from_index + count):
       -            pubkey = self.get_pubkey(change, index)
       -            scriptpubkey = self.pubkey_to_scriptpubkey(pubkey)
       -            self.scriptpubkey_index[scriptpubkey] = (change, index)
       -            result.append(scriptpubkey)
       -        self.next_index[change] = max(self.next_index[change], from_index+count)
       -        return result
       -
       -class SingleSigP2PKHWallet(SingleSigWallet):
       -    def pubkey_to_scriptpubkey(self, pubkey):
       -        pkh = bh2u(hash_160(bfh(pubkey)))
       -        #op_dup op_hash_160 length hash160 op_equalverify op_checksig
       -        return "76a914" + pkh + "88ac"
       -
       -class SingleSigP2WPKHWallet(SingleSigWallet):
       -    def pubkey_to_scriptpubkey(self, pubkey):
       -        pkh = bh2u(hash_160(bfh(pubkey)))
       -        #witness-version length hash160
       -        #witness version is always 0, length is always 0x14
       -        return "0014" + pkh
       -
       -class SingleSigP2WPKH_P2SHWallet(SingleSigWallet):
       -    def pubkey_to_scriptpubkey(self, pubkey):
       -        #witness-version length pubkeyhash
       -        #witness version is always 0, length is always 0x14
       -        redeem_script = '0014' + bh2u(hash_160(bfh(pubkey)))
       -        sh = bh2u(hash_160(bfh(redeem_script)))
       -        return "a914" + sh + "87"
       -
       -class SingleSigOldMnemonicWallet(SingleSigWallet):
       -    def __init__(self, mpk):
       -        super(SingleSigWallet, self).__init__()
       -        self.mpk = mpk
       -
       -    def get_pubkey(self, change, index):
       -        return btc.electrum_pubkey(self.mpk, index, change)
       -
       -    def pubkey_to_scriptpubkey(self, pubkey):
       -        pkh = bh2u(hash_160(bfh(pubkey)))
       -        #op_dup op_hash_160 length hash160 op_equalverify op_checksig
       -        return "76a914" + pkh + "88ac"
       -
       -class MultisigWallet(DeterministicWallet):
       -    def __init__(self, m, mpk_list):
       -        super(MultisigWallet, self).__init__()
       -        self.m = m
       -        self.pubkey_branches = [(btc.bip32_ckd(mpk, 0), btc.bip32_ckd(mpk, 1))
       -            for mpk in mpk_list]
       -        #derivation path for pubkeys is m/change/index
       -
       -    def redeem_script_to_scriptpubkey(self, redeem_script):
       -        raise RuntimeError()
       -
       -    def get_scriptpubkeys(self, change, from_index, count):
       -        result = []
       -        for index in range(from_index, from_index + count):
       -            pubkeys = [btc.bip32_extract_key(btc.bip32_ckd(branch[change],
       -                index)) for branch in self.pubkey_branches]
       -            pubkeys = sorted(pubkeys)
       -            redeemScript = ""
       -            redeemScript += "%x"%(0x50 + self.m) #op_m
       -            for p in pubkeys:
       -                redeemScript += "21" #length
       -                redeemScript += p
       -            redeemScript += "%x"%(0x50 + len(pubkeys)) #op_n
       -            redeemScript += "ae" # op_checkmultisig
       -            scriptpubkey = self.redeem_script_to_scriptpubkey(redeemScript)
       -            self.scriptpubkey_index[scriptpubkey] = (change, index)
       -            result.append(scriptpubkey)
       -        self.next_index[change] = max(self.next_index[change], from_index+count)
       -        return result
       -
       -class MultisigP2SHWallet(MultisigWallet):
       -    def redeem_script_to_scriptpubkey(self, redeem_script):
       -        sh = bh2u(hash_160(bfh(redeem_script)))
       -        #op_hash160 length hash160 op_equal
       -        return "a914" + sh + "87"
       -
       -class MultisigP2WSHWallet(MultisigWallet):
       -    def redeem_script_to_scriptpubkey(self, redeem_script):
       -        sh = bh2u(sha256(bfh(redeem_script)))
       -        #witness-version length sha256
       -        #witness version is always 0, length is always 0x20
       -        return "0020" + sh
       -
       -class MultisigP2WSH_P2SHWallet(MultisigWallet):
       -    def redeem_script_to_scriptpubkey(self, redeem_script):
       -        #witness-version length sha256
       -        #witness version is always 0, length is always 0x20
       -        nested_redeemScript = "0020" + bh2u(sha256(bfh(redeem_script)))
       -        sh = bh2u(hash_160(bfh(nested_redeemScript)))
       -        #op_hash160 length hash160 op_equal
       -        return "a914" + sh + "87"
       -
       -# electrum has its own tests here
       -#https://github.com/spesmilo/electrum/blob/03b40a3c0a7dd84e76bc0d0ea2ad390dafc92250/lib/tests/test_wallet_vertical.py
       -
       -electrum_keydata_test_vectors = [
       -    #p2pkh wallet
       -    ("xpub661MyMwAqRbcGVQTLtBFzc3ENvyZHoUEhWRdGwoqLZaf5wXP9VcDY2VJV7usvsFLZz" +
       -    "2RUTVhCVXYXc3S8zpLyAFbDFcfrpUiwLoE9VWH2yz", #pubkey
       -    ["76a914b1847c763c9a9b12631ab42335751c1bf843880c88ac" #recv scriptpubkeys
       -    ,"76a914d8b6b932e892fad5132ea888111adac2171c5af588ac"
       -    ,"76a914e44b19ef74814f977ae4e2823dd0a0b33480472a88ac"],
       -    ["76a914d2c2905ca383a5b8f94818cb7903498061a6286688ac" #change scriptpubkeys
       -    ,"76a914e7b4ddb7cede132e84ba807defc092cf52e005b888ac"
       -    ,"76a91433bdb046a1d373728d7844df89aa24f788443a4588ac"])
       -    , #p2wpkh wallet
       -    ("zpub6mr7wBKy3oJn89TCiXUAPBWpTTTx58BgEjPLzDNf5kMThvd6xchrobPTsJ5mP" +
       -    "w3NJ7zRhckN8cv4FhQBfwurZzNE5uTW5C5PYqNTkRAnTkP", #pubkey
       -    ['00142b82c61a7a48b7b10801f0eb247af46821bd33f5' #recv scriptpubkeys
       -    ,'0014073dc6bcbb18d6468c5996bdeba926f6805b74b1'
       -    ,'001400fa0b5cb21e8d442a7bd61af3d558a62be0c9aa'],
       -    ['00144f4a0655a4b586be1e08d97a2f55125120b84c69' #change scriptpubkeys
       -    ,'0014ef7967a7a56c23bbc9f317e612c93a5e23d25ffe'
       -    ,'0014ad768a11730bf54d10c72184d53239de0f310bc9'])
       -    ,#p2sh 2of2 multisig wallet
       -    ("2 tpubD6NzVbkrYhZ4YVMVzC7wZeRfz3bhqcHvV8M3UiULCfzFtLtp5nwvi6LnBQegrkx" +
       -    "YGPkSzXUEvcPEHcKdda8W1YShVBkhFBGkLxjSQ1Nx3cJ tpubD6NzVbkrYhZ4WjgNYq2nF" +
       -    "TbiSLW2SZAzs4g5JHLqwQ3AmR3tCWpqsZJJEoZuP5HAEBNxgYQhtWMezszoaeTCg6FWGQB" +
       -    "T74sszGaxaf64o5s", #m=2, 2 pubkeys, n=len(pubkeys)
       -    ['a914fe30a46a4e1b41f9bb758448fd84ee4628c103e187' #recv
       -    ,'a914dad5dd605871560ae5d219cd6275e6ad19bc6b9987'
       -    ,'a914471e158e2db190acdd8c76ed6d2ade102fe1e8ac87'
       -    ,'a914013449715a32f21d1a8a2b95a01b40eb41ada16f87'
       -    ,'a914ae3dd25567fb7c2f87be41220dd14025ca68b0e087'
       -    ,'a91462b90344947b610c4eadb7dd460fee3f32fefe7687'
       -    ,'a914d4388c7d5771ebf26b6e650c42e60e4cf7d4c5a187'
       -    ,'a914e4f0832e56591d01b71c72b9a3777dc8f9d9a92e87'
       -    ,'a914a5d5accd96d27403c7663b92fdb57299d7a871eb87'
       -    ,'a914f8f2c6ef2d80f972e4d8b418a15337a3c38af37f87'
       -    ,'a914a2bd2f67fac7c24e609b574ccc8cfaa2f90ebf8c87'
       -    ,'a914a56298a7decde1d18306f55d9305577c3fce690187'
       -    ,'a91430f2f83238ac29125a539055fa59efc86a73a23987'
       -    ,'a914263b4585d0735c5065987922af359d5eabeb880d87'
       -    ,'a91455d9d47113fb8b37705bdf6d4107d438afd63e4687'
       -    ,'a914970d754163b8957b73f4e8baaf23dea5f6e3db2287'
       -    ,'a914facbc921203a9ffd751cc246a884918beaac21b687'
       -    ,'a914fc7556833eca1e0f84c6d7acb875e645f7ed4e9687'
       -    ,'a914bbfe6a032d633f113b5d605e3a97cc08a47cc87d87'
       -    ,'a91403d733c4ca337b5fa1de95970ba6f898a9d36c4887'
       -    ,'a9148af27dc7c950e17c11e164065e672cd60ae3d48d87'
       -    ,'a914c026aa45377f2a4a62136bac1d3350c318fee5c587'
       -    ,'a9146337f59e3ea55e73725c9f2fc52a5ca5d68c361687'],
       -    ['a914aeaebf9d567ab8a6813e89668e16f40bf419408e87' #change
       -    ,'a914f2a6264dd3975297fa2a5a8e17321299a44f76d987'
       -    ,'a9142067a6c47958090a645137cc0898c0c7bbc69b5387'
       -    ,'a914210840f77ea5b7eb11cb55e5d719a93b7746fb9387'
       -    ,'a914163db6b8ca00362be63a26502c5f7bf64787506b87'
       -    ,'a91479b2c527594059c056e5367965ae92bbcf63512187'])
       -    ,#p2sh 2of3 multisig wallet
       -    ("2 tpubD6NzVbkrYhZ4WwaMJ3od4hANxdMVpb63Du3ERq1xjtowxVJEcTbGH2rFd9TFXxw" +
       -    "KJRKDn9vQjDPxFeaku6BHW6wHn2KPF1ijS4LwgwQFJ3B tpubD6NzVbkrYhZ4Wjv4ZRPD6" +
       -    "MNdiLmfvXztbKuuatkqHjukU3S6GXhmKnbAF5eU9bR2Nryiq8v67emUUSM1VUrAx5wcZ19" +
       -    "AsaGg3ZLmjbbwLXr tpubD6NzVbkrYhZ4Xxa2fEp7YsbnFnwuQNaogijbiX42Deqd4NiAD" +
       -    "tqNU6AXCU2d2kPFWBpAGG7K3HAKYwUfZBPgTLkfQp2dDg9SLVnkgYPgEXN",
       -    ['a914167c95beb25b984ace517d4346e6cdbf1381793687', #recv addrs
       -     'a914378bbda1ba7a713de18c3ba3c366f42212bfb45087',
       -     'a9142a5c9881c70906180f37dd02d8c830e9b6328d4a87',
       -     'a914ffe0832375b72ee5307bfa502896ba28cc470ee987',
       -     'a9147607d40e039fbea57d9c04e48b198c9fcf3356c187',
       -     'a9148d9582ad4cf0581c6e0697e4cba6a12e66ca1a0087',
       -     'a914d153a743b315ba19690823119019e16e3762104d87',
       -     'a914b4accc89e48610043e70371153fd8cb5a3eef34287',
       -     'a91406febca615e3631253fd75a1d819436e1d046e0487',
       -     'a914b863cbb888c6b28291cb87a2390539e28be37a9587',
       -     'a914ec39094e393184d2c352a29b9d7a3caddaccb6cf87',
       -     'a914da4faa4babbdf611caf511d287133f06c1c3244a87',
       -     'a9146e64561d0c5e2e9159ecff65db02e04b3277402487',
       -     'a914377d66386972492192ae827fb2208596af0941d187',
       -     'a914448d364ff2374449e57df13db33a40f5b099997c87',
       -     'a914f24b875d2cb99e0b138ab0e6dd65027932b3c6e787',
       -     'a914aa4bcee53406b1ef6c83852e3844e38a3a9d9f3087',
       -     'a9145e5ec40fdab54be0d6e21107bc38c39df97e37fc87',
       -     'a9141de4d402c82f4e9b0e6b792b331232a5405ebd3f87',
       -     'a9148873ee280e51f9c64d257dd6dedc8712fd652cc687'],
       -    ['a9142cc87d7562a85029a57cc37026e12dab72223db287', #change
       -     'a91499f4aee0b274f0b3ab48549a2c58cd667a62c0cb87',
       -     'a91497a89cd5ada3a766a1275f8151e9256fcf537f6c87',
       -     'a9147ffc9f3a3b60635ea1783243274f4d07ab617cb487',
       -     'a9143423113ab913d86fd47e55488a0c559e18b457b987',
       -     'a914a28a3773a37c52ff6fd7dff497d0eaf80a46febb87'])
       -    , #p2wsh 1of2 multisig wallet
       -    ("1 Vpub5fAqpSRkLmvXwqbuR61MaKMSwj5z5xUBwanaz3qnJ5MgaBDpFSLUvKTiNK9zHp" +
       -    "dvrg2LHHXkKxSXBHNWNpZz9b1VqADjmcCs3arSoxN3F3r Vpub5fvEo4MUpbVs9sZqr45" +
       -    "zmRVEsTcQ49MA9m3MLht3XzdZvS9eMXLLu1H6TL1j2SMnykHqXNzG5ycMyQmFDvEE5B32" +
       -    "sP8TmRe6wW8HjBgMssh",
       -    #recv scriptpubkeys
       -    ['002031fbaa839e96fc1abaf3453b9f770e0ccfe2d8e3e990bb381fdcb7db4722986a',
       -     '0020820ae739b36f4feb1c299ced201db383bbcf1634e0071e489b385f43c2323761',
       -     '0020eff05f4d14aa1968a7142b1009aa57a6208fb01b212f8b8f7df63645d26a1292',
       -     '002049c6e17979dca380ffb66295d27f609bea2879d4f0b590c96c70ff12260a8721',
       -     '002002bf2430fc7ebc6fb27da1cb80e52702edcc62a29f65c997e5c924dcd98411bd',
       -     '0020c7a58dcf9633453ba12860b57c14af67d87d022be5c52bf6be7a6abdc295c6e0',
       -     '0020136696059a5e932c72f4f0a05fa7f52faf9b54f1b7694e15acce710e6cc9e89d',
       -     '0020c372e880227f35c2ee35d0724bf05cea95e74dcb3e6aa67ff15f561a29c0645d',
       -     '002095c705590e2b84996fa44bff64179b26669e53bbd58d76bb6bbb5c5498a981ce',
       -     '00207217754dae083c3c365c7e1ce3ad889ca2bd88e4f809cec66b9987adc390aa26',
       -     '0020bee30906450e099357cc96a1f472c1ef70089cd4a0cba96749adfe1c9a2f9e87',
       -     '0020b1838b3d5a386ad6c90eeae9a27a9b812e32ce06376f261dea89e405bc8209d9',
       -     '0020231a3d05886efff601f0702d4c8450dfcce8d6a4bd90f17f7ff76f5c25c632de',
       -     '002071220f3941b5f65aca90e464db4291cd5ea63f37fa858fd5b66d5019f0dbab0f',
       -     '0020fc3c7db9f0e773f9f9c725d4286ddcc88db9575c45b2441d458018150eb4ef10',
       -     '00209f037bfc98dee2fc0d3cca54df09b2d20e92a0133fa381a4dd74c49e4d0a89f5',
       -     '0020c9060d0554ba2ca92048e1772e806d796ba41f10bf6aee2653a9eba96b05c944',
       -     '0020a7cb1dd2730dba564f414ed8d9312370ff89c34df1441b83125cb4d97a96005a',
       -     '00209fddc9b4e070b887dec034ed74f15f62d075a3ac8cf6eb95a88c635e0207534c',
       -     '0020c48f9c50958ab8e386a8bd3888076f31d12e5cf011ff46cc83c6fadfe6d47d20',
       -     '0020a659f4621dca404571917e73dedb26b6d7c49a07dacbf15890760ac0583d3267'],
       -    #change scriptpubkeys
       -    ['002030213b5d3b6988b86aa13a9eaca08e718d51f32dc130c70981abb0102173c791',
       -     '002027bd198f9783a58e9bc4d3fdbd1c75cc74154905cce1d23c7bd3e051695418fe',
       -     '0020c1fd2cdebf120d3b1dc990dfdaca62382ff9525beeb6a79a908ddecb40e2162c',
       -     '00207a3e478266e5fe49fe22e3d8f04d3adda3b6a0835806a0db1f77b84d0ba7f79c',
       -     '002059e66462023ecd54e20d4dce286795e7d5823af511989736edc0c7a844e249f5',
       -     '0020bd8077906dd367d6d107d960397e46db2daba5793249f1f032d8d7e12e6f193c'])
       -    , #p2wpkh-p2sh
       -    ("upub5E4QEumGPNTmSKD95TrYX2xqLwwvBULbRzzHkrpW9WKKCB1y9DEfPXDnUyQjLjmVs" +
       -    "7gSd7k5vRb1FoSb6BjyiWNg4arkJLaqk1jULzbwA5q",
       -    ["a914ae8f84a06668742f713d0743c1f54d248040e63387", #recv
       -     "a914c2e9bdcc48596b8cce418042ade72198fddf3cd987",
       -     "a914a44b6ad63ccef0ae1741eaccee99bf2fa83f842987",
       -     "a9148cf1c891d96a0be07893d0bddcf00ed5dad2c46e87",
       -     "a91414d677b32f2409f4dfb3073d382c302bcd6ed33587",
       -     "a9141b284bee7198d5134512f37ef60e4048864b4bd687"],
       -    ["a914a5aacff65860440893107b01912dc8f60cadab2b87", #change
       -     "a914dcd74ebc8bfc5cf0535717a3e833592d54b3c48687",
       -     "a91446793cae4c2b8149ade61c1627b96b90599bc08787",
       -     "a91439f3776831f321125bdb5099fbbd654923f8316c87"])
       -    , #p2wpkh-p2sh
       -    ("ypub6XrRLtXNB7NQo3vDaMNnffXVJe1WVaebXcb4ncpTHHADLuFYmf2CcPn96YzUbMt8s" +
       -    "HSMmtr1mCcMgCBLqNdY2hrXXcdiLxCdD9e2dChBLun",
       -    ["a91429c2ad045bbb162ef3c2d9cacb9812bec463061787", #recv
       -     "a91433ec6bb67b113978d9cfd307a97fd15bc0a5a62087",
       -     "a91450523020275ccbf4e916a0d8523ae42391ad988a87",
       -     "a91438c2e5e76a874d86cfc914fe9fc1868b6afb5c5487"],
       -    ["a91475f608698bb735120a17699fee854bce9a8dc8d387",
       -     "a91477e69344ef53587051c85a06a52a646457b44e6c87",
       -     "a914607c98ea34fbdffe39fee161ae2ffd5517bf1a5587"])
       -    , #old mnemonic mpk
       -    ("e9d4b7866dd1e91c862aebf62a49548c7dbf7bcc6e4b7b8c9da820c7737968df9c09d" +
       -    "5a3e271dc814a29981f81b3faaf2737b551ef5dcc6189cf0f8252c442b3",
       -    ["76a9149cd3dfb0d87a861770ae4e268e74b45335cf00ab88ac", #recv
       -     "76a914c30f2af6a79296b6531bf34dba14c8419be8fb7d88ac",
       -     "76a9145eb4eeaefcf9a709f8671444933243fbd05366a388ac",
       -     "76a914f96669095e6df76cfdf5c7e49a1909f002e123d088ac"],
       -    ["76a914ca14915184a2662b5d1505ce7142c8ca066c70e288ac", #change
       -     "76a9148942ac692ace81019176c4fb0ac408b18b49237f88ac",
       -     "76a914e1232622a96a04f5e5a24ca0792bb9c28b089d6e88ac"])
       -    , #p2wsh-p2sh 2of2 multisig
       -    ("2 Ypub6hWbqA2p47QgsLt5J4nxrR3ngu8xsPGb7PdV8CDh48KyNngNqPKSqertAqYhQ4u" +
       -    "mELu1UsZUCYfj9XPA6AdSMZWDZQobwF7EJ8uNrECaZg1 Ypub6iNDhL4WWq5kFZcdFqHHw" +
       -    "X4YTH4rYGp8xbndpRrY7WNZFFRfogSrL7wRTajmVHgR46AT1cqUG1mrcRd7h1WXwBsgX2Q" +
       -    "vT3zFbBCDiSDLkau",
       -    ["a91428060ade179c792fac07fc8817fd150ce7cdd3f987", #recv
       -     "a9145ba5ed441b9f3e22f71193d4043b645183e6aeee87",
       -     "a91484cc1f317b7d5afff115916f1e27319919601d0187",
       -     "a9144001695a154cac4d118af889d3fdcaf929af315787",
       -     "a914897888f3152a27cbd7611faf6aa01085931e542a87"],
       -    ["a91454dbb52de65795d144f3c4faeba0e37d9765c85687", #change
       -     "a914f725cbd61c67f34ed40355f243b5bb0650ce61c587",
       -     "a9143672bcd3d02d3ea7c3205ddbc825028a0d2a781987"])
       -]
       -
       -electrum_bad_keydata_test_vectors = [
       -    "zpub661MyMwAqRbcGVQTLtBFzc3ENvyZHoUEhWRdGwoqLZaf5wXP9VcDY2VJV7usvsFLZz" +
       -    "2RUTVhCVXYXc3S8zpLyAFbDFcfrpUiwLoE9VWH2yz", #bad checksum
       -    "a tpubD6NzVbkrYhZ4YVMVzC7wZeRfz3bhqcHvV8M3UiULCfzFtLtp5nwvi6LnBQegrkx" +
       -    "YGPkSzXUEvcPEHcKdda8W1YShVBkhFBGkLxjSQ1Nx3cJ tpubD6NzVbkrYhZ4WjgNYq2nF" +
       -    "TbiSLW2SZAzs4g5JHLqwQ3AmR3tCWpqsZJJEoZuP5HAEBNxgYQhtWMezszoaeTCg6FWGQB" +
       -    "T74sszGaxaf64o5s", #unparsable m number
       -    "2 tpubD6NzVbkrYhZ4YVMVzC7wZeRfz3bhqcHvV8M3UiULCfzFtLtp5nwvi6LnBQegrkx" +
       -    "YGPkSzXUEvcPEHcKdda8W1YShVBkhFBGkLxjSQ1Nx3cJ Vpub5fAqpSRkLmvXwqbuR61M" +
       -    "aKMSwj5z5xUBwanaz3qnJ5MgaBDpFSLUvKTiNK9zHpdvrg2LHHXkKxSXBHNWNpZz9b1Vq" +
       -    "ADjmcCs3arSoxN3F3r" #inconsistent magic
       -]
       -
       -def test():
       -    for keydata, recv_spks, change_spks in electrum_keydata_test_vectors:
       -        initial_count = 15
       -        gaplimit = 5
       -        wal = parse_electrum_master_public_key(keydata, gaplimit)
       -        spks = wal.get_scriptpubkeys(0, 0, initial_count)
       -        #for test, generate 15, check that the last 5 lead to gap limit overrun
       -        for i in range(initial_count - gaplimit):
       -            ret = wal.have_scriptpubkeys_overrun_gaplimit([spks[i]])
       -            assert ret == None
       -        for i in range(gaplimit):
       -            index = i + initial_count - gaplimit
       -            ret = wal.have_scriptpubkeys_overrun_gaplimit([spks[index]])
       -            assert ret != None and ret[0] == i+1
       -        last_index_add = 3
       -        last_index = initial_count - gaplimit + last_index_add
       -        ret = wal.have_scriptpubkeys_overrun_gaplimit(spks[2:last_index])
       -        assert ret[0] == last_index_add
       -        assert wal.get_scriptpubkeys(0, 0, len(recv_spks)) == recv_spks
       -        assert wal.get_scriptpubkeys(1, 0, len(change_spks)) == change_spks
       -    for keydata in electrum_bad_keydata_test_vectors:
       -        try:
       -            parse_electrum_master_public_key(keydata, 5)
       -            raised_error = False
       -        except (ValueError, Exception):
       -            raised_error = True
       -        assert raised_error
       -    print("All tests passed successfully")
       -
       -if __name__ == "__main__":
       -    test()
       -    pass
       -
   DIR diff --git a/electrumpersonalserver/deterministicwallet.py b/electrumpersonalserver/deterministicwallet.py
       t@@ -0,0 +1,429 @@
       +
       +import bitcoin as btc
       +from electrumpersonalserver.hashes import bh2u, hash_160, bfh, sha256
       +
       +# the class hierarchy for deterministic wallets in this file:
       +# subclasses are written towards the right
       +# each class knows how to create the scriptPubKeys of that wallet
       +#
       +#                                       |-- SingleSigOldMnemonicWallet
       +#                                       |-- SingleSigP2PKHWallet
       +#                                       |-- SingleSigP2WPKHWallet
       +#                     SingleSigWallet --|
       +#                    /                  |-- SingleSigP2WPKH_P2SHWallet
       +# DeterministicWallet
       +#                    \                 |-- MultisigP2SHWallet
       +#                     MultisigWallet --|
       +#                                      |-- MultisigP2WSHWallet
       +#                                      |-- MultisigP2WSH_P2SHWallet
       +
       +#the wallet types are here
       +#https://github.com/spesmilo/electrum/blob/3.0.6/RELEASE-NOTES
       +#and
       +#https://github.com/spesmilo/electrum-docs/blob/master/xpub_version_bytes.rst
       +
       +def is_string_parsable_as_hex_int(s):
       +    try:
       +        int(s, 16)
       +        return True
       +    except:
       +        return False
       +
       +def parse_electrum_master_public_key(keydata, gaplimit):
       +    if keydata[:4] in ("xpub", "tpub"):
       +        wallet = SingleSigP2PKHWallet(keydata)
       +    elif keydata[:4] in ("zpub", "vpub"):
       +        wallet = SingleSigP2WPKHWallet(keydata)
       +    elif keydata[:4] in ("ypub", "upub"):
       +        wallet = SingleSigP2WPKH_P2SHWallet(keydata)
       +    elif keydata.find(" ") != -1: #multiple keys = multisig
       +        chunks = keydata.split(" ")
       +        try:
       +            m = int(chunks[0])
       +        except ValueError:
       +            raise ValueError("Unable to parse m in multisig key data: "
       +                + chunks[0])
       +        pubkeys = chunks[1:]
       +        if not all([pubkeys[0][:4] == pub[:4] for pub in pubkeys[1:]]):
       +            raise ValueError("inconsistent bip32 pubkey types")
       +        if pubkeys[0][:4] in ("xpub", "tpub"):
       +            wallet = MultisigP2SHWallet(m, pubkeys)
       +        elif pubkeys[0][:4] in ("Zpub", "Vpub"):
       +            wallet = MultisigP2WSHWallet(m, pubkeys)
       +        elif pubkeys[0][:4] in ("Ypub", "Upub"):
       +            wallet = MultisigP2WSH_P2SHWallet(m, pubkeys)
       +    elif is_string_parsable_as_hex_int(keydata) and len(keydata) == 128:
       +        wallet = SingleSigOldMnemonicWallet(keydata)
       +    else:
       +        raise ValueError("Unrecognized electrum mpk format: " + keydata[:4])
       +    wallet.gaplimit = gaplimit
       +    return wallet
       +
       +class DeterministicWallet(object):
       +    def __init__(self):
       +        self.gaplimit = 0
       +        self.next_index = [0, 0]
       +        self.scriptpubkey_index = {}
       +
       +    def get_new_scriptpubkeys(self, change, count):
       +        """Returns newly-generated addresses from this deterministic wallet"""
       +        return self.get_scriptpubkeys(change, self.next_index[change],
       +            count)
       +
       +    def get_scriptpubkeys(self, change, from_index, count):
       +        """Returns addresses from this deterministic wallet"""
       +        pass
       +
       +    #called in check_for_new_txes() when a new tx of ours arrives
       +    #to see if we need to import more addresses
       +    def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys):
       +        """Return None if they havent, or how many addresses to
       +           import if they have"""
       +        result = {}
       +        for spk in scriptpubkeys:
       +            if spk not in self.scriptpubkey_index:
       +                continue
       +            change, index = self.scriptpubkey_index[spk]
       +            distance_from_next = self.next_index[change] - index
       +            if distance_from_next > self.gaplimit:
       +                continue
       +            #need to import more
       +            if change in result:
       +                result[change] = max(result[change], self.gaplimit
       +                    - distance_from_next + 1)
       +            else:
       +                result[change] = self.gaplimit - distance_from_next + 1
       +        if len(result) > 0:
       +            return result
       +        else:
       +            return None
       +
       +    def rewind_one(self, change):
       +        """Go back one pubkey in a branch"""
       +        self.next_index[change] -= 1
       +
       +class SingleSigWallet(DeterministicWallet):
       +    def __init__(self, mpk):
       +        super(SingleSigWallet, self).__init__()
       +        self.branches = (btc.bip32_ckd(mpk, 0), btc.bip32_ckd(mpk, 1))
       +        #m/change/i
       +
       +    def pubkey_to_scriptpubkey(self, pubkey):
       +        raise RuntimeError()
       +
       +    def get_pubkey(self, change, index):
       +        return btc.bip32_extract_key(btc.bip32_ckd(self.branches[change],
       +            index))
       +
       +    def get_scriptpubkeys(self, change, from_index, count):
       +        result = []
       +        for index in range(from_index, from_index + count):
       +            pubkey = self.get_pubkey(change, index)
       +            scriptpubkey = self.pubkey_to_scriptpubkey(pubkey)
       +            self.scriptpubkey_index[scriptpubkey] = (change, index)
       +            result.append(scriptpubkey)
       +        self.next_index[change] = max(self.next_index[change], from_index+count)
       +        return result
       +
       +class SingleSigP2PKHWallet(SingleSigWallet):
       +    def pubkey_to_scriptpubkey(self, pubkey):
       +        pkh = bh2u(hash_160(bfh(pubkey)))
       +        #op_dup op_hash_160 length hash160 op_equalverify op_checksig
       +        return "76a914" + pkh + "88ac"
       +
       +class SingleSigP2WPKHWallet(SingleSigWallet):
       +    def pubkey_to_scriptpubkey(self, pubkey):
       +        pkh = bh2u(hash_160(bfh(pubkey)))
       +        #witness-version length hash160
       +        #witness version is always 0, length is always 0x14
       +        return "0014" + pkh
       +
       +class SingleSigP2WPKH_P2SHWallet(SingleSigWallet):
       +    def pubkey_to_scriptpubkey(self, pubkey):
       +        #witness-version length pubkeyhash
       +        #witness version is always 0, length is always 0x14
       +        redeem_script = '0014' + bh2u(hash_160(bfh(pubkey)))
       +        sh = bh2u(hash_160(bfh(redeem_script)))
       +        return "a914" + sh + "87"
       +
       +class SingleSigOldMnemonicWallet(SingleSigWallet):
       +    def __init__(self, mpk):
       +        super(SingleSigWallet, self).__init__()
       +        self.mpk = mpk
       +
       +    def get_pubkey(self, change, index):
       +        return btc.electrum_pubkey(self.mpk, index, change)
       +
       +    def pubkey_to_scriptpubkey(self, pubkey):
       +        pkh = bh2u(hash_160(bfh(pubkey)))
       +        #op_dup op_hash_160 length hash160 op_equalverify op_checksig
       +        return "76a914" + pkh + "88ac"
       +
       +class MultisigWallet(DeterministicWallet):
       +    def __init__(self, m, mpk_list):
       +        super(MultisigWallet, self).__init__()
       +        self.m = m
       +        self.pubkey_branches = [(btc.bip32_ckd(mpk, 0), btc.bip32_ckd(mpk, 1))
       +            for mpk in mpk_list]
       +        #derivation path for pubkeys is m/change/index
       +
       +    def redeem_script_to_scriptpubkey(self, redeem_script):
       +        raise RuntimeError()
       +
       +    def get_scriptpubkeys(self, change, from_index, count):
       +        result = []
       +        for index in range(from_index, from_index + count):
       +            pubkeys = [btc.bip32_extract_key(btc.bip32_ckd(branch[change],
       +                index)) for branch in self.pubkey_branches]
       +            pubkeys = sorted(pubkeys)
       +            redeemScript = ""
       +            redeemScript += "%x"%(0x50 + self.m) #op_m
       +            for p in pubkeys:
       +                redeemScript += "21" #length
       +                redeemScript += p
       +            redeemScript += "%x"%(0x50 + len(pubkeys)) #op_n
       +            redeemScript += "ae" # op_checkmultisig
       +            scriptpubkey = self.redeem_script_to_scriptpubkey(redeemScript)
       +            self.scriptpubkey_index[scriptpubkey] = (change, index)
       +            result.append(scriptpubkey)
       +        self.next_index[change] = max(self.next_index[change], from_index+count)
       +        return result
       +
       +class MultisigP2SHWallet(MultisigWallet):
       +    def redeem_script_to_scriptpubkey(self, redeem_script):
       +        sh = bh2u(hash_160(bfh(redeem_script)))
       +        #op_hash160 length hash160 op_equal
       +        return "a914" + sh + "87"
       +
       +class MultisigP2WSHWallet(MultisigWallet):
       +    def redeem_script_to_scriptpubkey(self, redeem_script):
       +        sh = bh2u(sha256(bfh(redeem_script)))
       +        #witness-version length sha256
       +        #witness version is always 0, length is always 0x20
       +        return "0020" + sh
       +
       +class MultisigP2WSH_P2SHWallet(MultisigWallet):
       +    def redeem_script_to_scriptpubkey(self, redeem_script):
       +        #witness-version length sha256
       +        #witness version is always 0, length is always 0x20
       +        nested_redeemScript = "0020" + bh2u(sha256(bfh(redeem_script)))
       +        sh = bh2u(hash_160(bfh(nested_redeemScript)))
       +        #op_hash160 length hash160 op_equal
       +        return "a914" + sh + "87"
       +
       +# electrum has its own tests here
       +#https://github.com/spesmilo/electrum/blob/03b40a3c0a7dd84e76bc0d0ea2ad390dafc92250/lib/tests/test_wallet_vertical.py
       +
       +electrum_keydata_test_vectors = [
       +    #p2pkh wallet
       +    ("xpub661MyMwAqRbcGVQTLtBFzc3ENvyZHoUEhWRdGwoqLZaf5wXP9VcDY2VJV7usvsFLZz" +
       +    "2RUTVhCVXYXc3S8zpLyAFbDFcfrpUiwLoE9VWH2yz", #pubkey
       +    ["76a914b1847c763c9a9b12631ab42335751c1bf843880c88ac" #recv scriptpubkeys
       +    ,"76a914d8b6b932e892fad5132ea888111adac2171c5af588ac"
       +    ,"76a914e44b19ef74814f977ae4e2823dd0a0b33480472a88ac"],
       +    ["76a914d2c2905ca383a5b8f94818cb7903498061a6286688ac" #change scriptpubkeys
       +    ,"76a914e7b4ddb7cede132e84ba807defc092cf52e005b888ac"
       +    ,"76a91433bdb046a1d373728d7844df89aa24f788443a4588ac"])
       +    , #p2wpkh wallet
       +    ("zpub6mr7wBKy3oJn89TCiXUAPBWpTTTx58BgEjPLzDNf5kMThvd6xchrobPTsJ5mP" +
       +    "w3NJ7zRhckN8cv4FhQBfwurZzNE5uTW5C5PYqNTkRAnTkP", #pubkey
       +    ['00142b82c61a7a48b7b10801f0eb247af46821bd33f5' #recv scriptpubkeys
       +    ,'0014073dc6bcbb18d6468c5996bdeba926f6805b74b1'
       +    ,'001400fa0b5cb21e8d442a7bd61af3d558a62be0c9aa'],
       +    ['00144f4a0655a4b586be1e08d97a2f55125120b84c69' #change scriptpubkeys
       +    ,'0014ef7967a7a56c23bbc9f317e612c93a5e23d25ffe'
       +    ,'0014ad768a11730bf54d10c72184d53239de0f310bc9'])
       +    ,#p2sh 2of2 multisig wallet
       +    ("2 tpubD6NzVbkrYhZ4YVMVzC7wZeRfz3bhqcHvV8M3UiULCfzFtLtp5nwvi6LnBQegrkx" +
       +    "YGPkSzXUEvcPEHcKdda8W1YShVBkhFBGkLxjSQ1Nx3cJ tpubD6NzVbkrYhZ4WjgNYq2nF" +
       +    "TbiSLW2SZAzs4g5JHLqwQ3AmR3tCWpqsZJJEoZuP5HAEBNxgYQhtWMezszoaeTCg6FWGQB" +
       +    "T74sszGaxaf64o5s", #m=2, 2 pubkeys, n=len(pubkeys)
       +    ['a914fe30a46a4e1b41f9bb758448fd84ee4628c103e187' #recv
       +    ,'a914dad5dd605871560ae5d219cd6275e6ad19bc6b9987'
       +    ,'a914471e158e2db190acdd8c76ed6d2ade102fe1e8ac87'
       +    ,'a914013449715a32f21d1a8a2b95a01b40eb41ada16f87'
       +    ,'a914ae3dd25567fb7c2f87be41220dd14025ca68b0e087'
       +    ,'a91462b90344947b610c4eadb7dd460fee3f32fefe7687'
       +    ,'a914d4388c7d5771ebf26b6e650c42e60e4cf7d4c5a187'
       +    ,'a914e4f0832e56591d01b71c72b9a3777dc8f9d9a92e87'
       +    ,'a914a5d5accd96d27403c7663b92fdb57299d7a871eb87'
       +    ,'a914f8f2c6ef2d80f972e4d8b418a15337a3c38af37f87'
       +    ,'a914a2bd2f67fac7c24e609b574ccc8cfaa2f90ebf8c87'
       +    ,'a914a56298a7decde1d18306f55d9305577c3fce690187'
       +    ,'a91430f2f83238ac29125a539055fa59efc86a73a23987'
       +    ,'a914263b4585d0735c5065987922af359d5eabeb880d87'
       +    ,'a91455d9d47113fb8b37705bdf6d4107d438afd63e4687'
       +    ,'a914970d754163b8957b73f4e8baaf23dea5f6e3db2287'
       +    ,'a914facbc921203a9ffd751cc246a884918beaac21b687'
       +    ,'a914fc7556833eca1e0f84c6d7acb875e645f7ed4e9687'
       +    ,'a914bbfe6a032d633f113b5d605e3a97cc08a47cc87d87'
       +    ,'a91403d733c4ca337b5fa1de95970ba6f898a9d36c4887'
       +    ,'a9148af27dc7c950e17c11e164065e672cd60ae3d48d87'
       +    ,'a914c026aa45377f2a4a62136bac1d3350c318fee5c587'
       +    ,'a9146337f59e3ea55e73725c9f2fc52a5ca5d68c361687'],
       +    ['a914aeaebf9d567ab8a6813e89668e16f40bf419408e87' #change
       +    ,'a914f2a6264dd3975297fa2a5a8e17321299a44f76d987'
       +    ,'a9142067a6c47958090a645137cc0898c0c7bbc69b5387'
       +    ,'a914210840f77ea5b7eb11cb55e5d719a93b7746fb9387'
       +    ,'a914163db6b8ca00362be63a26502c5f7bf64787506b87'
       +    ,'a91479b2c527594059c056e5367965ae92bbcf63512187'])
       +    ,#p2sh 2of3 multisig wallet
       +    ("2 tpubD6NzVbkrYhZ4WwaMJ3od4hANxdMVpb63Du3ERq1xjtowxVJEcTbGH2rFd9TFXxw" +
       +    "KJRKDn9vQjDPxFeaku6BHW6wHn2KPF1ijS4LwgwQFJ3B tpubD6NzVbkrYhZ4Wjv4ZRPD6" +
       +    "MNdiLmfvXztbKuuatkqHjukU3S6GXhmKnbAF5eU9bR2Nryiq8v67emUUSM1VUrAx5wcZ19" +
       +    "AsaGg3ZLmjbbwLXr tpubD6NzVbkrYhZ4Xxa2fEp7YsbnFnwuQNaogijbiX42Deqd4NiAD" +
       +    "tqNU6AXCU2d2kPFWBpAGG7K3HAKYwUfZBPgTLkfQp2dDg9SLVnkgYPgEXN",
       +    ['a914167c95beb25b984ace517d4346e6cdbf1381793687', #recv addrs
       +     'a914378bbda1ba7a713de18c3ba3c366f42212bfb45087',
       +     'a9142a5c9881c70906180f37dd02d8c830e9b6328d4a87',
       +     'a914ffe0832375b72ee5307bfa502896ba28cc470ee987',
       +     'a9147607d40e039fbea57d9c04e48b198c9fcf3356c187',
       +     'a9148d9582ad4cf0581c6e0697e4cba6a12e66ca1a0087',
       +     'a914d153a743b315ba19690823119019e16e3762104d87',
       +     'a914b4accc89e48610043e70371153fd8cb5a3eef34287',
       +     'a91406febca615e3631253fd75a1d819436e1d046e0487',
       +     'a914b863cbb888c6b28291cb87a2390539e28be37a9587',
       +     'a914ec39094e393184d2c352a29b9d7a3caddaccb6cf87',
       +     'a914da4faa4babbdf611caf511d287133f06c1c3244a87',
       +     'a9146e64561d0c5e2e9159ecff65db02e04b3277402487',
       +     'a914377d66386972492192ae827fb2208596af0941d187',
       +     'a914448d364ff2374449e57df13db33a40f5b099997c87',
       +     'a914f24b875d2cb99e0b138ab0e6dd65027932b3c6e787',
       +     'a914aa4bcee53406b1ef6c83852e3844e38a3a9d9f3087',
       +     'a9145e5ec40fdab54be0d6e21107bc38c39df97e37fc87',
       +     'a9141de4d402c82f4e9b0e6b792b331232a5405ebd3f87',
       +     'a9148873ee280e51f9c64d257dd6dedc8712fd652cc687'],
       +    ['a9142cc87d7562a85029a57cc37026e12dab72223db287', #change
       +     'a91499f4aee0b274f0b3ab48549a2c58cd667a62c0cb87',
       +     'a91497a89cd5ada3a766a1275f8151e9256fcf537f6c87',
       +     'a9147ffc9f3a3b60635ea1783243274f4d07ab617cb487',
       +     'a9143423113ab913d86fd47e55488a0c559e18b457b987',
       +     'a914a28a3773a37c52ff6fd7dff497d0eaf80a46febb87'])
       +    , #p2wsh 1of2 multisig wallet
       +    ("1 Vpub5fAqpSRkLmvXwqbuR61MaKMSwj5z5xUBwanaz3qnJ5MgaBDpFSLUvKTiNK9zHp" +
       +    "dvrg2LHHXkKxSXBHNWNpZz9b1VqADjmcCs3arSoxN3F3r Vpub5fvEo4MUpbVs9sZqr45" +
       +    "zmRVEsTcQ49MA9m3MLht3XzdZvS9eMXLLu1H6TL1j2SMnykHqXNzG5ycMyQmFDvEE5B32" +
       +    "sP8TmRe6wW8HjBgMssh",
       +    #recv scriptpubkeys
       +    ['002031fbaa839e96fc1abaf3453b9f770e0ccfe2d8e3e990bb381fdcb7db4722986a',
       +     '0020820ae739b36f4feb1c299ced201db383bbcf1634e0071e489b385f43c2323761',
       +     '0020eff05f4d14aa1968a7142b1009aa57a6208fb01b212f8b8f7df63645d26a1292',
       +     '002049c6e17979dca380ffb66295d27f609bea2879d4f0b590c96c70ff12260a8721',
       +     '002002bf2430fc7ebc6fb27da1cb80e52702edcc62a29f65c997e5c924dcd98411bd',
       +     '0020c7a58dcf9633453ba12860b57c14af67d87d022be5c52bf6be7a6abdc295c6e0',
       +     '0020136696059a5e932c72f4f0a05fa7f52faf9b54f1b7694e15acce710e6cc9e89d',
       +     '0020c372e880227f35c2ee35d0724bf05cea95e74dcb3e6aa67ff15f561a29c0645d',
       +     '002095c705590e2b84996fa44bff64179b26669e53bbd58d76bb6bbb5c5498a981ce',
       +     '00207217754dae083c3c365c7e1ce3ad889ca2bd88e4f809cec66b9987adc390aa26',
       +     '0020bee30906450e099357cc96a1f472c1ef70089cd4a0cba96749adfe1c9a2f9e87',
       +     '0020b1838b3d5a386ad6c90eeae9a27a9b812e32ce06376f261dea89e405bc8209d9',
       +     '0020231a3d05886efff601f0702d4c8450dfcce8d6a4bd90f17f7ff76f5c25c632de',
       +     '002071220f3941b5f65aca90e464db4291cd5ea63f37fa858fd5b66d5019f0dbab0f',
       +     '0020fc3c7db9f0e773f9f9c725d4286ddcc88db9575c45b2441d458018150eb4ef10',
       +     '00209f037bfc98dee2fc0d3cca54df09b2d20e92a0133fa381a4dd74c49e4d0a89f5',
       +     '0020c9060d0554ba2ca92048e1772e806d796ba41f10bf6aee2653a9eba96b05c944',
       +     '0020a7cb1dd2730dba564f414ed8d9312370ff89c34df1441b83125cb4d97a96005a',
       +     '00209fddc9b4e070b887dec034ed74f15f62d075a3ac8cf6eb95a88c635e0207534c',
       +     '0020c48f9c50958ab8e386a8bd3888076f31d12e5cf011ff46cc83c6fadfe6d47d20',
       +     '0020a659f4621dca404571917e73dedb26b6d7c49a07dacbf15890760ac0583d3267'],
       +    #change scriptpubkeys
       +    ['002030213b5d3b6988b86aa13a9eaca08e718d51f32dc130c70981abb0102173c791',
       +     '002027bd198f9783a58e9bc4d3fdbd1c75cc74154905cce1d23c7bd3e051695418fe',
       +     '0020c1fd2cdebf120d3b1dc990dfdaca62382ff9525beeb6a79a908ddecb40e2162c',
       +     '00207a3e478266e5fe49fe22e3d8f04d3adda3b6a0835806a0db1f77b84d0ba7f79c',
       +     '002059e66462023ecd54e20d4dce286795e7d5823af511989736edc0c7a844e249f5',
       +     '0020bd8077906dd367d6d107d960397e46db2daba5793249f1f032d8d7e12e6f193c'])
       +    , #p2wpkh-p2sh
       +    ("upub5E4QEumGPNTmSKD95TrYX2xqLwwvBULbRzzHkrpW9WKKCB1y9DEfPXDnUyQjLjmVs" +
       +    "7gSd7k5vRb1FoSb6BjyiWNg4arkJLaqk1jULzbwA5q",
       +    ["a914ae8f84a06668742f713d0743c1f54d248040e63387", #recv
       +     "a914c2e9bdcc48596b8cce418042ade72198fddf3cd987",
       +     "a914a44b6ad63ccef0ae1741eaccee99bf2fa83f842987",
       +     "a9148cf1c891d96a0be07893d0bddcf00ed5dad2c46e87",
       +     "a91414d677b32f2409f4dfb3073d382c302bcd6ed33587",
       +     "a9141b284bee7198d5134512f37ef60e4048864b4bd687"],
       +    ["a914a5aacff65860440893107b01912dc8f60cadab2b87", #change
       +     "a914dcd74ebc8bfc5cf0535717a3e833592d54b3c48687",
       +     "a91446793cae4c2b8149ade61c1627b96b90599bc08787",
       +     "a91439f3776831f321125bdb5099fbbd654923f8316c87"])
       +    , #p2wpkh-p2sh
       +    ("ypub6XrRLtXNB7NQo3vDaMNnffXVJe1WVaebXcb4ncpTHHADLuFYmf2CcPn96YzUbMt8s" +
       +    "HSMmtr1mCcMgCBLqNdY2hrXXcdiLxCdD9e2dChBLun",
       +    ["a91429c2ad045bbb162ef3c2d9cacb9812bec463061787", #recv
       +     "a91433ec6bb67b113978d9cfd307a97fd15bc0a5a62087",
       +     "a91450523020275ccbf4e916a0d8523ae42391ad988a87",
       +     "a91438c2e5e76a874d86cfc914fe9fc1868b6afb5c5487"],
       +    ["a91475f608698bb735120a17699fee854bce9a8dc8d387",
       +     "a91477e69344ef53587051c85a06a52a646457b44e6c87",
       +     "a914607c98ea34fbdffe39fee161ae2ffd5517bf1a5587"])
       +    , #old mnemonic mpk
       +    ("e9d4b7866dd1e91c862aebf62a49548c7dbf7bcc6e4b7b8c9da820c7737968df9c09d" +
       +    "5a3e271dc814a29981f81b3faaf2737b551ef5dcc6189cf0f8252c442b3",
       +    ["76a9149cd3dfb0d87a861770ae4e268e74b45335cf00ab88ac", #recv
       +     "76a914c30f2af6a79296b6531bf34dba14c8419be8fb7d88ac",
       +     "76a9145eb4eeaefcf9a709f8671444933243fbd05366a388ac",
       +     "76a914f96669095e6df76cfdf5c7e49a1909f002e123d088ac"],
       +    ["76a914ca14915184a2662b5d1505ce7142c8ca066c70e288ac", #change
       +     "76a9148942ac692ace81019176c4fb0ac408b18b49237f88ac",
       +     "76a914e1232622a96a04f5e5a24ca0792bb9c28b089d6e88ac"])
       +    , #p2wsh-p2sh 2of2 multisig
       +    ("2 Ypub6hWbqA2p47QgsLt5J4nxrR3ngu8xsPGb7PdV8CDh48KyNngNqPKSqertAqYhQ4u" +
       +    "mELu1UsZUCYfj9XPA6AdSMZWDZQobwF7EJ8uNrECaZg1 Ypub6iNDhL4WWq5kFZcdFqHHw" +
       +    "X4YTH4rYGp8xbndpRrY7WNZFFRfogSrL7wRTajmVHgR46AT1cqUG1mrcRd7h1WXwBsgX2Q" +
       +    "vT3zFbBCDiSDLkau",
       +    ["a91428060ade179c792fac07fc8817fd150ce7cdd3f987", #recv
       +     "a9145ba5ed441b9f3e22f71193d4043b645183e6aeee87",
       +     "a91484cc1f317b7d5afff115916f1e27319919601d0187",
       +     "a9144001695a154cac4d118af889d3fdcaf929af315787",
       +     "a914897888f3152a27cbd7611faf6aa01085931e542a87"],
       +    ["a91454dbb52de65795d144f3c4faeba0e37d9765c85687", #change
       +     "a914f725cbd61c67f34ed40355f243b5bb0650ce61c587",
       +     "a9143672bcd3d02d3ea7c3205ddbc825028a0d2a781987"])
       +]
       +
       +electrum_bad_keydata_test_vectors = [
       +    "zpub661MyMwAqRbcGVQTLtBFzc3ENvyZHoUEhWRdGwoqLZaf5wXP9VcDY2VJV7usvsFLZz" +
       +    "2RUTVhCVXYXc3S8zpLyAFbDFcfrpUiwLoE9VWH2yz", #bad checksum
       +    "a tpubD6NzVbkrYhZ4YVMVzC7wZeRfz3bhqcHvV8M3UiULCfzFtLtp5nwvi6LnBQegrkx" +
       +    "YGPkSzXUEvcPEHcKdda8W1YShVBkhFBGkLxjSQ1Nx3cJ tpubD6NzVbkrYhZ4WjgNYq2nF" +
       +    "TbiSLW2SZAzs4g5JHLqwQ3AmR3tCWpqsZJJEoZuP5HAEBNxgYQhtWMezszoaeTCg6FWGQB" +
       +    "T74sszGaxaf64o5s", #unparsable m number
       +    "2 tpubD6NzVbkrYhZ4YVMVzC7wZeRfz3bhqcHvV8M3UiULCfzFtLtp5nwvi6LnBQegrkx" +
       +    "YGPkSzXUEvcPEHcKdda8W1YShVBkhFBGkLxjSQ1Nx3cJ Vpub5fAqpSRkLmvXwqbuR61M" +
       +    "aKMSwj5z5xUBwanaz3qnJ5MgaBDpFSLUvKTiNK9zHpdvrg2LHHXkKxSXBHNWNpZz9b1Vq" +
       +    "ADjmcCs3arSoxN3F3r" #inconsistent magic
       +]
       +
       +def test():
       +    for keydata, recv_spks, change_spks in electrum_keydata_test_vectors:
       +        initial_count = 15
       +        gaplimit = 5
       +        wal = parse_electrum_master_public_key(keydata, gaplimit)
       +        spks = wal.get_scriptpubkeys(0, 0, initial_count)
       +        #for test, generate 15, check that the last 5 lead to gap limit overrun
       +        for i in range(initial_count - gaplimit):
       +            ret = wal.have_scriptpubkeys_overrun_gaplimit([spks[i]])
       +            assert ret == None
       +        for i in range(gaplimit):
       +            index = i + initial_count - gaplimit
       +            ret = wal.have_scriptpubkeys_overrun_gaplimit([spks[index]])
       +            assert ret != None and ret[0] == i+1
       +        last_index_add = 3
       +        last_index = initial_count - gaplimit + last_index_add
       +        ret = wal.have_scriptpubkeys_overrun_gaplimit(spks[2:last_index])
       +        assert ret[0] == last_index_add
       +        assert wal.get_scriptpubkeys(0, 0, len(recv_spks)) == recv_spks
       +        assert wal.get_scriptpubkeys(1, 0, len(change_spks)) == change_spks
       +    for keydata in electrum_bad_keydata_test_vectors:
       +        try:
       +            parse_electrum_master_public_key(keydata, 5)
       +            raised_error = False
       +        except (ValueError, Exception):
       +            raised_error = True
       +        assert raised_error
       +    print("All tests passed successfully")
       +
       +if __name__ == "__main__":
       +    test()
       +    pass
       +
   DIR diff --git a/hashes.py b/electrumpersonalserver/hashes.py
   DIR diff --git a/jsonrpc.py b/electrumpersonalserver/jsonrpc.py
   DIR diff --git a/electrumpersonalserver/merkleproof.py b/electrumpersonalserver/merkleproof.py
       t@@ -0,0 +1,271 @@
       +
       +import bitcoin as btc
       +import binascii
       +from math import ceil, log
       +
       +from electrumpersonalserver.hashes import hash_encode, hash_decode
       +from electrumpersonalserver.hashes import Hash, hash_merkle_root
       +
       +#lots of ideas and code taken from bitcoin core and breadwallet
       +#https://github.com/bitcoin/bitcoin/blob/master/src/merkleblock.h
       +#https://github.com/breadwallet/breadwallet-core/blob/master/BRMerkleBlock.c
       +
       +def calc_tree_width(height, txcount):
       +    """Efficently calculates the number of nodes at given merkle tree height"""
       +    return (txcount + (1 << height) - 1) >> height
       +
       +def decend_merkle_tree(hashes, flags, height, txcount, pos):
       +    """Function recursively follows the flags bitstring down into the
       +       tree, building up a tree in memory"""
       +    flag = next(flags)
       +    if height > 0:
       +        #non-txid node
       +        if flag:
       +            left = decend_merkle_tree(hashes, flags, height-1, txcount, pos*2)
       +            #bitcoin's merkle tree format has a rule that if theres an
       +            # odd number of nodes in then the tree, the last hash is duplicated
       +            #in the electrum format we must hash together the duplicate
       +            # tree branch
       +            if pos*2+1 < calc_tree_width(height-1, txcount):
       +                right = decend_merkle_tree(hashes, flags, height-1,
       +                    txcount, pos*2+1)
       +            else:
       +                if isinstance(left, tuple):
       +                    right = expand_tree_hashing(left)
       +                else:
       +                    right = left
       +            return (left, right)
       +        else:
       +            hs = next(hashes)
       +            return hs
       +    else:
       +        #txid node
       +        hs = next(hashes)
       +        if flag:
       +            #for the actual transaction, also store its position with a flag 
       +            return "tx:" + str(pos) + ":" + hs
       +        else:
       +            return hs
       +
       +def deserialize_core_format_merkle_proof(hash_list, flag_value, txcount):
       +    """Converts core's format for a merkle proof into a tree in memory"""
       +    tree_depth = int(ceil(log(txcount, 2)))
       +    hashes = iter(hash_list)
       +    #one-liner which converts the flags value to a list of True/False bits
       +    flags = (flag_value[i//8]&1 << i%8 != 0 for i in range(len(flag_value)*8))
       +    try:
       +        root_node = decend_merkle_tree(hashes, flags, tree_depth, txcount, 0)
       +        return root_node
       +    except StopIteration:
       +        raise ValueError
       +
       +def expand_tree_electrum_format_merkle_proof(node, result):
       +    """Recurse down into the tree, adding hashes to the result list
       +       in depth order"""
       +    left, right = node
       +    if isinstance(left, tuple):
       +        expand_tree_electrum_format_merkle_proof(left, result)
       +    if isinstance(right, tuple):
       +        expand_tree_electrum_format_merkle_proof(right, result)
       +    if not isinstance(left, tuple):
       +        result.append(left)
       +    if not isinstance(right, tuple):
       +        result.append(right)
       +
       +def get_node_hash(node):
       +    if node.startswith("tx"):
       +        return node.split(":")[2]
       +    else:
       +        return node
       +
       +def expand_tree_hashing(node):
       +    """Recurse down into the tree, hashing everything and
       +       returning root hash"""
       +    left, right = node
       +    if isinstance(left, tuple):
       +        hash_left = expand_tree_hashing(left)
       +    else:
       +        hash_left = get_node_hash(left)
       +    if isinstance(right, tuple):
       +        hash_right = expand_tree_hashing(right)
       +    else:
       +        hash_right = get_node_hash(right)
       +    return hash_encode(Hash(hash_decode(hash_left) + hash_decode(hash_right)))
       +
       +def convert_core_to_electrum_merkle_proof(proof):
       +    """Bitcoin Core and Electrum use different formats for merkle
       +       proof, this function converts from Core's format to Electrum's format"""
       +    proof = binascii.unhexlify(proof)
       +    pos = [0]
       +    def read_as_int(bytez):
       +        pos[0] += bytez
       +        return btc.decode(proof[pos[0] - bytez:pos[0]][::-1], 256)
       +    def read_var_int():
       +        pos[0] += 1
       +        val = btc.from_byte_to_int(proof[pos[0] - 1])
       +        if val < 253:
       +            return val
       +        return read_as_int(pow(2, val - 252))
       +    def read_bytes(bytez):
       +        pos[0] += bytez
       +        return proof[pos[0] - bytez:pos[0]]
       +
       +    merkle_root = proof[36:36+32]
       +    pos[0] = 80
       +    txcount = read_as_int(4)
       +    hash_count = read_var_int()
       +    hashes = [hash_encode(read_bytes(32)) for i in range(hash_count)]
       +    flags_count = read_var_int()
       +    flags = read_bytes(flags_count)
       +
       +    root_node = deserialize_core_format_merkle_proof(hashes, flags, txcount)
       +    #check special case of a tree of zero height, block with only coinbase tx
       +    if not isinstance(root_node, tuple):
       +        root_node = root_node[5:] #remove the "tx:0:"
       +        result = {"pos": 0, "merkle": [], "txid": root_node,
       +            "merkleroot": hash_encode(merkle_root)}
       +        return result
       +
       +    hashes_list = []
       +    expand_tree_electrum_format_merkle_proof(root_node, hashes_list)
       +    #remove the first or second element which is the txhash
       +    tx = hashes_list[0]
       +    if hashes_list[1].startswith("tx"):
       +        tx = hashes_list[1]
       +    assert(tx.startswith("tx"))
       +    hashes_list.remove(tx)
       +    #if the txhash was duplicated, that _is_ included in electrum's format
       +    if hashes_list[0].startswith("tx"):
       +        hashes_list[0] = tx.split(":")[2]
       +    tx_pos, txid = tx.split(":")[1:3]
       +    tx_pos = int(tx_pos)
       +    result = {"pos": tx_pos, "merkle": hashes_list, "txid": txid,
       +        "merkleroot": hash_encode(merkle_root)}
       +    return result
       +
       +merkle_proof_test_vectors = [
       +    #txcount 819, pos 5
       +    "0300000026e696fba00f0a43907239305eed9e55824e0e376636380f000000000000000" + 
       +    "04f8a2ce51d6c69988029837688cbfc2f580799fa1747456b9c80ab808c1431acd0b07f" + 
       +    "5543201618cadcfbf7330300000b0ff1e0050fed22ca360e0935e053b0fe098f6f9e090" + 
       +    "f5631013361620d964fe2fd88544ae10b40621e1cd24bb4306e3815dc237f77118a45d7" + 
       +    "5ada9ee362314b70573732bce59615a3bcc1bbacd04b33b7819198212216b5d62d75be5" + 
       +    "9221ada17ba4fb2476b689cccd3be54732fd5630832a94f11fa3f0dafd6f904d43219e0" + 
       +    "d7de110158446b5b598bd241f7b5df4da0ebc7d30e7748d487917b718df51c681174e6a" + 
       +    "bab8042cc7c1c436221c098f06a56134f9247a812126d675d69c82ba1c715cfc0cde462" + 
       +    "fd1fbe5dc87f6b8db2b9c060fcd59a20e7fe8e921c3676937a873ff88684f4be4d015f2" + 
       +    "4f26af6d2cf78335e9218bcceba4507d0b4ba6cb933aa01ef77ae5eb411893ec0f74b69" + 
       +    "590fb0f5118ac937c02ccd47e9d90be78becd11ecf854d7d268eeb479b74d137278c0a5" + 
       +    "017d29e90cd5b35a4680201824fb0eb4f404e20dfeaec4d50549030b7e7e220b02eb210" + 
       +    "5f3d2e8bcc94d547214a9d03ff1600",
       +    #txcount 47, pos 9
       +    "0100000053696a625fbd16df418575bce0c4148886c422774fca5fcab80100000000000" + 
       +    "01532bfe4f9c4f56cd141028e5b59384c133740174b74b1982c7f01020b90ce05577c67" + 
       +    "508bdb051a7ec2ef942f000000076cde2eb7efa90b36d48aed612e559ff2ba638d8d400" + 
       +    "b14b0c58df00c6a6c33b65dc8fa02f4ca56e1f4dcf17186fa9bbd990ce150b6e2dc9e9e" + 
       +    "56bb4f270fe56fde6bdd73a7a7e82767714862888e6b759568fb117674ad23050e29311" + 
       +    "97494d457efb72efdb9cb79cd4a435724908a0eb31ec7f7a67ee03837319e098b43edad" + 
       +    "3be9af75ae7b30db6f4f93ba0fdd941fdf70fe8cc38982e03bd292f5bd02f28137d343f" + 
       +    "908c7d6417379afe8349a257af3ca1f74f623be6a416fe1aa96a8f259983f2cf32121bc" + 
       +    "e203955a378b3b44f132ea6ab94c7829a6c3b360c9f8da8e74027701",
       +    #txcount 2582, pos 330
       +    "000000206365d5e1d8b7fdf0b846cfa902115c1b8ced9dd49cb17800000000000000000" + 
       +    "01032e829e1f9a5a09d0492f9cd3ec0762b7facea555989c3927d3d975fd4078c771849" + 
       +    "5a45960018edd3b9e0160a00000dfe856a7d5d77c23ebf85c68b5eb303d85e56491ed6d" + 
       +    "204372625d0b4383df5a44d6e46d2db09d936b9f5d0b53e0dbcb3efb7773d457369c228" + 
       +    "fd1ce6e11645e366a58b3fc1e8a7c916710ce29a87265a6729a3b221b47ea9c8e6f4870" + 
       +    "7b112b8d67e5cfb3db5f88b042dc49e4e5bc2e61c28e1e0fbcba4c741bb5c75cac58ca0" + 
       +    "4161a7377d70f3fd19a3e248ed918c91709b49afd3760f89ed2fefbcc9c23447ccb40a2" + 
       +    "be7aba22b07189b0bf90c62db48a9fe37227e12c7af8c1d4c22f9f223530dacdd5f3ad8" + 
       +    "50ad4badf16cc24049a65334f59bf28c15cecda1a4cf3f2937bd70ee84f66569ce8ef95" + 
       +    "1d50cca46d60337e6c697685b38ad217967bbe6801d03c44fcb808cd035be31888380a2" + 
       +    "df1be14b6ff100de83cab0dce250e2b40ca3b47e8309f848646bee63b6185c176d84f15" + 
       +    "46a482e7a65a87d1a2d0d5a2b683e2cae0520df1e3525a71d71e1f551abd7d238c3bcb4" + 
       +    "ecaeea7d5988745fa421a8604a99857426957a2ccfa7cd8df145aa8293701989dd20750" + 
       +    "5923fcb339843944ce3d21dc259bcda9c251ed90d4e55af2cf5b15432050084f513ac74" + 
       +    "c0bdd4b6046fb70100",
       +    #txcount 2861, pos 2860, last tx with many duplicated nodes down the tree
       +    "00000020c656c90b521a2bbca14174f2939b882a28d23d86144b0e00000000000000000" + 
       +    "0cf5185a8e369c3de5f15e039e777760994fd66184b619d839dace3aec9953fd6d86159" + 
       +    "5ac1910018ee097a972d0b0000078d20d71c3114dbf52bb13f2c18f987891e8854d2d29" + 
       +    "f61c0b3d3256afcef7c0b1e6f76d6431f93390ebd28dbb81ad7c8f08459e85efeb23cc7" + 
       +    "2df2c5612215bf53dd4ab3703886bc8c82cb78ba761855e495fb5dc371cd8fe25ae974b" + 
       +    "df42269e267caf898a9f34cbf2350eaaa4afbeaea70636b5a3b73682186817db5b33290" + 
       +    "bd5c696bd8d0322671ff70c5447fcd7bdc127e5b84350c6b14b5a3b86b424d7db38d39f" + 
       +    "171f57e255a31c6c53415e3d65408b6519a40aacc49cad8e70646d4cb0d23d4a63068e6" + 
       +    "c220efc8a2781e9e774efdd334108d7453043bd3c8070d0e5903ad5b07",
       +    #txcount 7, pos 6, duplicated entry in the last depth, at tx level
       +    "0100000056e02c6d3278c754e0699517834741f7c4ad3dcbfeb7803a346200000000000" + 
       +    "0af3bdd5dd465443fd003e9281455e60aae573dd4d46304d7ba17276ea33d506488cbb4" + 
       +    "4dacb5001b9ebb193b0700000003cd3abb2eb7583165f36b56add7268be9027ead4cc8f" + 
       +    "888ec650d3b1c1f4de28a0ff7c8b463b2042d09598f0e5e5905de362aa1cf75252adc22" + 
       +    "719b8e1bc969adcfbc4782b8eafc9352263770b91a0f189ae051cbe0e26046c2b14cf3d" + 
       +    "8be0bc40135",
       +    #txcount 6, pos 5, duplicated entry in the last-but-one depth
       +    "01000000299edfd28524eae4fb6012e4087afdb6e1b912db85e612374b0300000000000" + 
       +    "0e16572394f8578a47bf36e15cd16faa5e3b9e18805cf4e271ae4ef95aa8cea7eb31fa1" + 
       +    "4e4b6d0b1a42857d960600000003f52b45ed953924366dab3e92707145c78615b639751" + 
       +    "ecb7be1c5ecc09b592ed588ca0e15a89e9049a2dbcadf4d8362bd1f74a6972f176617b5" + 
       +    "8a5466c8a4121fc3e2d6fa66c8637b387ef190ab46d6e9c9dae4bbccd871c72372b3dbc" + 
       +    "6edefea012d",
       +    #txcount 5, pos 4, duplicated on the last and second last depth
       +    "010000004d891de57908d89e9e5585648978d7650adc67e856c2d8c18c1800000000000" + 
       +    "04746fd317bffecd4ffb320239caa06685bafe0c1b5463b24d636e45788796657843d1b" + 
       +    "4d4c86041be68355c40500000002d8e26c89c46477f2407d866d2badbd98e43e732a670" + 
       +    "e96001faf1744b27e5fdd018733d72e31a2d6a0d94f2a3b35fcc66fb110c40c5bbff82b" + 
       +    "f87606553d541d011d",
       +    #txcount 2739, pos 0, coinbase tx
       +    "000000209f283da030c6e6d0ff5087a87c430d140ed6b4564fa34d00000000000000000" + 
       +    "0ec1513723e3652c6b8e777c41eb267ad8dd2025e85228840f5cfca7ffe1fb331afff8a" + 
       +    "5af8e961175e0f7691b30a00000df403e21a4751fbd52457f535378ac2dcf111199e9ea" + 
       +    "6f78f6c2663cb99b58203438d8f3b26f7f2804668c1df7d394a4726363d4873b2d85b71" + 
       +    "2e44cf4f5e4f33f22a8f3a1672846bd7c4570c668e6ee12befda23bfa3d0fcd30b1b079" + 
       +    "19b01c40b1e31b6d34fcdbb99539d46eb97a3ae15386f1ab0f28ecacadd9fc3fa4ce49a" + 
       +    "1a1839d815229f54036c8a3035d91e80e8dc127b62032b4e652550b4fc0aee0f6e85a14" + 
       +    "307d85ed9dde62acff9a0f7e3b52370a10d6c83ec13a0b4a8fafe87af368a167d7e9b63" + 
       +    "3b84b6ea65f1ce5e8ccc1840be0a4dab0099e25afccc7f2fdbda54cd65ecbac8d9a550c" + 
       +    "108b4e18d3af59129d373fde4c80848858fd6f7fc1e27387a38833473ca8a47729fa6e1" + 
       +    "cc14b584c14dad768108ff18cc6acdc9c31d32dc71c3c80856664a3fff870fe419a59aa" + 
       +    "9033356590475d36086f0b3c0ece34c0f3756675c610fb980ff3363af6f9c0918a7c677" + 
       +    "23371849de9c1026515c2900a80b3aee4f2625c8f48cd5eb967560ee8ebe58a8d41c331" + 
       +    "f6d5199795735d4f0494bdf592d166fa291062733619f0f133605087365639de2d9d5d6" + 
       +    "921f4b4204ff1f0000",
       +    #txcount 1, pos 0, coinbase tx in an empty block, tree with height 1
       +    "010000000508085c47cc849eb80ea905cc7800a3be674ffc57263cf210c59d8d0000000" + 
       +    "0112ba175a1e04b14ba9e7ea5f76ab640affeef5ec98173ac9799a852fa39add320cd66" + 
       +    "49ffff001d1e2de5650100000001112ba175a1e04b14ba9e7ea5f76ab640affeef5ec98" + 
       +    "173ac9799a852fa39add30101",
       +    #txcount 2, pos 1, tree with height 2
       +    "010000004e24a2880cd72d9bde7502087bd3756819794dc7548f68dd68dc30010000000" + 
       +    "02793fce9cdf91b4f84760571bf6009d5f0ffaddbfdc9234ef58a036096092117b10f4b" + 
       +    "4cfd68011c903e350b0200000002ee50562fc6f995eff2df61be0d5f943bac941149aa2" + 
       +    "1aacb32adc130c0f17d6a2077a642b1eabbc5120e31566a11e2689aa4d39b01cce9a190" + 
       +    "2360baa5e4328e0105"
       +]
       +
       +def test():
       +    for proof in merkle_proof_test_vectors:
       +        try:
       +            electrum_proof = convert_core_to_electrum_merkle_proof(proof)
       +            #print(electrum_proof)
       +            implied_merkle_root = hash_merkle_root(
       +                electrum_proof["merkle"], electrum_proof["txid"],
       +                electrum_proof["pos"])
       +            assert implied_merkle_root == electrum_proof["merkleroot"]
       +        except ValueError:
       +            import traceback
       +            traceback.print_exc()
       +            assert 0
       +    print("All tests passed")
       +
       +'''
       +proof = ""
       +def chunks(d, n):
       +    return [d[x:x + n] for x in range(0, len(d), n)]
       +#print(proof)
       +print("\" + \n\"".join(chunks(proof, 71)))
       +'''
       +
       +if __name__ == "__main__":
       +    test()
       +
   DIR diff --git a/electrumpersonalserver/transactionmonitor.py b/electrumpersonalserver/transactionmonitor.py
       t@@ -0,0 +1,723 @@
       +
       +import time, pprint, math, sys
       +from decimal import Decimal
       +
       +from electrumpersonalserver.jsonrpc import JsonRpcError
       +import electrumpersonalserver.hashes as hashes
       +
       +#internally this code uses scriptPubKeys, it only converts to bitcoin addresses
       +# when importing to bitcoind or checking whether enough addresses have been
       +# imported
       +#the electrum protocol uses sha256(scriptpubkey) as a key for lookups
       +# this code calls them scripthashes
       +
       +#code will generate the first address from each deterministic wallet
       +# and check whether they have been imported into the bitcoin node
       +# if no then initial_import_count addresses will be imported, then exit
       +# if yes then initial_import_count addresses will be generated and extra
       +# addresses will be generated one-by-one, each time checking whether they have
       +# been imported into the bitcoin node
       +# when an address has been reached that has not been imported, that means
       +# we've reached the end, then rewind the deterministic wallet index by one
       +
       +#when a transaction happens paying to an address from a deterministic wallet
       +# lookup the position of that address, if its less than gap_limit then
       +# import more addresses
       +
       +ADDRESSES_LABEL = "electrum-watchonly-addresses"
       +
       +def import_addresses(rpc, addrs, debug, log):
       +    debug("importing addrs = " + str(addrs))
       +    log("Importing " + str(len(addrs)) + " addresses in total")
       +    addr_i = iter(addrs)
       +    notifications = 10
       +    for i in range(notifications):
       +        pc = int(100.0 * i / notifications)
       +        sys.stdout.write("[" + str(pc) + "%]... ")
       +        sys.stdout.flush()
       +        for j in range(int(len(addrs) / notifications)):
       +            rpc.call("importaddress", [next(addr_i), ADDRESSES_LABEL, False])
       +    for a in addr_i: #import the reminder of addresses
       +        rpc.call("importaddress", [a, ADDRESSES_LABEL, False])
       +    print("[100%]")
       +    log("Importing done")
       +
       +class TransactionMonitor(object):
       +    """
       +    Class which monitors the bitcoind wallet for new transactions
       +    and builds a history datastructure for sending to electrum
       +    """
       +    def __init__(self, rpc, deterministic_wallets, debug, log):
       +        self.rpc = rpc
       +        self.deterministic_wallets = deterministic_wallets
       +        self.debug = debug
       +        self.log = log
       +        self.last_known_wallet_txid = None
       +        self.address_history = None
       +        self.unconfirmed_txes = None
       +
       +    def get_electrum_history_hash(self, scrhash):
       +        return hashes.get_status_electrum( ((h["tx_hash"], h["height"])
       +            for h in self.address_history[scrhash]["history"]) )
       +
       +    def get_electrum_history(self, scrhash):
       +        if scrhash in self.address_history:
       +            return self.address_history[scrhash]["history"]
       +        else:
       +            return None
       +
       +    def subscribe_address(self, scrhash):
       +        if scrhash in self.address_history:
       +            self.address_history[scrhash]["subscribed"] = True
       +            return True
       +        else:
       +            return False
       +
       +    def unsubscribe_all_addresses(self):
       +        for scrhash, his in self.address_history.items():
       +            his["subscribed"] = False
       +
       +    def build_address_history(self, monitored_scriptpubkeys):
       +        self.log("Building history with " + str(len(monitored_scriptpubkeys)) +
       +            " addresses")
       +        st = time.time()
       +        address_history = {}
       +        for spk in monitored_scriptpubkeys:
       +            address_history[hashes.script_to_scripthash(spk)] = {'history': [],
       +                'subscribed': False}
       +        wallet_addr_scripthashes = set(address_history.keys())
       +        #populate history
       +        #which is a blockheight-ordered list of ("txhash", height)
       +        #unconfirmed transactions go at the end as ("txhash", 0, fee)
       +        # 0=unconfirmed -1=unconfirmed with unconfirmed parents
       +
       +        BATCH_SIZE = 1000
       +        ret = list(range(BATCH_SIZE))
       +        t = 0
       +        count = 0
       +        obtained_txids = set()
       +        while len(ret) == BATCH_SIZE:
       +            ret = self.rpc.call("listtransactions", ["*", BATCH_SIZE, t, True])
       +            self.debug("listtransactions skip=" + str(t) + " len(ret)="
       +                + str(len(ret)))
       +            t += len(ret)
       +            for tx in ret:
       +                if "txid" not in tx or "category" not in tx:
       +                    continue
       +                if tx["category"] not in ("receive", "send"):
       +                    continue
       +                if tx["txid"] in obtained_txids:
       +                    continue
       +                self.debug("adding obtained tx=" + str(tx["txid"]))
       +                obtained_txids.add(tx["txid"])
       +
       +                #obtain all the addresses this transaction is involved with
       +                output_scriptpubkeys, input_scriptpubkeys, txd = \
       +                    self.get_input_and_output_scriptpubkeys(tx["txid"])
       +                output_scripthashes = [hashes.script_to_scripthash(sc)
       +                    for sc in output_scriptpubkeys]
       +                sh_to_add = wallet_addr_scripthashes.intersection(set(
       +                    output_scripthashes))
       +                input_scripthashes = [hashes.script_to_scripthash(sc)
       +                    for sc in input_scriptpubkeys]
       +                sh_to_add |= wallet_addr_scripthashes.intersection(set(
       +                    input_scripthashes))
       +                if len(sh_to_add) == 0:
       +                    continue
       +
       +                for wal in self.deterministic_wallets:
       +                    overrun_depths = wal.have_scriptpubkeys_overrun_gaplimit(
       +                        output_scriptpubkeys)
       +                    if overrun_depths != None:
       +                        self.log("ERROR: Not enough addresses imported.")
       +                        self.log("Delete wallet.dat and increase the value " +
       +                            "of `initial_import_count` in the file " + 
       +                            "`config.cfg` then reimport and rescan")
       +                        #TODO make it so users dont have to delete wallet.dat
       +                        # check whether all initial_import_count addresses are
       +                        # imported rather than just the first one
       +                        return False
       +                new_history_element = self.generate_new_history_element(tx, txd)
       +                for scripthash in sh_to_add:
       +                    address_history[scripthash][
       +                        "history"].append(new_history_element)
       +                count += 1
       +
       +        unconfirmed_txes = {}
       +        for scrhash, his in address_history.items():
       +            uctx = self.sort_address_history_list(his)
       +            for u in uctx:
       +                if u["tx_hash"] in unconfirmed_txes:
       +                    unconfirmed_txes[u["tx_hash"]].append(scrhash)
       +                else:
       +                    unconfirmed_txes[u["tx_hash"]] = [scrhash]
       +        self.debug("unconfirmed_txes = " + str(unconfirmed_txes))
       +        if len(ret) > 0:
       +            #txid doesnt uniquely identify transactions from listtransactions
       +            #but the tuple (txid, address) does
       +            self.last_known_wallet_txid = (ret[-1]["txid"], ret[-1]["address"])
       +        else:
       +            self.last_known_wallet_txid = None
       +        self.debug("last_known_wallet_txid = " + str(
       +            self.last_known_wallet_txid))
       +
       +        et = time.time()
       +        self.debug("address_history =\n" + pprint.pformat(address_history))
       +        self.log("Found " + str(count) + " txes. History built in " +
       +            str(et - st) + "sec")
       +        self.address_history = address_history
       +        self.unconfirmed_txes = unconfirmed_txes
       +        return True
       +
       +    def get_input_and_output_scriptpubkeys(self, txid):
       +        gettx = self.rpc.call("gettransaction", [txid])
       +        txd = self.rpc.call("decoderawtransaction", [gettx["hex"]])
       +        output_scriptpubkeys = [out["scriptPubKey"]["hex"]
       +            for out in txd["vout"]]
       +        input_scriptpubkeys = []
       +        for inn in txd["vin"]:
       +            try:
       +                wallet_tx = self.rpc.call("gettransaction", [inn["txid"]])
       +            except JsonRpcError:
       +                #wallet doesnt know about this tx, so the input isnt ours
       +                continue
       +            input_decoded = self.rpc.call("decoderawtransaction", [wallet_tx[
       +                "hex"]])
       +            script = input_decoded["vout"][inn["vout"]]["scriptPubKey"]["hex"]
       +            input_scriptpubkeys.append(script)
       +        return output_scriptpubkeys, input_scriptpubkeys, txd
       +
       +    def generate_new_history_element(self, tx, txd):
       +        if tx["confirmations"] == 0:
       +            unconfirmed_input = False
       +            total_input_value = 0
       +            for inn in txd["vin"]:
       +                utxo = self.rpc.call("gettxout", [inn["txid"], inn["vout"],
       +                    True])
       +                if utxo is None:
       +                    utxo = self.rpc.call("gettxout", [inn["txid"], inn["vout"],
       +                        False])
       +                    if utxo is None:
       +                        self.debug("utxo not found(!)")
       +                        #TODO detect this and figure out how to tell
       +                        # electrum that we dont know the fee
       +                total_input_value += int(Decimal(utxo["value"]) * Decimal(1e8))
       +                unconfirmed_input = (unconfirmed_input or
       +                    utxo["confirmations"] == 0)
       +            self.debug("total_input_value = " + str(total_input_value))
       +
       +            fee = total_input_value - sum([int(Decimal(out["value"])
       +                * Decimal(1e8)) for out in txd["vout"]])
       +            height = -1 if unconfirmed_input else 0
       +            new_history_element = ({"tx_hash": tx["txid"], "height": height,
       +                "fee": fee})
       +        else:
       +            blockheader = self.rpc.call("getblockheader", [tx['blockhash']])
       +            new_history_element = ({"tx_hash": tx["txid"],
       +                "height": blockheader["height"]})
       +        return new_history_element
       +
       +    def sort_address_history_list(self, his):
       +        unconfirm_txes = list(filter(lambda h:h["height"] == 0, his["history"]))
       +        confirm_txes = filter(lambda h:h["height"] != 0, his["history"])
       +        #TODO txes must be "in blockchain order"
       +        # the order they appear in the block
       +        # it might be "blockindex" in listtransactions and gettransaction
       +        #so must sort with key height+':'+blockindex
       +        #maybe check if any heights are the same then get the pos only for those
       +        #better way to do this is to have a separate dict that isnt in history
       +        # which maps txid => blockindex
       +        # and then sort by key height+":"+idx[txid]
       +        his["history"] = sorted(confirm_txes, key=lambda h:h["height"])
       +        his["history"].extend(unconfirm_txes)
       +        return unconfirm_txes
       +
       +    def check_for_updated_txes(self):
       +        updated_scrhashes1 = self.check_for_new_txes()
       +        updated_scrhashes2 = self.check_for_confirmations()
       +        updated_scrhashes = updated_scrhashes1 | updated_scrhashes2
       +        for ush in updated_scrhashes:
       +            his = self.address_history[ush]
       +            self.sort_address_history_list(his)
       +        if len(updated_scrhashes) > 0:
       +            self.debug("new tx address_history =\n"
       +                + pprint.pformat(self.address_history))
       +            self.debug("unconfirmed txes = " +
       +                pprint.pformat(self.unconfirmed_txes))
       +            self.debug("updated_scripthashes = " + str(updated_scrhashes))
       +        updated_scrhashes = filter(lambda sh:self.address_history[sh][
       +            "subscribed"], updated_scrhashes)
       +        return updated_scrhashes
       +
       +    def check_for_confirmations(self):
       +        confirmed_txes_scrhashes = []
       +        self.debug("check4con unconfirmed_txes = "
       +            + pprint.pformat(self.unconfirmed_txes))
       +        for uc_txid, scrhashes in self.unconfirmed_txes.items():
       +            tx = self.rpc.call("gettransaction", [uc_txid])
       +            self.debug("uc_txid=" + uc_txid + " => " + str(tx))
       +            if tx["confirmations"] == 0:
       +                continue #still unconfirmed
       +            self.log("A transaction confirmed: " + uc_txid)
       +            confirmed_txes_scrhashes.append((uc_txid, scrhashes))
       +            block = self.rpc.call("getblockheader", [tx["blockhash"]])
       +            for scrhash in scrhashes:
       +                #delete the old unconfirmed entry in address_history
       +                deleted_entries = [h for h in self.address_history[scrhash][
       +                    "history"] if h["tx_hash"] == uc_txid]
       +                for d_his in deleted_entries:
       +                    self.address_history[scrhash]["history"].remove(d_his)
       +                #create the new confirmed entry in address_history
       +                self.address_history[scrhash]["history"].append({"height":
       +                    block["height"], "tx_hash": uc_txid})
       +        updated_scrhashes = set()
       +        for tx, scrhashes in confirmed_txes_scrhashes:
       +            del self.unconfirmed_txes[tx]
       +            updated_scrhashes.update(set(scrhashes))
       +        return updated_scrhashes
       +
       +    def check_for_new_txes(self):
       +        MAX_TX_REQUEST_COUNT = 256 
       +        tx_request_count = 2
       +        max_attempts = int(math.log(MAX_TX_REQUEST_COUNT, 2))
       +        for i in range(max_attempts):
       +            self.debug("listtransactions tx_request_count="
       +                + str(tx_request_count))
       +            ret = self.rpc.call("listtransactions", ["*", tx_request_count, 0,
       +                True])
       +            ret = ret[::-1]
       +            if self.last_known_wallet_txid == None:
       +                recent_tx_index = len(ret) #=0 means no new txes
       +                break
       +            else:
       +                txid_list = [(tx["txid"], tx["address"]) for tx in ret]
       +                recent_tx_index = next((i for i, (txid, addr)
       +                    in enumerate(txid_list) if
       +                    txid == self.last_known_wallet_txid[0] and
       +                    addr == self.last_known_wallet_txid[1]), -1)
       +                if recent_tx_index != -1:
       +                    break
       +                tx_request_count *= 2
       +
       +        #TODO low priority: handle a user getting more than 255 new
       +        # transactions in 15 seconds
       +        self.debug("recent tx index = " + str(recent_tx_index) + " ret = " +
       +            str([(t["txid"], t["address"]) for t in ret]))
       +        if len(ret) > 0:
       +            self.last_known_wallet_txid = (ret[0]["txid"], ret[0]["address"])
       +            self.debug("last_known_wallet_txid = " + str(
       +                self.last_known_wallet_txid))
       +        assert(recent_tx_index != -1)
       +        if recent_tx_index == 0:
       +            return set()
       +        new_txes = ret[:recent_tx_index][::-1]
       +        self.debug("new txes = " + str(new_txes))
       +        obtained_txids = set()
       +        updated_scripthashes = []
       +        for tx in new_txes:
       +            if "txid" not in tx or "category" not in tx:
       +                continue
       +            if tx["category"] not in ("receive", "send"):
       +                continue
       +            if tx["txid"] in obtained_txids:
       +                continue
       +            obtained_txids.add(tx["txid"])
       +            output_scriptpubkeys, input_scriptpubkeys, txd = \
       +                self.get_input_and_output_scriptpubkeys(tx["txid"])
       +            matching_scripthashes = []
       +            for spk in (output_scriptpubkeys + input_scriptpubkeys):
       +                scripthash = hashes.script_to_scripthash(spk)
       +                if scripthash in self.address_history:
       +                    matching_scripthashes.append(scripthash)
       +            if len(matching_scripthashes) == 0:
       +                continue
       +
       +            for wal in self.deterministic_wallets:
       +                overrun_depths = wal.have_scriptpubkeys_overrun_gaplimit(
       +                    output_scriptpubkeys)
       +                if overrun_depths != None:
       +                    for change, import_count in overrun_depths.items():
       +                        spks = wal.get_new_scriptpubkeys(change, import_count)
       +                        for spk in spks:
       +                            self.address_history[hashes.script_to_scripthash(
       +                                spk)] =  {'history': [], 'subscribed': False}
       +                        new_addrs = [hashes.script_to_address(s, self.rpc)
       +                            for s in spks]
       +                        self.debug("importing " + str(len(spks)) +
       +                            " into change=" + str(change))
       +                        import_addresses(self.rpc, new_addrs, self.debug,
       +                            self.log)
       +
       +            updated_scripthashes.extend(matching_scripthashes)
       +            new_history_element = self.generate_new_history_element(tx, txd)
       +            self.log("Found new tx: " + str(new_history_element))
       +            for scrhash in matching_scripthashes:
       +                self.address_history[scrhash]["history"].append(
       +                    new_history_element)
       +                if new_history_element["height"] == 0:
       +                    if tx["txid"] in self.unconfirmed_txes:
       +                        self.unconfirmed_txes[tx["txid"]].append(scrhash)
       +                    else:
       +                        self.unconfirmed_txes[tx["txid"]] = [scrhash]
       +            #check whether gap limits have been overrun and import more addrs
       +        return set(updated_scripthashes)
       +
       +
       +## start tests here
       +
       +class TestJsonRpc(object):
       +    def __init__(self, txlist, utxoset, block_heights):
       +        self.txlist = txlist
       +        self.utxoset = utxoset
       +        self.block_heights = block_heights
       +        self.imported_addresses = []
       +
       +    def call(self, method, params):
       +        if method == "listtransactions":
       +            count = int(params[1])
       +            skip = int(params[2])
       +            return self.txlist[skip:skip + count]
       +        elif method == "gettransaction":
       +            for t in self.txlist:
       +                if t["txid"] == params[0]:
       +                    return t
       +            raise JsonRpcError({"code": None, "message": None})
       +        elif method == "decoderawtransaction":
       +            for t in self.txlist:
       +                if t["hex"] == params[0]:
       +                    return t
       +            assert 0
       +        elif method == "gettxout":
       +            for u in self.utxoset:
       +                if u["txid"] == params[0] and u["vout"] == params[1]:
       +                    return u
       +            assert 0
       +        elif method == "getblockheader":
       +            if params[0] not in self.block_heights:
       +                assert 0
       +            return {"height": self.block_heights[params[0]]}
       +        elif method == "decodescript":
       +            return {"addresses": [test_spk_to_address(params[0])]}
       +        elif method == "importaddress":
       +            self.imported_addresses.append(params[0])
       +        else:
       +            raise ValueError("unknown method in test jsonrpc")
       +
       +    def add_transaction(self, tx):
       +        self.txlist.append(tx)
       +
       +    def get_imported_addresses(self):
       +        return self.imported_addresses
       +
       +from electrumpersonalserver.deterministicwallet import DeterministicWallet
       +
       +class TestDeterministicWallet(DeterministicWallet):
       +    """Empty deterministic wallets"""
       +    def __init__(self):
       +        pass
       +
       +    def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys):
       +        return None #not overrun
       +
       +    def get_new_scriptpubkeys(self, change, count):
       +        pass
       +
       +def test_spk_to_address(spk):
       +    return spk + "-address"
       +
       +def assert_address_history_tx(address_history, spk, height, txid, subscribed):
       +    history_element = address_history[hashes.script_to_scripthash(spk)]
       +    assert history_element["history"][0]["height"] == height
       +    assert history_element["history"][0]["tx_hash"] == txid
       +    #fee always zero, its easier to test because otherwise you have
       +    # to use Decimal to stop float weirdness
       +    if height == 0:
       +        assert history_element["history"][0]["fee"] == 0
       +    assert history_element["subscribed"] == subscribed
       +
       +def test():
       +    debugf = lambda x: x
       +    logf = lambda x: x
       +    #empty deterministic wallets
       +    deterministic_wallets = [TestDeterministicWallet()]
       +    test_spk1 = "deadbeefdeadbeefdeadbeefdeadbeef"
       +    test_containing_block1 = "blockhash-placeholder1"
       +    test_paying_in_tx1 = {
       +        "txid": "placeholder-test-txid1",
       +        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk1}}],
       +        "address": test_spk_to_address(test_spk1),
       +        "category": "receive",
       +        "confirmations": 1,
       +        "blockhash": test_containing_block1,
       +        "hex": "placeholder-test-txhex1"
       +    }
       +    test_spk2 = "deadbeefdeadbeefdeadbeef"
       +    test_containing_block2 = "blockhash-placeholder2"
       +    test_paying_in_tx2 = {
       +        "txid": "placeholder-test-txid2",
       +        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk2}}],
       +        "address": test_spk_to_address(test_spk2),
       +        "category": "receive",
       +        "confirmations": 1,
       +        "blockhash": test_containing_block2,
       +        "hex": "placeholder-test-txhex2"
       +    }
       +
       +    ###single confirmed tx in wallet belonging to us, address history built
       +    rpc = TestJsonRpc([test_paying_in_tx1], [],
       +        {test_containing_block1: 420000})
       +    txmonitor1 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       +    assert txmonitor1.build_address_history([test_spk1])
       +    assert len(txmonitor1.address_history) == 1
       +    assert_address_history_tx(txmonitor1.address_history, spk=test_spk1,
       +        height=420000, txid=test_paying_in_tx1["txid"], subscribed=False)
       +
       +    ###two confirmed txes in wallet belonging to us, addr history built
       +    rpc = TestJsonRpc([test_paying_in_tx1, test_paying_in_tx2], [],
       +        {test_containing_block1: 1, test_containing_block2: 2})
       +    deterministic_wallets = [TestDeterministicWallet()]
       +    txmonitor2 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       +    assert txmonitor2.build_address_history([test_spk1, test_spk2])
       +    assert len(txmonitor2.address_history) == 2
       +    assert_address_history_tx(txmonitor2.address_history, spk=test_spk1,
       +        height=1, txid=test_paying_in_tx1["txid"], subscribed=False)
       +    assert_address_history_tx(txmonitor2.address_history, spk=test_spk2,
       +        height=2, txid=test_paying_in_tx2["txid"], subscribed=False)
       +
       +    ###one unconfirmed tx in wallet belonging to us, with confirmed inputs,
       +    ### addr history built, then tx confirms, not subscribed to address
       +    test_spk3 = "deadbeefdeadbeef"
       +    test_containing_block3 = "blockhash-placeholder3"
       +    input_utxo3 = {"txid": "placeholder-unknown-input-txid", "vout": 0,
       +        "value": 1, "confirmations": 1}
       +    test_paying_in_tx3 = {
       +        "txid": "placeholder-test-txid3",
       +        "vin": [input_utxo3],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk3}}],
       +        "address": test_spk_to_address(test_spk3),
       +        "category": "receive",
       +        "confirmations": 0,
       +        "blockhash": test_containing_block3,
       +        "hex": "placeholder-test-txhex3"
       +    }
       +    rpc = TestJsonRpc([test_paying_in_tx3], [input_utxo3],
       +        {test_containing_block3: 10})
       +    txmonitor3 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       +    assert txmonitor3.build_address_history([test_spk3])
       +    assert len(txmonitor3.address_history) == 1
       +    assert_address_history_tx(txmonitor3.address_history, spk=test_spk3,
       +        height=0, txid=test_paying_in_tx3["txid"], subscribed=False)
       +    assert len(list(txmonitor3.check_for_updated_txes())) == 0
       +    test_paying_in_tx3["confirmations"] = 1 #tx confirms
       +    #not subscribed so still only returns an empty list
       +    assert len(list(txmonitor3.check_for_updated_txes())) == 0
       +    assert_address_history_tx(txmonitor3.address_history, spk=test_spk3,
       +        height=10, txid=test_paying_in_tx3["txid"], subscribed=False)
       +
       +    ###build empty address history, subscribe one address
       +    ### an unconfirmed tx appears, then confirms
       +    test_spk4 = "deadbeefdeadbeefaa"
       +    test_containing_block4 = "blockhash-placeholder4"
       +    input_utxo4 = {"txid": "placeholder-unknown-input-txid", "vout": 0,
       +        "value": 1, "confirmations": 1}
       +    test_paying_in_tx4 = {
       +        "txid": "placeholder-test-txid4",
       +        "vin": [input_utxo4],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk4}}],
       +        "address": test_spk_to_address(test_spk4),
       +        "category": "receive",
       +        "confirmations": 0,
       +        "blockhash": test_containing_block4,
       +        "hex": "placeholder-test-txhex4"
       +    }
       +    rpc = TestJsonRpc([], [input_utxo4], {test_containing_block4: 10})
       +    txmonitor4 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       +    assert txmonitor4.build_address_history([test_spk4])
       +    assert len(txmonitor4.address_history) == 1
       +    sh4 = hashes.script_to_scripthash(test_spk4)
       +    assert len(txmonitor4.get_electrum_history(sh4)) == 0
       +    txmonitor4.subscribe_address(sh4)
       +    # unconfirm transaction appears
       +    assert len(list(txmonitor4.check_for_updated_txes())) == 0
       +    rpc.add_transaction(test_paying_in_tx4)
       +    assert len(list(txmonitor4.check_for_updated_txes())) == 1
       +    assert_address_history_tx(txmonitor4.address_history, spk=test_spk4,
       +        height=0, txid=test_paying_in_tx4["txid"], subscribed=True)
       +    # transaction confirms
       +    test_paying_in_tx4["confirmations"] = 1
       +    assert len(list(txmonitor4.check_for_updated_txes())) == 1
       +    assert_address_history_tx(txmonitor4.address_history, spk=test_spk4,
       +        height=10, txid=test_paying_in_tx4["txid"], subscribed=True)
       +
       +    ###transaction that has nothing to do with our wallet
       +    test_spk5 = "deadbeefdeadbeefbb"
       +    test_containing_block5 = "blockhash-placeholder5"
       +    test_paying_in_tx5 = {
       +        "txid": "placeholder-test-txid5",
       +        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk5}}],
       +        "address": test_spk_to_address(test_spk5),
       +        "category": "receive",
       +        "confirmations": 0,
       +        "blockhash": test_containing_block5,
       +        "hex": "placeholder-test-txhex5"
       +    }
       +    test_spk5_1 = "deadbeefdeadbeefcc"
       +    rpc = TestJsonRpc([test_paying_in_tx5], [], {test_containing_block4: 10})
       +    txmonitor5 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       +    assert txmonitor5.build_address_history([test_spk5_1])
       +    assert len(txmonitor5.address_history) == 1
       +    assert len(txmonitor5.get_electrum_history(hashes.script_to_scripthash(
       +        test_spk5_1))) == 0
       +
       +    ###transaction which arrives to an address which already has a tx on it
       +    test_spk6 = "deadbeefdeadbeefdd"
       +    test_containing_block6 = "blockhash-placeholder6"
       +    test_paying_in_tx6 = {
       +        "txid": "placeholder-test-txid6",
       +        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk6}}],
       +        "address": test_spk_to_address(test_spk6),
       +        "category": "receive",
       +        "confirmations": 1,
       +        "blockhash": test_containing_block6,
       +        "hex": "placeholder-test-txhex6"
       +    }
       +    test_paying_in_tx6_1 = {
       +        "txid": "placeholder-test-txid6_1",
       +        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk6}}],
       +        "address": test_spk_to_address(test_spk6),
       +        "category": "receive",
       +        "confirmations": 1,
       +        "blockhash": test_containing_block6,
       +        "hex": "placeholder-test-txhex6"
       +    }
       +    rpc = TestJsonRpc([test_paying_in_tx6], [], {test_containing_block6: 10})
       +    txmonitor6 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       +    assert txmonitor6.build_address_history([test_spk6])
       +    sh = hashes.script_to_scripthash(test_spk6)
       +    assert len(txmonitor6.get_electrum_history(sh)) == 1
       +    rpc.add_transaction(test_paying_in_tx6_1)
       +    assert len(txmonitor6.get_electrum_history(sh)) == 1
       +    txmonitor6.check_for_updated_txes()
       +    assert len(txmonitor6.get_electrum_history(sh)) == 2
       +
       +    ###transaction spending FROM one of our addresses
       +    test_spk7 = "deadbeefdeadbeefee"
       +    test_input_containing_block7 = "blockhash-input-placeholder7"
       +    test_input_tx7 = {
       +        "txid": "placeholder-input-test-txid7",
       +        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk7}}],
       +        "address": test_spk_to_address(test_spk7),
       +        "category": "send",
       +        "confirmations": 2,
       +        "blockhash": test_input_containing_block7,
       +        "hex": "placeholder-input-test-txhex7"
       +    }
       +    test_containing_block7 = "blockhash-placeholder7"
       +    test_paying_from_tx7 = {
       +        "txid": "placeholder-test-txid7",
       +        "vin": [{"txid": test_input_tx7["txid"], "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": "deadbeef"}}],
       +        "address": test_spk_to_address(test_spk7),
       +        "category": "receive",
       +        "confirmations": 1,
       +        "blockhash": test_containing_block7,
       +        "hex": "placeholder-test-txhex7"
       +    }
       +    rpc = TestJsonRpc([test_input_tx7, test_paying_from_tx7], [],
       +        {test_containing_block7: 9, test_input_containing_block7: 8})
       +    txmonitor7 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       +    assert txmonitor7.build_address_history([test_spk7])
       +    sh = hashes.script_to_scripthash(test_spk7)
       +    assert len(txmonitor7.get_electrum_history(sh)) == 2
       +
       +    ###transaction from one address to the other, both addresses in wallet
       +    test_spk8 = "deadbeefdeadbeefee"
       +    test_spk8_1 = "deadbeefdeadbeefff"
       +    test_input_containing_block8 = "blockhash-input-placeholder8"
       +    test_input_tx8 = {
       +        "txid": "placeholder-input-test-txid8",
       +        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk8}}],
       +        "address": test_spk_to_address(test_spk8),
       +        "category": "send",
       +        "confirmations": 2,
       +        "blockhash": test_input_containing_block8,
       +        "hex": "placeholder-input-test-txhex8"
       +    }
       +    test_containing_block8 = "blockhash-placeholder8"
       +    test_paying_from_tx8 = {
       +        "txid": "placeholder-test-txid8",
       +        "vin": [{"txid": test_input_tx8["txid"], "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk8_1}}],
       +        "address": test_spk_to_address(test_spk8),
       +        "category": "receive",
       +        "confirmations": 1,
       +        "blockhash": test_containing_block8,
       +        "hex": "placeholder-test-txhex8"
       +    }
       +    rpc = TestJsonRpc([test_input_tx8, test_paying_from_tx8], [],
       +        {test_containing_block8: 9, test_input_containing_block8: 8})
       +    txmonitor8 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       +    assert txmonitor8.build_address_history([test_spk8, test_spk8_1])
       +    assert len(txmonitor8.get_electrum_history(hashes.script_to_scripthash(
       +        test_spk8))) == 2
       +    assert len(txmonitor8.get_electrum_history(hashes.script_to_scripthash(
       +        test_spk8_1))) == 1
       +
       +    ###overrun gap limit so import address is needed
       +    test_spk9 = "deadbeefdeadbeef00"
       +    test_containing_block9 = "blockhash-placeholder9"
       +    test_paying_in_tx9 = {
       +        "txid": "placeholder-test-txid9",
       +        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       +        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk9}}],
       +        "address": test_spk_to_address(test_spk9),
       +        "category": "receive",
       +        "confirmations": 1,
       +        "blockhash": test_containing_block9,
       +        "hex": "placeholder-test-txhex9"
       +    }
       +    test_spk9_imported = "deadbeefdeadbeef11"
       +    class TestImportDeterministicWallet(DeterministicWallet):
       +        def __init__(self):
       +            pass
       +
       +        def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys):
       +            return {0: 1} #overrun by one
       +
       +        def get_new_scriptpubkeys(self, change, count):
       +            return [test_spk9_imported]
       +
       +    rpc = TestJsonRpc([], [], {test_containing_block9: 10})
       +    txmonitor9 = TransactionMonitor(rpc, [TestImportDeterministicWallet()],
       +        debugf, logf)
       +    assert txmonitor9.build_address_history([test_spk9])
       +    assert len(txmonitor9.address_history) == 1
       +    assert len(list(txmonitor9.check_for_updated_txes())) == 0
       +    assert len(txmonitor9.get_electrum_history(hashes.script_to_scripthash(
       +        test_spk9))) == 0
       +    rpc.add_transaction(test_paying_in_tx9)
       +    assert len(list(txmonitor9.check_for_updated_txes())) == 0
       +    assert len(txmonitor9.get_electrum_history(hashes.script_to_scripthash(
       +        test_spk9))) == 1
       +    assert len(txmonitor9.get_electrum_history(hashes.script_to_scripthash(
       +        test_spk9_imported))) == 0
       +    assert len(rpc.get_imported_addresses()) == 1
       +    assert rpc.get_imported_addresses()[0] == test_spk_to_address(
       +        test_spk9_imported)
       +
       +    #other possible stuff to test:
       +    #finding confirmed and unconfirmed tx, in that order, then both confirm
       +    #finding unconfirmed and confirmed tx, in that order, then both confirm
       +
       +    print("\nAll tests passed")
       +
       +if __name__ == "__main__":
       +    test()
       +
   DIR diff --git a/merkleproof.py b/merkleproof.py
       t@@ -1,270 +0,0 @@
       -
       -import bitcoin as btc
       -import binascii
       -from math import ceil, log
       -
       -from hashes import hash_encode, hash_decode, Hash, hash_merkle_root
       -
       -#lots of ideas and code taken from bitcoin core and breadwallet
       -#https://github.com/bitcoin/bitcoin/blob/master/src/merkleblock.h
       -#https://github.com/breadwallet/breadwallet-core/blob/master/BRMerkleBlock.c
       -
       -def calc_tree_width(height, txcount):
       -    """Efficently calculates the number of nodes at given merkle tree height"""
       -    return (txcount + (1 << height) - 1) >> height
       -
       -def decend_merkle_tree(hashes, flags, height, txcount, pos):
       -    """Function recursively follows the flags bitstring down into the
       -       tree, building up a tree in memory"""
       -    flag = next(flags)
       -    if height > 0:
       -        #non-txid node
       -        if flag:
       -            left = decend_merkle_tree(hashes, flags, height-1, txcount, pos*2)
       -            #bitcoin's merkle tree format has a rule that if theres an
       -            # odd number of nodes in then the tree, the last hash is duplicated
       -            #in the electrum format we must hash together the duplicate
       -            # tree branch
       -            if pos*2+1 < calc_tree_width(height-1, txcount):
       -                right = decend_merkle_tree(hashes, flags, height-1,
       -                    txcount, pos*2+1)
       -            else:
       -                if isinstance(left, tuple):
       -                    right = expand_tree_hashing(left)
       -                else:
       -                    right = left
       -            return (left, right)
       -        else:
       -            hs = next(hashes)
       -            return hs
       -    else:
       -        #txid node
       -        hs = next(hashes)
       -        if flag:
       -            #for the actual transaction, also store its position with a flag 
       -            return "tx:" + str(pos) + ":" + hs
       -        else:
       -            return hs
       -
       -def deserialize_core_format_merkle_proof(hash_list, flag_value, txcount):
       -    """Converts core's format for a merkle proof into a tree in memory"""
       -    tree_depth = int(ceil(log(txcount, 2)))
       -    hashes = iter(hash_list)
       -    #one-liner which converts the flags value to a list of True/False bits
       -    flags = (flag_value[i//8]&1 << i%8 != 0 for i in range(len(flag_value)*8))
       -    try:
       -        root_node = decend_merkle_tree(hashes, flags, tree_depth, txcount, 0)
       -        return root_node
       -    except StopIteration:
       -        raise ValueError
       -
       -def expand_tree_electrum_format_merkle_proof(node, result):
       -    """Recurse down into the tree, adding hashes to the result list
       -       in depth order"""
       -    left, right = node
       -    if isinstance(left, tuple):
       -        expand_tree_electrum_format_merkle_proof(left, result)
       -    if isinstance(right, tuple):
       -        expand_tree_electrum_format_merkle_proof(right, result)
       -    if not isinstance(left, tuple):
       -        result.append(left)
       -    if not isinstance(right, tuple):
       -        result.append(right)
       -
       -def get_node_hash(node):
       -    if node.startswith("tx"):
       -        return node.split(":")[2]
       -    else:
       -        return node
       -
       -def expand_tree_hashing(node):
       -    """Recurse down into the tree, hashing everything and
       -       returning root hash"""
       -    left, right = node
       -    if isinstance(left, tuple):
       -        hash_left = expand_tree_hashing(left)
       -    else:
       -        hash_left = get_node_hash(left)
       -    if isinstance(right, tuple):
       -        hash_right = expand_tree_hashing(right)
       -    else:
       -        hash_right = get_node_hash(right)
       -    return hash_encode(Hash(hash_decode(hash_left) + hash_decode(hash_right)))
       -
       -def convert_core_to_electrum_merkle_proof(proof):
       -    """Bitcoin Core and Electrum use different formats for merkle
       -       proof, this function converts from Core's format to Electrum's format"""
       -    proof = binascii.unhexlify(proof)
       -    pos = [0]
       -    def read_as_int(bytez):
       -        pos[0] += bytez
       -        return btc.decode(proof[pos[0] - bytez:pos[0]][::-1], 256)
       -    def read_var_int():
       -        pos[0] += 1
       -        val = btc.from_byte_to_int(proof[pos[0] - 1])
       -        if val < 253:
       -            return val
       -        return read_as_int(pow(2, val - 252))
       -    def read_bytes(bytez):
       -        pos[0] += bytez
       -        return proof[pos[0] - bytez:pos[0]]
       -
       -    merkle_root = proof[36:36+32]
       -    pos[0] = 80
       -    txcount = read_as_int(4)
       -    hash_count = read_var_int()
       -    hashes = [hash_encode(read_bytes(32)) for i in range(hash_count)]
       -    flags_count = read_var_int()
       -    flags = read_bytes(flags_count)
       -
       -    root_node = deserialize_core_format_merkle_proof(hashes, flags, txcount)
       -    #check special case of a tree of zero height, block with only coinbase tx
       -    if not isinstance(root_node, tuple):
       -        root_node = root_node[5:] #remove the "tx:0:"
       -        result = {"pos": 0, "merkle": [], "txid": root_node,
       -            "merkleroot": hash_encode(merkle_root)}
       -        return result
       -
       -    hashes_list = []
       -    expand_tree_electrum_format_merkle_proof(root_node, hashes_list)
       -    #remove the first or second element which is the txhash
       -    tx = hashes_list[0]
       -    if hashes_list[1].startswith("tx"):
       -        tx = hashes_list[1]
       -    assert(tx.startswith("tx"))
       -    hashes_list.remove(tx)
       -    #if the txhash was duplicated, that _is_ included in electrum's format
       -    if hashes_list[0].startswith("tx"):
       -        hashes_list[0] = tx.split(":")[2]
       -    tx_pos, txid = tx.split(":")[1:3]
       -    tx_pos = int(tx_pos)
       -    result = {"pos": tx_pos, "merkle": hashes_list, "txid": txid,
       -        "merkleroot": hash_encode(merkle_root)}
       -    return result
       -
       -merkle_proof_test_vectors = [
       -    #txcount 819, pos 5
       -    "0300000026e696fba00f0a43907239305eed9e55824e0e376636380f000000000000000" + 
       -    "04f8a2ce51d6c69988029837688cbfc2f580799fa1747456b9c80ab808c1431acd0b07f" + 
       -    "5543201618cadcfbf7330300000b0ff1e0050fed22ca360e0935e053b0fe098f6f9e090" + 
       -    "f5631013361620d964fe2fd88544ae10b40621e1cd24bb4306e3815dc237f77118a45d7" + 
       -    "5ada9ee362314b70573732bce59615a3bcc1bbacd04b33b7819198212216b5d62d75be5" + 
       -    "9221ada17ba4fb2476b689cccd3be54732fd5630832a94f11fa3f0dafd6f904d43219e0" + 
       -    "d7de110158446b5b598bd241f7b5df4da0ebc7d30e7748d487917b718df51c681174e6a" + 
       -    "bab8042cc7c1c436221c098f06a56134f9247a812126d675d69c82ba1c715cfc0cde462" + 
       -    "fd1fbe5dc87f6b8db2b9c060fcd59a20e7fe8e921c3676937a873ff88684f4be4d015f2" + 
       -    "4f26af6d2cf78335e9218bcceba4507d0b4ba6cb933aa01ef77ae5eb411893ec0f74b69" + 
       -    "590fb0f5118ac937c02ccd47e9d90be78becd11ecf854d7d268eeb479b74d137278c0a5" + 
       -    "017d29e90cd5b35a4680201824fb0eb4f404e20dfeaec4d50549030b7e7e220b02eb210" + 
       -    "5f3d2e8bcc94d547214a9d03ff1600",
       -    #txcount 47, pos 9
       -    "0100000053696a625fbd16df418575bce0c4148886c422774fca5fcab80100000000000" + 
       -    "01532bfe4f9c4f56cd141028e5b59384c133740174b74b1982c7f01020b90ce05577c67" + 
       -    "508bdb051a7ec2ef942f000000076cde2eb7efa90b36d48aed612e559ff2ba638d8d400" + 
       -    "b14b0c58df00c6a6c33b65dc8fa02f4ca56e1f4dcf17186fa9bbd990ce150b6e2dc9e9e" + 
       -    "56bb4f270fe56fde6bdd73a7a7e82767714862888e6b759568fb117674ad23050e29311" + 
       -    "97494d457efb72efdb9cb79cd4a435724908a0eb31ec7f7a67ee03837319e098b43edad" + 
       -    "3be9af75ae7b30db6f4f93ba0fdd941fdf70fe8cc38982e03bd292f5bd02f28137d343f" + 
       -    "908c7d6417379afe8349a257af3ca1f74f623be6a416fe1aa96a8f259983f2cf32121bc" + 
       -    "e203955a378b3b44f132ea6ab94c7829a6c3b360c9f8da8e74027701",
       -    #txcount 2582, pos 330
       -    "000000206365d5e1d8b7fdf0b846cfa902115c1b8ced9dd49cb17800000000000000000" + 
       -    "01032e829e1f9a5a09d0492f9cd3ec0762b7facea555989c3927d3d975fd4078c771849" + 
       -    "5a45960018edd3b9e0160a00000dfe856a7d5d77c23ebf85c68b5eb303d85e56491ed6d" + 
       -    "204372625d0b4383df5a44d6e46d2db09d936b9f5d0b53e0dbcb3efb7773d457369c228" + 
       -    "fd1ce6e11645e366a58b3fc1e8a7c916710ce29a87265a6729a3b221b47ea9c8e6f4870" + 
       -    "7b112b8d67e5cfb3db5f88b042dc49e4e5bc2e61c28e1e0fbcba4c741bb5c75cac58ca0" + 
       -    "4161a7377d70f3fd19a3e248ed918c91709b49afd3760f89ed2fefbcc9c23447ccb40a2" + 
       -    "be7aba22b07189b0bf90c62db48a9fe37227e12c7af8c1d4c22f9f223530dacdd5f3ad8" + 
       -    "50ad4badf16cc24049a65334f59bf28c15cecda1a4cf3f2937bd70ee84f66569ce8ef95" + 
       -    "1d50cca46d60337e6c697685b38ad217967bbe6801d03c44fcb808cd035be31888380a2" + 
       -    "df1be14b6ff100de83cab0dce250e2b40ca3b47e8309f848646bee63b6185c176d84f15" + 
       -    "46a482e7a65a87d1a2d0d5a2b683e2cae0520df1e3525a71d71e1f551abd7d238c3bcb4" + 
       -    "ecaeea7d5988745fa421a8604a99857426957a2ccfa7cd8df145aa8293701989dd20750" + 
       -    "5923fcb339843944ce3d21dc259bcda9c251ed90d4e55af2cf5b15432050084f513ac74" + 
       -    "c0bdd4b6046fb70100",
       -    #txcount 2861, pos 2860, last tx with many duplicated nodes down the tree
       -    "00000020c656c90b521a2bbca14174f2939b882a28d23d86144b0e00000000000000000" + 
       -    "0cf5185a8e369c3de5f15e039e777760994fd66184b619d839dace3aec9953fd6d86159" + 
       -    "5ac1910018ee097a972d0b0000078d20d71c3114dbf52bb13f2c18f987891e8854d2d29" + 
       -    "f61c0b3d3256afcef7c0b1e6f76d6431f93390ebd28dbb81ad7c8f08459e85efeb23cc7" + 
       -    "2df2c5612215bf53dd4ab3703886bc8c82cb78ba761855e495fb5dc371cd8fe25ae974b" + 
       -    "df42269e267caf898a9f34cbf2350eaaa4afbeaea70636b5a3b73682186817db5b33290" + 
       -    "bd5c696bd8d0322671ff70c5447fcd7bdc127e5b84350c6b14b5a3b86b424d7db38d39f" + 
       -    "171f57e255a31c6c53415e3d65408b6519a40aacc49cad8e70646d4cb0d23d4a63068e6" + 
       -    "c220efc8a2781e9e774efdd334108d7453043bd3c8070d0e5903ad5b07",
       -    #txcount 7, pos 6, duplicated entry in the last depth, at tx level
       -    "0100000056e02c6d3278c754e0699517834741f7c4ad3dcbfeb7803a346200000000000" + 
       -    "0af3bdd5dd465443fd003e9281455e60aae573dd4d46304d7ba17276ea33d506488cbb4" + 
       -    "4dacb5001b9ebb193b0700000003cd3abb2eb7583165f36b56add7268be9027ead4cc8f" + 
       -    "888ec650d3b1c1f4de28a0ff7c8b463b2042d09598f0e5e5905de362aa1cf75252adc22" + 
       -    "719b8e1bc969adcfbc4782b8eafc9352263770b91a0f189ae051cbe0e26046c2b14cf3d" + 
       -    "8be0bc40135",
       -    #txcount 6, pos 5, duplicated entry in the last-but-one depth
       -    "01000000299edfd28524eae4fb6012e4087afdb6e1b912db85e612374b0300000000000" + 
       -    "0e16572394f8578a47bf36e15cd16faa5e3b9e18805cf4e271ae4ef95aa8cea7eb31fa1" + 
       -    "4e4b6d0b1a42857d960600000003f52b45ed953924366dab3e92707145c78615b639751" + 
       -    "ecb7be1c5ecc09b592ed588ca0e15a89e9049a2dbcadf4d8362bd1f74a6972f176617b5" + 
       -    "8a5466c8a4121fc3e2d6fa66c8637b387ef190ab46d6e9c9dae4bbccd871c72372b3dbc" + 
       -    "6edefea012d",
       -    #txcount 5, pos 4, duplicated on the last and second last depth
       -    "010000004d891de57908d89e9e5585648978d7650adc67e856c2d8c18c1800000000000" + 
       -    "04746fd317bffecd4ffb320239caa06685bafe0c1b5463b24d636e45788796657843d1b" + 
       -    "4d4c86041be68355c40500000002d8e26c89c46477f2407d866d2badbd98e43e732a670" + 
       -    "e96001faf1744b27e5fdd018733d72e31a2d6a0d94f2a3b35fcc66fb110c40c5bbff82b" + 
       -    "f87606553d541d011d",
       -    #txcount 2739, pos 0, coinbase tx
       -    "000000209f283da030c6e6d0ff5087a87c430d140ed6b4564fa34d00000000000000000" + 
       -    "0ec1513723e3652c6b8e777c41eb267ad8dd2025e85228840f5cfca7ffe1fb331afff8a" + 
       -    "5af8e961175e0f7691b30a00000df403e21a4751fbd52457f535378ac2dcf111199e9ea" + 
       -    "6f78f6c2663cb99b58203438d8f3b26f7f2804668c1df7d394a4726363d4873b2d85b71" + 
       -    "2e44cf4f5e4f33f22a8f3a1672846bd7c4570c668e6ee12befda23bfa3d0fcd30b1b079" + 
       -    "19b01c40b1e31b6d34fcdbb99539d46eb97a3ae15386f1ab0f28ecacadd9fc3fa4ce49a" + 
       -    "1a1839d815229f54036c8a3035d91e80e8dc127b62032b4e652550b4fc0aee0f6e85a14" + 
       -    "307d85ed9dde62acff9a0f7e3b52370a10d6c83ec13a0b4a8fafe87af368a167d7e9b63" + 
       -    "3b84b6ea65f1ce5e8ccc1840be0a4dab0099e25afccc7f2fdbda54cd65ecbac8d9a550c" + 
       -    "108b4e18d3af59129d373fde4c80848858fd6f7fc1e27387a38833473ca8a47729fa6e1" + 
       -    "cc14b584c14dad768108ff18cc6acdc9c31d32dc71c3c80856664a3fff870fe419a59aa" + 
       -    "9033356590475d36086f0b3c0ece34c0f3756675c610fb980ff3363af6f9c0918a7c677" + 
       -    "23371849de9c1026515c2900a80b3aee4f2625c8f48cd5eb967560ee8ebe58a8d41c331" + 
       -    "f6d5199795735d4f0494bdf592d166fa291062733619f0f133605087365639de2d9d5d6" + 
       -    "921f4b4204ff1f0000",
       -    #txcount 1, pos 0, coinbase tx in an empty block, tree with height 1
       -    "010000000508085c47cc849eb80ea905cc7800a3be674ffc57263cf210c59d8d0000000" + 
       -    "0112ba175a1e04b14ba9e7ea5f76ab640affeef5ec98173ac9799a852fa39add320cd66" + 
       -    "49ffff001d1e2de5650100000001112ba175a1e04b14ba9e7ea5f76ab640affeef5ec98" + 
       -    "173ac9799a852fa39add30101",
       -    #txcount 2, pos 1, tree with height 2
       -    "010000004e24a2880cd72d9bde7502087bd3756819794dc7548f68dd68dc30010000000" + 
       -    "02793fce9cdf91b4f84760571bf6009d5f0ffaddbfdc9234ef58a036096092117b10f4b" + 
       -    "4cfd68011c903e350b0200000002ee50562fc6f995eff2df61be0d5f943bac941149aa2" + 
       -    "1aacb32adc130c0f17d6a2077a642b1eabbc5120e31566a11e2689aa4d39b01cce9a190" + 
       -    "2360baa5e4328e0105"
       -]
       -
       -def test():
       -    for proof in merkle_proof_test_vectors:
       -        try:
       -            electrum_proof = convert_core_to_electrum_merkle_proof(proof)
       -            #print(electrum_proof)
       -            implied_merkle_root = hash_merkle_root(
       -                electrum_proof["merkle"], electrum_proof["txid"],
       -                electrum_proof["pos"])
       -            assert implied_merkle_root == electrum_proof["merkleroot"]
       -        except ValueError:
       -            import traceback
       -            traceback.print_exc()
       -            assert 0
       -    print("All tests passed")
       -
       -'''
       -proof = ""
       -def chunks(d, n):
       -    return [d[x:x + n] for x in range(0, len(d), n)]
       -#print(proof)
       -print("\" + \n\"".join(chunks(proof, 71)))
       -'''
       -
       -if __name__ == "__main__":
       -    test()
       -
   DIR diff --git a/rescan-script.py b/rescan-script.py
       t@@ -1,7 +1,7 @@
        #! /usr/bin/python3
        
        from configparser import ConfigParser, NoSectionError
       -from jsonrpc import JsonRpc, JsonRpcError
       +from electrumpersonalserver.jsonrpc import JsonRpc, JsonRpcError
        from datetime import datetime
        import server
        
   DIR diff --git a/server.py b/server.py
       t@@ -1,13 +1,13 @@
        #! /usr/bin/python3
        
        import socket, time, json, datetime, struct, binascii, ssl, os.path, platform
       -import sys
        from configparser import ConfigParser, NoSectionError
        
       -from jsonrpc import JsonRpc, JsonRpcError
       -import hashes, merkleproof, deterministicwallet, transactionmonitor
       -
       -ADDRESSES_LABEL = "electrum-watchonly-addresses"
       +from electrumpersonalserver.jsonrpc import JsonRpc, JsonRpcError
       +import electrumpersonalserver.hashes as hashes
       +import electrumpersonalserver.merkleproof as merkleproof
       +import electrumpersonalserver.deterministicwallet as deterministicwallet
       +import electrumpersonalserver.transactionmonitor as transactionmonitor
        
        VERSION_NUMBER = "0.1"
        
       t@@ -304,7 +304,7 @@ def run_electrum_server(hostport, rpc, txmonitor, poll_interval_listening,
        
        def get_scriptpubkeys_to_monitor(rpc, config):
            imported_addresses = set(rpc.call("getaddressesbyaccount",
       -        [ADDRESSES_LABEL]))
       +        [transactionmonitor.ADDRESSES_LABEL]))
        
            deterministic_wallets = []
            for key in config.options("master-public-keys"):
       t@@ -373,22 +373,6 @@ def get_scriptpubkeys_to_monitor(rpc, config):
                for addr in watch_only_addresses])
            return False, spks_to_monitor, deterministic_wallets
        
       -def import_addresses(rpc, addrs):
       -    debug("importing addrs = " + str(addrs))
       -    log("Importing " + str(len(addrs)) + " addresses in total")
       -    addr_i = iter(addrs)
       -    notifications = 10
       -    for i in range(notifications):
       -        pc = int(100.0 * i / notifications)
       -        sys.stdout.write("[" + str(pc) + "%]... ")
       -        sys.stdout.flush()
       -        for j in range(int(len(addrs) / notifications)):
       -            rpc.call("importaddress", [next(addr_i), ADDRESSES_LABEL, False])
       -    for a in addr_i: #import the reminder of addresses
       -        rpc.call("importaddress", [a, ADDRESSES_LABEL, False])
       -    print("[100%]")
       -    log("Importing done")
       -
        def obtain_rpc_username_password(datadir):
            if len(datadir.strip()) == 0:
                debug("no datadir configuration, checking in default location")
       t@@ -446,7 +430,8 @@ def main():
            import_needed, relevant_spks_addrs, deterministic_wallets = \
                get_scriptpubkeys_to_monitor(rpc, config)
            if import_needed:
       -        import_addresses(rpc, relevant_spks_addrs)
       +        transactionmonitor.import_addresses(rpc, relevant_spks_addrs, debug,
       +            log)
                log("Done.\nIf recovering a wallet which already has existing " +
                    "transactions, then\nrun the rescan script. If you're confident " +
                    "that the wallets are new\nand empty then there's no need to " +
   DIR diff --git a/transactionmonitor.py b/transactionmonitor.py
       t@@ -1,705 +0,0 @@
       -
       -import time, pprint, math
       -from decimal import Decimal
       -
       -from jsonrpc import JsonRpcError
       -import server as s
       -import hashes
       -
       -#internally this code uses scriptPubKeys, it only converts to bitcoin addresses
       -# when importing to bitcoind or checking whether enough addresses have been
       -# imported
       -#the electrum protocol uses sha256(scriptpubkey) as a key for lookups
       -# this code calls them scripthashes
       -
       -#code will generate the first address from each deterministic wallet
       -# and check whether they have been imported into the bitcoin node
       -# if no then initial_import_count addresses will be imported, then exit
       -# if yes then initial_import_count addresses will be generated and extra
       -# addresses will be generated one-by-one, each time checking whether they have
       -# been imported into the bitcoin node
       -# when an address has been reached that has not been imported, that means
       -# we've reached the end, then rewind the deterministic wallet index by one
       -
       -#when a transaction happens paying to an address from a deterministic wallet
       -# lookup the position of that address, if its less than gap_limit then
       -# import more addresses
       -
       -class TransactionMonitor(object):
       -    """
       -    Class which monitors the bitcoind wallet for new transactions
       -    and builds a history datastructure for sending to electrum
       -    """
       -    def __init__(self, rpc, deterministic_wallets, debug, log):
       -        self.rpc = rpc
       -        self.deterministic_wallets = deterministic_wallets
       -        self.debug = debug
       -        self.log = log
       -        self.last_known_wallet_txid = None
       -        self.address_history = None
       -        self.unconfirmed_txes = None
       -
       -    def get_electrum_history_hash(self, scrhash):
       -        return hashes.get_status_electrum( ((h["tx_hash"], h["height"])
       -            for h in self.address_history[scrhash]["history"]) )
       -
       -    def get_electrum_history(self, scrhash):
       -        if scrhash in self.address_history:
       -            return self.address_history[scrhash]["history"]
       -        else:
       -            return None
       -
       -    def subscribe_address(self, scrhash):
       -        if scrhash in self.address_history:
       -            self.address_history[scrhash]["subscribed"] = True
       -            return True
       -        else:
       -            return False
       -
       -    def unsubscribe_all_addresses(self):
       -        for scrhash, his in self.address_history.items():
       -            his["subscribed"] = False
       -
       -    def build_address_history(self, monitored_scriptpubkeys):
       -        self.log("Building history with " + str(len(monitored_scriptpubkeys)) +
       -            " addresses")
       -        st = time.time()
       -        address_history = {}
       -        for spk in monitored_scriptpubkeys:
       -            address_history[hashes.script_to_scripthash(spk)] = {'history': [],
       -                'subscribed': False}
       -        wallet_addr_scripthashes = set(address_history.keys())
       -        #populate history
       -        #which is a blockheight-ordered list of ("txhash", height)
       -        #unconfirmed transactions go at the end as ("txhash", 0, fee)
       -        # 0=unconfirmed -1=unconfirmed with unconfirmed parents
       -
       -        BATCH_SIZE = 1000
       -        ret = list(range(BATCH_SIZE))
       -        t = 0
       -        count = 0
       -        obtained_txids = set()
       -        while len(ret) == BATCH_SIZE:
       -            ret = self.rpc.call("listtransactions", ["*", BATCH_SIZE, t, True])
       -            self.debug("listtransactions skip=" + str(t) + " len(ret)="
       -                + str(len(ret)))
       -            t += len(ret)
       -            for tx in ret:
       -                if "txid" not in tx or "category" not in tx:
       -                    continue
       -                if tx["category"] not in ("receive", "send"):
       -                    continue
       -                if tx["txid"] in obtained_txids:
       -                    continue
       -                self.debug("adding obtained tx=" + str(tx["txid"]))
       -                obtained_txids.add(tx["txid"])
       -
       -                #obtain all the addresses this transaction is involved with
       -                output_scriptpubkeys, input_scriptpubkeys, txd = \
       -                    self.get_input_and_output_scriptpubkeys(tx["txid"])
       -                output_scripthashes = [hashes.script_to_scripthash(sc)
       -                    for sc in output_scriptpubkeys]
       -                sh_to_add = wallet_addr_scripthashes.intersection(set(
       -                    output_scripthashes))
       -                input_scripthashes = [hashes.script_to_scripthash(sc)
       -                    for sc in input_scriptpubkeys]
       -                sh_to_add |= wallet_addr_scripthashes.intersection(set(
       -                    input_scripthashes))
       -                if len(sh_to_add) == 0:
       -                    continue
       -
       -                for wal in self.deterministic_wallets:
       -                    overrun_depths = wal.have_scriptpubkeys_overrun_gaplimit(
       -                        output_scriptpubkeys)
       -                    if overrun_depths != None:
       -                        self.log("ERROR: Not enough addresses imported.")
       -                        self.log("Delete wallet.dat and increase the value " +
       -                            "of `initial_import_count` in the file " + 
       -                            "`config.cfg` then reimport and rescan")
       -                        #TODO make it so users dont have to delete wallet.dat
       -                        # check whether all initial_import_count addresses are
       -                        # imported rather than just the first one
       -                        return False
       -                new_history_element = self.generate_new_history_element(tx, txd)
       -                for scripthash in sh_to_add:
       -                    address_history[scripthash][
       -                        "history"].append(new_history_element)
       -                count += 1
       -
       -        unconfirmed_txes = {}
       -        for scrhash, his in address_history.items():
       -            uctx = self.sort_address_history_list(his)
       -            for u in uctx:
       -                if u["tx_hash"] in unconfirmed_txes:
       -                    unconfirmed_txes[u["tx_hash"]].append(scrhash)
       -                else:
       -                    unconfirmed_txes[u["tx_hash"]] = [scrhash]
       -        self.debug("unconfirmed_txes = " + str(unconfirmed_txes))
       -        if len(ret) > 0:
       -            #txid doesnt uniquely identify transactions from listtransactions
       -            #but the tuple (txid, address) does
       -            self.last_known_wallet_txid = (ret[-1]["txid"], ret[-1]["address"])
       -        else:
       -            self.last_known_wallet_txid = None
       -        self.debug("last_known_wallet_txid = " + str(
       -            self.last_known_wallet_txid))
       -
       -        et = time.time()
       -        self.debug("address_history =\n" + pprint.pformat(address_history))
       -        self.log("Found " + str(count) + " txes. History built in " +
       -            str(et - st) + "sec")
       -        self.address_history = address_history
       -        self.unconfirmed_txes = unconfirmed_txes
       -        return True
       -
       -    def get_input_and_output_scriptpubkeys(self, txid):
       -        gettx = self.rpc.call("gettransaction", [txid])
       -        txd = self.rpc.call("decoderawtransaction", [gettx["hex"]])
       -        output_scriptpubkeys = [out["scriptPubKey"]["hex"]
       -            for out in txd["vout"]]
       -        input_scriptpubkeys = []
       -        for inn in txd["vin"]:
       -            try:
       -                wallet_tx = self.rpc.call("gettransaction", [inn["txid"]])
       -            except JsonRpcError:
       -                #wallet doesnt know about this tx, so the input isnt ours
       -                continue
       -            input_decoded = self.rpc.call("decoderawtransaction", [wallet_tx[
       -                "hex"]])
       -            script = input_decoded["vout"][inn["vout"]]["scriptPubKey"]["hex"]
       -            input_scriptpubkeys.append(script)
       -        return output_scriptpubkeys, input_scriptpubkeys, txd
       -
       -    def generate_new_history_element(self, tx, txd):
       -        if tx["confirmations"] == 0:
       -            unconfirmed_input = False
       -            total_input_value = 0
       -            for inn in txd["vin"]:
       -                utxo = self.rpc.call("gettxout", [inn["txid"], inn["vout"],
       -                    True])
       -                if utxo is None:
       -                    utxo = self.rpc.call("gettxout", [inn["txid"], inn["vout"],
       -                        False])
       -                    if utxo is None:
       -                        self.debug("utxo not found(!)")
       -                        #TODO detect this and figure out how to tell
       -                        # electrum that we dont know the fee
       -                total_input_value += int(Decimal(utxo["value"]) * Decimal(1e8))
       -                unconfirmed_input = (unconfirmed_input or
       -                    utxo["confirmations"] == 0)
       -            self.debug("total_input_value = " + str(total_input_value))
       -
       -            fee = total_input_value - sum([int(Decimal(out["value"])
       -                * Decimal(1e8)) for out in txd["vout"]])
       -            height = -1 if unconfirmed_input else 0
       -            new_history_element = ({"tx_hash": tx["txid"], "height": height,
       -                "fee": fee})
       -        else:
       -            blockheader = self.rpc.call("getblockheader", [tx['blockhash']])
       -            new_history_element = ({"tx_hash": tx["txid"],
       -                "height": blockheader["height"]})
       -        return new_history_element
       -
       -    def sort_address_history_list(self, his):
       -        unconfirm_txes = list(filter(lambda h:h["height"] == 0, his["history"]))
       -        confirm_txes = filter(lambda h:h["height"] != 0, his["history"])
       -        #TODO txes must be "in blockchain order"
       -        # the order they appear in the block
       -        # it might be "blockindex" in listtransactions and gettransaction
       -        #so must sort with key height+':'+blockindex
       -        #maybe check if any heights are the same then get the pos only for those
       -        #better way to do this is to have a separate dict that isnt in history
       -        # which maps txid => blockindex
       -        # and then sort by key height+":"+idx[txid]
       -        his["history"] = sorted(confirm_txes, key=lambda h:h["height"])
       -        his["history"].extend(unconfirm_txes)
       -        return unconfirm_txes
       -
       -    def check_for_updated_txes(self):
       -        updated_scrhashes1 = self.check_for_new_txes()
       -        updated_scrhashes2 = self.check_for_confirmations()
       -        updated_scrhashes = updated_scrhashes1 | updated_scrhashes2
       -        for ush in updated_scrhashes:
       -            his = self.address_history[ush]
       -            self.sort_address_history_list(his)
       -        if len(updated_scrhashes) > 0:
       -            self.debug("new tx address_history =\n"
       -                + pprint.pformat(self.address_history))
       -            self.debug("unconfirmed txes = " +
       -                pprint.pformat(self.unconfirmed_txes))
       -            self.debug("updated_scripthashes = " + str(updated_scrhashes))
       -        updated_scrhashes = filter(lambda sh:self.address_history[sh][
       -            "subscribed"], updated_scrhashes)
       -        return updated_scrhashes
       -
       -    def check_for_confirmations(self):
       -        confirmed_txes_scrhashes = []
       -        self.debug("check4con unconfirmed_txes = "
       -            + pprint.pformat(self.unconfirmed_txes))
       -        for uc_txid, scrhashes in self.unconfirmed_txes.items():
       -            tx = self.rpc.call("gettransaction", [uc_txid])
       -            self.debug("uc_txid=" + uc_txid + " => " + str(tx))
       -            if tx["confirmations"] == 0:
       -                continue #still unconfirmed
       -            self.log("A transaction confirmed: " + uc_txid)
       -            confirmed_txes_scrhashes.append((uc_txid, scrhashes))
       -            block = self.rpc.call("getblockheader", [tx["blockhash"]])
       -            for scrhash in scrhashes:
       -                #delete the old unconfirmed entry in address_history
       -                deleted_entries = [h for h in self.address_history[scrhash][
       -                    "history"] if h["tx_hash"] == uc_txid]
       -                for d_his in deleted_entries:
       -                    self.address_history[scrhash]["history"].remove(d_his)
       -                #create the new confirmed entry in address_history
       -                self.address_history[scrhash]["history"].append({"height":
       -                    block["height"], "tx_hash": uc_txid})
       -        updated_scrhashes = set()
       -        for tx, scrhashes in confirmed_txes_scrhashes:
       -            del self.unconfirmed_txes[tx]
       -            updated_scrhashes.update(set(scrhashes))
       -        return updated_scrhashes
       -
       -    def check_for_new_txes(self):
       -        MAX_TX_REQUEST_COUNT = 256 
       -        tx_request_count = 2
       -        max_attempts = int(math.log(MAX_TX_REQUEST_COUNT, 2))
       -        for i in range(max_attempts):
       -            self.debug("listtransactions tx_request_count="
       -                + str(tx_request_count))
       -            ret = self.rpc.call("listtransactions", ["*", tx_request_count, 0,
       -                True])
       -            ret = ret[::-1]
       -            if self.last_known_wallet_txid == None:
       -                recent_tx_index = len(ret) #=0 means no new txes
       -                break
       -            else:
       -                txid_list = [(tx["txid"], tx["address"]) for tx in ret]
       -                recent_tx_index = next((i for i, (txid, addr)
       -                    in enumerate(txid_list) if
       -                    txid == self.last_known_wallet_txid[0] and
       -                    addr == self.last_known_wallet_txid[1]), -1)
       -                if recent_tx_index != -1:
       -                    break
       -                tx_request_count *= 2
       -
       -        #TODO low priority: handle a user getting more than 255 new
       -        # transactions in 15 seconds
       -        self.debug("recent tx index = " + str(recent_tx_index) + " ret = " +
       -            str([(t["txid"], t["address"]) for t in ret]))
       -        if len(ret) > 0:
       -            self.last_known_wallet_txid = (ret[0]["txid"], ret[0]["address"])
       -            self.debug("last_known_wallet_txid = " + str(
       -                self.last_known_wallet_txid))
       -        assert(recent_tx_index != -1)
       -        if recent_tx_index == 0:
       -            return set()
       -        new_txes = ret[:recent_tx_index][::-1]
       -        self.debug("new txes = " + str(new_txes))
       -        obtained_txids = set()
       -        updated_scripthashes = []
       -        for tx in new_txes:
       -            if "txid" not in tx or "category" not in tx:
       -                continue
       -            if tx["category"] not in ("receive", "send"):
       -                continue
       -            if tx["txid"] in obtained_txids:
       -                continue
       -            obtained_txids.add(tx["txid"])
       -            output_scriptpubkeys, input_scriptpubkeys, txd = \
       -                self.get_input_and_output_scriptpubkeys(tx["txid"])
       -            matching_scripthashes = []
       -            for spk in (output_scriptpubkeys + input_scriptpubkeys):
       -                scripthash = hashes.script_to_scripthash(spk)
       -                if scripthash in self.address_history:
       -                    matching_scripthashes.append(scripthash)
       -            if len(matching_scripthashes) == 0:
       -                continue
       -
       -            for wal in self.deterministic_wallets:
       -                overrun_depths = wal.have_scriptpubkeys_overrun_gaplimit(
       -                    output_scriptpubkeys)
       -                if overrun_depths != None:
       -                    for change, import_count in overrun_depths.items():
       -                        spks = wal.get_new_scriptpubkeys(change, import_count)
       -                        for spk in spks:
       -                            self.address_history[hashes.script_to_scripthash(
       -                                spk)] =  {'history': [], 'subscribed': False}
       -                        new_addrs = [hashes.script_to_address(s, self.rpc)
       -                            for s in spks]
       -                        self.debug("importing " + str(len(spks)) +
       -                            " into change=" + str(change))
       -                        s.import_addresses(self.rpc, new_addrs)
       -
       -            updated_scripthashes.extend(matching_scripthashes)
       -            new_history_element = self.generate_new_history_element(tx, txd)
       -            self.log("Found new tx: " + str(new_history_element))
       -            for scrhash in matching_scripthashes:
       -                self.address_history[scrhash]["history"].append(
       -                    new_history_element)
       -                if new_history_element["height"] == 0:
       -                    if tx["txid"] in self.unconfirmed_txes:
       -                        self.unconfirmed_txes[tx["txid"]].append(scrhash)
       -                    else:
       -                        self.unconfirmed_txes[tx["txid"]] = [scrhash]
       -            #check whether gap limits have been overrun and import more addrs
       -        return set(updated_scripthashes)
       -
       -
       -## start tests here
       -
       -class TestJsonRpc(object):
       -    def __init__(self, txlist, utxoset, block_heights):
       -        self.txlist = txlist
       -        self.utxoset = utxoset
       -        self.block_heights = block_heights
       -        self.imported_addresses = []
       -
       -    def call(self, method, params):
       -        if method == "listtransactions":
       -            count = int(params[1])
       -            skip = int(params[2])
       -            return self.txlist[skip:skip + count]
       -        elif method == "gettransaction":
       -            for t in self.txlist:
       -                if t["txid"] == params[0]:
       -                    return t
       -            raise JsonRpcError({"code": None, "message": None})
       -        elif method == "decoderawtransaction":
       -            for t in self.txlist:
       -                if t["hex"] == params[0]:
       -                    return t
       -            assert 0
       -        elif method == "gettxout":
       -            for u in self.utxoset:
       -                if u["txid"] == params[0] and u["vout"] == params[1]:
       -                    return u
       -            assert 0
       -        elif method == "getblockheader":
       -            if params[0] not in self.block_heights:
       -                assert 0
       -            return {"height": self.block_heights[params[0]]}
       -        elif method == "decodescript":
       -            return {"addresses": [test_spk_to_address(params[0])]}
       -        elif method == "importaddress":
       -            self.imported_addresses.append(params[0])
       -        else:
       -            raise ValueError("unknown method in test jsonrpc")
       -
       -    def add_transaction(self, tx):
       -        self.txlist.append(tx)
       -
       -    def get_imported_addresses(self):
       -        return self.imported_addresses
       -
       -from deterministicwallet import DeterministicWallet
       -
       -class TestDeterministicWallet(DeterministicWallet):
       -    """Empty deterministic wallets"""
       -    def __init__(self):
       -        pass
       -
       -    def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys):
       -        return None #not overrun
       -
       -    def get_new_scriptpubkeys(self, change, count):
       -        pass
       -
       -def test_spk_to_address(spk):
       -    return spk + "-address"
       -
       -def assert_address_history_tx(address_history, spk, height, txid, subscribed):
       -    history_element = address_history[hashes.script_to_scripthash(spk)]
       -    assert history_element["history"][0]["height"] == height
       -    assert history_element["history"][0]["tx_hash"] == txid
       -    #fee always zero, its easier to test because otherwise you have
       -    # to use Decimal to stop float weirdness
       -    if height == 0:
       -        assert history_element["history"][0]["fee"] == 0
       -    assert history_element["subscribed"] == subscribed
       -
       -def test():
       -    debugf = lambda x: x
       -    logf = lambda x: x
       -    #empty deterministic wallets
       -    deterministic_wallets = [TestDeterministicWallet()]
       -    test_spk1 = "deadbeefdeadbeefdeadbeefdeadbeef"
       -    test_containing_block1 = "blockhash-placeholder1"
       -    test_paying_in_tx1 = {
       -        "txid": "placeholder-test-txid1",
       -        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk1}}],
       -        "address": test_spk_to_address(test_spk1),
       -        "category": "receive",
       -        "confirmations": 1,
       -        "blockhash": test_containing_block1,
       -        "hex": "placeholder-test-txhex1"
       -    }
       -    test_spk2 = "deadbeefdeadbeefdeadbeef"
       -    test_containing_block2 = "blockhash-placeholder2"
       -    test_paying_in_tx2 = {
       -        "txid": "placeholder-test-txid2",
       -        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk2}}],
       -        "address": test_spk_to_address(test_spk2),
       -        "category": "receive",
       -        "confirmations": 1,
       -        "blockhash": test_containing_block2,
       -        "hex": "placeholder-test-txhex2"
       -    }
       -
       -    ###single confirmed tx in wallet belonging to us, address history built
       -    rpc = TestJsonRpc([test_paying_in_tx1], [],
       -        {test_containing_block1: 420000})
       -    txmonitor1 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       -    assert txmonitor1.build_address_history([test_spk1])
       -    assert len(txmonitor1.address_history) == 1
       -    assert_address_history_tx(txmonitor1.address_history, spk=test_spk1,
       -        height=420000, txid=test_paying_in_tx1["txid"], subscribed=False)
       -
       -    ###two confirmed txes in wallet belonging to us, addr history built
       -    rpc = TestJsonRpc([test_paying_in_tx1, test_paying_in_tx2], [],
       -        {test_containing_block1: 1, test_containing_block2: 2})
       -    deterministic_wallets = [TestDeterministicWallet()]
       -    txmonitor2 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       -    assert txmonitor2.build_address_history([test_spk1, test_spk2])
       -    assert len(txmonitor2.address_history) == 2
       -    assert_address_history_tx(txmonitor2.address_history, spk=test_spk1,
       -        height=1, txid=test_paying_in_tx1["txid"], subscribed=False)
       -    assert_address_history_tx(txmonitor2.address_history, spk=test_spk2,
       -        height=2, txid=test_paying_in_tx2["txid"], subscribed=False)
       -
       -    ###one unconfirmed tx in wallet belonging to us, with confirmed inputs,
       -    ### addr history built, then tx confirms, not subscribed to address
       -    test_spk3 = "deadbeefdeadbeef"
       -    test_containing_block3 = "blockhash-placeholder3"
       -    input_utxo3 = {"txid": "placeholder-unknown-input-txid", "vout": 0,
       -        "value": 1, "confirmations": 1}
       -    test_paying_in_tx3 = {
       -        "txid": "placeholder-test-txid3",
       -        "vin": [input_utxo3],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk3}}],
       -        "address": test_spk_to_address(test_spk3),
       -        "category": "receive",
       -        "confirmations": 0,
       -        "blockhash": test_containing_block3,
       -        "hex": "placeholder-test-txhex3"
       -    }
       -    rpc = TestJsonRpc([test_paying_in_tx3], [input_utxo3],
       -        {test_containing_block3: 10})
       -    txmonitor3 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       -    assert txmonitor3.build_address_history([test_spk3])
       -    assert len(txmonitor3.address_history) == 1
       -    assert_address_history_tx(txmonitor3.address_history, spk=test_spk3,
       -        height=0, txid=test_paying_in_tx3["txid"], subscribed=False)
       -    assert len(list(txmonitor3.check_for_updated_txes())) == 0
       -    test_paying_in_tx3["confirmations"] = 1 #tx confirms
       -    #not subscribed so still only returns an empty list
       -    assert len(list(txmonitor3.check_for_updated_txes())) == 0
       -    assert_address_history_tx(txmonitor3.address_history, spk=test_spk3,
       -        height=10, txid=test_paying_in_tx3["txid"], subscribed=False)
       -
       -    ###build empty address history, subscribe one address
       -    ### an unconfirmed tx appears, then confirms
       -    test_spk4 = "deadbeefdeadbeefaa"
       -    test_containing_block4 = "blockhash-placeholder4"
       -    input_utxo4 = {"txid": "placeholder-unknown-input-txid", "vout": 0,
       -        "value": 1, "confirmations": 1}
       -    test_paying_in_tx4 = {
       -        "txid": "placeholder-test-txid4",
       -        "vin": [input_utxo4],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk4}}],
       -        "address": test_spk_to_address(test_spk4),
       -        "category": "receive",
       -        "confirmations": 0,
       -        "blockhash": test_containing_block4,
       -        "hex": "placeholder-test-txhex4"
       -    }
       -    rpc = TestJsonRpc([], [input_utxo4], {test_containing_block4: 10})
       -    txmonitor4 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       -    assert txmonitor4.build_address_history([test_spk4])
       -    assert len(txmonitor4.address_history) == 1
       -    sh4 = hashes.script_to_scripthash(test_spk4)
       -    assert len(txmonitor4.get_electrum_history(sh4)) == 0
       -    txmonitor4.subscribe_address(sh4)
       -    # unconfirm transaction appears
       -    assert len(list(txmonitor4.check_for_updated_txes())) == 0
       -    rpc.add_transaction(test_paying_in_tx4)
       -    assert len(list(txmonitor4.check_for_updated_txes())) == 1
       -    assert_address_history_tx(txmonitor4.address_history, spk=test_spk4,
       -        height=0, txid=test_paying_in_tx4["txid"], subscribed=True)
       -    # transaction confirms
       -    test_paying_in_tx4["confirmations"] = 1
       -    assert len(list(txmonitor4.check_for_updated_txes())) == 1
       -    assert_address_history_tx(txmonitor4.address_history, spk=test_spk4,
       -        height=10, txid=test_paying_in_tx4["txid"], subscribed=True)
       -
       -    ###transaction that has nothing to do with our wallet
       -    test_spk5 = "deadbeefdeadbeefbb"
       -    test_containing_block5 = "blockhash-placeholder5"
       -    test_paying_in_tx5 = {
       -        "txid": "placeholder-test-txid5",
       -        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk5}}],
       -        "address": test_spk_to_address(test_spk5),
       -        "category": "receive",
       -        "confirmations": 0,
       -        "blockhash": test_containing_block5,
       -        "hex": "placeholder-test-txhex5"
       -    }
       -    test_spk5_1 = "deadbeefdeadbeefcc"
       -    rpc = TestJsonRpc([test_paying_in_tx5], [], {test_containing_block4: 10})
       -    txmonitor5 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       -    assert txmonitor5.build_address_history([test_spk5_1])
       -    assert len(txmonitor5.address_history) == 1
       -    assert len(txmonitor5.get_electrum_history(hashes.script_to_scripthash(
       -        test_spk5_1))) == 0
       -
       -    ###transaction which arrives to an address which already has a tx on it
       -    test_spk6 = "deadbeefdeadbeefdd"
       -    test_containing_block6 = "blockhash-placeholder6"
       -    test_paying_in_tx6 = {
       -        "txid": "placeholder-test-txid6",
       -        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk6}}],
       -        "address": test_spk_to_address(test_spk6),
       -        "category": "receive",
       -        "confirmations": 1,
       -        "blockhash": test_containing_block6,
       -        "hex": "placeholder-test-txhex6"
       -    }
       -    test_paying_in_tx6_1 = {
       -        "txid": "placeholder-test-txid6_1",
       -        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk6}}],
       -        "address": test_spk_to_address(test_spk6),
       -        "category": "receive",
       -        "confirmations": 1,
       -        "blockhash": test_containing_block6,
       -        "hex": "placeholder-test-txhex6"
       -    }
       -    rpc = TestJsonRpc([test_paying_in_tx6], [], {test_containing_block6: 10})
       -    txmonitor6 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       -    assert txmonitor6.build_address_history([test_spk6])
       -    sh = hashes.script_to_scripthash(test_spk6)
       -    assert len(txmonitor6.get_electrum_history(sh)) == 1
       -    rpc.add_transaction(test_paying_in_tx6_1)
       -    assert len(txmonitor6.get_electrum_history(sh)) == 1
       -    txmonitor6.check_for_updated_txes()
       -    assert len(txmonitor6.get_electrum_history(sh)) == 2
       -
       -    ###transaction spending FROM one of our addresses
       -    test_spk7 = "deadbeefdeadbeefee"
       -    test_input_containing_block7 = "blockhash-input-placeholder7"
       -    test_input_tx7 = {
       -        "txid": "placeholder-input-test-txid7",
       -        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk7}}],
       -        "address": test_spk_to_address(test_spk7),
       -        "category": "send",
       -        "confirmations": 2,
       -        "blockhash": test_input_containing_block7,
       -        "hex": "placeholder-input-test-txhex7"
       -    }
       -    test_containing_block7 = "blockhash-placeholder7"
       -    test_paying_from_tx7 = {
       -        "txid": "placeholder-test-txid7",
       -        "vin": [{"txid": test_input_tx7["txid"], "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": "deadbeef"}}],
       -        "address": test_spk_to_address(test_spk7),
       -        "category": "receive",
       -        "confirmations": 1,
       -        "blockhash": test_containing_block7,
       -        "hex": "placeholder-test-txhex7"
       -    }
       -    rpc = TestJsonRpc([test_input_tx7, test_paying_from_tx7], [],
       -        {test_containing_block7: 9, test_input_containing_block7: 8})
       -    txmonitor7 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       -    assert txmonitor7.build_address_history([test_spk7])
       -    sh = hashes.script_to_scripthash(test_spk7)
       -    assert len(txmonitor7.get_electrum_history(sh)) == 2
       -
       -    ###transaction from one address to the other, both addresses in wallet
       -    test_spk8 = "deadbeefdeadbeefee"
       -    test_spk8_1 = "deadbeefdeadbeefff"
       -    test_input_containing_block8 = "blockhash-input-placeholder8"
       -    test_input_tx8 = {
       -        "txid": "placeholder-input-test-txid8",
       -        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk8}}],
       -        "address": test_spk_to_address(test_spk8),
       -        "category": "send",
       -        "confirmations": 2,
       -        "blockhash": test_input_containing_block8,
       -        "hex": "placeholder-input-test-txhex8"
       -    }
       -    test_containing_block8 = "blockhash-placeholder8"
       -    test_paying_from_tx8 = {
       -        "txid": "placeholder-test-txid8",
       -        "vin": [{"txid": test_input_tx8["txid"], "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk8_1}}],
       -        "address": test_spk_to_address(test_spk8),
       -        "category": "receive",
       -        "confirmations": 1,
       -        "blockhash": test_containing_block8,
       -        "hex": "placeholder-test-txhex8"
       -    }
       -    rpc = TestJsonRpc([test_input_tx8, test_paying_from_tx8], [],
       -        {test_containing_block8: 9, test_input_containing_block8: 8})
       -    txmonitor8 = TransactionMonitor(rpc, deterministic_wallets, debugf, logf)
       -    assert txmonitor8.build_address_history([test_spk8, test_spk8_1])
       -    assert len(txmonitor8.get_electrum_history(hashes.script_to_scripthash(
       -        test_spk8))) == 2
       -    assert len(txmonitor8.get_electrum_history(hashes.script_to_scripthash(
       -        test_spk8_1))) == 1
       -
       -    ###overrun gap limit so import address is needed
       -    test_spk9 = "deadbeefdeadbeef00"
       -    test_containing_block9 = "blockhash-placeholder9"
       -    test_paying_in_tx9 = {
       -        "txid": "placeholder-test-txid9",
       -        "vin": [{"txid": "placeholder-unknown-input-txid", "vout": 0}],
       -        "vout": [{"value": 1, "scriptPubKey": {"hex": test_spk9}}],
       -        "address": test_spk_to_address(test_spk9),
       -        "category": "receive",
       -        "confirmations": 1,
       -        "blockhash": test_containing_block9,
       -        "hex": "placeholder-test-txhex9"
       -    }
       -    test_spk9_imported = "deadbeefdeadbeef11"
       -    class TestImportDeterministicWallet(DeterministicWallet):
       -        def __init__(self):
       -            pass
       -
       -        def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys):
       -            return {0: 1} #overrun by one
       -
       -        def get_new_scriptpubkeys(self, change, count):
       -            return [test_spk9_imported]
       -
       -    rpc = TestJsonRpc([], [], {test_containing_block9: 10})
       -    txmonitor9 = TransactionMonitor(rpc, [TestImportDeterministicWallet()],
       -        debugf, logf)
       -    assert txmonitor9.build_address_history([test_spk9])
       -    assert len(txmonitor9.address_history) == 1
       -    assert len(list(txmonitor9.check_for_updated_txes())) == 0
       -    assert len(txmonitor9.get_electrum_history(hashes.script_to_scripthash(
       -        test_spk9))) == 0
       -    rpc.add_transaction(test_paying_in_tx9)
       -    assert len(list(txmonitor9.check_for_updated_txes())) == 0
       -    assert len(txmonitor9.get_electrum_history(hashes.script_to_scripthash(
       -        test_spk9))) == 1
       -    assert len(txmonitor9.get_electrum_history(hashes.script_to_scripthash(
       -        test_spk9_imported))) == 0
       -    assert len(rpc.get_imported_addresses()) == 1
       -    assert rpc.get_imported_addresses()[0] == test_spk_to_address(
       -        test_spk9_imported)
       -
       -    #other possible stuff to test:
       -    #finding confirmed and unconfirmed tx, in that order, then both confirm
       -    #finding unconfirmed and confirmed tx, in that order, then both confirm
       -
       -    print("\nAll tests passed")
       -
       -if __name__ == "__main__":
       -    test()
       -