URI: 
       tsupport native segwit transactions - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit d9f2edf6b0c8d8e1b5219bfa5cfeac903b6a2561
   DIR parent 68873d92f9973b3d6befe9d2b69f67be71217501
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Fri,  1 Sep 2017 14:15:54 +0200
       
       support native segwit transactions
       
       Diffstat:
         M lib/base_wizard.py                  |       2 +-
         M lib/bitcoin.py                      |      17 +++++++++++------
         M lib/commands.py                     |       3 ++-
         M lib/keystore.py                     |       2 ++
         M lib/transaction.py                  |     119 +++++++++++++++++++++----------
         M lib/wallet.py                       |      38 +++++++++++++++++--------------
       
       6 files changed, 120 insertions(+), 61 deletions(-)
       ---
   DIR diff --git a/lib/base_wizard.py b/lib/base_wizard.py
       t@@ -357,7 +357,7 @@ class BaseWizard(object):
        
            def create_seed(self):
                from . import mnemonic
       -        self.seed_type = 'segwit' if bitcoin.TESTNET and self.config.get('segwit') else 'standard'
       +        self.seed_type = 'segwit' if self.config.get('segwit') else 'standard'
                seed = mnemonic.Mnemonic('en').make_seed(self.seed_type)
                self.opt_bip39 = False
                f = lambda x: self.request_passphrase(seed, x)
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -251,7 +251,7 @@ def seed_type(x):
                return 'old'
            elif is_new_seed(x):
                return 'standard'
       -    elif TESTNET and is_new_seed(x, version.SEED_PREFIX_SW):
       +    elif is_new_seed(x, version.SEED_PREFIX_SW):
                return 'segwit'
            elif is_new_seed(x, version.SEED_PREFIX_2FA):
                return '2fa'
       t@@ -307,16 +307,21 @@ def b58_address_to_hash160(addr):
        def hash160_to_p2pkh(h160):
            return hash160_to_b58_address(h160, ADDRTYPE_P2PKH)
        
       -
        def hash160_to_p2sh(h160):
            return hash160_to_b58_address(h160, ADDRTYPE_P2SH)
        
       -
        def public_key_to_p2pkh(public_key):
            return hash160_to_p2pkh(hash_160(public_key))
        
       -def hash160_to_segwit_addr(h160):
       -    return segwit_addr.encode(SEGWIT_HRP, 0, h160)
       +def hash_to_segwit_addr(h):
       +    return segwit_addr.encode(SEGWIT_HRP, 0, h)
       +
       +def public_key_to_p2wpkh(public_key):
       +    return hash_to_segwit_addr(hash_160(public_key))
       +
       +def script_to_p2wsh(script):
       +    return hash_to_segwit_addr(sha256(bfh(script)))
       +
        
        def address_to_script(addr):
            if is_segwit_address(addr):
       t@@ -838,7 +843,7 @@ def deserialize_xkey(xkey, prv):
            c = xkey[13:13+32]
            header = XPRV_HEADER if prv else XPUB_HEADER
            xtype = int('0x' + bh2u(xkey[0:4]), 16) - header
       -    if xtype not in ([0, 1] if TESTNET else [0]):
       +    if xtype not in [0, 1]:
                raise BaseException('Invalid header')
            n = 33 if prv else 32
            K_or_k = xkey[13+n:]
   DIR diff --git a/lib/commands.py b/lib/commands.py
       t@@ -163,7 +163,8 @@ class Commands:
            def make_seed(self, nbits=132, entropy=1, language=None):
                """Create a seed"""
                from .mnemonic import Mnemonic
       -        s = Mnemonic(language).make_seed('standard', nbits, custom_entropy=entropy)
       +        t = 'segwit' if self.config.get('segwit') else 'standard'
       +        s = Mnemonic(language).make_seed(t, nbits, custom_entropy=entropy)
                return s
        
            @command('')
   DIR diff --git a/lib/keystore.py b/lib/keystore.py
       t@@ -701,6 +701,8 @@ def from_seed(seed, passphrase):
                bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase)
                xtype = 0 if t == 'standard' else 1
                keystore.add_xprv_from_seed(bip32_seed, xtype, "m/")
       +    else:
       +        raise BaseException(t)
            return keystore
        
        def from_private_key_list(text):
   DIR diff --git a/lib/transaction.py b/lib/transaction.py
       t@@ -116,6 +116,19 @@ class BCDataStream(object):
            def write_int64(self, val): return self._write_num('<q', val)
            def write_uint64(self, val): return self._write_num('<Q', val)
        
       +    def read_push_size(self):
       +        size = self.input[self.read_cursor]
       +        self.read_cursor += 1
       +        if size < 0x4c:
       +            return size
       +        if size == 0x4c:
       +            nsize = self.read_bytes(1)[0]
       +        elif size == 0x4d:
       +            nsize = self._read_num('<H')
       +        elif size == 0x4e:
       +            nsize = self._read_num('<I')
       +        return nsize
       +
            def read_compact_size(self):
                size = self.input[self.read_cursor]
                self.read_cursor += 1
       t@@ -309,9 +322,6 @@ def parse_scriptSig(d, _bytes):
                    d['address'] = bitcoin.hash160_to_p2sh(bitcoin.hash_160(item))
                    d['type'] = 'p2wpkh-p2sh'
                    d['redeemScript'] = redeemScript
       -            d['x_pubkeys'] = ["(witness)"]
       -            d['pubkeys'] = ["(witness)"]
       -            d['signatures'] = ['(witness)']
                    d['num_sig'] = 1
                else:
                    # payto_pubkey
       t@@ -350,7 +360,19 @@ def parse_scriptSig(d, _bytes):
                print_error("cannot find address in input script", bh2u(_bytes))
                return
            x_sig = [bh2u(x[1]) for x in decoded[1:-1]]
       -    dec2 = [ x for x in script_GetOp(decoded[-1][1]) ]
       +    m, n, x_pubkeys, pubkeys, redeemScript = parse_redeemScript(decoded[-1][1])
       +    # write result in d
       +    d['type'] = 'p2sh'
       +    d['num_sig'] = m
       +    d['signatures'] = parse_sig(x_sig)
       +    d['x_pubkeys'] = x_pubkeys
       +    d['pubkeys'] = pubkeys
       +    d['redeemScript'] = redeemScript
       +    d['address'] = hash160_to_p2sh(hash_160(bfh(redeemScript)))
       +
       +
       +def parse_redeemScript(s):
       +    dec2 = [ x for x in script_GetOp(s) ]
            m = dec2[0][0] - opcodes.OP_1 + 1
            n = dec2[-2][0] - opcodes.OP_1 + 1
            op_m = opcodes.OP_1 + m - 1
       t@@ -362,15 +384,7 @@ def parse_scriptSig(d, _bytes):
            x_pubkeys = [bh2u(x[1]) for x in dec2[1:-2]]
            pubkeys = [safe_parse_pubkey(x) for x in x_pubkeys]
            redeemScript = multisig_script(pubkeys, m)
       -    # write result in d
       -    d['type'] = 'p2sh'
       -    d['num_sig'] = m
       -    d['signatures'] = parse_sig(x_sig)
       -    d['x_pubkeys'] = x_pubkeys
       -    d['pubkeys'] = pubkeys
       -    d['redeemScript'] = redeemScript
       -    d['address'] = hash160_to_p2sh(hash_160(bfh(redeemScript)))
       -
       +    return m, n, x_pubkeys, pubkeys, redeemScript
        
        def get_address_from_output_script(_bytes):
            decoded = [x for x in script_GetOp(_bytes)]
       t@@ -395,7 +409,7 @@ def get_address_from_output_script(_bytes):
            # segwit address
            match = [ opcodes.OP_0, opcodes.OP_PUSHDATA4 ]
            if match_decoded(decoded, match):
       -        return TYPE_ADDRESS, hash160_to_segwit_addr(decoded[1][1])
       +        return TYPE_ADDRESS, hash_to_segwit_addr(decoded[1][1])
        
            return TYPE_SCRIPT, bh2u(_bytes)
        
       t@@ -406,7 +420,6 @@ def parse_input(vds):
            prevout_n = vds.read_uint32()
            scriptSig = vds.read_bytes(vds.read_compact_size())
            sequence = vds.read_uint32()
       -    d['scriptSig'] = bh2u(scriptSig)
            d['prevout_hash'] = prevout_hash
            d['prevout_n'] = prevout_n
            d['sequence'] = sequence
       t@@ -420,12 +433,30 @@ def parse_input(vds):
                d['type'] = 'unknown'
                d['num_sig'] = 0
                if scriptSig:
       -            parse_scriptSig(d, scriptSig)
       +            if len(scriptSig) == 8:
       +                d['value'] = struct.unpack_from('<Q', scriptSig, 0)[0]
       +                d['scriptSig'] = ''
       +            else:
       +                d['scriptSig'] = bh2u(scriptSig)
       +                parse_scriptSig(d, scriptSig)
            return d
        
       -def parse_witness(vds):
       +
       +def parse_witness(vds, txin):
            n = vds.read_compact_size()
       -    return list(vds.read_bytes(vds.read_compact_size()) for i in range(n))
       +    w = list(bh2u(vds.read_bytes(vds.read_push_size())) for i in range(n))
       +    if n > 2:
       +        txin['num_sig'] = n - 2
       +        txin['signatures'] = parse_sig(w[1:-1])
       +        m, n, x_pubkeys, pubkeys, witnessScript = parse_redeemScript(bfh(w[-1]))
       +        txin['x_pubkeys'] = x_pubkeys
       +        txin['pubkeys'] = pubkeys
       +        txin['witnessScript'] = witnessScript
       +    else:
       +        txin['num_sig'] = 1
       +        txin['pubkeys'] = [ w[-1] ]
       +        txin['signatures'] = parse_sig([w[:-1]])
       +
        
        def parse_output(vds, i):
            d = {}
       t@@ -451,19 +482,24 @@ def deserialize(raw):
                n_vin = vds.read_compact_size()
            d['inputs'] = [parse_input(vds) for i in range(n_vin)]
            n_vout = vds.read_compact_size()
       -    d['outputs'] = [parse_output(vds,i) for i in range(n_vout)]
       +    d['outputs'] = [parse_output(vds, i) for i in range(n_vout)]
            if is_segwit:
       -        d['witness'] = [parse_witness(vds) for i in range(n_vin)]
       +        for i in range(n_vin):
       +            txin = d['inputs'][i]
       +            parse_witness(vds, txin)
       +            if not txin.get('scriptSig'):
       +                if txin['num_sig'] == 1:
       +                    txin['type'] = 'p2wpkh'
       +                    txin['address'] = bitcoin.public_key_to_p2wpkh(bfh(txin['pubkeys'][0]))
       +                else:
       +                    txin['type'] = 'p2wsh'
       +                    txin['address'] = bitcoin.script_to_p2wsh(txin['witnessScript'])
            d['lockTime'] = vds.read_uint32()
            return d
        
        
        # pay & redeem scripts
        
       -
       -
       -
       -
        def segwit_script(pubkey):
            pubkey = safe_parse_pubkey(pubkey)
            pkh = bh2u(hash_160(bfh(pubkey)))
       t@@ -536,12 +572,7 @@ class Transaction:
                for i, txin in enumerate(self.inputs()):
                    pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin)
                    sigs1 = txin.get('signatures')
       -            if d.get('witness') is None:
       -                sigs2 = d['inputs'][i].get('signatures')
       -            else:
       -                # signatures are in the witnesses.  But the last item is
       -                # the pubkey or the multisig script, so skip that.
       -                sigs2 = d['witness'][i][:-1]
       +            sigs2 = d['inputs'][i].get('signatures')
                    for sig in sigs2:
                        if sig in sigs1:
                            continue
       t@@ -622,12 +653,18 @@ class Transaction:
            @classmethod
            def serialize_witness(self, txin):
                pubkeys, sig_list = self.get_siglist(txin)
       -        n = len(pubkeys) + len(sig_list)
       -        return var_int(n) + ''.join(push_script(x) for x in sig_list) + ''.join(push_script(x) for x in pubkeys)
       +        if txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']:
       +            n = 2
       +            return var_int(n) + push_script(sig_list[0]) + push_script(pubkeys[0])
       +        elif txin['type'] in ['p2wsh', 'p2wsh-p2sh']:
       +            n = len(sig_list) + 2
       +            # fixme: witness script must be decided by wallet
       +            witness_script = multisig_script(pubkeys, txin['num_sig'])
       +            return var_int(n) + '00' + ''.join(push_script(x) for x in sig_list) + push_script(witness_script)
        
            @classmethod
            def is_segwit_input(self, txin):
       -        return txin['type'] in ['p2wpkh-p2sh']
       +        return txin['type'] in ['p2wpkh', 'p2wpkh-p2sh', 'p2wsh', 'p2wsh-p2sh']
        
            @classmethod
            def input_script(self, txin, estimate_size=False):
       t@@ -645,7 +682,10 @@ class Transaction:
                    script += push_script(redeem_script)
                elif _type == 'p2pkh':
                    script += push_script(pubkeys[0])
       -        elif _type == 'p2wpkh-p2sh':
       +        elif _type in ['p2wpkh', 'p2wsh']:
       +            # if it is not complete we store the value
       +            return '' if self.is_txin_complete(txin) or estimate_size else int_to_hex(txin['value'], 8)
       +        elif _type in ['p2wpkh-p2sh', 'p2wsh-p2sh']:
                    redeem_script = txin.get('redeemScript') or segwit_script(pubkeys[0])
                    return push_script(redeem_script)
                elif _type == 'address':
       t@@ -655,14 +695,21 @@ class Transaction:
                return script
        
            @classmethod
       +    def is_txin_complete(self, txin):
       +        num_sig = txin.get('num_sig', 1)
       +        x_signatures = txin['signatures']
       +        signatures = list(filter(None, x_signatures))
       +        return len(signatures) == num_sig
       +
       +    @classmethod
            def get_preimage_script(self, txin):
                # only for non-segwit
                if txin['type'] == 'p2pkh':
                    return bitcoin.address_to_script(txin['address'])
       -        elif txin['type'] == 'p2sh':
       +        elif txin['type'] in ['p2sh', 'p2wsh']:
                    pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin)
                    return multisig_script(pubkeys, txin['num_sig'])
       -        elif txin['type'] == 'p2wpkh-p2sh':
       +        elif txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']:
                    pubkey = txin['pubkeys'][0]
                    pkh = bh2u(bitcoin.hash_160(bfh(pubkey)))
                    return '76a9' + push_script(pkh) + '88ac'
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -1545,7 +1545,7 @@ class Simple_Wallet(Abstract_Wallet):
            def load_keystore(self):
                self.keystore = load_keystore(self.storage, 'keystore')
                self.is_segwit = self.keystore.is_segwit()
       -        self.txin_type = 'p2wpkh-p2sh' if self.is_segwit else 'p2pkh'
       +        self.txin_type = 'p2wpkh' if self.is_segwit else 'p2pkh'
        
            def get_pubkey(self, c, i):
                return self.derive_pubkeys(c, i)
       t@@ -1635,15 +1635,6 @@ class Simple_Deterministic_Wallet(Deterministic_Wallet, Simple_Wallet):
                return addr
        
        
       -class P2SH:
       -
       -    def pubkeys_to_redeem_script(self, pubkeys):
       -        raise NotImplementedError()
       -
       -    def pubkeys_to_address(self, pubkey):
       -        redeem_script = self.pubkeys_to_redeem_script(pubkey)
       -        return bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script)))
       -
        
        class Standard_Wallet(Simple_Deterministic_Wallet):
            wallet_type = 'standard'
       t@@ -1653,19 +1644,20 @@ class Standard_Wallet(Simple_Deterministic_Wallet):
                    return transaction.segwit_script(pubkey)
        
            def pubkeys_to_address(self, pubkey):
       -        if not self.is_segwit:
       +        if self.txin_type == 'p2pkh':
                    return bitcoin.public_key_to_p2pkh(bfh(pubkey))
       -        elif bitcoin.TESTNET:
       +        elif self.txin_type == 'p2wpkh':
       +            return bitcoin.hash_to_segwit_addr(hash_160(bfh(pubkey)))
       +        elif self.txin_type == 'p2wpkh-p2sh':
                    redeem_script = self.pubkeys_to_redeem_script(pubkey)
                    return bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script)))
                else:
                    raise NotImplementedError()
        
        
       -class Multisig_Wallet(Deterministic_Wallet, P2SH):
       +class Multisig_Wallet(Deterministic_Wallet):
            # generic m of n
            gap_limit = 20
       -    txin_type = 'p2sh'
        
            def __init__(self, storage):
                self.wallet_type = storage.get('wallet_type')
       t@@ -1675,9 +1667,19 @@ class Multisig_Wallet(Deterministic_Wallet, P2SH):
            def get_pubkeys(self, c, i):
                return self.derive_pubkeys(c, i)
        
       -    def redeem_script(self, c, i):
       -        pubkeys = self.get_pubkeys(c, i)
       -        return transaction.multisig_script(sorted(pubkeys), self.m)
       +    def pubkeys_to_address(self, pubkey):
       +        if self.txin_type == 'p2sh':
       +            redeem_script = self.pubkeys_to_redeem_script(pubkey)
       +            return bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script)))
       +        elif self.txin_type == 'p2wsh':
       +            witness_script = self.pubkeys_to_redeem_script(pubkey)
       +            return bitcoin.script_to_p2wsh(witness_script)
       +        else:
       +            raise NotImplementedError()
       +
       +    #def redeem_script(self, c, i):
       +    #    pubkeys = self.get_pubkeys(c, i)
       +    #    return transaction.multisig_script(sorted(pubkeys), self.m)
        
            def pubkeys_to_redeem_script(self, pubkeys):
                return transaction.multisig_script(sorted(pubkeys), self.m)
       t@@ -1691,6 +1693,8 @@ class Multisig_Wallet(Deterministic_Wallet, P2SH):
                    name = 'x%d/'%(i+1)
                    self.keystores[name] = load_keystore(self.storage, name)
                self.keystore = self.keystores['x1/']
       +        self.is_segwit = self.keystore.is_segwit()
       +        self.txin_type = 'p2wsh' if self.is_segwit else 'p2sh'
        
            def save_keystore(self):
                for name, k in self.keystores.items():