URI: 
       tadded support for bech32 addresses, requires bitcoin core 0.16 - 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 f4e9cfba198c30c6f4209351a0966cdc2a36e4f0
   DIR parent 726ce096461b3d5321d01d2b60cd492cae10face
  HTML Author: chris-belcher <chris-belcher@users.noreply.github.com>
       Date:   Mon, 26 Feb 2018 16:15:36 +0000
       
       added support for bech32 addresses, requires bitcoin core 0.16
       
       Diffstat:
         M README.md                           |       8 ++++----
         M config.cfg_sample                   |       6 +++---
         M server.py                           |      28 +++++++++++++---------------
         M util.py                             |      53 +++++++++++++++++++++++--------
       
       4 files changed, 60 insertions(+), 35 deletions(-)
       ---
   DIR diff --git a/README.md b/README.md
       t@@ -21,8 +21,10 @@ See also the Electrum bitcoin wallet [website](https://electrum.org/).
        
        ## How To Use
        
       -This application requires python3 and a Bitcoin full node built with wallet
       -capability.
       +This application requires python3 and a Bitcoin full node version 0.16 or
       +higher. Make sure you
       +[verify the digital signatures](https://bitcoin.stackexchange.com/questions/50185/how-to-verify-bitcoin-core-release-signing-keys)
       +of any binaries before running them, or compile from source.
        
        Download the latest release or clone the git repository. Enter the directory
        and rename the file `config.cfg_sample` to `config.cfg`, edit this file to
       t@@ -66,8 +68,6 @@ features such as:
        * Deterministic wallets and master public keys are not supported. Addresses
          must be imported individually.
        
       -* Bech32 bitcoin addresses are not supported.
       -
        * The Electrum server protocol has a caveat about multiple transactions included
          in the same block. So there may be weird behaviour if that happens.
        
   DIR diff --git a/config.cfg_sample b/config.cfg_sample
       t@@ -5,12 +5,12 @@
        [wallets]
        ## Add addresses to this section
        
       -# These are just random addresses I found on a blockchain explorer
       +# These are just random example addresses found on a blockchain explorer
        
        # A key can be anything
        addr = 1DuqpoeTB9zLvVCXQG53VbMxvMkijk494n
        # A comma separated list is also accepted
       -my_test_addresses = 1KKszdQEpXgSY4EvsnGcGEzbQFmdcFwuNS,1EuEVUtQQ8hmuMHNdMdjLwjpkm6Ef7RYVk
       +my_test_addresses = 1KKszdQEpXgSY4EvsnGcGEzbQFmdcFwuNS,1EuEVUtQQ8hmuMHNdMdjLwjpkm6Ef7RYVk,bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
        # And space separated
        more_test_addresses = 3Hh7QujVLqz11tiQsnUE5CSL16WEHBmiyR 1Arzu6mWZuXGTF9yR2hZhMgBJgu1Xh2bNC 1PXRLo1FQoZyF1Jhnz4qbG5x8Bo3pFpybz
        
       t@@ -23,7 +23,7 @@ password = password
        #how often in seconds to poll for new transactions when electrum not connected
        poll_interval_listening = 30
        #how often in seconds to poll for new transactions when electrum is connected
       -poll_interval_connected = 5
       +poll_interval_connected = 2
        
        [electrum-server]
        #0.0.0.0 to accept connections from any IP
   DIR diff --git a/server.py b/server.py
       t@@ -57,7 +57,6 @@ from decimal import Decimal
        
        from jsonrpc import JsonRpc, JsonRpcError
        import util
       -import bitcoin as btc
        
        ADDRESSES_LABEL = "electrum-watchonly-addresses"
        
       t@@ -327,18 +326,17 @@ def run_electrum_server(hostport, rpc, address_history, unconfirmed_txes,
        
        def get_input_and_output_scriptpubkeys(rpc, txid):
            gettx = rpc.call("gettransaction", [txid])
       -    txd = btc.deserialize(gettx["hex"])
       -    output_scriptpubkeys = [sc['script'] for sc in txd['outs']]
       +    txd = rpc.call("decoderawtransaction", [gettx["hex"]])
       +    output_scriptpubkeys = [out["scriptPubKey"]["hex"] for out in txd["vout"]]
            input_scriptpubkeys = []
       -    for ins in txd["ins"]:
       +    for inn in txd["vin"]:
                try:
       -            wallet_tx = rpc.call("gettransaction", [ins["outpoint"][
       -                "hash"]])
       +            wallet_tx = rpc.call("gettransaction", [inn["txid"]])
                except JsonRpcError:
                    #wallet doesnt know about this tx, so the input isnt ours
                    continue
       -        script = btc.deserialize(str(wallet_tx["hex"]))["outs"][ins[
       -            "outpoint"]["index"]]["script"]
       +        input_decoded = rpc.call("decoderawtransaction", [wallet_tx["hex"]])
       +        script = input_decoded["vout"][inn["vout"]]["scriptPubKey"]["hex"]
                input_scriptpubkeys.append(script)
            return output_scriptpubkeys, input_scriptpubkeys, txd
        
       t@@ -346,12 +344,10 @@ def generate_new_history_element(rpc, tx, txd):
            if tx["confirmations"] == 0:
                unconfirmed_input = False
                total_input_value = 0
       -        for ins in txd["ins"]:
       -            utxo = rpc.call("gettxout", [ins["outpoint"]["hash"],
       -                ins["outpoint"]["index"], True])
       +        for inn in txd["vin"]:
       +            utxo = rpc.call("gettxout", [inn["txid"], inn["vout"], True])
                    if utxo is None:
       -                utxo = rpc.call("gettxout", [ins["outpoint"]["hash"],
       -                    ins["outpoint"]["index"], False])
       +                utxo = rpc.call("gettxout", [inn["txid"], inn["vout"], False])
                        if utxo is None:
                            debug("utxo not found(!)")
                            #TODO detect this and figure out how to tell
       t@@ -360,7 +356,8 @@ def generate_new_history_element(rpc, tx, txd):
                    unconfirmed_input = unconfirmed_input or utxo["confirmations"] == 0
                debug("total_input_value = " + str(total_input_value))
        
       -        fee = total_input_value - sum([sc["value"] for sc in txd["outs"]])
       +        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})
       t@@ -505,7 +502,7 @@ def build_address_history_index(rpc, wallet_addresses):
            st = time.time()
            address_history = {}
            for addr in wallet_addresses:
       -        scripthash = util.address_to_scripthash(addr)
       +        scripthash = util.address_to_scripthash(addr, rpc)
                address_history[scripthash] = {'addr': addr, 'history': [],
                    'subscribed': False}
            wallet_addr_scripthashes = set(address_history.keys())
       t@@ -530,6 +527,7 @@ def build_address_history_index(rpc, wallet_addresses):
                        continue
                    if tx["txid"] in obtained_txids:
                        continue
       +            debug("adding obtained tx=" + str(tx["txid"]))
                    obtained_txids.add(tx["txid"])
        
                    #obtain all the addresses this transaction is involved with
   DIR diff --git a/util.py b/util.py
       t@@ -31,9 +31,11 @@ def script_to_scripthash(script):
            h = sha256(bytes.fromhex(script))[0:32]
            return bh2u(bytes(reversed(h)))
        
       -def address_to_scripthash(addr):
       -    script = btc.address_to_script(addr)
       -    return script_to_scripthash(script)
       +def address_to_script(addr, rpc):
       +    return rpc.call("validateaddress", [addr])["scriptPubKey"]
       +
       +def address_to_scripthash(addr, rpc):
       +    return script_to_scripthash(address_to_script(addr, rpc))
        
        #the 'result' field in the blockchain.scripthash.subscribe method
        # reply uses this as a summary of the address
       t@@ -66,8 +68,10 @@ def hash_merkle_root(merkle_s, target_hash, pos):
        
        
        def script_to_address(script):
       +    #TODO why is this even here? its not used anywhere, maybe old code
            #TODO bech32 addresses
       -    #TODO testnet, although everything uses scripthash so the address vbyte doesnt matter
       +    #TODO testnet, although everything uses scripthash so the address
       +    #     vbyte doesnt matter
            return btc.script_to_address(script, 0x00)
        
        def calc_tree_width(height, txcount):
       t@@ -85,23 +89,27 @@ def decend_merkle_tree(hashes, flags, height, txcount, pos):
                    left = decend_merkle_tree(hashes, flags, height-1, txcount, pos*2)
                    #bitcoin has a rule that if theres an odd number of nodes in
                    # the merkle 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:
       -                right = left
       -                #TODO decend down one branch and hash it up, place in right
       +                if isinstance(left, tuple):
       +                    right = expand_tree_hashing(left)
       +                else:
       +                    right = left
                    return (left, right)
                else:
                    hs = next(hashes)
       -            hs = hs[:4] + '...' + hs[-4:]
       -            print(hs)
       +            #hs = hs[:4] + '...' + hs[-4:]
       +            #print(hs)
                    return hs
            else:
                #txid node
                hs = next(hashes)
       -        hs = hs[:4] + '...' + hs[-4:]
       -        print(hs)
       +        #hs = hs[:4] + '...' + hs[-4:]
       +        #print(hs)
                if flag:
                    return "tx:" + str(pos) + ":" + hs
                else:
       t@@ -130,6 +138,25 @@ def expand_tree_electrum_format(node, result):
            if not isinstance(right, tuple):
                result.append(right)
        
       +def deserialize_hash_node(node):
       +    if node.startswith("tx"):
       +        return node.split(":")[2]
       +    else:
       +        return node
       +
       +#recurse down into the tree, hashing everything and returning root hash
       +def expand_tree_hashing(node):
       +    left, right = node
       +    if isinstance(left, tuple):
       +        hash_left = expand_tree_hashing(left)
       +    else:
       +        hash_left = deserialize_hash_node(left)
       +    if isinstance(right, tuple):
       +        hash_right = expand_tree_hashing(right)
       +    else:
       +        hash_right = deserialize_hash_node(right)
       +    return hash_encode(Hash(hash_decode(hash_left) + hash_decode(hash_right)))
       +
        #https://github.com/bitcoin/bitcoin/blob/master/src/merkleblock.h
        #https://github.com/breadwallet/breadwallet-core/blob/master/BRMerkleBlock.c
        def convert_core_to_electrum_merkle_proof(proof):
       t@@ -279,20 +306,19 @@ merkle_test_vectors = [
parazyd.org:70 /git/electrum-personal-server/commit/f4e9cfba198c30c6f4209351a0966cdc2a36e4f0.gph:237: line too long